Doxygen
Loading...
Searching...
No Matches
doxygen.cpp
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * Copyright (C) 1997-2015 by Dimitri van Heesch.
4 *
5 * Permission to use, copy, modify, and distribute this software and its
6 * documentation under the terms of the GNU General Public License is hereby
7 * granted. No representations are made about the suitability of this software
8 * for any purpose. It is provided "as is" without express or implied warranty.
9 * See the GNU General Public License for more details.
10 *
11 * Documents produced by Doxygen are derivative works derived from the
12 * input used in their production; they are not affected by this license.
13 *
14 */
15
16#include <cstdio>
17#include <cstdlib>
18#include <cerrno>
19#include <sys/stat.h>
20
21#include <algorithm>
22#include <unordered_map>
23#include <memory>
24#include <cinttypes>
25#include <chrono>
26#include <clocale>
27#include <locale>
28
29#include "aliases.h"
30#include "arguments.h"
31#include "cite.h"
32#include "clangparser.h"
33#include "classlist.h"
34#include "cmdmapper.h"
35#include "code.h"
36#include "commentcnv.h"
37#include "conceptdef.h"
38#include "config.h"
39#include "debug.h"
40#include "declinfo.h"
41#include "defargs.h"
42#include "defgen.h"
43#include "dir.h"
44#include "dirdef.h"
45#include "docbookgen.h"
46#include "docparser.h"
47#include "docsets.h"
48#include "dot.h"
49#include "doxygen.h"
50#include "eclipsehelp.h"
51#include "emoji.h"
52#include "entry.h"
53#include "fileinfo.h"
54#include "filename.h"
55#include "fileparser.h"
56#include "formula.h"
57#include "fortrancode.h"
58#include "fortranscanner.h"
59#include "ftvhelp.h"
60#include "groupdef.h"
61#include "htags.h"
62#include "htmlgen.h"
63#include "htmlhelp.h"
64#include "index.h"
65#include "indexlist.h"
66#include "language.h"
67#include "latexgen.h"
68#include "layout.h"
69#include "lexcode.h"
70#include "lexscanner.h"
71#include "mangen.h"
72#include "markdown.h"
73#include "membergroup.h"
74#include "memberlist.h"
75#include "membername.h"
76#include "mermaid.h"
77#include "message.h"
78#include "moduledef.h"
79#include "msc.h"
80#include "namespacedef.h"
81#include "outputlist.h"
82#include "pagedef.h"
83#include "parserintf.h"
84#include "perlmodgen.h"
85#include "plantuml.h"
86#include "portable.h"
87#include "pre.h"
88#include "pycode.h"
89#include "pyscanner.h"
90#include "qhp.h"
91#include "reflist.h"
92#include "regex.h"
93#include "requirement.h"
94#include "rtfgen.h"
95#include "scanner.h"
96#include "searchindex_js.h"
97#include "settings.h"
98#include "singlecomment.h"
99#include "sitemap.h"
100#include "sqlcode.h"
101#include "sqlite3gen.h"
102#include "stlsupport.h"
103#include "stringutil.h"
104#include "symbolresolver.h"
105#include "tagreader.h"
106#include "threadpool.h"
107#include "trace.h"
108#include "util.h"
109#include "version.h"
110#include "vhdlcode.h"
111#include "vhdldocgen.h"
112#include "vhdljjparser.h"
113#include "xmlcode.h"
114#include "xmlgen.h"
115
116#include <sqlite3.h>
117
118#if USE_LIBCLANG
119#if defined(__GNUC__)
120#pragma GCC diagnostic push
121#pragma GCC diagnostic ignored "-Wshadow"
122#endif
123#include <clang/Basic/Version.h>
124#if defined(__GNUC__)
125#pragma GCC diagnostic pop
126#endif
127#endif
128
129// provided by the generated file resources.cpp
130extern void initResources();
131
132#if !defined(_WIN32) || defined(__CYGWIN__)
133#include <signal.h>
134#define HAS_SIGNALS
135#endif
136
137// globally accessible variables
149FileNameLinkedMap *Doxygen::includeNameLinkedMap = nullptr; // include names
155FileNameLinkedMap *Doxygen::plantUmlFileNameLinkedMap = nullptr;// plantuml files
156FileNameLinkedMap *Doxygen::mermaidFileNameLinkedMap = nullptr; // mermaid files
158StringMap Doxygen::tagDestinationMap; // all tag locations
159StringUnorderedSet Doxygen::tagFileSet; // all tag file names
160StringUnorderedSet Doxygen::expandAsDefinedSet; // all macros that should be expanded
161MemberGroupInfoMap Doxygen::memberGroupInfoMap; // dictionary of the member groups heading
162std::unique_ptr<PageDef> Doxygen::mainPage;
163std::unique_ptr<NamespaceDef> Doxygen::globalNamespaceDef;
165bool Doxygen::parseSourcesNeeded = false;
183std::mutex Doxygen::addExampleMutex;
185
186// locally accessible globals
187static std::multimap< std::string, const Entry* > g_classEntries;
189static OutputList *g_outputList = nullptr; // list of output generating objects
190static StringSet g_usingDeclarations; // used classes
191static bool g_successfulRun = false;
192static bool g_dumpSymbolMap = false;
194static bool g_singleComment=false;
195
196
197
198// keywords recognized as compounds
200{ "template class", "template struct", "class", "struct", "union", "interface", "exception" };
201
229
231{
232 public:
234 void begin(const char *name)
235 {
236 msg("{}", name);
237 stats.emplace_back(name,0);
238 startTime = std::chrono::steady_clock::now();
239 }
240 void end()
241 {
242 std::chrono::steady_clock::time_point endTime = std::chrono::steady_clock::now();
243 stats.back().elapsed = static_cast<double>(std::chrono::duration_cast<
244 std::chrono::microseconds>(endTime - startTime).count())/1000000.0;
245 warn_flush();
246 }
247 void print()
248 {
249 bool restore=false;
251 {
253 restore=true;
254 }
255 msg("----------------------\n");
256 for (const auto &s : stats)
257 {
258 msg("Spent {:.6f} seconds in {}",s.elapsed,s.name);
259 }
260 if (restore) Debug::setFlag(Debug::Time);
261 }
262 private:
263 struct stat
264 {
265 const char *name;
266 double elapsed;
267 //stat() : name(nullptr),elapsed(0) {}
268 stat(const char *n, double el) : name(n),elapsed(el) {}
269 };
270 std::vector<stat> stats;
271 std::chrono::steady_clock::time_point startTime;
273
274
275static void addMemberDocs(const Entry *root,MemberDefMutable *md, const DString &funcDecl,
276 const ArgumentList *al,bool over_load,TypeSpecifier spec);
277static void findMember(const Entry *root,
278 const DString &relates,
279 const DString &type,
280 const DString &args,
281 DString funcDecl,
282 bool overloaded,
283 bool isFunc
284 );
285
292
293
294static bool findClassRelation(
295 const Entry *root,
296 Definition *context,
297 ClassDefMutable *cd,
298 const BaseInfo *bi,
299 const TemplateNameMap &templateNames,
300 /*bool insertUndocumented*/
302 bool isArtificial
303 );
304
305//----------------------------------------------------------------------------
306
308 FileDef *fileScope,const TagInfo *tagInfo);
309static void resolveTemplateInstanceInType(const Entry *root,const Definition *scope,const MemberDef *md);
310
311static void addPageToContext(PageDef *pd,Entry *root)
312{
313 if (root->parent()) // add the page to it's scope
314 {
315 DString scope = root->parent()->name;
316 if (root->parent()->section.isPackageDoc())
317 {
318 scope=substitute(scope,".","::");
319 }
320 scope = stripAnonymousNamespaceScope(scope);
321 scope+="::"+pd->name();
323 if (d)
324 {
325 pd->setPageScope(d);
326 }
327 }
328}
329
330static void addRelatedPage(Entry *root)
331{
332 GroupDef *gd=nullptr;
333 for (const Grouping &g : root->groups)
334 {
335 if (!g.groupname.empty() && (gd=Doxygen::groupLinkedMap->find(g.groupname))) break;
336 }
337 //printf("---> addRelatedPage() %s gd=%p\n",qPrint(root->name),gd);
338 DString doc=root->doc+root->inbodyDocs;
339
340 PageDef *pd = addRelatedPage(root->name, // name
341 root->args, // ptitle
342 doc, // doc
343 root->docFile, // fileName
344 root->docLine, // docLine
345 root->startLine, // startLine
346 root->sli, // sli
347 gd, // gd
348 root->tagInfo(), // tagInfo
349 false, // xref
350 root->lang // lang
351 );
352 if (pd)
353 {
354 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
356 pd->setLocalToc(root->localToc);
357 addPageToContext(pd,root);
358 }
359}
360
361static void buildGroupListFiltered(const Entry *root,bool additional, bool includeExternal)
362{
363 if (root->section.isGroupDoc() && !root->name.empty() &&
364 ((!includeExternal && root->tagInfo()==nullptr) ||
365 ( includeExternal && root->tagInfo()!=nullptr))
366 )
367 {
368 AUTO_TRACE("additional={} includeExternal={}",additional,includeExternal);
369 if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
370 (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
371 {
373 AUTO_TRACE_ADD("Processing group '{}':'{}' gd={}", root->type,root->name,(void*)gd);
374
375 if (gd)
376 {
377 if ( !gd->hasGroupTitle() )
378 {
379 gd->setGroupTitle( root->type );
380 }
381 else if ( root->type.length() > 0 && root->name != root->type && gd->groupTitle() != root->type )
382 {
383 warn( root->fileName,root->startLine,
384 "group {}: ignoring title \"{}\" that does not match old title \"{}\"",
385 root->name, root->type, gd->groupTitle() );
386 }
387 gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
388 gd->setDocumentation( root->doc, root->docFile, root->docLine );
389 gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
391 gd->setRefItems(root->sli);
393 gd->setLanguage(root->lang);
395 {
396 root->commandOverrides.apply_groupGraph([&](bool b) { gd->overrideGroupGraph(b); });
397 }
398 }
399 else
400 {
401 if (root->tagInfo())
402 {
403 gd = Doxygen::groupLinkedMap->add(root->name,
404 std::unique_ptr<GroupDef>(
405 createGroupDef(root->fileName,root->startLine,root->name,root->type,root->tagInfo()->fileName)));
406 gd->setReference(root->tagInfo()->tagName);
407 }
408 else
409 {
410 gd = Doxygen::groupLinkedMap->add(root->name,
411 std::unique_ptr<GroupDef>(
412 createGroupDef(root->fileName,root->startLine,root->name,root->type)));
413 }
414 gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
415 // allow empty docs for group
416 gd->setDocumentation(!root->doc.empty() ? root->doc : DString(" "),root->docFile,root->docLine,false);
417 gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
419 gd->setRefItems(root->sli);
421 gd->setLanguage(root->lang);
423 {
424 root->commandOverrides.apply_groupGraph([&](bool b) { gd->overrideGroupGraph(b); });
425 }
426 }
427 }
428 }
429 for (const auto &e : root->children()) buildGroupListFiltered(e.get(),additional,includeExternal);
430}
431
432static void buildGroupList(const Entry *root)
433{
434 // --- first process only local groups
435 // first process the @defgroups blocks
436 buildGroupListFiltered(root,false,false);
437 // then process the @addtogroup, @weakgroup blocks
438 buildGroupListFiltered(root,true,false);
439
440 // --- then also process external groups
441 // first process the @defgroups blocks
442 buildGroupListFiltered(root,false,true);
443 // then process the @addtogroup, @weakgroup blocks
444 buildGroupListFiltered(root,true,true);
445}
446
447static void findGroupScope(const Entry *root)
448{
449 if (root->section.isGroupDoc() && !root->name.empty() &&
450 root->parent() && !root->parent()->name.empty())
451 {
453 if (gd)
454 {
455 DString scope = root->parent()->name;
456 if (root->parent()->section.isPackageDoc())
457 {
458 scope=substitute(scope,".","::");
459 }
460 scope = stripAnonymousNamespaceScope(scope);
461 scope+="::"+gd->name();
463 if (d)
464 {
465 gd->setGroupScope(d);
466 }
467 }
468 }
469 for (const auto &e : root->children()) findGroupScope(e.get());
470}
471
472static void organizeSubGroupsFiltered(const Entry *root,bool additional)
473{
474 if (root->section.isGroupDoc() && !root->name.empty())
475 {
476 AUTO_TRACE("additional={}",additional);
477 if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
478 (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
479 {
481 if (gd)
482 {
483 AUTO_TRACE_ADD("adding {} to group {}",root->name,gd->name());
484 addGroupToGroups(root,gd);
485 }
486 }
487 }
488 for (const auto &e : root->children()) organizeSubGroupsFiltered(e.get(),additional);
489}
490
491static void organizeSubGroups(const Entry *root)
492{
493 //printf("Defining groups\n");
494 // first process the @defgroups blocks
495 organizeSubGroupsFiltered(root,false);
496 //printf("Additional groups\n");
497 // then process the @addtogroup, @weakgroup blocks
498 organizeSubGroupsFiltered(root,true);
499}
500
501//----------------------------------------------------------------------
502
503static void buildFileList(const Entry *root)
504{
505 if ((root->section.isFileDoc() || (root->section.isFile() && Config_getBool(EXTRACT_ALL))) &&
506 !root->name.empty() && !root->tagInfo() // skip any file coming from tag files
507 )
508 {
509 bool ambig = false;
511 if (!fd || ambig)
512 {
513 bool save_ambig = ambig;
514 // use the directory of the file to see if the described file is in the same
515 // directory as the describing file.
516 DString fn = root->fileName;
517 size_t newIndex=fn.rfind('/');
518 if (newIndex==DString::npos)
519 {
520 fn = root->name;
521 }
522 else
523 {
524 fn = fn.left(newIndex)+"/"+root->name;
525 }
527 if (!fd) ambig = save_ambig;
528 }
529 //printf("**************** root->name=%s fd=%p\n",qPrint(root->name),(void*)fd);
530 if (fd && !ambig)
531 {
532 //printf("Adding documentation!\n");
533 // using false in setDocumentation is small hack to make sure a file
534 // is documented even if a \file command is used without further
535 // documentation
536 fd->setDocumentation(root->doc,root->docFile,root->docLine,false);
537 fd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
539 fd->setRefItems(root->sli);
541 root->commandOverrides.apply_includeGraph ([&](bool b) { fd->overrideIncludeGraph(b); });
542 root->commandOverrides.apply_includedByGraph([&](bool b) { fd->overrideIncludedByGraph(b); });
543 for (const Grouping &g : root->groups)
544 {
545 GroupDef *gd=nullptr;
547 {
548 if (!gd->containsFile(fd))
549 {
550 gd->addFile(fd);
551 fd->makePartOfGroup(gd);
552 //printf("File %s: in group %s\n",qPrint(fd->name()),qPrint(gd->name()));
553 }
554 }
555 else if (!gd && g.pri == Grouping::GROUPING_INGROUP)
556 {
557 warn(root->fileName, root->startLine,
558 "Found non-existing group '{}' for the command '{}', ignoring command",
560 );
561 }
562 }
563 }
564 else
565 {
566 DString text(4096, DString::ExplicitSize);
567 text.sprintf("the name '%s' supplied as "
568 "the argument in the \\file statement ",
569 qPrint(root->name));
570 if (ambig) // name is ambiguous
571 {
572 text+="matches the following input files:\n";
574 text+="\n";
575 text+="Please use a more specific name by "
576 "including a (larger) part of the path!";
577 }
578 else // name is not an input file
579 {
580 text+="is not an input file";
581 }
582 warn(root->fileName,root->startLine,"{}", text);
583 }
584 }
585 for (const auto &e : root->children()) buildFileList(e.get());
586}
587
588template<class DefMutable>
589static void addIncludeFile(DefMutable *cd,FileDef *ifd,const Entry *root)
590{
591 if (
592 (!root->doc.stripWhiteSpace().empty() ||
593 !root->brief.stripWhiteSpace().empty() ||
594 Config_getBool(EXTRACT_ALL)
595 ) && root->protection!=Protection::Private
596 )
597 {
598 //printf(">>>>>> includeFile=%s\n",qPrint(root->includeFile));
599
600 bool local=Config_getBool(FORCE_LOCAL_INCLUDES);
601 DString includeFile = root->includeFile;
602 if (!includeFile.empty() && includeFile.at(0)=='"')
603 {
604 local = true;
605 includeFile=includeFile.mid(1,includeFile.length()-2);
606 }
607 else if (!includeFile.empty() && includeFile.at(0)=='<')
608 {
609 local = false;
610 includeFile=includeFile.mid(1,includeFile.length()-2);
611 }
612
613 bool ambig = false;
614 FileDef *fd=nullptr;
615 // see if we need to include a verbatim copy of the header file
616 //printf("root->includeFile=%s\n",qPrint(root->includeFile));
617 if (!includeFile.empty() &&
618 (fd=Doxygen::inputNameLinkedMap->findFileDef(includeFile,ambig))==nullptr
619 )
620 { // explicit request
621 DString text;
622 text.sprintf("the name '%s' supplied as "
623 "the argument of the \\class, \\struct, \\union, or \\include command ",
624 qPrint(includeFile)
625 );
626 if (ambig) // name is ambiguous
627 {
628 text+="matches the following input files:\n";
630 text+="\n";
631 text+="Please use a more specific name by "
632 "including a (larger) part of the path!";
633 }
634 else // name is not an input file
635 {
636 text+="is not an input file";
637 }
638 warn(root->fileName,root->startLine, "{}", text);
639 }
640 else if (includeFile.empty() && ifd &&
641 // see if the file extension makes sense
642 EntryType::guessSection(ifd->name()).isHeader())
643 { // implicit assumption
644 fd=ifd;
645 }
646
647 // if a file is found, we mark it as a source file.
648 if (fd)
649 {
650 DString iName = !root->includeName.empty() ?
651 root->includeName : includeFile;
652 if (!iName.empty()) // user specified include file
653 {
654 if (iName.at(0)=='<') local=false; // explicit override
655 else if (iName.at(0)=='"') local=true;
656 if (iName.at(0)=='"' || iName.at(0)=='<')
657 {
658 iName=iName.mid(1,iName.length()-2); // strip quotes or brackets
659 }
660 if (iName.empty())
661 {
662 iName=fd->name();
663 }
664 }
665 else if (!Config_getList(STRIP_FROM_INC_PATH).empty())
666 {
668 }
669 else // use name of the file containing the class definition
670 {
671 iName=fd->name();
672 }
673 if (fd->generateSourceFile()) // generate code for header
674 {
675 cd->setIncludeFile(fd,iName,local,!root->includeName.empty());
676 }
677 else // put #include in the class documentation without link
678 {
679 cd->setIncludeFile(nullptr,iName,local,true);
680 }
681 }
682 }
683}
684
685
687{
688 size_t l = s.length();
689 int count=0;
690 int round=0;
691 DString result;
692 for (size_t i=0;i<l;i++)
693 {
694 char c=s.at(i);
695 if (c=='(') round++;
696 else if (c==')' && round>0) round--;
697 else if (c=='<' && round==0) count++;
698 if (count==0)
699 {
700 result+=c;
701 }
702 if (c=='>' && round==0 && count>0) count--;
703 }
704 //printf("stripTemplateSpecifiers(%s)=%s\n",qPrint(s),qPrint(result));
705 return result;
706}
707
708/*! returns the Definition object belonging to the first \a level levels of
709 * full qualified name \a name. Creates an artificial scope if the scope is
710 * not found and set the parent/child scope relation if the scope is found.
711 */
712[[maybe_unused]]
713static Definition *buildScopeFromQualifiedName(const DString &name_,SrcLangExt lang,const TagInfo *tagInfo)
714{
715 DString name = stripTemplateSpecifiers(name_);
716 name.stripPrefix("::");
717 int level = name.contains("::");
718 //printf("buildScopeFromQualifiedName(%s) level=%d\n",qPrint(name),level);
719 int i=0, p=0, l=0;
721 DString fullScope;
722 while (i<level)
723 {
724 int idx=getScopeFragment(name,p,&l);
725 if (idx==-1) return prevScope;
726 DString nsName = name.mid(idx,l);
727 if (nsName.empty()) return prevScope;
728 if (!fullScope.empty()) fullScope+="::";
729 fullScope+=nsName;
731 DefinitionMutable *innerScope = toDefinitionMutable(nd);
732 ClassDef *cd=nullptr;
733 if (nd==nullptr) cd = getClass(fullScope);
734 if (nd==nullptr && cd) // scope is a class
735 {
736 innerScope = toDefinitionMutable(cd);
737 }
738 else if (nd==nullptr && cd==nullptr && fullScope.find('<')==DString::npos) // scope is not known and could be a namespace!
739 {
740 // introduce bogus namespace
741 //printf("++ adding dummy namespace %s to %s tagInfo=%p\n",qPrint(nsName),qPrint(prevScope->name()),(void*)tagInfo);
742 NamespaceDefMutable *newNd=
744 Doxygen::namespaceLinkedMap->add(fullScope,
746 "[generated]",1,1,fullScope,
747 tagInfo?tagInfo->tagName:DString(),
748 tagInfo?tagInfo->fileName:DString())));
749 if (newNd)
750 {
751 newNd->setLanguage(lang);
752 newNd->setArtificial(true);
753 // add namespace to the list
754 innerScope = newNd;
755 }
756 }
757 else // scope is a namespace
758 {
759 }
760 if (innerScope)
761 {
762 // make the parent/child scope relation
763 DefinitionMutable *prevScopeMutable = toDefinitionMutable(prevScope);
764 if (prevScopeMutable)
765 {
766 prevScopeMutable->addInnerCompound(toDefinition(innerScope));
767 }
768 innerScope->setOuterScope(prevScope);
769 }
770 else // current scope is a class, so return only the namespace part...
771 {
772 return prevScope;
773 }
774 // proceed to the next scope fragment
775 p=idx+l+2;
776 prevScope=toDefinition(innerScope);
777 i++;
778 }
779 return prevScope;
780}
781
783 FileDef *fileScope,const TagInfo *tagInfo)
784{
785 //printf("<findScopeFromQualifiedName(%s,%s)\n",startScope ? qPrint(startScope->name()) : 0, qPrint(n));
786 Definition *resultScope=toDefinition(startScope);
787 if (resultScope==nullptr) resultScope=Doxygen::globalScope;
789 int l1 = 0;
790 int i1 = getScopeFragment(scope,0,&l1);
791 if (i1==-1)
792 {
793 //printf(">no fragments!\n");
794 return resultScope;
795 }
796 int p=i1+l1,l2=0,i2=0;
797 while ((i2=getScopeFragment(scope,p,&l2))!=-1)
798 {
799 DString nestedNameSpecifier = scope.mid(i1,l1);
800 Definition *orgScope = resultScope;
801 //printf(" nestedNameSpecifier=%s\n",qPrint(nestedNameSpecifier));
802 resultScope = const_cast<Definition*>(resultScope->findInnerCompound(nestedNameSpecifier));
803 //printf(" resultScope=%p\n",resultScope);
804 if (resultScope==nullptr)
805 {
806 if (orgScope==Doxygen::globalScope && fileScope && !fileScope->getUsedNamespaces().empty())
807 // also search for used namespaces
808 {
809 for (const auto &nd : fileScope->getUsedNamespaces())
810 {
812 if (mnd)
813 {
814 resultScope = findScopeFromQualifiedName(toNamespaceDefMutable(nd),n,fileScope,tagInfo);
815 if (resultScope!=nullptr) break;
816 }
817 }
818 if (resultScope)
819 {
820 // for a nested class A::I in used namespace N, we get
821 // N::A::I while looking for A, so we should compare
822 // resultScope->name() against scope.left(i2+l2)
823 //printf(" -> result=%s scope=%s\n",qPrint(resultScope->name()),qPrint(scope));
824 if (rightScopeMatch(resultScope->name(),scope.left(i2+l2)))
825 {
826 break;
827 }
828 goto nextFragment;
829 }
830 }
831
832 // also search for used classes. Complication: we haven't been able
833 // to put them in the right scope yet, because we are still resolving
834 // the scope relations!
835 // Therefore loop through all used classes and see if there is a right
836 // scope match between the used class and nestedNameSpecifier.
837 for (const auto &usedName : g_usingDeclarations)
838 {
839 //printf("Checking using class %s\n",qPrint(usedName));
840 if (rightScopeMatch(usedName,nestedNameSpecifier))
841 {
842 // ui.currentKey() is the fully qualified name of nestedNameSpecifier
843 // so use this instead.
844 DString fqn = usedName + scope.mid(p);
845 resultScope = buildScopeFromQualifiedName(fqn,startScope->getLanguage(),nullptr);
846 //printf("Creating scope from fqn=%s result %p\n",qPrint(fqn),resultScope);
847 if (resultScope)
848 {
849 //printf("> Match! resultScope=%s\n",qPrint(resultScope->name()));
850 return resultScope;
851 }
852 }
853 }
854
855 //printf("> name %s not found in scope %s\n",qPrint(nestedNameSpecifier),qPrint(orgScope->name()));
856 return nullptr;
857 }
858 nextFragment:
859 i1=i2;
860 l1=l2;
861 p=i2+l2;
862 }
863 //printf(">findScopeFromQualifiedName scope %s\n",qPrint(resultScope->name()));
864 return resultScope;
865}
866
867std::unique_ptr<ArgumentList> getTemplateArgumentsFromName(
868 const DString &name,
869 const ArgumentLists &tArgLists)
870{
871 // for each scope fragment, check if it is a template and advance through
872 // the list if so.
873 size_t i=0, p=0;
874 auto alIt = tArgLists.begin();
875 while ((i=name.find("::",p))!=DString::npos && alIt!=tArgLists.end())
876 {
878 if (nd==nullptr)
879 {
880 ClassDef *cd = getClass(name.left(i));
881 if (cd)
882 {
883 if (!cd->templateArguments().empty())
884 {
885 ++alIt;
886 }
887 }
888 }
889 p=i+2;
890 }
891 return alIt!=tArgLists.end() ?
892 std::make_unique<ArgumentList>(*alIt) :
893 std::unique_ptr<ArgumentList>();
894}
895
896static
898{
900
901 if (specifier.isStruct())
903 else if (specifier.isUnion())
904 sec=ClassDef::Union;
905 else if (specifier.isCategory())
907 else if (specifier.isInterface())
909 else if (specifier.isProtocol())
911 else if (specifier.isException())
913 else if (specifier.isService())
915 else if (specifier.isSingleton())
917
918 if (section.isUnionDoc())
919 sec=ClassDef::Union;
920 else if (section.isStructDoc())
922 else if (section.isInterfaceDoc())
924 else if (section.isProtocolDoc())
926 else if (section.isCategoryDoc())
928 else if (section.isExceptionDoc())
930 else if (section.isServiceDoc())
932 else if (section.isSingletonDoc())
934
935 return sec;
936}
937
938
939static void addClassToContext(const Entry *root)
940{
941 AUTO_TRACE("name={}",root->name);
942 FileDef *fd = root->fileDef();
943
944 DString scName;
945 if (root->parent()->section.isScope())
946 {
947 scName=root->parent()->name;
948 }
949 // name without parent's scope
950 DString fullName = root->name;
951
952 // strip off any template parameters (but not those for specializations)
953 if (size_t idx=fullName.find('>'); idx!=DString::npos && root->lang==SrcLangExt::CSharp) // mangle A<S,T>::N as A-2-g::N
954 {
955 fullName = mangleCSharpGenericName(fullName.left(idx+1))+fullName.mid(idx+1);
956 }
957 fullName=stripTemplateSpecifiersFromScope(fullName);
958
959 // name with scope (if not present already)
960 DString qualifiedName = fullName;
961 if (!scName.empty() && !leftScopeMatch(scName,fullName))
962 {
963 qualifiedName.prepend(scName+"::");
964 }
965
966 // see if we already found the class before
967 ClassDefMutable *cd = getClassMutable(qualifiedName);
968
969 AUTO_TRACE_ADD("Found class with name '{}', qualifiedName '{}'", cd ? cd->name() : root->name, qualifiedName);
970
971 if (cd)
972 {
973 fullName=cd->name();
974 AUTO_TRACE_ADD("Existing class '{}'",cd->name());
975 //if (cd->templateArguments()==0)
976 //{
977 // //printf("existing ClassDef tempArgList=%p specScope=%s\n",root->tArgList,qPrint(root->scopeSpec));
978 // cd->setTemplateArguments(tArgList);
979 //}
980
981 cd->setDocumentation(root->doc,root->docFile,root->docLine);
982 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
983 root->commandOverrides.apply_collaborationGraph([&](bool b ) { cd->overrideCollaborationGraph(b); });
984 root->commandOverrides.apply_inheritanceGraph ([&](CLASS_GRAPH_t gt) { cd->overrideInheritanceGraph(gt); });
985
986 if (!root->spec.isForwardDecl() && cd->isForwardDeclared())
987 {
988 cd->setDefFile(root->fileName,root->startLine,root->startColumn);
989 if (root->bodyLine!=-1)
990 {
991 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
992 cd->setBodyDef(fd);
993 }
994 }
995
996 if (cd->templateArguments().empty() || (cd->isForwardDeclared() && !root->spec.isForwardDecl()))
997 {
998 // this happens if a template class declared with @class is found
999 // before the actual definition or if a forward declaration has different template
1000 // parameter names.
1001 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(cd->name(),root->tArgLists);
1002 if (tArgList)
1003 {
1004 cd->setTemplateArguments(*tArgList);
1005 }
1006 }
1007 if (cd->requiresClause().empty() && !root->req.empty())
1008 {
1009 cd->setRequiresClause(root->req);
1010 }
1011
1013
1014 cd->setMetaData(root->metaData);
1015 }
1016 else // new class
1017 {
1019
1020 DString className;
1021 DString namespaceName;
1022 extractNamespaceName(fullName,className,namespaceName);
1023
1024 AUTO_TRACE_ADD("New class: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1025 fullName, namespaceName, className, Trace::trunc(root->brief), Trace::trunc(root->doc));
1026
1027 DString tagName;
1028 DString refFileName;
1029 const TagInfo *tagInfo = root->tagInfo();
1030 if (tagInfo)
1031 {
1032 tagName = tagInfo->tagName;
1033 refFileName = tagInfo->fileName;
1034 if (fullName.find("::")!=DString::npos)
1035 // symbols imported via tag files may come without the parent scope,
1036 // so we artificially create it here
1037 {
1038 buildScopeFromQualifiedName(fullName,root->lang,tagInfo);
1039 }
1040 }
1041 std::unique_ptr<ArgumentList> tArgList;
1042 size_t i=0;
1043 if ((root->lang==SrcLangExt::CSharp || root->lang==SrcLangExt::Java) &&
1044 (i=fullName.find('<'))!=DString::npos)
1045 {
1046 // a Java/C# generic class looks like a C++ specialization, so we need to split the
1047 // name and template arguments here
1048 tArgList = stringToArgumentList(root->lang,fullName.mid(i));
1049 if (i!=DString::npos && root->lang==SrcLangExt::CSharp) // in C# A, A<T>, and A<T,S> are different classes, so we need some way to disguish them using this name mangling
1050 // A -> A
1051 // A<T> -> A-1-g
1052 // A<T,S> -> A-2-g
1053 {
1054 fullName=mangleCSharpGenericName(fullName);
1055 }
1056 else
1057 {
1058 fullName=fullName.left(i);
1059 }
1060 }
1061 else
1062 {
1063 tArgList = getTemplateArgumentsFromName(fullName,root->tArgLists);
1064 }
1065 // add class to the list
1066 cd = toClassDefMutable(
1067 Doxygen::classLinkedMap->add(fullName,
1068 createClassDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1069 fullName,sec,tagName,refFileName,true,root->spec.isEnum()) ));
1070 if (cd)
1071 {
1072 AUTO_TRACE_ADD("New class '{}' type={} #tArgLists={} tagInfo={} hidden={} artificial={}",
1073 fullName,cd->compoundTypeString(),root->tArgLists.size(),
1074 fmt::ptr(tagInfo),root->hidden,root->artificial);
1075 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1076 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1077 cd->setLanguage(root->lang);
1078 cd->setId(root->id);
1079 cd->setHidden(root->hidden);
1080 cd->setArtificial(root->artificial);
1081 cd->setClassSpecifier(root->spec);
1082 if (root->lang==SrcLangExt::CSharp && !root->args.empty())
1083 {
1085 }
1086 cd->addQualifiers(root->qualifiers);
1087 cd->setTypeConstraints(root->typeConstr);
1088 root->commandOverrides.apply_collaborationGraph([&](bool b ) { cd->overrideCollaborationGraph(b); });
1089 root->commandOverrides.apply_inheritanceGraph ([&](CLASS_GRAPH_t gt) { cd->overrideInheritanceGraph(gt); });
1090
1091 if (tArgList)
1092 {
1093 cd->setTemplateArguments(*tArgList);
1094 }
1095 cd->setRequiresClause(root->req);
1096 cd->setProtection(root->protection);
1097 cd->setIsStatic(root->isStatic);
1098
1099 // file definition containing the class cd
1100 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1101 cd->setBodyDef(fd);
1102
1103 cd->setMetaData(root->metaData);
1104
1105 cd->insertUsedFile(fd);
1106 }
1107 else
1108 {
1109 AUTO_TRACE_ADD("Class {} not added, already exists as alias", fullName);
1110 }
1111 }
1112
1113 if (cd)
1114 {
1116 if (!root->subGrouping) cd->setSubGrouping(false);
1117 if (!root->spec.isForwardDecl())
1118 {
1119 if (cd->hasDocumentation())
1120 {
1121 addIncludeFile(cd,fd,root);
1122 }
1123 if (fd && root->section.isCompound())
1124 {
1125 AUTO_TRACE_ADD("Inserting class {} in file {} (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1126 cd->setFileDef(fd);
1127 fd->insertClass(cd);
1128 }
1129 }
1130 addClassToGroups(root,cd);
1132 cd->setRefItems(root->sli);
1133 cd->setRequirementReferences(root->rqli);
1134 }
1135}
1136
1137//----------------------------------------------------------------------
1138// build a list of all classes mentioned in the documentation
1139// and all classes that have a documentation block before their definition.
1140static void buildClassList(const Entry *root)
1141{
1142 if ((root->section.isCompound() || root->section.isObjcImpl()) && !root->name.empty())
1143 {
1144 AUTO_TRACE();
1145 addClassToContext(root);
1146 }
1147 for (const auto &e : root->children()) buildClassList(e.get());
1148}
1149
1150static void buildClassDocList(const Entry *root)
1151{
1152 if ((root->section.isCompoundDoc()) && !root->name.empty())
1153 {
1154 AUTO_TRACE();
1155 addClassToContext(root);
1156 }
1157 for (const auto &e : root->children()) buildClassDocList(e.get());
1158}
1159
1160//----------------------------------------------------------------------
1161// build a list of all classes mentioned in the documentation
1162// and all classes that have a documentation block before their definition.
1163
1164static void addConceptToContext(const Entry *root)
1165{
1166 AUTO_TRACE();
1167 FileDef *fd = root->fileDef();
1168
1169 DString scName;
1170 if (root->parent()->section.isScope())
1171 {
1172 scName=root->parent()->name;
1173 }
1174
1175 // name with scope (if not present already)
1176 DString qualifiedName = root->name;
1177 if (!scName.empty() && !leftScopeMatch(qualifiedName,scName))
1178 {
1179 qualifiedName.prepend(scName+"::");
1180 }
1181
1182 // see if we already found the concept before
1183 ConceptDefMutable *cd = getConceptMutable(qualifiedName);
1184
1185 AUTO_TRACE_ADD("Found concept with name '{}' (qualifiedName='{}')", cd ? cd->name() : root->name, qualifiedName);
1186
1187 if (cd)
1188 {
1189 qualifiedName=cd->name();
1190 AUTO_TRACE_ADD("Existing concept '{}'",cd->name());
1191
1192 cd->setDocumentation(root->doc,root->docFile,root->docLine);
1193 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1194
1195 addIncludeFile(cd,fd,root);
1196 }
1197 else // new concept
1198 {
1199 DString className;
1200 DString namespaceName;
1201 extractNamespaceName(qualifiedName,className,namespaceName);
1202
1203 AUTO_TRACE_ADD("New concept: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1204 qualifiedName,namespaceName,className,root->brief,root->doc);
1205
1206 DString tagName;
1207 DString refFileName;
1208 const TagInfo *tagInfo = root->tagInfo();
1209 if (tagInfo)
1210 {
1211 tagName = tagInfo->tagName;
1212 refFileName = tagInfo->fileName;
1213 if (qualifiedName.find("::")!=DString::npos)
1214 // symbols imported via tag files may come without the parent scope,
1215 // so we artificially create it here
1216 {
1217 buildScopeFromQualifiedName(qualifiedName,root->lang,tagInfo);
1218 }
1219 }
1220 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(qualifiedName,root->tArgLists);
1221 // add concept to the list
1223 Doxygen::conceptLinkedMap->add(qualifiedName,
1224 createConceptDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1225 qualifiedName,tagName,refFileName)));
1226 if (cd)
1227 {
1228 AUTO_TRACE_ADD("New concept '{}' #tArgLists={} tagInfo={}",
1229 qualifiedName,root->tArgLists.size(),fmt::ptr(tagInfo));
1230 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1231 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1232 cd->setLanguage(root->lang);
1233 cd->setId(root->id);
1234 cd->setHidden(root->hidden);
1235 cd->setGroupId(root->mGrpId);
1236 if (tArgList)
1237 {
1238 cd->setTemplateArguments(*tArgList);
1239 }
1240 cd->setInitializer(root->initializer.str());
1241 // file definition containing the class cd
1242 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1243 cd->setBodyDef(fd);
1245 cd->setRefItems(root->sli);
1246 cd->setRequirementReferences(root->rqli);
1247 addIncludeFile(cd,fd,root);
1248
1249 // also add namespace to the correct structural context
1250 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,qualifiedName,nullptr,tagInfo);
1252 {
1254 if (dm)
1255 {
1256 dm->addInnerCompound(cd);
1257 }
1258 cd->setOuterScope(d);
1259 }
1260 for (const auto &ce : root->children())
1261 {
1262 //printf("Concept %s has child %s\n",qPrint(root->name),qPrint(ce->section.to_string()));
1263 if (ce->section.isConceptDocPart())
1264 {
1265 cd->addSectionsToDefinition(ce->anchors);
1266 cd->setRefItems(ce->sli);
1267 cd->setRequirementReferences(ce->rqli);
1268 if (!ce->brief.empty())
1269 {
1270 cd->addDocPart(ce->brief,ce->startLine,ce->startColumn);
1271 //printf(" brief=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->brief),ce->startLine,ce->startColumn);
1272 }
1273 if (!ce->doc.empty())
1274 {
1275 cd->addDocPart(ce->doc,ce->startLine,ce->startColumn);
1276 //printf(" doc=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->doc),ce->startLine,ce->startColumn);
1277 }
1278 }
1279 else if (ce->section.isConceptCodePart())
1280 {
1281 cd->addCodePart(ce->initializer.str(),ce->startLine,ce->startColumn);
1282 //printf(" code=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->initializer.str()),ce->startLine,ce->startColumn);
1283 }
1284 }
1285 }
1286 else
1287 {
1288 AUTO_TRACE_ADD("Concept '{}' not added, already exists (as alias)", qualifiedName);
1289 }
1290 }
1291
1292 if (cd)
1293 {
1295 for (const auto &ce : root->children())
1296 {
1297 if (ce->section.isConceptDocPart())
1298 {
1299 cd->addSectionsToDefinition(ce->anchors);
1300 }
1301 }
1302 if (fd)
1303 {
1304 AUTO_TRACE_ADD("Inserting concept '{}' in file '{}' (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1305 cd->setFileDef(fd);
1306 fd->insertConcept(cd);
1307 }
1308 addConceptToGroups(root,cd);
1310 cd->setRefItems(root->sli);
1311 cd->setRequirementReferences(root->rqli);
1312 }
1313}
1314
1315static void findModuleDocumentation(const Entry *root)
1316{
1317 if (root->section.isModuleDoc())
1318 {
1319 AUTO_TRACE();
1321 }
1322 for (const auto &e : root->children()) findModuleDocumentation(e.get());
1323}
1324
1325static void buildConceptList(const Entry *root)
1326{
1327 if (root->section.isConcept())
1328 {
1329 AUTO_TRACE();
1330 addConceptToContext(root);
1331 }
1332 for (const auto &e : root->children()) buildConceptList(e.get());
1333}
1334
1335static void buildConceptDocList(const Entry *root)
1336{
1337 if (root->section.isConceptDoc())
1338 {
1339 AUTO_TRACE();
1340 addConceptToContext(root);
1341 }
1342 for (const auto &e : root->children()) buildConceptDocList(e.get());
1343}
1344
1345// This routine is to allow @ingroup X @{ concept A; concept B; @} to work
1346// (same also works for variable and functions because of logic in MemberGroup::insertMember)
1348{
1349 AUTO_TRACE();
1350 for (const auto &cd : *Doxygen::conceptLinkedMap)
1351 {
1352 if (cd->groupId()!=DOX_NOGROUP)
1353 {
1354 for (const auto &ocd : *Doxygen::conceptLinkedMap)
1355 {
1356 if (cd!=ocd && cd->groupId()==ocd->groupId() &&
1357 !cd->partOfGroups().empty() && ocd->partOfGroups().empty())
1358 {
1359 ConceptDefMutable *ocdm = toConceptDefMutable(ocd.get());
1360 if (ocdm)
1361 {
1362 for (const auto &gd : cd->partOfGroups())
1363 {
1364 if (gd)
1365 {
1366 AUTO_TRACE_ADD("making concept '{}' part of group '{}'",ocdm->name(),gd->name());
1367 ocdm->makePartOfGroup(gd);
1368 gd->addConcept(ocd.get());
1369 }
1370 }
1371 }
1372 }
1373 }
1374 }
1375 }
1376}
1377
1378//----------------------------------------------------------------------
1379
1381{
1382 ClassDefSet visitedClasses;
1383
1384 bool done=false;
1385 //int iteration=0;
1386 while (!done)
1387 {
1388 done=true;
1389 //++iteration;
1390 struct ClassAlias
1391 {
1392 ClassAlias(const DString &name,std::unique_ptr<ClassDef> cd,DefinitionMutable *ctx) :
1393 aliasFullName(name),aliasCd(std::move(cd)), aliasContext(ctx) {}
1394 DString aliasFullName;
1395 std::unique_ptr<ClassDef> aliasCd;
1396 DefinitionMutable *aliasContext;
1397 };
1398 std::vector<ClassAlias> aliases;
1399 for (const auto &icd : *Doxygen::classLinkedMap)
1400 {
1401 ClassDefMutable *cd = toClassDefMutable(icd.get());
1402 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1403 {
1404 DString name = stripAnonymousNamespaceScope(icd->name());
1405 //printf("processing=%s, iteration=%d\n",qPrint(cd->name()),iteration);
1406 // also add class to the correct structural context
1408 name,icd->getFileDef(),nullptr);
1409 if (d)
1410 {
1411 //printf("****** adding %s to scope %s in iteration %d\n",qPrint(cd->name()),qPrint(d->name()),iteration);
1413 if (dm)
1414 {
1415 dm->addInnerCompound(cd);
1416 }
1417 cd->setOuterScope(d);
1418
1419 // for inline namespace add an alias of the class to the outer scope
1421 {
1423 //printf("nd->isInline()=%d\n",nd->isInline());
1424 if (nd && nd->isInline())
1425 {
1426 d = d->getOuterScope();
1427 if (d)
1428 {
1429 dm = toDefinitionMutable(d);
1430 if (dm)
1431 {
1432 auto aliasCd = createClassDefAlias(d,cd);
1433 DString aliasFullName = d->qualifiedName()+"::"+aliasCd->localName();
1434 aliases.emplace_back(aliasFullName,std::move(aliasCd),dm);
1435 //printf("adding %s to %s as %s\n",qPrint(aliasCd->name()),qPrint(d->name()),qPrint(aliasFullName));
1436 }
1437 }
1438 }
1439 else
1440 {
1441 break;
1442 }
1443 }
1444
1445 visitedClasses.insert(icd.get());
1446 done=false;
1447 }
1448 //else
1449 //{
1450 // printf("****** ignoring %s: scope not (yet) found in iteration %d\n",qPrint(cd->name()),iteration);
1451 //}
1452 }
1453 }
1454 // add aliases
1455 for (auto &alias : aliases)
1456 {
1457 ClassDef *aliasCd = Doxygen::classLinkedMap->add(alias.aliasFullName,std::move(alias.aliasCd));
1458 if (aliasCd)
1459 {
1460 alias.aliasContext->addInnerCompound(aliasCd);
1461 }
1462 }
1463 }
1464
1465 //give warnings for unresolved compounds
1466 for (const auto &icd : *Doxygen::classLinkedMap)
1467 {
1468 ClassDefMutable *cd = toClassDefMutable(icd.get());
1469 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1470 {
1472 /// create the scope artificially
1473 // anyway, so we can at least relate scopes properly.
1474 Definition *d = buildScopeFromQualifiedName(name,cd->getLanguage(),nullptr);
1475 if (d && d!=cd && !cd->getDefFileName().empty())
1476 // avoid recursion in case of redundant scopes, i.e: namespace N { class N::C {}; }
1477 // for this case doxygen assumes the existence of a namespace N::N in which C is to be found!
1478 // also avoid warning for stuff imported via a tagfile.
1479 {
1481 if (dm)
1482 {
1483 dm->addInnerCompound(cd);
1484 }
1485 cd->setOuterScope(d);
1486 warn(cd->getDefFileName(),cd->getDefLine(),
1487 "Incomplete input: scope for class {} not found!{}",name,
1488 name.startsWith("std::") ? " Try enabling BUILTIN_STL_SUPPORT." : ""
1489 );
1490 }
1491 }
1492 }
1493}
1494
1496{
1497 //bool inlineGroupedClasses = Config_getBool(INLINE_GROUPED_CLASSES);
1498 //if (!inlineGroupedClasses) return;
1499 //printf("** distributeClassGroupRelations()\n");
1500
1501 ClassDefSet visitedClasses;
1502 for (const auto &cd : *Doxygen::classLinkedMap)
1503 {
1504 //printf("Checking %s\n",qPrint(cd->name()));
1505 // distribute the group to nested classes as well
1506 if (visitedClasses.find(cd.get())==visitedClasses.end() && !cd->partOfGroups().empty())
1507 {
1508 //printf(" Candidate for merging\n");
1509 GroupDef *gd = cd->partOfGroups().front();
1510 for (auto &ncd : cd->getClasses())
1511 {
1513 if (ncdm && ncdm->partOfGroups().empty())
1514 {
1515 //printf(" Adding %s to group '%s'\n",qPrint(ncd->name()),
1516 // gd->groupTitle());
1517 ncdm->makePartOfGroup(gd);
1518 gd->addClass(ncdm);
1519 }
1520 }
1521 visitedClasses.insert(cd.get()); // only visit every class once
1522 }
1523 }
1524}
1525
1526//----------------------------------------------------------------------
1527
1528template<typename Container>
1530 const Container *cd,
1531 const MemberDef *enumTypeMember,
1532 MemberListType mlFilter)
1533{
1534 if (md && md->isEnumerate() && md->name().startsWith("@")) // anonymous enum type
1535 {
1536 MemberList *eiml = cd->getMemberList(mlFilter);
1537 if (eiml)
1538 {
1539 for (const auto &eimd : *eiml)
1540 {
1541 DString vtype = eimd->typeString();
1542 if (vtype.find(md->name())!=DString::npos)
1543 {
1545 if (mimd)
1546 {
1547 mimd->setAnonymousEnumType(enumTypeMember);
1548 break;
1549 }
1550 }
1551 }
1552 }
1553 }
1554}
1555
1556static ClassDefMutable *createTagLessInstance(const Definition *root,const ClassDef *templ,const DString &fieldName)
1557{
1558 DString n = templ->name();
1559 // replace e.g. X::@1343:@4343::Y -> X::[struct]::Y
1560 if (size_t sn = n.find('@'); sn!=DString::npos)
1561 {
1562 const char *p = n.data()+sn;
1563 char c;
1564 while ((c=*p))
1565 {
1566 if (!isdigit(c) && c!='@' && c!=':') break;
1567 p++;
1568 }
1569 n = n.left(sn)+"["+templ->compoundTypeString().str()+"]"+p;
1570 }
1571 // add field name to the class name to make it unique again, e.g. X::[struct]::Y.m
1572 DString fullName = n+"."+fieldName;
1573
1574 //printf("** adding class %s based on %s in %s\n",qPrint(fullName),qPrint(templ->name()),qPrint(root->name()));
1576 Doxygen::classLinkedMap->add(fullName,
1578 templ->getDefLine(),
1579 templ->getDefColumn(),
1580 fullName,
1581 templ->compoundType())));
1582 if (cd)
1583 {
1584 //printf("cd->name()=%s displayName=%s\n",qPrint(cd->name()),qPrint(cd->displayName()));
1585 cd->setDocumentation(templ->documentation(),templ->docFile(),templ->docLine()); // copy docs to definition
1586 cd->setBriefDescription(templ->briefDescription(),templ->briefFile(),templ->briefLine());
1587 cd->setLanguage(templ->getLanguage());
1588 cd->setBodySegment(templ->getDefLine(),templ->getStartBodyLine(),templ->getEndBodyLine());
1589 cd->setBodyDef(templ->getBodyDef());
1590
1591 if (root!=Doxygen::globalScope)
1592 {
1593 DefinitionMutable *outerScope = toDefinitionMutable(const_cast<Definition*>(root));
1594 if (root && root->definitionType()==Definition::TypeFile)
1595 {
1596 FileDef *fd = toFileDef(const_cast<Definition*>(root));
1597 fd->insertClass(cd);
1598 cd->setFileDef(fd);
1600 }
1601 else if (outerScope)
1602 {
1603 outerScope->addInnerCompound(cd);
1604 cd->setOuterScope(const_cast<Definition*>(root));
1605 }
1606 }
1607
1608 for (auto &gd : root->partOfGroups())
1609 {
1610 cd->makePartOfGroup(gd);
1611 gd->addClass(cd);
1612 }
1613
1614 auto addMember = [&](const MemberDef *md) -> MemberDefMutable*
1615 {
1616 auto newMd = createMemberDef(md->getDefFileName(),md->getDefLine(),md->getDefColumn(),
1617 md->typeString(),md->name(),md->argsString(),md->excpString(),
1618 md->protection(),md->virtualness(),md->isStatic(),Relationship::Member,
1619 md->memberType(),
1620 ArgumentList(),ArgumentList(),"");
1621 MemberDefMutable *imd = toMemberDefMutable(newMd.get());
1622 imd->setMemberClass(cd);
1623 imd->setDefinition(md->definition());
1624 imd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
1625 imd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
1626 imd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
1627 imd->setMemberSpecifiers(md->getMemberSpecifiers());
1628 imd->setId(md->id());
1629 imd->addQualifiers(md->getQualifiers());
1630 imd->setVhdlSpecifiers(md->getVhdlSpecifiers());
1631 imd->setMemberGroupId(md->getMemberGroupId());
1632 imd->setInitializer(md->initializer());
1633 imd->setRequiresClause(md->requiresClause());
1634 imd->setMaxInitLines(md->initializerLines());
1635 imd->setBitfields(md->bitfieldString());
1636 imd->setLanguage(md->getLanguage());
1638 cd->insertMember(imd);
1639 associateVariableWithAnonymousEnumType(md,cd,imd,MemberListType::PubAttribs());
1640 MemberName *mn = Doxygen::memberNameLinkedMap->add(md->name());
1641 mn->push_back(std::move(newMd));
1642 return imd;
1643
1644 };
1645
1646 MemberList *ml = templ->getMemberList(MemberListType::PubAttribs());
1647 if (ml)
1648 {
1649 for (const auto &md : *ml)
1650 {
1651 //printf(" Member attribute %s def=%s\n",qPrint(md->name()),qPrint(md->definition()));
1652 addMember(md);
1653 }
1654 }
1655 ml = templ->getMemberList(MemberListType::PubTypes());
1656 if (ml)
1657 {
1658 for (const auto &md : *ml)
1659 {
1660 //printf(" Member type %s def=%s\n",qPrint(md->name()),qPrint(md->definition()));
1661 MemberDefMutable *mdm = addMember(md);
1662 if (md->isEnumerate() && md->name().startsWith("@")) // anonymous enum type
1663 {
1664 for (const auto &emd : md->enumFieldList())
1665 {
1666 //printf(" enum field %s\n",qPrint(emd->name()));
1667 MemberDefMutable *emdm = addMember(emd);
1668 mdm->insertEnumField(emdm);
1669 emdm->setEnumScope(md);
1670 }
1671 }
1672 }
1673 }
1674 }
1675 return cd;
1676}
1677
1678/** Look through the members of class \a cd and its public members.
1679 * If there is a member m of a tag less struct/union,
1680 * then we create a duplicate of the struct/union with the name of the
1681 * member to identify it.
1682 * So if cd has name S, then the tag less struct/union will get name S.m
1683 * Since tag less structs can be nested we need to call this function
1684 * recursively. Later on we need to patch the member types so we keep
1685 * track of the hierarchy of classes we create.
1686 */
1687template<typename Container, typename TagContainer>
1688static void processTagLessClasses(const Definition *root,
1689 const Container *cd,
1690 const TagContainer *tagParent,
1691 MemberListType varFilter,
1692 MemberListType typeFilter,
1693 const DString &prefix,int count)
1694{
1695 AUTO_TRACE("count={} name={}\n",count,cd->name());
1696 if (tagParent /*&& !cd->getClasses().empty()*/)
1697 {
1698 MemberList *ml = cd->getMemberList(varFilter);
1699 if (ml)
1700 {
1701 int pos=0;
1702 for (const auto &md : *ml)
1703 {
1704 DString type = md->typeString();
1705 //printf(" member %s: type='%s' outerScope='%s'\n",qPrint(md->name()),qPrint(type),qPrint(md->getOuterScope()?md->getOuterScope()->name():"<null>"));
1706 if ((cd->definitionType()!=Definition::TypeFile || md->getOuterScope()==Doxygen::globalScope) && // part namespace members only if cd is a namespace
1707 (type.find("::@")!=DString::npos || type.find(" @")!=DString::npos)) // member of tag less struct/union
1708 {
1709 std::vector<const ClassDef *> candidates;
1710 for (const auto &icd : cd->getClasses())
1711 {
1712 candidates.push_back(icd);
1713 }
1714 for (const auto &icd : candidates)
1715 {
1716 //printf(" comparing '%s'<->'%s'\n",qPrint(type),qPrint(icd->name()));
1717 if (type.find(icd->name())!=DString::npos) // matching tag less struct/union
1718 {
1719 DString name = md->name();
1720 if (md->isAnonymous()) name = "__unnamed" + DString().setNum(pos++)+"__";
1721 if (!prefix.empty()) name.prepend(prefix+".");
1722 //printf(" found %s in scope %s\n",qPrint(name),qPrint(cd->name()));
1723 ClassDefMutable *ncd = createTagLessInstance(root,icd,name);
1724 if (ncd)
1725 {
1726 processTagLessClasses(ncd,icd,ncd,MemberListType::PubAttribs(),MemberListType::PubTypes(),name,count+1);
1727 //printf(" addTagged %s to %s\n",qPrint(ncd->name()),qPrint(tagParent->name()));
1728 ncd->setTagLessReference(icd);
1729
1730 // associate the variable of the anonymous type with the type member
1731 MemberList *pml = tagParent->getMemberList(varFilter);
1732 if (pml)
1733 {
1734 for (const auto &pmd : *pml)
1735 {
1737 if (pmdm && pmd->name()==md->name())
1738 {
1739 pmdm->setClassDefOfAnonymousType(ncd);
1740 }
1741 }
1742 }
1743 }
1744 }
1745 else
1746 {
1747 //printf(" no match for %s in %s\n",qPrint(icd->name()),qPrint(type));
1748 }
1749 }
1750 }
1751 }
1752 }
1753 // associate the variable of the anonymous enum type with the type member
1754 ml = cd->getMemberList(typeFilter);
1755 if (ml)
1756 {
1757 for (const auto &md : *ml)
1758 {
1759 MemberListType mlFilter = cd->definitionType()==Definition::TypeClass ? MemberListType::PubAttribs() : MemberListType::DecVarMembers();
1760 associateVariableWithAnonymousEnumType(md,cd,md,mlFilter);
1761 }
1762 }
1763 }
1764}
1765
1766template<typename Container>
1767static void findTagLessClasses(std::set<const Definition *> &candidates,const Container *cd)
1768{
1769 for (const auto &icd : cd->getClasses())
1770 {
1771 if (icd->name().find('@')==DString::npos) // process all non-anonymous inner classes
1772 {
1773 findTagLessClasses(candidates,icd);
1774 }
1775 }
1776
1777 candidates.insert(cd);
1778}
1779
1781{
1782 std::set<const Definition *> candidates;
1783 for (auto &cd : *Doxygen::classLinkedMap)
1784 {
1785 Definition *scope = cd->getOuterScope();
1786 //printf(" scope=%s for class %s\n",qPrint(scope?scope->name():"<null>"),qPrint(cd->name()));
1787 if (scope && scope->definitionType()==Definition::TypeNamespace) // class that is not nested
1788 {
1789 const NamespaceDef *nd = toNamespaceDef(scope);
1790 if (nd && nd==Doxygen::globalScope) // class at global namespace
1791 {
1792 const FileDef *fd = cd->getFileDef();
1793 if (fd)
1794 {
1795 findTagLessClasses(candidates,fd);
1796 }
1797 }
1798 else if (nd) // class in a namespace
1799 {
1800 findTagLessClasses(candidates,nd);
1801 }
1802 }
1803 }
1804
1805 // since processTagLessClasses is potentially adding classes to Doxygen::classLinkedMap
1806 // we need to call it outside of the loop above, otherwise the iterator gets invalidated!
1807 for (const auto &d : candidates)
1808 {
1809 //printf("------ processing tag-less classes for %s\n",qPrint(d->name()));
1810 if (d->definitionType()==Definition::TypeNamespace)
1811 {
1812 const NamespaceDef *nd = toNamespaceDef(d);
1813 processTagLessClasses(nd,nd,nd,MemberListType::DecVarMembers(),MemberListType::DecEnumMembers(),"",0);
1814 }
1815 else if (d->definitionType()==Definition::TypeFile)
1816 {
1817 const FileDef *fd = toFileDef(d);
1818 processTagLessClasses(fd,fd,fd,MemberListType::DecVarMembers(),MemberListType::DecEnumMembers(),"",0);
1819 }
1820 else if (d->definitionType()==Definition::TypeClass)
1821 {
1822 const ClassDef *cd = toClassDef(d);
1823 processTagLessClasses(cd,cd,cd,MemberListType::PubAttribs(),MemberListType::PubTypes(),"",0);
1824 }
1825 }
1826}
1827
1828
1829//----------------------------------------------------------------------
1830// build a list of all namespaces mentioned in the documentation
1831// and all namespaces that have a documentation block before their definition.
1832static void buildNamespaceList(const Entry *root)
1833{
1834 if (
1835 (root->section.isNamespace() ||
1836 root->section.isNamespaceDoc() ||
1837 root->section.isPackageDoc()
1838 ) &&
1839 !root->name.empty()
1840 )
1841 {
1842 AUTO_TRACE("name={}",root->name);
1843
1844 DString fName = root->name;
1845 if (root->section.isPackageDoc())
1846 {
1847 fName=substitute(fName,".","::");
1848 }
1849
1850 DString fullName = stripAnonymousNamespaceScope(fName);
1851 if (!fullName.empty())
1852 {
1853 AUTO_TRACE_ADD("Found namespace {} in {} at line {}",root->name,root->fileName,root->startLine);
1855 if (ndi) // existing namespace
1856 {
1858 if (nd) // non-inline namespace
1859 {
1860 AUTO_TRACE_ADD("Existing namespace");
1861 nd->setDocumentation(root->doc,root->docFile,root->docLine);
1862 nd->setName(fullName); // change name to match docs
1864 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1865 if (nd->getLanguage()==SrcLangExt::Unknown)
1866 {
1867 nd->setLanguage(root->lang);
1868 }
1869 if (root->tagInfo()==nullptr && nd->isReference() && !(root->doc.empty() && root->brief.empty()))
1870 // if we previously found namespace nd in a tag file and now we find a
1871 // documented namespace with the same name in the project, then remove
1872 // the tag file reference
1873 {
1874 nd->setReference("");
1875 nd->setFileName(fullName);
1876 }
1877 nd->setMetaData(root->metaData);
1878
1879 // file definition containing the namespace nd
1880 FileDef *fd=root->fileDef();
1881 if (nd->isArtificial())
1882 {
1883 nd->setArtificial(false); // found namespace explicitly, so cannot be artificial
1884 nd->setDefFile(root->fileName,root->startLine,root->startColumn);
1885 }
1886 // insert the namespace in the file definition
1887 if (fd) fd->insertNamespace(nd);
1888 addNamespaceToGroups(root,nd);
1889 nd->setRefItems(root->sli);
1890 nd->setRequirementReferences(root->rqli);
1891 }
1892 }
1893 else // fresh namespace
1894 {
1895 DString tagName;
1896 DString tagFileName;
1897 const TagInfo *tagInfo = root->tagInfo();
1898 if (tagInfo)
1899 {
1900 tagName = tagInfo->tagName;
1901 tagFileName = tagInfo->fileName;
1902 }
1903 AUTO_TRACE_ADD("new namespace {} lang={} tagName={}",fullName,root->lang,tagName);
1904 // add namespace to the list
1906 Doxygen::namespaceLinkedMap->add(fullName,
1907 createNamespaceDef(tagInfo?tagName:root->fileName,root->startLine,
1908 root->startColumn,fullName,tagName,tagFileName,
1909 root->type,root->spec.isPublished())));
1910 if (nd)
1911 {
1912 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1913 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1915 nd->setHidden(root->hidden);
1916 nd->setArtificial(root->artificial);
1917 nd->setLanguage(root->lang);
1918 nd->setId(root->id);
1919 nd->setMetaData(root->metaData);
1920 nd->setInline(root->spec.isInline());
1921 nd->setExported(root->exported);
1922
1923 addNamespaceToGroups(root,nd);
1924 nd->setRefItems(root->sli);
1925 nd->setRequirementReferences(root->rqli);
1926
1927 // file definition containing the namespace nd
1928 FileDef *fd=root->fileDef();
1929 // insert the namespace in the file definition
1930 if (fd) fd->insertNamespace(nd);
1931
1932 // the empty string test is needed for extract all case
1933 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1934 nd->insertUsedFile(fd);
1935 nd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1936 nd->setBodyDef(fd);
1937
1938 // also add namespace to the correct structural context
1939 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,fullName,nullptr,tagInfo);
1940 AUTO_TRACE_ADD("adding namespace {} to context {}",nd->name(),d ? d->name() : DString("<none>"));
1941 if (d==nullptr) // we didn't find anything, create the scope artificially
1942 // anyway, so we can at least relate scopes properly.
1943 {
1944 d = buildScopeFromQualifiedName(fullName,nd->getLanguage(),tagInfo);
1946 if (dm)
1947 {
1948 dm->addInnerCompound(nd);
1949 }
1950 nd->setOuterScope(d);
1951 // TODO: Due to the order in which the tag file is written
1952 // a nested class can be found before its parent!
1953 }
1954 else
1955 {
1957 if (dm)
1958 {
1959 dm->addInnerCompound(nd);
1960 }
1961 nd->setOuterScope(d);
1962 // in case of d is an inline namespace, alias insert nd in the part scope of d.
1964 {
1965 NamespaceDef *pnd = toNamespaceDef(d);
1966 if (pnd && pnd->isInline())
1967 {
1968 d = d->getOuterScope();
1969 if (d)
1970 {
1971 dm = toDefinitionMutable(d);
1972 if (dm)
1973 {
1974 auto aliasNd = createNamespaceDefAlias(d,nd);
1975 dm->addInnerCompound(aliasNd.get());
1976 DString aliasName = aliasNd->name();
1977 AUTO_TRACE_ADD("adding alias {} to {}",aliasName,d->name());
1978 Doxygen::namespaceLinkedMap->add(aliasName,std::move(aliasNd));
1979 }
1980 }
1981 else
1982 {
1983 break;
1984 }
1985 }
1986 else
1987 {
1988 break;
1989 }
1990 }
1991 }
1992 }
1993 }
1994 }
1995 }
1996 for (const auto &e : root->children()) buildNamespaceList(e.get());
1997}
1998
1999//----------------------------------------------------------------------
2000
2002 const DString &name)
2003{
2004 NamespaceDef *usingNd =nullptr;
2005 for (auto &und : unl)
2006 {
2007 DString uScope=und->name()+"::";
2008 usingNd = getResolvedNamespace(uScope+name);
2009 if (usingNd!=nullptr) break;
2010 }
2011 return usingNd;
2012}
2013
2014static void findUsingDirectives(const Entry *root)
2015{
2016 if (root->section.isUsingDir())
2017 {
2018 AUTO_TRACE("Found using directive {} at line {} of {}",root->name,root->startLine,root->fileName);
2019 DString name=substitute(root->name,".","::");
2020 if (name.endsWith("::"))
2021 {
2022 name=name.left(name.length()-2);
2023 }
2024 if (!name.empty())
2025 {
2026 NamespaceDef *usingNd = nullptr;
2027 NamespaceDefMutable *nd = nullptr;
2028 FileDef *fd = root->fileDef();
2029 DString nsName;
2030
2031 // see if the using statement was found inside a namespace or inside
2032 // the global file scope.
2033 if (root->parent() && root->parent()->section.isNamespace() &&
2034 (fd==nullptr || fd->getLanguage()!=SrcLangExt::Java) // not a .java file
2035 )
2036 {
2037 nsName=stripAnonymousNamespaceScope(root->parent()->name);
2038 if (!nsName.empty())
2039 {
2040 nd = getResolvedNamespaceMutable(nsName);
2041 }
2042 }
2043
2044 // find the scope in which the 'using' namespace is defined by prepending
2045 // the possible scopes in which the using statement was found, starting
2046 // with the most inner scope and going to the most outer scope (i.e.
2047 // file scope).
2048 int scopeOffset = static_cast<int>(nsName.length());
2049 do
2050 {
2051 DString scope=scopeOffset>0 ?
2052 nsName.left(scopeOffset)+"::" : DString();
2053 usingNd = getResolvedNamespace(scope+name);
2054 //printf("Trying with scope='%s' usingNd=%p\n",(scope+qPrint(name)),usingNd);
2055 if (scopeOffset==0)
2056 {
2057 scopeOffset=-1;
2058 }
2059 else
2060 {
2061 size_t o = nsName.rfind("::",scopeOffset-1);
2062 scopeOffset = o!=DString::npos ? static_cast<int>(o) : 0;
2063 }
2064 } while (scopeOffset>=0 && usingNd==nullptr);
2065
2066 if (usingNd==nullptr && nd) // not found, try used namespaces in this scope
2067 // or in one of the parent namespace scopes
2068 {
2069 const NamespaceDefMutable *pnd = nd;
2070 while (pnd && usingNd==nullptr)
2071 {
2072 // also try with one of the used namespaces found earlier
2074
2075 // goto the parent
2076 Definition *s = pnd->getOuterScope();
2078 {
2080 }
2081 else
2082 {
2083 pnd = nullptr;
2084 }
2085 }
2086 }
2087 if (usingNd==nullptr && fd) // still nothing, also try used namespace in the
2088 // global scope
2089 {
2090 usingNd = findUsedNamespace(fd->getUsedNamespaces(),name);
2091 }
2092
2093 //printf("%s -> %s\n",qPrint(name),usingNd?qPrint(usingNd->name()):"<none>");
2094
2095 // add the namespace the correct scope
2096 if (usingNd)
2097 {
2098 //printf("using fd=%p nd=%p\n",fd,nd);
2099 if (nd)
2100 {
2101 //printf("Inside namespace %s\n",qPrint(nd->name()));
2102 nd->addUsingDirective(usingNd);
2103 }
2104 else if (fd)
2105 {
2106 //printf("Inside file %s\n",qPrint(fd->name()));
2107 fd->addUsingDirective(usingNd);
2108 }
2109 }
2110 else // unknown namespace, but add it anyway.
2111 {
2112 AUTO_TRACE_ADD("new unknown namespace {} lang={} hidden={}",name,root->lang,root->hidden);
2113 // add namespace to the list
2116 createNamespaceDef(root->fileName,root->startLine,root->startColumn,name)));
2117 if (nd)
2118 {
2119 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
2120 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2122 nd->setHidden(root->hidden);
2123 nd->setArtificial(true);
2124 nd->setLanguage(root->lang);
2125 nd->setId(root->id);
2126 nd->setMetaData(root->metaData);
2127 nd->setInline(root->spec.isInline());
2128 nd->setExported(root->exported);
2129
2130 for (const Grouping &g : root->groups)
2131 {
2132 GroupDef *gd=nullptr;
2134 gd->addNamespace(nd);
2135 }
2136
2137 // insert the namespace in the file definition
2138 if (fd)
2139 {
2140 fd->insertNamespace(nd);
2141 fd->addUsingDirective(nd);
2142 }
2143
2144 // the empty string test is needed for extract all case
2145 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2146 nd->insertUsedFile(fd);
2147 nd->setRefItems(root->sli);
2148 nd->setRequirementReferences(root->rqli);
2149 }
2150 }
2151 }
2152 }
2153 for (const auto &e : root->children()) findUsingDirectives(e.get());
2154}
2155
2156//----------------------------------------------------------------------
2157
2158static void buildListOfUsingDecls(const Entry *root)
2159{
2160 if (root->section.isUsingDecl() &&
2161 !root->parent()->section.isCompound() // not a class/struct member
2162 )
2163 {
2164 DString name = substitute(root->name,".","::");
2165 g_usingDeclarations.insert(name.str());
2166 }
2167 for (const auto &e : root->children()) buildListOfUsingDecls(e.get());
2168}
2169
2170
2171static void findUsingDeclarations(const Entry *root,bool filterPythonPackages)
2172{
2173 if (root->section.isUsingDecl() &&
2174 !root->parent()->section.isCompound() && // not a class/struct member
2175 (!filterPythonPackages || (root->lang==SrcLangExt::Python && root->fileName.endsWith("__init__.py")))
2176 )
2177 {
2178 AUTO_TRACE("Found using declaration '{}' at line {} of {} inside section {}",
2179 root->name,root->startLine,root->fileName,root->parent()->section);
2180 if (!root->name.empty())
2181 {
2182 const Definition *usingDef = nullptr;
2183 NamespaceDefMutable *nd = nullptr;
2184 FileDef *fd = root->fileDef();
2185 DString scName;
2186
2187 // see if the using statement was found inside a namespace or inside
2188 // the global file scope.
2189 if (root->parent()->section.isNamespace())
2190 {
2191 scName=root->parent()->name;
2192 if (!scName.empty())
2193 {
2194 nd = getResolvedNamespaceMutable(scName);
2195 }
2196 }
2197
2198 // Assume the using statement was used to import a class.
2199 // Find the scope in which the 'using' namespace is defined by prepending
2200 // the possible scopes in which the using statement was found, starting
2201 // with the most inner scope and going to the most outer scope (i.e.
2202 // file scope).
2203
2204 DString name = substitute(root->name,".","::"); //Java/C# scope->internal
2205
2206 SymbolResolver resolver;
2207 const Definition *scope = nd;
2208 if (nd==nullptr) scope = fd;
2209 usingDef = resolver.resolveSymbol(scope,name);
2210
2211 //printf("usingDef(scope=%s,name=%s)=%s\n",qPrint(nd?nd->qualifiedName():""),qPrint(name),usingDef?qPrint(usingDef->qualifiedName()):"nullptr");
2212
2213 if (!usingDef)
2214 {
2215 usingDef = getClass(name); // try direct lookup, this is needed to get
2216 // builtin STL classes to properly resolve, e.g.
2217 // vector -> std::vector
2218 }
2219 if (!usingDef)
2220 {
2221 usingDef = Doxygen::hiddenClassLinkedMap->find(name); // check if it is already hidden
2222 }
2223#if 0
2224 if (!usingDef)
2225 {
2226 AUTO_TRACE_ADD("New using class '{}' (sec={})! #tArgLists={}",
2227 name,root->section,root->tArgLists.size());
2230 createClassDef( "<using>",1,1, name, ClassDef::Class)));
2231 if (usingCd)
2232 {
2233 usingCd->setArtificial(true);
2234 usingCd->setLanguage(root->lang);
2235 usingDef = usingCd;
2236 }
2237 }
2238#endif
2239 else
2240 {
2241 AUTO_TRACE_ADD("Found used type '{}' in scope='{}'",
2242 usingDef->name(), nd ? nd->name(): fd ? fd->name() : DString("<unknown>"));
2243 }
2244
2245 if (usingDef)
2246 {
2247 if (nd)
2248 {
2249 nd->addUsingDeclaration(usingDef);
2250 }
2251 else if (fd)
2252 {
2253 fd->addUsingDeclaration(usingDef);
2254 }
2255 }
2256 }
2257 }
2258 for (const auto &e : root->children()) findUsingDeclarations(e.get(),filterPythonPackages);
2259}
2260
2261//----------------------------------------------------------------------
2262
2264{
2265 root->commandOverrides.apply_callGraph ([&](bool b) { md->overrideCallGraph(b); });
2266 root->commandOverrides.apply_callerGraph ([&](bool b) { md->overrideCallerGraph(b); });
2267 root->commandOverrides.apply_referencedByRelation([&](bool b) { md->overrideReferencedByRelation(b); });
2268 root->commandOverrides.apply_referencesRelation ([&](bool b) { md->overrideReferencesRelation(b); });
2269 root->commandOverrides.apply_inlineSource ([&](bool b) { md->overrideInlineSource(b); });
2270 root->commandOverrides.apply_enumValues ([&](bool b) { md->overrideEnumValues(b); });
2271}
2272
2273//----------------------------------------------------------------------
2274
2276 const DString &fileName,const DString &memName)
2277{
2278 AUTO_TRACE("creating new member {} for class {}",memName,cd->name());
2279 const ArgumentList &templAl = md->templateArguments();
2280 const ArgumentList &al = md->argumentList();
2281 auto newMd = createMemberDef(
2282 fileName,root->startLine,root->startColumn,
2283 md->typeString(),memName,md->argsString(),
2284 md->excpString(),root->protection,root->virt,
2285 md->isStatic(),Relationship::Member,md->memberType(),
2286 templAl,al,root->metaData
2287 );
2288 auto newMmd = toMemberDefMutable(newMd.get());
2289 newMmd->setMemberClass(cd);
2290 cd->insertMember(newMd.get());
2291 if (!root->doc.empty() || !root->brief.empty())
2292 {
2293 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2294 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2295 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2296 }
2297 else
2298 {
2299 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2300 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2301 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2302 }
2303 newMmd->setDefinition(md->definition());
2304 applyMemberOverrideOptions(root,newMmd);
2305 newMmd->addQualifiers(root->qualifiers);
2306 newMmd->setBitfields(md->bitfieldString());
2307 newMmd->addSectionsToDefinition(root->anchors);
2308 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2309 newMmd->setBodyDef(md->getBodyDef());
2310 newMmd->setInitializer(md->initializer());
2311 newMmd->setRequiresClause(md->requiresClause());
2312 newMmd->setMaxInitLines(md->initializerLines());
2313 newMmd->setMemberGroupId(root->mGrpId);
2314 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2315 newMmd->setVhdlSpecifiers(md->getVhdlSpecifiers());
2316 newMmd->setLanguage(root->lang);
2317 newMmd->setId(root->id);
2319 mn->push_back(std::move(newMd));
2320}
2321
2322static std::unordered_map<std::string,std::vector<ClassDefMutable*>> g_usingClassMap;
2323
2324static void findUsingDeclImports(const Entry *root)
2325{
2326 if (root->section.isUsingDecl() &&
2327 root->parent()->section.isCompound() // in a class/struct member
2328 )
2329 {
2330 AUTO_TRACE("Found using declaration '{}' inside section {}", root->name, root->parent()->section);
2331 DString fullName=removeRedundantWhiteSpace(root->parent()->name);
2332 fullName=stripAnonymousNamespaceScope(fullName);
2333 fullName=stripTemplateSpecifiersFromScope(fullName);
2334 ClassDefMutable *cd = getClassMutable(fullName);
2335 if (cd)
2336 {
2337 AUTO_TRACE_ADD("found class '{}'",cd->name());
2338 size_t i=root->name.rfind("::");
2339 if (i!=DString::npos)
2340 {
2341 DString scope=root->name.left(i);
2342 DString memName=root->name.mid(i+2);
2343 SymbolResolver resolver;
2344 const ClassDef *bcd = resolver.resolveClass(cd,scope); // todo: file in fileScope parameter
2345 AUTO_TRACE_ADD("name={} scope={} bcd={}",scope,cd?cd->name():"<none>",bcd?bcd->name():"<none>");
2346 if (bcd && bcd!=cd)
2347 {
2348 AUTO_TRACE_ADD("found class '{}' memName='{}'",bcd->name(),memName);
2350 const MemberNameInfo *mni = mnlm.find(memName);
2351 if (mni)
2352 {
2353 for (auto &mi : *mni)
2354 {
2355 const MemberDef *md = mi->memberDef();
2356 if (md && md->protection()!=Protection::Private)
2357 {
2358 AUTO_TRACE_ADD("found member '{}'",mni->memberName());
2359 DString fileName = root->fileName;
2360 if (fileName.empty() && root->tagInfo())
2361 {
2362 fileName = root->tagInfo()->tagName;
2363 }
2364 if (!cd->containsOverload(md))
2365 {
2366 createUsingMemberImportForClass(root,cd,md,fileName,memName);
2367 // also insert the member into copies of the class
2368 auto it = g_usingClassMap.find(cd->qualifiedName().str());
2369 if (it != g_usingClassMap.end())
2370 {
2371 for (const auto &copyCd : it->second)
2372 {
2373 createUsingMemberImportForClass(root,copyCd,md,fileName,memName);
2374 }
2375 }
2376 }
2377 }
2378 }
2379 }
2380 }
2381 }
2382 }
2383 }
2384 else if (root->section.isUsingDecl() &&
2385 (root->parent()->section.isNamespace() || root->parent()->section.isEmpty()) && // namespace or global member
2386 root->lang==SrcLangExt::Cpp // do we also want this for e.g. Fortran? (see test case 095)
2387 )
2388 {
2389 AUTO_TRACE("Found using declaration '{}' inside section {}", root->name, root->parent()->section);
2390 Definition *scope = nullptr;
2391 NamespaceDefMutable *nd = nullptr;
2392 FileDef *fd = root->parent()->fileDef();
2393 if (!root->parent()->name.empty())
2394 {
2395 DString fullName=removeRedundantWhiteSpace(root->parent()->name);
2396 fullName=stripAnonymousNamespaceScope(fullName);
2398 scope = nd;
2399 }
2400 else
2401 {
2402 scope = fd;
2403 }
2404 if (scope)
2405 {
2406 AUTO_TRACE_ADD("found scope '{}'",scope->name());
2407 SymbolResolver resolver;
2408 const Definition *def = resolver.resolveSymbol(root->name.startsWith("::") ? nullptr : scope,root->name);
2409 if (def && def->definitionType()==Definition::TypeMember)
2410 {
2411 size_t i=root->name.rfind("::");
2412 DString memName;
2413 if (i!=DString::npos)
2414 {
2415 memName = root->name.right(root->name.length()-i-2);
2416 }
2417 else
2418 {
2419 memName = root->name;
2420 }
2421 const MemberDef *md = toMemberDef(def);
2422 AUTO_TRACE_ADD("found member '{}' for name '{}'",md->qualifiedName(),root->name);
2423 DString fileName = root->fileName;
2424 if (fileName.empty() && root->tagInfo())
2425 {
2426 fileName = root->tagInfo()->tagName;
2427 }
2428 const ArgumentList &templAl = md->templateArguments();
2429 const ArgumentList &al = md->argumentList();
2430
2431 auto newMd = createMemberDef(
2432 fileName,root->startLine,root->startColumn,
2433 md->typeString(),memName,md->argsString(),
2434 md->excpString(),root->protection,root->virt,
2435 md->isStatic(),Relationship::Member,md->memberType(),
2436 templAl,al,root->metaData
2437 );
2438 auto newMmd = toMemberDefMutable(newMd.get());
2439 if (nd)
2440 {
2441 newMmd->setNamespace(nd);
2442 nd->insertMember(newMd.get());
2443 }
2444 if (fd)
2445 {
2446 newMmd->setFileDef(fd);
2447 fd->insertMember(newMd.get());
2448 }
2449 if (!root->doc.empty() || !root->brief.empty())
2450 {
2451 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2452 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2453 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2454 }
2455 else
2456 {
2457 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2458 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2459 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2460 }
2461 newMmd->setDefinition(md->definition());
2462 applyMemberOverrideOptions(root,newMmd);
2463 newMmd->addQualifiers(root->qualifiers);
2464 newMmd->setBitfields(md->bitfieldString());
2465 newMmd->addSectionsToDefinition(root->anchors);
2466 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2467 newMmd->setBodyDef(md->getBodyDef());
2468 newMmd->setInitializer(md->initializer());
2469 newMmd->setRequiresClause(md->requiresClause());
2470 newMmd->setMaxInitLines(md->initializerLines());
2471 newMmd->setMemberGroupId(root->mGrpId);
2472 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2473 newMmd->setVhdlSpecifiers(md->getVhdlSpecifiers());
2474 newMmd->setLanguage(root->lang);
2475 newMmd->setId(root->id);
2477 mn->push_back(std::move(newMd));
2478#if 0 // insert an alias instead of a copy
2479 const MemberDef *md = toMemberDef(def);
2480 AUTO_TRACE_ADD("found member '{}' for name '{}'",md->qualifiedName(),root->name);
2481 auto aliasMd = createMemberDefAlias(nd,md);
2482 DString aliasFullName = nd->qualifiedName()+"::"+aliasMd->localName();
2483 if (nd && aliasMd.get())
2484 {
2485 nd->insertMember(aliasMd.get());
2486 }
2487 if (fd && aliasMd.get())
2488 {
2489 fd->insertMember(aliasMd.get());
2490 }
2491 MemberName *mn = Doxygen::memberNameLinkedMap->add(aliasFullName);
2492 mn->push_back(std::move(aliasMd));
2493#endif
2494 }
2495 else if (def && def->definitionType()==Definition::TypeClass)
2496 {
2497 const ClassDef *cd = toClassDef(def);
2498 DString copyFullName;
2499 if (nd==nullptr)
2500 {
2501 copyFullName = cd->localName();
2502 }
2503 else
2504 {
2505 copyFullName = nd->qualifiedName()+"::"+cd->localName();
2506 }
2507 if (Doxygen::classLinkedMap->find(copyFullName)==nullptr)
2508 {
2510 Doxygen::classLinkedMap->add(copyFullName,
2511 cd->deepCopy(copyFullName)));
2512 AUTO_TRACE_ADD("found class '{}' for name '{}' copy '{}' obj={}",cd->qualifiedName(),root->name,copyFullName,(void*)ncdm);
2513 g_usingClassMap[cd->qualifiedName().str()].push_back(ncdm);
2514 if (ncdm)
2515 {
2516 if (nd) ncdm->moveTo(nd);
2517 if ((!root->doc.empty() || !root->brief.empty())) // use docs at using statement
2518 {
2519 ncdm->setDocumentation(root->doc,root->docFile,root->docLine);
2520 ncdm->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2521 }
2522 else // use docs from used class
2523 {
2524 ncdm->setDocumentation(cd->documentation(),cd->docFile(),cd->docLine());
2526 }
2527 if (nd)
2528 {
2529 nd->addInnerCompound(ncdm);
2530 nd->addUsingDeclaration(ncdm);
2531 }
2532 if (fd)
2533 {
2534 if (ncdm) ncdm->setFileDef(fd);
2535 fd->insertClass(ncdm);
2536 fd->addUsingDeclaration(ncdm);
2537 }
2538 }
2539 }
2540#if 0 // insert an alias instead of a copy
2541 auto aliasCd = createClassDefAlias(nd,cd);
2542 DString aliasFullName;
2543 if (nd==nullptr)
2544 {
2545 aliasFullName = aliasCd->localName();
2546 }
2547 else
2548 {
2549 aliasFullName = nd->qualifiedName()+"::"+aliasCd->localName();
2550 }
2551 AUTO_TRACE_ADD("found class '{}' for name '{}' aliasFullName='{}'",cd->qualifiedName(),root->name,aliasFullName);
2552 auto acd = Doxygen::classLinkedMap->add(aliasFullName,std::move(aliasCd));
2553 if (nd && acd)
2554 {
2555 nd->addInnerCompound(acd);
2556 }
2557 if (fd && acd)
2558 {
2559 fd->insertClass(acd);
2560 }
2561#endif
2562 }
2563 else if (scope)
2564 {
2565 AUTO_TRACE_ADD("no symbol with name '{}' in scope {}",root->name,scope->name());
2566 }
2567 }
2568 }
2569 for (const auto &e : root->children()) findUsingDeclImports(e.get());
2570}
2571
2572//----------------------------------------------------------------------
2573
2575{
2576 FileDefSet visitedFiles;
2577 // then recursively add using directives found in #include files
2578 // to files that have not been visited.
2579 for (const auto &fn : *Doxygen::inputNameLinkedMap)
2580 {
2581 for (const auto &fd : *fn)
2582 {
2583 //printf("----- adding using directives for file %s\n",qPrint(fd->name()));
2584 fd->addIncludedUsingDirectives(visitedFiles);
2585 }
2586 }
2587}
2588
2589//----------------------------------------------------------------------
2590
2592 const Entry *root,
2593 ClassDefMutable *cd,
2594 MemberType mtype,
2595 const DString &type,
2596 const DString &name,
2597 const DString &args,
2598 Protection prot,
2599 Relationship related)
2600{
2602 DString scopeSeparator="::";
2603 SrcLangExt lang = cd->getLanguage();
2604 if (lang==SrcLangExt::Java || lang==SrcLangExt::CSharp)
2605 {
2606 qualScope = substitute(qualScope,"::",".");
2607 scopeSeparator=".";
2608 }
2609 AUTO_TRACE("class variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} init='{}'",
2610 root->fileName, type, qualScope, name, args, root->protection, mtype, lang, root->initializer.str());
2611
2612 DString def;
2613 if (!type.empty())
2614 {
2615 if (related!=Relationship::Member || mtype==MemberType::Friend || Config_getBool(HIDE_SCOPE_NAMES))
2616 {
2617 if (root->spec.isAlias()) // turn 'typedef B A' into 'using A'
2618 {
2619 if (lang==SrcLangExt::Python)
2620 {
2621 def="type "+name+args;
2622 }
2623 else
2624 {
2625 def="using "+name;
2626 }
2627 }
2628 else
2629 {
2630 def=type+" "+name+args;
2631 }
2632 }
2633 else
2634 {
2635 if (root->spec.isAlias()) // turn 'typedef B C::A' into 'using C::A'
2636 {
2637 if (lang==SrcLangExt::Python)
2638 {
2639 def="type "+qualScope+scopeSeparator+name+args;
2640 }
2641 else
2642 {
2643 def="using "+qualScope+scopeSeparator+name;
2644 }
2645 }
2646 else
2647 {
2648 def=type+" "+qualScope+scopeSeparator+name+args;
2649 }
2650 }
2651 }
2652 else
2653 {
2654 if (Config_getBool(HIDE_SCOPE_NAMES))
2655 {
2656 def=name+args;
2657 }
2658 else
2659 {
2660 def=qualScope+scopeSeparator+name+args;
2661 }
2662 }
2663 def.stripPrefix("static ");
2664
2665 // see if the member is already found in the same scope
2666 // (this may be the case for a static member that is initialized
2667 // outside the class)
2669 if (mn)
2670 {
2671 for (const auto &imd : *mn)
2672 {
2673 //printf("md->getClassDef()=%p cd=%p type=[%s] md->typeString()=[%s]\n",
2674 // md->getClassDef(),cd,qPrint(type),md->typeString());
2675 MemberDefMutable *md = toMemberDefMutable(imd.get());
2676 if (md &&
2677 md->getClassDef()==cd &&
2678 ((lang==SrcLangExt::Python && type.empty() && !md->typeString().empty()) ||
2680 // member already in the scope
2681 {
2682
2683 if (root->lang==SrcLangExt::ObjC &&
2684 root->mtype==MethodTypes::Property &&
2685 md->memberType()==MemberType::Variable)
2686 { // Objective-C 2.0 property
2687 // turn variable into a property
2688 md->setProtection(root->protection);
2689 cd->reclassifyMember(md,MemberType::Property);
2690 }
2691 addMemberDocs(root,md,def,nullptr,false,root->spec);
2692 AUTO_TRACE_ADD("Member already found!");
2693 return md;
2694 }
2695 }
2696 }
2697
2698 DString fileName = root->fileName;
2699 if (fileName.empty() && root->tagInfo())
2700 {
2701 fileName = root->tagInfo()->tagName;
2702 }
2703
2704 // new member variable, typedef or enum value
2705 auto md = createMemberDef(
2706 fileName,root->startLine,root->startColumn,
2707 type,name,args,root->exception,
2708 prot,Specifier::Normal,root->isStatic,related,
2709 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2710 ArgumentList(), root->metaData);
2711 auto mmd = toMemberDefMutable(md.get());
2712 mmd->setTagInfo(root->tagInfo());
2713 mmd->setMemberClass(cd); // also sets outer scope (i.e. getOuterScope())
2714 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2715 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2716 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2717 mmd->setDefinition(def);
2718 mmd->setBitfields(root->bitfields);
2719 mmd->addSectionsToDefinition(root->anchors);
2720 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2721 mmd->setInitializer(root->initializer.str());
2722 mmd->setMaxInitLines(root->initLines);
2723 mmd->setMemberGroupId(root->mGrpId);
2724 mmd->setMemberSpecifiers(root->spec);
2725 mmd->setVhdlSpecifiers(root->vhdlSpec);
2726 mmd->setReadAccessor(root->read);
2727 mmd->setWriteAccessor(root->write);
2729 mmd->setHidden(root->hidden);
2730 mmd->setArtificial(root->artificial);
2731 mmd->setLanguage(root->lang);
2732 mmd->setId(root->id);
2733 addMemberToGroups(root,md.get());
2735 mmd->setBodyDef(root->fileDef());
2736 mmd->addQualifiers(root->qualifiers);
2737
2738 AUTO_TRACE_ADD("Adding new member '{}' to class '{}'",name,cd->name());
2739 cd->insertMember(md.get());
2740 mmd->setRefItems(root->sli);
2741 mmd->setRequirementReferences(root->rqli);
2742
2743 cd->insertUsedFile(root->fileDef());
2744 root->markAsProcessed();
2745
2746 if (mtype==MemberType::Typedef)
2747 {
2748 resolveTemplateInstanceInType(root,cd,md.get());
2749 }
2750
2751 // add the member to the global list
2752 MemberDef *result = md.get();
2754 mn->push_back(std::move(md));
2755
2756 return result;
2757}
2758
2759//----------------------------------------------------------------------
2760
2762 const Entry *root,
2763 MemberType mtype,
2764 const DString &scope,
2765 const DString &type,
2766 const DString &name,
2767 const DString &args)
2768{
2769 AUTO_TRACE("global variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} init='{}'",
2770 root->fileName, type, scope, name, args, root->protection, mtype, root->lang, root->initializer.str());
2771
2772 FileDef *fd = root->fileDef();
2773
2774 // see if we have a typedef that should hide a struct or union
2775 if (mtype==MemberType::Typedef && Config_getBool(TYPEDEF_HIDES_STRUCT))
2776 {
2777 DString ttype = type;
2778 ttype.stripPrefix("typedef ");
2779 if (ttype.stripPrefix("struct ") || ttype.stripPrefix("union "))
2780 {
2781 static const reg::Ex re(R"(\a\w*)");
2782 reg::Match match;
2783 const std::string &typ = ttype.str();
2784 if (reg::search(typ,match,re))
2785 {
2786 DString typeValue = match.str();
2787 ClassDefMutable *cd = getClassMutable(typeValue);
2788 if (cd)
2789 {
2790 // this typedef should hide compound name cd, so we
2791 // change the name that is displayed from cd.
2792 cd->setClassName(name);
2793 cd->setDocumentation(root->doc,root->docFile,root->docLine);
2794 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2795 return nullptr;
2796 }
2797 }
2798 }
2799 }
2800
2801 // see if the function is inside a namespace
2802 NamespaceDefMutable *nd = nullptr;
2803 if (!scope.empty())
2804 {
2805 if (scope.find('@')!=DString::npos) return nullptr; // anonymous scope!
2806 nd = getResolvedNamespaceMutable(scope);
2807 }
2808 DString def;
2809
2810 // determine the definition of the global variable
2811 if (nd && !nd->isAnonymous() &&
2812 !Config_getBool(HIDE_SCOPE_NAMES)
2813 )
2814 // variable is inside a namespace, so put the scope before the name
2815 {
2816 SrcLangExt lang = nd->getLanguage();
2818
2819 if (!type.empty())
2820 {
2821 if (root->spec.isAlias()) // turn 'typedef B NS::A' into 'using NS::A'
2822 {
2823 if (lang==SrcLangExt::Python)
2824 {
2825 def="type "+nd->name()+sep+name+args;
2826 }
2827 else
2828 {
2829 def="using "+nd->name()+sep+name;
2830 }
2831 }
2832 else // normal member
2833 {
2834 def=type+" "+nd->name()+sep+name+args;
2835 }
2836 }
2837 else
2838 {
2839 def=nd->name()+sep+name+args;
2840 }
2841 }
2842 else
2843 {
2844 if (!type.empty() && !root->name.empty())
2845 {
2846 if (name.at(0)=='@') // dummy variable representing anonymous union
2847 {
2848 def=type;
2849 }
2850 else
2851 {
2852 if (root->spec.isAlias()) // turn 'typedef B A' into 'using A'
2853 {
2854 if (root->lang==SrcLangExt::Python)
2855 {
2856 def="type "+root->name+args;
2857 }
2858 else
2859 {
2860 def="using "+root->name;
2861 }
2862 }
2863 else // normal member
2864 {
2865 def=type+" "+name+args;
2866 }
2867 }
2868 }
2869 else
2870 {
2871 def=name+args;
2872 }
2873 }
2874 def.stripPrefix("static ");
2875
2877 if (mn)
2878 {
2879 //DString nscope=removeAnonymousScopes(scope);
2880 //NamespaceDef *nd=nullptr;
2881 //if (!nscope.empty())
2882 if (!scope.empty())
2883 {
2884 nd = getResolvedNamespaceMutable(scope);
2885 }
2886 for (const auto &imd : *mn)
2887 {
2888 MemberDefMutable *md = toMemberDefMutable(imd.get());
2889 if (md &&
2890 ((nd==nullptr && md->getNamespaceDef()==nullptr && md->getFileDef() &&
2891 root->fileName==md->getFileDef()->absFilePath()
2892 ) // both variable names in the same file
2893 || (nd!=nullptr && md->getNamespaceDef()==nd) // both in same namespace
2894 )
2895 && !md->isDefine() // function style #define's can be "overloaded" by typedefs or variables
2896 && !md->isEnumerate() // in C# an enum value and enum can have the same name
2897 )
2898 // variable already in the scope
2899 {
2900 bool isPHPArray = md->getLanguage()==SrcLangExt::PHP &&
2901 md->argsString()!=args &&
2902 args.find('[')!=DString::npos;
2903 bool staticsInDifferentFiles =
2904 root->isStatic && md->isStatic() &&
2905 root->fileName!=md->getDefFileName();
2906
2907 if (md->getFileDef() &&
2908 !isPHPArray && // not a php array
2909 !staticsInDifferentFiles
2910 )
2911 // not a php array variable
2912 {
2913 AUTO_TRACE_ADD("variable already found: scope='{}'",md->getOuterScope()->name());
2914 addMemberDocs(root,md,def,nullptr,false,root->spec);
2915 md->setRefItems(root->sli);
2916 md->setRequirementReferences(root->rqli);
2917 // if md is a variable forward declaration and root is the definition that
2918 // turn md into the definition
2919 if (!root->explicitExternal && md->isExternal())
2920 {
2921 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
2922 md->setExplicitExternal(false,root->fileName,root->startLine,root->startColumn);
2923 }
2924 // if md is the definition and root point at a declaration, then add the
2925 // declaration info
2926 else if (root->explicitExternal && !md->isExternal())
2927 {
2928 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
2929 }
2930 return md;
2931 }
2932 }
2933 }
2934 }
2935
2936 DString fileName = root->fileName;
2937 if (fileName.empty() && root->tagInfo())
2938 {
2939 fileName = root->tagInfo()->tagName;
2940 }
2941
2942 AUTO_TRACE_ADD("new variable, namespace='{}'",nd?nd->name():DString("<global>"));
2943 // new global variable, enum value or typedef
2944 auto md = createMemberDef(
2945 fileName,root->startLine,root->startColumn,
2946 type,name,args,DString(),
2947 root->protection, Specifier::Normal,root->isStatic,Relationship::Member,
2948 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2949 root->argList, root->metaData);
2950 auto mmd = toMemberDefMutable(md.get());
2951 mmd->setTagInfo(root->tagInfo());
2952 mmd->setMemberSpecifiers(root->spec);
2953 mmd->setVhdlSpecifiers(root->vhdlSpec);
2954 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2955 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2956 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2957 mmd->addSectionsToDefinition(root->anchors);
2958 mmd->setInitializer(root->initializer.str());
2959 mmd->setMaxInitLines(root->initLines);
2960 mmd->setMemberGroupId(root->mGrpId);
2961 mmd->setDefinition(def);
2962 mmd->setLanguage(root->lang);
2963 mmd->setId(root->id);
2965 mmd->setExplicitExternal(root->explicitExternal,fileName,root->startLine,root->startColumn);
2966 mmd->addQualifiers(root->qualifiers);
2967 //md->setOuterScope(fd);
2968 if (!root->explicitExternal)
2969 {
2970 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2971 mmd->setBodyDef(fd);
2972 }
2973 addMemberToGroups(root,md.get());
2975
2976 mmd->setRefItems(root->sli);
2977 mmd->setRequirementReferences(root->rqli);
2978 if (nd && !nd->isAnonymous())
2979 {
2980 mmd->setNamespace(nd);
2981 nd->insertMember(md.get());
2982 }
2983
2984 // add member to the file (we do this even if we have already inserted
2985 // it into the namespace.
2986 if (fd)
2987 {
2988 mmd->setFileDef(fd);
2989 fd->insertMember(md.get());
2990 }
2991
2992 root->markAsProcessed();
2993
2994 if (mtype==MemberType::Typedef)
2995 {
2996 resolveTemplateInstanceInType(root,nd,md.get());
2997 }
2998
2999 // add member definition to the list of globals
3000 MemberDef *result = md.get();
3002 mn->push_back(std::move(md));
3003
3004
3005
3006 return result;
3007}
3008
3009/*! See if the return type string \a type is that of a function pointer
3010 * \returns -1 if this is not a function pointer variable or
3011 * the index at which the closing brace of (...*name) was found.
3012 */
3013static int findFunctionPtr(const std::string &type,SrcLangExt lang, int *pLength=nullptr)
3014{
3015 AUTO_TRACE("type='{}' lang={}",type,lang);
3016 if (lang == SrcLangExt::Fortran || lang == SrcLangExt::VHDL)
3017 {
3018 return -1; // Fortran and VHDL do not have function pointers
3019 }
3020
3021 static const reg::Ex re(R"(\‍([^)]*[*&^][^)]*\))");
3022 reg::Match match;
3023 size_t i=std::string::npos;
3024 size_t l=0;
3025 if (reg::search(type,match,re)) // contains (...*...) or (...&...) or (...^...)
3026 {
3027 i = match.position();
3028 l = match.length();
3029 }
3030 if (i!=std::string::npos)
3031 {
3032 size_t di = type.find("decltype(");
3033 if (di!=std::string::npos && di<i)
3034 {
3035 i = std::string::npos;
3036 }
3037 }
3038 size_t bb=type.find('<');
3039 size_t be=type.rfind('>');
3040 bool templFp = false;
3041 if (be!=std::string::npos) {
3042 size_t cc_ast = type.find("::*");
3043 size_t cc_amp = type.find("::&");
3044 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>::*)'
3045 }
3046
3047 if (!type.empty() && // return type is non-empty
3048 i!=std::string::npos && // contains (...*...)
3049 type.find("operator")==std::string::npos && // not an operator
3050 (type.find(")(")==std::string::npos || type.find("typedef ")!=std::string::npos) &&
3051 // not a function pointer return type
3052 (!(bb<i && i<be) || templFp) // bug665855: avoid treating "typedef A<void (T*)> type" as a function pointer
3053 )
3054 {
3055 if (pLength) *pLength=static_cast<int>(l);
3056 //printf("findFunctionPtr=%d\n",(int)i);
3057 AUTO_TRACE_EXIT("result={}",i);
3058 return static_cast<int>(i);
3059 }
3060 else
3061 {
3062 //printf("findFunctionPtr=%d\n",-1);
3063 AUTO_TRACE_EXIT("result=-1");
3064 return -1;
3065 }
3066}
3067
3068//--------------------------------------------------------------------------------------
3069
3070/*! Returns true iff \a type is a class within scope \a context.
3071 * Used to detect variable declarations that look like function prototypes.
3072 */
3073static bool isVarWithConstructor(const Entry *root)
3074{
3075 bool result = false;
3076 bool typeIsClass = false;
3077 bool typePtrType = false;
3078 DString type;
3079 Definition *ctx = nullptr;
3080 FileDef *fd = root->fileDef();
3081 SymbolResolver resolver(fd);
3082
3083 AUTO_TRACE("isVarWithConstructor({})",root->name);
3084 if (root->parent()->section.isCompound())
3085 { // inside a class
3086 result=false;
3087 AUTO_TRACE_EXIT("inside class: result={}",result);
3088 return result;
3089 }
3090 else if ((fd != nullptr) && (fd->name().endsWith(".c") || fd->name().endsWith(".h")))
3091 { // inside a .c file
3092 result=false;
3093 AUTO_TRACE_EXIT("inside C file: result={}",result);
3094 return result;
3095 }
3096 if (root->type.empty())
3097 {
3098 result=false;
3099 AUTO_TRACE_EXIT("no type: result={}",result);
3100 return result;
3101 }
3102 if (!root->parent()->name.empty())
3103 {
3105 }
3106 type = root->type;
3107 // remove qualifiers
3108 type.findAndRemoveWord("const");
3109 type.findAndRemoveWord("static");
3110 type.findAndRemoveWord("volatile");
3111 typePtrType = type.find('*')!=DString::npos || type.find('&')!=DString::npos;
3112 if (!typePtrType)
3113 {
3114 typeIsClass = resolver.resolveClass(ctx,type)!=nullptr;
3115 if (size_t ti=type.find('<'); !typeIsClass && ti!=DString::npos)
3116 {
3117 typeIsClass=resolver.resolveClass(ctx,type.left(ti))!=nullptr;
3118 }
3119 }
3120 if (typeIsClass) // now we still have to check if the arguments are
3121 // types or values. Since we do not have complete type info
3122 // we need to rely on heuristics :-(
3123 {
3124 if (root->argList.empty())
3125 {
3126 result=false; // empty arg list -> function prototype.
3127 AUTO_TRACE_EXIT("empty arg list: result={}",result);
3128 return result;
3129 }
3130 for (const Argument &a : root->argList)
3131 {
3132 static const reg::Ex initChars(R"([\d"'&*!^]+)");
3133 reg::Match match;
3134 if (!a.name.empty() || !a.defval.empty())
3135 {
3136 std::string name = a.name.str();
3137 if (reg::search(name,match,initChars) && match.position()==0)
3138 {
3139 result=true;
3140 }
3141 else
3142 {
3143 result=false; // arg has (type,name) pair -> function prototype
3144 }
3145 AUTO_TRACE_EXIT("function prototype: result={}",result);
3146 return result;
3147 }
3148 if (!a.type.empty() &&
3149 (a.type.at(a.type.length()-1)=='*' ||
3150 a.type.at(a.type.length()-1)=='&'))
3151 // type ends with * or & => pointer or reference
3152 {
3153 result=false;
3154 AUTO_TRACE_EXIT("pointer or reference: result={}",result);
3155 return result;
3156 }
3157 if (a.type.empty() || resolver.resolveClass(ctx,a.type)!=nullptr)
3158 {
3159 result=false; // arg type is a known type
3160 AUTO_TRACE_EXIT("known type: result={}",result);
3161 return result;
3162 }
3163 if (checkIfTypedef(ctx,fd,a.type))
3164 {
3165 result=false; // argument is a typedef
3166 AUTO_TRACE_EXIT("typedef: result={}",result);
3167 return result;
3168 }
3169 std::string atype = a.type.str();
3170 if (reg::search(atype,match,initChars) && match.position()==0)
3171 {
3172 result=true; // argument type starts with typical initializer char
3173 AUTO_TRACE_EXIT("argument with init char: result={}",result);
3174 return result;
3175 }
3176 std::string resType=resolveTypeDef(ctx,a.type).str();
3177 if (resType.empty()) resType=atype;
3178 static const reg::Ex idChars(R"(\a\w*)");
3179 if (reg::search(resType,match,idChars) && match.position()==0) // resType starts with identifier
3180 {
3181 resType=match.str();
3182 if (resType=="int" || resType=="long" ||
3183 resType=="float" || resType=="double" ||
3184 resType=="char" || resType=="void" ||
3185 resType=="signed" || resType=="unsigned" ||
3186 resType=="const" || resType=="volatile" )
3187 {
3188 result=false; // type keyword -> function prototype
3189 AUTO_TRACE_EXIT("type keyword: result={}",result);
3190 return result;
3191 }
3192 }
3193 }
3194 result=true;
3195 }
3196
3197 AUTO_TRACE_EXIT("end: result={}",result);
3198 return result;
3199}
3200
3201//--------------------------------------------------------------------------------------
3202
3203/*! Searches for the end of a template in prototype \a s starting from
3204 * character position \a startPos. If the end was found the position
3205 * of the closing > is returned, otherwise -1 is returned.
3206 *
3207 * Handles exotic cases such as
3208 * \code
3209 * Class<(id<0)>
3210 * Class<bits<<2>
3211 * Class<"<">
3212 * Class<'<'>
3213 * Class<(")<")>
3214 * \endcode
3215 */
3216static int findEndOfTemplate(const DString &s,size_t startPos)
3217{
3218 // locate end of template
3219 size_t e=startPos;
3220 int brCount=1;
3221 int roundCount=0;
3222 size_t len = s.length();
3223 bool insideString=false;
3224 bool insideChar=false;
3225 char pc = 0;
3226 while (e<len && brCount!=0)
3227 {
3228 char c=s.at(e);
3229 switch(c)
3230 {
3231 case '<':
3232 if (!insideString && !insideChar)
3233 {
3234 if (e<len-1 && s.at(e+1)=='<')
3235 e++;
3236 else if (roundCount==0)
3237 brCount++;
3238 }
3239 break;
3240 case '>':
3241 if (!insideString && !insideChar)
3242 {
3243 if (e<len-1 && s.at(e+1)=='>')
3244 e++;
3245 else if (roundCount==0)
3246 brCount--;
3247 }
3248 break;
3249 case '(':
3250 if (!insideString && !insideChar)
3251 roundCount++;
3252 break;
3253 case ')':
3254 if (!insideString && !insideChar)
3255 roundCount--;
3256 break;
3257 case '"':
3258 if (!insideChar)
3259 {
3260 if (insideString && pc!='\\')
3261 insideString=false;
3262 else
3263 insideString=true;
3264 }
3265 break;
3266 case '\'':
3267 if (!insideString)
3268 {
3269 if (insideChar && pc!='\\')
3270 insideChar=false;
3271 else
3272 insideChar=true;
3273 }
3274 break;
3275 }
3276 pc = c;
3277 e++;
3278 }
3279 return brCount==0 ? static_cast<int>(e) : -1;
3280}
3281
3282//--------------------------------------------------------------------------------------
3283
3284static void addVariable(const Entry *root,int isFuncPtr=-1)
3285{
3286 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
3287
3288 AUTO_TRACE("VARIABLE_SEC: type='{}' name='{}' args='{}' bodyLine={} endBodyLine={} mGrpId={} relates='{}'",
3289 root->type, root->name, root->args, root->bodyLine, root->endBodyLine, root->mGrpId, root->relates);
3290 //printf("root->parent->name=%s\n",qPrint(root->parent->name));
3291
3292 DString type = root->type;
3293 DString name = root->name;
3294 DString args = root->args;
3295 if (type.empty() && name.find("operator")==DString::npos &&
3296 (name.find('*')!=DString::npos || name.find('&')!=DString::npos))
3297 {
3298 // recover from parse error caused by redundant braces
3299 // like in "int *(var[10]);", which is parsed as
3300 // type="" name="int *" args="(var[10])"
3301
3302 type=name;
3303 std::string sargs = args.str();
3304 static const reg::Ex reName(R"(\a\w*)");
3305 reg::Match match;
3306 if (reg::search(sargs,match,reName))
3307 {
3308 name = match.str(); // e.g. 'var' in '(var[10])'
3309 sargs = match.suffix().str(); // e.g. '[10]) in '(var[10])'
3310 size_t j = sargs.find(')');
3311 if (j!=std::string::npos) args=sargs.substr(0,j); // extract, e.g '[10]' from '[10])'
3312 }
3313 }
3314 else
3315 {
3316 int i=isFuncPtr;
3317 if (i==-1 && (root->spec.isAlias())==0) i=findFunctionPtr(type.str(),root->lang); // for typedefs isFuncPtr is not yet set
3318 AUTO_TRACE_ADD("functionPtr={}",i!=-1?"yes":"no");
3319 if (i>=0) // function pointer
3320 {
3321 size_t ii = i;
3322 size_t ai = type.find('[',ii);
3323 if (ai>ii) // function pointer array
3324 {
3325 args.prepend(type.mid(ai));
3326 type=type.left(ai);
3327 }
3328 else if (type.find(')',ii)!=DString::npos) // function ptr, not variable like "int (*bla)[10]"
3329 {
3330 type=type.left(type.length()-1);
3331 args.prepend(") ");
3332 }
3333 }
3334 }
3335 AUTO_TRACE_ADD("after correction: type='{}' name='{}' args='{}'",type,name,args);
3336
3337 DString scope;
3338 name=removeRedundantWhiteSpace(name);
3339
3340 // find the scope of this variable
3341 int index = computeQualifiedIndex(name);
3342 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3343 // grouped members are stored with full scope
3344 {
3345 buildScopeFromQualifiedName(name.left(index+2),root->lang,root->tagInfo());
3346 scope=name.left(index);
3347 name=name.mid(index+2);
3348 }
3349 else
3350 {
3351 Entry *p = root->parent();
3352 while (p->section.isScope())
3353 {
3354 DString scopeName = p->name;
3355 if (!scopeName.empty())
3356 {
3357 scope.prepend(scopeName);
3358 break;
3359 }
3360 p=p->parent();
3361 }
3362 }
3363
3364 DString type_s = type;
3365 type=type.stripWhiteSpace();
3366 ClassDefMutable *cd=nullptr;
3367 bool isRelated=false;
3368 bool isMemberOf=false;
3369
3370 DString classScope=stripAnonymousNamespaceScope(scope);
3371 if (root->lang==SrcLangExt::CSharp)
3372 {
3373 classScope=mangleCSharpGenericName(classScope);
3374 }
3375 else
3376 {
3377 classScope=stripTemplateSpecifiersFromScope(classScope,false);
3378 }
3379 DString annScopePrefix=scope.left(scope.length()-classScope.length());
3380
3381
3382 // Look for last :: not part of template specifier
3383 int p=-1;
3384 for (size_t i=0;i<name.length()-1;i++)
3385 {
3386 if (name[i]==':' && name[i+1]==':')
3387 {
3388 p=static_cast<int>(i);
3389 }
3390 else if (name[i]=='<') // skip over template parts,
3391 // i.e. A::B<C::D> => p=1 and
3392 // A<B::C>::D => p=8
3393 {
3394 int e = findEndOfTemplate(name,i+1);
3395 if (e!=-1) i=static_cast<int>(e);
3396 }
3397 }
3398
3399 if (p!=-1) // found it
3400 {
3401 if (type=="friend class" || type=="friend struct" ||
3402 type=="friend union")
3403 {
3404 cd=getClassMutable(scope);
3405 if (cd)
3406 {
3407 addVariableToClass(root, // entry
3408 cd, // class to add member to
3409 MemberType::Friend, // type of member
3410 type, // type value as string
3411 name, // name of the member
3412 args, // arguments as string
3413 Protection::Public, // protection
3414 Relationship::Member // related to a class
3415 );
3416 }
3417 }
3418 if (root->bodyLine!=-1 && root->endBodyLine!=-1) // store the body location for later use
3419 {
3420 Doxygen::staticInitMap.emplace(name.str(),BodyInfo{root->startLine,root->bodyLine,root->endBodyLine});
3421 }
3422
3423
3424 AUTO_TRACE_ADD("static variable {} body=[{}..{}]",name,root->bodyLine,root->endBodyLine);
3425 return; /* skip this member, because it is a
3426 * static variable definition (always?), which will be
3427 * found in a class scope as well, but then we know the
3428 * correct protection level, so only then it will be
3429 * inserted in the correct list!
3430 */
3431 }
3432
3433 MemberType mtype = MemberType::Variable;
3434 if (type=="@")
3435 mtype=MemberType::EnumValue;
3436 else if (type_s.startsWith("typedef "))
3437 mtype=MemberType::Typedef;
3438 else if (type_s.startsWith("friend "))
3439 mtype=MemberType::Friend;
3440 else if (root->mtype==MethodTypes::Property)
3441 mtype=MemberType::Property;
3442 else if (root->mtype==MethodTypes::Event)
3443 mtype=MemberType::Event;
3444 else if (type.find("sequence<") != DString::npos)
3445 mtype=sliceOpt ? MemberType::Sequence : MemberType::Typedef;
3446 else if (type.find("dictionary<") != DString::npos)
3447 mtype=sliceOpt ? MemberType::Dictionary : MemberType::Typedef;
3448
3449 if (!root->relates.empty()) // related variable
3450 {
3451 isRelated=true;
3452 isMemberOf=(root->relatesType==RelatesType::MemberOf);
3453 if (getClass(root->relates)==nullptr && !scope.empty())
3454 scope=mergeScopes(scope,root->relates);
3455 else
3456 scope=root->relates;
3457 }
3458
3459 cd=getClassMutable(scope);
3460 if (cd==nullptr && classScope!=scope) cd=getClassMutable(classScope);
3461 if (cd)
3462 {
3463 // if cd is an anonymous (=tag less) scope we insert the member
3464 // into a non-anonymous parent scope as well. This is needed to
3465 // be able to refer to it using \var or \fn
3466
3467 Relationship relationship = isMemberOf ? Relationship::Foreign :
3468 isRelated ? Relationship::Related :
3469 Relationship::Member ;
3470
3471 addVariableToClass(root, // entry
3472 cd, // class to add member to
3473 mtype, // member type
3474 type, // type value as string
3475 name, // name of the member
3476 args, // arguments as string
3477 root->protection,
3478 relationship
3479 );
3480 }
3481 else if (!name.empty()) // global variable
3482 {
3483 addVariableToFile(root,mtype,scope,type,name,args);
3484 }
3485
3486}
3487
3488//----------------------------------------------------------------------
3489// Searches the Entry tree for typedef documentation sections.
3490// If found they are stored in their class or in the global list.
3491static void buildTypedefList(const Entry *root)
3492{
3493 //printf("buildVarList(%s)\n",qPrint(rootNav->name()));
3494 if (!root->name.empty() &&
3495 root->section.isVariable() &&
3496 root->type.find("typedef ")!=DString::npos // its a typedef
3497 )
3498 {
3499 AUTO_TRACE();
3500 DString rname = removeRedundantWhiteSpace(root->name);
3501 DString scope;
3502 int index = computeQualifiedIndex(rname);
3503 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3504 // grouped members are stored with full scope
3505 {
3506 buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
3507 scope=rname.left(index);
3508 rname=rname.mid(index+2);
3509 }
3510 else
3511 {
3512 scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
3513 }
3517 bool found=false;
3518 if (mn) // symbol with the same name already found
3519 {
3520 for (auto &imd : *mn)
3521 {
3522 if (!imd->isTypedef())
3523 continue;
3524
3525 DString rtype = root->type;
3526 rtype.stripPrefix("typedef ");
3527
3528 // merge the typedefs only if they're not both grouped, and both are
3529 // either part of the same class, part of the same namespace, or both
3530 // are global (i.e., neither in a class or a namespace)
3531 bool notBothGrouped = root->groups.empty() || imd->getGroupDef()==nullptr; // see example #100
3532 bool bothSameScope = (!cd && !nd) || (cd && imd->getClassDef() == cd) || (nd && imd->getNamespaceDef() == nd);
3533 //printf("imd->isTypedef()=%d imd->typeString()=%s root->type=%s\n",imd->isTypedef(),
3534 // qPrint(imd->typeString()),qPrint(root->type));
3535 if (notBothGrouped && bothSameScope && imd->typeString()==rtype)
3536 {
3537 MemberDefMutable *md = toMemberDefMutable(imd.get());
3538 if (md)
3539 {
3540 md->setDocumentation(root->doc,root->docFile,root->docLine);
3542 md->setDocsForDefinition(!root->proto);
3543 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3545 md->setRefItems(root->sli);
3546 md->setRequirementReferences(root->rqli);
3547 md->addQualifiers(root->qualifiers);
3548
3549 // merge ingroup specifiers
3550 if (md->getGroupDef()==nullptr && !root->groups.empty())
3551 {
3552 addMemberToGroups(root,md);
3553 }
3554 else if (md->getGroupDef()!=nullptr && root->groups.empty())
3555 {
3556 //printf("existing member is grouped, new member not\n");
3557 }
3558 else if (md->getGroupDef()!=nullptr && !root->groups.empty())
3559 {
3560 //printf("both members are grouped\n");
3561 }
3562 found=true;
3563 break;
3564 }
3565 }
3566 }
3567 }
3568 if (found)
3569 {
3570 AUTO_TRACE_ADD("typedef '{}' already found",rname);
3571 // mark the entry as processed, as we copied everything from it elsewhere
3572 // also, otherwise, due to containing `typedef` it may later get treated
3573 // as a function typedef in filterMemberDocumentation, which is incorrect
3574 root->markAsProcessed();
3575 }
3576 else
3577 {
3578 AUTO_TRACE_ADD("new typedef '{}'",rname);
3579 addVariable(root);
3580 }
3581
3582 }
3583 for (const auto &e : root->children())
3584 if (!e->section.isEnum())
3585 buildTypedefList(e.get());
3586}
3587
3588//----------------------------------------------------------------------
3589// Searches the Entry tree for sequence documentation sections.
3590// If found they are stored in the global list.
3591static void buildSequenceList(const Entry *root)
3592{
3593 if (!root->name.empty() &&
3594 root->section.isVariable() &&
3595 root->type.find("sequence<")!=DString::npos // it's a sequence
3596 )
3597 {
3598 AUTO_TRACE();
3599 addVariable(root);
3600 }
3601 for (const auto &e : root->children())
3602 if (!e->section.isEnum())
3603 buildSequenceList(e.get());
3604}
3605
3606//----------------------------------------------------------------------
3607// Searches the Entry tree for dictionary documentation sections.
3608// If found they are stored in the global list.
3609static void buildDictionaryList(const Entry *root)
3610{
3611 if (!root->name.empty() &&
3612 root->section.isVariable() &&
3613 root->type.find("dictionary<")!=DString::npos // it's a dictionary
3614 )
3615 {
3616 AUTO_TRACE();
3617 addVariable(root);
3618 }
3619 for (const auto &e : root->children())
3620 if (!e->section.isEnum())
3621 buildDictionaryList(e.get());
3622}
3623
3624//----------------------------------------------------------------------
3625// Searches the Entry tree for Variable documentation sections.
3626// If found they are stored in their class or in the global list.
3627
3628static void buildVarList(const Entry *root)
3629{
3630 //printf("buildVarList(%s) section=%08x\n",qPrint(rootNav->name()),rootNav->section());
3631 int isFuncPtr=-1;
3632 if (!root->name.empty() &&
3633 (root->type.empty() || g_compoundKeywords.find(root->type.str())==g_compoundKeywords.end()) &&
3634 (
3635 (root->section.isVariable() && // it's a variable
3636 root->type.find("typedef ")==DString::npos // and not a typedef
3637 ) ||
3638 (root->section.isFunction() && // or maybe a function pointer variable
3639 (isFuncPtr=findFunctionPtr(root->type.str(),root->lang))!=-1
3640 ) ||
3641 (root->section.isFunction() && // class variable initialized by constructor
3643 )
3644 )
3645 ) // documented variable
3646 {
3647 AUTO_TRACE();
3648 addVariable(root,isFuncPtr);
3649 }
3650 for (const auto &e : root->children())
3651 if (!e->section.isEnum())
3652 buildVarList(e.get());
3653}
3654
3655//----------------------------------------------------------------------
3656// Searches the Entry tree for Interface sections (UNO IDL only).
3657// If found they are stored in their service or in the global list.
3658//
3659
3661 const Entry *root,
3662 ClassDefMutable *cd,
3663 DString const& rname)
3664{
3665 FileDef *fd = root->fileDef();
3666 enum MemberType type = root->section.isExportedInterface() ? MemberType::Interface : MemberType::Service;
3667 DString fileName = root->fileName;
3668 if (fileName.empty() && root->tagInfo())
3669 {
3670 fileName = root->tagInfo()->tagName;
3671 }
3672 auto md = createMemberDef(
3673 fileName, root->startLine, root->startColumn, root->type, rname,
3674 "", "", root->protection, root->virt, root->isStatic, Relationship::Member,
3675 type, ArgumentList(), root->argList, root->metaData);
3676 auto mmd = toMemberDefMutable(md.get());
3677 mmd->setTagInfo(root->tagInfo());
3678 mmd->setMemberClass(cd);
3679 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3680 mmd->setDocsForDefinition(false);
3681 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3682 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3683 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3684 mmd->setMemberSpecifiers(root->spec);
3685 mmd->setVhdlSpecifiers(root->vhdlSpec);
3686 mmd->setMemberGroupId(root->mGrpId);
3687 mmd->setTypeConstraints(root->typeConstr);
3688 mmd->setLanguage(root->lang);
3689 mmd->setBodyDef(fd);
3690 mmd->setFileDef(fd);
3691 mmd->addSectionsToDefinition(root->anchors);
3692 DString const def = root->type + " " + rname;
3693 mmd->setDefinition(def);
3695 mmd->addQualifiers(root->qualifiers);
3696
3697 AUTO_TRACE("Interface member: fileName='{}' type='{}' name='{}' mtype='{}' prot={} virt={} state={} proto={} def='{}'",
3698 fileName,root->type,rname,type,root->protection,root->virt,root->isStatic,root->proto,def);
3699
3700 // add member to the class cd
3701 cd->insertMember(md.get());
3702 // also add the member as a "base" (to get nicer diagrams)
3703 // "optional" interface/service get Protected which turns into dashed line
3704 BaseInfo base(rname,
3705 root->spec.isOptional() ? Protection::Protected : Protection::Public, Specifier::Normal);
3706 TemplateNameMap templateNames;
3707 findClassRelation(root,cd,cd,&base,templateNames,DocumentedOnly,true) ||
3708 findClassRelation(root,cd,cd,&base,templateNames,Undocumented,true);
3709 // add file to list of used files
3710 cd->insertUsedFile(fd);
3711
3712 addMemberToGroups(root,md.get());
3714 root->markAsProcessed();
3715 mmd->setRefItems(root->sli);
3716 mmd->setRequirementReferences(root->rqli);
3717
3718 // add member to the global list of all members
3720 mn->push_back(std::move(md));
3721}
3722
3723static void buildInterfaceAndServiceList(const Entry *root)
3724{
3725 if (root->section.isExportedInterface() || root->section.isIncludedService())
3726 {
3727 AUTO_TRACE("Exported interface/included service: type='{}' scope='{}' name='{}' args='{}'"
3728 " relates='{}' relatesType='{}' file='{}' line={} bodyLine={} #tArgLists={}"
3729 " mGrpId={} spec={} proto={} docFile='{}'",
3730 root->type, root->parent()->name, root->name, root->args,
3731 root->relates, root->relatesType, root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(),
3732 root->mGrpId, root->spec, root->proto, root->docFile);
3733
3734 DString rname = removeRedundantWhiteSpace(root->name);
3735
3736 if (!rname.empty())
3737 {
3738 DString scope = root->parent()->name;
3739 ClassDefMutable *cd = getClassMutable(scope);
3740 assert(cd);
3741 if (cd && ((ClassDef::Interface == cd->compoundType()) ||
3742 (ClassDef::Service == cd->compoundType()) ||
3744 {
3746 }
3747 else
3748 {
3749 assert(false); // was checked by scanner.l
3750 }
3751 }
3752 else if (rname.empty())
3753 {
3754 warn(root->fileName,root->startLine,
3755 "Illegal member name found.");
3756 }
3757 }
3758 // can only have these in IDL anyway
3759 switch (root->lang)
3760 {
3761 case SrcLangExt::Unknown: // fall through (root node always is Unknown)
3762 case SrcLangExt::IDL:
3763 for (const auto &e : root->children()) buildInterfaceAndServiceList(e.get());
3764 break;
3765 default:
3766 return; // nothing to do here
3767 }
3768}
3769
3770
3771//----------------------------------------------------------------------
3772// Searches the Entry tree for Function sections.
3773// If found they are stored in their class or in the global list.
3774
3775static void addMethodToClass(const Entry *root,ClassDefMutable *cd,
3776 const DString &rtype,const DString &rname,const DString &rargs,
3777 bool isFriend,
3778 Protection protection,bool stat,Specifier virt,TypeSpecifier spec,
3779 const DString &relates
3780 )
3781{
3782 FileDef *fd=root->fileDef();
3783
3784 DString type = rtype;
3785 DString args = rargs;
3786
3788 name.stripPrefix("::");
3789
3790 MemberType mtype = MemberType::Function;
3791 if (isFriend) mtype=MemberType::Friend;
3792 else if (root->mtype==MethodTypes::Signal) mtype=MemberType::Signal;
3793 else if (root->mtype==MethodTypes::Slot) mtype=MemberType::Slot;
3794 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType::DCOP;
3795
3796 // strip redundant template specifier for constructors
3797 size_t i = DString::npos;
3798 size_t j = DString::npos;
3799 if ((fd==nullptr || fd->getLanguage()==SrcLangExt::Cpp) &&
3800 !name.startsWith("operator ") && // not operator
3801 (i=name.find('<'))!=DString::npos && // containing <
3802 (j=name.find('>'))!=DString::npos && // or >
3803 (j!=i+2 || name.at(i+1)!='=') // but not the C++20 spaceship operator <=>
3804 )
3805 {
3806 name=name.left(i);
3807 }
3808
3809 DString fileName = root->fileName;
3810 if (fileName.empty() && root->tagInfo())
3811 {
3812 fileName = root->tagInfo()->tagName;
3813 }
3814
3815 //printf("root->name='%s; args='%s' root->argList='%s'\n",
3816 // qPrint(root->name),qPrint(args),qPrint(argListToString(root->argList))
3817 // );
3818
3819 // adding class member
3820 Relationship relationship = relates.empty() ? Relationship::Member :
3821 root->relatesType==RelatesType::MemberOf ? Relationship::Foreign :
3822 Relationship::Related ;
3823 auto md = createMemberDef(
3824 fileName,root->startLine,root->startColumn,
3825 type,name,args,root->exception,
3826 protection,virt,
3827 stat && root->relatesType!=RelatesType::MemberOf,
3828 relationship,
3829 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3830 root->argList, root->metaData);
3831 auto mmd = toMemberDefMutable(md.get());
3832 mmd->setTagInfo(root->tagInfo());
3833 mmd->setMemberClass(cd);
3834 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3835 mmd->setDocsForDefinition(!root->proto);
3836 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3837 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3838 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3839 mmd->setMemberSpecifiers(spec);
3840 mmd->setVhdlSpecifiers(root->vhdlSpec);
3841 mmd->setMemberGroupId(root->mGrpId);
3842 mmd->setTypeConstraints(root->typeConstr);
3843 mmd->setLanguage(root->lang);
3844 mmd->setRequiresClause(root->req);
3845 mmd->setId(root->id);
3846 mmd->setBodyDef(fd);
3847 mmd->setFileDef(fd);
3848 mmd->addSectionsToDefinition(root->anchors);
3849 DString def;
3851 SrcLangExt lang = cd->getLanguage();
3852 DString scopeSeparator=getLanguageSpecificSeparator(lang);
3853 if (scopeSeparator!="::")
3854 {
3855 qualScope = substitute(qualScope,"::",scopeSeparator);
3856 }
3857 if (lang==SrcLangExt::PHP)
3858 {
3859 // for PHP we use Class::method and Namespace\method
3860 scopeSeparator="::";
3861 }
3862 if (!relates.empty() || isFriend || Config_getBool(HIDE_SCOPE_NAMES))
3863 {
3864 if (!type.empty())
3865 {
3866 def=type+" "+name; //+optArgs;
3867 }
3868 else
3869 {
3870 def=name; //+optArgs;
3871 }
3872 }
3873 else
3874 {
3875 if (!type.empty())
3876 {
3877 def=type+" "+qualScope+scopeSeparator+name; //+optArgs;
3878 }
3879 else
3880 {
3881 def=qualScope+scopeSeparator+name; //+optArgs;
3882 }
3883 }
3884 def.stripPrefix("friend ");
3885 mmd->setDefinition(def);
3887 mmd->addQualifiers(root->qualifiers);
3888
3889 AUTO_TRACE("function member: type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
3890 type, qualScope, rname, args, root->proto, def);
3891
3892 // add member to the class cd
3893 cd->insertMember(md.get());
3894 // add file to list of used files
3895 cd->insertUsedFile(fd);
3896
3897 addMemberToGroups(root,md.get());
3899 root->markAsProcessed();
3900 mmd->setRefItems(root->sli);
3901 mmd->setRequirementReferences(root->rqli);
3902
3903 // add member to the global list of all members
3904 //printf("Adding member=%s class=%s\n",qPrint(md->name()),qPrint(cd->name()));
3906 mn->push_back(std::move(md));
3907}
3908
3909//------------------------------------------------------------------------------------------
3910
3911static void addGlobalFunction(const Entry *root,const DString &rname,const DString &sc)
3912{
3913 DString scope = sc;
3914
3915 // new global function
3917 auto md = createMemberDef(
3918 root->fileName,root->startLine,root->startColumn,
3919 root->type,name,root->args,root->exception,
3920 root->protection,root->virt,root->isStatic,Relationship::Member,
3921 MemberType::Function,
3922 !root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3923 root->argList,root->metaData);
3924 auto mmd = toMemberDefMutable(md.get());
3925 mmd->setTagInfo(root->tagInfo());
3926 mmd->setLanguage(root->lang);
3927 mmd->setId(root->id);
3928 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3929 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3930 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3931 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
3932 mmd->setDocsForDefinition(!root->proto);
3933 mmd->setTypeConstraints(root->typeConstr);
3934 //md->setBody(root->body);
3935 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3936 FileDef *fd=root->fileDef();
3937 mmd->setBodyDef(fd);
3938 mmd->addSectionsToDefinition(root->anchors);
3939 mmd->setMemberSpecifiers(root->spec);
3940 mmd->setVhdlSpecifiers(root->vhdlSpec);
3941 mmd->setMemberGroupId(root->mGrpId);
3942 mmd->setRequiresClause(root->req);
3943 mmd->setExplicitExternal(root->explicitExternal,root->fileName,root->startLine,root->startColumn);
3944
3945 NamespaceDefMutable *nd = nullptr;
3946 // see if the function is inside a namespace that was not part of
3947 // the name already (in that case nd should be non-zero already)
3948 if (root->parent()->section.isNamespace())
3949 {
3950 //DString nscope=removeAnonymousScopes(root->parent()->name);
3951 DString nscope=root->parent()->name;
3952 if (!nscope.empty())
3953 {
3954 nd = getResolvedNamespaceMutable(nscope);
3955 }
3956 }
3957 else if (root->parent()->section.isGroupDoc() && !scope.empty())
3958 {
3960 }
3961
3962 if (!scope.empty())
3963 {
3965 if (sep!="::")
3966 {
3967 scope = substitute(scope,"::",sep);
3968 }
3969 scope+=sep;
3970 }
3971
3972 if (Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python) scope = "";
3973 DString def;
3974 //DString optArgs = root->argList.empty() ? DString() : root->args;
3975 if (!root->type.empty())
3976 {
3977 def=root->type+" "+scope+name; //+optArgs;
3978 }
3979 else
3980 {
3981 def=scope+name; //+optArgs;
3982 }
3983 AUTO_TRACE("new non-member function type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
3984 root->type,scope,rname,root->args,root->proto,def);
3985 mmd->setDefinition(def);
3987 mmd->addQualifiers(root->qualifiers);
3988
3989 mmd->setRefItems(root->sli);
3990 mmd->setRequirementReferences(root->rqli);
3991 if (nd && !nd->name().empty() && nd->name().at(0)!='@')
3992 {
3993 // add member to namespace
3994 mmd->setNamespace(nd);
3995 nd->insertMember(md.get());
3996 }
3997 if (fd)
3998 {
3999 // add member to the file (we do this even if we have already
4000 // inserted it into the namespace)
4001 mmd->setFileDef(fd);
4002 fd->insertMember(md.get());
4003 }
4004
4005 addMemberToGroups(root,md.get());
4007 if (root->relatesType == RelatesType::Simple) // if this is a relatesalso command,
4008 // allow find Member to pick it up
4009 {
4010 root->markAsProcessed(); // Otherwise we have finished with this entry.
4011 }
4012
4013 // add member to the list of file members
4015 mn->push_back(std::move(md));
4016}
4017
4018//------------------------------------------------------------------------------------------
4019
4020static void buildFunctionList(const Entry *root)
4021{
4022 if (root->section.isFunction())
4023 {
4024 AUTO_TRACE("member function: type='{}' scope='{}' name='{}' args='{}' relates='{}' relatesType='{}'"
4025 " file='{}' line={} bodyLine={} #tArgLists={} mGrpId={}"
4026 " spec={} proto={} docFile='{}'",
4027 root->type, root->parent()->name, root->name, root->args, root->relates, root->relatesType,
4028 root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(), root->mGrpId,
4029 root->spec, root->proto, root->docFile);
4030
4031 bool isFriend=root->type=="friend" || root->type.find("friend ")!=DString::npos;
4032 DString rname = removeRedundantWhiteSpace(root->name);
4033 //printf("rname=%s\n",qPrint(rname));
4034
4035 DString scope;
4036 int index = computeQualifiedIndex(rname);
4037 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
4038 // grouped members are stored with full scope
4039 {
4040 buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
4041 scope=rname.left(index);
4042 rname=rname.mid(index+2);
4043 }
4044 else
4045 {
4046 scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
4047 }
4048 if (!rname.empty() && scope.find('@')==DString::npos)
4049 {
4050 // check if this function's parent is a class
4051 if (root->lang==SrcLangExt::CSharp)
4052 {
4053 scope=mangleCSharpGenericName(scope);
4054 }
4055 else
4056 {
4057 scope=stripTemplateSpecifiersFromScope(scope,false);
4058 }
4059
4060 FileDef *rfd=root->fileDef();
4061
4062 size_t memIndex=rname.rfind("::");
4063
4065 if (cd && scope+"::"==rname.left(scope.length()+2)) // found A::f inside A
4066 {
4067 // strip scope from name
4068 rname=rname.mid(root->parent()->name.length()+2);
4069 }
4070
4071 bool isMember=false;
4072 if (memIndex!=DString::npos)
4073 {
4074 size_t ts=rname.find('<');
4075 size_t te=rname.find('>');
4076 if (memIndex>0 && (ts==DString::npos || te==DString::npos))
4077 {
4078 // note: the following code was replaced by inMember=true to deal with a
4079 // function rname='X::foo' of class X inside a namespace also called X...
4080 // bug id 548175
4081 //nd = Doxygen::namespaceLinkedMap->find(rname.left(memIndex));
4082 //isMember = nd==nullptr;
4083 //if (nd)
4084 //{
4085 // // strip namespace scope from name
4086 // scope=rname.left(memIndex);
4087 // rname=rname.mid(memIndex+2);
4088 //}
4089 isMember = true;
4090 }
4091 else
4092 {
4093 isMember=memIndex<ts || memIndex>te;
4094 }
4095 }
4096
4097 if (!root->parent()->name.empty() && root->parent()->section.isCompound() && cd)
4098 {
4099 AUTO_TRACE_ADD("member '{}' of class '{}'", rname,cd->name());
4100 addMethodToClass(root,cd,root->type,rname,root->args,isFriend,
4101 root->protection,root->isStatic,root->virt,root->spec,root->relates);
4102 }
4103 else if (root->parent()->section.isObjcImpl() && cd)
4104 {
4105 const MemberDef *md = cd->getMemberByName(rname);
4106 if (md)
4107 {
4108 MemberDefMutable *mdm = toMemberDefMutable(const_cast<MemberDef*>(md));
4109 if (mdm)
4110 {
4111 mdm->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
4112 mdm->setBodyDef(root->fileDef());
4113 }
4114 }
4115 }
4116 else if (!root->parent()->section.isCompound() && !root->parent()->section.isObjcImpl() &&
4117 !isMember &&
4118 (root->relates.empty() || root->relatesType==RelatesType::Duplicate) &&
4119 !root->type.startsWith("extern ") && !root->type.startsWith("typedef ")
4120 )
4121 // no member => unrelated function
4122 {
4123 /* check the uniqueness of the function name in the file.
4124 * A file could contain a function prototype and a function definition
4125 * or even multiple function prototypes.
4126 */
4127 bool found=false;
4128 MemberDef *md_found=nullptr;
4130 if (mn)
4131 {
4132 AUTO_TRACE_ADD("function '{}' already found",rname);
4133 for (const auto &imd : *mn)
4134 {
4135 MemberDefMutable *md = toMemberDefMutable(imd.get());
4136 if (md)
4137 {
4138 const NamespaceDef *mnd = md->getNamespaceDef();
4139 NamespaceDef *rnd = nullptr;
4140 //printf("root namespace=%s\n",qPrint(rootNav->parent()->name()));
4141 DString fullScope = scope;
4142 DString parentScope = root->parent()->name;
4143 if (!parentScope.empty() && !leftScopeMatch(parentScope,scope))
4144 {
4145 if (!scope.empty()) fullScope.prepend("::");
4146 fullScope.prepend(parentScope);
4147 }
4148 //printf("fullScope=%s\n",qPrint(fullScope));
4149 rnd = getResolvedNamespace(fullScope);
4150 const FileDef *mfd = md->getFileDef();
4151 DString nsName,rnsName;
4152 if (mnd) nsName = mnd->name();
4153 if (rnd) rnsName = rnd->name();
4154 //printf("matching arguments for %s%s %s%s\n",
4155 // qPrint(md->name()),md->argsString(),qPrint(rname),qPrint(argListToString(root->argList)));
4156 const ArgumentList &mdAl = md->argumentList();
4157 const ArgumentList &mdTempl = md->templateArguments();
4158
4159 // in case of template functions, we need to check if the
4160 // functions have the same number of template parameters
4161 bool sameTemplateArgs = true;
4162 bool matchingReturnTypes = true;
4163 bool sameRequiresClause = true;
4164 if (!mdTempl.empty() && !root->tArgLists.empty())
4165 {
4166 sameTemplateArgs = matchTemplateArguments(mdTempl,root->tArgLists.back());
4167 if (md->typeString()!=removeRedundantWhiteSpace(root->type))
4168 {
4169 matchingReturnTypes = false;
4170 }
4171 if (md->requiresClause()!=root->req)
4172 {
4173 sameRequiresClause = false;
4174 }
4175 }
4176 else if (!mdTempl.empty() || !root->tArgLists.empty())
4177 { // if one has template parameters and the other doesn't then that also counts as a
4178 // difference
4179 sameTemplateArgs = false;
4180 }
4181
4182 bool staticsInDifferentFiles =
4183 root->isStatic && md->isStatic() && root->fileName!=md->getDefFileName();
4184
4185 if (sameTemplateArgs &&
4186 matchingReturnTypes &&
4187 sameRequiresClause &&
4188 !staticsInDifferentFiles &&
4189 matchArguments2(md->getOuterScope(),mfd,md->typeString(),&mdAl,
4190 rnd ? rnd : Doxygen::globalScope,rfd,root->type,&root->argList,
4191 false,root->lang)
4192 )
4193 {
4194 GroupDef *gd=nullptr;
4195 if (!root->groups.empty() && !root->groups.front().groupname.empty())
4196 {
4197 gd = Doxygen::groupLinkedMap->find(root->groups.front().groupname);
4198 }
4199 //printf("match!\n");
4200 //printf("mnd=%p rnd=%p nsName=%s rnsName=%s\n",mnd,rnd,qPrint(nsName),qPrint(rnsName));
4201 // see if we need to create a new member
4202 found=(mnd && rnd && nsName==rnsName) || // members are in the same namespace
4203 ((mnd==nullptr && rnd==nullptr && mfd!=nullptr && // no external reference and
4204 mfd->absFilePath()==root->fileName // prototype in the same file
4205 )
4206 );
4207 // otherwise, allow a duplicate global member with the same argument list
4208 if (!found && gd && gd==md->getGroupDef() && nsName==rnsName)
4209 {
4210 // member is already in the group, so we don't want to add it again.
4211 found=true;
4212 }
4213
4214 AUTO_TRACE_ADD("combining function with prototype found={} in namespace '{}'",found,nsName);
4215
4216 if (found)
4217 {
4218 // merge argument lists
4219 ArgumentList mergedArgList = root->argList;
4220 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.empty());
4221 // merge documentation
4222 if (md->documentation().empty() && !root->doc.empty())
4223 {
4224 if (root->proto)
4225 {
4227 }
4228 else
4229 {
4231 }
4232 }
4233
4234 md->setDocumentation(root->doc,root->docFile,root->docLine);
4236 md->setDocsForDefinition(!root->proto);
4237 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
4238 {
4239 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
4240 md->setBodyDef(rfd);
4241 }
4242
4243 if (md->briefDescription().empty() && !root->brief.empty())
4244 {
4245 md->setArgsString(root->args);
4246 }
4247 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
4248
4250
4252 md->addQualifiers(root->qualifiers);
4253
4254 // merge ingroup specifiers
4255 if (md->getGroupDef()==nullptr && !root->groups.empty())
4256 {
4257 addMemberToGroups(root,md);
4258 }
4259 else if (md->getGroupDef()!=nullptr && root->groups.empty())
4260 {
4261 //printf("existing member is grouped, new member not\n");
4262 }
4263 else if (md->getGroupDef()!=nullptr && !root->groups.empty())
4264 {
4265 //printf("both members are grouped\n");
4266 }
4268
4269 // if md is a declaration and root is the corresponding
4270 // definition, then turn md into a definition.
4271 if (md->isPrototype() && !root->proto)
4272 {
4273 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
4274 md->setPrototype(false,root->fileName,root->startLine,root->startColumn);
4275 }
4276 // if md is already the definition, then add the declaration info
4277 else if (!md->isPrototype() && root->proto)
4278 {
4279 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
4280 }
4281 }
4282 }
4283 }
4284 if (found)
4285 {
4286 md_found = md;
4287 break;
4288 }
4289 }
4290 }
4291 if (!found) /* global function is unique with respect to the file */
4292 {
4293 addGlobalFunction(root,rname,scope);
4294 }
4295 else
4296 {
4297 FileDef *fd=root->fileDef();
4298 if (fd)
4299 {
4300 // add member to the file (we do this even if we have already
4301 // inserted it into the namespace)
4302 fd->insertMember(md_found);
4303 }
4304 }
4305
4306 AUTO_TRACE_ADD("unrelated function type='{}' name='{}' args='{}'",root->type,rname,root->args);
4307 }
4308 else
4309 {
4310 AUTO_TRACE_ADD("function '{}' is not processed",rname);
4311 }
4312 }
4313 else if (rname.empty())
4314 {
4315 warn(root->fileName,root->startLine,
4316 "Illegal member name found."
4317 );
4318 }
4319 }
4320 for (const auto &e : root->children()) buildFunctionList(e.get());
4321}
4322
4323//----------------------------------------------------------------------
4324
4325static void findFriends()
4326{
4327 AUTO_TRACE();
4328 for (const auto &fn : *Doxygen::functionNameLinkedMap) // for each global function name
4329 {
4330 MemberName *mn = Doxygen::memberNameLinkedMap->find(fn->memberName());
4331 if (mn)
4332 { // there are members with the same name
4333 // for each function with that name
4334 for (const auto &ifmd : *fn)
4335 {
4336 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
4337 // for each member with that name
4338 for (const auto &immd : *mn)
4339 {
4340 MemberDefMutable *mmd = toMemberDefMutable(immd.get());
4341 //printf("Checking for matching arguments
4342 // mmd->isRelated()=%d mmd->isFriend()=%d mmd->isFunction()=%d\n",
4343 // mmd->isRelated(),mmd->isFriend(),mmd->isFunction());
4344 if (fmd && mmd &&
4345 (mmd->isFriend() || (mmd->isRelated() && mmd->isFunction())) &&
4346 matchArguments2(mmd->getOuterScope(), mmd->getFileDef(), mmd->typeString(), &mmd->argumentList(),
4347 fmd->getOuterScope(), fmd->getFileDef(), fmd->typeString(), &fmd->argumentList(),
4348 true,mmd->getLanguage()
4349 )
4350
4351 ) // if the member is related and the arguments match then the
4352 // function is actually a friend.
4353 {
4354 AUTO_TRACE_ADD("Merging related global and member '{}' isFriend={} isRelated={} isFunction={}",
4355 mmd->name(),mmd->isFriend(),mmd->isRelated(),mmd->isFunction());
4356 const ArgumentList &mmdAl = mmd->argumentList();
4357 const ArgumentList &fmdAl = fmd->argumentList();
4358 mergeArguments(const_cast<ArgumentList&>(fmdAl),const_cast<ArgumentList&>(mmdAl));
4359
4360 // reset argument lists to add missing default parameters
4361 DString mmdAlStr = argListToString(mmdAl);
4362 DString fmdAlStr = argListToString(fmdAl);
4363 mmd->setArgsString(mmdAlStr);
4364 fmd->setArgsString(fmdAlStr);
4365 mmd->moveDeclArgumentList(std::make_unique<ArgumentList>(mmdAl));
4366 fmd->moveDeclArgumentList(std::make_unique<ArgumentList>(fmdAl));
4367 AUTO_TRACE_ADD("friend args='{}' member args='{}'",argListToString(fmd->argumentList()),argListToString(mmd->argumentList()));
4368
4369 if (!fmd->documentation().empty())
4370 {
4371 mmd->setDocumentation(fmd->documentation(),fmd->docFile(),fmd->docLine());
4372 }
4373 else if (!mmd->documentation().empty())
4374 {
4375 fmd->setDocumentation(mmd->documentation(),mmd->docFile(),mmd->docLine());
4376 }
4377 if (mmd->briefDescription().empty() && !fmd->briefDescription().empty())
4378 {
4379 mmd->setBriefDescription(fmd->briefDescription(),fmd->briefFile(),fmd->briefLine());
4380 }
4381 else if (!mmd->briefDescription().empty() && !fmd->briefDescription().empty())
4382 {
4383 fmd->setBriefDescription(mmd->briefDescription(),mmd->briefFile(),mmd->briefLine());
4384 }
4385 if (!fmd->inbodyDocumentation().empty())
4386 {
4388 }
4389 else if (!mmd->inbodyDocumentation().empty())
4390 {
4392 }
4393 //printf("body mmd %d fmd %d\n",mmd->getStartBodyLine(),fmd->getStartBodyLine());
4394 if (mmd->getStartBodyLine()==-1 && fmd->getStartBodyLine()!=-1)
4395 {
4396 mmd->setBodySegment(fmd->getDefLine(),fmd->getStartBodyLine(),fmd->getEndBodyLine());
4397 mmd->setBodyDef(fmd->getBodyDef());
4398 //mmd->setBodyMember(fmd);
4399 }
4400 else if (mmd->getStartBodyLine()!=-1 && fmd->getStartBodyLine()==-1)
4401 {
4402 fmd->setBodySegment(mmd->getDefLine(),mmd->getStartBodyLine(),mmd->getEndBodyLine());
4403 fmd->setBodyDef(mmd->getBodyDef());
4404 //fmd->setBodyMember(mmd);
4405 }
4407
4409
4410 mmd->addQualifiers(fmd->getQualifiers());
4411 fmd->addQualifiers(mmd->getQualifiers());
4412
4413 }
4414 }
4415 }
4416 }
4417 }
4418}
4419
4420//----------------------------------------------------------------------
4421
4423{
4424 AUTO_TRACE();
4425
4426 // find matching function declaration and definitions.
4427 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4428 {
4429 //printf("memberName=%s count=%zu\n",qPrint(mn->memberName()),mn->size());
4430 /* find a matching function declaration and definition for this function */
4431 for (const auto &imdec : *mn)
4432 {
4433 MemberDefMutable *mdec = toMemberDefMutable(imdec.get());
4434 if (mdec &&
4435 (mdec->isPrototype() ||
4436 (mdec->isVariable() && mdec->isExternal())
4437 ))
4438 {
4439 for (const auto &imdef : *mn)
4440 {
4441 MemberDefMutable *mdef = toMemberDefMutable(imdef.get());
4442 if (mdef && mdec!=mdef &&
4443 mdec->getNamespaceDef()==mdef->getNamespaceDef())
4444 {
4446 }
4447 }
4448 }
4449 }
4450 }
4451}
4452
4453//----------------------------------------------------------------------
4454
4456{
4457 AUTO_TRACE();
4458 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4459 {
4460 MemberDefMutable *mdef=nullptr,*mdec=nullptr;
4461 /* find a matching function declaration and definition for this function */
4462 for (const auto &imd : *mn)
4463 {
4464 MemberDefMutable *md = toMemberDefMutable(imd.get());
4465 if (md)
4466 {
4467 if (md->isPrototype())
4468 mdec=md;
4469 else if (md->isVariable() && md->isExternal())
4470 mdec=md;
4471
4472 if (md->isFunction() && !md->isStatic() && !md->isPrototype())
4473 mdef=md;
4474 else if (md->isVariable() && !md->isExternal() && !md->isStatic())
4475 mdef=md;
4476 }
4477
4478 if (mdef && mdec) break;
4479 }
4480 if (mdef && mdec)
4481 {
4482 const ArgumentList &mdefAl = mdef->argumentList();
4483 const ArgumentList &mdecAl = mdec->argumentList();
4484 if (
4485 matchArguments2(mdef->getOuterScope(),mdef->getFileDef(),mdef->typeString(),const_cast<ArgumentList*>(&mdefAl),
4486 mdec->getOuterScope(),mdec->getFileDef(),mdec->typeString(),const_cast<ArgumentList*>(&mdecAl),
4487 true,mdef->getLanguage()
4488 )
4489 ) /* match found */
4490 {
4491 AUTO_TRACE_ADD("merging references for mdec={} mdef={}",mdec->name(),mdef->name());
4492 mdef->mergeReferences(mdec);
4493 mdec->mergeReferences(mdef);
4494 mdef->mergeReferencedBy(mdec);
4495 mdec->mergeReferencedBy(mdef);
4496 }
4497 }
4498 }
4499}
4500
4501//----------------------------------------------------------------------
4502
4504{
4505 AUTO_TRACE();
4506 // find match between function declaration and definition for
4507 // related functions
4508 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4509 {
4510 /* find a matching function declaration and definition for this function */
4511 // for each global function
4512 for (const auto &imd : *mn)
4513 {
4514 MemberDefMutable *md = toMemberDefMutable(imd.get());
4515 if (md)
4516 {
4517 //printf(" Function '%s'\n",qPrint(md->name()));
4519 if (rmn) // check if there is a member with the same name
4520 {
4521 //printf(" Member name found\n");
4522 // for each member with the same name
4523 for (const auto &irmd : *rmn)
4524 {
4525 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
4526 //printf(" Member found: related='%d'\n",rmd->isRelated());
4527 if (rmd &&
4528 (rmd->isRelated() || rmd->isForeign()) && // related function
4529 matchArguments2( md->getOuterScope(), md->getFileDef(), md->typeString(), &md->argumentList(),
4530 rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmd->argumentList(),
4531 true,md->getLanguage()
4532 )
4533 )
4534 {
4535 AUTO_TRACE_ADD("Found related member '{}'",md->name());
4536 if (rmd->relatedAlso())
4537 md->setRelatedAlso(rmd->relatedAlso());
4538 else if (rmd->isForeign())
4539 md->makeForeign();
4540 else
4541 md->makeRelated();
4542 }
4543 }
4544 }
4545 }
4546 }
4547 }
4548}
4549
4550//----------------------------------------------------------------------
4551
4553{
4554 AUTO_TRACE();
4555 for (const auto &[qualifiedName,bodyInfo] : Doxygen::staticInitMap)
4556 {
4557 size_t i=qualifiedName.rfind("::");
4558 if (i!=std::string::npos)
4559 {
4560 DString scope = qualifiedName.substr(0,i);
4561 DString name = qualifiedName.substr(i+2);
4563 if (mn)
4564 {
4565 for (const auto &imd : *mn)
4566 {
4567 MemberDefMutable *md = toMemberDefMutable(imd.get());
4568 if (md && md->qualifiedName().str()==qualifiedName && md->isVariable())
4569 {
4570 AUTO_TRACE_ADD("found static member {} body [{}..{}]\n",
4571 md->qualifiedName(),bodyInfo.startLine,bodyInfo.endLine);
4572 md->setBodySegment(bodyInfo.defLine,
4573 bodyInfo.startLine,
4574 bodyInfo.endLine);
4575 }
4576 }
4577 }
4578 }
4579 }
4580}
4581
4582//----------------------------------------------------------------------
4583
4584/*! make a dictionary of all template arguments of class cd
4585 * that are part of the base class name.
4586 * Example: A template class A with template arguments <R,S,T>
4587 * that inherits from B<T,T,S> will have T and S in the dictionary.
4588 */
4589static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments,const std::string &name)
4590{
4591 std::map<std::string,int> templateNames;
4592 int count=0;
4593 for (const Argument &arg : templateArguments)
4594 {
4595 static const reg::Ex re(R"(\a[\w:]*)");
4596 reg::Iterator it(name,re);
4598 for (; it!=end ; ++it)
4599 {
4600 const auto &match = *it;
4601 std::string n = match.str();
4602 if (n==arg.name.str())
4603 {
4604 if (templateNames.find(n)==templateNames.end())
4605 {
4606 templateNames.emplace(n,count);
4607 }
4608 }
4609 }
4610 }
4611 return templateNames;
4612}
4613
4614/*! Searches a class from within \a context and \a cd and returns its
4615 * definition if found (otherwise nullptr is returned).
4616 */
4618{
4619 ClassDef *result=nullptr;
4620 if (cd==nullptr)
4621 {
4622 return result;
4623 }
4624 FileDef *fd=cd->getFileDef();
4625 SymbolResolver resolver(fd);
4626 if (context && cd!=context)
4627 {
4628 result = const_cast<ClassDef*>(resolver.resolveClass(context,name,true,true));
4629 }
4630 //printf("1. result=%p\n",result);
4631 if (result==nullptr)
4632 {
4633 result = const_cast<ClassDef*>(resolver.resolveClass(cd,name,true,true));
4634 }
4635 //printf("2. result=%p\n",result);
4636 if (result==nullptr) // try direct class, needed for namespaced classes imported via tag files (see bug624095)
4637 {
4638 result = getClass(name);
4639 }
4640 //printf("3. result=%p\n",result);
4641 //printf("** Trying to find %s within context %s class %s result=%s lookup=%p\n",
4642 // qPrint(name),
4643 // context ? qPrint(context->name()) : "<none>",
4644 // cd ? qPrint(cd->name()) : "<none>",
4645 // result ? qPrint(result->name()) : "<none>",
4646 // Doxygen::classLinkedMap->find(name)
4647 // );
4648 return result;
4649}
4650
4651
4652static void findUsedClassesForClass(const Entry *root,
4653 Definition *context,
4654 ClassDefMutable *masterCd,
4655 ClassDefMutable *instanceCd,
4656 bool isArtificial,
4657 const ArgumentList *actualArgs = nullptr,
4658 const TemplateNameMap &templateNames = TemplateNameMap()
4659 )
4660{
4661 AUTO_TRACE();
4662 const ArgumentList &formalArgs = masterCd->templateArguments();
4663 for (auto &mni : masterCd->memberNameInfoLinkedMap())
4664 {
4665 for (auto &mi : *mni)
4666 {
4667 const MemberDef *md=mi->memberDef();
4668 if (md->isVariable() || md->isObjCProperty()) // for each member variable in this class
4669 {
4670 AUTO_TRACE_ADD("Found variable '{}' in class '{}'",md->name(),masterCd->name());
4671 DString type = normalizeNonTemplateArgumentsInString(md->typeString(),masterCd,formalArgs);
4672 DString typedefValue = md->getLanguage()==SrcLangExt::Java ? type : resolveTypeDef(masterCd,type);
4673 if (!typedefValue.empty())
4674 {
4675 type = typedefValue;
4676 }
4677 int pos=0;
4678 DString usedClassName;
4679 DString templSpec;
4680 bool found=false;
4681 // the type can contain template variables, replace them if present
4682 type = substituteTemplateArgumentsInString(type,formalArgs,actualArgs);
4683
4684 //printf(" template substitution gives=%s\n",qPrint(type));
4685 while (!found && extractClassNameFromType(type,pos,usedClassName,templSpec,root->lang)!=-1)
4686 {
4687 // find the type (if any) that matches usedClassName
4688 SymbolResolver resolver(masterCd->getFileDef());
4689 const ClassDefMutable *typeCd = resolver.resolveClassMutable(masterCd,usedClassName,false,true);
4690 //printf("====> usedClassName=%s -> typeCd=%s\n",
4691 // qPrint(usedClassName),typeCd?qPrint(typeCd->name()):"<none>");
4692 if (typeCd)
4693 {
4694 usedClassName = typeCd->name();
4695 }
4696
4697 // replace any namespace aliases
4698 replaceNamespaceAliases(usedClassName);
4699 // add any template arguments to the class
4700 DString usedName = removeRedundantWhiteSpace(usedClassName+templSpec);
4701 //printf(" usedName=%s usedClassName=%s templSpec=%s\n",qPrint(usedName),qPrint(usedClassName),qPrint(templSpec));
4702
4703 TemplateNameMap formTemplateNames;
4704 if (templateNames.empty())
4705 {
4706 formTemplateNames = getTemplateArgumentsInName(formalArgs,usedName.str());
4707 }
4708 BaseInfo bi(usedName,Protection::Public,Specifier::Normal);
4709 findClassRelation(root,context,instanceCd,&bi,formTemplateNames,TemplateInstances,isArtificial);
4710
4711 for (const Argument &arg : masterCd->templateArguments())
4712 {
4713 if (arg.name==usedName) // type is a template argument
4714 {
4715 ClassDef *usedCd = Doxygen::hiddenClassLinkedMap->find(usedName);
4716 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4717 if (usedCd==nullptr)
4718 {
4719 usedCdm = toClassDefMutable(
4720 Doxygen::hiddenClassLinkedMap->add(usedName,
4722 masterCd->getDefFileName(),masterCd->getDefLine(),
4723 masterCd->getDefColumn(),
4724 usedName,
4725 ClassDef::Class)));
4726 if (usedCdm)
4727 {
4728 //printf("making %s a template argument!!!\n",qPrint(usedCd->name()));
4729 usedCdm->makeTemplateArgument();
4730 usedCdm->setUsedOnly(true);
4731 usedCdm->setLanguage(masterCd->getLanguage());
4732 usedCd = usedCdm;
4733 }
4734 }
4735 if (usedCd)
4736 {
4737 found=true;
4738 AUTO_TRACE_ADD("case 1: adding used class '{}'", usedCd->name());
4739 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4740 if (usedCdm)
4741 {
4742 if (isArtificial) usedCdm->setArtificial(true);
4743 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4744 }
4745 }
4746 }
4747 }
4748
4749 if (!found)
4750 {
4751 ClassDef *usedCd=findClassWithinClassContext(context,masterCd,usedName);
4752 //printf("Looking for used class %s: result=%s master=%s\n",
4753 // qPrint(usedName),usedCd?qPrint(usedCd->name()):"<none>",masterCd?qPrint(masterCd->name()):"<none>");
4754
4755 if (usedCd)
4756 {
4757 found=true;
4758 AUTO_TRACE_ADD("case 2: adding used class '{}'", usedCd->name());
4759 instanceCd->addUsedClass(usedCd,md->name(),md->protection()); // class exists
4760 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4761 if (usedCdm)
4762 {
4763 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4764 }
4765 }
4766 }
4767 }
4768 if (!found && !type.empty()) // used class is not documented in any scope
4769 {
4771 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4772 if (usedCd==nullptr && !Config_getBool(HIDE_UNDOC_RELATIONS))
4773 {
4774 if (type.endsWith("(*") || type.endsWith("(^")) // type is a function pointer
4775 {
4776 type+=md->argsString();
4777 }
4778 AUTO_TRACE_ADD("New undocumented used class '{}'", type);
4779 usedCdm = toClassDefMutable(
4782 masterCd->getDefFileName(),masterCd->getDefLine(),
4783 masterCd->getDefColumn(),
4784 type,ClassDef::Class)));
4785 if (usedCdm)
4786 {
4787 usedCdm->setUsedOnly(true);
4788 usedCdm->setLanguage(masterCd->getLanguage());
4789 usedCd = usedCdm;
4790 }
4791 }
4792 if (usedCd)
4793 {
4794 AUTO_TRACE_ADD("case 3: adding used class '{}'", usedCd->name());
4795 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4796 if (usedCdm)
4797 {
4798 if (isArtificial) usedCdm->setArtificial(true);
4799 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4800 }
4801 }
4802 }
4803 }
4804 }
4805 }
4806}
4807
4809 const Entry *root,
4810 Definition *context,
4811 ClassDefMutable *masterCd,
4812 ClassDefMutable *instanceCd,
4814 bool isArtificial,
4815 const ArgumentList *actualArgs = nullptr,
4816 const TemplateNameMap &templateNames=TemplateNameMap()
4817 )
4818{
4819 AUTO_TRACE("name={}",root->name);
4820 // The base class could ofcouse also be a non-nested class
4821 const ArgumentList &formalArgs = masterCd->templateArguments();
4822 for (const BaseInfo &bi : root->extends)
4823 {
4824 //printf("masterCd=%s bi.name='%s' #actualArgs=%d\n",
4825 // qPrint(masterCd->localName()),qPrint(bi.name),actualArgs ? (int)actualArgs->size() : -1);
4826 TemplateNameMap formTemplateNames;
4827 if (templateNames.empty())
4828 {
4829 formTemplateNames = getTemplateArgumentsInName(formalArgs,bi.name.str());
4830 }
4831 BaseInfo tbi = bi;
4832 tbi.name = substituteTemplateArgumentsInString(bi.name,formalArgs,actualArgs);
4833 //printf("masterCd=%p instanceCd=%p bi->name=%s tbi.name=%s\n",(void*)masterCd,(void*)instanceCd,qPrint(bi.name),qPrint(tbi.name));
4834
4835 if (mode==DocumentedOnly)
4836 {
4837 // find a documented base class in the correct scope
4838 if (!findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,DocumentedOnly,isArtificial))
4839 {
4840 // 1.8.2: decided to show inheritance relations even if not documented,
4841 // we do make them artificial, so they do not appear in the index
4842 //if (!Config_getBool(HIDE_UNDOC_RELATIONS))
4843 bool b = Config_getBool(HIDE_UNDOC_RELATIONS) ? true : isArtificial;
4844 //{
4845 // no documented base class -> try to find an undocumented one
4846 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,Undocumented,b);
4847 //}
4848 }
4849 }
4850 else if (mode==TemplateInstances)
4851 {
4852 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,TemplateInstances,isArtificial);
4853 }
4854 }
4855}
4856
4857//----------------------------------------------------------------------
4858
4859static void findTemplateInstanceRelation(const Entry *root,
4860 Definition *context,
4861 ClassDefMutable *templateClass,const DString &templSpec,
4862 const TemplateNameMap &templateNames,
4863 bool isArtificial)
4864{
4865 AUTO_TRACE("Derived from template '{}' with parameters '{}' isArtificial={}",
4866 templateClass->name(),templSpec,isArtificial);
4867
4868 DString tempArgsStr = tempArgListToString(templateClass->templateArguments(),root->lang,false);
4869 bool existingClass = templSpec==tempArgsStr;
4870 if (existingClass) return; // avoid recursion
4871
4872 bool freshInstance=false;
4873 ClassDefMutable *instanceClass = toClassDefMutable(
4874 templateClass->insertTemplateInstance(
4875 root->fileName,root->startLine,root->startColumn,templSpec,freshInstance));
4876 if (instanceClass)
4877 {
4878 if (freshInstance)
4879 {
4880 instanceClass->setArtificial(true);
4881 instanceClass->setLanguage(root->lang);
4882
4883 AUTO_TRACE_ADD("found fresh instance '{}'",instanceClass->name());
4884 instanceClass->setTemplateBaseClassNames(templateNames);
4885
4886 // search for new template instances caused by base classes of
4887 // instanceClass
4888 auto it_pair = g_classEntries.equal_range(templateClass->name().str());
4889 for (auto it=it_pair.first ; it!=it_pair.second ; ++it)
4890 {
4891 const Entry *templateRoot = it->second;
4892 AUTO_TRACE_ADD("template root found '{}' templSpec='{}'",templateRoot->name,templSpec);
4893 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(root->lang,templSpec);
4894 findBaseClassesForClass(templateRoot,context,templateClass,instanceClass,
4895 TemplateInstances,isArtificial,templArgs.get(),templateNames);
4896
4897 findUsedClassesForClass(templateRoot,context,templateClass,instanceClass,
4898 isArtificial,templArgs.get(),templateNames);
4899 }
4900 }
4901 else
4902 {
4903 AUTO_TRACE_ADD("instance already exists");
4904 }
4905 }
4906}
4907
4908//----------------------------------------------------------------------
4909
4910static void resolveTemplateInstanceInType(const Entry *root,const Definition *scope,const MemberDef *md)
4911{
4912 // For a statement like 'using X = T<A>', add a template instance 'T<A>' as a symbol, so it can
4913 // be used to match arguments (see issue #11111)
4914 AUTO_TRACE();
4915 DString ttype = md->typeString();
4916 ttype.stripPrefix("typedef ");
4917 if (size_t ti=ttype.find('<'); ti!=DString::npos)
4918 {
4919 DString templateClassName = ttype.left(ti);
4920 SymbolResolver resolver(root->fileDef());
4921 ClassDefMutable *baseClass = resolver.resolveClassMutable(scope ? scope : Doxygen::globalScope,
4922 templateClassName, true, true);
4923 AUTO_TRACE_ADD("templateClassName={} baseClass={}",templateClassName,baseClass?baseClass->name():"<none>");
4924 if (baseClass)
4925 {
4926 const ArgumentList &tl = baseClass->templateArguments();
4927 TemplateNameMap templateNames = getTemplateArgumentsInName(tl,templateClassName.str());
4929 baseClass,
4930 ttype.mid(ti),
4931 templateNames,
4932 baseClass->isArtificial());
4933 }
4934 }
4935}
4936
4937//----------------------------------------------------------------------
4938
4939static bool isRecursiveBaseClass(const DString &scope,const DString &name)
4940{
4941 DString n=name;
4942 if (size_t index=n.find('<'); index!=DString::npos)
4943 {
4944 n=n.left(index);
4945 }
4946 bool result = rightScopeMatch(scope,n);
4947 return result;
4948}
4949
4951{
4952 if (name.empty()) return 0;
4953 int l = static_cast<int>(name.length());
4954 if (name[l-1]=='>') // search backward to find the matching <, allowing nested <...> and strings.
4955 {
4956 int count=1;
4957 int i=l-2;
4958 char insideQuote=0;
4959 while (count>0 && i>=0)
4960 {
4961 char c = name[i--];
4962 switch (c)
4963 {
4964 case '>': if (!insideQuote) count++; break;
4965 case '<': if (!insideQuote) count--; break;
4966 case '\'': if (!insideQuote) insideQuote=c;
4967 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
4968 break;
4969 case '"': if (!insideQuote) insideQuote=c;
4970 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
4971 break;
4972 default: break;
4973 }
4974 }
4975 if (i>=0) l=i+1;
4976 }
4977 return l;
4978}
4979
4981 const Entry *root,
4982 Definition *context,
4983 ClassDefMutable *cd,
4984 const BaseInfo *bi,
4985 const TemplateNameMap &templateNames,
4987 bool isArtificial
4988 )
4989{
4990 AUTO_TRACE("name={} base={} isArtificial={} mode={}",cd->name(),bi->name,isArtificial,(int)mode);
4991
4992 DString biName=bi->name;
4993 bool explicitGlobalScope=false;
4994 if (biName.startsWith("::")) // explicit global scope
4995 {
4996 biName=biName.mid(2);
4997 explicitGlobalScope=true;
4998 }
4999
5000 Entry *parentNode=root->parent();
5001 bool lastParent=false;
5002 do // for each parent scope, starting with the largest scope
5003 // (in case of nested classes)
5004 {
5005 DString scopeName= parentNode ? parentNode->name : DString();
5006 int scopeOffset=explicitGlobalScope ? 0 : static_cast<int>(scopeName.length());
5007 do // try all parent scope prefixes, starting with the largest scope
5008 {
5009 //printf("scopePrefix='%s' biName='%s'\n",
5010 // qPrint(scopeName.left(scopeOffset)),qPrint(biName));
5011
5012 DString baseClassName=biName;
5013 if (scopeOffset>0)
5014 {
5015 baseClassName.prepend(scopeName.left(scopeOffset)+"::");
5016 }
5017 if (root->lang==SrcLangExt::CSharp)
5018 {
5019 baseClassName = mangleCSharpGenericName(baseClassName);
5020 }
5021 AUTO_TRACE_ADD("cd='{}' baseClassName='{}'",cd->name(),baseClassName);
5022 SymbolResolver resolver(cd->getFileDef());
5023 ClassDefMutable *baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
5024 baseClassName,
5025 mode==Undocumented,
5026 true
5027 );
5028 const MemberDef *baseClassTypeDef = resolver.getTypedef();
5029 DString templSpec = resolver.getTemplateSpec();
5030 //printf("baseClassName=%s baseClass=%p cd=%p explicitGlobalScope=%d\n",
5031 // qPrint(baseClassName),baseClass,cd,explicitGlobalScope);
5032 //printf(" scope='%s' baseClassName='%s' baseClass=%s templSpec=%s\n",
5033 // cd ? qPrint(cd->name()):"<none>",
5034 // qPrint(baseClassName),
5035 // baseClass?qPrint(baseClass->name()):"<none>",
5036 // qPrint(templSpec)
5037 // );
5038 //if (baseClassName.left(root->name.length())!=root->name ||
5039 // baseClassName.at(root->name.length())!='<'
5040 // ) // Check for base class with the same name.
5041 // // If found then look in the outer scope for a match
5042 // // and prevent recursion.
5043 if (!isRecursiveBaseClass(root->name,baseClassName)
5044 || explicitGlobalScope
5045 // sadly isRecursiveBaseClass always true for UNO IDL ifc/svc members
5046 // (i.e. this is needed for addInterfaceOrServiceToServiceOrSingleton)
5047 || (root->lang==SrcLangExt::IDL &&
5048 (root->section.isExportedInterface() ||
5049 root->section.isIncludedService()))
5050 )
5051 {
5052 AUTO_TRACE_ADD("class relation '{}' inherited/used by '{}' found prot={} virt={} templSpec='{}'",
5053 baseClassName, root->name, bi->prot, bi->virt, templSpec);
5054
5055 int i=findTemplateSpecializationPosition(baseClassName);
5056 size_t si=baseClassName.rfind("::",i);
5057 if (si==DString::npos) si=0;
5058 if (baseClass==nullptr && static_cast<size_t>(i)!=baseClassName.length())
5059 // base class has template specifiers
5060 {
5061 // TODO: here we should try to find the correct template specialization
5062 // but for now, we only look for the unspecialized base class.
5063 int e=findEndOfTemplate(baseClassName,i+1);
5064 //printf("baseClass==0 i=%d e=%d\n",i,e);
5065 if (e!=-1) // end of template was found at e
5066 {
5067 templSpec = removeRedundantWhiteSpace(baseClassName.mid(i,e-i));
5068 baseClassName = baseClassName.left(i)+baseClassName.mid(e);
5069 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
5070 baseClassName,
5071 mode==Undocumented,
5072 true
5073 );
5074 baseClassTypeDef = resolver.getTypedef();
5075 //printf("baseClass=%p -> baseClass=%s templSpec=%s\n",
5076 // baseClass,qPrint(baseClassName),qPrint(templSpec));
5077 }
5078 }
5079 else if (baseClass && !templSpec.empty()) // we have a known class, but also
5080 // know it is a template, so see if
5081 // we can also link to the explicit
5082 // instance (for instance if a class
5083 // derived from a template argument)
5084 {
5085 //printf("baseClass=%s templSpec=%s\n",qPrint(baseClass->name()),qPrint(templSpec));
5086 ClassDefMutable *templClass=getClassMutable(baseClass->name()+templSpec);
5087 if (templClass)
5088 {
5089 // use the template instance instead of the template base.
5090 baseClass = templClass;
5091 templSpec.clear();
5092 }
5093 }
5094
5095 //printf("cd=%p baseClass=%p\n",cd,baseClass);
5096 bool found=baseClass!=nullptr && (baseClass!=cd || mode==TemplateInstances);
5097 AUTO_TRACE_ADD("1. found={}",found);
5098 if (!found && si!=DString::npos)
5099 {
5100 // replace any namespace aliases
5101 replaceNamespaceAliases(baseClassName);
5102 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
5103 baseClassName,
5104 mode==Undocumented,
5105 true
5106 );
5107 baseClassTypeDef = resolver.getTypedef();
5108 found=baseClass!=nullptr && baseClass!=cd;
5109 if (found) templSpec = resolver.getTemplateSpec();
5110 }
5111 AUTO_TRACE_ADD("2. found={}",found);
5112
5113 if (!found)
5114 {
5115 baseClass=toClassDefMutable(findClassWithinClassContext(context,cd,baseClassName));
5116 //printf("findClassWithinClassContext(%s,%s)=%p\n",
5117 // qPrint(cd->name()),qPrint(baseClassName),baseClass);
5118 found = baseClass!=nullptr && baseClass!=cd;
5119
5120 }
5121 AUTO_TRACE_ADD("3. found={}",found);
5122 if (!found)
5123 {
5124 // for PHP the "use A\B as C" construct map class C to A::B, so we lookup
5125 // the class name also in the alias mapping.
5126 auto it = Doxygen::namespaceAliasMap.find(baseClassName.str());
5127 if (it!=Doxygen::namespaceAliasMap.end()) // see if it is indeed a class.
5128 {
5129 baseClass=getClassMutable(it->second.alias);
5130 found = baseClass!=nullptr && baseClass!=cd;
5131 }
5132 }
5133 bool isATemplateArgument = templateNames.find(biName.str())!=templateNames.end();
5134
5135 AUTO_TRACE_ADD("4. found={}",found);
5136 if (found)
5137 {
5138 AUTO_TRACE_ADD("Documented base class '{}' templSpec='{}'",biName,templSpec);
5139 // add base class to this class
5140
5141 // if templSpec is not empty then we should "instantiate"
5142 // the template baseClass. A new ClassDef should be created
5143 // to represent the instance. To be able to add the (instantiated)
5144 // members and documentation of a template class
5145 // (inserted in that template class at a later stage),
5146 // the template should know about its instances.
5147 // the instantiation process, should be done in a recursive way,
5148 // since instantiating a template may introduce new inheritance
5149 // relations.
5150 if (!templSpec.empty() && mode==TemplateInstances)
5151 {
5152 // if baseClass is actually a typedef then we should not
5153 // instantiate it, since typedefs are in a different namespace
5154 // see bug531637 for an example where this would otherwise hang
5155 // Doxygen
5156 if (baseClassTypeDef==nullptr)
5157 {
5158 //printf(" => findTemplateInstanceRelation: %s\n",qPrint(baseClass->name()));
5159 findTemplateInstanceRelation(root,context,baseClass,templSpec,templateNames,baseClass->isArtificial());
5160 }
5161 }
5162 else if (mode==DocumentedOnly || mode==Undocumented)
5163 {
5164 //printf(" => insert base class\n");
5165 DString usedName;
5166 if (baseClassTypeDef)
5167 {
5168 usedName=biName;
5169 //printf("***** usedName=%s templSpec=%s\n",qPrint(usedName),qPrint(templSpec));
5170 }
5171 Protection prot = bi->prot;
5172 if (Config_getBool(SIP_SUPPORT)) prot=Protection::Public;
5173 if (cd!=baseClass && !cd->isSubClass(baseClass) && baseClass->isBaseClass(cd,true,templSpec)==0) // check for recursion, see bug690787
5174 {
5175 AUTO_TRACE_ADD("insertBaseClass name={} prot={} virt={} templSpec={}",usedName,prot,bi->virt,templSpec);
5176 cd->insertBaseClass(baseClass,usedName,prot,bi->virt,templSpec);
5177 // add this class as super class to the base class
5178 baseClass->insertSubClass(cd,prot,bi->virt,templSpec);
5179 }
5180 else
5181 {
5182 warn(root->fileName,root->startLine,
5183 "Detected potential recursive class relation "
5184 "between class {} and base class {}!",
5185 cd->name(),baseClass->name()
5186 );
5187 }
5188 }
5189 return true;
5190 }
5191 else if (mode==Undocumented && (scopeOffset==0 || isATemplateArgument))
5192 {
5193 AUTO_TRACE_ADD("New undocumented base class '{}' baseClassName='{}' templSpec='{}' isArtificial={}",
5194 biName,baseClassName,templSpec,isArtificial);
5195 baseClass=nullptr;
5196 if (isATemplateArgument)
5197 {
5198 baseClass = toClassDefMutable(Doxygen::hiddenClassLinkedMap->find(baseClassName));
5199 if (baseClass==nullptr) // not found (or alias)
5200 {
5201 baseClass= toClassDefMutable(
5202 Doxygen::hiddenClassLinkedMap->add(baseClassName,
5203 createClassDef(root->fileName,root->startLine,root->startColumn,
5204 baseClassName,
5205 ClassDef::Class)));
5206 if (baseClass) // really added (not alias)
5207 {
5208 if (isArtificial) baseClass->setArtificial(true);
5209 baseClass->setLanguage(root->lang);
5210 }
5211 }
5212 }
5213 else
5214 {
5215 baseClass = toClassDefMutable(Doxygen::classLinkedMap->find(baseClassName));
5216 //printf("*** classDDict->find(%s)=%p biName=%s templSpec=%s\n",
5217 // qPrint(baseClassName),baseClass,qPrint(biName),qPrint(templSpec));
5218 if (baseClass==nullptr) // not found (or alias)
5219 {
5220 baseClass = toClassDefMutable(
5221 Doxygen::classLinkedMap->add(baseClassName,
5222 createClassDef(root->fileName,root->startLine,root->startColumn,
5223 baseClassName,
5224 ClassDef::Class)));
5225 if (baseClass) // really added (not alias)
5226 {
5227 if (isArtificial) baseClass->setArtificial(true);
5228 baseClass->setLanguage(root->lang);
5229 si = baseClassName.rfind("::");
5230 if (si!=DString::npos) // class is nested
5231 {
5232 Definition *sd = findScopeFromQualifiedName(Doxygen::globalScope,baseClassName.left(si),nullptr,root->tagInfo());
5233 if (sd==nullptr || sd==Doxygen::globalScope) // outer scope not found
5234 {
5235 baseClass->setArtificial(true); // see bug678139
5236 }
5237 }
5238 }
5239 }
5240 }
5241 if (baseClass)
5242 {
5243 if (biName.endsWith("-p"))
5244 {
5245 biName="<"+biName.left(biName.length()-2)+">";
5246 }
5247 if (!cd->isSubClass(baseClass) && cd!=baseClass && cd->isBaseClass(baseClass,true,templSpec)==0) // check for recursion
5248 {
5249 AUTO_TRACE_ADD("insertBaseClass name={} prot={} virt={} templSpec={}",biName,bi->prot,bi->virt,templSpec);
5250 // add base class to this class
5251 cd->insertBaseClass(baseClass,biName,bi->prot,bi->virt,templSpec);
5252 // add this class as super class to the base class
5253 baseClass->insertSubClass(cd,bi->prot,bi->virt,templSpec);
5254 }
5255 // the undocumented base was found in this file
5256 baseClass->insertUsedFile(root->fileDef());
5257
5258 Definition *scope = buildScopeFromQualifiedName(baseClass->name(),root->lang,nullptr);
5259 if (scope!=baseClass)
5260 {
5261 baseClass->setOuterScope(scope);
5262 }
5263
5264 if (baseClassName.endsWith("-p"))
5265 {
5267 }
5268 return true;
5269 }
5270 else
5271 {
5272 AUTO_TRACE_ADD("Base class '{}' not created (alias?)",biName);
5273 }
5274 }
5275 else
5276 {
5277 AUTO_TRACE_ADD("Base class '{}' not found",biName);
5278 }
5279 }
5280 else
5281 {
5282 if (mode!=TemplateInstances)
5283 {
5284 warn(root->fileName,root->startLine,
5285 "Detected potential recursive class relation "
5286 "between class {} and base class {}!",
5287 root->name,baseClassName
5288 );
5289 }
5290 // for mode==TemplateInstance this case is quite common and
5291 // indicates a relation between a template class and a template
5292 // instance with the same name.
5293 }
5294 if (scopeOffset==0)
5295 {
5296 scopeOffset=-1;
5297 }
5298 else
5299 {
5300 size_t o = scopeName.rfind("::",scopeOffset-1);
5301 scopeOffset = o!=DString::npos ? static_cast<int>(o) : 0;
5302 }
5303 //printf("new scopeOffset='%d'",scopeOffset);
5304 } while (scopeOffset>=0);
5305
5306 if (parentNode==nullptr)
5307 {
5308 lastParent=true;
5309 }
5310 else
5311 {
5312 parentNode=parentNode->parent();
5313 }
5314 } while (lastParent);
5315
5316 return false;
5317}
5318
5319//----------------------------------------------------------------------
5320// Computes the base and super classes for each class in the tree
5321
5322static bool isClassSection(const Entry *root)
5323{
5324 if ( !root->name.empty() )
5325 {
5326 if (root->section.isCompound())
5327 // is it a compound (class, struct, union, interface ...)
5328 {
5329 return true;
5330 }
5331 else if (root->section.isCompoundDoc())
5332 // is it a documentation block with inheritance info.
5333 {
5334 bool hasExtends = !root->extends.empty();
5335 if (hasExtends) return true;
5336 }
5337 }
5338 return false;
5339}
5340
5341
5342/*! Builds a dictionary of all entry nodes in the tree starting with \a root
5343 */
5344static void findClassEntries(const Entry *root)
5345{
5346 if (isClassSection(root))
5347 {
5348 g_classEntries.emplace(root->name.str(),root);
5349 }
5350 for (const auto &e : root->children()) findClassEntries(e.get());
5351}
5352
5353static DString extractClassName(const Entry *root)
5354{
5355 // strip any anonymous scopes first
5358 if (size_t i=bName.find('<'); (root->lang==SrcLangExt::CSharp || root->lang==SrcLangExt::Java) && i!=DString::npos)
5359 {
5360 // a Java/C# generic class looks like a C++ specialization, so we need to strip the
5361 // template part before looking for matches
5362 if (root->lang==SrcLangExt::CSharp)
5363 {
5364 bName = mangleCSharpGenericName(root->name);
5365 }
5366 else
5367 {
5368 bName = bName.left(i);
5369 }
5370 }
5371 return bName;
5372}
5373
5374/*! Using the dictionary build by findClassEntries(), this
5375 * function will look for additional template specialization that
5376 * exists as inheritance relations only. These instances will be
5377 * added to the template they are derived from.
5378 */
5380{
5381 AUTO_TRACE();
5382 ClassDefSet visitedClasses;
5383 for (const auto &[name,root] : g_classEntries)
5384 {
5385 DString bName = extractClassName(root);
5386 ClassDefMutable *cdm = getClassMutable(bName);
5387 if (cdm)
5388 {
5389 findBaseClassesForClass(root,cdm,cdm,cdm,TemplateInstances,false);
5390 }
5391 }
5392}
5393
5395{
5396 AUTO_TRACE("root->name={} cd={}",root->name,cd->name());
5397 size_t i = root->name.find('<');
5398 size_t j = root->name.rfind('>');
5399 size_t k = j!=DString::npos ? root->name.find("::",j+1) : DString::npos; // A<T::B> => ok, A<T>::B => nok
5400 if (i!=DString::npos && j!=DString::npos && k==DString::npos && root->lang!=SrcLangExt::CSharp && root->lang!=SrcLangExt::Java)
5401 {
5402 ClassDefMutable *master = getClassMutable(root->name.left(i));
5403 if (master && master!=cd && !cd->templateMaster())
5404 {
5405 AUTO_TRACE_ADD("class={} master={}",cd->name(),cd->templateMaster()?cd->templateMaster()->name():"<none>",master->name());
5406 cd->setTemplateMaster(master);
5407 master->insertExplicitTemplateInstance(cd,root->name.mid(i));
5408 }
5409 }
5410}
5411
5413{
5414 AUTO_TRACE();
5415 for (const auto &[name,root] : g_classEntries)
5416 {
5417 DString bName = extractClassName(root);
5418 ClassDefMutable *cdm = getClassMutable(bName);
5419 if (cdm)
5420 {
5421 findUsedClassesForClass(root,cdm,cdm,cdm,true);
5423 cdm->addTypeConstraints();
5424 }
5425 }
5426}
5427
5429{
5430 AUTO_TRACE();
5431 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5432 {
5433 if (!nd->hasDocumentation())
5434 {
5435 if ((EntryType::guessSection(nd->getDefFileName()).isHeader() ||
5436 nd->getLanguage() == SrcLangExt::Fortran) && // Fortran doesn't have header files.
5437 !Config_getBool(HIDE_UNDOC_NAMESPACES) // undocumented namespaces are visible
5438 )
5439 {
5440 warn_undoc(nd->getDefFileName(),nd->getDefLine(), "{} {} is not documented.",
5441 nd->getLanguage() == SrcLangExt::Fortran ? "Module" : "Namespace",
5442 nd->name());
5443 }
5444 }
5445 }
5446}
5447
5449{
5450 AUTO_TRACE();
5451 for (const auto &[name,root] : g_classEntries)
5452 {
5453 DString bName = extractClassName(root);
5454 ClassDefMutable *cd = getClassMutable(bName);
5455 if (cd)
5456 {
5457 findBaseClassesForClass(root,cd,cd,cd,DocumentedOnly,false);
5458 }
5459 size_t numMembers = cd ? cd->memberNameInfoLinkedMap().size() : 0;
5460 if ((cd==nullptr || (!cd->hasDocumentation() && !cd->isReference())) && numMembers>0 && !bName.endsWith("::"))
5461 {
5462 if (!root->name.empty() && root->name.find('@')==DString::npos && // normal name
5463 (EntryType::guessSection(root->fileName).isHeader() ||
5464 Config_getBool(EXTRACT_LOCAL_CLASSES)) && // not defined in source file
5465 protectionLevelVisible(root->protection) && // hidden by protection
5466 !Config_getBool(HIDE_UNDOC_CLASSES) // undocumented class are visible
5467 )
5468 warn_undoc(root->fileName,root->startLine, "Compound {} is not documented.", root->name);
5469 }
5470 }
5471}
5472
5474{
5475 AUTO_TRACE();
5476 for (const auto &[name,root] : g_classEntries)
5477 {
5481 // strip any anonymous scopes first
5482 if (cd && !cd->getTemplateInstances().empty())
5483 {
5484 AUTO_TRACE_ADD("Template class '{}'",cd->name());
5485 for (const auto &ti : cd->getTemplateInstances()) // for each template instance
5486 {
5487 ClassDefMutable *tcd=toClassDefMutable(ti.classDef);
5488 if (tcd)
5489 {
5490 AUTO_TRACE_ADD("Template instance '{}'",tcd->name());
5491 DString templSpec = ti.templSpec;
5492 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(tcd->getLanguage(),templSpec);
5493 for (const BaseInfo &bi : root->extends)
5494 {
5495 // check if the base class is a template argument
5496 BaseInfo tbi = bi;
5497 const ArgumentList &tl = cd->templateArguments();
5498 if (!tl.empty())
5499 {
5500 TemplateNameMap baseClassNames = tcd->getTemplateBaseClassNames();
5501 TemplateNameMap templateNames = getTemplateArgumentsInName(tl,bi.name.str());
5502 // for each template name that we inherit from we need to
5503 // substitute the formal with the actual arguments
5504 TemplateNameMap actualTemplateNames;
5505 for (const auto &tn_kv : templateNames)
5506 {
5507 size_t templIndex = tn_kv.second;
5508 Argument actArg;
5509 bool hasActArg=false;
5510 if (templIndex<templArgs->size())
5511 {
5512 actArg=templArgs->at(templIndex);
5513 hasActArg=true;
5514 }
5515 if (hasActArg &&
5516 baseClassNames.find(actArg.type.str())!=baseClassNames.end() &&
5517 actualTemplateNames.find(actArg.type.str())==actualTemplateNames.end()
5518 )
5519 {
5520 actualTemplateNames.emplace(actArg.type.str(),static_cast<int>(templIndex));
5521 }
5522 }
5523
5524 tbi.name = substituteTemplateArgumentsInString(bi.name,tl,templArgs.get());
5525 // find a documented base class in the correct scope
5526 if (!findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,DocumentedOnly,false))
5527 {
5528 // no documented base class -> try to find an undocumented one
5529 findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,Undocumented,true);
5530 }
5531 }
5532 }
5533 }
5534 }
5535 }
5536 }
5537}
5538
5539//-----------------------------------------------------------------------
5540// compute the references (anchors in HTML) for each function in the file
5541
5543{
5544 AUTO_TRACE();
5545 for (const auto &cd : *Doxygen::classLinkedMap)
5546 {
5547 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5548 if (cdm)
5549 {
5550 cdm->computeAnchors();
5551 }
5552 }
5553 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5554 {
5555 for (const auto &fd : *fn)
5556 {
5557 fd->computeAnchors();
5558 }
5559 }
5560 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5561 {
5563 if (ndm)
5564 {
5565 ndm->computeAnchors();
5566 }
5567 }
5568 for (const auto &gd : *Doxygen::groupLinkedMap)
5569 {
5570 gd->computeAnchors();
5571 }
5572}
5573
5574//----------------------------------------------------------------------
5575
5576
5577template<typename Func>
5578static void applyToAllDefinitions(Func func)
5579{
5580 for (const auto &cd : *Doxygen::classLinkedMap)
5581 {
5582 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5583 if (cdm)
5584 {
5585 func(cdm);
5586 }
5587 }
5588
5589 for (const auto &cd : *Doxygen::conceptLinkedMap)
5590 {
5591 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
5592 if (cdm)
5593 {
5594 func(cdm);
5595 }
5596 }
5597
5598 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5599 {
5600 for (const auto &fd : *fn)
5601 {
5602 func(fd.get());
5603 }
5604 }
5605
5606 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5607 {
5609 if (ndm)
5610 {
5611 func(ndm);
5612 }
5613 }
5614
5615 for (const auto &gd : *Doxygen::groupLinkedMap)
5616 {
5617 func(gd.get());
5618 }
5619
5620 for (const auto &pd : *Doxygen::pageLinkedMap)
5621 {
5622 func(pd.get());
5623 }
5624
5625 for (const auto &dd : *Doxygen::dirLinkedMap)
5626 {
5627 func(dd.get());
5628 }
5629
5630 func(&ModuleManager::instance());
5631}
5632
5633//----------------------------------------------------------------------
5634
5636{
5637 AUTO_TRACE();
5638 applyToAllDefinitions([](auto* obj) { obj->addRequirementReferences(); });
5639}
5640
5641//----------------------------------------------------------------------
5642
5644{
5645 AUTO_TRACE();
5646 applyToAllDefinitions([](auto* obj) { obj->addListReferences(); });
5647}
5648
5649
5650//----------------------------------------------------------------------
5651
5653{
5654 AUTO_TRACE();
5656 {
5657 rl->generatePage();
5658 }
5659}
5660
5661//----------------------------------------------------------------------
5662// Copy the documentation in entry 'root' to member definition 'md' and
5663// set the function declaration of the member to 'funcDecl'. If the boolean
5664// over_load is set the standard overload text is added.
5665
5666static void addMemberDocs(const Entry *root,
5667 MemberDefMutable *md, const DString &funcDecl,
5668 const ArgumentList *al,
5669 bool over_load,
5670 TypeSpecifier spec
5671 )
5672{
5673 if (md==nullptr) return;
5674 AUTO_TRACE("scope='{}' name='{}' args='{}' funcDecl='{}' mSpec={}",
5675 root->parent()->name,md->name(),md->argsString(),funcDecl,spec);
5676 if (!root->section.isDoc()) // @fn or @var does not need to specify the complete definition, so don't overwrite it
5677 {
5678 DString fDecl=funcDecl;
5679 // strip extern specifier
5680 fDecl.stripPrefix("extern ");
5681 md->setDefinition(fDecl);
5682 }
5684 md->addQualifiers(root->qualifiers);
5686 const NamespaceDef *nd=md->getNamespaceDef();
5687 DString fullName;
5688 if (cd)
5689 fullName = cd->name();
5690 else if (nd)
5691 fullName = nd->name();
5692
5693 if (!fullName.empty()) fullName+="::";
5694 fullName+=md->name();
5695 FileDef *rfd=root->fileDef();
5696
5697 // TODO determine scope based on root not md
5698 Definition *rscope = md->getOuterScope();
5699
5700 const ArgumentList &mdAl = md->argumentList();
5701 if (al)
5702 {
5703 ArgumentList mergedAl = *al;
5704 //printf("merging arguments (1) docs=%d\n",root->doc.empty());
5705 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedAl,!root->doc.empty());
5706 }
5707 else
5708 {
5709 if (
5710 matchArguments2( md->getOuterScope(), md->getFileDef(),md->typeString(),const_cast<ArgumentList*>(&mdAl),
5711 rscope,rfd,root->type,&root->argList,
5712 true, root->lang
5713 )
5714 )
5715 {
5716 //printf("merging arguments (2)\n");
5717 ArgumentList mergedArgList = root->argList;
5718 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.empty());
5719 }
5720 }
5721 if (over_load) // the \overload keyword was used
5722 {
5724 if (!root->doc.empty())
5725 {
5726 doc+="<p>";
5727 doc+=root->doc;
5728 }
5729 md->setDocumentation(doc,root->docFile,root->docLine);
5731 md->setDocsForDefinition(!root->proto);
5732 }
5733 else
5734 {
5735 //printf("overwrite!\n");
5736 md->setDocumentation(root->doc,root->docFile,root->docLine);
5737 md->setDocsForDefinition(!root->proto);
5738
5739 //printf("overwrite!\n");
5740 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
5741
5742 if (
5743 (md->inbodyDocumentation().empty() ||
5744 !root->parent()->name.empty()
5745 ) && !root->inbodyDocs.empty()
5746 )
5747 {
5749 }
5750 }
5751
5752 //printf("initializer: '%s'(isEmpty=%d) '%s'(isEmpty=%d)\n",
5753 // qPrint(md->initializer()),md->initializer().empty(),
5754 // qPrint(root->initializer),root->initializer.empty()
5755 // );
5756 std::string rootInit = root->initializer.str();
5757 if (md->initializer().empty() && !rootInit.empty())
5758 {
5759 //printf("setInitializer\n");
5760 md->setInitializer(rootInit);
5761 }
5762 if (md->requiresClause().empty() && !root->req.empty())
5763 {
5764 md->setRequiresClause(root->req);
5765 }
5766
5767 md->setMaxInitLines(root->initLines);
5768
5769 if (rfd)
5770 {
5771 if ((md->getStartBodyLine()==-1 && root->bodyLine!=-1)
5772 )
5773 {
5774 //printf("Setting new body segment [%d,%d]\n",root->bodyLine,root->endBodyLine);
5775 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
5776 md->setBodyDef(rfd);
5777 }
5778
5779 md->setRefItems(root->sli);
5780 md->setRequirementReferences(root->rqli);
5781 }
5782
5784 md->addQualifiers(root->qualifiers);
5785
5786 md->mergeMemberSpecifiers(spec);
5788 addMemberToGroups(root,md);
5790 if (cd) cd->insertUsedFile(rfd);
5791 //printf("root->mGrpId=%d\n",root->mGrpId);
5792 if (root->mGrpId!=-1)
5793 {
5794 if (md->getMemberGroupId()!=-1)
5795 {
5796 if (md->getMemberGroupId()!=root->mGrpId)
5797 {
5798 warn(root->fileName,root->startLine,
5799 "member {} belongs to two different groups. The second one found here will be ignored.",
5800 md->name()
5801 );
5802 }
5803 }
5804 else // set group id
5805 {
5806 //printf("setMemberGroupId=%d md=%s\n",root->mGrpId,qPrint(md->name()));
5807 md->setMemberGroupId(root->mGrpId);
5808 }
5809 }
5810 md->addQualifiers(root->qualifiers);
5811}
5812
5813//----------------------------------------------------------------------
5814// find a class definition given the scope name and (optionally) a
5815// template list specifier
5816
5818 const DString &scopeName)
5819{
5820 SymbolResolver resolver(fd);
5821 const ClassDef *tcd = resolver.resolveClass(nd,scopeName,true,true);
5822 //printf("findClassDefinition(fd=%s,ns=%s,scopeName=%s)='%s'\n",
5823 // qPrint(fd?fd->name():""),qPrint(nd?nd->name():""),
5824 // qPrint(scopeName),qPrint(tcd?tcd->name():""));
5825 return tcd;
5826}
5827
5828//----------------------------------------------------------------------------
5829// Returns true, if the entry belongs to the group of the member definition,
5830// otherwise false.
5831
5832static bool isEntryInGroupOfMember(const Entry *root,const MemberDef *md,bool allowNoGroup=false)
5833{
5834 const GroupDef *gd = md->getGroupDef();
5835 if (!gd)
5836 {
5837 return allowNoGroup;
5838 }
5839
5840 for (const auto &g : root->groups)
5841 {
5842 if (g.groupname == gd->name())
5843 {
5844 return true; // matching group
5845 }
5846 }
5847
5848 return false;
5849}
5850
5851//----------------------------------------------------------------------
5852// Adds the documentation contained in 'root' to a global function
5853// with name 'name' and argument list 'args' (for overloading) and
5854// function declaration 'decl' to the corresponding member definition.
5855
5856static bool findGlobalMember(const Entry *root,
5857 const DString &namespaceName,
5858 const DString &type,
5859 const DString &name,
5860 const DString &tempArg,
5861 const DString &,
5862 const DString &decl,
5863 TypeSpecifier /* spec */)
5864{
5865 AUTO_TRACE("namespace='{}' type='{}' name='{}' tempArg='{}' decl='{}'",namespaceName,type,name,tempArg,decl);
5866 DString n=name;
5867 if (n.empty()) return false;
5868 if (n.find("::")!=DString::npos) return false; // skip undefined class members
5869 MemberName *mn=Doxygen::functionNameLinkedMap->find(n+tempArg); // look in function dictionary
5870 if (mn==nullptr)
5871 {
5872 mn=Doxygen::functionNameLinkedMap->find(n); // try without template arguments
5873 }
5874 if (mn) // function name defined
5875 {
5876 AUTO_TRACE_ADD("Found symbol name");
5877 //int count=0;
5878 bool found=false;
5879 for (const auto &md : *mn)
5880 {
5881 // If the entry has groups, then restrict the search to members which are
5882 // in one of the groups of the entry. If md is not associated with a group yet,
5883 // allow this documentation entry to add the group info.
5884 if (!root->groups.empty() && !isEntryInGroupOfMember(root, md.get(), true))
5885 {
5886 continue;
5887 }
5888
5889 const NamespaceDef *nd=nullptr;
5890 if (md->isAlias() && md->getOuterScope() &&
5891 md->getOuterScope()->definitionType()==Definition::TypeNamespace)
5892 {
5893 nd = toNamespaceDef(md->getOuterScope());
5894 }
5895 else
5896 {
5897 nd = md->getNamespaceDef();
5898 }
5899
5900 // special case for strong enums
5901 size_t enumNamePos=0;
5902 if (nd && md->isEnumValue() && (enumNamePos=namespaceName.rfind("::"))!=DString::npos)
5903 { // md part of a strong enum in a namespace?
5904 DString enumName = namespaceName.mid(enumNamePos+2);
5905 if (namespaceName.left(enumNamePos)==nd->name())
5906 {
5908 if (enumMn)
5909 {
5910 for (const auto &emd : *enumMn)
5911 {
5912 found = emd->isStrong() && md->getEnumScope()==emd.get();
5913 if (found)
5914 {
5915 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,nullptr,false,root->spec);
5916 break;
5917 }
5918 }
5919 }
5920 }
5921 if (found)
5922 {
5923 break;
5924 }
5925 }
5926 else if (nd==nullptr && md->isEnumValue()) // md part of global strong enum?
5927 {
5928 MemberName *enumMn=Doxygen::functionNameLinkedMap->find(namespaceName);
5929 if (enumMn)
5930 {
5931 for (const auto &emd : *enumMn)
5932 {
5933 found = emd->isStrong() && md->getEnumScope()==emd.get();
5934 if (found)
5935 {
5936 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,nullptr,false,root->spec);
5937 break;
5938 }
5939 }
5940 }
5941 }
5942
5943 const FileDef *fd=root->fileDef();
5944 //printf("File %s\n",fd ? qPrint(fd->name()) : "<none>");
5946 if (fd)
5947 {
5948 nl = fd->getUsedNamespaces();
5949 }
5950 //printf("NamespaceList %p\n",nl);
5951
5952 // search in the list of namespaces that are imported via a
5953 // using declaration
5954 bool viaUsingDirective = nd && nl.find(nd->qualifiedName())!=nullptr;
5955
5956 if ((namespaceName.empty() && nd==nullptr) || // not in a namespace
5957 (nd && nd->name()==namespaceName) || // or in the same namespace
5958 viaUsingDirective // member in 'using' namespace
5959 )
5960 {
5961 AUTO_TRACE_ADD("Try to add member '{}' to scope '{}'",md->name(),namespaceName);
5962
5963 NamespaceDef *rnd = nullptr;
5964 if (!namespaceName.empty()) rnd = Doxygen::namespaceLinkedMap->find(namespaceName);
5965
5966 const ArgumentList &mdAl = md.get()->argumentList();
5967 bool matching=
5968 (mdAl.empty() && root->argList.empty()) ||
5969 md->isVariable() || md->isTypedef() || /* in case of function pointers */
5970 matchArguments2(md->getOuterScope(),md->getFileDef(),md->typeString(),&mdAl,
5971 rnd ? rnd : Doxygen::globalScope,fd,root->type,&root->argList,
5972 false,root->lang);
5973
5974 // for template members we need to check if the number of
5975 // template arguments is the same, otherwise we are dealing with
5976 // different functions.
5977 if (matching && !root->tArgLists.empty())
5978 {
5979 const ArgumentList &mdTempl = md->templateArguments();
5980 if (root->tArgLists.back().size()!=mdTempl.size())
5981 {
5982 matching=false;
5983 }
5984 }
5985
5986 //printf("%s<->%s\n",
5987 // qPrint(argListToString(md->argumentList())),
5988 // qPrint(argListToString(root->argList)));
5989
5990 // For static members we also check if the comment block was found in
5991 // the same file. This is needed because static members with the same
5992 // name can be in different files. Thus it would be wrong to just
5993 // put the comment block at the first syntactically matching member. If
5994 // the comment block belongs to a group of the static member, then add
5995 // the documentation even if it is in a different file.
5996 if (matching && md->isStatic() &&
5997 md->getDefFileName()!=root->fileName &&
5998 mn->size()>1 &&
5999 !isEntryInGroupOfMember(root,md.get()))
6000 {
6001 matching = false;
6002 }
6003
6004 // for template member we also need to check the return type and requires
6005 if (!md->templateArguments().empty() && !root->tArgLists.empty())
6006 {
6007 //printf("Comparing return types '%s'<->'%s'\n",
6008 // md->typeString(),type);
6009 //printf("%s: Comparing '%s'<=>'%s'\n",qPrint(md->name()),qPrint(md->requiresClause()),qPrint(root->req));
6010 if (md->templateArguments().size()!=root->tArgLists.back().size() ||
6011 md->typeString()!=type ||
6012 md->requiresClause()!=root->req)
6013 {
6014 //printf(" ---> no matching\n");
6015 matching = false;
6016 }
6017 }
6018
6019 if (matching) // add docs to the member
6020 {
6021 AUTO_TRACE_ADD("Match found");
6022 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,&root->argList,false,root->spec);
6023 found=true;
6024 break;
6025 }
6026 }
6027 }
6028 if (!found && root->relatesType!=RelatesType::Duplicate && root->section.isFunction()) // no match
6029 {
6030 DString fullFuncDecl=decl;
6031 if (!root->argList.empty()) fullFuncDecl+=argListToString(root->argList,true);
6032 DString warnMsg = "no matching file member found for \n"+fullFuncDecl;
6033 if (mn->size()>0)
6034 {
6035 warnMsg+="\nPossible candidates:";
6036 for (const auto &md : *mn)
6037 {
6038 warnMsg+="\n '";
6039 warnMsg+=replaceAnonymousScopes(md->declaration());
6040 warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
6041 }
6042 }
6043 warn(root->fileName,root->startLine, "{}", qPrint(warnMsg));
6044 }
6045 }
6046 else // got docs for an undefined member!
6047 {
6048 if (root->type!="friend class" &&
6049 root->type!="friend struct" &&
6050 root->type!="friend union" &&
6051 root->type!="friend" &&
6052 (!Config_getBool(TYPEDEF_HIDES_STRUCT) ||
6053 root->type.find("typedef ")==DString::npos)
6054 )
6055 {
6056 warn(root->fileName,root->startLine,
6057 "documented symbol '{}' was not declared or defined.",qPrint(decl)
6058 );
6059 }
6060 }
6061 return true;
6062}
6063
6065 const ArgumentLists &srcTempArgLists,
6066 const ArgumentLists &dstTempArgLists
6067 )
6068{
6069 auto srcIt = srcTempArgLists.begin();
6070 auto dstIt = dstTempArgLists.begin();
6071 while (srcIt!=srcTempArgLists.end() && dstIt!=dstTempArgLists.end())
6072 {
6073 if ((*srcIt).size()!=(*dstIt).size()) return true;
6074 ++srcIt;
6075 ++dstIt;
6076 }
6077 return false;
6078}
6079
6080static bool scopeIsTemplate(const Definition *d)
6081{
6082 bool result=false;
6083 //printf("> scopeIsTemplate(%s)\n",qPrint(d?d->name():"null"));
6085 {
6086 auto cd = toClassDef(d);
6087 result = cd->templateArguments().hasParameters() || cd->templateMaster()!=nullptr ||
6089 }
6090 //printf("< scopeIsTemplate=%d\n",result);
6091 return result;
6092}
6093
6095 const ArgumentLists &srcTempArgLists,
6096 const ArgumentLists &dstTempArgLists,
6097 const std::string &src
6098 )
6099{
6100 std::string dst;
6101 static const reg::Ex re(R"(\a\w*)");
6102 reg::Iterator it(src,re);
6104 //printf("type=%s\n",qPrint(sa->type));
6105 size_t p=0;
6106 for (; it!=end ; ++it) // for each word in srcType
6107 {
6108 const auto &match = *it;
6109 size_t i = match.position();
6110 size_t l = match.length();
6111 bool found=false;
6112 dst+=src.substr(p,i-p);
6113 std::string name=match.str();
6114
6115 auto srcIt = srcTempArgLists.begin();
6116 auto dstIt = dstTempArgLists.begin();
6117 while (srcIt!=srcTempArgLists.end() && !found)
6118 {
6119 const ArgumentList *tdAli = nullptr;
6120 std::vector<Argument>::const_iterator tdaIt;
6121 if (dstIt!=dstTempArgLists.end())
6122 {
6123 tdAli = &(*dstIt);
6124 tdaIt = tdAli->begin();
6125 ++dstIt;
6126 }
6127
6128 const ArgumentList &tsaLi = *srcIt;
6129 for (auto tsaIt = tsaLi.begin(); tsaIt!=tsaLi.end() && !found; ++tsaIt)
6130 {
6131 Argument tsa = *tsaIt;
6132 const Argument *tda = nullptr;
6133 if (tdAli && tdaIt!=tdAli->end())
6134 {
6135 tda = &(*tdaIt);
6136 ++tdaIt;
6137 }
6138 //if (tda) printf("tsa=%s|%s tda=%s|%s\n",
6139 // qPrint(tsa.type),qPrint(tsa.name),
6140 // qPrint(tda->type),qPrint(tda->name));
6141 if (name==tsa.name.str())
6142 {
6143 if (tda && tda->name.empty())
6144 {
6145 DString tdaName = tda->name;
6146 DString tdaType = tda->type;
6147 int vc=0;
6148 if (tdaType.startsWith("class ")) vc=6;
6149 else if (tdaType.startsWith("typename ")) vc=9;
6150 if (vc>0) // convert type=="class T" to type=="class" name=="T"
6151 {
6152 tdaName = tdaType.mid(vc);
6153 }
6154 if (!tdaName.empty())
6155 {
6156 name=tdaName.str(); // substitute
6157 found=true;
6158 }
6159 }
6160 }
6161 }
6162
6163 //printf(" srcList='%s' dstList='%s faList='%s'\n",
6164 // qPrint(argListToString(srclali.current())),
6165 // qPrint(argListToString(dstlali.current())),
6166 // funcTempArgList ? qPrint(argListToString(funcTempArgList)) : "<none>");
6167 ++srcIt;
6168 }
6169 dst+=name;
6170 p=i+l;
6171 }
6172 dst+=src.substr(p);
6173 //printf(" substituteTemplatesInString(%s)=%s\n",
6174 // qPrint(src),qPrint(dst));
6175 return dst;
6176}
6177
6179 const ArgumentLists &srcTempArgLists,
6180 const ArgumentLists &dstTempArgLists,
6181 const ArgumentList &src,
6182 ArgumentList &dst
6183 )
6184{
6185 auto dstIt = dst.begin();
6186 for (const Argument &sa : src)
6187 {
6188 DString dstType = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.type.str());
6189 DString dstArray = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.array.str());
6190 if (dstIt == dst.end())
6191 {
6192 Argument da = sa;
6193 da.type = dstType;
6194 da.array = dstArray;
6195 dst.push_back(da);
6196 dstIt = dst.end();
6197 }
6198 else
6199 {
6200 Argument da = *dstIt;
6201 da.type = dstType;
6202 da.array = dstArray;
6203 ++dstIt;
6204 }
6205 }
6210 srcTempArgLists,dstTempArgLists,
6211 src.trailingReturnType().str()));
6212 dst.setIsDeleted(src.isDeleted());
6213 dst.setRefQualifier(src.refQualifier());
6214 dst.setNoParameters(src.noParameters());
6215 //printf("substituteTemplatesInArgList: replacing %s with %s\n",
6216 // qPrint(argListToString(src)),qPrint(argListToString(dst))
6217 // );
6218}
6219
6220//-------------------------------------------------------------------------------------------
6221
6222static void addLocalObjCMethod(const Entry *root,
6223 const DString &scopeName,
6224 const DString &funcType,const DString &funcName,const DString &funcArgs,
6225 const DString &exceptions,const DString &funcDecl,
6226 TypeSpecifier spec)
6227{
6228 AUTO_TRACE();
6229 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
6230 ClassDefMutable *cd=nullptr;
6231 if (Config_getBool(EXTRACT_LOCAL_METHODS) && (cd=getClassMutable(scopeName)))
6232 {
6233 AUTO_TRACE_ADD("Local objective C method '{}' scopeName='{}'",root->name,scopeName);
6234 auto md = createMemberDef(
6235 root->fileName,root->startLine,root->startColumn,
6236 funcType,funcName,funcArgs,exceptions,
6237 root->protection,root->virt,root->isStatic,Relationship::Member,
6238 MemberType::Function,ArgumentList(),root->argList,root->metaData);
6239 auto mmd = toMemberDefMutable(md.get());
6240 mmd->setTagInfo(root->tagInfo());
6241 mmd->setLanguage(root->lang);
6242 mmd->setId(root->id);
6243 mmd->makeImplementationDetail();
6244 mmd->setMemberClass(cd);
6245 mmd->setDefinition(funcDecl);
6247 mmd->addQualifiers(root->qualifiers);
6248 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6249 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6250 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6251 mmd->setDocsForDefinition(!root->proto);
6252 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6253 mmd->addSectionsToDefinition(root->anchors);
6254 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6255 FileDef *fd=root->fileDef();
6256 mmd->setBodyDef(fd);
6257 mmd->setMemberSpecifiers(spec);
6258 mmd->setVhdlSpecifiers(root->vhdlSpec);
6259 mmd->setMemberGroupId(root->mGrpId);
6260 cd->insertMember(md.get());
6261 cd->insertUsedFile(fd);
6262 mmd->setRefItems(root->sli);
6263 mmd->setRequirementReferences(root->rqli);
6264
6266 mn->push_back(std::move(md));
6267 }
6268 else
6269 {
6270 // local objective C method found for class without interface
6271 }
6272}
6273
6274//-------------------------------------------------------------------------------------------
6275
6276static void addMemberFunction(const Entry *root,
6277 MemberName *mn,
6278 const DString &scopeName,
6279 const DString &namespaceName,
6280 const DString &className,
6281 const DString &funcTyp,
6282 const DString &funcName,
6283 const DString &funcArgs,
6284 const DString &funcTempList,
6285 const DString &exceptions,
6286 const DString &type,
6287 const DString &args,
6288 bool isFriend,
6289 TypeSpecifier spec,
6290 const DString &relates,
6291 const DString &funcDecl,
6292 bool overloaded,
6293 bool isFunc)
6294{
6295 AUTO_TRACE();
6296 DString funcType = funcTyp;
6297 int count=0;
6298 int noMatchCount=0;
6299 bool memFound=false;
6300 for (const auto &imd : *mn)
6301 {
6302 MemberDefMutable *md = toMemberDefMutable(imd.get());
6303 if (md==nullptr) continue;
6305 if (cd==nullptr) continue;
6306 //AUTO_TRACE_ADD("member definition found, scope needed='{}' scope='{}' args='{}' fileName='{}'",
6307 // scopeName, cd->name(), md->argsString(), root->fileName);
6308 FileDef *fd=root->fileDef();
6309 NamespaceDef *nd=nullptr;
6310 if (!namespaceName.empty()) nd=getResolvedNamespace(namespaceName);
6311
6312 //printf("scopeName %s->%s\n",qPrint(scopeName),
6313 // qPrint(stripTemplateSpecifiersFromScope(scopeName,false)));
6314
6315 // if the member we are searching for is an enum value that is part of
6316 // a "strong" enum, we need to look into the fields of the enum for a match
6317 size_t enumNamePos=0;
6318 if (md->isEnumValue() && (enumNamePos=className.rfind("::"))!=DString::npos)
6319 {
6320 DString enumName = className.mid(enumNamePos+2);
6321 DString fullScope = className.left(enumNamePos);
6322 if (!namespaceName.empty()) fullScope.prepend(namespaceName+"::");
6323 if (fullScope==cd->name())
6324 {
6325 MemberName *enumMn=Doxygen::memberNameLinkedMap->find(enumName);
6326 //printf("enumMn(%s)=%p\n",qPrint(className),(void*)enumMn);
6327 if (enumMn)
6328 {
6329 for (const auto &emd : *enumMn)
6330 {
6331 memFound = emd->isStrong() && md->getEnumScope()==emd.get();
6332 if (memFound)
6333 {
6334 addMemberDocs(root,md,funcDecl,nullptr,overloaded,spec);
6335 count++;
6336 }
6337 if (memFound) break;
6338 }
6339 }
6340 }
6341 }
6342 if (memFound) break;
6343
6344 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
6345 if (tcd==nullptr && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
6346 {
6347 // don't be fooled by anonymous scopes
6348 tcd=cd;
6349 }
6350 //printf("Looking for %s inside nd=%s result=%s cd=%s\n",
6351 // qPrint(scopeName),nd?qPrint(nd->name()):"<none>",tcd?qPrint(tcd->name()):"",qPrint(cd->name()));
6352
6353 if (cd && tcd==cd) // member's classes match
6354 {
6355 AUTO_TRACE_ADD("class definition '{}' found",cd->name());
6356
6357 // get the template parameter lists found at the member declaration
6358 ArgumentLists declTemplArgs = cd->getTemplateParameterLists();
6359 const ArgumentList &templAl = md->templateArguments();
6360 if (!templAl.empty())
6361 {
6362 declTemplArgs.push_back(templAl);
6363 }
6364
6365 // get the template parameter lists found at the member definition
6366 const ArgumentLists &defTemplArgs = root->tArgLists;
6367 //printf("defTemplArgs=%p\n",defTemplArgs);
6368
6369 // do we replace the decl argument lists with the def argument lists?
6370 bool substDone=false;
6371 ArgumentList argList;
6372
6373 /* substitute the occurrences of class template names in the
6374 * argument list before matching
6375 */
6376 const ArgumentList &mdAl = md->argumentList();
6377 if (declTemplArgs.size()>0 && declTemplArgs.size()==defTemplArgs.size())
6378 {
6379 /* the function definition has template arguments
6380 * and the class definition also has template arguments, so
6381 * we must substitute the template names of the class by that
6382 * of the function definition before matching.
6383 */
6384 substituteTemplatesInArgList(declTemplArgs,defTemplArgs,mdAl,argList);
6385
6386 substDone=true;
6387 }
6388 else /* no template arguments, compare argument lists directly */
6389 {
6390 argList = mdAl;
6391 }
6392
6393 bool matching=
6394 md->isVariable() || md->isTypedef() || // needed for function pointers
6396 md->getClassDef(),md->getFileDef(),md->typeString(),&argList,
6397 cd,fd,root->type,&root->argList,
6398 true,root->lang);
6399
6400 AUTO_TRACE_ADD("matching '{}'<=>'{}' className='{}' namespaceName='{}' result={}",
6401 argListToString(argList,true),argListToString(root->argList,true),className,namespaceName,matching);
6402
6403 if (md->getLanguage()==SrcLangExt::ObjC && md->isVariable() && root->section.isFunction())
6404 {
6405 matching = false; // don't match methods and attributes with the same name
6406 }
6407
6408 // for template member we also need to check the return type
6409 if (!md->templateArguments().empty() && !root->tArgLists.empty())
6410 {
6411 DString memType = md->typeString();
6412 memType.stripPrefix("static "); // see bug700696
6413 funcType=substitute(stripTemplateSpecifiersFromScope(funcType,true),
6414 className+"::",""); // see bug700693 & bug732594
6415 memType=substitute(stripTemplateSpecifiersFromScope(memType,true),
6416 className+"::",""); // see bug758900
6417 if (memType=="auto" && !argList.trailingReturnType().empty())
6418 {
6419 memType = argList.trailingReturnType();
6420 memType.stripPrefix(" -> ");
6421 }
6422 if (funcType=="auto" && !root->argList.trailingReturnType().empty())
6423 {
6424 funcType = root->argList.trailingReturnType();
6425 funcType.stripPrefix(" -> ");
6427 substDone=true;
6428 }
6429 AUTO_TRACE_ADD("Comparing return types '{}'<->'{}' #args {}<->{}",
6430 memType,funcType,md->templateArguments().size(),root->tArgLists.back().size());
6431 if (md->templateArguments().size()!=root->tArgLists.back().size() || memType!=funcType)
6432 {
6433 //printf(" ---> no matching\n");
6434 matching = false;
6435 }
6436 }
6437 else if (defTemplArgs.size()>declTemplArgs.size())
6438 {
6439 AUTO_TRACE_ADD("Different number of template arguments {} vs {}",defTemplArgs.size(),declTemplArgs.size());
6440 // avoid matching a non-template function in a template class against a
6441 // template function with the same name and parameters, see issue #10184
6442 substDone = false;
6443 matching = false;
6444 }
6445 bool rootIsUserDoc = root->section.isMemberDoc();
6446 bool classIsTemplate = scopeIsTemplate(md->getClassDef());
6447 bool mdIsTemplate = md->templateArguments().hasParameters();
6448 bool classOrMdIsTemplate = mdIsTemplate || classIsTemplate;
6449 bool rootIsTemplate = !root->tArgLists.empty();
6450 //printf("classIsTemplate=%d mdIsTemplate=%d rootIsTemplate=%d\n",classIsTemplate,mdIsTemplate,rootIsTemplate);
6451 if (!rootIsUserDoc && // don't check out-of-line @fn references, see bug722457
6452 (mdIsTemplate || rootIsTemplate) && // either md or root is a template
6453 ((classOrMdIsTemplate && !rootIsTemplate) || (!classOrMdIsTemplate && rootIsTemplate))
6454 )
6455 {
6456 // Method with template return type does not match method without return type
6457 // even if the parameters are the same. See also bug709052
6458 AUTO_TRACE_ADD("Comparing return types: template v.s. non-template");
6459 matching = false;
6460 }
6461
6462 AUTO_TRACE_ADD("Match results of matchArguments2='{}' substDone='{}'",matching,substDone);
6463
6464 if (substDone) // found a new argument list
6465 {
6466 if (matching) // replace member's argument list
6467 {
6469 md->moveArgumentList(std::make_unique<ArgumentList>(argList));
6470 }
6471 else // no match
6472 {
6473 if (!funcTempList.empty() &&
6474 isSpecialization(declTemplArgs,defTemplArgs))
6475 {
6476 // check if we are dealing with a partial template
6477 // specialization. In this case we add it to the class
6478 // even though the member arguments do not match.
6479
6480 addMethodToClass(root,cd,type,md->name(),args,isFriend,
6481 md->protection(),md->isStatic(),md->virtualness(),spec,relates);
6482 return;
6483 }
6484 }
6485 }
6486 if (matching)
6487 {
6488 addMemberDocs(root,md,funcDecl,nullptr,overloaded,spec);
6489 count++;
6490 memFound=true;
6491 }
6492 }
6493 else if (cd && cd!=tcd) // we did find a class with the same name as cd
6494 // but in a different namespace
6495 {
6496 noMatchCount++;
6497 }
6498
6499 if (memFound) break;
6500 }
6501 if (count==0 && root->parent() && root->parent()->section.isObjcImpl())
6502 {
6503 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
6504 return;
6505 }
6506 if (count==0 && !(isFriend && funcType=="class"))
6507 {
6508 int candidates=0;
6509 const ClassDef *ecd = nullptr, *ucd = nullptr;
6510 MemberDef *emd = nullptr, *umd = nullptr;
6511 //printf("Assume template class\n");
6512 for (const auto &md : *mn)
6513 {
6514 MemberDef *cmd=md.get();
6516 ClassDefMutable *ccd=cdmdm ? cdmdm->getClassDefMutable() : nullptr;
6517 //printf("ccd->name()==%s className=%s\n",qPrint(ccd->name()),qPrint(className));
6518 if (ccd!=nullptr && rightScopeMatch(ccd->name(),className))
6519 {
6520 const ArgumentList &templAl = md->templateArguments();
6521 if (!root->tArgLists.empty() && !templAl.empty() &&
6522 root->tArgLists.back().size()<=templAl.size())
6523 {
6524 AUTO_TRACE_ADD("add template specialization");
6525 addMethodToClass(root,ccd,type,md->name(),args,isFriend,
6526 root->protection,root->isStatic,root->virt,spec,relates);
6527 return;
6528 }
6529 if (argListToString(md->argumentList(),false,false) ==
6530 argListToString(root->argList,false,false))
6531 { // exact argument list match -> remember
6532 ucd = ecd = ccd;
6533 umd = emd = cmd;
6534 AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': exact match",
6535 className,ccd->name(),md->argsString());
6536 }
6537 else // arguments do not match, but member name and scope do -> remember
6538 {
6539 ucd = ccd;
6540 umd = cmd;
6541 AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': no match",
6542 className,ccd->name(),md->argsString());
6543 }
6544 candidates++;
6545 }
6546 }
6547 bool strictProtoMatching = Config_getBool(STRICT_PROTO_MATCHING);
6548 if (!strictProtoMatching)
6549 {
6550 if (candidates==1 && ucd && umd)
6551 {
6552 // we didn't find an actual match on argument lists, but there is only 1 member with this
6553 // name in the same scope, so that has to be the one.
6554 addMemberDocs(root,toMemberDefMutable(umd),funcDecl,nullptr,overloaded,spec);
6555 return;
6556 }
6557 else if (candidates>1 && ecd && emd)
6558 {
6559 // we didn't find a unique match using type resolution,
6560 // but one of the matches has the exact same signature so
6561 // we take that one.
6562 addMemberDocs(root,toMemberDefMutable(emd),funcDecl,nullptr,overloaded,spec);
6563 return;
6564 }
6565 }
6566
6567 DString warnMsg = "no ";
6568 if (noMatchCount>1) warnMsg+="uniquely ";
6569 warnMsg+="matching class member found for \n";
6570
6571 for (const ArgumentList &al : root->tArgLists)
6572 {
6573 warnMsg+=" template ";
6574 warnMsg+=tempArgListToString(al,root->lang);
6575 warnMsg+='\n';
6576 }
6577
6578 DString fullFuncDecl=funcDecl;
6579 if (isFunc) fullFuncDecl+=argListToString(root->argList,true);
6580
6581 warnMsg+=" ";
6582 warnMsg+=fullFuncDecl;
6583
6584 if (candidates>0 || noMatchCount>=1)
6585 {
6586 warnMsg+="\nPossible candidates:";
6587
6588 NamespaceDef *nd=nullptr;
6589 if (!namespaceName.empty()) nd=getResolvedNamespace(namespaceName);
6590 FileDef *fd=root->fileDef();
6591
6592 for (const auto &md : *mn)
6593 {
6594 const ClassDef *cd=md->getClassDef();
6595 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
6596 if (tcd==nullptr && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
6597 {
6598 // don't be fooled by anonymous scopes
6599 tcd=cd;
6600 }
6601 if (cd!=nullptr && (rightScopeMatch(cd->name(),className) || (cd!=tcd)))
6602 {
6603 warnMsg+='\n';
6604 const ArgumentList &templAl = md->templateArguments();
6605 warnMsg+=" '";
6606 if (templAl.hasParameters())
6607 {
6608 warnMsg+="template ";
6609 warnMsg+=tempArgListToString(templAl,root->lang);
6610 warnMsg+='\n';
6611 warnMsg+=" ";
6612 }
6613 if (!md->typeString().empty())
6614 {
6615 warnMsg+=md->typeString();
6616 warnMsg+=' ';
6617 }
6619 if (!qScope.empty())
6620 warnMsg+=qScope+"::"+md->name();
6621 warnMsg+=md->argsString();
6622 warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
6623 }
6624 }
6625 }
6626 warn(root->fileName,root->startLine,"{}",warnMsg);
6627 }
6628}
6629
6630//-------------------------------------------------------------------------------------------
6631
6632static void addMemberSpecialization(const Entry *root,
6633 MemberName *mn,
6634 ClassDefMutable *cd,
6635 const DString &funcType,
6636 const DString &funcName,
6637 const DString &funcArgs,
6638 const DString &funcDecl,
6639 const DString &exceptions,
6640 TypeSpecifier spec
6641 )
6642{
6643 AUTO_TRACE("funcType={} funcName={} funcArgs={} funcDecl={} spec={}",funcType,funcName,funcArgs,funcDecl,spec);
6644 MemberDef *declMd=nullptr;
6645 for (const auto &md : *mn)
6646 {
6647 if (md->getClassDef()==cd)
6648 {
6649 // TODO: we should probably also check for matching arguments
6650 declMd = md.get();
6651 break;
6652 }
6653 }
6654 MemberType mtype=MemberType::Function;
6655 ArgumentList tArgList;
6656 // getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6657 auto md = createMemberDef(
6658 root->fileName,root->startLine,root->startColumn,
6659 funcType,funcName,funcArgs,exceptions,
6660 declMd ? declMd->protection() : root->protection,
6661 root->virt,root->isStatic,Relationship::Member,
6662 mtype,tArgList,root->argList,root->metaData);
6663 auto mmd = toMemberDefMutable(md.get());
6664 //printf("new specialized member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6665 mmd->setTagInfo(root->tagInfo());
6666 mmd->setLanguage(root->lang);
6667 mmd->setId(root->id);
6668 mmd->setMemberClass(cd);
6669 mmd->setTemplateSpecialization(true);
6670 mmd->setTypeConstraints(root->typeConstr);
6671 mmd->setDefinition(funcDecl);
6673 mmd->addQualifiers(root->qualifiers);
6674 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6675 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6676 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6677 mmd->setDocsForDefinition(!root->proto);
6678 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6679 mmd->addSectionsToDefinition(root->anchors);
6680 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6681 FileDef *fd=root->fileDef();
6682 mmd->setBodyDef(fd);
6683 mmd->setMemberSpecifiers(spec);
6684 mmd->setVhdlSpecifiers(root->vhdlSpec);
6685 mmd->setMemberGroupId(root->mGrpId);
6686 cd->insertMember(md.get());
6687 mmd->setRefItems(root->sli);
6688 mmd->setRequirementReferences(root->rqli);
6689
6690 mn->push_back(std::move(md));
6691}
6692
6693//-------------------------------------------------------------------------------------------
6694
6695static void addOverloaded(const Entry *root,MemberName *mn,
6696 const DString &funcType,const DString &funcName,const DString &funcArgs,
6697 const DString &funcDecl,const DString &exceptions,TypeSpecifier spec)
6698{
6699 // for unique overloaded member we allow the class to be
6700 // omitted, this is to be Qt compatible. Using this should
6701 // however be avoided, because it is error prone
6702 bool sameClass=false;
6703 if (mn->size()>0)
6704 {
6705 // check if all members with the same name are also in the same class
6706 sameClass = std::equal(mn->begin()+1,mn->end(),mn->begin(),
6707 [](const auto &md1,const auto &md2)
6708 { return md1->getClassDef()->name()==md2->getClassDef()->name(); });
6709 }
6710 if (sameClass)
6711 {
6712 MemberDefMutable *mdm = toMemberDefMutable(mn->front().get());
6713 ClassDefMutable *cd = mdm ? mdm->getClassDefMutable() : nullptr;
6714 if (cd==nullptr) return;
6715
6716 MemberType mtype = MemberType::Function;
6717 if (root->mtype==MethodTypes::Signal) mtype=MemberType::Signal;
6718 else if (root->mtype==MethodTypes::Slot) mtype=MemberType::Slot;
6719 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType::DCOP;
6720
6721 // new overloaded member function
6722 std::unique_ptr<ArgumentList> tArgList =
6723 getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6724 //printf("new related member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6725 auto md = createMemberDef(
6726 root->fileName,root->startLine,root->startColumn,
6727 funcType,funcName,funcArgs,exceptions,
6728 root->protection,root->virt,root->isStatic,Relationship::Related,
6729 mtype,tArgList ? *tArgList : ArgumentList(),root->argList,root->metaData);
6730 auto mmd = toMemberDefMutable(md.get());
6731 mmd->setTagInfo(root->tagInfo());
6732 mmd->setLanguage(root->lang);
6733 mmd->setId(root->id);
6734 mmd->setTypeConstraints(root->typeConstr);
6735 mmd->setMemberClass(cd);
6736 mmd->setDefinition(funcDecl);
6738 mmd->addQualifiers(root->qualifiers);
6740 doc+="<p>";
6741 doc+=root->doc;
6742 mmd->setDocumentation(doc,root->docFile,root->docLine);
6743 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6744 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6745 mmd->setDocsForDefinition(!root->proto);
6746 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6747 mmd->addSectionsToDefinition(root->anchors);
6748 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6749 FileDef *fd=root->fileDef();
6750 mmd->setBodyDef(fd);
6751 mmd->setMemberSpecifiers(spec);
6752 mmd->setVhdlSpecifiers(root->vhdlSpec);
6753 mmd->setMemberGroupId(root->mGrpId);
6754 cd->insertMember(md.get());
6755 cd->insertUsedFile(fd);
6756 mmd->setRefItems(root->sli);
6757 mmd->setRequirementReferences(root->rqli);
6758
6759 mn->push_back(std::move(md));
6760 }
6761}
6762
6763static void insertMemberAlias(Definition *outerScope,const MemberDef *md)
6764{
6765 if (outerScope && outerScope!=Doxygen::globalScope)
6766 {
6767 auto aliasMd = createMemberDefAlias(outerScope,md);
6768 if (outerScope->definitionType()==Definition::TypeClass)
6769 {
6770 ClassDefMutable *cdm = toClassDefMutable(outerScope);
6771 if (cdm)
6772 {
6773 cdm->insertMember(aliasMd.get());
6774 }
6775 }
6776 else if (outerScope->definitionType()==Definition::TypeNamespace)
6777 {
6778 NamespaceDefMutable *ndm = toNamespaceDefMutable(outerScope);
6779 if (ndm)
6780 {
6781 ndm->insertMember(aliasMd.get());
6782 }
6783 }
6784 else if (outerScope->definitionType()==Definition::TypeFile)
6785 {
6786 toFileDef(outerScope)->insertMember(aliasMd.get());
6787 }
6788 if (aliasMd)
6789 {
6790 Doxygen::functionNameLinkedMap->add(md->name())->push_back(std::move(aliasMd));
6791 }
6792 }
6793}
6794
6795//-------------------------------------------------------------------------------------------
6796
6797/*! This function tries to find a member (in a documented class/file/namespace)
6798 * that corresponds to the function/variable declaration given in \a funcDecl.
6799 *
6800 * The boolean \a overloaded is used to specify whether or not a standard
6801 * overload documentation line should be generated.
6802 *
6803 * The boolean \a isFunc is a hint that indicates that this is a function
6804 * instead of a variable or typedef.
6805 */
6806static void findMember(const Entry *root,
6807 const DString &relates,
6808 const DString &type,
6809 const DString &args,
6810 DString funcDecl,
6811 bool overloaded,
6812 bool isFunc
6813 )
6814{
6815 AUTO_TRACE("root='{}' funcDecl='{}' related='{}' overload={} isFunc={} mGrpId={} #tArgList={} spec={} lang={}",
6816 root->name, funcDecl, relates, overloaded, isFunc, root->mGrpId, root->tArgLists.size(),
6817 root->spec, root->lang);
6818
6819 DString scopeName;
6820 DString className;
6821 DString namespaceName;
6822 DString funcType;
6823 DString funcName;
6824 DString funcArgs;
6825 DString funcTempList;
6826 DString exceptions;
6827 DString funcSpec;
6828 bool isRelated=false;
6829 bool isMemberOf=false;
6830 bool isFriend=false;
6831 bool done=false;
6832 TypeSpecifier spec = root->spec;
6833 while (!done)
6834 {
6835 done=true;
6836 if (funcDecl.stripPrefix("friend ")) // treat friends as related members
6837 {
6838 isFriend=true;
6839 done=false;
6840 }
6841 if (funcDecl.stripPrefix("inline "))
6842 {
6843 spec.setInline(true);
6844 done=false;
6845 }
6846 if (funcDecl.stripPrefix("explicit "))
6847 {
6848 spec.setExplicit(true);
6849 done=false;
6850 }
6851 if (funcDecl.stripPrefix("mutable "))
6852 {
6853 spec.setMutable(true);
6854 done=false;
6855 }
6856 if (funcDecl.stripPrefix("thread_local "))
6857 {
6858 spec.setThreadLocal(true);
6859 done=false;
6860 }
6861 if (funcDecl.stripPrefix("virtual "))
6862 {
6863 done=false;
6864 }
6865 }
6866
6867 // delete any ; from the function declaration
6868 size_t sep=0;
6869 while ((sep=funcDecl.find(';'))!=DString::npos)
6870 {
6871 funcDecl=(funcDecl.left(sep)+funcDecl.mid(sep+1)).stripWhiteSpace();
6872 }
6873
6874 // make sure the first character is a space to simplify searching.
6875 if (!funcDecl.empty() && funcDecl[0]!=' ') funcDecl.prepend(" ");
6876
6877 // remove some superfluous spaces
6878 funcDecl= substitute(
6879 substitute(
6880 substitute(funcDecl,"~ ","~"),
6881 ":: ","::"
6882 ),
6883 " ::","::"
6884 ).stripWhiteSpace();
6885
6886 //printf("funcDecl='%s'\n",qPrint(funcDecl));
6887 if (isFriend && funcDecl.startsWith("class "))
6888 {
6889 //printf("friend class\n");
6890 funcDecl=funcDecl.mid(6);
6891 funcName = funcDecl;
6892 }
6893 else if (isFriend && funcDecl.startsWith("struct "))
6894 {
6895 funcDecl=funcDecl.mid(7);
6896 funcName = funcDecl;
6897 }
6898 else
6899 {
6900 // extract information from the declarations
6901 parseFuncDecl(funcDecl,root->lang,scopeName,funcType,funcName,
6902 funcArgs,funcTempList,exceptions
6903 );
6904 }
6905
6906 // the class name can also be a namespace name, we decide this later.
6907 // if a related class name is specified and the class name could
6908 // not be derived from the function declaration, then use the
6909 // related field.
6910 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}' funcType='{}' funcName='{}' funcArgs='{}'",
6911 scopeName,className,namespaceName,funcType,funcName,funcArgs);
6912 if (!relates.empty())
6913 { // related member, prefix user specified scope
6914 isRelated=true;
6915 isMemberOf=(root->relatesType == RelatesType::MemberOf);
6916 if (getClass(relates)==nullptr && !scopeName.empty())
6917 {
6918 scopeName= mergeScopes(scopeName,relates);
6919 }
6920 else
6921 {
6922 scopeName = relates;
6923 }
6924 }
6925
6926 if (relates.empty() && root->parent() &&
6927 (root->parent()->section.isScope() || root->parent()->section.isObjcImpl()) &&
6928 !root->parent()->name.empty()) // see if we can combine scopeName
6929 // with the scope in which it was found
6930 {
6931 DString joinedName = root->parent()->name+"::"+scopeName;
6932 if (!scopeName.empty() &&
6933 (getClass(joinedName) || Doxygen::namespaceLinkedMap->find(joinedName)))
6934 {
6935 scopeName = joinedName;
6936 }
6937 else
6938 {
6939 scopeName = mergeScopes(root->parent()->name,scopeName);
6940 }
6941 }
6942 else // see if we can prefix a namespace or class that is used from the file
6943 {
6944 FileDef *fd=root->fileDef();
6945 if (fd)
6946 {
6947 for (const auto &fnd : fd->getUsedNamespaces())
6948 {
6949 DString joinedName = fnd->name()+"::"+scopeName;
6950 if (Doxygen::namespaceLinkedMap->find(joinedName))
6951 {
6952 scopeName=joinedName;
6953 break;
6954 }
6955 }
6956 }
6957 }
6959 removeRedundantWhiteSpace(scopeName),false,&funcSpec,DString(),false);
6960
6961 // funcSpec contains the last template specifiers of the given scope.
6962 // If this method does not have any template arguments or they are
6963 // empty while funcSpec is not empty we assume this is a
6964 // specialization of a method. If not, we clear the funcSpec and treat
6965 // this as a normal method of a template class.
6966 if (!(root->tArgLists.size()>0 &&
6967 root->tArgLists.front().size()==0
6968 )
6969 )
6970 {
6971 funcSpec.clear();
6972 }
6973
6974 //namespaceName=removeAnonymousScopes(namespaceName);
6975 if (!Config_getBool(EXTRACT_ANON_NSPACES) && scopeName.find('@')!=DString::npos) return; // skip stuff in anonymous namespace...
6976
6977 // split scope into a namespace and a class part
6978 extractNamespaceName(scopeName,className,namespaceName,true);
6979 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}'",scopeName,className,namespaceName);
6980
6981 //printf("namespaceName='%s' className='%s'\n",qPrint(namespaceName),qPrint(className));
6982 // merge class and namespace scopes again
6983 scopeName.clear();
6984 if (!namespaceName.empty())
6985 {
6986 if (className.empty())
6987 {
6988 scopeName=namespaceName;
6989 }
6990 else if (!relates.empty() || // relates command with explicit scope
6991 !getClass(className)) // class name only exists in a namespace
6992 {
6993 scopeName=namespaceName+"::"+className;
6994 }
6995 else
6996 {
6997 scopeName=className;
6998 }
6999 }
7000 else if (!className.empty())
7001 {
7002 scopeName=className;
7003 }
7004 //printf("new scope='%s'\n",qPrint(scopeName));
7005
7006 DString tempScopeName=scopeName;
7007 ClassDefMutable *cd=getClassMutable(scopeName);
7008 if (cd)
7009 {
7010 if (funcSpec.empty())
7011 {
7012 uint32_t argListIndex=0;
7013 tempScopeName=cd->qualifiedNameWithTemplateParameters(&root->tArgLists,&argListIndex);
7014 }
7015 else
7016 {
7017 tempScopeName=scopeName+funcSpec;
7018 }
7019 }
7020 //printf("scopeName=%s cd=%p root->tArgLists=%p result=%s\n",
7021 // qPrint(scopeName),cd,root->tArgLists,qPrint(tempScopeName));
7022
7023 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
7024 // rebuild the function declaration (needed to get the scope right).
7025 if (!scopeName.empty() && !isRelated && !isFriend && !Config_getBool(HIDE_SCOPE_NAMES) && root->lang!=SrcLangExt::Python)
7026 {
7027 if (!funcType.empty())
7028 {
7029 if (isFunc) // a function -> we use argList for the arguments
7030 {
7031 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcTempList;
7032 }
7033 else
7034 {
7035 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcArgs;
7036 }
7037 }
7038 else
7039 {
7040 if (isFunc) // a function => we use argList for the arguments
7041 {
7042 funcDecl=tempScopeName+"::"+funcName+funcTempList;
7043 }
7044 else // variable => add 'argument' list
7045 {
7046 funcDecl=tempScopeName+"::"+funcName+funcArgs;
7047 }
7048 }
7049 }
7050 else // build declaration without scope
7051 {
7052 if (!funcType.empty()) // but with a type
7053 {
7054 if (isFunc) // function => omit argument list
7055 {
7056 funcDecl=funcType+" "+funcName+funcTempList;
7057 }
7058 else // variable => add 'argument' list
7059 {
7060 funcDecl=funcType+" "+funcName+funcArgs;
7061 }
7062 }
7063 else // no type
7064 {
7065 if (isFunc)
7066 {
7067 funcDecl=funcName+funcTempList;
7068 }
7069 else
7070 {
7071 funcDecl=funcName+funcArgs;
7072 }
7073 }
7074 }
7075
7076 if (funcType=="template class" && !funcTempList.empty())
7077 return; // ignore explicit template instantiations
7078
7079 AUTO_TRACE_ADD("Parse results: namespaceName='{}' className=`{}` funcType='{}' funcSpec='{}' "
7080 " funcName='{}' funcArgs='{}' funcTempList='{}' funcDecl='{}' relates='{}'"
7081 " exceptions='{}' isRelated={} isMemberOf={} isFriend={} isFunc={}",
7082 namespaceName, className, funcType, funcSpec,
7083 funcName, funcArgs, funcTempList, funcDecl, relates,
7084 exceptions, isRelated, isMemberOf, isFriend, isFunc);
7085
7086 if (!funcName.empty()) // function name is valid
7087 {
7088 // check if 'className' is actually a scoped enum, in which case we need to
7089 // process it as a global, see issue #6471
7090 bool strongEnum = false;
7091 MemberName *mn=nullptr;
7092 if (!className.empty() && (mn=Doxygen::functionNameLinkedMap->find(className)))
7093 {
7094 for (const auto &imd : *mn)
7095 {
7096 MemberDefMutable *md = toMemberDefMutable(imd.get());
7097 Definition *mdScope = nullptr;
7098 if (md && md->isEnumerate() && md->isStrong() && (mdScope=md->getOuterScope()) &&
7099 // need filter for the correct scope, see issue #9668
7100 ((namespaceName.empty() && mdScope==Doxygen::globalScope) || (mdScope->name()==namespaceName)))
7101 {
7102 AUTO_TRACE_ADD("'{}' is a strong enum! (namespace={} md->getOuterScope()->name()={})",md->name(),namespaceName,md->getOuterScope()->name());
7103 strongEnum = true;
7104 // pass the scope name name as a 'namespace' to the findGlobalMember function
7105 if (!namespaceName.empty())
7106 {
7107 namespaceName+="::"+className;
7108 }
7109 else
7110 {
7111 namespaceName=className;
7112 }
7113 }
7114 }
7115 }
7116
7117 if (funcName.startsWith("operator ")) // strip class scope from cast operator
7118 {
7119 funcName = substitute(funcName,className+"::","");
7120 }
7121 mn = nullptr;
7122 if (!funcTempList.empty()) // try with member specialization
7123 {
7124 mn=Doxygen::memberNameLinkedMap->find(funcName+funcTempList);
7125 }
7126 if (mn==nullptr) // try without specialization
7127 {
7128 mn=Doxygen::memberNameLinkedMap->find(funcName);
7129 }
7130 if (!isRelated && !strongEnum && mn) // function name already found
7131 {
7132 AUTO_TRACE_ADD("member name exists ({} members with this name)",mn->size());
7133 if (!className.empty()) // class name is valid
7134 {
7135 if (funcSpec.empty()) // not a member specialization
7136 {
7137 addMemberFunction(root,mn,scopeName,namespaceName,className,funcType,funcName,
7138 funcArgs,funcTempList,exceptions,
7139 type,args,isFriend,spec,relates,funcDecl,overloaded,isFunc);
7140 }
7141 else if (cd) // member specialization
7142 {
7143 addMemberSpecialization(root,mn,cd,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
7144 }
7145 else
7146 {
7147 //printf("*** Specialized member %s of unknown scope %s%s found!\n",
7148 // qPrint(scopeName),qPrint(funcName),qPrint(funcArgs));
7149 }
7150 }
7151 else if (overloaded) // check if the function belongs to only one class
7152 {
7153 addOverloaded(root,mn,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
7154 }
7155 else // unrelated function with the same name as a member
7156 {
7157 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
7158 {
7159 DString fullFuncDecl=funcDecl;
7160 if (isFunc) fullFuncDecl+=argListToString(root->argList,true);
7161 warn(root->fileName,root->startLine,
7162 "Cannot determine class for function\n{}",
7163 fullFuncDecl
7164 );
7165 }
7166 }
7167 }
7168 else if (isRelated && !relates.empty())
7169 {
7170 AUTO_TRACE_ADD("related function scopeName='{}' className='{}'",scopeName,className);
7171 if (className.empty()) className=relates;
7172 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
7173 if ((cd=getClassMutable(scopeName)))
7174 {
7175 bool newMember=true; // assume we have a new member
7176 MemberDefMutable *mdDefine=nullptr;
7177 {
7178 mn = Doxygen::functionNameLinkedMap->find(funcName);
7179 if (mn)
7180 {
7181 for (const auto &imd : *mn)
7182 {
7183 MemberDefMutable *md = toMemberDefMutable(imd.get());
7184 if (md && md->isDefine())
7185 {
7186 mdDefine = md;
7187 break;
7188 }
7189 }
7190 }
7191 }
7192
7193 if (mdDefine) // macro definition is already created by the preprocessor and inserted as a file member
7194 {
7195 //printf("moving #define %s into class %s\n",qPrint(mdDefine->name()),qPrint(cd->name()));
7196
7197 // take mdDefine from the Doxygen::functionNameLinkedMap (without deleting the data)
7198 auto mdDefineTaken = Doxygen::functionNameLinkedMap->take(funcName,mdDefine);
7199 // insert it as a class member
7200 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==nullptr)
7201 {
7202 mn=Doxygen::memberNameLinkedMap->add(funcName);
7203 }
7204
7205 if (mdDefine->getFileDef())
7206 {
7207 mdDefine->getFileDef()->removeMember(mdDefine);
7208 }
7209 mdDefine->makeRelated();
7210 mdDefine->setMemberClass(cd);
7211 mdDefine->moveTo(cd);
7212 cd->insertMember(mdDefine);
7213 // also insert the member as an alias in the parent's scope, so it can be referenced also without cd's scope
7214 insertMemberAlias(cd->getOuterScope(),mdDefine);
7215 mn->push_back(std::move(mdDefineTaken));
7216 }
7217 else // normal member, needs to be created and added to the class
7218 {
7219 FileDef *fd=root->fileDef();
7220
7221 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==nullptr)
7222 {
7223 mn=Doxygen::memberNameLinkedMap->add(funcName);
7224 }
7225 else
7226 {
7227 // see if we got another member with matching arguments
7228 MemberDefMutable *rmd_found = nullptr;
7229 for (const auto &irmd : *mn)
7230 {
7231 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
7232 if (rmd)
7233 {
7234 const ArgumentList &rmdAl = rmd->argumentList();
7235
7236 newMember=
7237 className!=rmd->getOuterScope()->name() ||
7238 !matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmdAl,
7239 cd,fd,root->type,&root->argList,
7240 true,root->lang);
7241 if (!newMember)
7242 {
7243 rmd_found = rmd;
7244 }
7245 }
7246 }
7247 if (rmd_found) // member already exists as rmd -> add docs
7248 {
7249 AUTO_TRACE_ADD("addMemberDocs for related member {}",root->name);
7250 addMemberDocs(root,rmd_found,funcDecl,nullptr,overloaded,spec);
7251 newMember=false;
7252 }
7253 }
7254
7255 if (newMember) // need to create a new member
7256 {
7257 MemberType mtype = MemberType::Function;
7258 switch (root->mtype)
7259 {
7260 case MethodTypes::Method: mtype = MemberType::Function; break;
7261 case MethodTypes::Signal: mtype = MemberType::Signal; break;
7262 case MethodTypes::Slot: mtype = MemberType::Slot; break;
7263 case MethodTypes::DCOP: mtype = MemberType::DCOP; break;
7264 case MethodTypes::Property: mtype = MemberType::Property; break;
7265 case MethodTypes::Event: mtype = MemberType::Event; break;
7266 }
7267
7268 //printf("New related name '%s' '%d'\n",qPrint(funcName),
7269 // root->argList ? (int)root->argList->count() : -1);
7270
7271 // first note that we pass:
7272 // (root->tArgLists ? root->tArgLists->last() : nullptr)
7273 // for the template arguments for the new "member."
7274 // this accurately reflects the template arguments of
7275 // the related function, which don't have to do with
7276 // those of the related class.
7277 auto md = createMemberDef(
7278 root->fileName,root->startLine,root->startColumn,
7279 funcType,funcName,funcArgs,exceptions,
7280 root->protection,root->virt,
7281 root->isStatic,
7282 isMemberOf ? Relationship::Foreign : Relationship::Related,
7283 mtype,
7284 (!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList()),
7285 funcArgs.empty() ? ArgumentList() : root->argList,
7286 root->metaData);
7287 auto mmd = toMemberDefMutable(md.get());
7288
7289 // also insert the member as an alias in the parent's scope, so it can be referenced also without cd's scope
7290 insertMemberAlias(cd->getOuterScope(),md.get());
7291
7292 // we still have the problem that
7293 // MemberDef::writeDocumentation() in memberdef.cpp
7294 // writes the template argument list for the class,
7295 // as if this member is a member of the class.
7296 // fortunately, MemberDef::writeDocumentation() has
7297 // a special mechanism that allows us to totally
7298 // override the set of template argument lists that
7299 // are printed. We use that and set it to the
7300 // template argument lists of the related function.
7301 //
7302 mmd->setDefinitionTemplateParameterLists(root->tArgLists);
7303
7304 mmd->setTagInfo(root->tagInfo());
7305
7306 //printf("Related member name='%s' decl='%s' bodyLine='%d'\n",
7307 // qPrint(funcName),qPrint(funcDecl),root->bodyLine);
7308
7309 // try to find the matching line number of the body from the
7310 // global function list
7311 bool found=false;
7312 if (root->bodyLine==-1)
7313 {
7315 if (rmn)
7316 {
7317 const MemberDefMutable *rmd_found=nullptr;
7318 for (const auto &irmd : *rmn)
7319 {
7320 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
7321 if (rmd)
7322 {
7323 const ArgumentList &rmdAl = rmd->argumentList();
7324 // check for matching argument lists
7325 if (
7326 matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmdAl,
7327 cd,fd,root->type,&root->argList,
7328 true,root->lang)
7329 )
7330 {
7331 found=true;
7332 rmd_found = rmd;
7333 break;
7334 }
7335 }
7336 }
7337 if (rmd_found) // member found -> copy line number info
7338 {
7339 mmd->setBodySegment(rmd_found->getDefLine(),rmd_found->getStartBodyLine(),rmd_found->getEndBodyLine());
7340 mmd->setBodyDef(rmd_found->getBodyDef());
7341 //md->setBodyMember(rmd);
7342 }
7343 }
7344 }
7345 if (!found) // line number could not be found or is available in this
7346 // entry
7347 {
7348 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7349 mmd->setBodyDef(fd);
7350 }
7351
7352 //if (root->mGrpId!=-1)
7353 //{
7354 // md->setMemberGroup(memberGroupDict[root->mGrpId]);
7355 //}
7356 mmd->setMemberClass(cd);
7357 mmd->setMemberSpecifiers(spec);
7358 mmd->setVhdlSpecifiers(root->vhdlSpec);
7359 mmd->setDefinition(funcDecl);
7361 mmd->addQualifiers(root->qualifiers);
7362 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7363 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7364 mmd->setDocsForDefinition(!root->proto);
7365 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
7366 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7367 mmd->addSectionsToDefinition(root->anchors);
7368 mmd->setMemberGroupId(root->mGrpId);
7369 mmd->setLanguage(root->lang);
7370 mmd->setId(root->id);
7371 //md->setMemberDefTemplateArguments(root->mtArgList);
7372 cd->insertMember(md.get());
7373 cd->insertUsedFile(fd);
7374 mmd->setRefItems(root->sli);
7375 mmd->setRequirementReferences(root->rqli);
7376 if (root->relatesType==RelatesType::Duplicate) mmd->setRelatedAlso(cd);
7377 addMemberToGroups(root,md.get());
7379 //printf("Adding member=%s\n",qPrint(md->name()));
7380 mn->push_back(std::move(md));
7381 }
7382 if (root->relatesType==RelatesType::Duplicate)
7383 {
7384 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
7385 {
7386 DString fullFuncDecl=funcDecl;
7387 if (isFunc) fullFuncDecl+=argListToString(root->argList,true);
7388 warn(root->fileName,root->startLine,
7389 "Cannot determine file/namespace for relatedalso function\n{}",
7390 fullFuncDecl
7391 );
7392 }
7393 }
7394 }
7395 }
7396 else
7397 {
7398 warn_undoc(root->fileName,root->startLine, "class '{}' for related function '{}' is not documented.", className,funcName);
7399 }
7400 }
7401 else if (root->parent() && root->parent()->section.isObjcImpl())
7402 {
7403 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
7404 }
7405 else // unrelated not overloaded member found
7406 {
7407 bool globMem = findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec);
7408 if (className.empty() && !globMem)
7409 {
7410 warn(root->fileName,root->startLine, "class for member '{}' cannot be found.", funcName);
7411 }
7412 else if (!className.empty() && !globMem)
7413 {
7414 warn(root->fileName,root->startLine,
7415 "member '{}' of class '{}' cannot be found",
7416 funcName,className);
7417 }
7418 }
7419 }
7420 else
7421 {
7422 // this should not be called
7423 warn(root->fileName,root->startLine,"member with no name found.");
7424 }
7425 return;
7426}
7427
7428//----------------------------------------------------------------------
7429// find the members corresponding to the different documentation blocks
7430// that are extracted from the sources.
7431
7432static void filterMemberDocumentation(const Entry *root,const DString &relates)
7433{
7434 AUTO_TRACE("root->type='{}' root->inside='{}' root->name='{}' root->args='{}' section={} root->spec={} root->mGrpId={}",
7435 root->type,root->inside,root->name,root->args,root->section,root->spec,root->mGrpId);
7436 //printf("root->parent()->name=%s\n",qPrint(root->parent()->name));
7437 bool isFunc=true;
7438
7439 DString type = root->type;
7440 DString args = root->args;
7441 int i=-1, l=0;
7442 if ( // detect func variable/typedef to func ptr
7443 (i=findFunctionPtr(type.str(),root->lang,&l))!=-1
7444 )
7445 {
7446 //printf("Fixing function pointer!\n");
7447 // fix type and argument
7448 args.prepend(type.mid(i+l));
7449 type=type.left(i+l);
7450 //printf("Results type=%s,name=%s,args=%s\n",qPrint(type),qPrint(root->name),qPrint(args));
7451 isFunc=false;
7452 }
7453 else if ((type.startsWith("typedef ") && args.find('(')!=DString::npos))
7454 // detect function types marked as functions
7455 {
7456 isFunc=false;
7457 }
7458
7459 //printf("Member %s isFunc=%d\n",qPrint(root->name),isFunc);
7460 if (root->section.isMemberDoc())
7461 {
7462 //printf("Documentation for inline member '%s' found args='%s'\n",
7463 // qPrint(root->name),qPrint(args));
7464 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
7465 if (type.empty())
7466 {
7467 findMember(root,
7468 relates,
7469 type,
7470 args,
7471 root->name + args + root->exception,
7472 false,
7473 isFunc);
7474 }
7475 else
7476 {
7477 findMember(root,
7478 relates,
7479 type,
7480 args,
7481 type + " " + root->name + args + root->exception,
7482 false,
7483 isFunc);
7484 }
7485 }
7486 else if (root->section.isOverloadDoc())
7487 {
7488 //printf("Overloaded member %s found\n",qPrint(root->name));
7489 findMember(root,
7490 relates,
7491 type,
7492 args,
7493 root->name,
7494 true,
7495 isFunc);
7496 }
7497 else if
7498 ((root->section.isFunction() // function
7499 ||
7500 (root->section.isVariable() && // variable
7501 !type.empty() && // with a type
7502 g_compoundKeywords.find(type.str())==g_compoundKeywords.end() // that is not a keyword
7503 // (to skip forward declaration of class etc.)
7504 )
7505 )
7506 )
7507 {
7508 //printf("Documentation for member '%s' found args='%s' excp='%s'\n",
7509 // qPrint(root->name),qPrint(args),qPrint(root->exception));
7510 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
7511 //printf("Inside=%s\n Relates=%s\n",qPrint(root->inside),qPrint(relates));
7512 if (type=="friend class" || type=="friend struct" ||
7513 type=="friend union")
7514 {
7515 findMember(root,
7516 relates,
7517 type,
7518 args,
7519 type+" "+root->name,
7520 false,false);
7521
7522 }
7523 else if (!type.empty())
7524 {
7525 findMember(root,
7526 relates,
7527 type,
7528 args,
7529 type+" "+ root->inside + root->name + args + root->exception,
7530 false,isFunc);
7531 }
7532 else
7533 {
7534 findMember(root,
7535 relates,
7536 type,
7537 args,
7538 root->inside + root->name + args + root->exception,
7539 false,isFunc);
7540 }
7541 }
7542 else if (root->section.isDefine() && !relates.empty())
7543 {
7544 findMember(root,
7545 relates,
7546 type,
7547 args,
7548 root->name + args,
7549 false,
7550 !args.empty());
7551 }
7552 else if (root->section.isVariableDoc())
7553 {
7554 //printf("Documentation for variable %s found\n",qPrint(root->name));
7555 //if (!relates.empty()) printf(" Relates %s\n",qPrint(relates));
7556 findMember(root,
7557 relates,
7558 type,
7559 args,
7560 root->name,
7561 false,
7562 false);
7563 }
7564 else if (root->section.isExportedInterface() ||
7565 root->section.isIncludedService())
7566 {
7567 findMember(root,
7568 relates,
7569 type,
7570 args,
7571 type + " " + root->name,
7572 false,
7573 false);
7574 }
7575 else
7576 {
7577 // skip section
7578 //printf("skip section\n");
7579 }
7580}
7581
7582static void findMemberDocumentation(const Entry *root)
7583{
7584 if (root->section.isMemberDoc() ||
7585 root->section.isOverloadDoc() ||
7586 root->section.isFunction() ||
7587 root->section.isVariable() ||
7588 root->section.isVariableDoc() ||
7589 root->section.isDefine() ||
7590 root->section.isIncludedService() ||
7591 root->section.isExportedInterface()
7592 )
7593 {
7594 AUTO_TRACE();
7595 if (root->relatesType==RelatesType::Duplicate && !root->relates.empty())
7596 {
7598 }
7600 }
7601 for (const auto &e : root->children())
7602 {
7603 if (!e->section.isEnum())
7604 {
7605 findMemberDocumentation(e.get());
7606 }
7607 }
7608}
7609
7610//----------------------------------------------------------------------
7611
7612static void findObjCMethodDefinitions(const Entry *root)
7613{
7614 AUTO_TRACE();
7615 for (const auto &objCImpl : root->children())
7616 {
7617 if (objCImpl->section.isObjcImpl())
7618 {
7619 for (const auto &objCMethod : objCImpl->children())
7620 {
7621 if (objCMethod->section.isFunction())
7622 {
7623 //printf(" Found ObjC method definition %s\n",qPrint(objCMethod->name));
7624 findMember(objCMethod.get(),
7625 objCMethod->relates,
7626 objCMethod->type,
7627 objCMethod->args,
7628 objCMethod->type+" "+objCImpl->name+"::"+objCMethod->name+" "+objCMethod->args,
7629 false,true);
7630 objCMethod->section=EntryType::makeEmpty();
7631 }
7632 }
7633 }
7634 }
7635}
7636
7637//----------------------------------------------------------------------
7638// find and add the enumeration to their classes, namespaces or files
7639
7640static void findEnums(const Entry *root)
7641{
7642 if (root->section.isEnum())
7643 {
7644 AUTO_TRACE("name={}",root->name);
7645 ClassDefMutable *cd = nullptr;
7646 FileDef *fd = nullptr;
7647 NamespaceDefMutable *nd = nullptr;
7648 MemberNameLinkedMap *mnsd = nullptr;
7649 bool isGlobal = false;
7650 bool isRelated = false;
7651 bool isMemberOf = false;
7652 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7653
7654 DString name;
7655 DString scope;
7656
7657 if (size_t i = root->name.rfind("::"); i!=DString::npos) // scope is specified
7658 {
7659 scope=root->name.left(i); // extract scope
7660 if (root->lang==SrcLangExt::CSharp)
7661 {
7662 scope = mangleCSharpGenericName(scope);
7663 }
7664 name=root->name.right(root->name.length()-i-2); // extract name
7665 if ((cd=getClassMutable(scope))==nullptr)
7666 {
7668 }
7669 }
7670 else // no scope, check the scope in which the docs where found
7671 {
7672 if (root->parent()->section.isScope() && !root->parent()->name.empty()) // found enum docs inside a compound
7673 {
7674 scope=root->parent()->name;
7675 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7676 }
7677 name=root->name;
7678 }
7679
7680 if (!root->relates.empty())
7681 { // related member, prefix user specified scope
7682 isRelated=true;
7683 isMemberOf=(root->relatesType==RelatesType::MemberOf);
7684 if (getClass(root->relates)==nullptr && !scope.empty())
7685 scope=mergeScopes(scope,root->relates);
7686 else
7687 scope=root->relates;
7688 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7689 }
7690
7691 if (cd && !name.empty()) // found a enum inside a compound
7692 {
7693 //printf("Enum '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7694 fd=nullptr;
7696 isGlobal=false;
7697 }
7698 else if (nd) // found enum inside namespace
7699 {
7701 isGlobal=true;
7702 }
7703 else // found a global enum
7704 {
7705 fd=root->fileDef();
7707 isGlobal=true;
7708 }
7709
7710 if (!name.empty())
7711 {
7712 // new enum type
7713 AUTO_TRACE_ADD("new enum {} at line {} of {}",name,root->bodyLine,root->fileName);
7714 auto md = createMemberDef(
7715 root->fileName,root->startLine,root->startColumn,
7716 DString(),name,DString(),DString(),
7717 root->protection,Specifier::Normal,false,
7718 isMemberOf ? Relationship::Foreign : isRelated ? Relationship::Related : Relationship::Member,
7719 MemberType::Enumeration,
7721 auto mmd = toMemberDefMutable(md.get());
7722 mmd->setTagInfo(root->tagInfo());
7723 mmd->setLanguage(root->lang);
7724 mmd->setId(root->id);
7725 if (!isGlobal) mmd->setMemberClass(cd); else mmd->setFileDef(fd);
7726 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7727 mmd->setBodyDef(root->fileDef());
7728 mmd->setMemberSpecifiers(root->spec);
7729 mmd->setVhdlSpecifiers(root->vhdlSpec);
7730 mmd->setEnumBaseType(root->args);
7731 //printf("Enum %s definition at line %d of %s: protection=%d scope=%s\n",
7732 // qPrint(root->name),root->bodyLine,qPrint(root->fileName),root->protection,cd?qPrint(cd->name()):"<none>");
7733 mmd->addSectionsToDefinition(root->anchors);
7734 mmd->setMemberGroupId(root->mGrpId);
7736 mmd->addQualifiers(root->qualifiers);
7737 //printf("%s::setRefItems(%zu)\n",qPrint(md->name()),root->sli.size());
7738 mmd->setRefItems(root->sli);
7739 mmd->setRequirementReferences(root->rqli);
7740 //printf("found enum %s nd=%p\n",qPrint(md->name()),nd);
7741 bool defSet=false;
7742
7743 DString baseType = root->args;
7744 if (!baseType.empty())
7745 {
7746 baseType.prepend(" : ");
7747 }
7748
7749 if (nd)
7750 {
7751 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python)
7752 {
7753 mmd->setDefinition(name+baseType);
7754 }
7755 else
7756 {
7757 mmd->setDefinition(nd->name()+"::"+name+baseType);
7758 }
7759 //printf("definition=%s\n",md->definition());
7760 defSet=true;
7761 mmd->setNamespace(nd);
7762 nd->insertMember(md.get());
7763 }
7764
7765 // even if we have already added the enum to a namespace, we still
7766 // also want to add it to other appropriate places such as file
7767 // or class.
7768 if (isGlobal && (nd==nullptr || !nd->isAnonymous()))
7769 {
7770 if (!defSet) mmd->setDefinition(name+baseType);
7771 if (fd==nullptr && root->parent())
7772 {
7773 fd=root->parent()->fileDef();
7774 }
7775 if (fd)
7776 {
7777 mmd->setFileDef(fd);
7778 fd->insertMember(md.get());
7779 }
7780 }
7781 else if (cd)
7782 {
7783 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python)
7784 {
7785 mmd->setDefinition(name+baseType);
7786 }
7787 else
7788 {
7789 mmd->setDefinition(cd->name()+"::"+name+baseType);
7790 }
7791 cd->insertMember(md.get());
7792 cd->insertUsedFile(fd);
7793 }
7794 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7795 mmd->setDocsForDefinition(!root->proto);
7796 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7797 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7798
7799 //printf("Adding member=%s\n",qPrint(md->name()));
7800 addMemberToGroups(root,md.get());
7802
7803 MemberName *mn = mnsd->add(name);
7804 mn->push_back(std::move(md));
7805 }
7806 }
7807 else
7808 {
7809 for (const auto &e : root->children()) findEnums(e.get());
7810 }
7811}
7812
7813//----------------------------------------------------------------------
7814
7815static void addEnumValuesToEnums(const Entry *root)
7816{
7817 if (root->section.isEnum())
7818 // non anonymous enumeration
7819 {
7820 AUTO_TRACE("name={}",root->name);
7821 ClassDefMutable *cd = nullptr;
7822 FileDef *fd = nullptr;
7823 NamespaceDefMutable *nd = nullptr;
7824 MemberNameLinkedMap *mnsd = nullptr;
7825 bool isGlobal = false;
7826 bool isRelated = false;
7827 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7828
7829 DString name;
7830 DString scope;
7831
7832 if (size_t i = root->name.rfind("::"); i!=DString::npos) // scope is specified
7833 {
7834 scope=root->name.left(i); // extract scope
7835 if (root->lang==SrcLangExt::CSharp)
7836 {
7837 scope = mangleCSharpGenericName(scope);
7838 }
7839 name=root->name.right(root->name.length()-i-2); // extract name
7840 if ((cd=getClassMutable(scope))==nullptr)
7841 {
7843 }
7844 }
7845 else // no scope, check the scope in which the docs where found
7846 {
7847 if (root->parent()->section.isScope() && !root->parent()->name.empty()) // found enum docs inside a compound
7848 {
7849 scope=root->parent()->name;
7850 if (root->lang==SrcLangExt::CSharp)
7851 {
7852 scope = mangleCSharpGenericName(scope);
7853 }
7854 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7855 }
7856 name=root->name;
7857 }
7858
7859 if (!root->relates.empty())
7860 { // related member, prefix user specified scope
7861 isRelated=true;
7862 if (getClassMutable(root->relates)==nullptr && !scope.empty())
7863 scope=mergeScopes(scope,root->relates);
7864 else
7865 scope=root->relates;
7866 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7867 }
7868
7869 if (cd && !name.empty()) // found a enum inside a compound
7870 {
7871 //printf("Enum in class '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7872 fd=nullptr;
7874 isGlobal=false;
7875 }
7876 else if (nd && !nd->isAnonymous()) // found enum inside namespace
7877 {
7878 //printf("Enum in namespace '%s'::'%s'\n",qPrint(nd->name()),qPrint(name));
7880 isGlobal=true;
7881 }
7882 else // found a global enum
7883 {
7884 fd=root->fileDef();
7885 //printf("Enum in file '%s': '%s'\n",qPrint(fd->name()),qPrint(name));
7887 isGlobal=true;
7888 }
7889
7890 if (!name.empty())
7891 {
7892 //printf("** name=%s\n",qPrint(name));
7893 MemberName *mn = mnsd->find(name); // for all members with this name
7894 if (mn)
7895 {
7896 struct EnumValueInfo
7897 {
7898 EnumValueInfo(const DString &n,std::unique_ptr<MemberDef> &&md) :
7899 name(n), member(std::move(md)) {}
7900 DString name;
7901 std::unique_ptr<MemberDef> member;
7902 };
7903 std::vector< EnumValueInfo > extraMembers;
7904 // for each enum in this list
7905 for (const auto &imd : *mn)
7906 {
7907 MemberDefMutable *md = toMemberDefMutable(imd.get());
7908 // use raw pointer in this loop, since we modify mn and can then invalidate mdp.
7909 if (md && md->isEnumerate() && !root->children().empty())
7910 {
7911 AUTO_TRACE_ADD("enum {} with {} children",md->name(),root->children().size());
7912 for (const auto &e : root->children())
7913 {
7914 SrcLangExt sle = root->lang;
7915 bool isJavaLike = sle==SrcLangExt::CSharp || sle==SrcLangExt::Java || sle==SrcLangExt::XML;
7916 if ( isJavaLike || root->spec.isStrong())
7917 {
7918 if (sle == SrcLangExt::Cpp && e->section.isDefine()) continue;
7919 // Unlike classic C/C++ enums, for C++11, C# & Java enum
7920 // values are only visible inside the enum scope, so we must create
7921 // them here and only add them to the enum
7922 //printf("md->qualifiedName()=%s e->name=%s tagInfo=%p name=%s\n",
7923 // qPrint(md->qualifiedName()),qPrint(e->name),(void*)e->tagInfo(),qPrint(e->name));
7924 DString qualifiedName = root->name;
7925 if (size_t i = qualifiedName.rfind("::"); i!=DString::npos && sle==SrcLangExt::CSharp)
7926 {
7927 qualifiedName = mangleCSharpGenericName(qualifiedName.left(i))+qualifiedName.mid(i);
7928 }
7929 if (isJavaLike)
7930 {
7931 qualifiedName=substitute(qualifiedName,"::",".");
7932 }
7933 if (md->qualifiedName()==qualifiedName) // enum value scope matches that of the enum
7934 {
7935 DString fileName = e->fileName;
7936 if (fileName.empty() && e->tagInfo())
7937 {
7938 fileName = e->tagInfo()->tagName;
7939 }
7940 AUTO_TRACE_ADD("strong enum value {}",e->name);
7941 auto fmd = createMemberDef(
7942 fileName,e->startLine,e->startColumn,
7943 e->type,e->name,e->args,DString(),
7944 e->protection, Specifier::Normal,e->isStatic,Relationship::Member,
7945 MemberType::EnumValue,ArgumentList(),ArgumentList(),e->metaData);
7946 auto fmmd = toMemberDefMutable(fmd.get());
7947 NamespaceDef *mnd = md->getNamespaceDef();
7948 if (md->getClassDef())
7949 fmmd->setMemberClass(md->getClassDef());
7950 else if (mnd && (mnd->isLinkable() || mnd->isAnonymous()))
7951 fmmd->setNamespace(mnd);
7952 else if (md->getFileDef())
7953 fmmd->setFileDef(md->getFileDef());
7954 fmmd->setOuterScope(md->getOuterScope());
7955 fmmd->setTagInfo(e->tagInfo());
7956 fmmd->setLanguage(e->lang);
7957 fmmd->setBodySegment(e->startLine,e->bodyLine,e->endBodyLine);
7958 fmmd->setBodyDef(e->fileDef());
7959 fmmd->setId(e->id);
7960 fmmd->setDocumentation(e->doc,e->docFile,e->docLine);
7961 fmmd->setBriefDescription(e->brief,e->briefFile,e->briefLine);
7962 fmmd->addSectionsToDefinition(e->anchors);
7963 fmmd->setInitializer(e->initializer.str());
7964 fmmd->setMaxInitLines(e->initLines);
7965 fmmd->setMemberGroupId(e->mGrpId);
7966 fmmd->setExplicitExternal(e->explicitExternal,fileName,e->startLine,e->startColumn);
7967 fmmd->setRefItems(e->sli);
7968 fmmd->setRequirementReferences(e->rqli);
7969 fmmd->setAnchor();
7970 md->insertEnumField(fmd.get());
7971 fmmd->setEnumScope(md,true);
7972 extraMembers.emplace_back(e->name,std::move(fmd));
7973 }
7974 }
7975 else
7976 {
7977 AUTO_TRACE_ADD("enum value {}",e->name);
7978 //printf("e->name=%s isRelated=%d\n",qPrint(e->name),isRelated);
7979 MemberName *fmn=nullptr;
7980 MemberNameLinkedMap *emnsd = isRelated ? Doxygen::functionNameLinkedMap : mnsd;
7981 if (!e->name.empty() && (fmn=emnsd->find(e->name)))
7982 // get list of members with the same name as the field
7983 {
7984 for (const auto &ifmd : *fmn)
7985 {
7986 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
7987 if (fmd && fmd->isEnumValue() && fmd->getOuterScope()==md->getOuterScope()) // in same scope
7988 {
7989 //printf("found enum value with same name %s in scope %s\n",
7990 // qPrint(fmd->name()),qPrint(fmd->getOuterScope()->name()));
7991 if (nd && !nd->isAnonymous())
7992 {
7993 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
7994 {
7995 const NamespaceDef *fnd=fmd->getNamespaceDef();
7996 if (fnd==nd) // enum value is inside a namespace
7997 {
7998 md->insertEnumField(fmd);
7999 fmd->setEnumScope(md);
8000 }
8001 }
8002 }
8003 else if (isGlobal)
8004 {
8005 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
8006 {
8007 const FileDef *ffd=fmd->getFileDef();
8008 if (ffd==fd && ffd==md->getFileDef()) // enum value has file scope
8009 {
8010 md->insertEnumField(fmd);
8011 fmd->setEnumScope(md);
8012 }
8013 }
8014 }
8015 else if (isRelated && cd) // reparent enum value to
8016 // match the enum's scope
8017 {
8018 md->insertEnumField(fmd); // add field def to list
8019 fmd->setEnumScope(md); // cross ref with enum name
8020 fmd->setEnumClassScope(cd); // cross ref with enum name
8021 fmd->setOuterScope(cd);
8022 fmd->makeRelated();
8023 cd->insertMember(fmd);
8024 }
8025 else
8026 {
8027 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
8028 {
8029 const ClassDef *fcd=fmd->getClassDef();
8030 if (fcd==cd) // enum value is inside a class
8031 {
8032 //printf("Inserting enum field %s in enum scope %s\n",
8033 // qPrint(fmd->name()),qPrint(md->name()));
8034 md->insertEnumField(fmd); // add field def to list
8035 fmd->setEnumScope(md); // cross ref with enum name
8036 }
8037 }
8038 }
8039 }
8040 }
8041 }
8042 }
8043 }
8044 }
8045 }
8046 // move the newly added members into mn
8047 for (auto &e : extraMembers)
8048 {
8049 MemberName *emn=mnsd->add(e.name);
8050 emn->push_back(std::move(e.member));
8051 }
8052 }
8053 }
8054 }
8055 else
8056 {
8057 for (const auto &e : root->children()) addEnumValuesToEnums(e.get());
8058 }
8059}
8060
8061//----------------------------------------------------------------------
8062
8063static void addEnumDocs(const Entry *root,MemberDefMutable *md)
8064{
8065 AUTO_TRACE();
8066 // documentation outside a compound overrides the documentation inside it
8067 {
8068 md->setDocumentation(root->doc,root->docFile,root->docLine);
8069 md->setDocsForDefinition(!root->proto);
8070 }
8071
8072 // brief descriptions inside a compound override the documentation
8073 // outside it
8074 {
8075 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
8076 }
8077
8078 if (md->inbodyDocumentation().empty() || !root->parent()->name.empty())
8079 {
8081 }
8082
8083 if (root->mGrpId!=-1 && md->getMemberGroupId()==-1)
8084 {
8085 md->setMemberGroupId(root->mGrpId);
8086 }
8087
8089 md->setRefItems(root->sli);
8090 md->setRequirementReferences(root->rqli);
8091
8092 const GroupDef *gd=md->getGroupDef();
8093 if (gd==nullptr && !root->groups.empty()) // member not grouped but out-of-line documentation is
8094 {
8095 addMemberToGroups(root,md);
8096 }
8098}
8099
8100//----------------------------------------------------------------------
8101// Search for the name in the associated groups. If a matching member
8102// definition exists, then add the documentation to it and return true,
8103// otherwise false.
8104
8105static bool tryAddEnumDocsToGroupMember(const Entry *root,const DString &name)
8106{
8107 for (const auto &g : root->groups)
8108 {
8109 const GroupDef *gd = Doxygen::groupLinkedMap->find(g.groupname);
8110 if (gd)
8111 {
8112 MemberList *ml = gd->getMemberList(MemberListType::DecEnumMembers());
8113 if (ml)
8114 {
8115 MemberDefMutable *md = toMemberDefMutable(ml->find(name));
8116 if (md)
8117 {
8118 addEnumDocs(root,md);
8119 return true;
8120 }
8121 }
8122 }
8123 else if (!gd && g.pri == Grouping::GROUPING_INGROUP)
8124 {
8125 warn(root->fileName, root->startLine,
8126 "Found non-existing group '{}' for the command '{}', ignoring command",
8127 g.groupname, Grouping::getGroupPriName( g.pri )
8128 );
8129 }
8130 }
8131
8132 return false;
8133}
8134
8135//----------------------------------------------------------------------
8136// find the documentation blocks for the enumerations
8137
8138static void findEnumDocumentation(const Entry *root)
8139{
8140 if (root->section.isEnumDoc() &&
8141 !root->name.empty() &&
8142 root->name.at(0)!='@' // skip anonymous enums
8143 )
8144 {
8145 DString name;
8146 DString scope;
8147 if (size_t i = root->name.rfind("::"); i!=DString::npos) // scope is specified as part of the name
8148 {
8149 name=root->name.mid(i+2); // extract name
8150 scope=root->name.left(i); // extract scope
8151 //printf("Scope='%s' Name='%s'\n",qPrint(scope),qPrint(name));
8152 }
8153 else // just the name
8154 {
8155 name=root->name;
8156 }
8157 if (root->parent()->section.isScope() && !root->parent()->name.empty()) // found enum docs inside a compound
8158 {
8159 if (!scope.empty()) scope.prepend("::");
8160 scope.prepend(root->parent()->name);
8161 }
8162 const ClassDef *cd = getClass(scope);
8164 const FileDef *fd = root->fileDef();
8165 AUTO_TRACE("Found docs for enum with name '{}' and scope '{}' in context '{}' cd='{}', nd='{}' fd='{}'",
8166 name,scope,root->parent()->name,
8167 cd ? cd->name() : DString("<none>"),
8168 nd ? nd->name() : DString("<none>"),
8169 fd ? fd->name() : DString("<none>"));
8170
8171 if (!name.empty())
8172 {
8173 bool found = tryAddEnumDocsToGroupMember(root, name);
8174 if (!found)
8175 {
8177 if (mn)
8178 {
8179 for (const auto &imd : *mn)
8180 {
8181 MemberDefMutable *md = toMemberDefMutable(imd.get());
8182 if (md && md->isEnumerate())
8183 {
8184 const ClassDef *mcd = md->getClassDef();
8185 const NamespaceDef *mnd = md->getNamespaceDef();
8186 const FileDef *mfd = md->getFileDef();
8187 if (cd && mcd==cd)
8188 {
8189 AUTO_TRACE_ADD("Match found for class scope");
8190 addEnumDocs(root,md);
8191 found = true;
8192 break;
8193 }
8194 else if (cd==nullptr && mcd==nullptr && nd!=nullptr && mnd==nd)
8195 {
8196 AUTO_TRACE_ADD("Match found for namespace scope");
8197 addEnumDocs(root,md);
8198 found = true;
8199 break;
8200 }
8201 else if (cd==nullptr && nd==nullptr && mcd==nullptr && mnd==nullptr && fd==mfd)
8202 {
8203 AUTO_TRACE_ADD("Match found for global scope");
8204 addEnumDocs(root,md);
8205 found = true;
8206 break;
8207 }
8208 }
8209 }
8210 }
8211 }
8212 if (!found)
8213 {
8214 warn(root->fileName,root->startLine, "Documentation for undefined enum '{}' found.", name);
8215 }
8216 }
8217 }
8218 for (const auto &e : root->children()) findEnumDocumentation(e.get());
8219}
8220
8221// search for each enum (member or function) in mnl if it has documented
8222// enum values.
8223static void findDEV(const MemberNameLinkedMap &mnsd)
8224{
8225 // for each member name
8226 for (const auto &mn : mnsd)
8227 {
8228 // for each member definition
8229 for (const auto &imd : *mn)
8230 {
8231 MemberDefMutable *md = toMemberDefMutable(imd.get());
8232 if (md && md->isEnumerate()) // member is an enum
8233 {
8234 int documentedEnumValues=0;
8235 // for each enum value
8236 for (const auto &fmd : md->enumFieldList())
8237 {
8238 if (fmd->isLinkableInProject()) documentedEnumValues++;
8239 }
8240 // at least one enum value is documented
8241 if (documentedEnumValues>0) md->setDocumentedEnumValues(true);
8242 }
8243 }
8244 }
8245}
8246
8247// search for each enum (member or function) if it has documented enum
8248// values.
8254
8255//----------------------------------------------------------------------
8256
8258{
8259 auto &index = Index::instance();
8260 // for each class member name
8261 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8262 {
8263 // for each member definition
8264 for (const auto &md : *mn)
8265 {
8266 index.addClassMemberNameToIndex(md.get());
8267 if (md->getModuleDef())
8268 {
8269 index.addModuleMemberNameToIndex(md.get());
8270 }
8271 }
8272 }
8273 // for each file/namespace function name
8274 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8275 {
8276 // for each member definition
8277 for (const auto &md : *mn)
8278 {
8279 if (md->getNamespaceDef())
8280 {
8281 index.addNamespaceMemberNameToIndex(md.get());
8282 }
8283 else
8284 {
8285 index.addFileMemberNameToIndex(md.get());
8286 }
8287 if (md->getModuleDef())
8288 {
8289 index.addModuleMemberNameToIndex(md.get());
8290 }
8291 }
8292 }
8293
8294 index.sortMemberIndexLists();
8295}
8296
8297//----------------------------------------------------------------------
8298
8299static void addToIndices()
8300{
8301 for (const auto &cd : *Doxygen::classLinkedMap)
8302 {
8303 if (cd->isLinkableInProject())
8304 {
8305 Doxygen::indexList->addIndexItem(cd.get(),nullptr);
8306 if (Doxygen::searchIndex.enabled())
8307 {
8308 Doxygen::searchIndex.setCurrentDoc(cd.get(),cd->anchor(),false);
8309 Doxygen::searchIndex.addWord(cd->localName(),true);
8310 }
8311 }
8312 }
8313
8314 for (const auto &cd : *Doxygen::conceptLinkedMap)
8315 {
8316 if (cd->isLinkableInProject())
8317 {
8318 Doxygen::indexList->addIndexItem(cd.get(),nullptr);
8319 if (Doxygen::searchIndex.enabled())
8320 {
8321 Doxygen::searchIndex.setCurrentDoc(cd.get(),cd->anchor(),false);
8322 Doxygen::searchIndex.addWord(cd->localName(),true);
8323 }
8324 }
8325 }
8326
8327 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8328 {
8329 if (nd->isLinkableInProject())
8330 {
8331 Doxygen::indexList->addIndexItem(nd.get(),nullptr);
8332 if (Doxygen::searchIndex.enabled())
8333 {
8334 Doxygen::searchIndex.setCurrentDoc(nd.get(),nd->anchor(),false);
8335 Doxygen::searchIndex.addWord(nd->localName(),true);
8336 }
8337 }
8338 }
8339
8340 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8341 {
8342 for (const auto &fd : *fn)
8343 {
8344 if (Doxygen::searchIndex.enabled() && fd->isLinkableInProject())
8345 {
8346 Doxygen::searchIndex.setCurrentDoc(fd.get(),fd->anchor(),false);
8347 Doxygen::searchIndex.addWord(fd->localName(),true);
8348 }
8349 }
8350 }
8351
8352 auto addWordsForTitle = [](const Definition *d,const DString &anchor,const DString &title)
8353 {
8355 if (Doxygen::searchIndex.enabled())
8356 {
8357 Doxygen::searchIndex.setCurrentDoc(d,anchor,false);
8358 std::string s = title.str();
8359 static const reg::Ex re(R"(\a[\w-]*)");
8360 reg::Iterator it(s,re);
8362 for (; it!=end ; ++it)
8363 {
8364 const auto &match = *it;
8365 std::string matchStr = match.str();
8366 Doxygen::searchIndex.addWord(matchStr,true);
8367 }
8368 }
8369 };
8370
8371 for (const auto &gd : *Doxygen::groupLinkedMap)
8372 {
8373 if (gd->isLinkableInProject())
8374 {
8375 addWordsForTitle(gd.get(),gd->anchor(),gd->groupTitle());
8376 }
8377 }
8378
8379 for (const auto &pd : *Doxygen::pageLinkedMap)
8380 {
8381 if (pd->isLinkableInProject())
8382 {
8383 addWordsForTitle(pd.get(),pd->anchor(),pd->title());
8384 }
8385 }
8386
8388 {
8389 addWordsForTitle(Doxygen::mainPage.get(),Doxygen::mainPage->anchor(),Doxygen::mainPage->title());
8390 }
8391
8392 auto addMemberToSearchIndex = [](const MemberDef *md)
8393 {
8394 if (Doxygen::searchIndex.enabled())
8395 {
8396 Doxygen::searchIndex.setCurrentDoc(md,md->anchor(),false);
8397 DString ln=md->localName();
8398 DString qn=md->qualifiedName();
8400 if (ln!=qn)
8401 {
8403 if (md->getClassDef())
8404 {
8405 Doxygen::searchIndex.addWord(md->getClassDef()->displayName(),true);
8406 }
8407 if (md->getNamespaceDef())
8408 {
8409 Doxygen::searchIndex.addWord(md->getNamespaceDef()->displayName(),true);
8410 }
8411 }
8412 }
8413 };
8414
8415 auto getScope = [](const MemberDef *md)
8416 {
8417 const Definition *scope = nullptr;
8418 if (md->getGroupDef()) scope = md->getGroupDef();
8419 else if (md->getClassDef()) scope = md->getClassDef();
8420 else if (md->getNamespaceDef()) scope = md->getNamespaceDef();
8421 else if (md->getFileDef()) scope = md->getFileDef();
8422 return scope;
8423 };
8424
8425 auto addMemberToIndices = [addMemberToSearchIndex,getScope](const MemberDef *md)
8426 {
8427 if (md->isLinkableInProject())
8428 {
8429 if (!(md->isEnumerate() && md->isAnonymous()))
8430 {
8431 Doxygen::indexList->addIndexItem(getScope(md),md);
8432 addMemberToSearchIndex(md);
8433 }
8434 if (md->isEnumerate())
8435 {
8436 for (const auto &fmd : md->enumFieldList())
8437 {
8438 Doxygen::indexList->addIndexItem(getScope(fmd),fmd);
8439 addMemberToSearchIndex(fmd);
8440 }
8441 }
8442 }
8443 };
8444
8445 // for each class member name
8446 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8447 {
8448 // for each member definition
8449 for (const auto &md : *mn)
8450 {
8451 addMemberToIndices(md.get());
8452 }
8453 }
8454 // for each file/namespace function name
8455 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8456 {
8457 // for each member definition
8458 for (const auto &md : *mn)
8459 {
8460 addMemberToIndices(md.get());
8461 }
8462 }
8463}
8464
8465//----------------------------------------------------------------------
8466
8468{
8469 // for each member name
8470 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8471 {
8472 // for each member definition
8473 for (const auto &imd : *mn)
8474 {
8475 MemberDefMutable *md = toMemberDefMutable(imd.get());
8476 if (md)
8477 {
8479 }
8480 }
8481 }
8482 // for each member name
8483 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8484 {
8485 // for each member definition
8486 for (const auto &imd : *mn)
8487 {
8488 MemberDefMutable *md = toMemberDefMutable(imd.get());
8489 if (md)
8490 {
8492 }
8493 }
8494 }
8495}
8496
8497// recursive helper function looking for reimplements/implemented
8498// by relations between class cd and direct or indirect base class bcd
8500{
8501 for (const auto &mn : cd->memberNameInfoLinkedMap()) // for each member in class cd with a unique name
8502 {
8503 for (const auto &imd : *mn) // for each member with a given name
8504 {
8505 MemberDefMutable *md = toMemberDefMutable(imd->memberDef());
8506 if (md && (md->isFunction() || md->isCSharpProperty())) // filter on reimplementable members
8507 {
8508 ClassDef *mbcd = bcd->classDef;
8509 if (mbcd && mbcd->isLinkable()) // filter on linkable classes
8510 {
8511 const auto &bmn = mbcd->memberNameInfoLinkedMap();
8512 const auto &bmni = bmn.find(mn->memberName());
8513 if (bmni) // there are base class members with the same name
8514 {
8515 for (const auto &ibmd : *bmni) // for base class member with that name
8516 {
8517 MemberDefMutable *bmd = toMemberDefMutable(ibmd->memberDef());
8518 if (bmd) // not part of an inline namespace
8519 {
8520 auto lang = bmd->getLanguage();
8521 auto compType = mbcd->compoundType();
8522 if (bmd->virtualness()!=Specifier::Normal ||
8523 lang==SrcLangExt::Python ||
8524 lang==SrcLangExt::Java ||
8525 lang==SrcLangExt::PHP ||
8526 compType==ClassDef::Interface ||
8527 compType==ClassDef::Protocol)
8528 {
8529 const ArgumentList &bmdAl = bmd->argumentList();
8530 const ArgumentList &mdAl = md->argumentList();
8531 //printf(" Base argList='%s'\n Super argList='%s'\n",
8532 // qPrint(argListToString(bmdAl)),
8533 // qPrint(argListToString(mdAl))
8534 // );
8535 if (
8536 lang==SrcLangExt::Python ||
8537 matchArguments2(bmd->getOuterScope(),bmd->getFileDef(),bmd->typeString(),&bmdAl,
8538 md->getOuterScope(), md->getFileDef(), md->typeString(),&mdAl,
8539 true,lang
8540 )
8541 )
8542 {
8543 if (lang==SrcLangExt::Python && md->name().startsWith("__")) continue; // private members do not reimplement
8544 //printf("match!\n");
8545 const MemberDef *rmd = md->reimplements();
8546 if (rmd==nullptr) // not already assigned
8547 {
8548 //printf("%s: setting (new) reimplements member %s\n",qPrint(md->qualifiedName()),qPrint(bmd->qualifiedName()));
8549 md->setReimplements(bmd);
8550 }
8551 //printf("%s: add reimplementedBy member %s\n",qPrint(bmd->qualifiedName()),qPrint(md->qualifiedName()));
8552 bmd->insertReimplementedBy(md);
8553 }
8554 else
8555 {
8556 //printf("no match!\n");
8557 }
8558 }
8559 }
8560 }
8561 }
8562 }
8563 }
8564 }
8565 }
8566
8567 // do also for indirect base classes
8568 for (const auto &bbcd : bcd->classDef->baseClasses())
8569 {
8571 }
8572}
8573
8574//----------------------------------------------------------------------
8575// computes the relation between all members. For each member 'm'
8576// the members that override the implementation of 'm' are searched and
8577// the member that 'm' overrides is searched.
8578
8580{
8581 for (const auto &cd : *Doxygen::classLinkedMap)
8582 {
8583 if (cd->isLinkable())
8584 {
8585 for (const auto &bcd : cd->baseClasses())
8586 {
8588 }
8589 }
8590 }
8591}
8592
8593//----------------------------------------------------------------------------
8594
8596{
8597 // for each class
8598 for (const auto &cd : *Doxygen::classLinkedMap)
8599 {
8600 // that is a template
8601 for (const auto &ti : cd->getTemplateInstances())
8602 {
8603 ClassDefMutable *tcdm = toClassDefMutable(ti.classDef);
8604 if (tcdm)
8605 {
8606 tcdm->addMembersToTemplateInstance(cd.get(),cd->templateArguments(),ti.templSpec);
8607 }
8608 }
8609 }
8610}
8611
8612//----------------------------------------------------------------------------
8613
8614static void mergeCategories()
8615{
8616 AUTO_TRACE();
8617 // merge members of categories into the class they extend
8618 for (const auto &cd : *Doxygen::classLinkedMap)
8619 {
8620 if (size_t i=cd->name().find('('); i!=DString::npos) // it is an Objective-C category
8621 {
8622 DString baseName=cd->name().left(i);
8623 ClassDefMutable *baseClass=toClassDefMutable(Doxygen::classLinkedMap->find(baseName));
8624 if (baseClass)
8625 {
8626 AUTO_TRACE_ADD("merging members of category {} into {}",cd->name(),baseClass->name());
8627 baseClass->mergeCategory(cd.get());
8628 }
8629 }
8630 }
8631}
8632
8633// builds the list of all members for each class
8634
8636{
8637 // merge the member list of base classes into the inherited classes.
8638 for (const auto &cd : *Doxygen::classLinkedMap)
8639 {
8640 if (// !cd->isReference() && // not an external class
8641 cd->subClasses().empty() && // is a root of the hierarchy
8642 !cd->baseClasses().empty()) // and has at least one base class
8643 {
8644 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8645 if (cdm)
8646 {
8647 //printf("*** merging members for %s\n",qPrint(cd->name()));
8648 cdm->mergeMembers();
8649 }
8650 }
8651 }
8652 // now sort the member list of all members for all classes.
8653 for (const auto &cd : *Doxygen::classLinkedMap)
8654 {
8655 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8656 if (cdm)
8657 {
8658 cdm->sortAllMembersList();
8659 }
8660 }
8661}
8662
8663//----------------------------------------------------------------------------
8664
8666{
8667 auto processSourceFile = [](FileDef *fd,OutputList &ol,ClangTUParser *parser)
8668 {
8669 bool showSources = fd->generateSourceFile() && !Htags::useHtags; // sources need to be shown in the output
8670 bool parseSources = !fd->isReference() && Doxygen::parseSourcesNeeded; // we needed to parse the sources even if we do not show them
8671 if (showSources)
8672 {
8673 msg("Generating code for file {}...\n",fd->docName());
8674 fd->writeSourceHeader(ol);
8675 fd->writeSourceBody(ol,parser);
8676 fd->writeSourceFooter(ol);
8677 }
8678 else if (parseSources)
8679 {
8680 msg("Parsing code for file {}...\n",fd->docName());
8681 fd->parseSource(parser);
8682 }
8683 };
8684 if (!Doxygen::inputNameLinkedMap->empty())
8685 {
8686#if USE_LIBCLANG
8688 {
8689 StringUnorderedSet processedFiles;
8690
8691 // create a dictionary with files to process
8692 StringUnorderedSet filesToProcess;
8693
8694 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8695 {
8696 for (const auto &fd : *fn)
8697 {
8698 filesToProcess.insert(fd->absFilePath().str());
8699 }
8700 }
8701 // process source files (and their include dependencies)
8702 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8703 {
8704 for (const auto &fd : *fn)
8705 {
8706 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt::Cpp &&
8707 (fd->generateSourceFile() ||
8709 )
8710 )
8711 {
8712 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8713 clangParser->parse();
8714 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8715
8716 for (auto incFile : clangParser->filesInSameTU())
8717 {
8718 if (filesToProcess.find(incFile)!=filesToProcess.end() && // part of input
8719 fd->absFilePath()!=incFile && // not same file
8720 processedFiles.find(incFile)==processedFiles.end()) // not yet marked as processed
8721 {
8722 StringVector moreFiles;
8723 bool ambig = false;
8724 FileDef *ifd=Doxygen::inputNameLinkedMap->findFileDef(incFile,ambig);
8725 if (ifd && !ifd->isReference())
8726 {
8727 processSourceFile(ifd,*g_outputList,clangParser.get());
8728 processedFiles.insert(incFile);
8729 }
8730 }
8731 }
8732 processedFiles.insert(fd->absFilePath().str());
8733 }
8734 }
8735 }
8736 // process remaining files
8737 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8738 {
8739 for (const auto &fd : *fn)
8740 {
8741 if (processedFiles.find(fd->absFilePath().str())==processedFiles.end()) // not yet processed
8742 {
8743 if (fd->getLanguage()==SrcLangExt::Cpp) // C/C++ file, use clang parser
8744 {
8745 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8746 clangParser->parse();
8747 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8748 }
8749 else // non C/C++ file, use built-in parser
8750 {
8751 processSourceFile(fd.get(),*g_outputList,nullptr);
8752 }
8753 }
8754 }
8755 }
8756 }
8757 else
8758#endif
8759 {
8760 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8761 if (numThreads>1)
8762 {
8763 msg("Generating code files using {} threads.\n",numThreads);
8764 struct SourceContext
8765 {
8766 SourceContext(FileDef *fd_,bool gen_,const OutputList &ol_)
8767 : fd(fd_), generateSourceFile(gen_), ol(ol_) {}
8768 FileDef *fd;
8769 bool generateSourceFile;
8770 OutputList ol;
8771 };
8772 ThreadPool threadPool(numThreads);
8773 std::vector< std::future< std::shared_ptr<SourceContext> > > results;
8774 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8775 {
8776 for (const auto &fd : *fn)
8777 {
8778 bool generateSourceFile = fd->generateSourceFile() && !Htags::useHtags;
8779 auto ctx = std::make_shared<SourceContext>(fd.get(),generateSourceFile,*g_outputList);
8780 auto processFile = [ctx]()
8781 {
8782 if (ctx->generateSourceFile)
8783 {
8784 msg("Generating code for file {}...\n",ctx->fd->docName());
8785 }
8786 else
8787 {
8788 msg("Parsing code for file {}...\n",ctx->fd->docName());
8789 }
8790 StringVector filesInSameTu;
8791 ctx->fd->getAllIncludeFilesRecursively(filesInSameTu);
8792 if (ctx->generateSourceFile) // sources need to be shown in the output
8793 {
8794 ctx->fd->writeSourceHeader(ctx->ol);
8795 ctx->fd->writeSourceBody(ctx->ol,nullptr);
8796 ctx->fd->writeSourceFooter(ctx->ol);
8797 }
8798 else if (!ctx->fd->isReference() && Doxygen::parseSourcesNeeded)
8799 // we needed to parse the sources even if we do not show them
8800 {
8801 ctx->fd->parseSource(nullptr);
8802 }
8803 return ctx;
8804 };
8805 results.emplace_back(threadPool.queue(processFile));
8806 }
8807 }
8808 for (auto &f : results)
8809 {
8810 auto ctx = f.get();
8811 }
8812 }
8813 else // single threaded version
8814 {
8815 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8816 {
8817 for (const auto &fd : *fn)
8818 {
8819 StringVector filesInSameTu;
8820 fd->getAllIncludeFilesRecursively(filesInSameTu);
8821 processSourceFile(fd.get(),*g_outputList,nullptr);
8822 }
8823 }
8824 }
8825 }
8826 }
8827}
8828
8829//----------------------------------------------------------------------------
8830
8831static void generateFileDocs()
8832{
8833 if (Index::instance().numDocumentedFiles()==0) return;
8834
8835 if (!Doxygen::inputNameLinkedMap->empty())
8836 {
8837 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8838 if (numThreads>1) // multi threaded processing
8839 {
8840 struct DocContext
8841 {
8842 DocContext(FileDef *fd_,const OutputList &ol_)
8843 : fd(fd_), ol(ol_) {}
8844 FileDef *fd;
8845 OutputList ol;
8846 };
8847 ThreadPool threadPool(numThreads);
8848 std::vector< std::future< std::shared_ptr<DocContext> > > results;
8849 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8850 {
8851 for (const auto &fd : *fn)
8852 {
8853 bool doc = fd->isLinkableInProject();
8854 if (doc)
8855 {
8856 auto ctx = std::make_shared<DocContext>(fd.get(),*g_outputList);
8857 auto processFile = [ctx]() {
8858 msg("Generating docs for file {}...\n",ctx->fd->docName());
8859 ctx->fd->writeDocumentation(ctx->ol);
8860 return ctx;
8861 };
8862 results.emplace_back(threadPool.queue(processFile));
8863 }
8864 }
8865 }
8866 for (auto &f : results)
8867 {
8868 auto ctx = f.get();
8869 }
8870 }
8871 else // single threaded processing
8872 {
8873 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8874 {
8875 for (const auto &fd : *fn)
8876 {
8877 bool doc = fd->isLinkableInProject();
8878 if (doc)
8879 {
8880 msg("Generating docs for file {}...\n",fd->docName());
8881 fd->writeDocumentation(*g_outputList);
8882 }
8883 }
8884 }
8885 }
8886 }
8887}
8888
8889//----------------------------------------------------------------------------
8890
8892{
8893 // add source references for class definitions
8894 for (const auto &cd : *Doxygen::classLinkedMap)
8895 {
8896 const FileDef *fd=cd->getBodyDef();
8897 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8898 {
8899 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),nullptr);
8900 }
8901 }
8902 // add source references for concept definitions
8903 for (const auto &cd : *Doxygen::conceptLinkedMap)
8904 {
8905 const FileDef *fd=cd->getBodyDef();
8906 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8907 {
8908 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),nullptr);
8909 }
8910 }
8911 // add source references for namespace definitions
8912 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8913 {
8914 const FileDef *fd=nd->getBodyDef();
8915 if (fd && nd->isLinkableInProject() && nd->getStartDefLine()!=-1)
8916 {
8917 const_cast<FileDef*>(fd)->addSourceRef(nd->getStartDefLine(),nd.get(),nullptr);
8918 }
8919 }
8920
8921 // add source references for member names
8922 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8923 {
8924 for (const auto &md : *mn)
8925 {
8926 //printf("class member %s: def=%s body=%d link?=%d\n",
8927 // qPrint(md->name()),
8928 // md->getBodyDef()?qPrint(md->getBodyDef()->name()):"<none>",
8929 // md->getStartBodyLine(),md->isLinkableInProject());
8930 const FileDef *fd=md->getBodyDef();
8931 if (fd &&
8932 md->getStartDefLine()!=-1 &&
8933 md->isLinkableInProject() &&
8935 )
8936 {
8937 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8938 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8939 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8940 }
8941 }
8942 }
8943 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8944 {
8945 for (const auto &md : *mn)
8946 {
8947 const FileDef *fd=md->getBodyDef();
8948 //printf("member %s body=[%d,%d] fd=%p link=%d parseSources=%d\n",
8949 // qPrint(md->name()),
8950 // md->getStartBodyLine(),md->getEndBodyLine(),fd,
8951 // md->isLinkableInProject(),
8952 // Doxygen::parseSourcesNeeded);
8953 if (fd &&
8954 md->getStartDefLine()!=-1 &&
8955 md->isLinkableInProject() &&
8957 )
8958 {
8959 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8960 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8961 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8962 }
8963 }
8964 }
8965}
8966
8967//----------------------------------------------------------------------------
8968
8969// add the macro definitions found during preprocessing as file members
8970static void buildDefineList()
8971{
8972 AUTO_TRACE();
8973 for (const auto &s : g_inputFiles)
8974 {
8975 auto it = Doxygen::macroDefinitions.find(s);
8977 {
8978 for (const auto &def : it->second)
8979 {
8980 auto md = createMemberDef(
8981 def.fileName,def.lineNr,def.columnNr,
8982 "#define",def.name,def.args,DString(),
8983 Protection::Public,Specifier::Normal,false,Relationship::Member,MemberType::Define,
8984 ArgumentList(),ArgumentList(),"");
8985 auto mmd = toMemberDefMutable(md.get());
8986
8987 if (!def.args.empty())
8988 {
8989 mmd->moveArgumentList(stringToArgumentList(SrcLangExt::Cpp, def.args));
8990 }
8991 mmd->setInitializer(def.definition);
8992 mmd->setFileDef(def.fileDef);
8993 mmd->setDefinition("#define "+def.name);
8994
8996 if (def.fileDef)
8997 {
8998 const MemberList *defMl = def.fileDef->getMemberList(MemberListType::DocDefineMembers());
8999 if (defMl)
9000 {
9001 const MemberDef *defMd = defMl->findRev(def.name);
9002 if (defMd) // definition already stored
9003 {
9004 mmd->setRedefineCount(defMd->redefineCount()+1);
9005 }
9006 }
9007 def.fileDef->insertMember(md.get());
9008 }
9009 AUTO_TRACE_ADD("adding macro {} with definition {}",def.name,def.definition);
9010 mn->push_back(std::move(md));
9011 }
9012 }
9013 }
9014}
9015
9016//----------------------------------------------------------------------------
9017
9018static void sortMemberLists()
9019{
9020 // sort class member lists
9021 for (const auto &cd : *Doxygen::classLinkedMap)
9022 {
9023 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9024 if (cdm)
9025 {
9026 cdm->sortMemberLists();
9027 }
9028 }
9029
9030 // sort namespace member lists
9031 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9032 {
9034 if (ndm)
9035 {
9036 ndm->sortMemberLists();
9037 }
9038 }
9039
9040 // sort file member lists
9041 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9042 {
9043 for (const auto &fd : *fn)
9044 {
9045 fd->sortMemberLists();
9046 }
9047 }
9048
9049 // sort group member lists
9050 for (const auto &gd : *Doxygen::groupLinkedMap)
9051 {
9052 gd->sortMemberLists();
9053 }
9054
9056}
9057
9058//----------------------------------------------------------------------------
9059
9060static bool isSymbolHidden(const Definition *d)
9061{
9062 bool hidden = d->isHidden();
9063 const Definition *parent = d->getOuterScope();
9064 return parent ? hidden || isSymbolHidden(parent) : hidden;
9065}
9066
9068{
9069 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9070 if (numThreads>1)
9071 {
9072 ThreadPool threadPool(numThreads);
9073 std::vector < std::future< void > > results;
9074 // queue the work
9075 for (const auto &[name,symList] : *Doxygen::symbolMap)
9076 {
9077 for (const auto &def : symList)
9078 {
9080 if (dm && !isSymbolHidden(def) && !def->isArtificial() && def->isLinkableInProject())
9081 {
9082 auto processTooltip = [dm]() {
9083 dm->computeTooltip();
9084 };
9085 results.emplace_back(threadPool.queue(processTooltip));
9086 }
9087 }
9088 }
9089 // wait for the results
9090 for (auto &f : results)
9091 {
9092 f.get();
9093 }
9094 }
9095 else
9096 {
9097 for (const auto &[name,symList] : *Doxygen::symbolMap)
9098 {
9099 for (const auto &def : symList)
9100 {
9102 if (dm && !isSymbolHidden(def) && !def->isArtificial() && def->isLinkableInProject())
9103 {
9104 dm->computeTooltip();
9105 }
9106 }
9107 }
9108 }
9109}
9110
9111//----------------------------------------------------------------------------
9112
9114{
9115 for (const auto &cd : *Doxygen::classLinkedMap)
9116 {
9117 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9118 if (cdm)
9119 {
9120 cdm->setAnonymousEnumType();
9121 }
9122 }
9123}
9124
9125//----------------------------------------------------------------------------
9126
9127static void countMembers()
9128{
9129 for (const auto &cd : *Doxygen::classLinkedMap)
9130 {
9131 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9132 if (cdm)
9133 {
9134 cdm->countMembers();
9135 }
9136 }
9137
9138 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9139 {
9141 if (ndm)
9142 {
9143 ndm->countMembers();
9144 }
9145 }
9146
9147 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9148 {
9149 for (const auto &fd : *fn)
9150 {
9151 fd->countMembers();
9152 }
9153 }
9154
9155 for (const auto &gd : *Doxygen::groupLinkedMap)
9156 {
9157 gd->countMembers();
9158 }
9159
9160 auto &mm = ModuleManager::instance();
9161 mm.countMembers();
9162}
9163
9164
9165//----------------------------------------------------------------------------
9166// generate the documentation for all classes
9167
9168static void generateDocsForClassList(const std::vector<ClassDefMutable*> &classList)
9169{
9170 AUTO_TRACE();
9171 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9172 if (numThreads>1) // multi threaded processing
9173 {
9174 struct DocContext
9175 {
9176 DocContext(ClassDefMutable *cd_,const OutputList &ol_)
9177 : cd(cd_), ol(ol_) {}
9178 ClassDefMutable *cd;
9179 OutputList ol;
9180 };
9181 ThreadPool threadPool(numThreads);
9182 std::vector< std::future< std::shared_ptr<DocContext> > > results;
9183 for (const auto &cd : classList)
9184 {
9185 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
9186 if (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9187 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
9188 )
9189 {
9190 auto ctx = std::make_shared<DocContext>(cd,*g_outputList);
9191 auto processFile = [ctx]()
9192 {
9193 msg("Generating docs for compound {}...\n",ctx->cd->displayName());
9194
9195 // skip external references, anonymous compounds and
9196 // template instances
9197 if (!ctx->cd->isHidden() && !ctx->cd->isEmbeddedInOuterScope() &&
9198 ctx->cd->isLinkableInProject() && !ctx->cd->isImplicitTemplateInstance())
9199 {
9200 ctx->cd->writeDocumentation(ctx->ol);
9201 ctx->cd->writeMemberList(ctx->ol);
9202 }
9203
9204 // even for undocumented classes, the inner classes can be documented.
9205 ctx->cd->writeDocumentationForInnerClasses(ctx->ol);
9206 return ctx;
9207 };
9208 results.emplace_back(threadPool.queue(processFile));
9209 }
9210 }
9211 for (auto &f : results)
9212 {
9213 auto ctx = f.get();
9214 }
9215 }
9216 else // single threaded processing
9217 {
9218 for (const auto &cd : classList)
9219 {
9220 //printf("cd=%s getOuterScope=%p global=%p hidden=%d embeddedInOuterScope=%d\n",
9221 // qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope,cd->isHidden(),cd->isEmbeddedInOuterScope());
9222 if (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9223 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
9224 )
9225 {
9226 // skip external references, anonymous compounds and
9227 // template instances
9228 if ( !cd->isHidden() && !cd->isEmbeddedInOuterScope() &&
9229 cd->isLinkableInProject() && !cd->isImplicitTemplateInstance())
9230 {
9231 msg("Generating docs for compound {}...\n",cd->displayName());
9232
9233 cd->writeDocumentation(*g_outputList);
9234 cd->writeMemberList(*g_outputList);
9235 }
9236 // even for undocumented classes, the inner classes can be documented.
9237 cd->writeDocumentationForInnerClasses(*g_outputList);
9238 }
9239 }
9240 }
9241}
9242
9243static void addClassAndNestedClasses(std::vector<ClassDefMutable*> &list,ClassDefMutable *cd)
9244{
9245 list.push_back(cd);
9246 for (const auto &innerCdi : cd->getClasses())
9247 {
9248 ClassDefMutable *innerCd = toClassDefMutable(innerCdi);
9249 if (innerCd)
9250 {
9251 AUTO_TRACE("innerCd={} isLinkable={} isImplicitTemplateInstance={} protectLevelVisible={} embeddedInOuterScope={}",
9252 innerCd->name(),innerCd->isLinkableInProject(),innerCd->isImplicitTemplateInstance(),protectionLevelVisible(innerCd->protection()),
9253 innerCd->isEmbeddedInOuterScope());
9254 }
9255 if (innerCd && innerCd->isLinkableInProject() && !innerCd->isImplicitTemplateInstance() &&
9256 protectionLevelVisible(innerCd->protection()) &&
9257 !innerCd->isEmbeddedInOuterScope()
9258 )
9259 {
9260 list.push_back(innerCd);
9261 addClassAndNestedClasses(list,innerCd);
9262 }
9263 }
9264}
9265
9267{
9268 std::vector<ClassDefMutable*> classList;
9269 for (const auto &cdi : *Doxygen::classLinkedMap)
9270 {
9271 ClassDefMutable *cd = toClassDefMutable(cdi.get());
9272 if (cd && (cd->getOuterScope()==nullptr ||
9274 {
9275 addClassAndNestedClasses(classList,cd);
9276 }
9277 }
9278 for (const auto &cdi : *Doxygen::hiddenClassLinkedMap)
9279 {
9280 ClassDefMutable *cd = toClassDefMutable(cdi.get());
9281 if (cd && (cd->getOuterScope()==nullptr ||
9283 {
9284 addClassAndNestedClasses(classList,cd);
9285 }
9286 }
9287 generateDocsForClassList(classList);
9288}
9289
9290//----------------------------------------------------------------------------
9291
9293{
9294 for (const auto &cdi : *Doxygen::conceptLinkedMap)
9295 {
9297
9298 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
9299 if (cd &&
9300 (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9301 cd->getOuterScope()==Doxygen::globalScope // only look at global concepts
9302 ) && !cd->isHidden() && cd->isLinkableInProject()
9303 )
9304 {
9305 msg("Generating docs for concept {}...\n",cd->displayName());
9307 }
9308 }
9309}
9310
9311//----------------------------------------------------------------------------
9312
9314{
9315 for (const auto &mn : *Doxygen::memberNameLinkedMap)
9316 {
9317 for (const auto &imd : *mn)
9318 {
9319 MemberDefMutable *md = toMemberDefMutable(imd.get());
9320 //static int count=0;
9321 //printf("%04d Member '%s'\n",count++,qPrint(md->qualifiedName()));
9322 if (md && md->documentation().empty() && md->briefDescription().empty())
9323 { // no documentation yet
9324 const MemberDef *bmd = md->reimplements();
9325 while (bmd && bmd->documentation().empty() &&
9326 bmd->briefDescription().empty()
9327 )
9328 { // search up the inheritance tree for a documentation member
9329 //printf("bmd=%s class=%s\n",qPrint(bmd->name()),qPrint(bmd->getClassDef()->name()));
9330 bmd = bmd->reimplements();
9331 }
9332 if (bmd) // copy the documentation from the reimplemented member
9333 {
9334 md->setInheritsDocsFrom(bmd);
9335 md->setDocumentation(bmd->documentation(),bmd->docFile(),bmd->docLine());
9337 md->setBriefDescription(bmd->briefDescription(),bmd->briefFile(),bmd->briefLine());
9338 md->copyArgumentNames(bmd);
9340 }
9341 }
9342 }
9343 }
9344}
9345
9346//----------------------------------------------------------------------------
9347
9349{
9350 // for each file
9351 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9352 {
9353 for (const auto &fd : *fn)
9354 {
9355 fd->combineUsingRelations();
9356 }
9357 }
9358
9359 // for each namespace
9360 NamespaceDefSet visitedNamespaces;
9361 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9362 {
9364 if (ndm)
9365 {
9366 ndm->combineUsingRelations(visitedNamespaces);
9367 }
9368 }
9369}
9370
9371//----------------------------------------------------------------------------
9372
9374{
9375 // for each class
9376 for (const auto &cd : *Doxygen::classLinkedMap)
9377 {
9378 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9379 if (cdm)
9380 {
9382 }
9383 }
9384 // for each file
9385 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9386 {
9387 for (const auto &fd : *fn)
9388 {
9389 fd->addMembersToMemberGroup();
9390 }
9391 }
9392 // for each namespace
9393 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9394 {
9396 if (ndm)
9397 {
9399 }
9400 }
9401 // for each group
9402 for (const auto &gd : *Doxygen::groupLinkedMap)
9403 {
9404 gd->addMembersToMemberGroup();
9405 }
9407}
9408
9409//----------------------------------------------------------------------------
9410
9412{
9413 // for each class
9414 for (const auto &cd : *Doxygen::classLinkedMap)
9415 {
9416 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9417 if (cdm)
9418 {
9420 }
9421 }
9422 // for each file
9423 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9424 {
9425 for (const auto &fd : *fn)
9426 {
9427 fd->distributeMemberGroupDocumentation();
9428 }
9429 }
9430 // for each namespace
9431 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9432 {
9434 if (ndm)
9435 {
9437 }
9438 }
9439 // for each group
9440 for (const auto &gd : *Doxygen::groupLinkedMap)
9441 {
9442 gd->distributeMemberGroupDocumentation();
9443 }
9445}
9446
9447//----------------------------------------------------------------------------
9448
9450{
9451 // for each class
9452 for (const auto &cd : *Doxygen::classLinkedMap)
9453 {
9454 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9455 if (cdm)
9456 {
9458 }
9459 }
9460 // for each concept
9461 for (const auto &cd : *Doxygen::conceptLinkedMap)
9462 {
9463 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
9464 if (cdm)
9465 {
9467 }
9468 }
9469 // for each file
9470 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9471 {
9472 for (const auto &fd : *fn)
9473 {
9474 fd->findSectionsInDocumentation();
9475 }
9476 }
9477 // for each namespace
9478 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9479 {
9481 if (ndm)
9482 {
9484 }
9485 }
9486 // for each group
9487 for (const auto &gd : *Doxygen::groupLinkedMap)
9488 {
9489 gd->findSectionsInDocumentation();
9490 }
9491 // for each page
9492 for (const auto &pd : *Doxygen::pageLinkedMap)
9493 {
9494 pd->findSectionsInDocumentation();
9495 }
9496 // for each directory
9497 for (const auto &dd : *Doxygen::dirLinkedMap)
9498 {
9499 dd->findSectionsInDocumentation();
9500 }
9502 if (Doxygen::mainPage) Doxygen::mainPage->findSectionsInDocumentation();
9503}
9504
9505//----------------------------------------------------------------------
9506
9507
9509{
9510 // remove all references to classes from the cache
9511 // as there can be new template instances in the inheritance path
9512 // to this class. Optimization: only remove those classes that
9513 // have inheritance instances as direct or indirect sub classes.
9515
9516 // remove all cached typedef resolutions whose target is a
9517 // template class as this may now be a template instance
9518 // for each global function name
9519 for (const auto &fn : *Doxygen::functionNameLinkedMap)
9520 {
9521 // for each function with that name
9522 for (const auto &ifmd : *fn)
9523 {
9524 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
9525 if (fmd && fmd->isTypedefValCached())
9526 {
9527 const ClassDef *cd = fmd->getCachedTypedefVal();
9528 if (cd->isTemplate()) fmd->invalidateTypedefValCache();
9529 }
9530 }
9531 }
9532 // for each class method name
9533 for (const auto &nm : *Doxygen::memberNameLinkedMap)
9534 {
9535 // for each function with that name
9536 for (const auto &imd : *nm)
9537 {
9538 MemberDefMutable *md = toMemberDefMutable(imd.get());
9539 if (md && md->isTypedefValCached())
9540 {
9541 const ClassDef *cd = md->getCachedTypedefVal();
9542 if (cd->isTemplate()) md->invalidateTypedefValCache();
9543 }
9544 }
9545 }
9546}
9547
9548//----------------------------------------------------------------------------
9549
9551{
9552 // Remove all unresolved references to classes from the cache.
9553 // This is needed before resolving the inheritance relations, since
9554 // it would otherwise not find the inheritance relation
9555 // for C in the example below, as B::I was already found to be unresolvable
9556 // (which is correct if you ignore the inheritance relation between A and B).
9557 //
9558 // class A { class I {} };
9559 // class B : public A {};
9560 // class C : public B::I {};
9562
9563 // for each class method name
9564 for (const auto &nm : *Doxygen::memberNameLinkedMap)
9565 {
9566 // for each function with that name
9567 for (const auto &imd : *nm)
9568 {
9569 MemberDefMutable *md = toMemberDefMutable(imd.get());
9570 if (md)
9571 {
9573 }
9574 }
9575 }
9576
9577}
9578
9579//----------------------------------------------------------------------------
9580// Returns true if the entry and member definition have equal file names,
9581// otherwise false.
9582
9583static bool haveEqualFileNames(const Entry *root, const MemberDef *md)
9584{
9585 if (const FileDef *fd = md->getFileDef())
9586 {
9587 return fd->absFilePath() == root->fileName;
9588 }
9589 return false;
9590}
9591
9592//----------------------------------------------------------------------------
9593
9594static void addDefineDoc(const Entry *root, MemberDefMutable *md)
9595{
9596 md->setDocumentation(root->doc,root->docFile,root->docLine);
9597 md->setDocsForDefinition(!root->proto);
9598 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9599 if (md->inbodyDocumentation().empty())
9600 {
9602 }
9603 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
9604 {
9605 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
9606 md->setBodyDef(root->fileDef());
9607 }
9609 md->setMaxInitLines(root->initLines);
9611 md->setRefItems(root->sli);
9612 md->setRequirementReferences(root->rqli);
9613 md->addQualifiers(root->qualifiers);
9614 if (root->mGrpId!=-1) md->setMemberGroupId(root->mGrpId);
9615 addMemberToGroups(root,md);
9617}
9618
9619//----------------------------------------------------------------------------
9620
9622{
9623 if ((root->section.isDefineDoc() || root->section.isDefine()) && !root->name.empty())
9624 {
9625 //printf("found define '%s' '%s' brief='%s' doc='%s'\n",
9626 // qPrint(root->name),qPrint(root->args),qPrint(root->brief),qPrint(root->doc));
9627
9628 if (root->tagInfo() && !root->name.empty()) // define read from a tag file
9629 {
9630 auto md = createMemberDef(root->tagInfo()->tagName,1,1,
9631 "#define",root->name,root->args,DString(),
9632 Protection::Public,Specifier::Normal,false,Relationship::Member,MemberType::Define,
9633 ArgumentList(),ArgumentList(),"");
9634 auto mmd = toMemberDefMutable(md.get());
9635 mmd->setTagInfo(root->tagInfo());
9636 mmd->setLanguage(root->lang);
9637 mmd->addQualifiers(root->qualifiers);
9638 //printf("Searching for '%s' fd=%p\n",qPrint(filePathName),fd);
9639 mmd->setFileDef(root->parent()->fileDef());
9640 //printf("Adding member=%s\n",qPrint(md->name()));
9642 mn->push_back(std::move(md));
9643 }
9645 if (mn)
9646 {
9647 int count=0;
9648 for (const auto &md : *mn)
9649 {
9650 if (md->memberType()==MemberType::Define) count++;
9651 }
9652 if (count==1)
9653 {
9654 for (const auto &imd : *mn)
9655 {
9656 MemberDefMutable *md = toMemberDefMutable(imd.get());
9657 if (md && md->memberType()==MemberType::Define)
9658 {
9659 addDefineDoc(root,md);
9660 }
9661 }
9662 }
9663 else if (count>1 &&
9664 (!root->doc.empty() ||
9665 !root->brief.empty() ||
9666 root->bodyLine!=-1
9667 )
9668 )
9669 // multiple defines don't know where to add docs
9670 // but maybe they are in different files together with their documentation
9671 {
9672 for (const auto &imd : *mn)
9673 {
9674 MemberDefMutable *md = toMemberDefMutable(imd.get());
9675 if (md && md->memberType()==MemberType::Define)
9676 {
9677 if (haveEqualFileNames(root, md) || isEntryInGroupOfMember(root, md))
9678 // doc and define in the same file or group assume they belong together.
9679 {
9680 addDefineDoc(root,md);
9681 }
9682 }
9683 }
9684 //warn("define {} found in the following files:\n",root->name);
9685 //warn("Cannot determine where to add the documentation found "
9686 // "at line {} of file {}. \n",
9687 // root->startLine,root->fileName);
9688 }
9689 }
9690 else if (!root->doc.empty() || !root->brief.empty()) // define not found
9691 {
9692 bool preEnabled = Config_getBool(ENABLE_PREPROCESSING);
9693 if (preEnabled)
9694 {
9695 warn(root->fileName,root->startLine,"documentation for unknown define {} found.",root->name);
9696 }
9697 else
9698 {
9699 warn(root->fileName,root->startLine, "found documented #define {} but ignoring it because ENABLE_PREPROCESSING is NO.", root->name);
9700 }
9701 }
9702 }
9703 for (const auto &e : root->children()) findDefineDocumentation(e.get());
9704}
9705
9706//----------------------------------------------------------------------------
9707
9708static void findDirDocumentation(const Entry *root)
9709{
9710 if (root->section.isDirDoc())
9711 {
9712 DString normalizedName = root->name;
9713 normalizedName = substitute(normalizedName,"\\","/");
9714 //printf("root->docFile=%s normalizedName=%s\n",
9715 // qPrint(root->docFile),qPrint(normalizedName));
9716 if (root->docFile==normalizedName) // current dir?
9717 {
9718 if (size_t lastSlashPos=normalizedName.rfind('/'); lastSlashPos!=DString::npos) // strip file name
9719 {
9720 normalizedName=normalizedName.left(lastSlashPos);
9721 }
9722 }
9723 if (normalizedName.at(normalizedName.length()-1)!='/')
9724 {
9725 normalizedName+='/';
9726 }
9727 DirDef *matchingDir=nullptr;
9728 for (const auto &dir : *Doxygen::dirLinkedMap)
9729 {
9730 //printf("Dir: %s<->%s\n",qPrint(dir->name()),qPrint(normalizedName));
9731 if (dir->name().right(normalizedName.length())==normalizedName)
9732 {
9733 if (matchingDir)
9734 {
9735 warn(root->fileName,root->startLine,
9736 "\\dir command matches multiple directories.\n"
9737 " Applying the command for directory {}\n"
9738 " Ignoring the command for directory {}",
9739 matchingDir->name(),dir->name()
9740 );
9741 }
9742 else
9743 {
9744 matchingDir=dir.get();
9745 }
9746 }
9747 }
9748 if (matchingDir)
9749 {
9750 //printf("Match for with dir %s #anchor=%zu\n",qPrint(matchingDir->name()),root->anchors.size());
9751 matchingDir->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9752 matchingDir->setDocumentation(root->doc,root->docFile,root->docLine);
9753 matchingDir->setRefItems(root->sli);
9754 matchingDir->setRequirementReferences(root->rqli);
9755 matchingDir->addSectionsToDefinition(root->anchors);
9756 root->commandOverrides.apply_directoryGraph([&](bool b) { matchingDir->overrideDirectoryGraph(b); });
9757 addDirToGroups(root,matchingDir);
9758 }
9759 else
9760 {
9761 warn(root->fileName,root->startLine,"No matching directory found for command \\dir {}",normalizedName);
9762 }
9763 }
9764 for (const auto &e : root->children()) findDirDocumentation(e.get());
9765}
9766
9767//----------------------------------------------------------------------------
9769{
9770 if (root->section.isRequirementDoc())
9771 {
9773 }
9774 for (const auto &e : root->children()) buildRequirementsList(e.get());
9775}
9776
9777//----------------------------------------------------------------------------
9778// create a (sorted) list of separate documentation pages
9779
9780static void buildPageList(Entry *root)
9781{
9782 if (root->section.isPageDoc())
9783 {
9784 if (!root->name.empty())
9785 {
9786 addRelatedPage(root);
9787 }
9788 }
9789 else if (root->section.isMainpageDoc())
9790 {
9791 DString title=root->args.stripWhiteSpace();
9792 if (title.empty()) title=theTranslator->trMainPage();
9793 //DString name = Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9794 DString name = "index";
9795 addRefItem(root->sli,
9796 name,
9797 theTranslator->trPage(true,true),
9798 name,
9799 title,
9800 DString(),nullptr
9801 );
9802 }
9803 for (const auto &e : root->children()) buildPageList(e.get());
9804}
9805
9806// search for the main page defined in this project
9807static void findMainPage(Entry *root)
9808{
9809 if (root->section.isMainpageDoc())
9810 {
9811 if (Doxygen::mainPage==nullptr && root->tagInfo()==nullptr)
9812 {
9813 //printf("mainpage: docLine=%d startLine=%d\n",root->docLine,root->startLine);
9814 //printf("Found main page! \n======\n%s\n=======\n",qPrint(root->doc));
9815 DString title=root->args.stripWhiteSpace();
9816 if (title.empty()) title = Config_getString(PROJECT_NAME);
9817 //DString indexName=Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9818 DString indexName="index";
9820 indexName, root->brief+root->doc+root->inbodyDocs,title);
9821 //setFileNameForSections(root->anchors,"index",Doxygen::mainPage);
9822 Doxygen::mainPage->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9823 Doxygen::mainPage->setBodySegment(root->startLine,root->startLine,-1);
9824 Doxygen::mainPage->setFileName(indexName);
9825 Doxygen::mainPage->setLocalToc(root->localToc);
9827
9829 if (si)
9830 {
9831 if (!si->ref().empty()) // we are from a tag file
9832 {
9833 // a page name is a label as well! but should no be double either
9835 Doxygen::mainPage->name(),
9836 indexName,
9837 root->startLine,
9838 Doxygen::mainPage->title(),
9840 0); // level 0
9841 }
9842 else if (si->lineNr() != -1)
9843 {
9844 warn(root->fileName,root->startLine,"multiple use of section label '{}' for main page, (first occurrence: {}, line {})",
9845 Doxygen::mainPage->name(),si->fileName(),si->lineNr());
9846 }
9847 else
9848 {
9849 warn(root->fileName,root->startLine,"multiple use of section label '{}' for main page, (first occurrence: {})",
9850 Doxygen::mainPage->name(),si->fileName());
9851 }
9852 }
9853 else
9854 {
9855 // a page name is a label as well! but should no be double either
9857 Doxygen::mainPage->name(),
9858 indexName,
9859 root->startLine,
9860 Doxygen::mainPage->title(),
9862 0); // level 0
9863 }
9864 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9865 }
9866 else if (root->tagInfo()==nullptr)
9867 {
9868 warn(root->fileName,root->startLine,
9869 "found more than one \\mainpage comment block! (first occurrence: {}, line {}), Skipping current block!",
9870 Doxygen::mainPage->docFile(),Doxygen::mainPage->getStartBodyLine());
9871 }
9872 }
9873 for (const auto &e : root->children()) findMainPage(e.get());
9874}
9875
9876// search for the main page imported via tag files and add only the section labels
9877static void findMainPageTagFiles(Entry *root)
9878{
9879 if (root->section.isMainpageDoc())
9880 {
9881 if (Doxygen::mainPage && root->tagInfo())
9882 {
9883 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9884 }
9885 }
9886 for (const auto &e : root->children()) findMainPageTagFiles(e.get());
9887}
9888
9889static void computePageRelations(Entry *root)
9890{
9891 if ((root->section.isPageDoc() || root->section.isMainpageDoc()) && !root->name.empty())
9892 {
9893 PageDef *pd = root->section.isPageDoc() ?
9895 Doxygen::mainPage.get();
9896 if (pd)
9897 {
9898 for (const BaseInfo &bi : root->extends)
9899 {
9901 if (pd==subPd)
9902 {
9903 term("page defined {} with label {} is a direct "
9904 "subpage of itself! Please remove this cyclic dependency.\n",
9905 warn_line(pd->docFile(),pd->docLine()),pd->name());
9906 }
9907 else if (subPd)
9908 {
9909 pd->addInnerCompound(subPd);
9910 //printf("*** Added subpage relation: %s->%s\n",
9911 // qPrint(pd->name()),qPrint(subPd->name()));
9912 }
9913 }
9914 }
9915 }
9916 for (const auto &e : root->children()) computePageRelations(e.get());
9917}
9918
9920{
9921 for (const auto &pd : *Doxygen::pageLinkedMap)
9922 {
9923 Definition *ppd = pd->getOuterScope();
9924 while (ppd)
9925 {
9926 if (ppd==pd.get())
9927 {
9928 term("page defined {} with label {} is a subpage "
9929 "of itself! Please remove this cyclic dependency.\n",
9930 warn_line(pd->docFile(),pd->docLine()),pd->name());
9931 }
9932 ppd=ppd->getOuterScope();
9933 }
9934 }
9935}
9936
9937//----------------------------------------------------------------------------
9938
9940{
9941 for (const auto &si : SectionManager::instance())
9942 {
9943 //printf("si->label='%s' si->definition=%s si->fileName='%s'\n",
9944 // qPrint(si->label),si->definition?qPrint(si->definition->name()):"<none>",
9945 // qPrint(si->fileName));
9946 PageDef *pd=nullptr;
9947
9948 // hack: the items of a todo/test/bug/deprecated list are all fragments from
9949 // different files, so the resulting section's all have the wrong file
9950 // name (not from the todo/test/bug/deprecated list, but from the file in
9951 // which they are defined). We correct this here by looking at the
9952 // generated section labels!
9954 {
9955 DString label="_"+rl->listName(); // "_todo", "_test", ...
9956 if (si->label().left(label.length())==label)
9957 {
9958 si->setFileName(rl->listName());
9959 si->setGenerated(true);
9960 break;
9961 }
9962 }
9963
9964 //printf("start: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
9965 if (!si->generated())
9966 {
9967 // if this section is in a page and the page is in a group, then we
9968 // have to adjust the link file name to point to the group.
9969 if (!si->fileName().empty() &&
9970 (pd=Doxygen::pageLinkedMap->find(si->fileName())) &&
9971 pd->getGroupDef())
9972 {
9973 si->setFileName(pd->getGroupDef()->getOutputFileBase());
9974 }
9975
9976 if (si->definition())
9977 {
9978 // TODO: there should be one function in Definition that returns
9979 // the file to link to, so we can avoid the following tests.
9980 const GroupDef *gd=nullptr;
9981 if (si->definition()->definitionType()==Definition::TypeMember)
9982 {
9983 gd = (toMemberDef(si->definition()))->getGroupDef();
9984 }
9985
9986 if (gd)
9987 {
9988 si->setFileName(gd->getOutputFileBase());
9989 }
9990 else
9991 {
9992 //si->fileName=si->definition->getOutputFileBase();
9993 //printf("Setting si->fileName to %s\n",qPrint(si->fileName));
9994 }
9995 }
9996 }
9997 //printf("end: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
9998 }
9999}
10000
10001
10002
10003//----------------------------------------------------------------------------
10004// generate all separate documentation pages
10005
10006
10007static void generatePageDocs()
10008{
10009 //printf("documentedPages=%d real=%d\n",documentedPages,Doxygen::pageLinkedMap->count());
10010 if (Index::instance().numDocumentedPages()==0) return;
10011 for (const auto &pd : *Doxygen::pageLinkedMap)
10012 {
10013 if (!pd->getGroupDef() && !pd->isReference())
10014 {
10015 msg("Generating docs for page {}...\n",pd->name());
10016 pd->writeDocumentation(*g_outputList);
10017 }
10018 }
10019}
10020
10021//----------------------------------------------------------------------------
10022// create a (sorted) list & dictionary of example pages
10023
10024static void buildExampleList(Entry *root)
10025{
10026 if ((root->section.isExample() || root->section.isExampleLineno()) && !root->name.empty())
10027 {
10028 if (Doxygen::exampleLinkedMap->find(root->name))
10029 {
10030 warn(root->fileName,root->startLine,"Example {} was already documented. Ignoring documentation found here.",root->name);
10031 }
10032 else
10033 {
10035 createPageDef(root->fileName,root->startLine,
10036 root->name,root->brief+root->doc+root->inbodyDocs,root->args));
10037 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
10038 pd->setFileName(convertNameToFile(pd->name()+"-example",false,true));
10040 pd->setLanguage(root->lang);
10041 pd->setShowLineNo(root->section.isExampleLineno());
10042
10043 //we don't add example to groups
10044 //addExampleToGroups(root,pd);
10045 }
10046 }
10047 for (const auto &e : root->children()) buildExampleList(e.get());
10048}
10049
10050//----------------------------------------------------------------------------
10051// prints the Entry tree (for debugging)
10052
10053void printNavTree(Entry *root,int indent)
10054{
10056 {
10057 DString indentStr;
10058 indentStr.fill(' ',indent);
10059 Debug::print(Debug::Entries,0,"{}{} at {}:{} (sec={}, spec={})\n",
10060 indentStr.empty()?"":indentStr,
10061 root->name.empty()?"<empty>":root->name,
10062 root->fileName,root->startLine,
10063 root->section.to_string(),
10064 root->spec.to_string());
10065 for (const auto &e : root->children())
10066 {
10067 printNavTree(e.get(),indent+2);
10068 }
10069 }
10070}
10071
10072
10073//----------------------------------------------------------------------------
10074// prints the Sections tree (for debugging)
10075
10077{
10079 {
10080 for (const auto &si : SectionManager::instance())
10081 {
10082 Debug::print(Debug::Sections,0,"Section = {}, file = {}, title = {}, type = {}, ref = {}\n",
10083 si->label(),si->fileName(),si->title(),si->type().level(),si->ref());
10084 }
10085 }
10086}
10087
10088
10089//----------------------------------------------------------------------------
10090// generate the example documentation
10091
10093{
10095 for (const auto &pd : *Doxygen::exampleLinkedMap)
10096 {
10097 msg("Generating docs for example {}...\n",pd->name());
10098 SrcLangExt lang = getLanguageFromFileName(pd->name(), SrcLangExt::Unknown);
10099 if (lang != SrcLangExt::Unknown)
10100 {
10101 DString ext = getFileNameExtension(pd->name());
10102 auto intf = Doxygen::parserManager->getCodeParser(ext);
10103 intf->resetCodeParserState();
10104 }
10105 DString n=pd->getOutputFileBase();
10106 startFile(*g_outputList,n,false,n,pd->name());
10108 g_outputList->docify(pd->name());
10111 DString lineNoOptStr;
10112 if (pd->showLineNo())
10113 {
10114 lineNoOptStr="{lineno}";
10115 }
10116 g_outputList->generateDoc(pd->docFile(), // file
10117 pd->docLine(), // startLine
10118 pd.get(), // context
10119 nullptr, // memberDef
10120 (pd->briefDescription().empty()?"":pd->briefDescription()+"\n\n")+
10121 pd->documentation()+"\n\n\\include"+lineNoOptStr+" "+pd->name(), // docs
10122 DocOptions()
10123 .setIndexWords(true)
10124 .setExample(pd->name()));
10125 endFile(*g_outputList); // contains g_outputList->endContents()
10126 }
10128}
10129
10130//----------------------------------------------------------------------------
10131// generate module pages
10132
10134{
10135 for (const auto &gd : *Doxygen::groupLinkedMap)
10136 {
10137 if (!gd->isReference())
10138 {
10139 gd->writeDocumentation(*g_outputList);
10140 }
10141 }
10142}
10143
10144//----------------------------------------------------------------------------
10145// generate module pages
10146
10148{
10149 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10150 if (numThreads>1) // multi threaded processing
10151 {
10152 struct DocContext
10153 {
10154 DocContext(ClassDefMutable *cdm_,const OutputList &ol_)
10155 : cdm(cdm_), ol(ol_) {}
10156 ClassDefMutable *cdm;
10157 OutputList ol;
10158 };
10159 ThreadPool threadPool(numThreads);
10160 std::vector< std::future< std::shared_ptr<DocContext> > > results;
10161 // for each class in the namespace...
10162 for (const auto &cd : classList)
10163 {
10165 if (cdm)
10166 {
10167 auto ctx = std::make_shared<DocContext>(cdm,*g_outputList);
10168 auto processFile = [ctx]()
10169 {
10170 if ( ( ctx->cdm->isLinkableInProject() &&
10171 !ctx->cdm->isImplicitTemplateInstance()
10172 ) // skip external references, anonymous compounds and
10173 // template instances and nested classes
10174 && !ctx->cdm->isHidden() && !ctx->cdm->isEmbeddedInOuterScope()
10175 )
10176 {
10177 msg("Generating docs for compound {}...\n",ctx->cdm->displayName());
10178 ctx->cdm->writeDocumentation(ctx->ol);
10179 ctx->cdm->writeMemberList(ctx->ol);
10180 }
10181 ctx->cdm->writeDocumentationForInnerClasses(ctx->ol);
10182 return ctx;
10183 };
10184 results.emplace_back(threadPool.queue(processFile));
10185 }
10186 }
10187 // wait for the results
10188 for (auto &f : results)
10189 {
10190 auto ctx = f.get();
10191 }
10192 }
10193 else // single threaded processing
10194 {
10195 // for each class in the namespace...
10196 for (const auto &cd : classList)
10197 {
10199 if (cdm)
10200 {
10201 if ( ( cd->isLinkableInProject() &&
10202 !cd->isImplicitTemplateInstance()
10203 ) // skip external references, anonymous compounds and
10204 // template instances and nested classes
10205 && !cd->isHidden() && !cd->isEmbeddedInOuterScope()
10206 )
10207 {
10208 msg("Generating docs for compound {}...\n",cd->displayName());
10209
10212 }
10214 }
10215 }
10216 }
10217}
10218
10220{
10221 // for each concept in the namespace...
10222 for (const auto &cd : conceptList)
10223 {
10225 if ( cdm && cd->isLinkableInProject() && !cd->isHidden())
10226 {
10227 msg("Generating docs for concept {}...\n",cd->name());
10229 }
10230 }
10231}
10232
10234{
10235 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
10236
10237 //writeNamespaceIndex(*g_outputList);
10238
10239 // for each namespace...
10240 for (const auto &nd : *Doxygen::namespaceLinkedMap)
10241 {
10242 if (nd->isLinkableInProject())
10243 {
10245 if (ndm)
10246 {
10247 msg("Generating docs for namespace {}\n",nd->displayName());
10249 }
10250 }
10251
10252 generateNamespaceClassDocs(nd->getClasses());
10253 if (sliceOpt)
10254 {
10255 generateNamespaceClassDocs(nd->getInterfaces());
10256 generateNamespaceClassDocs(nd->getStructs());
10257 generateNamespaceClassDocs(nd->getExceptions());
10258 }
10259 generateNamespaceConceptDocs(nd->getConcepts());
10260 }
10261}
10262
10264{
10265 std::string oldDir = Dir::currentDirPath();
10266 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
10269 {
10270 err("failed to run html help compiler on {}\n", HtmlHelp::hhpFileName);
10271 }
10272 Dir::setCurrent(oldDir);
10273}
10274
10276{
10277 DString args = Qhp::qhpFileName + " -o \"" + Qhp::getQchFileName() + "\"";
10278 std::string oldDir = Dir::currentDirPath();
10279 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
10280
10281 DString qhgLocation=Config_getString(QHG_LOCATION);
10282 if (Debug::isFlagSet(Debug::Qhp)) // produce info for debugging
10283 {
10284 // run qhelpgenerator -v and extract the Qt version used
10285 DString cmd=qhgLocation+ " -v 2>&1";
10286 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
10287 FILE *f=Portable::popen(cmd,"r");
10288 if (!f)
10289 {
10290 err("could not execute {}\n",qhgLocation);
10291 }
10292 else
10293 {
10294 const size_t bufSize = 1024;
10295 char inBuf[bufSize+1];
10296 size_t numRead=fread(inBuf,1,bufSize,f);
10297 inBuf[numRead] = '\0';
10298 Debug::print(Debug::Qhp,0,"{}",inBuf);
10300
10301 int qtVersion=0;
10302 static const reg::Ex versionReg(R"(Qt (\d+)\.(\d+)\.(\d+))");
10303 reg::Match match;
10304 std::string s = inBuf;
10305 if (reg::search(s,match,versionReg))
10306 {
10307 qtVersion = 10000*DString(match[1].str()).toInt() +
10308 100*DString(match[2].str()).toInt() +
10309 DString(match[3].str()).toInt();
10310 }
10311 if (qtVersion>0 && (qtVersion<60000 || qtVersion >= 60205))
10312 {
10313 // dump the output of qhelpgenerator -c file.qhp
10314 // Qt<6 or Qt>=6.2.5 or higher, see https://bugreports.qt.io/browse/QTBUG-101070
10315 cmd=qhgLocation+ " -c " + Qhp::qhpFileName + " 2>&1";
10316 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
10317 f=Portable::popen(cmd,"r");
10318 if (!f)
10319 {
10320 err("could not execute {}\n",qhgLocation);
10321 }
10322 else
10323 {
10324 std::string output;
10325 while ((numRead=fread(inBuf,1,bufSize,f))>0)
10326 {
10327 inBuf[numRead] = '\0';
10328 output += inBuf;
10329 }
10331 Debug::print(Debug::Qhp,0,"{}",output);
10332 }
10333 }
10334 }
10335 }
10336
10337 if (Portable::system(qhgLocation, args, false))
10338 {
10339 err("failed to run qhelpgenerator on {}\n",Qhp::qhpFileName);
10340 }
10341 Dir::setCurrent(oldDir);
10342}
10343
10344//----------------------------------------------------------------------------
10345
10347{
10348 // check dot path
10349 DString dotPath = Config_getString(DOT_PATH);
10350 if (!dotPath.empty())
10351 {
10352 FileInfo fi(dotPath.str());
10353 if (!(fi.exists() && fi.isFile()) )// not an existing user specified path + exec
10354 {
10355 dotPath = dotPath+"/dot"+Portable::commandExtension();
10356 FileInfo dp(dotPath.str());
10357 if (!dp.exists() || !dp.isFile())
10358 {
10359 warn_uncond("the dot tool could not be found as '{}'\n",dotPath);
10360 dotPath = "dot";
10361 dotPath += Portable::commandExtension();
10362 }
10363 }
10364#if defined(_WIN32) // convert slashes
10365 size_t l=dotPath.length();
10366 for (size_t i=0;i<l;i++) if (dotPath.at(i)=='/') dotPath.at(i)='\\';
10367#endif
10368 }
10369 else
10370 {
10371 dotPath = "dot";
10372 dotPath += Portable::commandExtension();
10373 }
10374 Doxygen::verifiedDotPath = dotPath;
10376}
10377
10378//----------------------------------------------------------------------------
10379
10380/*! Generate a template version of the configuration file.
10381 * If the \a shortList parameter is true a configuration file without
10382 * comments will be generated.
10383 */
10384static void generateConfigFile(const DString &configFile,bool shortList,
10385 bool updateOnly=false)
10386{
10387 std::ofstream f;
10388 bool fileOpened=openOutputFile(configFile,f);
10389 bool writeToStdout=configFile=="-";
10390 if (fileOpened)
10391 {
10392 TextStream t(&f);
10393 Config::writeTemplate(t,shortList,updateOnly);
10394 if (!writeToStdout)
10395 {
10396 if (!updateOnly)
10397 {
10398 msg("\n\nConfiguration file '{}' created.\n\n",configFile);
10399 msg("Now edit the configuration file and enter\n\n");
10400 if (configFile!="Doxyfile" && configFile!="doxyfile")
10401 msg(" doxygen {}\n\n",configFile);
10402 else
10403 msg(" doxygen\n\n");
10404 msg("to generate the documentation for your project\n\n");
10405 }
10406 else
10407 {
10408 msg("\n\nConfiguration file '{}' updated.\n\n",configFile);
10409 }
10410 }
10411 }
10412 else
10413 {
10414 term("Cannot open file {} for writing\n",configFile);
10415 }
10416}
10417
10419{
10420 std::ofstream f;
10421 bool fileOpened=openOutputFile("-",f);
10422 if (fileOpened)
10423 {
10424 TextStream t(&f);
10425 Config::compareDoxyfile(t,diffList);
10426 }
10427 else
10428 {
10429 term("Cannot open stdout for writing\n");
10430 }
10431}
10432
10433//----------------------------------------------------------------------------
10434// read and parse a tag file
10435
10436static void readTagFile(const std::shared_ptr<Entry> &root,const DString &tagLine)
10437{
10438 DString fileName;
10439 DString destName;
10440 if (size_t eqPos = tagLine.find('='); eqPos!=DString::npos) // tag command contains a destination
10441 {
10442 fileName = tagLine.left(eqPos).stripWhiteSpace();
10443 destName = tagLine.mid(eqPos+1).stripWhiteSpace();
10444 if (fileName.empty() || destName.empty()) return;
10445 //printf("insert tagDestination %s->%s\n",qPrint(fi.fileName()),qPrint(destName));
10446 }
10447 else
10448 {
10449 fileName = tagLine;
10450 }
10451
10452 FileInfo fi(fileName.str());
10453 if (!fi.exists() || !fi.isFile())
10454 {
10455 err("Tag file '{}' does not exist or is not a file. Skipping it...\n",fileName);
10456 return;
10457 }
10458
10459 if (Doxygen::tagFileSet.find(fi.absFilePath()) != Doxygen::tagFileSet.end()) return;
10460
10461 Doxygen::tagFileSet.emplace(fi.absFilePath());
10462
10463 if (!destName.empty())
10464 {
10465 Doxygen::tagDestinationMap.emplace(fi.absFilePath(), destName.str());
10466 msg("Reading tag file '{}', location '{}'...\n",fileName,destName);
10467 }
10468 else
10469 {
10470 msg("Reading tag file '{}'...\n",fileName);
10471 }
10472
10473 parseTagFile(root,fi.absFilePath().c_str());
10474}
10475
10476//----------------------------------------------------------------------------
10478{
10479 StringVector latexExtraStyleSheet = Config_getList(LATEX_EXTRA_STYLESHEET);
10480 for (const auto &sheet : latexExtraStyleSheet)
10481 {
10482 std::string fileName = sheet;
10483 if (!fileName.empty())
10484 {
10485 FileInfo fi(fileName);
10486 if (!fi.exists())
10487 {
10488 err("Style sheet '{}' specified by LATEX_EXTRA_STYLESHEET does not exist!\n",fileName);
10489 }
10490 else if (fi.isDir())
10491 {
10492 err("Style sheet '{}' specified by LATEX_EXTRA_STYLESHEET is a directory, it has to be a file!\n", fileName);
10493 }
10494 else
10495 {
10496 DString destFileName = Config_getString(LATEX_OUTPUT)+"/"+fi.fileName();
10498 {
10499 destFileName += LATEX_STYLE_EXTENSION;
10500 }
10501 copyFile(fileName, destFileName);
10502 }
10503 }
10504 }
10505}
10506
10507//----------------------------------------------------------------------------
10508static void copyStyleSheet()
10509{
10510 DString htmlStyleSheet = Config_getString(HTML_STYLESHEET);
10511 if (!htmlStyleSheet.empty())
10512 {
10513 if (!htmlStyleSheet.startsWith("http:") && !htmlStyleSheet.startsWith("https:"))
10514 {
10515 FileInfo fi(htmlStyleSheet.str());
10516 if (!fi.exists())
10517 {
10518 err("Style sheet '{}' specified by HTML_STYLESHEET does not exist!\n",htmlStyleSheet);
10519 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
10520 }
10521 else if (fi.isDir())
10522 {
10523 err("Style sheet '{}' specified by HTML_STYLESHEET is a directory, it has to be a file!\n",htmlStyleSheet);
10524 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
10525 }
10526 else
10527 {
10528 DString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
10529 copyFile(htmlStyleSheet,destFileName);
10530 }
10531 }
10532 }
10533 StringVector htmlExtraStyleSheet = Config_getList(HTML_EXTRA_STYLESHEET);
10534 for (const auto &sheet : htmlExtraStyleSheet)
10535 {
10536 DString fileName(sheet);
10537 if (!fileName.empty() && !fileName.startsWith("http:") && !fileName.startsWith("https:"))
10538 {
10539 FileInfo fi(fileName.str());
10540 if (!fi.exists())
10541 {
10542 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET does not exist!\n",fileName);
10543 }
10544 else if (fi.fileName()=="doxygen.css" || fi.fileName()=="tabs.css" || fi.fileName()=="navtree.css")
10545 {
10546 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET is already a built-in stylesheet. Please use a different name\n",fi.fileName());
10547 }
10548 else if (fi.isDir())
10549 {
10550 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET is a directory, it has to be a file!\n",fileName);
10551 }
10552 else
10553 {
10554 DString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
10555 copyFile(fileName, destFileName);
10556 }
10557 }
10558 }
10559}
10560
10561static void copyLogo(const DString &outputOption, bool toIndex)
10562{
10563 DString projectLogo = projectLogoFile();
10564 if (!projectLogo.empty())
10565 {
10566 FileInfo fi(projectLogo.str());
10567 if (!fi.exists())
10568 {
10569 err("Project logo '{}' specified by PROJECT_LOGO does not exist!\n",projectLogo);
10570 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
10571 }
10572 else if (fi.isDir())
10573 {
10574 err("Project logo '{}' specified by PROJECT_LOGO is a directory, it has to be a file!\n",projectLogo);
10575 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
10576 }
10577 else
10578 {
10579 DString destFileName = outputOption+"/"+fi.fileName();
10580 copyFile(projectLogo,destFileName);
10581 if (toIndex) Doxygen::indexList->addImageFile(fi.fileName());
10582 }
10583 }
10584}
10585
10586static void copyIcon(const DString &outputOption, bool toIndex)
10587{
10588 DString projectIcon = Config_getString(PROJECT_ICON);
10589 if (!projectIcon.empty())
10590 {
10591 FileInfo fi(projectIcon.str());
10592 if (!fi.exists())
10593 {
10594 err("Project icon '{}' specified by PROJECT_ICON does not exist!\n",projectIcon);
10595 projectIcon = Config_updateString(PROJECT_ICON,""); // revert to the default
10596 }
10597 else if (fi.isDir())
10598 {
10599 err("Project icon '{}' specified by PROJECT_ICON is a directory, it has to be a file!\n",projectIcon);
10600 projectIcon = Config_updateString(PROJECT_ICON,""); // revert to the default
10601 }
10602 else
10603 {
10604 DString destFileName = outputOption+"/"+fi.fileName();
10605 copyFile(projectIcon,destFileName);
10606 if (toIndex) Doxygen::indexList->addImageFile(fi.fileName());
10607 }
10608 }
10609}
10610
10611static inline void copyExtraFiles(StringVector files,const DString &filesOption,const DString &outputOption, bool toIndex)
10612{
10613 for (const auto &fileName : files)
10614 {
10615 if (!fileName.empty())
10616 {
10617 FileInfo fi(fileName);
10618 if (!fi.exists())
10619 {
10620 err("Extra file '{}' specified in {} does not exist!\n", fileName,filesOption);
10621 }
10622 else if (fi.isDir())
10623 {
10624 err("Extra file '{}' specified in {} is a directory, it has to be a file!\n", fileName,filesOption);
10625 }
10626 else
10627 {
10628 DString destFileName = outputOption+"/"+fi.fileName();
10629 copyFile(fileName, destFileName);
10630 if (toIndex) Doxygen::indexList->addImageFile(fi.fileName());
10631 }
10632 }
10633 }
10634}
10635
10636//----------------------------------------------------------------------------
10637
10639{
10640 for (const auto &fn : *Doxygen::inputNameLinkedMap)
10641 {
10642 struct FileEntry
10643 {
10644 FileEntry(const DString &p,FileDef *fd) : path(p), fileDef(fd) {}
10645 DString path;
10646 FileDef *fileDef;
10647 };
10648
10649 // collect the entry for which to compute the longest common prefix (LCP) of the path
10650 std::vector<FileEntry> fileEntries;
10651 for (const auto &fd : *fn)
10652 {
10653 if (!fd->isReference()) // skip external references
10654 {
10655 fileEntries.emplace_back(fd->getPath(),fd.get());
10656 }
10657 }
10658
10659 size_t size = fileEntries.size();
10660
10661 if (size==1) // name if unique, so diskname is simply the name
10662 {
10663 FileDef *fd = fileEntries[0].fileDef;
10664 fd->setDiskName(fn->fileName());
10665 }
10666 else if (size>1) // multiple occurrences of the same file name
10667 {
10668 // sort the array
10669 std::stable_sort(fileEntries.begin(),
10670 fileEntries.end(),
10671 [](const FileEntry &fe1,const FileEntry &fe2)
10672 { return dstricmp_sort(fe1.path,fe2.path)<0; }
10673 );
10674
10675 // since the entries are sorted, the common prefix of the whole array is same
10676 // as the common prefix between the first and last entry
10677 const FileEntry &first = fileEntries[0];
10678 const FileEntry &last = fileEntries[size-1];
10679 int first_path_size = static_cast<int>(first.path.size())-1; // -1 to skip trailing slash
10680 int last_path_size = static_cast<int>(last.path.size())-1; // -1 to skip trailing slash
10681 int j=0;
10682 int i=0;
10683 for (i=0;i<first_path_size && i<last_path_size;i++)
10684 {
10685 if (first.path[i]=='/') j=i;
10686 if (first.path[i]!=last.path[i]) break;
10687 }
10688 if (i==first_path_size && i<last_path_size && last.path[i]=='/')
10689 {
10690 // case first='some/path' and last='some/path/more' => match is 'some/path'
10691 j=first_path_size;
10692 }
10693 else if (i==last_path_size && i<first_path_size && first.path[i]=='/')
10694 {
10695 // case first='some/path/more' and last='some/path' => match is 'some/path'
10696 j=last_path_size;
10697 }
10698
10699 // add non-common part of the path to the name
10700 for (auto &fileEntry : fileEntries)
10701 {
10702 DString prefix = fileEntry.path.right(fileEntry.path.length()-j-1);
10703 fileEntry.fileDef->setName(prefix+fn->fileName());
10704 //printf("!!!!!!!! non unique disk name=%s:%s\n",qPrint(prefix),fn->fileName());
10705 fileEntry.fileDef->setDiskName(prefix+fn->fileName());
10706 }
10707 }
10708 }
10709}
10710
10711
10712
10713//----------------------------------------------------------------------------
10714
10715static std::unique_ptr<OutlineParserInterface> getParserForFile(const DString &fn)
10716{
10717 DString fileName=fn;
10718 DString extension;
10719 size_t sep = fileName.rfind('/');
10720 size_t ei = fileName.rfind('.');
10721 if (ei!=DString::npos && (sep==DString::npos || ei>sep)) // matches dir/file.ext but not dir.1/file
10722 {
10723 extension=fileName.mid(ei);
10724 }
10725 else
10726 {
10727 extension = ".no_extension";
10728 }
10729
10730 return Doxygen::parserManager->getOutlineParser(extension);
10731}
10732
10733static std::shared_ptr<Entry> parseFile(OutlineParserInterface &parser,
10734 FileDef *fd,const DString &fn,
10735 ClangTUParser *clangParser,bool newTU)
10736{
10737 DString fileName=fn;
10738 AUTO_TRACE("fileName={}",fileName);
10739 DString extension;
10740 if (size_t ei = fileName.rfind('.'); ei!=DString::npos)
10741 {
10742 extension=fileName.mid(ei);
10743 }
10744 else
10745 {
10746 extension = ".no_extension";
10747 }
10748
10749 FileInfo fi(fileName.str());
10750 std::string preBuf;
10751
10752 if (Config_getBool(ENABLE_PREPROCESSING) &&
10753 parser.needsPreprocessing(extension))
10754 {
10755 Preprocessor preprocessor;
10756 StringVector includePath = Config_getList(INCLUDE_PATH);
10757 for (const auto &s : includePath)
10758 {
10759 std::string absPath = FileInfo(s).absFilePath();
10760 preprocessor.addSearchDir(absPath);
10761 }
10762 std::string inBuf;
10763 msg("Preprocessing {}...\n",fn);
10764 readInputFile(fileName,inBuf);
10765 addTerminalCharIfMissing(inBuf,'\n');
10766 preprocessor.processFile(fileName,inBuf,preBuf);
10767 }
10768 else // no preprocessing
10769 {
10770 msg("Reading {}...\n",fn);
10771 readInputFile(fileName,preBuf);
10772 addTerminalCharIfMissing(preBuf,'\n');
10773 }
10774
10775 std::string convBuf;
10776 convBuf.reserve(preBuf.size()+1024);
10777
10778 // convert multi-line C++ comments to C style comments
10779 convertCppComments(preBuf,convBuf,fileName.str());
10780
10781 std::shared_ptr<Entry> fileRoot = std::make_shared<Entry>();
10782 // use language parse to parse the file
10783 if (clangParser)
10784 {
10785 if (newTU) clangParser->parse();
10786 clangParser->switchToFile(fd);
10787 }
10788 parser.parseInput(fileName,convBuf.data(),fileRoot,clangParser);
10789 fileRoot->setFileDef(fd);
10790 return fileRoot;
10791}
10792
10793//! parse the list of input files
10794static void parseFilesMultiThreading(const std::shared_ptr<Entry> &root)
10795{
10796 AUTO_TRACE();
10797#if USE_LIBCLANG
10799 {
10800 StringUnorderedSet processedFiles;
10801
10802 // create a dictionary with files to process
10803 StringUnorderedSet filesToProcess;
10804 for (const auto &s : g_inputFiles)
10805 {
10806 filesToProcess.insert(s);
10807 }
10808
10809 std::mutex processedFilesLock;
10810 // process source files (and their include dependencies)
10811 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10812 msg("Processing input using {} threads.\n",numThreads);
10813 ThreadPool threadPool(numThreads);
10814 using FutureType = std::vector< std::shared_ptr<Entry> >;
10815 std::vector< std::future< FutureType > > results;
10816 for (const auto &s : g_inputFiles)
10817 {
10818 bool ambig = false;
10819 DString qs = s;
10821 ASSERT(fd!=nullptr);
10822 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt::Cpp) // this is a source file
10823 {
10824 // lambda representing the work to executed by a thread
10825 auto processFile = [qs,&filesToProcess,&processedFilesLock,&processedFiles]() {
10826 bool ambig_l = false;
10827 std::vector< std::shared_ptr<Entry> > roots;
10828 FileDef *fd_l = Doxygen::inputNameLinkedMap->findFileDef(qs,ambig_l);
10829 auto clangParser = ClangParser::instance()->createTUParser(fd_l);
10830 auto parser = getParserForFile(qs);
10831 auto fileRoot { parseFile(*parser.get(),fd_l,qs,clangParser.get(),true) };
10832 roots.push_back(fileRoot);
10833
10834 // Now process any include files in the same translation unit
10835 // first. When libclang is used this is much more efficient.
10836 for (auto incFile : clangParser->filesInSameTU())
10837 {
10838 DString qincFile = incFile;
10839 if (filesToProcess.find(incFile)!=filesToProcess.end())
10840 {
10841 bool needsToBeProcessed = false;
10842 {
10843 std::lock_guard<std::mutex> lock(processedFilesLock);
10844 needsToBeProcessed = processedFiles.find(incFile)==processedFiles.end();
10845 if (needsToBeProcessed) processedFiles.insert(incFile);
10846 }
10847 if (qincFile!=qs && needsToBeProcessed)
10848 {
10849 FileDef *ifd=Doxygen::inputNameLinkedMap->findFileDef(qincFile,ambig_l);
10850 if (ifd && !ifd->isReference())
10851 {
10852 //printf(" Processing %s in same translation unit as %s\n",incFile,qPrint(s));
10853 fileRoot = parseFile(*parser.get(),ifd,qincFile,clangParser.get(),false);
10854 roots.push_back(fileRoot);
10855 }
10856 }
10857 }
10858 }
10859 return roots;
10860 };
10861 // dispatch the work and collect the future results
10862 results.emplace_back(threadPool.queue(processFile));
10863 }
10864 }
10865 // synchronize with the Entry result lists produced and add them to the root
10866 for (auto &f : results)
10867 {
10868 auto l = f.get();
10869 for (auto &e : l)
10870 {
10871 root->moveToSubEntryAndKeep(e);
10872 }
10873 }
10874 // process remaining files
10875 results.clear();
10876 for (const auto &s : g_inputFiles)
10877 {
10878 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
10879 {
10880 // lambda representing the work to executed by a thread
10881 auto processFile = [s]() {
10882 bool ambig = false;
10883 DString qs = s;
10884 std::vector< std::shared_ptr<Entry> > roots;
10886 auto parser { getParserForFile(qs) };
10887 bool useClang = getLanguageFromFileName(qs)==SrcLangExt::Cpp;
10888 if (useClang)
10889 {
10890 auto clangParser = ClangParser::instance()->createTUParser(fd);
10891 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
10892 roots.push_back(fileRoot);
10893 }
10894 else
10895 {
10896 auto fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10897 roots.push_back(fileRoot);
10898 }
10899 return roots;
10900 };
10901 results.emplace_back(threadPool.queue(processFile));
10902 }
10903 }
10904 // synchronize with the Entry result lists produced and add them to the root
10905 for (auto &f : results)
10906 {
10907 auto l = f.get();
10908 for (auto &e : l)
10909 {
10910 root->moveToSubEntryAndKeep(e);
10911 }
10912 }
10913 }
10914 else // normal processing
10915#endif
10916 {
10917 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10918 msg("Processing input using {} threads.\n",numThreads);
10919 ThreadPool threadPool(numThreads);
10920 using FutureType = std::shared_ptr<Entry>;
10921 std::vector< std::future< FutureType > > results;
10922 for (const auto &s : g_inputFiles)
10923 {
10924 // lambda representing the work to executed by a thread
10925 auto processFile = [s]() {
10926 bool ambig = false;
10927 DString qs = s;
10929 auto parser = getParserForFile(qs);
10930 auto fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10931 return fileRoot;
10932 };
10933 // dispatch the work and collect the future results
10934 results.emplace_back(threadPool.queue(processFile));
10935 }
10936 // synchronize with the Entry results produced and add them to the root
10937 for (auto &f : results)
10938 {
10939 root->moveToSubEntryAndKeep(f.get());
10940 }
10941 }
10942}
10943
10944//! parse the list of input files
10945static void parseFilesSingleThreading(const std::shared_ptr<Entry> &root)
10946{
10947 AUTO_TRACE();
10948#if USE_LIBCLANG
10950 {
10951 StringUnorderedSet processedFiles;
10952
10953 // create a dictionary with files to process
10954 StringUnorderedSet filesToProcess;
10955 for (const auto &s : g_inputFiles)
10956 {
10957 filesToProcess.insert(s);
10958 }
10959
10960 // process source files (and their include dependencies)
10961 for (const auto &s : g_inputFiles)
10962 {
10963 bool ambig = false;
10964 DString qs =s;
10966 ASSERT(fd!=nullptr);
10967 if (fd->isSource() && !fd->isReference() && getLanguageFromFileName(qs)==SrcLangExt::Cpp) // this is a source file
10968 {
10969 auto clangParser = ClangParser::instance()->createTUParser(fd);
10970 auto parser { getParserForFile(qs) };
10971 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
10972 root->moveToSubEntryAndKeep(fileRoot);
10973 processedFiles.insert(s);
10974
10975 // Now process any include files in the same translation unit
10976 // first. When libclang is used this is much more efficient.
10977 for (auto incFile : clangParser->filesInSameTU())
10978 {
10979 //printf(" file %s\n",qPrint(incFile));
10980 if (filesToProcess.find(incFile)!=filesToProcess.end() && // file need to be processed
10981 processedFiles.find(incFile)==processedFiles.end()) // and is not processed already
10982 {
10983 FileDef *ifd=Doxygen::inputNameLinkedMap->findFileDef(incFile,ambig);
10984 if (ifd && !ifd->isReference())
10985 {
10986 //printf(" Processing %s in same translation unit as %s\n",qPrint(incFile),qPrint(qs));
10987 fileRoot = parseFile(*parser.get(),ifd,incFile,clangParser.get(),false);
10988 root->moveToSubEntryAndKeep(fileRoot);
10989 processedFiles.insert(incFile);
10990 }
10991 }
10992 }
10993 }
10994 }
10995 // process remaining files
10996 for (const auto &s : g_inputFiles)
10997 {
10998 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
10999 {
11000 bool ambig = false;
11001 DString qs = s;
11003 if (getLanguageFromFileName(qs)==SrcLangExt::Cpp) // not yet processed
11004 {
11005 auto clangParser = ClangParser::instance()->createTUParser(fd);
11006 auto parser { getParserForFile(qs) };
11007 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
11008 root->moveToSubEntryAndKeep(fileRoot);
11009 }
11010 else
11011 {
11012 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(qs) };
11013 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
11014 root->moveToSubEntryAndKeep(fileRoot);
11015 }
11016 processedFiles.insert(s);
11017 }
11018 }
11019 }
11020 else // normal processing
11021#endif
11022 {
11023 for (const auto &s : g_inputFiles)
11024 {
11025 bool ambig = false;
11026 DString qs = s;
11028 ASSERT(fd!=nullptr);
11029 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(qs) };
11030 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
11031 root->moveToSubEntryAndKeep(std::move(fileRoot));
11032 }
11033 }
11034}
11035
11036// resolves a path that may include symlinks, if a recursive symlink is
11037// found an empty string is returned.
11038static std::string resolveSymlink(const std::string &path)
11039{
11040 size_t sepPos=0;
11041 size_t oldPos=0;
11042 StringUnorderedSet nonSymlinks;
11043 StringUnorderedSet known;
11044 DString result(path);
11045 DString oldPrefix = "/";
11046 do
11047 {
11048#if defined(_WIN32)
11049 // UNC path, skip server and share name
11050 if (sepPos==0 && (result.startsWith("//") || result.startsWith("\\\\")))
11051 sepPos = result.find('/',2);
11052 if (sepPos!=DString::npos)
11053 sepPos = result.find('/',sepPos+1);
11054#else
11055 sepPos = result.find('/',sepPos+1);
11056#endif
11057 DString prefix = sepPos==DString::npos ? result : result.left(sepPos);
11058 if (nonSymlinks.find(prefix.str())==nonSymlinks.end())
11059 {
11060 FileInfo fi(prefix.str());
11061 if (fi.isSymLink())
11062 {
11063 DString target = fi.readLink();
11064 bool isRelative = FileInfo(target.str()).isRelative();
11065 if (isRelative)
11066 {
11067 target = Dir::cleanDirPath(oldPrefix.str()+"/"+target.str());
11068 }
11069 if (sepPos!=DString::npos)
11070 {
11071 if (fi.isDir() && !target.empty() && target.at(target.length()-1)!='/')
11072 {
11073 target+='/';
11074 }
11075 target+=result.mid(sepPos);
11076 }
11077 result = Dir::cleanDirPath(target.str());
11078 if (known.find(result.str())!=known.end()) return std::string(); // recursive symlink!
11079 known.insert(result.str());
11080 if (isRelative)
11081 {
11082 sepPos = oldPos;
11083 }
11084 else // link to absolute path
11085 {
11086 sepPos = 0;
11087 oldPrefix = "/";
11088 }
11089 }
11090 else
11091 {
11092 nonSymlinks.insert(prefix.str());
11093 oldPrefix = prefix;
11094 }
11095 oldPos = sepPos;
11096 }
11097 }
11098 while (sepPos!=DString::npos);
11099 return Dir::cleanDirPath(result.str());
11100}
11101
11103
11104//----------------------------------------------------------------------------
11105// Read all files matching at least one pattern in 'patList' in the
11106// directory represented by 'fi'.
11107// The directory is read iff the recursiveFlag is set.
11108// The contents of all files is append to the input string
11109
11110static void readDir(FileInfo *fi,
11111 FileNameLinkedMap *fnMap,
11112 StringUnorderedSet *exclSet,
11113 const StringVector *patList,
11114 const StringVector *exclPatList,
11115 StringVector *resultList,
11116 StringUnorderedSet *resultSet,
11117 bool errorIfNotExist,
11118 bool recursive,
11119 StringUnorderedSet *killSet,
11120 StringUnorderedSet *paths
11121 )
11122{
11123 std::string dirName = fi->absFilePath();
11124 if (paths && !dirName.empty())
11125 {
11126 paths->insert(dirName);
11127 }
11128 //printf("%s isSymLink()=%d\n",qPrint(dirName),fi->isSymLink());
11129 if (fi->isSymLink())
11130 {
11131 dirName = resolveSymlink(dirName);
11132 if (dirName.empty())
11133 {
11134 //printf("RECURSIVE SYMLINK: %s\n",qPrint(dirName));
11135 return; // recursive symlink
11136 }
11137 }
11138
11139 if (g_pathsVisited.find(dirName)!=g_pathsVisited.end())
11140 {
11141 //printf("PATH ALREADY VISITED: %s\n",qPrint(dirName));
11142 return; // already visited path
11143 }
11144 g_pathsVisited.insert(dirName);
11145
11146 Dir dir(dirName);
11147 msg("Searching for files in directory {}\n", fi->absFilePath());
11148 //printf("killSet=%p count=%d\n",killSet,killSet ? (int)killSet->count() : -1);
11149
11150 StringVector dirResultList;
11151
11152 bool caseSenseNames = useCaseSenseNames();
11153
11154 for (const auto &dirEntry : dir.iterator())
11155 {
11156 FileInfo cfi(dirEntry.path());
11157 auto checkPatterns = [&]() -> bool
11158 {
11159 return (patList==nullptr || cfi.match(*patList,caseSenseNames)) &&
11160 (exclPatList==nullptr || !cfi.match(*exclPatList,caseSenseNames)) &&
11161 (killSet==nullptr || killSet->find(cfi.absFilePath())==killSet->end());
11162 };
11163
11164 if (exclSet==nullptr || exclSet->find(cfi.absFilePath())==exclSet->end())
11165 { // file should not be excluded
11166 //printf("killSet->find(%s)\n",qPrint(cfi->absFilePath()));
11167 if (Config_getBool(EXCLUDE_SYMLINKS) && cfi.isSymLink())
11168 {
11169 }
11170 else if (!cfi.exists() || !cfi.isReadable())
11171 {
11172 if (errorIfNotExist && checkPatterns())
11173 {
11174 warn_uncond("source '{}' is not a readable file or directory... skipping.\n",cfi.absFilePath());
11175 }
11176 }
11177 else if (cfi.isFile() && checkPatterns())
11178 {
11179 std::string name=cfi.fileName();
11180 std::string path=cfi.dirPath()+"/";
11181 std::string fullName=path+name;
11182 if (fnMap)
11183 {
11184 auto fd = createFileDef(path,name);
11185 FileName *fn=nullptr;
11186 if (!name.empty())
11187 {
11188 fn = fnMap->add(name);
11189 fn->push_back(std::move(fd));
11190 }
11191 }
11192 dirResultList.push_back(fullName);
11193 if (resultSet) resultSet->insert(fullName);
11194 if (killSet) killSet->insert(fullName);
11195 }
11196 else if (recursive &&
11197 cfi.isDir() &&
11198 (exclPatList==nullptr || !cfi.match(*exclPatList,caseSenseNames)) &&
11199 cfi.fileName().at(0)!='.') // skip "." ".." and ".dir"
11200 {
11201 FileInfo acfi(cfi.absFilePath());
11202 readDir(&acfi,fnMap,exclSet,
11203 patList,exclPatList,&dirResultList,resultSet,errorIfNotExist,
11204 recursive,killSet,paths);
11205 }
11206 }
11207 }
11208 if (resultList && !dirResultList.empty())
11209 {
11210 // sort the resulting list to make the order platform independent.
11211 std::stable_sort(dirResultList.begin(),
11212 dirResultList.end(),
11213 [](const auto &f1,const auto &f2) { return dstricmp_sort(f1.c_str(),f2.c_str())<0; });
11214
11215 // append the sorted results to resultList
11216 resultList->insert(resultList->end(), dirResultList.begin(), dirResultList.end());
11217 }
11218}
11219
11220
11221//----------------------------------------------------------------------------
11222// read a file or all files in a directory and append their contents to the
11223// input string. The names of the files are appended to the 'fiList' list.
11224
11226 FileNameLinkedMap *fnMap,
11227 StringUnorderedSet *exclSet,
11228 const StringVector *patList,
11229 const StringVector *exclPatList,
11230 StringVector *resultList,
11231 StringUnorderedSet *resultSet,
11232 bool recursive,
11233 bool errorIfNotExist,
11234 StringUnorderedSet *killSet,
11235 StringUnorderedSet *paths
11236 )
11237{
11238 //printf("killSet count=%d\n",killSet ? (int)killSet->size() : -1);
11239 // strip trailing slashes
11240 if (s.empty()) return;
11241
11242 g_pathsVisited.clear();
11243
11244 FileInfo fi(s.str());
11245 //printf("readFileOrDirectory(%s)\n",s);
11246 {
11247 if (exclSet==nullptr || exclSet->find(fi.absFilePath())==exclSet->end())
11248 {
11249 if (Config_getBool(EXCLUDE_SYMLINKS) && fi.isSymLink())
11250 {
11251 }
11252 else if (!fi.exists() || !fi.isReadable())
11253 {
11254 if (errorIfNotExist)
11255 {
11256 warn_uncond("source '{}' is not a readable file or directory... skipping.\n",s);
11257 }
11258 }
11259 else if (fi.isFile())
11260 {
11261 std::string dirPath = fi.dirPath(true);
11262 std::string filePath = fi.absFilePath();
11263 if (paths && !dirPath.empty())
11264 {
11265 paths->insert(dirPath);
11266 }
11267 //printf("killSet.find(%s)=%d\n",qPrint(fi.absFilePath()),killSet.find(fi.absFilePath())!=killSet.end());
11268 if (killSet==nullptr || killSet->find(filePath)==killSet->end())
11269 {
11270 std::string name=fi.fileName();
11271 if (fnMap)
11272 {
11273 auto fd = createFileDef(dirPath+"/",name);
11274 if (!name.empty())
11275 {
11276 FileName *fn = fnMap->add(name);
11277 fn->push_back(std::move(fd));
11278 }
11279 }
11280 if (resultList || resultSet)
11281 {
11282 if (resultList) resultList->push_back(filePath);
11283 if (resultSet) resultSet->insert(filePath);
11284 }
11285
11286 if (killSet) killSet->insert(fi.absFilePath());
11287 }
11288 }
11289 else if (fi.isDir()) // readable dir
11290 {
11291 readDir(&fi,fnMap,exclSet,patList,
11292 exclPatList,resultList,resultSet,errorIfNotExist,
11293 recursive,killSet,paths);
11294 }
11295 }
11296 }
11297}
11298
11299//----------------------------------------------------------------------------
11300
11302{
11303 DString anchor;
11305 {
11306 MemberDef *md = toMemberDef(d);
11307 anchor=":"+md->anchor();
11308 }
11309 DString scope;
11310 DString fn = d->getOutputFileBase();
11313 {
11314 scope = fn;
11315 }
11316 t << "REPLACE INTO symbols (symbol_id,scope_id,name,file,line) VALUES('"
11317 << fn+anchor << "','"
11318 << scope << "','"
11319 << d->name() << "','"
11320 << d->getDefFileName() << "','"
11321 << d->getDefLine()
11322 << "');\n";
11323}
11324
11325static void dumpSymbolMap()
11326{
11327 std::ofstream f = Portable::openOutputStream("symbols.sql");
11328 if (f.is_open())
11329 {
11330 TextStream t(&f);
11331 for (const auto &[name,symList] : *Doxygen::symbolMap)
11332 {
11333 for (const auto &def : symList)
11334 {
11335 dumpSymbol(t,def);
11336 }
11337 }
11338 }
11339}
11340
11341// print developer options of Doxygen
11342static void devUsage()
11343{
11345 msg("Developer parameters:\n");
11346 msg(" -m dump symbol map\n");
11347 msg(" -b making messages output unbuffered\n");
11348 msg(" -c <file> process input file as a comment block and produce HTML output\n");
11349#if ENABLE_TRACING
11350 msg(" -t [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout)\n");
11351 msg(" -t_time [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout),\n"
11352 " and include time and thread information\n");
11353#endif
11354 msg(" -d <level> enable a debug level, such as (multiple invocations of -d are possible):\n");
11356}
11357
11358
11359//----------------------------------------------------------------------------
11360// print the version of Doxygen
11361
11362static void version(const bool extended)
11363{
11365 DString versionString = getFullVersion();
11366 msg("{}\n",versionString);
11367 if (extended)
11368 {
11369 DString extVers;
11370 if (!extVers.empty()) extVers+= ", ";
11371 extVers += "sqlite3 ";
11372 extVers += sqlite3_libversion();
11373#if USE_LIBCLANG
11374 if (!extVers.empty()) extVers+= ", ";
11375 extVers += "clang support ";
11376 extVers += CLANG_VERSION_STRING;
11377#endif
11378 if (!extVers.empty())
11379 {
11380 if (size_t lastComma = extVers.rfind(','); lastComma != DString::npos)
11381 {
11382 extVers = extVers.replace(lastComma,1," and");
11383 }
11384 msg(" with {}.\n",extVers);
11385 }
11386 }
11387}
11388
11389//----------------------------------------------------------------------------
11390// print the usage of Doxygen
11391
11392static void usage(const DString &name,const DString &versionString)
11393{
11395 msg("Doxygen version {0}\nCopyright Dimitri van Heesch 1997-2025\n\n"
11396 "You can use Doxygen in a number of ways:\n\n"
11397 "1) Use Doxygen to generate a template configuration file*:\n"
11398 " {1} [-s] -g [configName]\n\n"
11399 "2) Use Doxygen to update an old configuration file*:\n"
11400 " {1} [-s] -u [configName]\n\n"
11401 "3) Use Doxygen to generate documentation using an existing "
11402 "configuration file*:\n"
11403 " {1} [configName]\n\n"
11404 "4) Use Doxygen to generate a template file controlling the layout of the\n"
11405 " generated documentation:\n"
11406 " {1} -l [layoutFileName]\n\n"
11407 " In case layoutFileName is omitted DoxygenLayout.xml will be used as filename.\n"
11408 " If - is used for layoutFileName Doxygen will write to standard output.\n\n"
11409 "5) Use Doxygen to generate a template style sheet file for RTF, HTML or Latex.\n"
11410 " RTF: {1} -w rtf styleSheetFile\n"
11411 " HTML: {1} -w html headerFile footerFile styleSheetFile [configFile]\n"
11412 " LaTeX: {1} -w latex headerFile footerFile styleSheetFile [configFile]\n\n"
11413 "6) Use Doxygen to generate a rtf extensions file\n"
11414 " {1} -e rtf extensionsFile\n\n"
11415 " If - is used for extensionsFile Doxygen will write to standard output.\n\n"
11416 "7) Use Doxygen to compare the used configuration file with the template configuration file\n"
11417 " {1} -x [configFile]\n\n"
11418 " Use Doxygen to compare the used configuration file with the template configuration file\n"
11419 " without replacing the environment variables or CMake type replacement variables\n"
11420 " {1} -x_noenv [configFile]\n\n"
11421 "8) Use Doxygen to show a list of built-in emojis.\n"
11422 " {1} -f emoji outputFileName\n\n"
11423 " If - is used for outputFileName Doxygen will write to standard output.\n\n"
11424 "*) If -s is specified the comments of the configuration items in the config file will be omitted.\n"
11425 " If configName is omitted 'Doxyfile' will be used as a default.\n"
11426 " If - is used for configFile Doxygen will write / read the configuration to /from standard output / input.\n\n"
11427 "If -q is used for a Doxygen documentation run, Doxygen will see this as if QUIET=YES has been set.\n\n"
11428 "-v print version string, -V print extended version information\n"
11429 "-h,-? prints usage help information\n"
11430 "{1} -d prints additional usage flags for debugging purposes\n",versionString,name);
11431}
11432
11433//----------------------------------------------------------------------------
11434// read the argument of option 'c' from the comment argument list and
11435// update the option index 'optInd'.
11436
11437static const char *getArg(int argc,char **argv,int &optInd)
11438{
11439 char *s=nullptr;
11440 if (dstrlen(&argv[optInd][2])>0)
11441 s=&argv[optInd][2];
11442 else if (optInd+1<argc && argv[optInd+1][0]!='-')
11443 s=argv[++optInd];
11444 return s;
11445}
11446
11447//----------------------------------------------------------------------------
11448
11449/** @brief /dev/null outline parser */
11451{
11452 public:
11453 void parseInput(const DString &/* file */, const char * /* buf */,const std::shared_ptr<Entry> &, ClangTUParser*) override {}
11454 bool needsPreprocessing(const DString &) const override { return false; }
11455 void parsePrototype(const DString &) override {}
11456};
11457
11458
11459template<class T> std::function< std::unique_ptr<T>() > make_parser_factory()
11460{
11461 return []() { return std::make_unique<T>(); };
11462}
11463
11465{
11466 initResources();
11467 DString lang = Portable::getenv("LC_ALL");
11468 if (!lang.empty()) Portable::setenv("LANG",lang);
11469 std::setlocale(LC_ALL,"");
11470 std::setlocale(LC_CTYPE,"C"); // to get isspace(0xA0)==0, needed for UTF-8
11471 std::setlocale(LC_NUMERIC,"C");
11472
11474
11498
11499 // register any additional parsers here...
11500
11502
11503#if USE_LIBCLANG
11505#endif
11514 Doxygen::pageLinkedMap = new PageLinkedMap; // all doc pages
11515 Doxygen::exampleLinkedMap = new PageLinkedMap; // all examples
11516 //Doxygen::tagDestinationDict.setAutoDelete(true);
11518
11519 // initialization of these globals depends on
11520 // configuration switches so we need to postpone these
11521 Doxygen::globalScope = nullptr;
11531
11532}
11533
11566
11567void readConfiguration(int argc, char **argv)
11568{
11569 DString versionString = getFullVersion();
11570
11571 // helper that calls \a func to write to file \a fileName via a TextStream
11572 auto writeFile = [](const char *fileName,std::function<void(TextStream&)> func) -> bool
11573 {
11574 std::ofstream f;
11575 if (openOutputFile(fileName,f))
11576 {
11577 TextStream t(&f);
11578 func(t);
11579 return true;
11580 }
11581 return false;
11582 };
11583
11584
11585 /**************************************************************************
11586 * Handle arguments *
11587 **************************************************************************/
11588
11589 int optInd=1;
11590 DString configName;
11591 DString traceName;
11592 bool genConfig=false;
11593 bool shortList=false;
11594 bool traceTiming=false;
11596 bool updateConfig=false;
11597 bool quiet = false;
11598 while (optInd<argc && argv[optInd][0]=='-' &&
11599 (isalpha(argv[optInd][1]) || argv[optInd][1]=='?' ||
11600 argv[optInd][1]=='-')
11601 )
11602 {
11603 switch(argv[optInd][1])
11604 {
11605 case 'g':
11606 {
11607 genConfig=true;
11608 }
11609 break;
11610 case 'l':
11611 {
11612 DString layoutName;
11613 if (optInd+1>=argc)
11614 {
11615 layoutName="DoxygenLayout.xml";
11616 }
11617 else
11618 {
11619 layoutName=argv[optInd+1];
11620 }
11621 writeDefaultLayoutFile(layoutName);
11623 exit(0);
11624 }
11625 break;
11626 case 'c':
11627 if (optInd+1>=argc) // no file name given
11628 {
11629 msg("option \"-c\" is missing the file name to read\n");
11630 devUsage();
11632 exit(1);
11633 }
11634 else
11635 {
11636 g_commentFileName=argv[optInd+1];
11637 optInd++;
11638 }
11639 g_singleComment=true;
11640 quiet=true;
11641 break;
11642 case 'd':
11643 {
11644 DString debugLabel=getArg(argc,argv,optInd);
11645 if (debugLabel.empty())
11646 {
11647 devUsage();
11649 exit(0);
11650 }
11651 int retVal = Debug::setFlagStr(debugLabel);
11652 if (!retVal)
11653 {
11654 msg("option \"-d\" has unknown debug specifier: \"{}\".\n",debugLabel);
11655 devUsage();
11657 exit(1);
11658 }
11659 }
11660 break;
11661 case 't':
11662 {
11663#if ENABLE_TRACING
11664 if (!strcmp(argv[optInd]+1,"t_time"))
11665 {
11666 traceTiming = true;
11667 }
11668 else if (!strcmp(argv[optInd]+1,"t"))
11669 {
11670 traceTiming = false;
11671 }
11672 else
11673 {
11674 err("option should be \"-t\" or \"-t_time\", found: \"{}\".\n",argv[optInd]);
11676 exit(1);
11677 }
11678 if (optInd+1>=argc || argv[optInd+1][0] == '-') // no file name given
11679 {
11680 traceName="stdout";
11681 }
11682 else
11683 {
11684 traceName=argv[optInd+1];
11685 optInd++;
11686 }
11687#else
11688 err("support for option \"-t\" has not been compiled in (use a debug build or a release build with tracing enabled).\n");
11690 exit(1);
11691#endif
11692 }
11693 break;
11694 case 'x':
11695 if (!strcmp(argv[optInd]+1,"x_noenv")) diffList=Config::CompareMode::CompressedNoEnv;
11696 else if (!strcmp(argv[optInd]+1,"x")) diffList=Config::CompareMode::Compressed;
11697 else
11698 {
11699 err("option should be \"-x\" or \"-x_noenv\", found: \"{}\".\n",argv[optInd]);
11701 exit(1);
11702 }
11703 break;
11704 case 's':
11705 shortList=true;
11706 break;
11707 case 'u':
11708 updateConfig=true;
11709 break;
11710 case 'e':
11711 {
11712 DString formatName=getArg(argc,argv,optInd);
11713 if (formatName.empty())
11714 {
11715 err("option \"-e\" is missing format specifier rtf.\n");
11717 exit(1);
11718 }
11719 if (dstricmp(formatName.data(),"rtf")==0)
11720 {
11721 if (optInd+1>=argc)
11722 {
11723 err("option \"-e rtf\" is missing an extensions file name\n");
11725 exit(1);
11726 }
11727 writeFile(argv[optInd+1],RTFGenerator::writeExtensionsFile);
11729 exit(0);
11730 }
11731 err("option \"-e\" has invalid format specifier.\n");
11733 exit(1);
11734 }
11735 break;
11736 case 'f':
11737 {
11738 DString listName=getArg(argc,argv,optInd);
11739 if (listName.empty())
11740 {
11741 err("option \"-f\" is missing list specifier.\n");
11743 exit(1);
11744 }
11745 if (dstricmp(listName.data(),"emoji")==0)
11746 {
11747 if (optInd+1>=argc)
11748 {
11749 err("option \"-f emoji\" is missing an output file name\n");
11751 exit(1);
11752 }
11753 writeFile(argv[optInd+1],[](TextStream &t) { EmojiEntityMapper::instance().writeEmojiFile(t); });
11755 exit(0);
11756 }
11757 err("option \"-f\" has invalid list specifier.\n");
11759 exit(1);
11760 }
11761 break;
11762 case 'w':
11763 {
11764 DString formatName=getArg(argc,argv,optInd);
11765 if (formatName.empty())
11766 {
11767 err("option \"-w\" is missing format specifier rtf, html or latex\n");
11769 exit(1);
11770 }
11771 if (dstricmp(formatName.data(),"rtf")==0)
11772 {
11773 if (optInd+1>=argc)
11774 {
11775 err("option \"-w rtf\" is missing a style sheet file name\n");
11777 exit(1);
11778 }
11779 if (!writeFile(argv[optInd+1],RTFGenerator::writeStyleSheetFile))
11780 {
11781 err("error opening RTF style sheet file {}!\n",argv[optInd+1]);
11783 exit(1);
11784 }
11786 exit(0);
11787 }
11788 else if (dstricmp(formatName.data(),"html")==0)
11789 {
11790 Config::init();
11791 if (optInd+4<argc || FileInfo("Doxyfile").exists() || FileInfo("doxyfile").exists())
11792 // explicit config file mentioned or default found on disk
11793 {
11794 DString df = optInd+4<argc ? argv[optInd+4] : (FileInfo("Doxyfile").exists() ? DString("Doxyfile") : DString("doxyfile"));
11795 if (!Config::parse(df)) // parse the config file
11796 {
11797 err("error opening or reading configuration file {}!\n",argv[optInd+4]);
11799 exit(1);
11800 }
11801 }
11802 if (optInd+3>=argc)
11803 {
11804 err("option \"-w html\" does not have enough arguments\n");
11806 exit(1);
11807 }
11808 Config::postProcess(true);
11811 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11812 writeFile(argv[optInd+1],[&](TextStream &t) { HtmlGenerator::writeHeaderFile(t,argv[optInd+3]); });
11813 writeFile(argv[optInd+2],HtmlGenerator::writeFooterFile);
11814 writeFile(argv[optInd+3],HtmlGenerator::writeStyleSheetFile);
11816 exit(0);
11817 }
11818 else if (dstricmp(formatName.data(),"latex")==0)
11819 {
11820 Config::init();
11821 if (optInd+4<argc || FileInfo("Doxyfile").exists() || FileInfo("doxyfile").exists())
11822 {
11823 DString df = optInd+4<argc ? argv[optInd+4] : (FileInfo("Doxyfile").exists() ? DString("Doxyfile") : DString("doxyfile"));
11824 if (!Config::parse(df))
11825 {
11826 err("error opening or reading configuration file {}!\n",argv[optInd+4]);
11828 exit(1);
11829 }
11830 }
11831 if (optInd+3>=argc)
11832 {
11833 err("option \"-w latex\" does not have enough arguments\n");
11835 exit(1);
11836 }
11837 Config::postProcess(true);
11840 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11841 writeFile(argv[optInd+1],LatexGenerator::writeHeaderFile);
11842 writeFile(argv[optInd+2],LatexGenerator::writeFooterFile);
11843 writeFile(argv[optInd+3],LatexGenerator::writeStyleSheetFile);
11845 exit(0);
11846 }
11847 else
11848 {
11849 err("Illegal format specifier \"{}\": should be one of rtf, html or latex\n",formatName);
11851 exit(1);
11852 }
11853 }
11854 break;
11855 case 'm':
11856 g_dumpSymbolMap = true;
11857 break;
11858 case 'v':
11859 version(false);
11861 exit(0);
11862 break;
11863 case 'V':
11864 version(true);
11866 exit(0);
11867 break;
11868 case '-':
11869 if (dstrcmp(&argv[optInd][2],"help")==0)
11870 {
11871 usage(argv[0],versionString);
11872 exit(0);
11873 }
11874 else if (dstrcmp(&argv[optInd][2],"version")==0)
11875 {
11876 version(false);
11878 exit(0);
11879 }
11880 else if ((dstrcmp(&argv[optInd][2],"Version")==0) ||
11881 (dstrcmp(&argv[optInd][2],"VERSION")==0))
11882 {
11883 version(true);
11885 exit(0);
11886 }
11887 else
11888 {
11889 err("Unknown option \"-{}\"\n",&argv[optInd][1]);
11890 usage(argv[0],versionString);
11891 exit(1);
11892 }
11893 break;
11894 case 'b':
11895 setvbuf(stdout,nullptr,_IONBF,0);
11896 break;
11897 case 'q':
11898 quiet = true;
11899 break;
11900 case 'h':
11901 case '?':
11902 usage(argv[0],versionString);
11903 exit(0);
11904 break;
11905 default:
11906 err("Unknown option \"-{:c}\"\n",argv[optInd][1]);
11907 usage(argv[0],versionString);
11908 exit(1);
11909 }
11910 optInd++;
11911 }
11912
11913 /**************************************************************************
11914 * Parse or generate the config file *
11915 **************************************************************************/
11916
11917 initTracing(traceName.data(),traceTiming);
11918 TRACE("Doxygen version used: {}",getFullVersion());
11919 Config::init();
11920
11921 FileInfo configFileInfo1("Doxyfile"),configFileInfo2("doxyfile");
11922 if (optInd>=argc)
11923 {
11924 if (configFileInfo1.exists())
11925 {
11926 configName="Doxyfile";
11927 }
11928 else if (configFileInfo2.exists())
11929 {
11930 configName="doxyfile";
11931 }
11932 else if (genConfig)
11933 {
11934 configName="Doxyfile";
11935 }
11936 else
11937 {
11938 err("Doxyfile not found and no input file specified!\n");
11939 usage(argv[0],versionString);
11940 exit(1);
11941 }
11942 }
11943 else
11944 {
11945 FileInfo fi(argv[optInd]);
11946 if (fi.exists() || dstrcmp(argv[optInd],"-")==0 || genConfig)
11947 {
11948 configName=argv[optInd];
11949 }
11950 else
11951 {
11952 err("configuration file {} not found!\n",argv[optInd]);
11953 usage(argv[0],versionString);
11954 exit(1);
11955 }
11956 }
11957
11958 if (genConfig)
11959 {
11960 generateConfigFile(configName,shortList);
11962 exit(0);
11963 }
11964
11965 if (!Config::parse(configName,updateConfig,diffList))
11966 {
11967 err("could not open or read configuration file {}!\n",configName);
11969 exit(1);
11970 }
11971
11972 if (diffList!=Config::CompareMode::Full)
11973 {
11975 compareDoxyfile(diffList);
11977 exit(0);
11978 }
11979
11980 if (updateConfig)
11981 {
11983 generateConfigFile(configName,shortList,true);
11985 exit(0);
11986 }
11987
11988 /* Perlmod wants to know the path to the config file.*/
11989 FileInfo configFileInfo(configName.str());
11990 setPerlModDoxyfile(configFileInfo.absFilePath());
11991
11992 /* handle -q option */
11993 if (quiet) Config_updateBool(QUIET,true);
11994}
11995
11996/** check and resolve config options */
11998{
11999 AUTO_TRACE();
12000
12001 Config::postProcess(false);
12005}
12006
12007/** adjust globals that depend on configuration settings. */
12009{
12010 AUTO_TRACE();
12011 Doxygen::globalNamespaceDef = createNamespaceDef("<globalScope>",1,1,"<globalScope>");
12022
12023 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
12024
12025 /* Set the global html file extension. */
12026 Doxygen::htmlFileExtension = Config_getString(HTML_FILE_EXTENSION);
12027
12028
12030 Config_getBool(CALLER_GRAPH) ||
12031 Config_getBool(REFERENCES_RELATION) ||
12032 Config_getBool(REFERENCED_BY_RELATION);
12033
12034 /**************************************************************************
12035 * Add custom extension mappings
12036 **************************************************************************/
12037
12038 StringVector extMaps = Config_getList(EXTENSION_MAPPING);
12039 for (const auto &mapping : extMaps)
12040 {
12041 DString mapStr = mapping;
12042 if (size_t i=mapStr.find('='); i==DString::npos)
12043 {
12044 continue;
12045 }
12046 else
12047 {
12048 DString ext = mapStr.left(i).stripWhiteSpace().lower();
12049 DString language = mapStr.mid(i+1).stripWhiteSpace().lower();
12050 if (ext.empty() || language.empty())
12051 {
12052 continue;
12053 }
12054
12055 if (!updateLanguageMapping(ext,language))
12056 {
12057 err("Failed to map file extension '{}' to unsupported language '{}'.\n"
12058 "Check the EXTENSION_MAPPING setting in the config file.\n",
12059 ext,language);
12060 }
12061 else
12062 {
12063 msg("Adding custom extension mapping: '{}' will be treated as language '{}'\n",
12064 ext,language);
12065 }
12066 }
12067 }
12068 // create input file exncodings
12069
12070 // check INPUT_ENCODING
12071 void *cd = portable_iconv_open("UTF-8",Config_getString(INPUT_ENCODING).data());
12072 if (cd==reinterpret_cast<void *>(-1))
12073 {
12074 term("unsupported character conversion: '{}'->'UTF-8': {}\n"
12075 "Check the 'INPUT_ENCODING' setting in the config file!\n",
12076 Config_getString(INPUT_ENCODING),strerror(errno));
12077 }
12078 else
12079 {
12081 }
12082
12083 // check and split INPUT_FILE_ENCODING
12084 StringVector fileEncod = Config_getList(INPUT_FILE_ENCODING);
12085 for (const auto &mapping : fileEncod)
12086 {
12087 DString mapStr = mapping;
12088 if (size_t i=mapStr.find('='); i==DString::npos)
12089 {
12090 continue;
12091 }
12092 else
12093 {
12094 DString pattern = mapStr.left(i).stripWhiteSpace().lower();
12095 DString encoding = mapStr.mid(i+1).stripWhiteSpace().lower();
12096 if (pattern.empty() || encoding.empty())
12097 {
12098 continue;
12099 }
12100 cd = portable_iconv_open("UTF-8",encoding.data());
12101 if (cd==reinterpret_cast<void *>(-1))
12102 {
12103 term("unsupported character conversion: '{}'->'UTF-8': {}\n"
12104 "Check the 'INPUT_FILE_ENCODING' setting in the config file!\n",
12105 encoding,strerror(errno));
12106 }
12107 else
12108 {
12110 }
12111
12112 Doxygen::inputFileEncodingList.emplace_back(pattern, encoding);
12113 }
12114 }
12115
12116 // add predefined macro name to a dictionary
12117 StringVector expandAsDefinedList = Config_getList(EXPAND_AS_DEFINED);
12118 for (const auto &s : expandAsDefinedList)
12119 {
12121 }
12122
12123 // read aliases and store them in a dictionary
12124 readAliases();
12125
12126 // store number of spaces in a tab into Doxygen::spaces
12127 int tabSize = Config_getInt(TAB_SIZE);
12128 Doxygen::spaces.resize(tabSize);
12129 for (int sp=0; sp<tabSize; sp++) Doxygen::spaces.at(sp)=' ';
12130 Doxygen::spaces.at(tabSize)='\0';
12131}
12132
12133#ifdef HAS_SIGNALS
12134static void stopDoxygen(int)
12135{
12136 signal(SIGINT,SIG_DFL); // Re-register signal handler for default action
12137 Dir thisDir;
12138 msg("Cleaning up...\n");
12139 if (!Doxygen::filterDBFileName.empty())
12140 {
12141 thisDir.remove(Doxygen::filterDBFileName.str());
12142 }
12143 killpg(0,SIGINT);
12145 exitTracing();
12146 exit(1);
12147}
12148#endif
12149
12150static void writeTagFile()
12151{
12152 DString generateTagFile = Config_getString(GENERATE_TAGFILE);
12153 if (generateTagFile.empty()) return;
12154
12155 std::ofstream f = Portable::openOutputStream(generateTagFile);
12156 if (!f.is_open())
12157 {
12158 err("cannot open tag file {} for writing\n", generateTagFile);
12159 return;
12160 }
12161 TextStream tagFile(&f);
12162 tagFile << "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>\n";
12163 tagFile << "<tagfile doxygen_version=\"" << getDoxygenVersion() << "\"";
12164 std::string gitVersion = getGitVersion();
12165 if (!gitVersion.empty())
12166 {
12167 tagFile << " doxygen_gitid=\"" << gitVersion << "\"";
12168 }
12169 tagFile << ">\n";
12170
12171 // for each file
12172 for (const auto &fn : *Doxygen::inputNameLinkedMap)
12173 {
12174 for (const auto &fd : *fn)
12175 {
12176 if (fd->isLinkableInProject()) fd->writeTagFile(tagFile);
12177 }
12178 }
12179 // for each class
12180 for (const auto &cd : *Doxygen::classLinkedMap)
12181 {
12182 ClassDefMutable *cdm = toClassDefMutable(cd.get());
12183 if (cdm && cdm->isLinkableInProject())
12184 {
12185 cdm->writeTagFile(tagFile);
12186 }
12187 }
12188 // for each concept
12189 for (const auto &cd : *Doxygen::conceptLinkedMap)
12190 {
12191 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
12192 if (cdm && cdm->isLinkableInProject())
12193 {
12194 cdm->writeTagFile(tagFile);
12195 }
12196 }
12197 // for each namespace
12198 for (const auto &nd : *Doxygen::namespaceLinkedMap)
12199 {
12201 if (ndm && nd->isLinkableInProject())
12202 {
12203 ndm->writeTagFile(tagFile);
12204 }
12205 }
12206 // for each group
12207 for (const auto &gd : *Doxygen::groupLinkedMap)
12208 {
12209 if (gd->isLinkableInProject()) gd->writeTagFile(tagFile);
12210 }
12211 // for each module
12212 for (const auto &mod : ModuleManager::instance().modules())
12213 {
12214 if (mod->isLinkableInProject()) mod->writeTagFile(tagFile);
12215 }
12216 // for each page
12217 for (const auto &pd : *Doxygen::pageLinkedMap)
12218 {
12219 if (pd->isLinkableInProject()) pd->writeTagFile(tagFile);
12220 }
12221 // for requirements
12223 // for each directory
12224 for (const auto &dd : *Doxygen::dirLinkedMap)
12225 {
12226 if (dd->isLinkableInProject()) dd->writeTagFile(tagFile);
12227 }
12228 if (Doxygen::mainPage) Doxygen::mainPage->writeTagFile(tagFile);
12229
12230 tagFile << "</tagfile>\n";
12231}
12232
12233static void exitDoxygen() noexcept
12234{
12235 if (!g_successfulRun) // premature exit
12236 {
12237 Dir thisDir;
12238 msg("Exiting...\n");
12239 if (!Doxygen::filterDBFileName.empty())
12240 {
12241 thisDir.remove(Doxygen::filterDBFileName.str());
12242 }
12243 }
12244}
12245
12246static DString createOutputDirectory(const DString &baseDirName,
12247 const DString &formatDirName,
12248 const char *defaultDirName)
12249{
12250 DString result = formatDirName;
12251 if (result.empty())
12252 {
12253 result = baseDirName + defaultDirName;
12254 }
12255 else if (formatDirName[0]!='/' && (formatDirName.length()==1 || formatDirName[1]!=':'))
12256 {
12257 result.prepend(baseDirName+"/");
12258 }
12259 Dir formatDir(result.str());
12260 if (!formatDir.exists() && !formatDir.mkdir(result.str()))
12261 {
12262 term("Could not create output directory {}\n", result);
12263 }
12264 return result;
12265}
12266
12268{
12269 StringUnorderedSet killSet;
12270
12271 StringVector exclPatterns = Config_getList(EXCLUDE_PATTERNS);
12272 bool alwaysRecursive = Config_getBool(RECURSIVE);
12273 StringUnorderedSet excludeNameSet;
12274
12275 // gather names of all files in the include path
12276 g_s.begin("Searching for include files...\n");
12277 killSet.clear();
12278 StringVector includePathList = Config_getList(INCLUDE_PATH);
12279 for (const auto &s : includePathList)
12280 {
12281 size_t plSize = Config_getList(INCLUDE_FILE_PATTERNS).size();
12282 StringVector pl = plSize==0 ? Config_getList(FILE_PATTERNS) :
12283 Config_getList(INCLUDE_FILE_PATTERNS);
12284 readFileOrDirectory(s, // s
12286 nullptr, // exclSet
12287 &pl, // patList
12288 &exclPatterns, // exclPatList
12289 nullptr, // resultList
12290 nullptr, // resultSet
12291 false, // INCLUDE_PATH isn't recursive
12292 true, // errorIfNotExist
12293 &killSet); // killSet
12294 }
12295 g_s.end();
12296
12297 g_s.begin("Searching for example files...\n");
12298 killSet.clear();
12299 StringVector examplePathList = Config_getList(EXAMPLE_PATH);
12300 for (const auto &s : examplePathList)
12301 {
12302 StringVector patterns = Config_getList(EXAMPLE_PATTERNS);
12303 readFileOrDirectory(s, // s
12305 nullptr, // exclSet
12306 &patterns, // patList
12307 nullptr, // exclPatList
12308 nullptr, // resultList
12309 nullptr, // resultSet
12310 (alwaysRecursive || Config_getBool(EXAMPLE_RECURSIVE)), // recursive
12311 true, // errorIfNotExist
12312 &killSet); // killSet
12313 }
12314 g_s.end();
12315
12316 g_s.begin("Searching for images...\n");
12317 killSet.clear();
12318 StringVector imagePathList=Config_getList(IMAGE_PATH);
12319 for (const auto &s : imagePathList)
12320 {
12321 readFileOrDirectory(s, // s
12323 nullptr, // exclSet
12324 nullptr, // patList
12325 nullptr, // exclPatList
12326 nullptr, // resultList
12327 nullptr, // resultSet
12328 alwaysRecursive, // recursive
12329 true, // errorIfNotExist
12330 &killSet); // killSet
12331 }
12332 g_s.end();
12333
12334 g_s.begin("Searching for dot files...\n");
12335 killSet.clear();
12336 StringVector dotFileList=Config_getList(DOTFILE_DIRS);
12337 for (const auto &s : dotFileList)
12338 {
12339 readFileOrDirectory(s, // s
12341 nullptr, // exclSet
12342 nullptr, // patList
12343 nullptr, // exclPatList
12344 nullptr, // resultList
12345 nullptr, // resultSet
12346 alwaysRecursive, // recursive
12347 true, // errorIfNotExist
12348 &killSet); // killSet
12349 }
12350 g_s.end();
12351
12352 g_s.begin("Searching for msc files...\n");
12353 killSet.clear();
12354 StringVector mscFileList=Config_getList(MSCFILE_DIRS);
12355 for (const auto &s : mscFileList)
12356 {
12357 readFileOrDirectory(s, // s
12359 nullptr, // exclSet
12360 nullptr, // patList
12361 nullptr, // exclPatList
12362 nullptr, // resultList
12363 nullptr, // resultSet
12364 alwaysRecursive, // recursive
12365 true, // errorIfNotExist
12366 &killSet); // killSet
12367 }
12368 g_s.end();
12369
12370 g_s.begin("Searching for dia files...\n");
12371 killSet.clear();
12372 StringVector diaFileList=Config_getList(DIAFILE_DIRS);
12373 for (const auto &s : diaFileList)
12374 {
12375 readFileOrDirectory(s, // s
12377 nullptr, // exclSet
12378 nullptr, // patList
12379 nullptr, // exclPatList
12380 nullptr, // resultList
12381 nullptr, // resultSet
12382 alwaysRecursive, // recursive
12383 true, // errorIfNotExist
12384 &killSet); // killSet
12385 }
12386 g_s.end();
12387
12388 g_s.begin("Searching for plantuml files...\n");
12389 killSet.clear();
12390 StringVector plantUmlFileList=Config_getList(PLANTUMLFILE_DIRS);
12391 for (const auto &s : plantUmlFileList)
12392 {
12393 readFileOrDirectory(s, // s
12395 nullptr, // exclSet
12396 nullptr, // patList
12397 nullptr, // exclPatList
12398 nullptr, // resultList
12399 nullptr, // resultSet
12400 alwaysRecursive, // recursive
12401 true, // errorIfNotExist
12402 &killSet); // killSet
12403 }
12404 g_s.end();
12405
12406 g_s.begin("Searching for mermaid files...\n");
12407 killSet.clear();
12408 StringVector mermaidFileList=Config_getList(MERMAIDFILE_DIRS);
12409 for (const auto &s : mermaidFileList)
12410 {
12411 readFileOrDirectory(s, // s
12413 nullptr, // exclSet
12414 nullptr, // patList
12415 nullptr, // exclPatList
12416 nullptr, // resultList
12417 nullptr, // resultSet
12418 alwaysRecursive, // recursive
12419 true, // errorIfNotExist
12420 &killSet); // killSet
12421 }
12422 g_s.end();
12423
12424 g_s.begin("Searching for files to exclude\n");
12425 StringVector excludeList = Config_getList(EXCLUDE);
12426 for (const auto &s : excludeList)
12427 {
12428 StringVector filePatterns = Config_getList(FILE_PATTERNS);
12429 readFileOrDirectory(s, // s
12430 nullptr, // fnDict
12431 nullptr, // exclSet
12432 &filePatterns, // patList
12433 nullptr, // exclPatList
12434 nullptr, // resultList
12435 &excludeNameSet, // resultSet
12436 alwaysRecursive, // recursive
12437 false); // errorIfNotExist
12438 }
12439 g_s.end();
12440
12441 /**************************************************************************
12442 * Determine Input Files *
12443 **************************************************************************/
12444
12445 g_s.begin("Searching INPUT for files to process...\n");
12446 killSet.clear();
12447 Doxygen::inputPaths.clear();
12448 StringVector inputList=Config_getList(INPUT);
12449 for (const auto &s : inputList)
12450 {
12451 DString path = s;
12452 size_t l = path.length();
12453 if (l>0)
12454 {
12455 // strip trailing slashes
12456 if (path.at(l-1)=='\\' || path.at(l-1)=='/') path=path.left(l-1);
12457
12458 StringVector filePatterns = Config_getList(FILE_PATTERNS);
12460 path, // s
12462 &excludeNameSet, // exclSet
12463 &filePatterns, // patList
12464 &exclPatterns, // exclPatList
12465 &g_inputFiles, // resultList
12466 nullptr, // resultSet
12467 alwaysRecursive, // recursive
12468 true, // errorIfNotExist
12469 &killSet, // killSet
12470 &Doxygen::inputPaths); // paths
12471 }
12472 }
12473
12474 // Sort the FileDef objects by full path to get a predictable ordering over multiple runs
12475 for (auto &fileName : *Doxygen::inputNameLinkedMap)
12476 {
12477 if (fileName->size()>1)
12478 {
12479 std::stable_sort(fileName->begin(),fileName->end(),[](const auto &f1,const auto &f2)
12480 {
12481 return dstricmp_sort(f1->absFilePath(),f2->absFilePath())<0;
12482 });
12483 }
12484 }
12485 std::stable_sort(Doxygen::inputNameLinkedMap->begin(),
12487 [](const auto &f1,const auto &f2)
12488 {
12489 return dstricmp_sort(f1->front()->absFilePath(),f2->front()->absFilePath())<0;
12490 });
12491 if (Doxygen::inputNameLinkedMap->empty())
12492 {
12493 warn_uncond("No files to be processed, please check your settings, in particular INPUT, FILE_PATTERNS, and RECURSIVE\n");
12494 }
12495 g_s.end();
12496}
12497
12498
12500{
12501 if (Config_getBool(MARKDOWN_SUPPORT))
12502 {
12503 DString mdfileAsMainPage = Config_getString(USE_MDFILE_AS_MAINPAGE);
12504 if (mdfileAsMainPage.empty()) return;
12505 FileInfo fi(mdfileAsMainPage.data());
12506 if (!fi.exists())
12507 {
12508 warn_uncond("Specified markdown mainpage '{}' does not exist\n",mdfileAsMainPage);
12509 return;
12510 }
12511 bool ambig = false;
12512 if (Doxygen::inputNameLinkedMap->findFileDef(fi.absFilePath(),ambig)==nullptr)
12513 {
12514 warn_uncond("Specified markdown mainpage '{}' has not been defined as input file\n",mdfileAsMainPage);
12515 return;
12516 }
12517 }
12518}
12519
12521{
12522 AUTO_TRACE();
12523 std::atexit(exitDoxygen);
12524
12525 Portable::correctPath(Config_getList(EXTERNAL_TOOL_PATH));
12526
12527#if USE_LIBCLANG
12528 Doxygen::clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING);
12529#endif
12530
12531 // we would like to show the versionString earlier, but we first have to handle the configuration file
12532 // to know the value of the QUIET setting.
12533 DString versionString = getFullVersion();
12534 msg("Doxygen version used: {}\n",versionString);
12535
12537
12538 /**************************************************************************
12539 * Make sure the output directory exists
12540 **************************************************************************/
12541 DString outputDirectory = Config_getString(OUTPUT_DIRECTORY);
12542 if (!g_singleComment)
12543 {
12544 if (outputDirectory.empty())
12545 {
12546 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,Dir::currentDirPath());
12547 }
12548 else
12549 {
12550 Dir dir(outputDirectory.str());
12551 if (!dir.exists())
12552 {
12554 if (!dir.mkdir(outputDirectory.str()))
12555 {
12556 term("tag OUTPUT_DIRECTORY: Output directory '{}' does not "
12557 "exist and cannot be created\n",outputDirectory);
12558 }
12559 else
12560 {
12561 msg("Notice: Output directory '{}' does not exist. "
12562 "I have created it for you.\n", outputDirectory);
12563 }
12564 dir.setPath(outputDirectory.str());
12565 }
12566 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,dir.absPath());
12567 }
12568 }
12569 AUTO_TRACE_ADD("outputDirectory={}",outputDirectory);
12570
12571 /**************************************************************************
12572 * Initialize global lists and dictionaries
12573 **************************************************************************/
12574
12575#ifdef HAS_SIGNALS
12576 signal(SIGINT, stopDoxygen);
12577#endif
12578
12579 uint32_t pid = Portable::pid();
12580 Doxygen::filterDBFileName.sprintf("doxygen_filterdb_%d.tmp",pid);
12581 Doxygen::filterDBFileName.prepend(outputDirectory+"/");
12582
12583 /**************************************************************************
12584 * Check/create output directories *
12585 **************************************************************************/
12586
12587 bool generateHtml = Config_getBool(GENERATE_HTML);
12588 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
12589 bool generateXml = Config_getBool(GENERATE_XML);
12590 bool generateLatex = Config_getBool(GENERATE_LATEX);
12591 bool generateRtf = Config_getBool(GENERATE_RTF);
12592 bool generateMan = Config_getBool(GENERATE_MAN);
12593 bool generateSql = Config_getBool(GENERATE_SQLITE3);
12594 DString htmlOutput;
12595 DString docbookOutput;
12596 DString xmlOutput;
12597 DString latexOutput;
12598 DString rtfOutput;
12599 DString manOutput;
12600 DString sqlOutput;
12601
12602 if (!g_singleComment)
12603 {
12604 if (generateHtml)
12605 {
12606 htmlOutput = createOutputDirectory(outputDirectory,Config_getString(HTML_OUTPUT),"/html");
12607 Config_updateString(HTML_OUTPUT,htmlOutput);
12608
12609 DString sitemapUrl = Config_getString(SITEMAP_URL);
12610 bool generateSitemap = !sitemapUrl.empty();
12611 if (generateSitemap && !sitemapUrl.endsWith("/"))
12612 {
12613 Config_updateString(SITEMAP_URL,sitemapUrl+"/");
12614 }
12615
12616 // add HTML indexers that are enabled
12617 bool generateHtmlHelp = Config_getBool(GENERATE_HTMLHELP);
12618 bool generateEclipseHelp = Config_getBool(GENERATE_ECLIPSEHELP);
12619 bool generateQhp = Config_getBool(GENERATE_QHP);
12620 bool generateTreeView = Config_getBool(GENERATE_TREEVIEW);
12621 bool generateDocSet = Config_getBool(GENERATE_DOCSET);
12622 if (generateEclipseHelp) Doxygen::indexList->addIndex<EclipseHelp>();
12623 if (generateHtmlHelp) Doxygen::indexList->addIndex<HtmlHelp>();
12624 if (generateQhp) Doxygen::indexList->addIndex<Qhp>();
12625 if (generateSitemap) Doxygen::indexList->addIndex<Sitemap>();
12626 if (generateTreeView) Doxygen::indexList->addIndex<FTVHelp>(true);
12627 if (generateDocSet) Doxygen::indexList->addIndex<DocSets>();
12630 }
12631
12632 if (generateDocbook)
12633 {
12634 docbookOutput = createOutputDirectory(outputDirectory,Config_getString(DOCBOOK_OUTPUT),"/docbook");
12635 Config_updateString(DOCBOOK_OUTPUT,docbookOutput);
12636 }
12637
12638 if (generateXml)
12639 {
12640 xmlOutput = createOutputDirectory(outputDirectory,Config_getString(XML_OUTPUT),"/xml");
12641 Config_updateString(XML_OUTPUT,xmlOutput);
12642 }
12643
12644 if (generateLatex)
12645 {
12646 latexOutput = createOutputDirectory(outputDirectory,Config_getString(LATEX_OUTPUT), "/latex");
12647 Config_updateString(LATEX_OUTPUT,latexOutput);
12648 }
12649
12650 if (generateRtf)
12651 {
12652 rtfOutput = createOutputDirectory(outputDirectory,Config_getString(RTF_OUTPUT),"/rtf");
12653 Config_updateString(RTF_OUTPUT,rtfOutput);
12654 }
12655
12656 if (generateMan)
12657 {
12658 manOutput = createOutputDirectory(outputDirectory,Config_getString(MAN_OUTPUT),"/man");
12659 Config_updateString(MAN_OUTPUT,manOutput);
12660 }
12661
12662 if (generateSql)
12663 {
12664 sqlOutput = createOutputDirectory(outputDirectory,Config_getString(SQLITE3_OUTPUT),"/sqlite3");
12665 Config_updateString(SQLITE3_OUTPUT,sqlOutput);
12666 }
12667 }
12668
12669 if (Config_getBool(HAVE_DOT))
12670 {
12671 DString curFontPath = Config_getString(DOT_FONTPATH);
12672 if (curFontPath.empty())
12673 {
12674 Portable::getenv("DOTFONTPATH");
12675 DString newFontPath = ".";
12676 if (!curFontPath.empty())
12677 {
12678 newFontPath+=Portable::pathListSeparator();
12679 newFontPath+=curFontPath;
12680 }
12681 Portable::setenv("DOTFONTPATH",qPrint(newFontPath));
12682 }
12683 else
12684 {
12685 Portable::setenv("DOTFONTPATH",qPrint(curFontPath));
12686 }
12687 }
12688
12689 /**************************************************************************
12690 * Handle layout file *
12691 **************************************************************************/
12692
12694 DString layoutFileName = Config_getString(LAYOUT_FILE);
12695 bool defaultLayoutUsed = false;
12696 if (layoutFileName.empty())
12697 {
12698 layoutFileName = Config_updateString(LAYOUT_FILE,"DoxygenLayout.xml");
12699 defaultLayoutUsed = true;
12700 }
12701 AUTO_TRACE_ADD("defaultLayoutUsed={}, layoutFileName={}",defaultLayoutUsed,layoutFileName);
12702
12703 FileInfo fi(layoutFileName.str());
12704 if (fi.exists())
12705 {
12706 msg("Parsing layout file {}...\n",layoutFileName);
12707 LayoutDocManager::instance().parse(layoutFileName);
12708 }
12709 else if (!defaultLayoutUsed)
12710 {
12711 warn_uncond("failed to open layout file '{}' for reading! Using default settings.\n",layoutFileName);
12712 }
12713 printLayout();
12714
12715 /**************************************************************************
12716 * Read and preprocess input *
12717 **************************************************************************/
12718
12719 // prevent search in the output directories
12720 StringVector exclPatterns = Config_getList(EXCLUDE_PATTERNS);
12721 if (generateHtml) exclPatterns.push_back(htmlOutput.str());
12722 if (generateDocbook) exclPatterns.push_back(docbookOutput.str());
12723 if (generateXml) exclPatterns.push_back(xmlOutput.str());
12724 if (generateLatex) exclPatterns.push_back(latexOutput.str());
12725 if (generateRtf) exclPatterns.push_back(rtfOutput.str());
12726 if (generateMan) exclPatterns.push_back(manOutput.str());
12727 Config_updateList(EXCLUDE_PATTERNS,exclPatterns);
12728
12729 if (!g_singleComment)
12730 {
12732
12734 }
12735
12736 // Notice: the order of the function calls below is very important!
12737
12738 if (generateHtml && !Config_getBool(USE_MATHJAX))
12739 {
12741 }
12742 if (generateRtf)
12743 {
12745 }
12746 if (generateDocbook)
12747 {
12749 }
12750
12752
12753 /**************************************************************************
12754 * Handle Tag Files *
12755 **************************************************************************/
12756
12757 std::shared_ptr<Entry> root = std::make_shared<Entry>();
12758
12759 if (!g_singleComment)
12760 {
12761 msg("Reading and parsing tag files\n");
12762 StringVector tagFileList = Config_getList(TAGFILES);
12763 for (const auto &s : tagFileList)
12764 {
12765 readTagFile(root,s.c_str());
12766 }
12767 }
12768
12769 /**************************************************************************
12770 * Parse source files *
12771 **************************************************************************/
12772
12773 addSTLSupport(root);
12774
12775 g_s.begin("Parsing files\n");
12776 if (g_singleComment)
12777 {
12778 //printf("Parsing comment %s\n",qPrint(g_commentFileName));
12779 if (g_commentFileName=="-")
12780 {
12781 std::string text = fileToString(g_commentFileName).str();
12782 addTerminalCharIfMissing(text,'\n');
12783 generateHtmlForComment("stdin.md",text);
12784 }
12785 else if (FileInfo(g_commentFileName.str()).isFile())
12786 {
12787 std::string text;
12789 addTerminalCharIfMissing(text,'\n');
12791 }
12792 else
12793 {
12794 }
12796 exit(0);
12797 }
12798 else
12799 {
12800 if (Config_getInt(NUM_PROC_THREADS)==1)
12801 {
12803 }
12804 else
12805 {
12807 }
12808 }
12809 g_s.end();
12810
12811 /**************************************************************************
12812 * Gather information *
12813 **************************************************************************/
12814
12815 g_s.begin("Building macro definition list...\n");
12817 g_s.end();
12818
12819 g_s.begin("Building group list...\n");
12820 buildGroupList(root.get());
12821 organizeSubGroups(root.get());
12822 g_s.end();
12823
12824 g_s.begin("Building directory list...\n");
12826 findDirDocumentation(root.get());
12827 g_s.end();
12828
12829 g_s.begin("Building namespace list...\n");
12830 buildNamespaceList(root.get());
12831 findUsingDirectives(root.get());
12832 g_s.end();
12833
12834 g_s.begin("Building file list...\n");
12835 buildFileList(root.get());
12836 g_s.end();
12837
12838 g_s.begin("Building class list...\n");
12839 buildClassList(root.get());
12840 g_s.end();
12841
12842 g_s.begin("Building concept list...\n");
12843 buildConceptList(root.get());
12844 g_s.end();
12845
12846 // build list of using declarations here (global list)
12847 buildListOfUsingDecls(root.get());
12848 g_s.end();
12849
12850 g_s.begin("Computing nesting relations for classes...\n");
12852 g_s.end();
12853 // 1.8.2-20121111: no longer add nested classes to the group as well
12854 //distributeClassGroupRelations();
12855
12856 // calling buildClassList may result in cached relations that
12857 // become invalid after resolveClassNestingRelations(), that's why
12858 // we need to clear the cache here
12860 // we don't need the list of using declaration anymore
12861 g_usingDeclarations.clear();
12862
12863 g_s.begin("Associating documentation with classes...\n");
12864 buildClassDocList(root.get());
12865 g_s.end();
12866
12867 g_s.begin("Associating documentation with concepts...\n");
12868 buildConceptDocList(root.get());
12870 g_s.end();
12871
12872 g_s.begin("Associating documentation with modules...\n");
12873 findModuleDocumentation(root.get());
12874 g_s.end();
12875
12876 g_s.begin("Building example list...\n");
12877 buildExampleList(root.get());
12878 g_s.end();
12879
12880 g_s.begin("Searching for enumerations...\n");
12881 findEnums(root.get());
12882 g_s.end();
12883
12884 // Since buildVarList calls isVarWithConstructor
12885 // and this calls getResolvedClass we need to process
12886 // typedefs first so the relations between classes via typedefs
12887 // are properly resolved. See bug 536385 for an example.
12888 g_s.begin("Searching for documented typedefs...\n");
12889 buildTypedefList(root.get());
12890 g_s.end();
12891
12892 if (Config_getBool(OPTIMIZE_OUTPUT_SLICE))
12893 {
12894 g_s.begin("Searching for documented sequences...\n");
12895 buildSequenceList(root.get());
12896 g_s.end();
12897
12898 g_s.begin("Searching for documented dictionaries...\n");
12899 buildDictionaryList(root.get());
12900 g_s.end();
12901 }
12902
12903 g_s.begin("Searching for members imported via using declarations...\n");
12904 // this should be after buildTypedefList in order to properly import
12905 // used typedefs
12906 findUsingDeclarations(root.get(),true); // do for python packages first
12907 findUsingDeclarations(root.get(),false); // then the rest
12908 g_s.end();
12909
12910 g_s.begin("Searching for included using directives...\n");
12912 g_s.end();
12913
12914 g_s.begin("Searching for documented variables...\n");
12915 buildVarList(root.get());
12916 g_s.end();
12917
12918 g_s.begin("Building interface member list...\n");
12919 buildInterfaceAndServiceList(root.get()); // UNO IDL
12920
12921 g_s.begin("Building member list...\n"); // using class info only !
12922 buildFunctionList(root.get());
12923 g_s.end();
12924
12925 g_s.begin("Searching for friends...\n");
12926 findFriends();
12927 g_s.end();
12928
12929 g_s.begin("Searching for documented defines...\n");
12930 findDefineDocumentation(root.get());
12931 g_s.end();
12932
12933 g_s.begin("Computing class inheritance relations...\n");
12934 findClassEntries(root.get());
12936 g_s.end();
12937
12938 g_s.begin("Computing class usage relations...\n");
12940 g_s.end();
12941
12942 g_s.begin("Flushing cached template relations that have become invalid...\n");
12944 g_s.end();
12945
12946 g_s.begin("Warn for undocumented namespaces...\n");
12948 g_s.end();
12949
12950 g_s.begin("Computing class relations...\n");
12953 if (Config_getBool(OPTIMIZE_OUTPUT_VHDL))
12954 {
12956 }
12958 g_classEntries.clear();
12959 g_s.end();
12960
12961 g_s.begin("Add enum values to enums...\n");
12962 addEnumValuesToEnums(root.get());
12963 findEnumDocumentation(root.get());
12964 g_s.end();
12965
12966 g_s.begin("Searching for member function documentation...\n");
12967 findObjCMethodDefinitions(root.get());
12968 findMemberDocumentation(root.get()); // may introduce new members !
12969 findUsingDeclImports(root.get()); // may introduce new members !
12970 g_usingClassMap.clear();
12974 g_s.end();
12975
12976 // moved to after finding and copying documentation,
12977 // as this introduces new members see bug 722654
12978 g_s.begin("Creating members for template instances...\n");
12980 g_s.end();
12981
12982 g_s.begin("Searching for tag less structs...\n");
12984 g_s.end();
12985
12986 g_s.begin("Building page list...\n");
12987 buildPageList(root.get());
12988 g_s.end();
12989
12990 g_s.begin("Building requirements list...\n");
12991 buildRequirementsList(root.get());
12992 g_s.end();
12993
12994 g_s.begin("Search for main page...\n");
12995 findMainPage(root.get());
12996 findMainPageTagFiles(root.get());
12997 g_s.end();
12998
12999 g_s.begin("Computing page relations...\n");
13000 computePageRelations(root.get());
13002 g_s.end();
13003
13004 g_s.begin("Determining the scope of groups...\n");
13005 findGroupScope(root.get());
13006 g_s.end();
13007
13008 g_s.begin("Computing module relations...\n");
13009 auto &mm = ModuleManager::instance();
13010 mm.resolvePartitions();
13011 mm.resolveImports();
13012 mm.collectExportedSymbols();
13013 g_s.end();
13014
13015 auto memberNameComp = [](const MemberNameLinkedMap::Ptr &n1,const MemberNameLinkedMap::Ptr &n2)
13016 {
13017 return dstricmp_sort(n1->memberName().data()+getPrefixIndex(n1->memberName()),
13018 n2->memberName().data()+getPrefixIndex(n2->memberName())
13019 )<0;
13020 };
13021
13022 auto classComp = [](const ClassLinkedMap::Ptr &c1,const ClassLinkedMap::Ptr &c2)
13023 {
13024 if (Config_getBool(SORT_BY_SCOPE_NAME))
13025 {
13026 return dstricmp_sort(c1->name(), c2->name())<0;
13027 }
13028 else
13029 {
13030 int i = dstricmp_sort(c1->className(), c2->className());
13031 return i==0 ? dstricmp_sort(c1->name(), c2->name())<0 : i<0;
13032 }
13033 };
13034
13035 auto namespaceComp = [](const NamespaceLinkedMap::Ptr &n1,const NamespaceLinkedMap::Ptr &n2)
13036 {
13037 return dstricmp_sort(n1->name(),n2->name())<0;
13038 };
13039
13040 auto conceptComp = [](const ConceptLinkedMap::Ptr &c1,const ConceptLinkedMap::Ptr &c2)
13041 {
13042 return dstricmp_sort(c1->name(),c2->name())<0;
13043 };
13044
13045 g_s.begin("Sorting lists...\n");
13046 std::stable_sort(Doxygen::memberNameLinkedMap->begin(),
13048 memberNameComp);
13049 std::stable_sort(Doxygen::functionNameLinkedMap->begin(),
13051 memberNameComp);
13052 std::stable_sort(Doxygen::hiddenClassLinkedMap->begin(),
13054 classComp);
13055 std::stable_sort(Doxygen::classLinkedMap->begin(),
13057 classComp);
13058 std::stable_sort(Doxygen::conceptLinkedMap->begin(),
13060 conceptComp);
13061 std::stable_sort(Doxygen::namespaceLinkedMap->begin(),
13063 namespaceComp);
13064 g_s.end();
13065
13066 g_s.begin("Determining which enums are documented\n");
13068 g_s.end();
13069
13070 g_s.begin("Computing member relations...\n");
13073 g_s.end();
13074
13075 g_s.begin("Building full member lists recursively...\n");
13077 g_s.end();
13078
13079 g_s.begin("Adding members to member groups.\n");
13081 g_s.end();
13082
13083 if (Config_getBool(DISTRIBUTE_GROUP_DOC))
13084 {
13085 g_s.begin("Distributing member group documentation.\n");
13087 g_s.end();
13088 }
13089
13090 g_s.begin("Computing member references...\n");
13092 g_s.end();
13093
13094 if (Config_getBool(INHERIT_DOCS))
13095 {
13096 g_s.begin("Inheriting documentation...\n");
13098 g_s.end();
13099 }
13100
13101
13102 // compute the shortest possible names of all files
13103 // without losing the uniqueness of the file names.
13104 g_s.begin("Generating disk names...\n");
13106 g_s.end();
13107
13108 g_s.begin("Adding source references...\n");
13110 g_s.end();
13111
13112 g_s.begin("Adding xrefitems...\n");
13115 g_s.end();
13116
13117 g_s.begin("Adding requirements...\n");
13120 g_s.end();
13121
13122 g_s.begin("Sorting member lists...\n");
13124 g_s.end();
13125
13126 g_s.begin("Setting anonymous enum type...\n");
13128 g_s.end();
13129
13130 g_s.begin("Computing dependencies between directories...\n");
13132 g_s.end();
13133
13134 g_s.begin("Generating citations page...\n");
13136 g_s.end();
13137
13138 g_s.begin("Counting data structures...\n");
13140 g_s.end();
13141
13142 g_s.begin("Resolving user defined references...\n");
13144 g_s.end();
13145
13146 g_s.begin("Finding anchors and sections in the documentation...\n");
13148 g_s.end();
13149
13150 g_s.begin("Transferring function references...\n");
13152 g_s.end();
13153
13154 g_s.begin("Combining using relations...\n");
13156 g_s.end();
13157
13159 g_s.begin("Adding members to index pages...\n");
13161 addToIndices();
13162 g_s.end();
13163
13164 g_s.begin("Correcting members for VHDL...\n");
13166 g_s.end();
13167
13168 g_s.begin("Computing tooltip texts...\n");
13170 g_s.end();
13171
13172 if (Config_getBool(SORT_GROUP_NAMES))
13173 {
13174 std::stable_sort(Doxygen::groupLinkedMap->begin(),
13176 [](const auto &g1,const auto &g2)
13177 { return g1->groupTitle() < g2->groupTitle(); });
13178
13179 for (const auto &gd : *Doxygen::groupLinkedMap)
13180 {
13181 gd->sortSubGroups();
13182 }
13183 }
13184
13185 printNavTree(root.get(),0);
13187}
13188
13190{
13191 AUTO_TRACE();
13192 /**************************************************************************
13193 * Initialize output generators *
13194 **************************************************************************/
13195
13196 /// add extra languages for which we can only produce syntax highlighted code
13198
13199 //// dump all symbols
13200 if (g_dumpSymbolMap)
13201 {
13202 dumpSymbolMap();
13203 exit(0);
13204 }
13205
13206 bool generateHtml = Config_getBool(GENERATE_HTML);
13207 bool generateLatex = Config_getBool(GENERATE_LATEX);
13208 bool generateMan = Config_getBool(GENERATE_MAN);
13209 bool generateRtf = Config_getBool(GENERATE_RTF);
13210 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
13211
13212
13214 if (generateHtml)
13215 {
13219 }
13220 if (generateLatex)
13221 {
13224 }
13225 if (generateDocbook)
13226 {
13229 }
13230 if (generateMan)
13231 {
13234 }
13235 if (generateRtf)
13236 {
13239 }
13240 if (Config_getBool(USE_HTAGS))
13241 {
13242 Htags::useHtags = true;
13243 DString htmldir = Config_getString(HTML_OUTPUT);
13244 if (!Htags::execute(htmldir))
13245 err("USE_HTAGS is YES but htags(1) failed. \n");
13246 else if (!Htags::loadFilemap(htmldir))
13247 err("htags(1) ended normally but failed to load the filemap. \n");
13248 }
13249
13250 /**************************************************************************
13251 * Generate documentation *
13252 **************************************************************************/
13253
13254 g_s.begin("Generating style sheet...\n");
13255 //printf("writing style info\n");
13256 g_outputList->writeStyleInfo(0); // write first part
13257 g_s.end();
13258
13259 bool searchEngine = Config_getBool(SEARCHENGINE);
13260 bool serverBasedSearch = Config_getBool(SERVER_BASED_SEARCH);
13261
13262 g_s.begin("Generating search indices...\n");
13263 if (searchEngine && !serverBasedSearch && generateHtml)
13264 {
13266 }
13267
13268 // generate search indices (need to do this before writing other HTML
13269 // pages as these contain a drop down menu with options depending on
13270 // what categories we find in this function.
13271 if (generateHtml && searchEngine)
13272 {
13273 DString searchDirName = Config_getString(HTML_OUTPUT)+"/search";
13274 Dir searchDir(searchDirName.str());
13275 if (!searchDir.exists() && !searchDir.mkdir(searchDirName.str()))
13276 {
13277 term("Could not create search results directory '{}' $PWD='{}'\n",
13278 searchDirName,Dir::currentDirPath());
13279 }
13280 HtmlGenerator::writeSearchData(searchDirName);
13281 if (!serverBasedSearch) // client side search index
13282 {
13284 }
13285 }
13286 g_s.end();
13287
13288 // copy static stuff
13289 if (generateHtml)
13290 {
13292 copyLogo(Config_getString(HTML_OUTPUT),true);
13293 copyIcon(Config_getString(HTML_OUTPUT),true);
13294 copyExtraFiles(Config_getList(HTML_EXTRA_FILES),"HTML_EXTRA_FILES",Config_getString(HTML_OUTPUT),true);
13295 }
13296 if (generateLatex)
13297 {
13299 copyLogo(Config_getString(LATEX_OUTPUT),false);
13300 copyIcon(Config_getString(LATEX_OUTPUT),false);
13301 copyExtraFiles(Config_getList(LATEX_EXTRA_FILES),"LATEX_EXTRA_FILES",Config_getString(LATEX_OUTPUT),false);
13302 }
13303 if (generateDocbook)
13304 {
13305 copyLogo(Config_getString(DOCBOOK_OUTPUT),false);
13306 copyIcon(Config_getString(DOCBOOK_OUTPUT),false);
13307 }
13308 if (generateRtf)
13309 {
13310 copyLogo(Config_getString(RTF_OUTPUT),false);
13311 copyIcon(Config_getString(RTF_OUTPUT),false);
13312 copyExtraFiles(Config_getList(RTF_EXTRA_FILES),"RTF_EXTRA_FILES",Config_getString(RTF_OUTPUT),false);
13313 }
13314
13316 if (fm.hasFormulas() && generateHtml
13317 && !Config_getBool(USE_MATHJAX))
13318 {
13319 g_s.begin("Generating images for formulas in HTML...\n");
13320 fm.generateImages(Config_getString(HTML_OUTPUT), true, Config_getEnum(HTML_FORMULA_FORMAT)==HTML_FORMULA_FORMAT_t::svg ?
13322 g_s.end();
13323 }
13324 if (fm.hasFormulas() && generateRtf)
13325 {
13326 g_s.begin("Generating images for formulas in RTF...\n");
13328 g_s.end();
13329 }
13330
13331 if (fm.hasFormulas() && generateDocbook)
13332 {
13333 g_s.begin("Generating images for formulas in Docbook...\n");
13335 g_s.end();
13336 }
13337
13338 g_s.begin("Generating example documentation...\n");
13340 g_s.end();
13341
13342 g_s.begin("Generating file sources...\n");
13344 g_s.end();
13345
13346 g_s.begin("Counting members...\n");
13347 // needs to be done after generating the sources
13348 // but before generating the compound documentation, see bug #12233
13349 countMembers();
13350 g_s.end();
13351
13352 g_s.begin("Generating file documentation...\n");
13354 g_s.end();
13355
13356 g_s.begin("Generating page documentation...\n");
13358 g_s.end();
13359
13360 g_s.begin("Generating group documentation...\n");
13362 g_s.end();
13363
13364 g_s.begin("Generating class documentation...\n");
13366 g_s.end();
13367
13368 g_s.begin("Generating concept documentation...\n");
13370 g_s.end();
13371
13372 g_s.begin("Generating module documentation...\n");
13374 g_s.end();
13375
13376 g_s.begin("Generating namespace documentation...\n");
13378 g_s.end();
13379
13380 if (Config_getBool(GENERATE_LEGEND))
13381 {
13382 g_s.begin("Generating graph info page...\n");
13384 g_s.end();
13385 }
13386
13387 g_s.begin("Generating directory documentation...\n");
13389 g_s.end();
13390
13391 if (g_outputList->size()>0)
13392 {
13394 }
13395
13396 g_s.begin("finalizing index lists...\n");
13398 g_s.end();
13399
13400 g_s.begin("writing tag file...\n");
13401 writeTagFile();
13402 g_s.end();
13403
13404 if (Config_getBool(GENERATE_XML))
13405 {
13406 g_s.begin("Generating XML output...\n");
13408 generateXML();
13410 g_s.end();
13411 }
13412 if (Config_getBool(GENERATE_SQLITE3))
13413 {
13414 g_s.begin("Generating SQLITE3 output...\n");
13416 g_s.end();
13417 }
13418
13419 if (Config_getBool(GENERATE_AUTOGEN_DEF))
13420 {
13421 g_s.begin("Generating AutoGen DEF output...\n");
13422 generateDEF();
13423 g_s.end();
13424 }
13425 if (Config_getBool(GENERATE_PERLMOD))
13426 {
13427 g_s.begin("Generating Perl module output...\n");
13429 g_s.end();
13430 }
13431 if (generateHtml && searchEngine && serverBasedSearch)
13432 {
13433 g_s.begin("Generating search index\n");
13434 if (Doxygen::searchIndex.kind()==SearchIndexIntf::Internal) // write own search index
13435 {
13437 Doxygen::searchIndex.write(Config_getString(HTML_OUTPUT)+"/search/search.idx");
13438 }
13439 else // write data for external search index
13440 {
13442 DString searchDataFile = Config_getString(SEARCHDATA_FILE);
13443 if (searchDataFile.empty())
13444 {
13445 searchDataFile="searchdata.xml";
13446 }
13447 if (!Portable::isAbsolutePath(searchDataFile.data()))
13448 {
13449 searchDataFile.prepend(Config_getString(OUTPUT_DIRECTORY)+"/");
13450 }
13451 Doxygen::searchIndex.write(searchDataFile);
13452 }
13453 g_s.end();
13454 }
13455
13456 if (generateRtf)
13457 {
13458 g_s.begin("Combining RTF output...\n");
13459 if (!RTFGenerator::preProcessFileInplace(Config_getString(RTF_OUTPUT),"refman.rtf"))
13460 {
13461 err("An error occurred during post-processing the RTF files!\n");
13462 }
13463 g_s.end();
13464 }
13465
13466 if (PlantumlManager::instance().needToRun())
13467 {
13468 g_s.begin("Running plantuml with JAVA...\n");
13470 g_s.end();
13471 }
13472
13473 if (MermaidManager::instance().needToRun())
13474 {
13475 g_s.begin("Running mermaid (mmdc)...\n");
13477 g_s.end();
13478 }
13479
13480 if (Config_getBool(HAVE_DOT) && DotManager::instance()->needToRun())
13481 {
13482 g_s.begin("Running dot...\n");
13484 g_s.end();
13485 }
13486
13487 if (generateHtml &&
13488 Config_getBool(GENERATE_HTMLHELP) &&
13489 !Config_getString(HHC_LOCATION).empty())
13490 {
13491 g_s.begin("Running html help compiler...\n");
13493 g_s.end();
13494 }
13495
13496 if ( generateHtml &&
13497 Config_getBool(GENERATE_QHP) &&
13498 !Config_getString(QHG_LOCATION).empty())
13499 {
13500 g_s.begin("Running qhelpgenerator...\n");
13502 g_s.end();
13503 }
13504
13507
13509
13511 {
13512
13513 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
13514 if (numThreads<1) numThreads=1;
13515 msg("Total elapsed time: {:.6f} seconds\n(of which an average of {:.6f} seconds per thread waiting for external tools to finish)\n",
13516 (static_cast<double>(Debug::elapsedTime())),
13517 Portable::getSysElapsedTime()/static_cast<double>(numThreads)
13518 );
13519 g_s.print();
13520
13522 msg("finished...\n");
13524 }
13525 else
13526 {
13527 msg("finished...\n");
13528 }
13529
13530
13531 /**************************************************************************
13532 * Start cleaning up *
13533 **************************************************************************/
13534
13536
13538 Dir thisDir;
13539 thisDir.remove(Doxygen::filterDBFileName.str());
13541 exitTracing();
13543 delete Doxygen::clangUsrMap;
13544 g_successfulRun=true;
13545
13546 //dumpDocNodeSizes();
13547}
void readAliases()
Definition aliases.cpp:161
constexpr auto prefix
Definition anchor.cpp:44
std::vector< ArgumentList > ArgumentLists
Definition arguments.h:147
This class represents an function or template argument list.
Definition arguments.h:65
RefQualifierType refQualifier() const
Definition arguments.h:116
bool noParameters() const
Definition arguments.h:117
bool pureSpecifier() const
Definition arguments.h:113
iterator end()
Definition arguments.h:94
bool hasParameters() const
Definition arguments.h:76
DString trailingReturnType() const
Definition arguments.h:114
bool isDeleted() const
Definition arguments.h:115
size_t size() const
Definition arguments.h:100
void setPureSpecifier(bool b)
Definition arguments.h:121
bool constSpecifier() const
Definition arguments.h:111
void setTrailingReturnType(const DString &s)
Definition arguments.cpp:36
void push_back(const Argument &a)
Definition arguments.h:102
bool empty() const
Definition arguments.h:99
void setConstSpecifier(bool b)
Definition arguments.h:119
void setRefQualifier(RefQualifierType t)
Definition arguments.h:126
void setIsDeleted(bool b)
Definition arguments.h:125
iterator begin()
Definition arguments.h:93
bool volatileSpecifier() const
Definition arguments.h:112
void setNoParameters(bool b)
Definition arguments.h:127
void setVolatileSpecifier(bool b)
Definition arguments.h:120
static CitationManager & instance()
Definition cite.cpp:87
void clear()
clears the database
Definition cite.cpp:112
void generatePage()
Generate the citations page.
Definition cite.cpp:333
std::unique_ptr< ClangTUParser > createTUParser(const FileDef *fd) const
static ClangParser * instance()
Returns the one and only instance of the class.
Clang parser object for a single translation unit, which consists of a source file and the directly o...
Definition clangparser.h:25
void switchToFile(const FileDef *fd)
Switches to another file within the translation unit started with start().
void parse()
Parse the file given at construction time as a translation unit This file should already be preproces...
A abstract class representing of a compound symbol.
Definition classdef.h:104
virtual DString requiresClause() const =0
virtual const MemberDef * getMemberByName(const DString &) const =0
Returns the member with the given name.
virtual const ArgumentList & templateArguments() const =0
Returns the template arguments of this class.
virtual void writeDocumentation(OutputList &ol) const =0
virtual void writeMemberList(OutputList &ol) const =0
virtual bool isTemplate() const =0
Returns true if this class is a template.
virtual const BaseClassList & baseClasses() const =0
Returns the list of base classes from which this class directly inherits.
virtual int isBaseClass(const ClassDef *bcd, bool followInstances, const DString &templSpec=DString()) const =0
Returns true iff bcd is a direct or indirect base class of this class.
virtual const TemplateInstanceList & getTemplateInstances() const =0
Returns a sorted dictionary with all template instances found for this template class.
virtual ArgumentLists getTemplateParameterLists() const =0
Returns the template parameter lists that form the template declaration of this class.
virtual Protection protection() const =0
Return the protection level (Public,Protected,Private) in which this compound was found.
virtual DString compoundTypeString() const =0
Returns the type of compound as a string.
virtual MemberList * getMemberList(MemberListType lt) const =0
Returns the members in the list identified by lt.
virtual std::unique_ptr< ClassDef > deepCopy(const DString &name) const =0
virtual bool isForwardDeclared() const =0
Returns true if this class represents a forward declaration of a template class.
virtual bool isSubClass(ClassDef *bcd, int level=0) const =0
Returns true iff bcd is a direct or indirect sub class of this class.
virtual void moveTo(Definition *)=0
virtual const TemplateNameMap & getTemplateBaseClassNames() const =0
virtual DString qualifiedNameWithTemplateParameters(const ArgumentLists *actualParams=nullptr, uint32_t *actualParamIndex=nullptr) const =0
virtual bool isEmbeddedInOuterScope() const =0
virtual const MemberNameInfoLinkedMap & memberNameInfoLinkedMap() const =0
Returns a dictionary of all members.
virtual bool isImplicitTemplateInstance() const =0
virtual const ClassDef * templateMaster() const =0
Returns the template master of which this class is an instance.
CompoundType
The various compound types.
Definition classdef.h:109
@ Singleton
Definition classdef.h:117
@ Interface
Definition classdef.h:112
@ Exception
Definition classdef.h:115
virtual CompoundType compoundType() const =0
Returns the type of compound this is, i.e. class/struct/union/...
virtual bool containsOverload(const MemberDef *md) const =0
virtual ClassLinkedRefMap getClasses() const =0
returns the classes nested into this class
virtual FileDef * getFileDef() const =0
Returns the namespace this compound is in, or 0 if it has a global scope.
virtual void writeTagFile(TextStream &) const =0
virtual void writeDocumentationForInnerClasses(OutputList &ol) const =0
virtual void computeAnchors()=0
virtual void addTypeConstraints()=0
virtual void overrideCollaborationGraph(bool e)=0
virtual void addUsedClass(ClassDef *cd, const DString &accessName, Protection prot)=0
virtual void insertExplicitTemplateInstance(ClassDef *instance, const DString &spec)=0
virtual void countMembers()=0
virtual void addMembersToMemberGroup()=0
virtual void setPrimaryConstructorParams(const ArgumentList &list)=0
virtual void setTemplateBaseClassNames(const TemplateNameMap &templateNames)=0
virtual 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:89
void clear()
Definition dstring.h:219
DString & setNum(short n)
Definition dstring.h:557
void resize(size_t newlen)
Definition dstring.h:214
DString()=default
size_t rfind(char c, size_t pos=npos) const
Definition dstring.h:249
DString fill(char c, size_t len)
Fills a string with a predefined character.
Definition dstring.h:283
DString mid(size_t index, size_t len=npos) const
Definition dstring.h:323
DString lower() const
Definition dstring.h:331
bool empty() const
Returns true iff the string is empty (std::string compatible alias for isEmpty()).
Definition dstring.h:153
DString & replace(size_t index, size_t len, const char *s)
Definition dstring.cpp:150
DString substr(size_t pos=0, size_t count=npos) const
Returns a substring of length count starting at pos.
Definition dstring.h:228
static constexpr size_t npos
value used to indicate 'not found' or 'to the end of the string', matching std::string::npos
Definition dstring.h:183
char & at(size_t i)
Returns a reference to the character at index i.
Definition dstring.h:691
bool findAndRemoveWord(const char *word)
removes occurrences of whole word from this string, while keeping internal spaces and reducing multip...
Definition dstring.cpp:656
DString right(size_t len) const
Definition dstring.h:316
size_t size() const
Returns the length of the string, not counting the 0-terminator.
Definition dstring.h:159
DString & prepend(const char *s)
Definition dstring.h:520
int contains(char c, bool cs=true) const
Definition dstring.cpp:81
size_t find(char c, size_t pos=0) const
Definition dstring.h:244
DString & sprintf(const char *format,...)
Definition dstring.cpp:30
@ ExplicitSize
Definition dstring.h:136
DString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
Definition dstring.h:342
DString left(size_t len) const
Definition dstring.h:311
const std::string & str() const
Definition dstring.h:650
bool stripPrefix(const DString &prefix)
Definition dstring.h:295
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition dstring.h:162
bool startsWith(const char *s) const
Definition dstring.h:605
bool endsWith(const char *s) const
Definition dstring.h:622
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition dstring.h:156
@ ExtCmd
Definition debug.h:36
@ Sections
Definition debug.h:48
@ Time
Definition debug.h:35
@ Qhp
Definition debug.h:44
@ Entries
Definition debug.h:47
static void printFlags()
Definition debug.cpp:138
static void clearFlag(const DebugMask mask)
Definition debug.cpp:123
static bool isFlagSet(const DebugMask mask)
Definition debug.cpp:133
static void print(DebugMask mask, int prio, fmt::format_string< Args... > fmt, Args &&... args)
Definition debug.h:77
static double elapsedTime()
Definition debug.cpp:201
static void startTimer()
Definition debug.cpp:196
static bool setFlagStr(const DString &label)
Definition debug.cpp:104
static void setFlag(const DebugMask mask)
Definition debug.cpp:118
The common base class of all entity definitions found in the sources.
Definition definition.h:77
virtual DString briefDescription(bool abbreviate=false) const =0
virtual DString inbodyFile() const =0
virtual int getEndBodyLine() const =0
virtual DString briefFile() const =0
virtual SrcLangExt getLanguage() const =0
Returns the programming language this definition was written in.
virtual int docLine() const =0
virtual DString getDefFileName() const =0
virtual DString documentation() const =0
virtual bool isLinkable() const =0
virtual DString inbodyDocumentation() const =0
virtual int getDefLine() const =0
virtual DefType definitionType() const =0
virtual const DString & name() const =0
virtual const DString & localName() const =0
virtual int inbodyLine() const =0
virtual const FileDef * getBodyDef() const =0
virtual int briefLine() const =0
virtual bool hasDocumentation() const =0
virtual bool isLinkableInProject() const =0
virtual 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:110
virtual void overrideDirectoryGraph(bool e)=0
Class representing a directory in the file system.
Definition dir.h:75
static std::string currentDirPath()
Definition dir.cpp:342
std::string absPath() const
Definition dir.cpp:364
bool mkdir(const std::string &path, bool acceptsAbsPath=true) const
Definition dir.cpp:295
void setPath(const std::string &path)
Definition dir.cpp:229
bool remove(const std::string &path, bool acceptsAbsPath=true) const
Definition dir.cpp:314
DirIterator iterator() const
Definition dir.cpp:239
static std::string cleanDirPath(const std::string &path)
Definition dir.cpp:357
static bool setCurrent(const std::string &path)
Definition dir.cpp:350
bool exists() const
Definition dir.cpp:257
A linked map of directories.
Definition dirdef.h:175
A class that generates docset files.
Definition docsets.h:36
static void init()
bool run()
Definition dot.cpp:120
static DotManager * instance()
Definition dot.cpp:79
static NamespaceLinkedMap * namespaceLinkedMap
Definition doxygen.h:115
static ConceptLinkedMap * conceptLinkedMap
Definition doxygen.h:97
static bool suppressDocWarnings
Definition doxygen.h:130
static FileNameLinkedMap * plantUmlFileNameLinkedMap
Definition doxygen.h:109
static bool parseSourcesNeeded
Definition doxygen.h:123
static StringUnorderedSet inputPaths
Definition doxygen.h:103
static std::unique_ptr< PageDef > mainPage
Definition doxygen.h:100
static bool clangAssistedParsing
Definition doxygen.h:136
static StringUnorderedSet expandAsDefinedSet
Definition doxygen.h:119
static FileNameLinkedMap * inputNameLinkedMap
Definition doxygen.h:104
static DString filterDBFileName
Definition doxygen.h:131
static ParserManager * parserManager
Definition doxygen.h:129
static InputFileEncodingList inputFileEncodingList
Definition doxygen.h:138
static DString verifiedDotPath
Definition doxygen.h:137
static ClassLinkedMap * classLinkedMap
Definition doxygen.h:95
static MemberNameLinkedMap * functionNameLinkedMap
Definition doxygen.h:112
static PageLinkedMap * exampleLinkedMap
Definition doxygen.h:98
static FileNameLinkedMap * dotFileNameLinkedMap
Definition doxygen.h:106
static NamespaceDefMutable * globalScope
Definition doxygen.h:121
static FileNameLinkedMap * imageNameLinkedMap
Definition doxygen.h:105
static FileNameLinkedMap * mscFileNameLinkedMap
Definition doxygen.h:107
static FileNameLinkedMap * mermaidFileNameLinkedMap
Definition doxygen.h:110
static MemberGroupInfoMap memberGroupInfoMap
Definition doxygen.h:118
static IndexList * indexList
Definition doxygen.h:132
static StaticInitMap staticInitMap
Definition doxygen.h:141
static StringMap tagDestinationMap
Definition doxygen.h:116
static std::mutex countFlowKeywordsMutex
Definition doxygen.h:139
static ClassLinkedMap * hiddenClassLinkedMap
Definition doxygen.h:96
static FileNameLinkedMap * diaFileNameLinkedMap
Definition doxygen.h:108
static DString spaces
Definition doxygen.h:133
static PageLinkedMap * pageLinkedMap
Definition doxygen.h:99
static bool generatingXmlOutput
Definition doxygen.h:134
static std::unique_ptr< NamespaceDef > globalNamespaceDef
Definition doxygen.h:120
static DString htmlFileExtension
Definition doxygen.h:122
static DefinesPerFileList macroDefinitions
Definition doxygen.h:135
static DirLinkedMap * dirLinkedMap
Definition doxygen.h:127
static NamespaceAliasInfoMap namespaceAliasMap
Definition doxygen.h:113
static MemberNameLinkedMap * memberNameLinkedMap
Definition doxygen.h:111
static SymbolMap< Definition > * symbolMap
Definition doxygen.h:125
static StringUnorderedSet tagFileSet
Definition doxygen.h:117
static FileNameLinkedMap * includeNameLinkedMap
Definition doxygen.h:101
static FileNameLinkedMap * exampleNameLinkedMap
Definition doxygen.h:102
static SearchIndexIntf searchIndex
Definition doxygen.h:124
static DirRelationLinkedMap dirRelations
Definition doxygen.h:128
static std::mutex addExampleMutex
Definition doxygen.h:140
static ClangUsrMap * clangUsrMap
Definition doxygen.h:126
static GroupLinkedMap * groupLinkedMap
Definition doxygen.h:114
Generator for Eclipse help files.
Definition eclipsehelp.h:44
static EmojiEntityMapper & instance()
Returns the one and only instance of the Emoji entity mapper.
Definition emoji.cpp:1978
void writeEmojiFile(TextStream &t)
Writes the list of supported emojis to the given file.
Definition emoji.cpp:1999
Represents an unstructured piece of information, about an entity found in the sources.
Definition entry.h:117
TextStream initializer
initial value (for variables)
Definition entry.h:199
VhdlSpecifier vhdlSpec
VHDL specifiers.
Definition entry.h:184
bool subGrouping
automatically group class members?
Definition entry.h:189
RequirementRefs rqli
references to requirements
Definition entry.h:229
const std::vector< std::shared_ptr< Entry > > & children() const
Definition entry.h:140
bool proto
prototype ?
Definition entry.h:188
GroupDocType groupDocType
Definition entry.h:233
int docLine
line number at which the documentation was found
Definition entry.h:203
DString includeName
include name (3 arg of \class)
Definition entry.h:201
DString bitfields
member's bit fields
Definition entry.h:194
ArgumentList typeConstr
where clause (C#) for type constraints
Definition entry.h:217
void markAsProcessed() const
Definition entry.h:168
int endBodyLine
line number where the definition ends
Definition entry.h:220
DString write
property write accessor
Definition entry.h:214
bool exported
is the symbol exported from a C++20 module
Definition entry.h:190
const TagInfo * tagInfo() const
Definition entry.h:178
DString includeFile
include file (2 arg of \class, must be unique)
Definition entry.h:200
DString fileName
file this entry was extracted from
Definition entry.h:225
ArgumentLists tArgLists
template argument declarations
Definition entry.h:197
DString docFile
file in which the documentation was found
Definition entry.h:204
LocalToc localToc
Definition entry.h:235
MethodTypes mtype
signal, slot, (dcop) method, or property?
Definition entry.h:182
@ GROUPDOC_NORMAL
defgroup
Definition entry.h:122
DString args
member argument string
Definition entry.h:193
SrcLangExt lang
programming language in which this entry was found
Definition entry.h:230
Entry * parent() const
Definition entry.h:135
DString inside
name of the class in which documents are found
Definition entry.h:215
DString doc
documentation block (partly parsed)
Definition entry.h:202
DString req
C++20 requires clause.
Definition entry.h:237
bool explicitExternal
explicitly defined as external?
Definition entry.h:187
DString brief
brief description (doc block)
Definition entry.h:205
std::vector< const SectionInfo * > anchors
list of anchors defined in this entry
Definition entry.h:224
RelatesType relatesType
how relates is handled
Definition entry.h:212
std::vector< Grouping > groups
list of groups this entry belongs to
Definition entry.h:223
CommandOverrides commandOverrides
store info for commands whose default can be overridden
Definition entry.h:191
int startLine
start line of entry in the source
Definition entry.h:226
size_t startColumn
start column of entry in the source
Definition entry.h:227
ArgumentList argList
member arguments as a list
Definition entry.h:196
DString type
member type
Definition entry.h:174
int inbodyLine
line number at which the body doc was found
Definition entry.h:209
EntryType section
entry type (see Sections);
Definition entry.h:173
int bodyLine
line number of the body in the source
Definition entry.h:218
DString exception
throw specification
Definition entry.h:216
DString relates
related class (doc block)
Definition entry.h:211
int mGrpId
member group id
Definition entry.h:221
std::vector< BaseInfo > extends
list of base classes
Definition entry.h:222
DString inbodyFile
file in which the body doc was found
Definition entry.h:210
Specifier virt
virtualness of the entry
Definition entry.h:192
DString metaData
Slice metadata.
Definition entry.h:236
std::vector< std::string > qualifiers
qualifiers specified with the qualifier command
Definition entry.h:238
DString name
member name
Definition entry.h:175
RefItemVector sli
special lists (test/todo/bug/deprecated/..) this entry is in
Definition entry.h:228
Protection protection
class protection
Definition entry.h:181
bool artificial
Artificially introduced item.
Definition entry.h:232
bool hidden
does this represent an entity that is hidden from the output
Definition entry.h:231
DString inbodyDocs
documentation inside the body of a function
Definition entry.h:208
int briefLine
line number at which the brief desc. was found
Definition entry.h:206
FileDef * fileDef() const
Definition entry.h:170
DString id
libclang id
Definition entry.h:234
int initLines
define/variable initializer lines to show
Definition entry.h:185
bool isStatic
static ?
Definition entry.h:186
TypeSpecifier spec
class/member specifiers
Definition entry.h:183
DString briefFile
file in which the brief desc. was found
Definition entry.h:207
DString read
property read accessor
Definition entry.h:213
Wrapper class for the Entry type.
Definition types.h:856
constexpr bool isCompoundDoc() const noexcept
Definition types.h:866
constexpr bool isFile() const noexcept
Definition types.h:865
constexpr bool isDoc() const noexcept
Definition types.h:867
ENTRY_TYPES constexpr bool isCompound() const noexcept
Definition types.h:863
std::string to_string() const
Definition types.h:868
static EntryType guessSection(const DString &name)
Definition types.cpp:19
constexpr bool isScope() const noexcept
Definition types.h:864
A class that generates a dynamic tree view side panel.
Definition ftvhelp.h:41
A model of a file symbol.
Definition filedef.h:99
virtual void addUsingDeclaration(const Definition *d)=0
virtual void removeMember(MemberDef *md)=0
virtual void insertClass(ClassDef *cd)=0
virtual void setDiskName(const DString &name)=0
virtual void insertConcept(ConceptDef *cd)=0
virtual void overrideIncludeGraph(bool e)=0
virtual void writeSourceHeader(OutputList &ol)=0
virtual bool generateSourceFile() const =0
virtual DString absFilePath() const =0
virtual const DString & docName() const =0
virtual const LinkedRefMap< NamespaceDef > & getUsedNamespaces() const =0
virtual bool isSource() const =0
virtual void parseSource(ClangTUParser *clangParser)=0
virtual void getAllIncludeFilesRecursively(StringVector &incFiles) const =0
virtual void writeSourceFooter(OutputList &ol)=0
virtual void writeSourceBody(OutputList &ol, ClangTUParser *clangParser)=0
virtual void addUsingDirective(NamespaceDef *nd)=0
virtual void overrideIncludedByGraph(bool e)=0
virtual void insertMember(MemberDef *md)=0
virtual void insertNamespace(NamespaceDef *nd)=0
Minimal replacement for QFileInfo.
Definition fileinfo.h:26
std::string readLink() const
Definition fileinfo.cpp:84
bool isSymLink() const
Definition fileinfo.cpp:77
FileInfo(const std::string &name)
Definition fileinfo.h:28
bool exists() const
Definition fileinfo.cpp:30
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:118
bool isReadable() const
Definition fileinfo.cpp:44
bool isDir() const
Definition fileinfo.cpp:70
bool isFile() const
Definition fileinfo.cpp:63
std::string dirPath(bool absPath=true) const
Definition fileinfo.cpp:137
std::string absFilePath() const
Definition fileinfo.cpp:101
Class representing all files with a certain base name.
Definition filename.h:30
Ordered dictionary of FileName objects.
Definition filename.h:69
DString showFileDefMatches(const DString &n) const
Returns a list of file definitions in fnMap that match the file name n.
Definition filename.cpp:129
FileDef * findFileDef(const DString &n, bool &ambig) const
Returns the file definition in fnMap that matches the file name n.
Definition filename.cpp:36
bool hasFormulas() const
Definition formula.cpp:719
void initFromRepository(const DString &dir)
Definition formula.cpp:59
void checkRepositories()
Definition formula.cpp:172
static FormulaManager & instance()
Definition formula.cpp:53
void generateImages(const DString &outputDir, bool toIndex, Format format, HighDPI hd=HighDPI::Off)
Definition formula.cpp:634
A model of a group of symbols.
Definition groupdef.h:52
virtual DString groupTitle() const =0
virtual void overrideGroupGraph(bool e)=0
virtual bool addClass(ClassDef *def)=0
virtual bool containsFile(const FileDef *def) const =0
virtual bool addNamespace(NamespaceDef *def)=0
virtual void setGroupScope(Definition *d)=0
virtual void addFile(FileDef *def)=0
virtual void setGroupTitle(const DString &newtitle)=0
virtual MemberList * getMemberList(MemberListType lt) const =0
virtual bool hasGroupTitle() const =0
Generator for HTML output.
Definition htmlgen.h:96
static void writeSearchData(const DString &dir)
Definition htmlgen.cpp:1462
static void init()
Definition htmlgen.cpp:1282
static void writeSearchPage()
Definition htmlgen.cpp:3279
static void writeHeaderFile(TextStream &t, const DString &cssname)
Definition htmlgen.cpp:1630
static void writeFooterFile(TextStream &t)
Definition htmlgen.cpp:1636
static void writeTabData()
Additional initialization after indices have been created.
Definition htmlgen.cpp:1446
static void writeExternalSearchPage()
Definition htmlgen.cpp:3378
static void writeStyleSheetFile(TextStream &t)
Definition htmlgen.cpp:1624
A class that generated the HTML Help specific files.
Definition htmlhelp.h:36
static const DString hhpFileName
Definition htmlhelp.h:89
static Index & instance()
Definition index.cpp:109
void countDataStructures()
Definition index.cpp:265
A list of index interfaces.
Definition indexlist.h:64
void initialize()
Definition indexlist.h:100
void addIndexItem(const Definition *context, const MemberDef *md, const DString &sectionAnchor=DString(), const DString &title=DString())
Definition indexlist.h:117
void addIndex(As &&... args)
Add an index generator to the list, using a syntax similar to std::make_unique<T>().
Definition indexlist.h:97
void addImageFile(const DString &name)
Definition indexlist.h:123
void finalize()
Definition indexlist.h:103
Generator for LaTeX output.
Definition latexgen.h:94
static void writeFooterFile(TextStream &t)
Definition latexgen.cpp: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:1469
static LayoutDocManager & instance()
Returns a reference to this singleton.
Definition layout.cpp:1436
void clear()
Definition linkedmap.h:212
std::unique_ptr< RefList > Ptr
Definition linkedmap.h:38
size_t size() const
Definition linkedmap.h:210
T * add(const char *k, Args &&... args)
Definition linkedmap.h:90
const T * find(const std::string &key) const
Definition linkedmap.h:47
Container class representing a vector of objects with keys.
Definition linkedmap.h:232
const T * find(const std::string &key) const
Definition linkedmap.h:243
bool empty() const
Definition linkedmap.h:374
Generator for Man page output.
Definition mangen.h:69
static void init()
Definition mangen.cpp:272
A model of a class/file/namespace member symbol.
Definition memberdef.h:48
virtual DString requiresClause() const =0
virtual bool isFriend() const =0
virtual DString argsString() const =0
virtual bool isForeign() const =0
virtual bool isRelated() const =0
virtual DString definition() const =0
virtual const ClassDef * getCachedTypedefVal() const =0
virtual const ClassDef * getClassDef() const =0
virtual DString excpString() const =0
virtual const ArgumentList & templateArguments() const =0
virtual const DString & initializer() const =0
virtual GroupDef * getGroupDef()=0
virtual bool isCSharpProperty() const =0
virtual bool isTypedef() const =0
virtual const MemberVector & enumFieldList() const =0
virtual void moveTo(Definition *)=0
virtual const FileDef * getFileDef() const =0
virtual const ArgumentList & argumentList() const =0
virtual bool isStrongEnumValue() const =0
virtual VhdlSpecifier getVhdlSpecifiers() const =0
virtual bool isFunction() const =0
virtual bool isExternal() const =0
virtual int getMemberGroupId() const =0
virtual DString bitfieldString() const =0
virtual bool isStatic() const =0
virtual const MemberDef * reimplements() const =0
virtual StringVector getQualifiers() const =0
virtual bool isTypedefValCached() const =0
virtual bool isDocsForDefinition() const =0
virtual bool isDefine() const =0
virtual const NamespaceDef * getNamespaceDef() const =0
virtual bool isObjCProperty() const =0
virtual Protection protection() const =0
virtual TypeSpecifier getMemberSpecifiers() const =0
virtual bool isEnumerate() const =0
virtual MemberType memberType() const =0
virtual ClassDef * relatedAlso() const =0
virtual bool isVariable() const =0
virtual bool isStrong() const =0
virtual DString typeString() const =0
virtual Specifier virtualness(int count=0) const =0
virtual int redefineCount() const =0
virtual int initializerLines() const =0
virtual const MemberDef * getEnumScope() const =0
virtual bool isEnumValue() const =0
virtual bool isPrototype() const =0
virtual void setRequiresClause(const DString &req)=0
virtual void setClassDefOfAnonymousType(const ClassDef *cd)=0
virtual void setMemberClass(ClassDef *cd)=0
virtual void setProtection(Protection p)=0
virtual void setDefinition(const DString &d)=0
virtual void setMemberGroupId(int id)=0
virtual void setDocumentedEnumValues(bool value)=0
virtual void setMemberSpecifiers(TypeSpecifier s)=0
virtual 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:125
Wrapper class for the MemberListType type.
Definition types.h:346
Ptr & front()
Definition membername.h:51
size_t size() const
Definition membername.h:48
iterator begin()
Definition membername.h:37
iterator end()
Definition membername.h:38
void push_back(Ptr &&p)
Definition membername.h:54
Ordered dictionary of MemberName objects.
Definition membername.h:63
MemberName::Ptr take(const DString &key, const MemberDef *value)
Definition membername.h:65
const MemberDef * findRev(const DString &name) const
Definition memberlist.h:106
const MemberDef * find(const DString &name) const
Definition memberlist.h:93
void run()
Run mmdc tool for all collected diagrams.
Definition mermaid.cpp:250
static MermaidManager & instance()
Definition mermaid.cpp:33
void sortMemberLists()
static ModuleManager & instance()
void addDocs(const Entry *root)
void addConceptToModule(const Entry *root, ConceptDef *cd)
void addClassToModule(const Entry *root, ClassDef *cd)
void addMemberToModule(const Entry *root, MemberDef *md)
void writeDocumentation(OutputList &ol)
void addMembersToMemberGroup()
void findSectionsInDocumentation()
void distributeMemberGroupDocumentation()
An abstract interface of a namespace symbol.
virtual const LinkedRefMap< NamespaceDef > & getUsedNamespaces() const =0
virtual bool isInline() const =0
virtual void insertUsedFile(FileDef *fd)=0
virtual void findSectionsInDocumentation()=0
virtual void countMembers()=0
virtual void addUsingDirective(NamespaceDef *nd)=0
virtual void setFileName(const DString &fn)=0
virtual void insertMember(MemberDef *md)=0
virtual void setMetaData(const DString &m)=0
virtual void addUsingDeclaration(const Definition *d)=0
virtual void distributeMemberGroupDocumentation()=0
virtual void writeTagFile(TextStream &)=0
virtual void writeDocumentation(OutputList &ol)=0
virtual void setInline(bool isInline)=0
virtual void computeAnchors()=0
virtual void combineUsingRelations(NamespaceDefSet &visitedNamespace)=0
virtual void sortMemberLists()=0
virtual void addMembersToMemberGroup()=0
/dev/null outline parser
void parseInput(const DString &, const char *, const std::shared_ptr< Entry > &, ClangTUParser *) override
Parses a single input file with the goal to build an Entry tree.
void parsePrototype(const DString &) override
Callback function called by the comment block scanner.
bool needsPreprocessing(const DString &) const override
Returns true if the language identified by extension needs the C preprocessor to be run before feed t...
Abstract interface for outline parsers.
Definition parserintf.h:42
virtual bool needsPreprocessing(const DString &extension) const =0
Returns true if the language identified by extension needs the C preprocessor to be run before feed t...
virtual void parseInput(const DString &fileName, const char *fileBuf, const std::shared_ptr< Entry > &root, ClangTUParser *clangParser)=0
Parses a single input file with the goal to build an Entry tree.
Class representing a list of output generators that are written to in parallel.
Definition outputlist.h:315
void disable(OutputType o)
void add()
Definition outputlist.h:352
void enable(OutputType o)
size_t size() const
Definition outputlist.h:361
void docify(const DString &s)
Definition outputlist.h:437
void writeStyleInfo(int part)
Definition outputlist.h:395
void generateDoc(const DString &fileName, int startLine, const Definition *ctx, const MemberDef *md, const DString &docStr, const DocOptions &options)
void cleanup()
Definition outputlist.h:752
void startContents()
Definition outputlist.h:618
A model of a page symbol.
Definition pagedef.h: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:183
std::unique_ptr< CodeParserInterface > getCodeParser(const DString &extension)
Gets the interface to the parser associated with a given extension.
Definition parserintf.h:254
void registerParser(const DString &name, const OutlineParserFactory &outlineParserFactory, const CodeParserFactory &codeParserFactory)
Registers an additional parser.
Definition parserintf.h:216
ParserManager(const OutlineParserFactory &outlineParserFactory, const CodeParserFactory &codeParserFactory)
Create the parser manager.
Definition parserintf.h:202
std::unique_ptr< OutlineParserInterface > getOutlineParser(const DString &extension)
Gets the interface to the parser associated with a given extension.
Definition parserintf.h:245
static PlantumlManager & instance()
Definition plantuml.cpp:230
void run()
Run plant UML tool for all images.
Definition plantuml.cpp:426
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:27
static DString getQchFileName()
Definition qhp.cpp:427
static const DString qhpFileName
Definition qhp.h:47
Generator for RTF output.
Definition rtfgen.h:80
static void init()
Definition rtfgen.cpp:462
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:2462
static void writeStyleSheetFile(TextStream &t)
Definition rtfgen.cpp:395
static void writeExtensionsFile(TextStream &t)
Definition rtfgen.cpp:410
static RefListManager & instance()
Definition reflist.h:121
static RequirementManager & instance()
void writeTagFile(TextStream &tagFile)
void addRequirement(Entry *e)
Abstract proxy interface for non-javascript based search indices.
void addWord(const DString &word, bool hiPriority)
void setCurrentDoc(const Definition *ctx, const DString &anchor, bool isSourceFile)
void write(const DString &file)
class that provide information about a section.
Definition section.h:58
DString fileName() const
Definition section.h:74
int lineNr() const
Definition section.h:73
DString ref() const
Definition section.h:72
SectionInfo * replace(const DString &label, const DString &fileName, int lineNr, const DString &title, SectionType type, int level, const DString &ref=DString())
Definition section.h:157
SectionInfo * add(const SectionInfo &si)
Definition section.h:139
static SectionManager & instance()
returns a reference to the singleton
Definition section.h:179
static constexpr int Page
Definition section.h:31
std::vector< stat > stats
Definition doxygen.cpp:270
void print()
Definition doxygen.cpp:247
void begin(const char *name)
Definition doxygen.cpp:234
void end()
Definition doxygen.cpp:240
std::chrono::steady_clock::time_point startTime
Definition doxygen.cpp:271
static void showCacheUsage()
Show usage of the type lookup cache.
static void clearTypeLookupCache(ClearScope scope)
ClassDefMutable * resolveClassMutable(const Definition *scope, const DString &name, bool mayBeUnlinkable=false, bool mayBeHidden=false)
Wrapper around resolveClass that returns a mutable interface to the class object or a nullptr if the ...
const ClassDef * resolveClass(const Definition *scope, const DString &name, bool maybeUnlinkable=false, bool mayBeHidden=false)
Find the class definition matching name within the scope set.
const Definition * resolveSymbol(const Definition *scope, const DString &name, const DString &args=DString(), bool checkCV=false, bool insideCode=false, bool onlyLinkable=false)
Find the symbool definition matching name within the scope set.
DString getTemplateSpec() const
In case a call to resolveClass() points to a template specialization, the template part is return via...
const MemberDef * getTypedef() const
In case a call to resolveClass() resolves to a type member (e.g. an enum) this method will return it.
Text streaming class that buffers data.
Definition textstream.h:36
std::string str() const
Return the contents of the buffer as a std::string object.
Definition textstream.h: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:799
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:573
ClassDef * toClassDef(Definition *d)
std::unordered_set< const ClassDef * > ClassDefSet
Definition classdef.h:95
std::map< std::string, int > TemplateNameMap
Definition classdef.h:93
ClassDefMutable * getClassMutable(const DString &key)
Definition classdef.h:470
Class representing a regular expression.
Definition regex.h:39
Class to iterate through matches.
Definition regex.h:239
Object representing the matching results.
Definition regex.h:154
First pass comment processing.
void convertCppComments(const std::string &inBuf, std::string &outBuf, const std::string &fn)
Converts the comments in a file.
ConceptDefMutable * toConceptDefMutable(Definition *d)
std::unique_ptr< ConceptDef > createConceptDef(const DString &fileName, int startLine, 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:325
std::unique_ptr< ArgumentList > stringToArgumentList(SrcLangExt lang, const DString &argsString, DString *extraTypeChars=nullptr)
Definition defargs.l:828
void generateDEF()
Definition defgen.cpp:464
std::unordered_map< std::string, DefineList > DefinesPerFileList
Definition define.h:50
Definition * toDefinition(DefinitionMutable *dm)
DefinitionMutable * toDefinitionMutable(Definition *d)
DirIterator begin(DirIterator it) noexcept
Definition dir.cpp:170
DirIterator end(const DirIterator &) noexcept
Definition dir.cpp:175
void buildDirectories()
Definition dirdef.cpp:1095
void computeDirDependencies()
Definition dirdef.cpp:1169
void generateDirDocs(OutputList &ol)
Definition dirdef.cpp:1186
#define AUTO_TRACE_ADD(...)
Definition docnode.cpp:51
#define AUTO_TRACE(...)
Definition docnode.cpp:50
#define AUTO_TRACE_EXIT(...)
Definition docnode.cpp:52
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:5379
void printNavTree(Entry *root, int indent)
static void addClassToContext(const Entry *root)
Definition doxygen.cpp:939
static void makeTemplateInstanceRelation(const Entry *root, ClassDefMutable *cd)
Definition doxygen.cpp:5394
static StringUnorderedSet g_pathsVisited(1009)
static void buildGroupList(const Entry *root)
Definition doxygen.cpp:432
static void insertMemberAlias(Definition *outerScope, const MemberDef *md)
Definition doxygen.cpp:6763
static void findUsingDeclarations(const Entry *root, bool filterPythonPackages)
Definition doxygen.cpp:2171
static void flushCachedTemplateRelations()
Definition doxygen.cpp:9508
static void copyLatexStyleSheet()
static void generateDocsForClassList(const std::vector< ClassDefMutable * > &classList)
Definition doxygen.cpp:9168
static int findFunctionPtr(const std::string &type, SrcLangExt lang, int *pLength=nullptr)
Definition doxygen.cpp:3013
static bool isSpecialization(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists)
Definition doxygen.cpp:6064
static void computeTemplateClassRelations()
Definition doxygen.cpp:5473
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:8223
static void runQHelpGenerator()
static void addConceptToContext(const Entry *root)
Definition doxygen.cpp:1164
static void addRelatedPage(Entry *root)
Definition doxygen.cpp:330
void initDoxygen()
static void addIncludeFile(DefMutable *cd, FileDef *ifd, const Entry *root)
Definition doxygen.cpp:589
static StringVector g_inputFiles
Definition doxygen.cpp:188
void printSectionsTree()
class Statistics g_s
static void generateXRefPages()
Definition doxygen.cpp:5652
static Definition * buildScopeFromQualifiedName(const DString &name_, SrcLangExt lang, const TagInfo *tagInfo)
Definition doxygen.cpp:713
static void findUsingDeclImports(const Entry *root)
Definition doxygen.cpp:2324
static void copyStyleSheet()
FindBaseClassRelation_Mode
Definition doxygen.cpp:287
@ Undocumented
Definition doxygen.cpp:290
@ TemplateInstances
Definition doxygen.cpp:288
@ DocumentedOnly
Definition doxygen.cpp:289
void distributeClassGroupRelations()
Definition doxygen.cpp:1495
static void generateGroupDocs()
static void findDirDocumentation(const Entry *root)
Definition doxygen.cpp:9708
void checkConfiguration()
check and resolve config options
static void processTagLessClasses(const Definition *root, const Container *cd, const TagContainer *tagParent, MemberListType varFilter, MemberListType typeFilter, const DString &prefix, int count)
Look through the members of class cd and its public members.
Definition doxygen.cpp:1688
static bool findClassRelation(const Entry *root, Definition *context, ClassDefMutable *cd, const BaseInfo *bi, const TemplateNameMap &templateNames, FindBaseClassRelation_Mode mode, bool isArtificial)
Definition doxygen.cpp:4980
static void resolveTemplateInstanceInType(const Entry *root, const Definition *scope, const MemberDef *md)
Definition doxygen.cpp:4910
static void organizeSubGroupsFiltered(const Entry *root, bool additional)
Definition doxygen.cpp:472
static void warnUndocumentedNamespaces()
Definition doxygen.cpp:5428
static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments, const std::string &name)
Definition doxygen.cpp:4589
static void buildConceptList(const Entry *root)
Definition doxygen.cpp:1325
static void addMemberSpecialization(const Entry *root, MemberName *mn, ClassDefMutable *cd, const DString &funcType, const DString &funcName, const DString &funcArgs, const DString &funcDecl, const DString &exceptions, TypeSpecifier spec)
Definition doxygen.cpp:6632
static void resolveClassNestingRelations()
Definition doxygen.cpp:1380
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:2761
static void findClassEntries(const Entry *root)
Definition doxygen.cpp:5344
static void vhdlCorrectMemberProperties()
Definition doxygen.cpp:8467
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:6276
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:6178
static void copyLogo(const DString &outputOption, bool toIndex)
static void computeMemberReferences()
Definition doxygen.cpp:5542
static void transferRelatedFunctionDocumentation()
Definition doxygen.cpp:4503
static void addMembersToMemberGroup()
Definition doxygen.cpp:9373
static void findMainPageTagFiles(Entry *root)
Definition doxygen.cpp:9877
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:6222
static void distributeConceptGroups()
Definition doxygen.cpp:1347
static NamespaceDef * findUsedNamespace(const LinkedRefMap< NamespaceDef > &unl, const DString &name)
Definition doxygen.cpp:2001
static void transferFunctionDocumentation()
Definition doxygen.cpp:4422
static void setAnonymousEnumType()
Definition doxygen.cpp:9113
static void sortMemberLists()
Definition doxygen.cpp:9018
static void findMember(const Entry *root, const DString &relates, const DString &type, const DString &args, DString funcDecl, bool overloaded, bool isFunc)
Definition doxygen.cpp:6806
static void createTemplateInstanceMembers()
Definition doxygen.cpp:8595
void transferStaticInstanceInitializers()
Definition doxygen.cpp:4552
static void findObjCMethodDefinitions(const Entry *root)
Definition doxygen.cpp:7612
static void addMemberDocs(const Entry *root, MemberDefMutable *md, const DString &funcDecl, const ArgumentList *al, bool over_load, TypeSpecifier spec)
Definition doxygen.cpp:5666
static void dumpSymbolMap()
static void buildTypedefList(const Entry *root)
Definition doxygen.cpp:3491
static void findGroupScope(const Entry *root)
Definition doxygen.cpp:447
static void generateFileDocs()
Definition doxygen.cpp:8831
static int findEndOfTemplate(const DString &s, size_t startPos)
Definition doxygen.cpp:3216
static void findDefineDocumentation(Entry *root)
Definition doxygen.cpp:9621
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:4652
void parseInput()
static void findMemberDocumentation(const Entry *root)
Definition doxygen.cpp:7582
static void distributeMemberGroupDocumentation()
Definition doxygen.cpp:9411
static void generateNamespaceClassDocs(const ClassLinkedRefMap &classList)
static void addEnumValuesToEnums(const Entry *root)
Definition doxygen.cpp:7815
static void generatePageDocs()
static void resolveUserReferences()
Definition doxygen.cpp:9939
static void buildRequirementsList(Entry *root)
Definition doxygen.cpp:9768
static void compareDoxyfile(Config::CompareMode diffList)
static void addPageToContext(PageDef *pd, Entry *root)
Definition doxygen.cpp:311
static std::shared_ptr< Entry > parseFile(OutlineParserInterface &parser, FileDef *fd, const DString &fn, ClangTUParser *clangParser, bool newTU)
static void buildVarList(const Entry *root)
Definition doxygen.cpp:3628
static void copyIcon(const DString &outputOption, bool toIndex)
static void buildSequenceList(const Entry *root)
Definition doxygen.cpp:3591
static void generateFileSources()
Definition doxygen.cpp:8665
static void copyExtraFiles(StringVector files, const DString &filesOption, const DString &outputOption, bool toIndex)
static void generateClassDocs()
Definition doxygen.cpp:9266
static int findTemplateSpecializationPosition(const DString &name)
Definition doxygen.cpp:4950
static void buildNamespaceList(const Entry *root)
Definition doxygen.cpp:1832
static void findIncludedUsingDirectives()
Definition doxygen.cpp:2574
static void addDefineDoc(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:9594
static void countMembers()
Definition doxygen.cpp:9127
void clearAll()
Definition doxygen.cpp:202
static void devUsage()
static ClassDef * findClassWithinClassContext(Definition *context, ClassDef *cd, const DString &name)
Definition doxygen.cpp:4617
static void organizeSubGroups(const Entry *root)
Definition doxygen.cpp:491
static void applyMemberOverrideOptions(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:2263
void searchInputFiles()
static void addOverloaded(const Entry *root, MemberName *mn, const DString &funcType, const DString &funcName, const DString &funcArgs, const DString &funcDecl, const DString &exceptions, TypeSpecifier spec)
Definition doxygen.cpp:6695
static void findFriends()
Definition doxygen.cpp:4325
static void findEnums(const Entry *root)
Definition doxygen.cpp:7640
static void generateConfigFile(const DString &configFile, bool shortList, bool updateOnly=false)
static DString g_commentFileName
Definition doxygen.cpp:193
static void dumpSymbol(TextStream &t, Definition *d)
static void addClassAndNestedClasses(std::vector< ClassDefMutable * > &list, ClassDefMutable *cd)
Definition doxygen.cpp:9243
static void addEnumDocs(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:8063
static void addListReferences()
Definition doxygen.cpp:5643
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:5856
static bool isClassSection(const Entry *root)
Definition doxygen.cpp:5322
static void buildGroupListFiltered(const Entry *root, bool additional, bool includeExternal)
Definition doxygen.cpp:361
static void runHtmlHelpCompiler()
static void addMembersToIndex()
Definition doxygen.cpp:8257
static bool g_dumpSymbolMap
Definition doxygen.cpp:192
static void version(const bool extended)
static OutputList * g_outputList
Definition doxygen.cpp:189
static DString extractClassName(const Entry *root)
Definition doxygen.cpp:5353
static void findMainPage(Entry *root)
Definition doxygen.cpp:9807
static ClassDef::CompoundType convertToCompoundType(EntryType section, TypeSpecifier specifier)
Definition doxygen.cpp:897
DString stripTemplateSpecifiers(const DString &s)
Definition doxygen.cpp:686
static void findUsingDirectives(const Entry *root)
Definition doxygen.cpp:2014
static void addInterfaceOrServiceToServiceOrSingleton(const Entry *root, ClassDefMutable *cd, DString const &rname)
Definition doxygen.cpp:3660
static bool g_successfulRun
Definition doxygen.cpp:191
static bool tryAddEnumDocsToGroupMember(const Entry *root, const DString &name)
Definition doxygen.cpp:8105
static void addSourceReferences()
Definition doxygen.cpp:8891
static void associateVariableWithAnonymousEnumType(const MemberDef *md, const Container *cd, const MemberDef *enumTypeMember, MemberListType mlFilter)
Definition doxygen.cpp:1529
static void createUsingMemberImportForClass(const Entry *root, ClassDefMutable *cd, const MemberDef *md, const DString &fileName, const DString &memName)
Definition doxygen.cpp:2275
static DString substituteTemplatesInString(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists, const std::string &src)
Definition doxygen.cpp:6094
std::function< std::unique_ptr< T >() > make_parser_factory()
static void buildExampleList(Entry *root)
static void inheritDocumentation()
Definition doxygen.cpp:9313
static void flushUnresolvedRelations()
Definition doxygen.cpp:9550
static bool isSymbolHidden(const Definition *d)
Definition doxygen.cpp:9060
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:4859
static void findDocumentedEnumValues()
Definition doxygen.cpp:8249
static void generateDiskNames()
static void addToIndices()
Definition doxygen.cpp:8299
static void computeClassRelations()
Definition doxygen.cpp:5448
static void buildFunctionList(const Entry *root)
Definition doxygen.cpp:4020
static void checkPageRelations()
Definition doxygen.cpp:9919
static void addGlobalFunction(const Entry *root, const DString &rname, const DString &sc)
Definition doxygen.cpp:3911
static void findModuleDocumentation(const Entry *root)
Definition doxygen.cpp:1315
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:2591
static void readTagFile(const std::shared_ptr< Entry > &root, const DString &tagLine)
static void findEnumDocumentation(const Entry *root)
Definition doxygen.cpp:8138
static void computePageRelations(Entry *root)
Definition doxygen.cpp:9889
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:3775
void initResources()
static bool isVarWithConstructor(const Entry *root)
Definition doxygen.cpp:3073
static StringSet g_usingDeclarations
Definition doxygen.cpp:190
static void buildDictionaryList(const Entry *root)
Definition doxygen.cpp:3609
static bool haveEqualFileNames(const Entry *root, const MemberDef *md)
Definition doxygen.cpp:9583
void readFileOrDirectory(const DString &s, FileNameLinkedMap *fnMap, StringUnorderedSet *exclSet, const StringVector *patList, const StringVector *exclPatList, StringVector *resultList, StringUnorderedSet *resultSet, bool recursive, bool errorIfNotExist, StringUnorderedSet *killSet, StringUnorderedSet *paths)
static void generateNamespaceDocs()
static void buildClassDocList(const Entry *root)
Definition doxygen.cpp:1150
static void buildPageList(Entry *root)
Definition doxygen.cpp:9780
static void writeTagFile()
static void addRequirementReferences()
Definition doxygen.cpp:5635
static void computeVerifiedDotPath()
static bool g_singleComment
Definition doxygen.cpp:194
static void findSectionsInDocumentation()
Definition doxygen.cpp:9449
static void mergeCategories()
Definition doxygen.cpp:8614
static const StringUnorderedSet g_compoundKeywords
Definition doxygen.cpp:199
static bool scopeIsTemplate(const Definition *d)
Definition doxygen.cpp:6080
static void buildFileList(const Entry *root)
Definition doxygen.cpp:503
static void buildClassList(const Entry *root)
Definition doxygen.cpp:1140
static void usage(const DString &name, const DString &versionString)
static void findUsedTemplateInstances()
Definition doxygen.cpp:5412
static void computeTooltipTexts()
Definition doxygen.cpp:9067
static void addVariable(const Entry *root, int isFuncPtr=-1)
Definition doxygen.cpp:3284
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:4808
static void parseFilesSingleThreading(const std::shared_ptr< Entry > &root)
parse the list of input files
static void buildCompleteMemberLists()
Definition doxygen.cpp:8635
static const ClassDef * findClassDefinition(FileDef *fd, NamespaceDef *nd, const DString &scopeName)
Definition doxygen.cpp:5817
static void filterMemberDocumentation(const Entry *root, const DString &relates)
Definition doxygen.cpp:7432
static void generateConceptDocs()
Definition doxygen.cpp:9292
static bool isRecursiveBaseClass(const DString &scope, const DString &name)
Definition doxygen.cpp:4939
static std::unique_ptr< OutlineParserInterface > getParserForFile(const DString &fn)
static void combineUsingRelations()
Definition doxygen.cpp:9348
static const char * getArg(int argc, char **argv, int &optInd)
std::unique_ptr< ArgumentList > getTemplateArgumentsFromName(const DString &name, const ArgumentLists &tArgLists)
Definition doxygen.cpp:867
static void findTagLessClasses(std::set< const Definition * > &candidates, const Container *cd)
Definition doxygen.cpp:1767
static ClassDefMutable * createTagLessInstance(const Definition *root, const ClassDef *templ, const DString &fieldName)
Definition doxygen.cpp:1556
static void checkMarkdownMainfile()
static std::unordered_map< std::string, std::vector< ClassDefMutable * > > g_usingClassMap
Definition doxygen.cpp:2322
static void buildConceptDocList(const Entry *root)
Definition doxygen.cpp:1335
static bool isEntryInGroupOfMember(const Entry *root, const MemberDef *md, bool allowNoGroup=false)
Definition doxygen.cpp:5832
static void applyToAllDefinitions(Func func)
Definition doxygen.cpp:5578
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:4455
static void buildDefineList()
Definition doxygen.cpp:8970
static void buildInterfaceAndServiceList(const Entry *root)
Definition doxygen.cpp:3723
static Definition * findScopeFromQualifiedName(NamespaceDefMutable *startScope, const DString &n, FileDef *fileScope, const TagInfo *tagInfo)
Definition doxygen.cpp:782
static void computeMemberRelations()
Definition doxygen.cpp:8579
static void buildListOfUsingDecls(const Entry *root)
Definition doxygen.cpp:2158
static void computeMemberRelationsForBaseClass(const ClassDef *cd, const BaseClassDef *bcd)
Definition doxygen.cpp:8499
static std::multimap< std::string, const Entry * > g_classEntries
Definition doxygen.cpp:187
std::vector< InputFileEncoding > InputFileEncodingList
Definition doxygen.h:80
std::unordered_map< std::string, BodyInfo > StaticInitMap
Definition doxygen.h:84
std::unordered_map< std::string, NamespaceAliasInfo > NamespaceAliasInfoMap
Definition doxygen.h:86
std::unordered_map< std::string, const Definition * > ClangUsrMap
Definition doxygen.h:82
DString substitute(const DString &s, const DString &src, const DString &dst)
substitute all occurrences of src in s by dst
Definition dstring.cpp:481
int dstricmp(const char *s1, const char *s2)
Definition dstring.cpp:440
uint32_t dstrlen(const char *str)
Returns the length of string str, or 0 if a null pointer is passed.
Definition dstring.h:44
int dstricmp_sort(const char *str1, const char *str2)
Definition dstring.h:72
int dstrcmp(const char *str1, const char *str2)
Definition dstring.h:55
const char * qPrint(const char *s)
Definition dstring.h:788
#define ASSERT(x)
Definition dstring.h:29
FileDef * toFileDef(Definition *d)
Definition filedef.cpp:1976
std::unique_ptr< FileDef > createFileDef(const DString &p, const DString &n, const DString &ref, const DString &dn)
Definition filedef.cpp:270
std::unordered_set< const FileDef * > FileDefSet
Definition filedef.h:44
void addNamespaceToGroups(const Entry *root, NamespaceDef *nd)
void addGroupToGroups(const Entry *root, GroupDef *subGroup)
void addClassToGroups(const Entry *root, ClassDef *cd)
void addDirToGroups(const Entry *root, DirDef *dd)
std::unique_ptr< GroupDef > createGroupDef(const DString &fileName, int line, const DString &name, const DString &title, const DString &refFileName)
Definition groupdef.cpp:179
void addConceptToGroups(const Entry *root, ConceptDef *cd)
void addMemberToGroups(const Entry *root, MemberDef *md)
void writeGraphInfo(OutputList &ol)
Definition index.cpp:4099
void endTitle(OutputList &ol, const DString &fileName, const DString &name)
Definition index.cpp:397
void writeIndexHierarchy(OutputList &ol)
Definition index.cpp:5813
void endFile(OutputList &ol, bool skipNavIndex, bool skipEndContents, const DString &navPath)
Definition index.cpp:430
void startTitle(OutputList &ol, const DString &fileName, const DefinitionMutable *def)
Definition index.cpp:387
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:404
Translator * theTranslator
Definition language.cpp:71
void setTranslator(OUTPUT_LANGUAGE_t langName)
Definition language.cpp:73
#define LATEX_STYLE_EXTENSION
Definition latexgen.h:22
void writeDefaultLayoutFile(const DString &fileName)
Definition layout.cpp:1733
void printLayout()
Definition layout.cpp:1821
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:27
void initWarningFormat()
Definition message.cpp:231
void warn_flush()
Definition message.cpp:224
DString warn_line(const DString &file, int line)
Definition message.cpp:209
void finishWarnExit()
Definition message.cpp:289
#define warn_undoc(file, line, fmt,...)
Definition message.h:102
#define warn_uncond(fmt,...)
Definition message.h:122
#define warn(file, line, fmt,...)
Definition message.h:97
#define msg(fmt,...)
Definition message.h:94
#define err(fmt,...)
Definition message.h:127
#define term(fmt,...)
Definition message.h:137
void postProcess(bool clearHeaderAndFooter, CompareMode compareMode=CompareMode::Full)
CompareMode
Definition config.h:54
void checkAndCorrect(bool quiet, const bool check)
void compareDoxyfile(TextStream &t, CompareMode compareMode)
void writeTemplate(TextStream &t, bool shortList, bool updateOnly=false)
void deinit()
void init()
void updateObsolete()
bool parse(const DString &fileName, bool update=false, CompareMode compareMode=CompareMode::Full)
void correctPath(const StringVector &list)
Correct a possible wrong PATH variable.
Definition portable.cpp:516
FILE * popen(const DString &name, const DString &type)
Definition portable.cpp:479
double getSysElapsedTime()
Definition portable.cpp:97
void setenv(const DString &variable, const DString &value)
Definition portable.cpp:286
uint32_t pid()
Definition portable.cpp:248
int system(const DString &command, const DString &args, bool commandHasConsole=true)
Definition portable.cpp:105
int pclose(FILE *stream)
Definition portable.cpp:488
DString getenv(const DString &variable)
Definition portable.cpp:321
bool isAbsolutePath(const DString &fileName)
Definition portable.cpp:497
DString pathListSeparator()
Definition portable.cpp:383
std::ofstream openOutputStream(const DString &name, bool append=false)
Definition portable.cpp:665
const char * commandExtension()
Definition portable.cpp:461
void setShortDir()
Definition portable.cpp:553
DString trunc(const DString &s, size_t numChars=15)
Definition trace.h:56
std::unique_ptr< NamespaceDef > createNamespaceDef(const DString &defFileName, int defLine, 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:847
std::unique_ptr< PageDef > createPageDef(const DString &f, int l, const DString &n, const DString &d, const DString &t)
Definition pagedef.cpp:86
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:135
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
This class contains the information about the argument of a function or template.
Definition arguments.h:27
DString defval
Definition arguments.h:46
DString array
Definition arguments.h:45
DString name
Definition arguments.h:44
DString type
Definition arguments.h:42
Class that contains information about an inheritance relation.
Definition classdef.h:55
ClassDef * classDef
Class definition that this relation inherits from.
Definition classdef.h:60
This class stores information about an inheritance relation.
Definition entry.h:91
Protection prot
inheritance type
Definition entry.h:96
Specifier virt
virtualness
Definition entry.h:97
DString name
the name of the base class
Definition entry.h:95
Data associated with description found in the body.
Definition definition.h:64
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:38
static bool loadFilemap(const DString &htmldir)
Definition htags.cpp:107
static bool useHtags
Definition htags.h:23
stat(const char *n, double el)
Definition doxygen.cpp:268
const char * name
Definition doxygen.cpp:265
This struct is used to capture the tag file information for an Entry.
Definition entry.h:104
DString tagName
Definition entry.h:105
DString fileName
Definition entry.h:106
void parseTagFile(const std::shared_ptr< Entry > &root, const char *fullName)
void exitTracing()
Definition trace.cpp:52
void initTracing(const DString &logFile, bool timing)
Definition trace.cpp:22
#define TRACE(...)
Definition trace.h:77
MemberType
Definition types.h:569
Protection
Definition types.h:32
SrcLangExt
Definition types.h:207
Relationship
Definition types.h:167
Specifier
Definition types.h:80
bool useCaseSenseNames()
Returns true if the names of the symbols can be case sensitive.
Definition util.cpp:2681
DString substituteTemplateArgumentsInString(const DString &nm, const ArgumentList &formalArgs, const ArgumentList *actualArgs)
Definition util.cpp:3526
bool protectionLevelVisible(Protection prot)
Definition util.cpp:4673
DString stripFromIncludePath(const DString &path)
Definition util.cpp:229
DString mergeScopes(const DString &leftScope, const DString &rightScope)
Definition util.cpp:3755
DString filterTitle(const DString &title)
Definition util.cpp:4456
bool matchTemplateArguments(const ArgumentList &srcAl, const ArgumentList &dstAl)
Definition util.cpp:1835
void addCodeOnlyMappings()
Definition util.cpp:4164
bool rightScopeMatch(const DString &scope, const DString &name)
Definition util.cpp:731
bool checkIfTypedef(const Definition *scope, const FileDef *fileScope, const DString &n)
Definition util.cpp:4273
DString replaceAnonymousScopes(const DString &s, const DString &replacement)
Definition util.cpp:157
void cleanupInlineGraphs()
Definition util.cpp:5385
int computeQualifiedIndex(const DString &name)
Return the index of the last :: in the string name that is still before the first <.
Definition util.cpp:5280
DString removeRedundantWhiteSpace(const DString &s)
Definition util.cpp:428
bool checkExtension(const DString &fName, const DString &ext)
Definition util.cpp:3928
DString convertNameToFile(const DString &name, bool allowDots, bool allowUnderscore)
Definition util.cpp:2865
bool leftScopeMatch(const DString &scope, const DString &name)
Definition util.cpp:742
DString tempArgListToString(const ArgumentList &al, SrcLangExt lang, bool includeDefault)
Definition util.cpp:905
DString getFileNameExtension(const DString &fn)
Definition util.cpp:4212
DString resolveTypeDef(const Definition *context, const DString &qualifiedName, const Definition **typedefContext)
Definition util.cpp:234
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:4376
DString normalizeNonTemplateArgumentsInString(const DString &name, const Definition *context, const ArgumentList &formalArgs)
Definition util.cpp:3467
SrcLangExt getLanguageFromFileName(const DString &fileName, SrcLangExt defLang)
Definition util.cpp:4170
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:1591
void initDefaultExtensionMapping()
Definition util.cpp:4097
void addHtmlExtensionIfMissing(DString &fName)
Definition util.cpp:3933
DString fileToString(const DString &name, bool filter, bool isSourceCode)
Definition util.cpp:1055
void extractNamespaceName(const DString &scopeName, DString &className, DString &namespaceName, bool allowEmptyClass)
Definition util.cpp:3028
DString stripTemplateSpecifiersFromScope(const DString &fullName, bool parentOnly, DString *pLastScopeStripped, DString scopeName, bool allowArtificial)
Definition util.cpp:3688
DString argListToString(const ArgumentList &al, bool useCanonicalType, bool showDefVals)
Definition util.cpp:861
DString projectLogoFile()
Definition util.cpp:2582
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:4621
int getPrefixIndex(const DString &name)
Definition util.cpp:2653
DString getLanguageSpecificSeparator(SrcLangExt lang, bool classScope)
Definition util.cpp:4631
bool updateLanguageMapping(const DString &extension, const DString &language)
Definition util.cpp:4065
DString mangleCSharpGenericName(const DString &name)
Definition util.cpp:5321
void mergeArguments(ArgumentList &srcAl, ArgumentList &dstAl, bool forceNameOverwrite)
Definition util.cpp:1691
int getScopeFragment(const DString &s, int p, int *l)
Definition util.cpp:3800
int extractClassNameFromType(const DString &type, int &pos, DString &name, DString &templSpec, SrcLangExt lang)
Definition util.cpp:3382
bool openOutputFile(const DString &outFile, std::ofstream &f)
Definition util.cpp:4973
DString stripAnonymousNamespaceScope(const DString &s)
Definition util.cpp:169
A bunch of utility functions.
void generateXML()
Definition xmlgen.cpp:2316