Doxygen
Loading...
Searching...
No Matches
doxygen.cpp
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * Copyright (C) 1997-2015 by Dimitri van Heesch.
4 *
5 * Permission to use, copy, modify, and distribute this software and its
6 * documentation under the terms of the GNU General Public License is hereby
7 * granted. No representations are made about the suitability of this software
8 * for any purpose. It is provided "as is" without express or implied warranty.
9 * See the GNU General Public License for more details.
10 *
11 * Documents produced by Doxygen are derivative works derived from the
12 * input used in their production; they are not affected by this license.
13 *
14 */
15
16// own header
17#include "doxygen.h"
18
19// standard includes
20#include <algorithm>
21#include <cerrno>
22#include <chrono>
23#include <cinttypes>
24#include <clocale>
25#include <cstdio>
26#include <cstdlib>
27#include <locale>
28#include <memory>
29#include <sys/stat.h>
30#include <unordered_map>
31
32// other includes
33#include "aliases.h"
34#include "arguments.h"
35#include "cite.h"
36#include "clangparser.h"
37#include "classlist.h"
38#include "cmdmapper.h"
39#include "code.h"
40#include "commentcnv.h"
41#include "conceptdef.h"
42#include "config.h"
43#include "debug.h"
44#include "declinfo.h"
45#include "defargs.h"
46#include "defgen.h"
47#include "dir.h"
48#include "dirdef.h"
49#include "docbookgen.h"
50#include "docparser.h"
51#include "docsets.h"
52#include "dot.h"
53#include "eclipsehelp.h"
54#include "emoji.h"
55#include "entry.h"
56#include "fileinfo.h"
57#include "filename.h"
58#include "fileparser.h"
59#include "formula.h"
60#include "fortrancode.h"
61#include "fortranscanner.h"
62#include "ftvhelp.h"
63#include "groupdef.h"
64#include "htags.h"
65#include "htmlgen.h"
66#include "htmlhelp.h"
67#include "index.h"
68#include "indexlist.h"
69#include "language.h"
70#include "latexgen.h"
71#include "layout.h"
72#include "lexcode.h"
73#include "lexscanner.h"
74#include "mangen.h"
75#include "markdown.h"
76#include "membergroup.h"
77#include "memberlist.h"
78#include "membername.h"
79#include "mermaid.h"
80#include "message.h"
81#include "moduledef.h"
82#include "msc.h"
83#include "namespacedef.h"
84#include "outputlist.h"
85#include "pagedef.h"
86#include "parserintf.h"
87#include "perlmodgen.h"
88#include "plantuml.h"
89#include "portable.h"
90#include "pre.h"
91#include "pycode.h"
92#include "pyscanner.h"
93#include "qhp.h"
94#include "reflist.h"
95#include "regex.h"
96#include "requirement.h"
97#include "rtfgen.h"
98#include "scanner.h"
99#include "searchindex_js.h"
100#include "settings.h"
101#include "singlecomment.h"
102#include "sitemap.h"
103#include "sqlcode.h"
104#include "sqlite3gen.h"
105#include "stlsupport.h"
106#include "stringutil.h"
107#include "symbolresolver.h"
108#include "tagreader.h"
109#include "threadpool.h"
110#include "trace.h"
111#include "util.h"
112#include "version.h"
113#include "vhdlcode.h"
114#include "vhdldocgen.h"
115#include "vhdljjparser.h"
116#include "xmlcode.h"
117#include "xmlgen.h"
118
119#include <sqlite3.h>
120
121#if USE_LIBCLANG
122#if defined(__GNUC__)
123#pragma GCC diagnostic push
124#pragma GCC diagnostic ignored "-Wshadow"
125#endif
126#include <clang/Basic/Version.h>
127#if defined(__GNUC__)
128#pragma GCC diagnostic pop
129#endif
130#endif
131
132// provided by the generated file resources.cpp
133extern void initResources();
134
135#if !defined(_WIN32) || defined(__CYGWIN__)
136#include <signal.h>
137#define HAS_SIGNALS
138#endif
139
140// globally accessible variables
152FileNameLinkedMap *Doxygen::includeNameLinkedMap = nullptr; // include names
158FileNameLinkedMap *Doxygen::plantUmlFileNameLinkedMap = nullptr;// plantuml files
159FileNameLinkedMap *Doxygen::mermaidFileNameLinkedMap = nullptr; // mermaid files
161StringMap Doxygen::tagDestinationMap; // all tag locations
162StringUnorderedSet Doxygen::tagFileSet; // all tag file names
163StringUnorderedSet Doxygen::expandAsDefinedSet; // all macros that should be expanded
164MemberGroupInfoMap Doxygen::memberGroupInfoMap; // dictionary of the member groups heading
165std::unique_ptr<PageDef> Doxygen::mainPage;
166std::unique_ptr<NamespaceDef> Doxygen::globalNamespaceDef;
168bool Doxygen::parseSourcesNeeded = false;
186std::mutex Doxygen::addExampleMutex;
188
189// locally accessible globals
190static std::multimap< std::string, const Entry* > g_classEntries;
192static OutputList *g_outputList = nullptr; // list of output generating objects
193static StringSet g_usingDeclarations; // used classes
194static bool g_successfulRun = false;
195static bool g_dumpSymbolMap = false;
197static bool g_singleComment=false;
198
199
200
201// keywords recognized as compounds
203{ "template class", "template struct", "class", "struct", "union", "interface", "exception" };
204
232
234{
235 public:
237 void begin(const char *name)
238 {
239 msg("{}", name);
240 stats.emplace_back(name,0);
241 startTime = std::chrono::steady_clock::now();
242 }
243 void end()
244 {
245 std::chrono::steady_clock::time_point endTime = std::chrono::steady_clock::now();
246 stats.back().elapsed = static_cast<double>(std::chrono::duration_cast<
247 std::chrono::microseconds>(endTime - startTime).count())/1000000.0;
248 warn_flush();
249 }
250 void print()
251 {
252 bool restore=false;
254 {
256 restore=true;
257 }
258 msg("----------------------\n");
259 for (const auto &s : stats)
260 {
261 msg("Spent {:.6f} seconds in {}",s.elapsed,s.name);
262 }
263 if (restore) Debug::setFlag(Debug::Time);
264 }
265 private:
266 struct stat
267 {
268 const char *name;
269 double elapsed;
270 //stat() : name(nullptr),elapsed(0) {}
271 stat(const char *n, double el) : name(n),elapsed(el) {}
272 };
273 std::vector<stat> stats;
274 std::chrono::steady_clock::time_point startTime;
276
277
278static void addMemberDocs(const Entry *root,MemberDefMutable *md, const DString &funcDecl,
279 const ArgumentList *al,bool over_load,TypeSpecifier spec);
280static void findMember(const Entry *root,
281 const DString &relates,
282 const DString &type,
283 const DString &args,
284 DString funcDecl,
285 bool overloaded,
286 bool isFunc
287 );
288
295
296
297static bool findClassRelation(
298 const Entry *root,
299 Definition *context,
300 ClassDefMutable *cd,
301 const BaseInfo *bi,
302 const TemplateNameMap &templateNames,
303 /*bool insertUndocumented*/
305 bool isArtificial
306 );
307
308//----------------------------------------------------------------------------
309
311 FileDef *fileScope,const TagInfo *tagInfo);
312static void resolveTemplateInstanceInType(const Entry *root,const Definition *scope,const MemberDef *md);
313
314static void addPageToContext(PageDef *pd,Entry *root)
315{
316 if (root->parent()) // add the page to it's scope
317 {
318 DString scope = root->parent()->name;
319 if (root->parent()->section.isPackageDoc())
320 {
321 scope=substitute(scope,".","::");
322 }
323 scope = stripAnonymousNamespaceScope(scope);
324 scope+="::"+pd->name();
326 if (d)
327 {
328 pd->setPageScope(d);
329 }
330 }
331}
332
333static void addRelatedPage(Entry *root)
334{
335 GroupDef *gd=nullptr;
336 for (const Grouping &g : root->groups)
337 {
338 if (!g.groupname.empty() && (gd=Doxygen::groupLinkedMap->find(g.groupname))) break;
339 }
340 //printf("---> addRelatedPage() %s gd=%p\n",qPrint(root->name),gd);
341 DString doc=root->doc+root->inbodyDocs;
342
343 PageDef *pd = addRelatedPage(root->name, // name
344 root->args, // ptitle
345 doc, // doc
346 root->docFile, // fileName
347 root->docLine, // docLine
348 root->startLine, // startLine
349 root->sli, // sli
350 gd, // gd
351 root->tagInfo(), // tagInfo
352 false, // xref
353 root->lang // lang
354 );
355 if (pd)
356 {
357 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
359 pd->setLocalToc(root->localToc);
360 addPageToContext(pd,root);
361 }
362}
363
364static void buildGroupListFiltered(const Entry *root,bool additional, bool includeExternal)
365{
366 if (root->section.isGroupDoc() && !root->name.empty() &&
367 ((!includeExternal && root->tagInfo()==nullptr) ||
368 ( includeExternal && root->tagInfo()!=nullptr))
369 )
370 {
371 AUTO_TRACE("additional={} includeExternal={}",additional,includeExternal);
372 if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
373 (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
374 {
376 AUTO_TRACE_ADD("Processing group '{}':'{}' gd={}", root->type,root->name,(void*)gd);
377
378 if (gd)
379 {
380 if ( !gd->hasGroupTitle() )
381 {
382 gd->setGroupTitle( root->type );
383 }
384 else if ( root->type.length() > 0 && root->name != root->type && gd->groupTitle() != root->type )
385 {
386 warn( root->fileName,root->startLine,
387 "group {}: ignoring title \"{}\" that does not match old title \"{}\"",
388 root->name, root->type, gd->groupTitle() );
389 }
390 gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
391 gd->setDocumentation( root->doc, root->docFile, root->docLine );
392 gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
394 gd->setRefItems(root->sli);
396 gd->setLanguage(root->lang);
398 {
399 root->commandOverrides.apply_groupGraph([&](bool b) { gd->overrideGroupGraph(b); });
400 }
401 }
402 else
403 {
404 if (root->tagInfo())
405 {
406 gd = Doxygen::groupLinkedMap->add(root->name,
407 std::unique_ptr<GroupDef>(
408 createGroupDef(root->fileName,root->startLine,root->name,root->type,root->tagInfo()->fileName)));
409 gd->setReference(root->tagInfo()->tagName);
410 }
411 else
412 {
413 gd = Doxygen::groupLinkedMap->add(root->name,
414 std::unique_ptr<GroupDef>(
415 createGroupDef(root->fileName,root->startLine,root->name,root->type)));
416 }
417 gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
418 // allow empty docs for group
419 gd->setDocumentation(!root->doc.empty() ? root->doc : DString(" "),root->docFile,root->docLine,false);
420 gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
422 gd->setRefItems(root->sli);
424 gd->setLanguage(root->lang);
426 {
427 root->commandOverrides.apply_groupGraph([&](bool b) { gd->overrideGroupGraph(b); });
428 }
429 }
430 }
431 }
432 for (const auto &e : root->children()) buildGroupListFiltered(e.get(),additional,includeExternal);
433}
434
435static void buildGroupList(const Entry *root)
436{
437 // --- first process only local groups
438 // first process the @defgroups blocks
439 buildGroupListFiltered(root,false,false);
440 // then process the @addtogroup, @weakgroup blocks
441 buildGroupListFiltered(root,true,false);
442
443 // --- then also process external groups
444 // first process the @defgroups blocks
445 buildGroupListFiltered(root,false,true);
446 // then process the @addtogroup, @weakgroup blocks
447 buildGroupListFiltered(root,true,true);
448}
449
450static void findGroupScope(const Entry *root)
451{
452 if (root->section.isGroupDoc() && !root->name.empty() &&
453 root->parent() && !root->parent()->name.empty())
454 {
456 if (gd)
457 {
458 DString scope = root->parent()->name;
459 if (root->parent()->section.isPackageDoc())
460 {
461 scope=substitute(scope,".","::");
462 }
463 scope = stripAnonymousNamespaceScope(scope);
464 scope+="::"+gd->name();
466 if (d)
467 {
468 gd->setGroupScope(d);
469 }
470 }
471 }
472 for (const auto &e : root->children()) findGroupScope(e.get());
473}
474
475static void organizeSubGroupsFiltered(const Entry *root,bool additional)
476{
477 if (root->section.isGroupDoc() && !root->name.empty())
478 {
479 AUTO_TRACE("additional={}",additional);
480 if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
481 (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
482 {
484 if (gd)
485 {
486 AUTO_TRACE_ADD("adding {} to group {}",root->name,gd->name());
487 addGroupToGroups(root,gd);
488 }
489 }
490 }
491 for (const auto &e : root->children()) organizeSubGroupsFiltered(e.get(),additional);
492}
493
494static void organizeSubGroups(const Entry *root)
495{
496 //printf("Defining groups\n");
497 // first process the @defgroups blocks
498 organizeSubGroupsFiltered(root,false);
499 //printf("Additional groups\n");
500 // then process the @addtogroup, @weakgroup blocks
501 organizeSubGroupsFiltered(root,true);
502}
503
504//----------------------------------------------------------------------
505
506static void buildFileList(const Entry *root)
507{
508 if ((root->section.isFileDoc() || (root->section.isFile() && Config_getBool(EXTRACT_ALL))) &&
509 !root->name.empty() && !root->tagInfo() // skip any file coming from tag files
510 )
511 {
512 bool ambig = false;
514 if (!fd || ambig)
515 {
516 bool save_ambig = ambig;
517 // use the directory of the file to see if the described file is in the same
518 // directory as the describing file.
519 DString fn = root->fileName;
520 size_t newIndex=fn.rfind('/');
521 if (newIndex==DString::npos)
522 {
523 fn = root->name;
524 }
525 else
526 {
527 fn = fn.left(newIndex)+"/"+root->name;
528 }
530 if (!fd) ambig = save_ambig;
531 }
532 //printf("**************** root->name=%s fd=%p\n",qPrint(root->name),(void*)fd);
533 if (fd && !ambig)
534 {
535 //printf("Adding documentation!\n");
536 // using false in setDocumentation is small hack to make sure a file
537 // is documented even if a \file command is used without further
538 // documentation
539 fd->setDocumentation(root->doc,root->docFile,root->docLine,false);
540 fd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
542 fd->setRefItems(root->sli);
544 root->commandOverrides.apply_includeGraph ([&](bool b) { fd->overrideIncludeGraph(b); });
545 root->commandOverrides.apply_includedByGraph([&](bool b) { fd->overrideIncludedByGraph(b); });
546 for (const Grouping &g : root->groups)
547 {
548 GroupDef *gd=nullptr;
550 {
551 if (!gd->containsFile(fd))
552 {
553 gd->addFile(fd);
554 fd->makePartOfGroup(gd);
555 //printf("File %s: in group %s\n",qPrint(fd->name()),qPrint(gd->name()));
556 }
557 }
558 else if (!gd && g.pri == Grouping::GROUPING_INGROUP)
559 {
560 warn(root->fileName, root->startLine,
561 "Found non-existing group '{}' for the command '{}', ignoring command",
563 );
564 }
565 }
566 }
567 else
568 {
569 DString text(4096, DString::ExplicitSize);
570 text.sprintf("the name '%s' supplied as "
571 "the argument in the \\file statement ",
572 qPrint(root->name));
573 if (ambig) // name is ambiguous
574 {
575 text+="matches the following input files:\n";
577 text+="\n";
578 text+="Please use a more specific name by "
579 "including a (larger) part of the path!";
580 }
581 else // name is not an input file
582 {
583 text+="is not an input file";
584 }
585 warn(root->fileName,root->startLine,"{}", text);
586 }
587 }
588 for (const auto &e : root->children()) buildFileList(e.get());
589}
590
591template<class DefMutable>
592static void addIncludeFile(DefMutable *cd,FileDef *ifd,const Entry *root)
593{
594 if (
595 (!root->doc.stripWhiteSpace().empty() ||
596 !root->brief.stripWhiteSpace().empty() ||
597 Config_getBool(EXTRACT_ALL)
598 ) && root->protection!=Protection::Private
599 )
600 {
601 //printf(">>>>>> includeFile=%s\n",qPrint(root->includeFile));
602
603 bool local=Config_getBool(FORCE_LOCAL_INCLUDES);
604 DString includeFile = root->includeFile;
605 if (!includeFile.empty() && includeFile.at(0)=='"')
606 {
607 local = true;
608 includeFile=includeFile.mid(1,includeFile.length()-2);
609 }
610 else if (!includeFile.empty() && includeFile.at(0)=='<')
611 {
612 local = false;
613 includeFile=includeFile.mid(1,includeFile.length()-2);
614 }
615
616 bool ambig = false;
617 FileDef *fd=nullptr;
618 // see if we need to include a verbatim copy of the header file
619 //printf("root->includeFile=%s\n",qPrint(root->includeFile));
620 if (!includeFile.empty() &&
621 (fd=Doxygen::inputNameLinkedMap->findFileDef(includeFile,ambig))==nullptr
622 )
623 { // explicit request
624 DString text;
625 text.sprintf("the name '%s' supplied as "
626 "the argument of the \\class, \\struct, \\union, or \\include command ",
627 qPrint(includeFile)
628 );
629 if (ambig) // name is ambiguous
630 {
631 text+="matches the following input files:\n";
633 text+="\n";
634 text+="Please use a more specific name by "
635 "including a (larger) part of the path!";
636 }
637 else // name is not an input file
638 {
639 text+="is not an input file";
640 }
641 warn(root->fileName,root->startLine, "{}", text);
642 }
643 else if (includeFile.empty() && ifd &&
644 // see if the file extension makes sense
645 EntryType::guessSection(ifd->name()).isHeader())
646 { // implicit assumption
647 fd=ifd;
648 }
649
650 // if a file is found, we mark it as a source file.
651 if (fd)
652 {
653 DString iName = !root->includeName.empty() ?
654 root->includeName : includeFile;
655 if (!iName.empty()) // user specified include file
656 {
657 if (iName.at(0)=='<') local=false; // explicit override
658 else if (iName.at(0)=='"') local=true;
659 if (iName.at(0)=='"' || iName.at(0)=='<')
660 {
661 iName=iName.mid(1,iName.length()-2); // strip quotes or brackets
662 }
663 if (iName.empty())
664 {
665 iName=fd->name();
666 }
667 }
668 else if (!Config_getList(STRIP_FROM_INC_PATH).empty())
669 {
671 }
672 else // use name of the file containing the class definition
673 {
674 iName=fd->name();
675 }
676 if (fd->generateSourceFile()) // generate code for header
677 {
678 cd->setIncludeFile(fd,iName,local,!root->includeName.empty());
679 }
680 else // put #include in the class documentation without link
681 {
682 cd->setIncludeFile(nullptr,iName,local,true);
683 }
684 }
685 }
686}
687
688
690{
691 size_t l = s.length();
692 int count=0;
693 int round=0;
694 DString result;
695 for (size_t i=0;i<l;i++)
696 {
697 char c=s.at(i);
698 if (c=='(') round++;
699 else if (c==')' && round>0) round--;
700 else if (c=='<' && round==0) count++;
701 if (count==0)
702 {
703 result+=c;
704 }
705 if (c=='>' && round==0 && count>0) count--;
706 }
707 //printf("stripTemplateSpecifiers(%s)=%s\n",qPrint(s),qPrint(result));
708 return result;
709}
710
711/*! returns the Definition object belonging to the first \a level levels of
712 * full qualified name \a name. Creates an artificial scope if the scope is
713 * not found and set the parent/child scope relation if the scope is found.
714 */
715[[maybe_unused]]
716static Definition *buildScopeFromQualifiedName(const DString &name_,SrcLangExt lang,const TagInfo *tagInfo)
717{
718 DString name = stripTemplateSpecifiers(name_);
719 name.stripPrefix("::");
720 int level = name.contains("::");
721 //printf("buildScopeFromQualifiedName(%s) level=%d\n",qPrint(name),level);
722 int i=0, p=0, l=0;
724 DString fullScope;
725 while (i<level)
726 {
727 int idx=getScopeFragment(name,p,&l);
728 if (idx==-1) return prevScope;
729 DString nsName = name.mid(idx,l);
730 if (nsName.empty()) return prevScope;
731 if (!fullScope.empty()) fullScope+="::";
732 fullScope+=nsName;
734 DefinitionMutable *innerScope = toDefinitionMutable(nd);
735 ClassDef *cd=nullptr;
736 if (nd==nullptr) cd = getClass(fullScope);
737 if (nd==nullptr && cd) // scope is a class
738 {
739 innerScope = toDefinitionMutable(cd);
740 }
741 else if (nd==nullptr && cd==nullptr && fullScope.find('<')==DString::npos) // scope is not known and could be a namespace!
742 {
743 // introduce bogus namespace
744 //printf("++ adding dummy namespace %s to %s tagInfo=%p\n",qPrint(nsName),qPrint(prevScope->name()),(void*)tagInfo);
745 NamespaceDefMutable *newNd=
747 Doxygen::namespaceLinkedMap->add(fullScope,
749 "[generated]",1,1,fullScope,
750 tagInfo?tagInfo->tagName:DString(),
751 tagInfo?tagInfo->fileName:DString())));
752 if (newNd)
753 {
754 newNd->setLanguage(lang);
755 newNd->setArtificial(true);
756 // add namespace to the list
757 innerScope = newNd;
758 }
759 }
760 else // scope is a namespace
761 {
762 }
763 if (innerScope)
764 {
765 // make the parent/child scope relation
766 DefinitionMutable *prevScopeMutable = toDefinitionMutable(prevScope);
767 if (prevScopeMutable)
768 {
769 prevScopeMutable->addInnerCompound(toDefinition(innerScope));
770 }
771 innerScope->setOuterScope(prevScope);
772 }
773 else // current scope is a class, so return only the namespace part...
774 {
775 return prevScope;
776 }
777 // proceed to the next scope fragment
778 p=idx+l+2;
779 prevScope=toDefinition(innerScope);
780 i++;
781 }
782 return prevScope;
783}
784
786 FileDef *fileScope,const TagInfo *tagInfo)
787{
788 //printf("<findScopeFromQualifiedName(%s,%s)\n",startScope ? qPrint(startScope->name()) : 0, qPrint(n));
789 Definition *resultScope=toDefinition(startScope);
790 if (resultScope==nullptr) resultScope=Doxygen::globalScope;
792 int l1 = 0;
793 int i1 = getScopeFragment(scope,0,&l1);
794 if (i1==-1)
795 {
796 //printf(">no fragments!\n");
797 return resultScope;
798 }
799 int p=i1+l1,l2=0,i2=0;
800 while ((i2=getScopeFragment(scope,p,&l2))!=-1)
801 {
802 DString nestedNameSpecifier = scope.mid(i1,l1);
803 Definition *orgScope = resultScope;
804 //printf(" nestedNameSpecifier=%s\n",qPrint(nestedNameSpecifier));
805 resultScope = const_cast<Definition*>(resultScope->findInnerCompound(nestedNameSpecifier));
806 //printf(" resultScope=%p\n",resultScope);
807 if (resultScope==nullptr)
808 {
809 if (orgScope==Doxygen::globalScope && fileScope && !fileScope->getUsedNamespaces().empty())
810 // also search for used namespaces
811 {
812 for (const auto &nd : fileScope->getUsedNamespaces())
813 {
815 if (mnd)
816 {
817 resultScope = findScopeFromQualifiedName(toNamespaceDefMutable(nd),n,fileScope,tagInfo);
818 if (resultScope!=nullptr) break;
819 }
820 }
821 if (resultScope)
822 {
823 // for a nested class A::I in used namespace N, we get
824 // N::A::I while looking for A, so we should compare
825 // resultScope->name() against scope.left(i2+l2)
826 //printf(" -> result=%s scope=%s\n",qPrint(resultScope->name()),qPrint(scope));
827 if (rightScopeMatch(resultScope->name(),scope.left(i2+l2)))
828 {
829 break;
830 }
831 goto nextFragment;
832 }
833 }
834
835 // also search for used classes. Complication: we haven't been able
836 // to put them in the right scope yet, because we are still resolving
837 // the scope relations!
838 // Therefore loop through all used classes and see if there is a right
839 // scope match between the used class and nestedNameSpecifier.
840 for (const auto &usedName : g_usingDeclarations)
841 {
842 //printf("Checking using class %s\n",qPrint(usedName));
843 if (rightScopeMatch(usedName,nestedNameSpecifier))
844 {
845 // ui.currentKey() is the fully qualified name of nestedNameSpecifier
846 // so use this instead.
847 DString fqn = usedName + scope.mid(p);
848 resultScope = buildScopeFromQualifiedName(fqn,startScope->getLanguage(),nullptr);
849 //printf("Creating scope from fqn=%s result %p\n",qPrint(fqn),resultScope);
850 if (resultScope)
851 {
852 //printf("> Match! resultScope=%s\n",qPrint(resultScope->name()));
853 return resultScope;
854 }
855 }
856 }
857
858 //printf("> name %s not found in scope %s\n",qPrint(nestedNameSpecifier),qPrint(orgScope->name()));
859 return nullptr;
860 }
861 nextFragment:
862 i1=i2;
863 l1=l2;
864 p=i2+l2;
865 }
866 //printf(">findScopeFromQualifiedName scope %s\n",qPrint(resultScope->name()));
867 return resultScope;
868}
869
870std::unique_ptr<ArgumentList> getTemplateArgumentsFromName(
871 const DString &name,
872 const ArgumentLists &tArgLists)
873{
874 // for each scope fragment, check if it is a template and advance through
875 // the list if so.
876 size_t i=0, p=0;
877 auto alIt = tArgLists.begin();
878 while ((i=name.find("::",p))!=DString::npos && alIt!=tArgLists.end())
879 {
881 if (nd==nullptr)
882 {
883 ClassDef *cd = getClass(name.left(i));
884 if (cd)
885 {
886 if (!cd->templateArguments().empty())
887 {
888 ++alIt;
889 }
890 }
891 }
892 p=i+2;
893 }
894 return alIt!=tArgLists.end() ?
895 std::make_unique<ArgumentList>(*alIt) :
896 std::unique_ptr<ArgumentList>();
897}
898
899static
901{
903
904 if (specifier.isStruct())
906 else if (specifier.isUnion())
907 sec=ClassDef::Union;
908 else if (specifier.isCategory())
910 else if (specifier.isInterface())
912 else if (specifier.isProtocol())
914 else if (specifier.isException())
916 else if (specifier.isService())
918 else if (specifier.isSingleton())
920
921 if (section.isUnionDoc())
922 sec=ClassDef::Union;
923 else if (section.isStructDoc())
925 else if (section.isInterfaceDoc())
927 else if (section.isProtocolDoc())
929 else if (section.isCategoryDoc())
931 else if (section.isExceptionDoc())
933 else if (section.isServiceDoc())
935 else if (section.isSingletonDoc())
937
938 return sec;
939}
940
941
942static void addClassToContext(const Entry *root)
943{
944 AUTO_TRACE("name={}",root->name);
945 FileDef *fd = root->fileDef();
946
947 DString scName;
948 if (root->parent()->section.isScope())
949 {
950 scName=root->parent()->name;
951 }
952 // name without parent's scope
953 DString fullName = root->name;
954
955 // strip off any template parameters (but not those for specializations)
956 if (size_t idx=fullName.find('>'); idx!=DString::npos && root->lang==SrcLangExt::CSharp) // mangle A<S,T>::N as A-2-g::N
957 {
958 fullName = mangleCSharpGenericName(fullName.left(idx+1))+fullName.mid(idx+1);
959 }
960 fullName=stripTemplateSpecifiersFromScope(fullName);
961
962 // name with scope (if not present already)
963 DString qualifiedName = fullName;
964 if (!scName.empty() && !leftScopeMatch(scName,fullName))
965 {
966 qualifiedName.prepend(scName+"::");
967 }
968
969 // see if we already found the class before
970 ClassDefMutable *cd = getClassMutable(qualifiedName);
971
972 AUTO_TRACE_ADD("Found class with name '{}', qualifiedName '{}'", cd ? cd->name() : root->name, qualifiedName);
973
974 if (cd)
975 {
976 fullName=cd->name();
977 AUTO_TRACE_ADD("Existing class '{}'",cd->name());
978 //if (cd->templateArguments()==0)
979 //{
980 // //printf("existing ClassDef tempArgList=%p specScope=%s\n",root->tArgList,qPrint(root->scopeSpec));
981 // cd->setTemplateArguments(tArgList);
982 //}
983
984 cd->setDocumentation(root->doc,root->docFile,root->docLine);
985 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
986 root->commandOverrides.apply_collaborationGraph([&](bool b ) { cd->overrideCollaborationGraph(b); });
987 root->commandOverrides.apply_inheritanceGraph ([&](CLASS_GRAPH_t gt) { cd->overrideInheritanceGraph(gt); });
988
989 if (!root->spec.isForwardDecl() && cd->isForwardDeclared())
990 {
991 cd->setDefFile(root->fileName,root->startLine,root->startColumn);
992 if (root->bodyLine!=-1)
993 {
994 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
995 cd->setBodyDef(fd);
996 }
997 }
998
999 if (cd->templateArguments().empty() || (cd->isForwardDeclared() && !root->spec.isForwardDecl()))
1000 {
1001 // this happens if a template class declared with @class is found
1002 // before the actual definition or if a forward declaration has different template
1003 // parameter names.
1004 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(cd->name(),root->tArgLists);
1005 if (tArgList)
1006 {
1007 cd->setTemplateArguments(*tArgList);
1008 }
1009 }
1010 if (cd->requiresClause().empty() && !root->req.empty())
1011 {
1012 cd->setRequiresClause(root->req);
1013 }
1014
1016
1017 cd->setMetaData(root->metaData);
1018 }
1019 else // new class
1020 {
1022
1023 DString className;
1024 DString namespaceName;
1025 extractNamespaceName(fullName,className,namespaceName);
1026
1027 AUTO_TRACE_ADD("New class: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1028 fullName, namespaceName, className, Trace::trunc(root->brief), Trace::trunc(root->doc));
1029
1030 DString tagName;
1031 DString refFileName;
1032 const TagInfo *tagInfo = root->tagInfo();
1033 if (tagInfo)
1034 {
1035 tagName = tagInfo->tagName;
1036 refFileName = tagInfo->fileName;
1037 if (fullName.find("::")!=DString::npos)
1038 // symbols imported via tag files may come without the parent scope,
1039 // so we artificially create it here
1040 {
1041 buildScopeFromQualifiedName(fullName,root->lang,tagInfo);
1042 }
1043 }
1044 std::unique_ptr<ArgumentList> tArgList;
1045 size_t i=0;
1046 if ((root->lang==SrcLangExt::CSharp || root->lang==SrcLangExt::Java) &&
1047 (i=fullName.find('<'))!=DString::npos)
1048 {
1049 // a Java/C# generic class looks like a C++ specialization, so we need to split the
1050 // name and template arguments here
1051 tArgList = stringToArgumentList(root->lang,fullName.mid(i));
1052 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
1053 // A -> A
1054 // A<T> -> A-1-g
1055 // A<T,S> -> A-2-g
1056 {
1057 fullName=mangleCSharpGenericName(fullName);
1058 }
1059 else
1060 {
1061 fullName=fullName.left(i);
1062 }
1063 }
1064 else
1065 {
1066 tArgList = getTemplateArgumentsFromName(fullName,root->tArgLists);
1067 }
1068 // add class to the list
1069 cd = toClassDefMutable(
1070 Doxygen::classLinkedMap->add(fullName,
1071 createClassDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1072 fullName,sec,tagName,refFileName,true,root->spec.isEnum()) ));
1073 if (cd)
1074 {
1075 AUTO_TRACE_ADD("New class '{}' type={} #tArgLists={} tagInfo={} hidden={} artificial={}",
1076 fullName,cd->compoundTypeString(),root->tArgLists.size(),
1077 fmt::ptr(tagInfo),root->hidden,root->artificial);
1078 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1079 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1080 cd->setLanguage(root->lang);
1081 cd->setId(root->id);
1082 cd->setHidden(root->hidden);
1083 cd->setArtificial(root->artificial);
1084 cd->setClassSpecifier(root->spec);
1085 if (root->lang==SrcLangExt::CSharp && !root->args.empty())
1086 {
1088 }
1089 cd->addQualifiers(root->qualifiers);
1090 cd->setTypeConstraints(root->typeConstr);
1091 root->commandOverrides.apply_collaborationGraph([&](bool b ) { cd->overrideCollaborationGraph(b); });
1092 root->commandOverrides.apply_inheritanceGraph ([&](CLASS_GRAPH_t gt) { cd->overrideInheritanceGraph(gt); });
1093
1094 if (tArgList)
1095 {
1096 cd->setTemplateArguments(*tArgList);
1097 }
1098 cd->setRequiresClause(root->req);
1099 cd->setProtection(root->protection);
1100 cd->setIsStatic(root->isStatic);
1101
1102 // file definition containing the class cd
1103 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1104 cd->setBodyDef(fd);
1105
1106 cd->setMetaData(root->metaData);
1107
1108 cd->insertUsedFile(fd);
1109 }
1110 else
1111 {
1112 AUTO_TRACE_ADD("Class {} not added, already exists as alias", fullName);
1113 }
1114 }
1115
1116 if (cd)
1117 {
1119 if (!root->subGrouping) cd->setSubGrouping(false);
1120 if (!root->spec.isForwardDecl())
1121 {
1122 if (cd->hasDocumentation())
1123 {
1124 addIncludeFile(cd,fd,root);
1125 }
1126 if (fd && root->section.isCompound())
1127 {
1128 AUTO_TRACE_ADD("Inserting class {} in file {} (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1129 cd->setFileDef(fd);
1130 fd->insertClass(cd);
1131 }
1132 }
1133 addClassToGroups(root,cd);
1135 cd->setRefItems(root->sli);
1136 cd->setRequirementReferences(root->rqli);
1137 }
1138}
1139
1140//----------------------------------------------------------------------
1141// build a list of all classes mentioned in the documentation
1142// and all classes that have a documentation block before their definition.
1143static void buildClassList(const Entry *root)
1144{
1145 if ((root->section.isCompound() || root->section.isObjcImpl()) && !root->name.empty())
1146 {
1147 AUTO_TRACE();
1148 addClassToContext(root);
1149 }
1150 for (const auto &e : root->children()) buildClassList(e.get());
1151}
1152
1153static void buildClassDocList(const Entry *root)
1154{
1155 if ((root->section.isCompoundDoc()) && !root->name.empty())
1156 {
1157 AUTO_TRACE();
1158 addClassToContext(root);
1159 }
1160 for (const auto &e : root->children()) buildClassDocList(e.get());
1161}
1162
1163//----------------------------------------------------------------------
1164// build a list of all classes mentioned in the documentation
1165// and all classes that have a documentation block before their definition.
1166
1167static void addConceptToContext(const Entry *root)
1168{
1169 AUTO_TRACE();
1170 FileDef *fd = root->fileDef();
1171
1172 DString scName;
1173 if (root->parent()->section.isScope())
1174 {
1175 scName=root->parent()->name;
1176 }
1177
1178 // name with scope (if not present already)
1179 DString qualifiedName = root->name;
1180 if (!scName.empty() && !leftScopeMatch(qualifiedName,scName))
1181 {
1182 qualifiedName.prepend(scName+"::");
1183 }
1184
1185 // see if we already found the concept before
1186 ConceptDefMutable *cd = getConceptMutable(qualifiedName);
1187
1188 AUTO_TRACE_ADD("Found concept with name '{}' (qualifiedName='{}')", cd ? cd->name() : root->name, qualifiedName);
1189
1190 if (cd)
1191 {
1192 qualifiedName=cd->name();
1193 AUTO_TRACE_ADD("Existing concept '{}'",cd->name());
1194
1195 cd->setDocumentation(root->doc,root->docFile,root->docLine);
1196 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1197
1198 addIncludeFile(cd,fd,root);
1199 }
1200 else // new concept
1201 {
1202 DString className;
1203 DString namespaceName;
1204 extractNamespaceName(qualifiedName,className,namespaceName);
1205
1206 AUTO_TRACE_ADD("New concept: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1207 qualifiedName,namespaceName,className,root->brief,root->doc);
1208
1209 DString tagName;
1210 DString refFileName;
1211 const TagInfo *tagInfo = root->tagInfo();
1212 if (tagInfo)
1213 {
1214 tagName = tagInfo->tagName;
1215 refFileName = tagInfo->fileName;
1216 if (qualifiedName.find("::")!=DString::npos)
1217 // symbols imported via tag files may come without the parent scope,
1218 // so we artificially create it here
1219 {
1220 buildScopeFromQualifiedName(qualifiedName,root->lang,tagInfo);
1221 }
1222 }
1223 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(qualifiedName,root->tArgLists);
1224 // add concept to the list
1226 Doxygen::conceptLinkedMap->add(qualifiedName,
1227 createConceptDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1228 qualifiedName,tagName,refFileName)));
1229 if (cd)
1230 {
1231 AUTO_TRACE_ADD("New concept '{}' #tArgLists={} tagInfo={}",
1232 qualifiedName,root->tArgLists.size(),fmt::ptr(tagInfo));
1233 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1234 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1235 cd->setLanguage(root->lang);
1236 cd->setId(root->id);
1237 cd->setHidden(root->hidden);
1238 cd->setGroupId(root->mGrpId);
1239 if (tArgList)
1240 {
1241 cd->setTemplateArguments(*tArgList);
1242 }
1243 cd->setInitializer(root->initializer.str());
1244 // file definition containing the class cd
1245 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1246 cd->setBodyDef(fd);
1248 cd->setRefItems(root->sli);
1249 cd->setRequirementReferences(root->rqli);
1250 addIncludeFile(cd,fd,root);
1251
1252 // also add namespace to the correct structural context
1253 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,qualifiedName,nullptr,tagInfo);
1255 {
1257 if (dm)
1258 {
1259 dm->addInnerCompound(cd);
1260 }
1261 cd->setOuterScope(d);
1262 }
1263 for (const auto &ce : root->children())
1264 {
1265 //printf("Concept %s has child %s\n",qPrint(root->name),qPrint(ce->section.to_string()));
1266 if (ce->section.isConceptDocPart())
1267 {
1268 cd->addSectionsToDefinition(ce->anchors);
1269 cd->setRefItems(ce->sli);
1270 cd->setRequirementReferences(ce->rqli);
1271 if (!ce->brief.empty())
1272 {
1273 cd->addDocPart(ce->brief,ce->startLine,ce->startColumn);
1274 //printf(" brief=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->brief),ce->startLine,ce->startColumn);
1275 }
1276 if (!ce->doc.empty())
1277 {
1278 cd->addDocPart(ce->doc,ce->startLine,ce->startColumn);
1279 //printf(" doc=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->doc),ce->startLine,ce->startColumn);
1280 }
1281 }
1282 else if (ce->section.isConceptCodePart())
1283 {
1284 cd->addCodePart(ce->initializer.str(),ce->startLine,ce->startColumn);
1285 //printf(" code=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->initializer.str()),ce->startLine,ce->startColumn);
1286 }
1287 }
1288 }
1289 else
1290 {
1291 AUTO_TRACE_ADD("Concept '{}' not added, already exists (as alias)", qualifiedName);
1292 }
1293 }
1294
1295 if (cd)
1296 {
1298 for (const auto &ce : root->children())
1299 {
1300 if (ce->section.isConceptDocPart())
1301 {
1302 cd->addSectionsToDefinition(ce->anchors);
1303 }
1304 }
1305 if (fd)
1306 {
1307 AUTO_TRACE_ADD("Inserting concept '{}' in file '{}' (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1308 cd->setFileDef(fd);
1309 fd->insertConcept(cd);
1310 }
1311 addConceptToGroups(root,cd);
1313 cd->setRefItems(root->sli);
1314 cd->setRequirementReferences(root->rqli);
1315 }
1316}
1317
1318static void findModuleDocumentation(const Entry *root)
1319{
1320 if (root->section.isModuleDoc())
1321 {
1322 AUTO_TRACE();
1324 }
1325 for (const auto &e : root->children()) findModuleDocumentation(e.get());
1326}
1327
1328static void buildConceptList(const Entry *root)
1329{
1330 if (root->section.isConcept())
1331 {
1332 AUTO_TRACE();
1333 addConceptToContext(root);
1334 }
1335 for (const auto &e : root->children()) buildConceptList(e.get());
1336}
1337
1338static void buildConceptDocList(const Entry *root)
1339{
1340 if (root->section.isConceptDoc())
1341 {
1342 AUTO_TRACE();
1343 addConceptToContext(root);
1344 }
1345 for (const auto &e : root->children()) buildConceptDocList(e.get());
1346}
1347
1348// This routine is to allow @ingroup X @{ concept A; concept B; @} to work
1349// (same also works for variable and functions because of logic in MemberGroup::insertMember)
1351{
1352 AUTO_TRACE();
1353 for (const auto &cd : *Doxygen::conceptLinkedMap)
1354 {
1355 if (cd->groupId()!=DOX_NOGROUP)
1356 {
1357 for (const auto &ocd : *Doxygen::conceptLinkedMap)
1358 {
1359 if (cd!=ocd && cd->groupId()==ocd->groupId() &&
1360 !cd->partOfGroups().empty() && ocd->partOfGroups().empty())
1361 {
1362 ConceptDefMutable *ocdm = toConceptDefMutable(ocd.get());
1363 if (ocdm)
1364 {
1365 for (const auto &gd : cd->partOfGroups())
1366 {
1367 if (gd)
1368 {
1369 AUTO_TRACE_ADD("making concept '{}' part of group '{}'",ocdm->name(),gd->name());
1370 ocdm->makePartOfGroup(gd);
1371 gd->addConcept(ocd.get());
1372 }
1373 }
1374 }
1375 }
1376 }
1377 }
1378 }
1379}
1380
1381//----------------------------------------------------------------------
1382
1384{
1385 ClassDefSet visitedClasses;
1386
1387 bool done=false;
1388 //int iteration=0;
1389 while (!done)
1390 {
1391 done=true;
1392 //++iteration;
1393 struct ClassAlias
1394 {
1395 ClassAlias(const DString &name,std::unique_ptr<ClassDef> cd,DefinitionMutable *ctx) :
1396 aliasFullName(name),aliasCd(std::move(cd)), aliasContext(ctx) {}
1397 DString aliasFullName;
1398 std::unique_ptr<ClassDef> aliasCd;
1399 DefinitionMutable *aliasContext;
1400 };
1401 std::vector<ClassAlias> aliases;
1402 for (const auto &icd : *Doxygen::classLinkedMap)
1403 {
1404 ClassDefMutable *cd = toClassDefMutable(icd.get());
1405 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1406 {
1407 DString name = stripAnonymousNamespaceScope(icd->name());
1408 //printf("processing=%s, iteration=%d\n",qPrint(cd->name()),iteration);
1409 // also add class to the correct structural context
1411 name,icd->getFileDef(),nullptr);
1412 if (d)
1413 {
1414 //printf("****** adding %s to scope %s in iteration %d\n",qPrint(cd->name()),qPrint(d->name()),iteration);
1416 if (dm)
1417 {
1418 dm->addInnerCompound(cd);
1419 }
1420 cd->setOuterScope(d);
1421
1422 // for inline namespace add an alias of the class to the outer scope
1424 {
1426 //printf("nd->isInline()=%d\n",nd->isInline());
1427 if (nd && nd->isInline())
1428 {
1429 d = d->getOuterScope();
1430 if (d)
1431 {
1432 dm = toDefinitionMutable(d);
1433 if (dm)
1434 {
1435 auto aliasCd = createClassDefAlias(d,cd);
1436 DString aliasFullName = d->qualifiedName()+"::"+aliasCd->localName();
1437 aliases.emplace_back(aliasFullName,std::move(aliasCd),dm);
1438 //printf("adding %s to %s as %s\n",qPrint(aliasCd->name()),qPrint(d->name()),qPrint(aliasFullName));
1439 }
1440 }
1441 }
1442 else
1443 {
1444 break;
1445 }
1446 }
1447
1448 visitedClasses.insert(icd.get());
1449 done=false;
1450 }
1451 //else
1452 //{
1453 // printf("****** ignoring %s: scope not (yet) found in iteration %d\n",qPrint(cd->name()),iteration);
1454 //}
1455 }
1456 }
1457 // add aliases
1458 for (auto &alias : aliases)
1459 {
1460 ClassDef *aliasCd = Doxygen::classLinkedMap->add(alias.aliasFullName,std::move(alias.aliasCd));
1461 if (aliasCd)
1462 {
1463 alias.aliasContext->addInnerCompound(aliasCd);
1464 }
1465 }
1466 }
1467
1468 //give warnings for unresolved compounds
1469 for (const auto &icd : *Doxygen::classLinkedMap)
1470 {
1471 ClassDefMutable *cd = toClassDefMutable(icd.get());
1472 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1473 {
1475 /// create the scope artificially
1476 // anyway, so we can at least relate scopes properly.
1477 Definition *d = buildScopeFromQualifiedName(name,cd->getLanguage(),nullptr);
1478 if (d && d!=cd && !cd->getDefFileName().empty())
1479 // avoid recursion in case of redundant scopes, i.e: namespace N { class N::C {}; }
1480 // for this case doxygen assumes the existence of a namespace N::N in which C is to be found!
1481 // also avoid warning for stuff imported via a tagfile.
1482 {
1484 if (dm)
1485 {
1486 dm->addInnerCompound(cd);
1487 }
1488 cd->setOuterScope(d);
1489 warn(cd->getDefFileName(),cd->getDefLine(),
1490 "Incomplete input: scope for class {} not found!{}",name,
1491 name.startsWith("std::") ? " Try enabling BUILTIN_STL_SUPPORT." : ""
1492 );
1493 }
1494 }
1495 }
1496}
1497
1499{
1500 //bool inlineGroupedClasses = Config_getBool(INLINE_GROUPED_CLASSES);
1501 //if (!inlineGroupedClasses) return;
1502 //printf("** distributeClassGroupRelations()\n");
1503
1504 ClassDefSet visitedClasses;
1505 for (const auto &cd : *Doxygen::classLinkedMap)
1506 {
1507 //printf("Checking %s\n",qPrint(cd->name()));
1508 // distribute the group to nested classes as well
1509 if (visitedClasses.find(cd.get())==visitedClasses.end() && !cd->partOfGroups().empty())
1510 {
1511 //printf(" Candidate for merging\n");
1512 GroupDef *gd = cd->partOfGroups().front();
1513 for (auto &ncd : cd->getClasses())
1514 {
1516 if (ncdm && ncdm->partOfGroups().empty())
1517 {
1518 //printf(" Adding %s to group '%s'\n",qPrint(ncd->name()),
1519 // gd->groupTitle());
1520 ncdm->makePartOfGroup(gd);
1521 gd->addClass(ncdm);
1522 }
1523 }
1524 visitedClasses.insert(cd.get()); // only visit every class once
1525 }
1526 }
1527}
1528
1529//----------------------------------------------------------------------
1530
1531template<typename Container>
1533 const Container *cd,
1534 const MemberDef *enumTypeMember,
1535 MemberListType mlFilter)
1536{
1537 if (md && md->isEnumerate() && md->name().startsWith("@")) // anonymous enum type
1538 {
1539 MemberList *eiml = cd->getMemberList(mlFilter);
1540 if (eiml)
1541 {
1542 for (const auto &eimd : *eiml)
1543 {
1544 DString vtype = eimd->typeString();
1545 if (vtype.find(md->name())!=DString::npos)
1546 {
1548 if (mimd)
1549 {
1550 mimd->setAnonymousEnumType(enumTypeMember);
1551 break;
1552 }
1553 }
1554 }
1555 }
1556 }
1557}
1558
1559static ClassDefMutable *createTagLessInstance(const Definition *root,const ClassDef *templ,const DString &fieldName)
1560{
1561 DString n = templ->name();
1562 // replace e.g. X::@1343:@4343::Y -> X::[struct]::Y
1563 if (size_t sn = n.find('@'); sn!=DString::npos)
1564 {
1565 const char *p = n.data()+sn;
1566 char c;
1567 while ((c=*p))
1568 {
1569 if (!isdigit(c) && c!='@' && c!=':') break;
1570 p++;
1571 }
1572 n = n.left(sn)+"["+templ->compoundTypeString().str()+"]"+p;
1573 }
1574 // add field name to the class name to make it unique again, e.g. X::[struct]::Y.m
1575 DString fullName = n+"."+fieldName;
1576
1577 //printf("** adding class %s based on %s in %s\n",qPrint(fullName),qPrint(templ->name()),qPrint(root->name()));
1579 Doxygen::classLinkedMap->add(fullName,
1581 templ->getDefLine(),
1582 templ->getDefColumn(),
1583 fullName,
1584 templ->compoundType())));
1585 if (cd)
1586 {
1587 //printf("cd->name()=%s displayName=%s\n",qPrint(cd->name()),qPrint(cd->displayName()));
1588 cd->setDocumentation(templ->documentation(),templ->docFile(),templ->docLine()); // copy docs to definition
1589 cd->setBriefDescription(templ->briefDescription(),templ->briefFile(),templ->briefLine());
1590 cd->setLanguage(templ->getLanguage());
1591 cd->setBodySegment(templ->getDefLine(),templ->getStartBodyLine(),templ->getEndBodyLine());
1592 cd->setBodyDef(templ->getBodyDef());
1593
1594 if (root!=Doxygen::globalScope)
1595 {
1596 DefinitionMutable *outerScope = toDefinitionMutable(const_cast<Definition*>(root));
1597 if (root && root->definitionType()==Definition::TypeFile)
1598 {
1599 FileDef *fd = toFileDef(const_cast<Definition*>(root));
1600 fd->insertClass(cd);
1601 cd->setFileDef(fd);
1603 }
1604 else if (outerScope)
1605 {
1606 outerScope->addInnerCompound(cd);
1607 cd->setOuterScope(const_cast<Definition*>(root));
1608 }
1609 }
1610
1611 for (auto &gd : root->partOfGroups())
1612 {
1613 cd->makePartOfGroup(gd);
1614 gd->addClass(cd);
1615 }
1616
1617 auto addMember = [&](const MemberDef *md) -> MemberDefMutable*
1618 {
1619 auto newMd = createMemberDef(md->getDefFileName(),md->getDefLine(),md->getDefColumn(),
1620 md->typeString(),md->name(),md->argsString(),md->excpString(),
1621 md->protection(),md->virtualness(),md->isStatic(),Relationship::Member,
1622 md->memberType(),
1623 ArgumentList(),ArgumentList(),"");
1624 MemberDefMutable *imd = toMemberDefMutable(newMd.get());
1625 imd->setMemberClass(cd);
1626 imd->setDefinition(md->definition());
1627 imd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
1628 imd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
1629 imd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
1630 imd->setMemberSpecifiers(md->getMemberSpecifiers());
1631 imd->setId(md->id());
1632 imd->addQualifiers(md->getQualifiers());
1633 imd->setVhdlSpecifiers(md->getVhdlSpecifiers());
1634 imd->setMemberGroupId(md->getMemberGroupId());
1635 imd->setInitializer(md->initializer());
1636 imd->setRequiresClause(md->requiresClause());
1637 imd->setMaxInitLines(md->initializerLines());
1638 imd->setBitfields(md->bitfieldString());
1639 imd->setLanguage(md->getLanguage());
1641 cd->insertMember(imd);
1642 associateVariableWithAnonymousEnumType(md,cd,imd,MemberListType::PubAttribs());
1643 MemberName *mn = Doxygen::memberNameLinkedMap->add(md->name());
1644 mn->push_back(std::move(newMd));
1645 return imd;
1646
1647 };
1648
1649 MemberList *ml = templ->getMemberList(MemberListType::PubAttribs());
1650 if (ml)
1651 {
1652 for (const auto &md : *ml)
1653 {
1654 //printf(" Member attribute %s def=%s\n",qPrint(md->name()),qPrint(md->definition()));
1655 addMember(md);
1656 }
1657 }
1658 ml = templ->getMemberList(MemberListType::PubTypes());
1659 if (ml)
1660 {
1661 for (const auto &md : *ml)
1662 {
1663 //printf(" Member type %s def=%s\n",qPrint(md->name()),qPrint(md->definition()));
1664 MemberDefMutable *mdm = addMember(md);
1665 if (md->isEnumerate() && md->name().startsWith("@")) // anonymous enum type
1666 {
1667 for (const auto &emd : md->enumFieldList())
1668 {
1669 //printf(" enum field %s\n",qPrint(emd->name()));
1670 MemberDefMutable *emdm = addMember(emd);
1671 mdm->insertEnumField(emdm);
1672 emdm->setEnumScope(md);
1673 }
1674 }
1675 }
1676 }
1677 }
1678 return cd;
1679}
1680
1681/** Look through the members of class \a cd and its public members.
1682 * If there is a member m of a tag less struct/union,
1683 * then we create a duplicate of the struct/union with the name of the
1684 * member to identify it.
1685 * So if cd has name S, then the tag less struct/union will get name S.m
1686 * Since tag less structs can be nested we need to call this function
1687 * recursively. Later on we need to patch the member types so we keep
1688 * track of the hierarchy of classes we create.
1689 */
1690template<typename Container, typename TagContainer>
1691static void processTagLessClasses(const Definition *root,
1692 const Container *cd,
1693 const TagContainer *tagParent,
1694 MemberListType varFilter,
1695 MemberListType typeFilter,
1696 const DString &prefix,int count)
1697{
1698 AUTO_TRACE("count={} name={}\n",count,cd->name());
1699 if (tagParent /*&& !cd->getClasses().empty()*/)
1700 {
1701 MemberList *ml = cd->getMemberList(varFilter);
1702 if (ml)
1703 {
1704 int pos=0;
1705 for (const auto &md : *ml)
1706 {
1707 DString type = md->typeString();
1708 //printf(" member %s: type='%s' outerScope='%s'\n",qPrint(md->name()),qPrint(type),qPrint(md->getOuterScope()?md->getOuterScope()->name():"<null>"));
1709 if ((cd->definitionType()!=Definition::TypeFile || md->getOuterScope()==Doxygen::globalScope) && // part namespace members only if cd is a namespace
1710 (type.find("::@")!=DString::npos || type.find(" @")!=DString::npos)) // member of tag less struct/union
1711 {
1712 std::vector<const ClassDef *> candidates;
1713 for (const auto &icd : cd->getClasses())
1714 {
1715 candidates.push_back(icd);
1716 }
1717 for (const auto &icd : candidates)
1718 {
1719 //printf(" comparing '%s'<->'%s'\n",qPrint(type),qPrint(icd->name()));
1720 if (type.find(icd->name())!=DString::npos) // matching tag less struct/union
1721 {
1722 DString name = md->name();
1723 if (md->isAnonymous()) name = "__unnamed" + DString().setNum(pos++)+"__";
1724 if (!prefix.empty()) name.prepend(prefix+".");
1725 //printf(" found %s in scope %s\n",qPrint(name),qPrint(cd->name()));
1726 ClassDefMutable *ncd = createTagLessInstance(root,icd,name);
1727 if (ncd)
1728 {
1729 processTagLessClasses(ncd,icd,ncd,MemberListType::PubAttribs(),MemberListType::PubTypes(),name,count+1);
1730 //printf(" addTagged %s to %s\n",qPrint(ncd->name()),qPrint(tagParent->name()));
1731 ncd->setTagLessReference(icd);
1732
1733 // associate the variable of the anonymous type with the type member
1734 MemberList *pml = tagParent->getMemberList(varFilter);
1735 if (pml)
1736 {
1737 for (const auto &pmd : *pml)
1738 {
1740 if (pmdm && pmd->name()==md->name())
1741 {
1742 pmdm->setClassDefOfAnonymousType(ncd);
1743 }
1744 }
1745 }
1746 }
1747 }
1748 else
1749 {
1750 //printf(" no match for %s in %s\n",qPrint(icd->name()),qPrint(type));
1751 }
1752 }
1753 }
1754 }
1755 }
1756 // associate the variable of the anonymous enum type with the type member
1757 ml = cd->getMemberList(typeFilter);
1758 if (ml)
1759 {
1760 for (const auto &md : *ml)
1761 {
1762 MemberListType mlFilter = cd->definitionType()==Definition::TypeClass ? MemberListType::PubAttribs() : MemberListType::DecVarMembers();
1763 associateVariableWithAnonymousEnumType(md,cd,md,mlFilter);
1764 }
1765 }
1766 }
1767}
1768
1769template<typename Container>
1770static void findTagLessClasses(std::set<const Definition *> &candidates,const Container *cd)
1771{
1772 for (const auto &icd : cd->getClasses())
1773 {
1774 if (icd->name().find('@')==DString::npos) // process all non-anonymous inner classes
1775 {
1776 findTagLessClasses(candidates,icd);
1777 }
1778 }
1779
1780 candidates.insert(cd);
1781}
1782
1784{
1785 std::set<const Definition *> candidates;
1786 for (auto &cd : *Doxygen::classLinkedMap)
1787 {
1788 Definition *scope = cd->getOuterScope();
1789 //printf(" scope=%s for class %s\n",qPrint(scope?scope->name():"<null>"),qPrint(cd->name()));
1790 if (scope && scope->definitionType()==Definition::TypeNamespace) // class that is not nested
1791 {
1792 const NamespaceDef *nd = toNamespaceDef(scope);
1793 if (nd && nd==Doxygen::globalScope) // class at global namespace
1794 {
1795 const FileDef *fd = cd->getFileDef();
1796 if (fd)
1797 {
1798 findTagLessClasses(candidates,fd);
1799 }
1800 }
1801 else if (nd) // class in a namespace
1802 {
1803 findTagLessClasses(candidates,nd);
1804 }
1805 }
1806 }
1807
1808 // since processTagLessClasses is potentially adding classes to Doxygen::classLinkedMap
1809 // we need to call it outside of the loop above, otherwise the iterator gets invalidated!
1810 for (const auto &d : candidates)
1811 {
1812 //printf("------ processing tag-less classes for %s\n",qPrint(d->name()));
1813 if (d->definitionType()==Definition::TypeNamespace)
1814 {
1815 const NamespaceDef *nd = toNamespaceDef(d);
1816 processTagLessClasses(nd,nd,nd,MemberListType::DecVarMembers(),MemberListType::DecEnumMembers(),"",0);
1817 }
1818 else if (d->definitionType()==Definition::TypeFile)
1819 {
1820 const FileDef *fd = toFileDef(d);
1821 processTagLessClasses(fd,fd,fd,MemberListType::DecVarMembers(),MemberListType::DecEnumMembers(),"",0);
1822 }
1823 else if (d->definitionType()==Definition::TypeClass)
1824 {
1825 const ClassDef *cd = toClassDef(d);
1826 processTagLessClasses(cd,cd,cd,MemberListType::PubAttribs(),MemberListType::PubTypes(),"",0);
1827 }
1828 }
1829}
1830
1831
1832//----------------------------------------------------------------------
1833// build a list of all namespaces mentioned in the documentation
1834// and all namespaces that have a documentation block before their definition.
1835static void buildNamespaceList(const Entry *root)
1836{
1837 if (
1838 (root->section.isNamespace() ||
1839 root->section.isNamespaceDoc() ||
1840 root->section.isPackageDoc()
1841 ) &&
1842 !root->name.empty()
1843 )
1844 {
1845 AUTO_TRACE("name={}",root->name);
1846
1847 DString fName = root->name;
1848 if (root->section.isPackageDoc())
1849 {
1850 fName=substitute(fName,".","::");
1851 }
1852
1853 DString fullName = stripAnonymousNamespaceScope(fName);
1854 if (!fullName.empty())
1855 {
1856 AUTO_TRACE_ADD("Found namespace {} in {} at line {}",root->name,root->fileName,root->startLine);
1858 if (ndi) // existing namespace
1859 {
1861 if (nd) // non-inline namespace
1862 {
1863 AUTO_TRACE_ADD("Existing namespace");
1864 nd->setDocumentation(root->doc,root->docFile,root->docLine);
1865 nd->setName(fullName); // change name to match docs
1867 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1868 if (nd->getLanguage()==SrcLangExt::Unknown)
1869 {
1870 nd->setLanguage(root->lang);
1871 }
1872 if (root->tagInfo()==nullptr && nd->isReference() && !(root->doc.empty() && root->brief.empty()))
1873 // if we previously found namespace nd in a tag file and now we find a
1874 // documented namespace with the same name in the project, then remove
1875 // the tag file reference
1876 {
1877 nd->setReference("");
1878 nd->setFileName(fullName);
1879 }
1880 nd->setMetaData(root->metaData);
1881
1882 // file definition containing the namespace nd
1883 FileDef *fd=root->fileDef();
1884 if (nd->isArtificial())
1885 {
1886 nd->setArtificial(false); // found namespace explicitly, so cannot be artificial
1887 nd->setDefFile(root->fileName,root->startLine,root->startColumn);
1888 }
1889 // insert the namespace in the file definition
1890 if (fd) fd->insertNamespace(nd);
1891 addNamespaceToGroups(root,nd);
1892 nd->setRefItems(root->sli);
1893 nd->setRequirementReferences(root->rqli);
1894 }
1895 }
1896 else // fresh namespace
1897 {
1898 DString tagName;
1899 DString tagFileName;
1900 const TagInfo *tagInfo = root->tagInfo();
1901 if (tagInfo)
1902 {
1903 tagName = tagInfo->tagName;
1904 tagFileName = tagInfo->fileName;
1905 }
1906 AUTO_TRACE_ADD("new namespace {} lang={} tagName={}",fullName,root->lang,tagName);
1907 // add namespace to the list
1909 Doxygen::namespaceLinkedMap->add(fullName,
1910 createNamespaceDef(tagInfo?tagName:root->fileName,root->startLine,
1911 root->startColumn,fullName,tagName,tagFileName,
1912 root->type,root->spec.isPublished())));
1913 if (nd)
1914 {
1915 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1916 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1918 nd->setHidden(root->hidden);
1919 nd->setArtificial(root->artificial);
1920 nd->setLanguage(root->lang);
1921 nd->setId(root->id);
1922 nd->setMetaData(root->metaData);
1923 nd->setInline(root->spec.isInline());
1924 nd->setExported(root->exported);
1925
1926 addNamespaceToGroups(root,nd);
1927 nd->setRefItems(root->sli);
1928 nd->setRequirementReferences(root->rqli);
1929
1930 // file definition containing the namespace nd
1931 FileDef *fd=root->fileDef();
1932 // insert the namespace in the file definition
1933 if (fd) fd->insertNamespace(nd);
1934
1935 // the empty string test is needed for extract all case
1936 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1937 nd->insertUsedFile(fd);
1938 nd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1939 nd->setBodyDef(fd);
1940
1941 // also add namespace to the correct structural context
1942 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,fullName,nullptr,tagInfo);
1943 AUTO_TRACE_ADD("adding namespace {} to context {}",nd->name(),d ? d->name() : DString("<none>"));
1944 if (d==nullptr) // we didn't find anything, create the scope artificially
1945 // anyway, so we can at least relate scopes properly.
1946 {
1947 d = buildScopeFromQualifiedName(fullName,nd->getLanguage(),tagInfo);
1949 if (dm)
1950 {
1951 dm->addInnerCompound(nd);
1952 }
1953 nd->setOuterScope(d);
1954 // TODO: Due to the order in which the tag file is written
1955 // a nested class can be found before its parent!
1956 }
1957 else
1958 {
1960 if (dm)
1961 {
1962 dm->addInnerCompound(nd);
1963 }
1964 nd->setOuterScope(d);
1965 // in case of d is an inline namespace, alias insert nd in the part scope of d.
1967 {
1968 NamespaceDef *pnd = toNamespaceDef(d);
1969 if (pnd && pnd->isInline())
1970 {
1971 d = d->getOuterScope();
1972 if (d)
1973 {
1974 dm = toDefinitionMutable(d);
1975 if (dm)
1976 {
1977 auto aliasNd = createNamespaceDefAlias(d,nd);
1978 dm->addInnerCompound(aliasNd.get());
1979 DString aliasName = aliasNd->name();
1980 AUTO_TRACE_ADD("adding alias {} to {}",aliasName,d->name());
1981 Doxygen::namespaceLinkedMap->add(aliasName,std::move(aliasNd));
1982 }
1983 }
1984 else
1985 {
1986 break;
1987 }
1988 }
1989 else
1990 {
1991 break;
1992 }
1993 }
1994 }
1995 }
1996 }
1997 }
1998 }
1999 for (const auto &e : root->children()) buildNamespaceList(e.get());
2000}
2001
2002//----------------------------------------------------------------------
2003
2005 const DString &name)
2006{
2007 NamespaceDef *usingNd =nullptr;
2008 for (auto &und : unl)
2009 {
2010 DString uScope=und->name()+"::";
2011 usingNd = getResolvedNamespace(uScope+name);
2012 if (usingNd!=nullptr) break;
2013 }
2014 return usingNd;
2015}
2016
2017static void findUsingDirectives(const Entry *root)
2018{
2019 if (root->section.isUsingDir())
2020 {
2021 AUTO_TRACE("Found using directive {} at line {} of {}",root->name,root->startLine,root->fileName);
2022 DString name=substitute(root->name,".","::");
2023 if (name.endsWith("::"))
2024 {
2025 name=name.left(name.length()-2);
2026 }
2027 if (!name.empty())
2028 {
2029 NamespaceDef *usingNd = nullptr;
2030 NamespaceDefMutable *nd = nullptr;
2031 FileDef *fd = root->fileDef();
2032 DString nsName;
2033
2034 // see if the using statement was found inside a namespace or inside
2035 // the global file scope.
2036 if (root->parent() && root->parent()->section.isNamespace() &&
2037 (fd==nullptr || fd->getLanguage()!=SrcLangExt::Java) // not a .java file
2038 )
2039 {
2040 nsName=stripAnonymousNamespaceScope(root->parent()->name);
2041 if (!nsName.empty())
2042 {
2043 nd = getResolvedNamespaceMutable(nsName);
2044 }
2045 }
2046
2047 // find the scope in which the 'using' namespace is defined by prepending
2048 // the possible scopes in which the using statement was found, starting
2049 // with the most inner scope and going to the most outer scope (i.e.
2050 // file scope).
2051 int scopeOffset = static_cast<int>(nsName.length());
2052 do
2053 {
2054 DString scope=scopeOffset>0 ?
2055 nsName.left(scopeOffset)+"::" : DString();
2056 usingNd = getResolvedNamespace(scope+name);
2057 //printf("Trying with scope='%s' usingNd=%p\n",(scope+qPrint(name)),usingNd);
2058 if (scopeOffset==0)
2059 {
2060 scopeOffset=-1;
2061 }
2062 else
2063 {
2064 size_t o = nsName.rfind("::",scopeOffset-1);
2065 scopeOffset = o!=DString::npos ? static_cast<int>(o) : 0;
2066 }
2067 } while (scopeOffset>=0 && usingNd==nullptr);
2068
2069 if (usingNd==nullptr && nd) // not found, try used namespaces in this scope
2070 // or in one of the parent namespace scopes
2071 {
2072 const NamespaceDefMutable *pnd = nd;
2073 while (pnd && usingNd==nullptr)
2074 {
2075 // also try with one of the used namespaces found earlier
2077
2078 // goto the parent
2079 Definition *s = pnd->getOuterScope();
2081 {
2083 }
2084 else
2085 {
2086 pnd = nullptr;
2087 }
2088 }
2089 }
2090 if (usingNd==nullptr && fd) // still nothing, also try used namespace in the
2091 // global scope
2092 {
2093 usingNd = findUsedNamespace(fd->getUsedNamespaces(),name);
2094 }
2095
2096 //printf("%s -> %s\n",qPrint(name),usingNd?qPrint(usingNd->name()):"<none>");
2097
2098 // add the namespace the correct scope
2099 if (usingNd)
2100 {
2101 //printf("using fd=%p nd=%p\n",fd,nd);
2102 if (nd)
2103 {
2104 //printf("Inside namespace %s\n",qPrint(nd->name()));
2105 nd->addUsingDirective(usingNd);
2106 }
2107 else if (fd)
2108 {
2109 //printf("Inside file %s\n",qPrint(fd->name()));
2110 fd->addUsingDirective(usingNd);
2111 }
2112 }
2113 else // unknown namespace, but add it anyway.
2114 {
2115 AUTO_TRACE_ADD("new unknown namespace {} lang={} hidden={}",name,root->lang,root->hidden);
2116 // add namespace to the list
2119 createNamespaceDef(root->fileName,root->startLine,root->startColumn,name)));
2120 if (nd)
2121 {
2122 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
2123 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2125 nd->setHidden(root->hidden);
2126 nd->setArtificial(true);
2127 nd->setLanguage(root->lang);
2128 nd->setId(root->id);
2129 nd->setMetaData(root->metaData);
2130 nd->setInline(root->spec.isInline());
2131 nd->setExported(root->exported);
2132
2133 for (const Grouping &g : root->groups)
2134 {
2135 GroupDef *gd=nullptr;
2137 gd->addNamespace(nd);
2138 }
2139
2140 // insert the namespace in the file definition
2141 if (fd)
2142 {
2143 fd->insertNamespace(nd);
2144 fd->addUsingDirective(nd);
2145 }
2146
2147 // the empty string test is needed for extract all case
2148 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2149 nd->insertUsedFile(fd);
2150 nd->setRefItems(root->sli);
2151 nd->setRequirementReferences(root->rqli);
2152 }
2153 }
2154 }
2155 }
2156 for (const auto &e : root->children()) findUsingDirectives(e.get());
2157}
2158
2159//----------------------------------------------------------------------
2160
2161static void buildListOfUsingDecls(const Entry *root)
2162{
2163 if (root->section.isUsingDecl() &&
2164 !root->parent()->section.isCompound() // not a class/struct member
2165 )
2166 {
2167 DString name = substitute(root->name,".","::");
2168 g_usingDeclarations.insert(name.str());
2169 }
2170 for (const auto &e : root->children()) buildListOfUsingDecls(e.get());
2171}
2172
2173
2174static void findUsingDeclarations(const Entry *root,bool filterPythonPackages)
2175{
2176 if (root->section.isUsingDecl() &&
2177 !root->parent()->section.isCompound() && // not a class/struct member
2178 (!filterPythonPackages || (root->lang==SrcLangExt::Python && root->fileName.endsWith("__init__.py")))
2179 )
2180 {
2181 AUTO_TRACE("Found using declaration '{}' at line {} of {} inside section {}",
2182 root->name,root->startLine,root->fileName,root->parent()->section);
2183 if (!root->name.empty())
2184 {
2185 const Definition *usingDef = nullptr;
2186 NamespaceDefMutable *nd = nullptr;
2187 FileDef *fd = root->fileDef();
2188 DString scName;
2189
2190 // see if the using statement was found inside a namespace or inside
2191 // the global file scope.
2192 if (root->parent()->section.isNamespace())
2193 {
2194 scName=root->parent()->name;
2195 if (!scName.empty())
2196 {
2197 nd = getResolvedNamespaceMutable(scName);
2198 }
2199 }
2200
2201 // Assume the using statement was used to import a class.
2202 // Find the scope in which the 'using' namespace is defined by prepending
2203 // the possible scopes in which the using statement was found, starting
2204 // with the most inner scope and going to the most outer scope (i.e.
2205 // file scope).
2206
2207 DString name = substitute(root->name,".","::"); //Java/C# scope->internal
2208
2209 SymbolResolver resolver;
2210 const Definition *scope = nd;
2211 if (nd==nullptr) scope = fd;
2212 usingDef = resolver.resolveSymbol(scope,name);
2213
2214 //printf("usingDef(scope=%s,name=%s)=%s\n",qPrint(nd?nd->qualifiedName():""),qPrint(name),usingDef?qPrint(usingDef->qualifiedName()):"nullptr");
2215
2216 if (!usingDef)
2217 {
2218 usingDef = getClass(name); // try direct lookup, this is needed to get
2219 // builtin STL classes to properly resolve, e.g.
2220 // vector -> std::vector
2221 }
2222 if (!usingDef)
2223 {
2224 usingDef = Doxygen::hiddenClassLinkedMap->find(name); // check if it is already hidden
2225 }
2226#if 0
2227 if (!usingDef)
2228 {
2229 AUTO_TRACE_ADD("New using class '{}' (sec={})! #tArgLists={}",
2230 name,root->section,root->tArgLists.size());
2233 createClassDef( "<using>",1,1, name, ClassDef::Class)));
2234 if (usingCd)
2235 {
2236 usingCd->setArtificial(true);
2237 usingCd->setLanguage(root->lang);
2238 usingDef = usingCd;
2239 }
2240 }
2241#endif
2242 else
2243 {
2244 AUTO_TRACE_ADD("Found used type '{}' in scope='{}'",
2245 usingDef->name(), nd ? nd->name(): fd ? fd->name() : DString("<unknown>"));
2246 }
2247
2248 if (usingDef)
2249 {
2250 if (nd)
2251 {
2252 nd->addUsingDeclaration(usingDef);
2253 }
2254 else if (fd)
2255 {
2256 fd->addUsingDeclaration(usingDef);
2257 }
2258 }
2259 }
2260 }
2261 for (const auto &e : root->children()) findUsingDeclarations(e.get(),filterPythonPackages);
2262}
2263
2264//----------------------------------------------------------------------
2265
2267{
2268 root->commandOverrides.apply_callGraph ([&](bool b) { md->overrideCallGraph(b); });
2269 root->commandOverrides.apply_callerGraph ([&](bool b) { md->overrideCallerGraph(b); });
2270 root->commandOverrides.apply_referencedByRelation([&](bool b) { md->overrideReferencedByRelation(b); });
2271 root->commandOverrides.apply_referencesRelation ([&](bool b) { md->overrideReferencesRelation(b); });
2272 root->commandOverrides.apply_inlineSource ([&](bool b) { md->overrideInlineSource(b); });
2273 root->commandOverrides.apply_enumValues ([&](bool b) { md->overrideEnumValues(b); });
2274}
2275
2276//----------------------------------------------------------------------
2277
2279 const DString &fileName,const DString &memName)
2280{
2281 AUTO_TRACE("creating new member {} for class {}",memName,cd->name());
2282 const ArgumentList &templAl = md->templateArguments();
2283 const ArgumentList &al = md->argumentList();
2284 auto newMd = createMemberDef(
2285 fileName,root->startLine,root->startColumn,
2286 md->typeString(),memName,md->argsString(),
2287 md->excpString(),root->protection,root->virt,
2288 md->isStatic(),Relationship::Member,md->memberType(),
2289 templAl,al,root->metaData
2290 );
2291 auto newMmd = toMemberDefMutable(newMd.get());
2292 newMmd->setMemberClass(cd);
2293 cd->insertMember(newMd.get());
2294 if (!root->doc.empty() || !root->brief.empty())
2295 {
2296 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2297 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2298 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2299 }
2300 else
2301 {
2302 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2303 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2304 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2305 }
2306 newMmd->setDefinition(md->definition());
2307 applyMemberOverrideOptions(root,newMmd);
2308 newMmd->addQualifiers(root->qualifiers);
2309 newMmd->setBitfields(md->bitfieldString());
2310 newMmd->addSectionsToDefinition(root->anchors);
2311 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2312 newMmd->setBodyDef(md->getBodyDef());
2313 newMmd->setInitializer(md->initializer());
2314 newMmd->setRequiresClause(md->requiresClause());
2315 newMmd->setMaxInitLines(md->initializerLines());
2316 newMmd->setMemberGroupId(root->mGrpId);
2317 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2318 newMmd->setVhdlSpecifiers(md->getVhdlSpecifiers());
2319 newMmd->setLanguage(root->lang);
2320 newMmd->setId(root->id);
2322 mn->push_back(std::move(newMd));
2323}
2324
2325static std::unordered_map<std::string,std::vector<ClassDefMutable*>> g_usingClassMap;
2326
2327static void findUsingDeclImports(const Entry *root)
2328{
2329 if (root->section.isUsingDecl() &&
2330 root->parent()->section.isCompound() // in a class/struct member
2331 )
2332 {
2333 AUTO_TRACE("Found using declaration '{}' inside section {}", root->name, root->parent()->section);
2334 DString fullName=removeRedundantWhiteSpace(root->parent()->name);
2335 fullName=stripAnonymousNamespaceScope(fullName);
2336 fullName=stripTemplateSpecifiersFromScope(fullName);
2337 ClassDefMutable *cd = getClassMutable(fullName);
2338 if (cd)
2339 {
2340 AUTO_TRACE_ADD("found class '{}'",cd->name());
2341 size_t i=root->name.rfind("::");
2342 if (i!=DString::npos)
2343 {
2344 DString scope=root->name.left(i);
2345 DString memName=root->name.mid(i+2);
2346 SymbolResolver resolver;
2347 const ClassDef *bcd = resolver.resolveClass(cd,scope); // todo: file in fileScope parameter
2348 AUTO_TRACE_ADD("name={} scope={} bcd={}",scope,cd?cd->name():"<none>",bcd?bcd->name():"<none>");
2349 if (bcd && bcd!=cd)
2350 {
2351 AUTO_TRACE_ADD("found class '{}' memName='{}'",bcd->name(),memName);
2353 const MemberNameInfo *mni = mnlm.find(memName);
2354 if (mni)
2355 {
2356 for (auto &mi : *mni)
2357 {
2358 const MemberDef *md = mi->memberDef();
2359 if (md && md->protection()!=Protection::Private)
2360 {
2361 AUTO_TRACE_ADD("found member '{}'",mni->memberName());
2362 DString fileName = root->fileName;
2363 if (fileName.empty() && root->tagInfo())
2364 {
2365 fileName = root->tagInfo()->tagName;
2366 }
2367 if (!cd->containsOverload(md))
2368 {
2369 createUsingMemberImportForClass(root,cd,md,fileName,memName);
2370 // also insert the member into copies of the class
2371 auto it = g_usingClassMap.find(cd->qualifiedName().str());
2372 if (it != g_usingClassMap.end())
2373 {
2374 for (const auto &copyCd : it->second)
2375 {
2376 createUsingMemberImportForClass(root,copyCd,md,fileName,memName);
2377 }
2378 }
2379 }
2380 }
2381 }
2382 }
2383 }
2384 }
2385 }
2386 }
2387 else if (root->section.isUsingDecl() &&
2388 (root->parent()->section.isNamespace() || root->parent()->section.isEmpty()) && // namespace or global member
2389 root->lang==SrcLangExt::Cpp // do we also want this for e.g. Fortran? (see test case 095)
2390 )
2391 {
2392 AUTO_TRACE("Found using declaration '{}' inside section {}", root->name, root->parent()->section);
2393 Definition *scope = nullptr;
2394 NamespaceDefMutable *nd = nullptr;
2395 FileDef *fd = root->parent()->fileDef();
2396 if (!root->parent()->name.empty())
2397 {
2398 DString fullName=removeRedundantWhiteSpace(root->parent()->name);
2399 fullName=stripAnonymousNamespaceScope(fullName);
2401 scope = nd;
2402 }
2403 else
2404 {
2405 scope = fd;
2406 }
2407 if (scope)
2408 {
2409 AUTO_TRACE_ADD("found scope '{}'",scope->name());
2410 SymbolResolver resolver;
2411 const Definition *def = resolver.resolveSymbol(root->name.startsWith("::") ? nullptr : scope,root->name);
2412 if (def && def->definitionType()==Definition::TypeMember)
2413 {
2414 size_t i=root->name.rfind("::");
2415 DString memName;
2416 if (i!=DString::npos)
2417 {
2418 memName = root->name.right(root->name.length()-i-2);
2419 }
2420 else
2421 {
2422 memName = root->name;
2423 }
2424 const MemberDef *md = toMemberDef(def);
2425 AUTO_TRACE_ADD("found member '{}' for name '{}'",md->qualifiedName(),root->name);
2426 DString fileName = root->fileName;
2427 if (fileName.empty() && root->tagInfo())
2428 {
2429 fileName = root->tagInfo()->tagName;
2430 }
2431 const ArgumentList &templAl = md->templateArguments();
2432 const ArgumentList &al = md->argumentList();
2433
2434 auto newMd = createMemberDef(
2435 fileName,root->startLine,root->startColumn,
2436 md->typeString(),memName,md->argsString(),
2437 md->excpString(),root->protection,root->virt,
2438 md->isStatic(),Relationship::Member,md->memberType(),
2439 templAl,al,root->metaData
2440 );
2441 auto newMmd = toMemberDefMutable(newMd.get());
2442 if (nd)
2443 {
2444 newMmd->setNamespace(nd);
2445 nd->insertMember(newMd.get());
2446 }
2447 if (fd)
2448 {
2449 newMmd->setFileDef(fd);
2450 fd->insertMember(newMd.get());
2451 }
2452 if (!root->doc.empty() || !root->brief.empty())
2453 {
2454 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2455 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2456 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2457 }
2458 else
2459 {
2460 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2461 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2462 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2463 }
2464 newMmd->setDefinition(md->definition());
2465 applyMemberOverrideOptions(root,newMmd);
2466 newMmd->addQualifiers(root->qualifiers);
2467 newMmd->setBitfields(md->bitfieldString());
2468 newMmd->addSectionsToDefinition(root->anchors);
2469 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2470 newMmd->setBodyDef(md->getBodyDef());
2471 newMmd->setInitializer(md->initializer());
2472 newMmd->setRequiresClause(md->requiresClause());
2473 newMmd->setMaxInitLines(md->initializerLines());
2474 newMmd->setMemberGroupId(root->mGrpId);
2475 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2476 newMmd->setVhdlSpecifiers(md->getVhdlSpecifiers());
2477 newMmd->setLanguage(root->lang);
2478 newMmd->setId(root->id);
2480 mn->push_back(std::move(newMd));
2481#if 0 // insert an alias instead of a copy
2482 const MemberDef *md = toMemberDef(def);
2483 AUTO_TRACE_ADD("found member '{}' for name '{}'",md->qualifiedName(),root->name);
2484 auto aliasMd = createMemberDefAlias(nd,md);
2485 DString aliasFullName = nd->qualifiedName()+"::"+aliasMd->localName();
2486 if (nd && aliasMd.get())
2487 {
2488 nd->insertMember(aliasMd.get());
2489 }
2490 if (fd && aliasMd.get())
2491 {
2492 fd->insertMember(aliasMd.get());
2493 }
2494 MemberName *mn = Doxygen::memberNameLinkedMap->add(aliasFullName);
2495 mn->push_back(std::move(aliasMd));
2496#endif
2497 }
2498 else if (def && def->definitionType()==Definition::TypeClass)
2499 {
2500 const ClassDef *cd = toClassDef(def);
2501 DString copyFullName;
2502 if (nd==nullptr)
2503 {
2504 copyFullName = cd->localName();
2505 }
2506 else
2507 {
2508 copyFullName = nd->qualifiedName()+"::"+cd->localName();
2509 }
2510 if (Doxygen::classLinkedMap->find(copyFullName)==nullptr)
2511 {
2513 Doxygen::classLinkedMap->add(copyFullName,
2514 cd->deepCopy(copyFullName)));
2515 AUTO_TRACE_ADD("found class '{}' for name '{}' copy '{}' obj={}",cd->qualifiedName(),root->name,copyFullName,(void*)ncdm);
2516 g_usingClassMap[cd->qualifiedName().str()].push_back(ncdm);
2517 if (ncdm)
2518 {
2519 if (nd) ncdm->moveTo(nd);
2520 if ((!root->doc.empty() || !root->brief.empty())) // use docs at using statement
2521 {
2522 ncdm->setDocumentation(root->doc,root->docFile,root->docLine);
2523 ncdm->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2524 }
2525 else // use docs from used class
2526 {
2527 ncdm->setDocumentation(cd->documentation(),cd->docFile(),cd->docLine());
2529 }
2530 if (nd)
2531 {
2532 nd->addInnerCompound(ncdm);
2533 nd->addUsingDeclaration(ncdm);
2534 }
2535 if (fd)
2536 {
2537 if (ncdm) ncdm->setFileDef(fd);
2538 fd->insertClass(ncdm);
2539 fd->addUsingDeclaration(ncdm);
2540 }
2541 }
2542 }
2543#if 0 // insert an alias instead of a copy
2544 auto aliasCd = createClassDefAlias(nd,cd);
2545 DString aliasFullName;
2546 if (nd==nullptr)
2547 {
2548 aliasFullName = aliasCd->localName();
2549 }
2550 else
2551 {
2552 aliasFullName = nd->qualifiedName()+"::"+aliasCd->localName();
2553 }
2554 AUTO_TRACE_ADD("found class '{}' for name '{}' aliasFullName='{}'",cd->qualifiedName(),root->name,aliasFullName);
2555 auto acd = Doxygen::classLinkedMap->add(aliasFullName,std::move(aliasCd));
2556 if (nd && acd)
2557 {
2558 nd->addInnerCompound(acd);
2559 }
2560 if (fd && acd)
2561 {
2562 fd->insertClass(acd);
2563 }
2564#endif
2565 }
2566 else if (scope)
2567 {
2568 AUTO_TRACE_ADD("no symbol with name '{}' in scope {}",root->name,scope->name());
2569 }
2570 }
2571 }
2572 for (const auto &e : root->children()) findUsingDeclImports(e.get());
2573}
2574
2575//----------------------------------------------------------------------
2576
2578{
2579 FileDefSet visitedFiles;
2580 // then recursively add using directives found in #include files
2581 // to files that have not been visited.
2582 for (const auto &fn : *Doxygen::inputNameLinkedMap)
2583 {
2584 for (const auto &fd : *fn)
2585 {
2586 //printf("----- adding using directives for file %s\n",qPrint(fd->name()));
2587 fd->addIncludedUsingDirectives(visitedFiles);
2588 }
2589 }
2590}
2591
2592//----------------------------------------------------------------------
2593
2595 const Entry *root,
2596 ClassDefMutable *cd,
2597 MemberType mtype,
2598 const DString &type,
2599 const DString &name,
2600 const DString &args,
2601 Protection prot,
2602 Relationship related)
2603{
2605 DString scopeSeparator="::";
2606 SrcLangExt lang = cd->getLanguage();
2607 if (lang==SrcLangExt::Java || lang==SrcLangExt::CSharp)
2608 {
2609 qualScope = substitute(qualScope,"::",".");
2610 scopeSeparator=".";
2611 }
2612 AUTO_TRACE("class variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} init='{}'",
2613 root->fileName, type, qualScope, name, args, root->protection, mtype, lang, root->initializer.str());
2614
2615 DString def;
2616 if (!type.empty())
2617 {
2618 if (related!=Relationship::Member || mtype==MemberType::Friend || Config_getBool(HIDE_SCOPE_NAMES))
2619 {
2620 if (root->spec.isAlias()) // turn 'typedef B A' into 'using A'
2621 {
2622 if (lang==SrcLangExt::Python)
2623 {
2624 def="type "+name+args;
2625 }
2626 else
2627 {
2628 def="using "+name;
2629 }
2630 }
2631 else
2632 {
2633 def=type+" "+name+args;
2634 }
2635 }
2636 else
2637 {
2638 if (root->spec.isAlias()) // turn 'typedef B C::A' into 'using C::A'
2639 {
2640 if (lang==SrcLangExt::Python)
2641 {
2642 def="type "+qualScope+scopeSeparator+name+args;
2643 }
2644 else
2645 {
2646 def="using "+qualScope+scopeSeparator+name;
2647 }
2648 }
2649 else
2650 {
2651 def=type+" "+qualScope+scopeSeparator+name+args;
2652 }
2653 }
2654 }
2655 else
2656 {
2657 if (Config_getBool(HIDE_SCOPE_NAMES))
2658 {
2659 def=name+args;
2660 }
2661 else
2662 {
2663 def=qualScope+scopeSeparator+name+args;
2664 }
2665 }
2666 def.stripPrefix("static ");
2667
2668 // see if the member is already found in the same scope
2669 // (this may be the case for a static member that is initialized
2670 // outside the class)
2672 if (mn)
2673 {
2674 for (const auto &imd : *mn)
2675 {
2676 //printf("md->getClassDef()=%p cd=%p type=[%s] md->typeString()=[%s]\n",
2677 // md->getClassDef(),cd,qPrint(type),md->typeString());
2678 MemberDefMutable *md = toMemberDefMutable(imd.get());
2679 if (md &&
2680 md->getClassDef()==cd &&
2681 ((lang==SrcLangExt::Python && type.empty() && !md->typeString().empty()) ||
2683 // member already in the scope
2684 {
2685
2686 if (root->lang==SrcLangExt::ObjC &&
2687 root->mtype==MethodTypes::Property &&
2688 md->memberType()==MemberType::Variable)
2689 { // Objective-C 2.0 property
2690 // turn variable into a property
2691 md->setProtection(root->protection);
2692 cd->reclassifyMember(md,MemberType::Property);
2693 }
2694 addMemberDocs(root,md,def,nullptr,false,root->spec);
2695 AUTO_TRACE_ADD("Member already found!");
2696 return md;
2697 }
2698 }
2699 }
2700
2701 DString fileName = root->fileName;
2702 if (fileName.empty() && root->tagInfo())
2703 {
2704 fileName = root->tagInfo()->tagName;
2705 }
2706
2707 // new member variable, typedef or enum value
2708 auto md = createMemberDef(
2709 fileName,root->startLine,root->startColumn,
2710 type,name,args,root->exception,
2711 prot,Specifier::Normal,root->isStatic,related,
2712 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2713 ArgumentList(), root->metaData);
2714 auto mmd = toMemberDefMutable(md.get());
2715 mmd->setTagInfo(root->tagInfo());
2716 mmd->setMemberClass(cd); // also sets outer scope (i.e. getOuterScope())
2717 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2718 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2719 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2720 mmd->setDefinition(def);
2721 mmd->setBitfields(root->bitfields);
2722 mmd->addSectionsToDefinition(root->anchors);
2723 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2724 mmd->setInitializer(root->initializer.str());
2725 mmd->setMaxInitLines(root->initLines);
2726 mmd->setMemberGroupId(root->mGrpId);
2727 mmd->setMemberSpecifiers(root->spec);
2728 mmd->setVhdlSpecifiers(root->vhdlSpec);
2729 mmd->setReadAccessor(root->read);
2730 mmd->setWriteAccessor(root->write);
2732 mmd->setHidden(root->hidden);
2733 mmd->setArtificial(root->artificial);
2734 mmd->setLanguage(root->lang);
2735 mmd->setId(root->id);
2736 addMemberToGroups(root,md.get());
2738 mmd->setBodyDef(root->fileDef());
2739 mmd->addQualifiers(root->qualifiers);
2740
2741 AUTO_TRACE_ADD("Adding new member '{}' to class '{}'",name,cd->name());
2742 cd->insertMember(md.get());
2743 mmd->setRefItems(root->sli);
2744 mmd->setRequirementReferences(root->rqli);
2745
2746 cd->insertUsedFile(root->fileDef());
2747 root->markAsProcessed();
2748
2749 if (mtype==MemberType::Typedef)
2750 {
2751 resolveTemplateInstanceInType(root,cd,md.get());
2752 }
2753
2754 // add the member to the global list
2755 MemberDef *result = md.get();
2757 mn->push_back(std::move(md));
2758
2759 return result;
2760}
2761
2762//----------------------------------------------------------------------
2763
2765 const Entry *root,
2766 MemberType mtype,
2767 const DString &scope,
2768 const DString &type,
2769 const DString &name,
2770 const DString &args)
2771{
2772 AUTO_TRACE("global variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} init='{}'",
2773 root->fileName, type, scope, name, args, root->protection, mtype, root->lang, root->initializer.str());
2774
2775 FileDef *fd = root->fileDef();
2776
2777 // see if we have a typedef that should hide a struct or union
2778 if (mtype==MemberType::Typedef && Config_getBool(TYPEDEF_HIDES_STRUCT))
2779 {
2780 DString ttype = type;
2781 ttype.stripPrefix("typedef ");
2782 if (ttype.stripPrefix("struct ") || ttype.stripPrefix("union "))
2783 {
2784 static const reg::Ex re(R"(\a\w*)");
2785 reg::Match match;
2786 const std::string &typ = ttype.str();
2787 if (reg::search(typ,match,re))
2788 {
2789 DString typeValue = match.str();
2790 ClassDefMutable *cd = getClassMutable(typeValue);
2791 if (cd)
2792 {
2793 // this typedef should hide compound name cd, so we
2794 // change the name that is displayed from cd.
2795 cd->setClassName(name);
2796 cd->setDocumentation(root->doc,root->docFile,root->docLine);
2797 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2798 return nullptr;
2799 }
2800 }
2801 }
2802 }
2803
2804 // see if the function is inside a namespace
2805 NamespaceDefMutable *nd = nullptr;
2806 if (!scope.empty())
2807 {
2808 if (scope.find('@')!=DString::npos) return nullptr; // anonymous scope!
2809 nd = getResolvedNamespaceMutable(scope);
2810 }
2811 DString def;
2812
2813 // determine the definition of the global variable
2814 if (nd && !nd->isAnonymous() &&
2815 !Config_getBool(HIDE_SCOPE_NAMES)
2816 )
2817 // variable is inside a namespace, so put the scope before the name
2818 {
2819 SrcLangExt lang = nd->getLanguage();
2821
2822 if (!type.empty())
2823 {
2824 if (root->spec.isAlias()) // turn 'typedef B NS::A' into 'using NS::A'
2825 {
2826 if (lang==SrcLangExt::Python)
2827 {
2828 def="type "+nd->name()+sep+name+args;
2829 }
2830 else
2831 {
2832 def="using "+nd->name()+sep+name;
2833 }
2834 }
2835 else // normal member
2836 {
2837 def=type+" "+nd->name()+sep+name+args;
2838 }
2839 }
2840 else
2841 {
2842 def=nd->name()+sep+name+args;
2843 }
2844 }
2845 else
2846 {
2847 if (!type.empty() && !root->name.empty())
2848 {
2849 if (name.at(0)=='@') // dummy variable representing anonymous union
2850 {
2851 def=type;
2852 }
2853 else
2854 {
2855 if (root->spec.isAlias()) // turn 'typedef B A' into 'using A'
2856 {
2857 if (root->lang==SrcLangExt::Python)
2858 {
2859 def="type "+root->name+args;
2860 }
2861 else
2862 {
2863 def="using "+root->name;
2864 }
2865 }
2866 else // normal member
2867 {
2868 def=type+" "+name+args;
2869 }
2870 }
2871 }
2872 else
2873 {
2874 def=name+args;
2875 }
2876 }
2877 def.stripPrefix("static ");
2878
2880 if (mn)
2881 {
2882 //DString nscope=removeAnonymousScopes(scope);
2883 //NamespaceDef *nd=nullptr;
2884 //if (!nscope.empty())
2885 if (!scope.empty())
2886 {
2887 nd = getResolvedNamespaceMutable(scope);
2888 }
2889 for (const auto &imd : *mn)
2890 {
2891 MemberDefMutable *md = toMemberDefMutable(imd.get());
2892 if (md &&
2893 ((nd==nullptr && md->getNamespaceDef()==nullptr && md->getFileDef() &&
2894 root->fileName==md->getFileDef()->absFilePath()
2895 ) // both variable names in the same file
2896 || (nd!=nullptr && md->getNamespaceDef()==nd) // both in same namespace
2897 )
2898 && !md->isDefine() // function style #define's can be "overloaded" by typedefs or variables
2899 && !md->isEnumerate() // in C# an enum value and enum can have the same name
2900 )
2901 // variable already in the scope
2902 {
2903 bool isPHPArray = md->getLanguage()==SrcLangExt::PHP &&
2904 md->argsString()!=args &&
2905 args.find('[')!=DString::npos;
2906 bool staticsInDifferentFiles =
2907 root->isStatic && md->isStatic() &&
2908 root->fileName!=md->getDefFileName();
2909
2910 if (md->getFileDef() &&
2911 !isPHPArray && // not a php array
2912 !staticsInDifferentFiles
2913 )
2914 // not a php array variable
2915 {
2916 AUTO_TRACE_ADD("variable already found: scope='{}'",md->getOuterScope()->name());
2917 addMemberDocs(root,md,def,nullptr,false,root->spec);
2918 md->setRefItems(root->sli);
2919 md->setRequirementReferences(root->rqli);
2920 // if md is a variable forward declaration and root is the definition that
2921 // turn md into the definition
2922 if (!root->explicitExternal && md->isExternal())
2923 {
2924 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
2925 md->setExplicitExternal(false,root->fileName,root->startLine,root->startColumn);
2926 }
2927 // if md is the definition and root point at a declaration, then add the
2928 // declaration info
2929 else if (root->explicitExternal && !md->isExternal())
2930 {
2931 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
2932 }
2933 return md;
2934 }
2935 }
2936 }
2937 }
2938
2939 DString fileName = root->fileName;
2940 if (fileName.empty() && root->tagInfo())
2941 {
2942 fileName = root->tagInfo()->tagName;
2943 }
2944
2945 AUTO_TRACE_ADD("new variable, namespace='{}'",nd?nd->name():DString("<global>"));
2946 // new global variable, enum value or typedef
2947 auto md = createMemberDef(
2948 fileName,root->startLine,root->startColumn,
2949 type,name,args,DString(),
2950 root->protection, Specifier::Normal,root->isStatic,Relationship::Member,
2951 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2952 root->argList, root->metaData);
2953 auto mmd = toMemberDefMutable(md.get());
2954 mmd->setTagInfo(root->tagInfo());
2955 mmd->setMemberSpecifiers(root->spec);
2956 mmd->setVhdlSpecifiers(root->vhdlSpec);
2957 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2958 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2959 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2960 mmd->addSectionsToDefinition(root->anchors);
2961 mmd->setInitializer(root->initializer.str());
2962 mmd->setMaxInitLines(root->initLines);
2963 mmd->setMemberGroupId(root->mGrpId);
2964 mmd->setDefinition(def);
2965 mmd->setLanguage(root->lang);
2966 mmd->setId(root->id);
2968 mmd->setExplicitExternal(root->explicitExternal,fileName,root->startLine,root->startColumn);
2969 mmd->addQualifiers(root->qualifiers);
2970 //md->setOuterScope(fd);
2971 if (!root->explicitExternal)
2972 {
2973 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2974 mmd->setBodyDef(fd);
2975 }
2976 addMemberToGroups(root,md.get());
2978
2979 mmd->setRefItems(root->sli);
2980 mmd->setRequirementReferences(root->rqli);
2981 if (nd && !nd->isAnonymous())
2982 {
2983 mmd->setNamespace(nd);
2984 nd->insertMember(md.get());
2985 }
2986
2987 // add member to the file (we do this even if we have already inserted
2988 // it into the namespace.
2989 if (fd)
2990 {
2991 mmd->setFileDef(fd);
2992 fd->insertMember(md.get());
2993 }
2994
2995 root->markAsProcessed();
2996
2997 if (mtype==MemberType::Typedef)
2998 {
2999 resolveTemplateInstanceInType(root,nd,md.get());
3000 }
3001
3002 // add member definition to the list of globals
3003 MemberDef *result = md.get();
3005 mn->push_back(std::move(md));
3006
3007
3008
3009 return result;
3010}
3011
3012/*! See if the return type string \a type is that of a function pointer
3013 * \returns -1 if this is not a function pointer variable or
3014 * the index at which the closing brace of (...*name) was found.
3015 */
3016static int findFunctionPtr(const std::string &type,SrcLangExt lang, int *pLength=nullptr)
3017{
3018 AUTO_TRACE("type='{}' lang={}",type,lang);
3019 if (lang == SrcLangExt::Fortran || lang == SrcLangExt::VHDL)
3020 {
3021 return -1; // Fortran and VHDL do not have function pointers
3022 }
3023
3024 static const reg::Ex re(R"(\‍([^)]*[*&^][^)]*\))");
3025 reg::Match match;
3026 size_t i=std::string::npos;
3027 size_t l=0;
3028 if (reg::search(type,match,re)) // contains (...*...) or (...&...) or (...^...)
3029 {
3030 i = match.position();
3031 l = match.length();
3032 }
3033 if (i!=std::string::npos)
3034 {
3035 size_t di = type.find("decltype(");
3036 if (di!=std::string::npos && di<i)
3037 {
3038 i = std::string::npos;
3039 }
3040 }
3041 size_t bb=type.find('<');
3042 size_t be=type.rfind('>');
3043 bool templFp = false;
3044 if (be!=std::string::npos) {
3045 size_t cc_ast = type.find("::*");
3046 size_t cc_amp = type.find("::&");
3047 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>::*)'
3048 }
3049
3050 if (!type.empty() && // return type is non-empty
3051 i!=std::string::npos && // contains (...*...)
3052 type.find("operator")==std::string::npos && // not an operator
3053 (type.find(")(")==std::string::npos || type.find("typedef ")!=std::string::npos) &&
3054 // not a function pointer return type
3055 (!((bb!=std::string::npos && bb<i) && (be!=std::string::npos && i<be)) || templFp) // bug665855: avoid treating "typedef A<void (T*)> type" as a function pointer
3056 )
3057 {
3058 if (pLength) *pLength=static_cast<int>(l);
3059 //printf("findFunctionPtr=%d\n",(int)i);
3060 AUTO_TRACE_EXIT("result={}",i);
3061 return static_cast<int>(i);
3062 }
3063 else
3064 {
3065 //printf("findFunctionPtr=%d\n",-1);
3066 AUTO_TRACE_EXIT("result=-1");
3067 return -1;
3068 }
3069}
3070
3071//--------------------------------------------------------------------------------------
3072
3073/*! Returns true iff \a type is a class within scope \a context.
3074 * Used to detect variable declarations that look like function prototypes.
3075 */
3076static bool isVarWithConstructor(const Entry *root)
3077{
3078 bool result = false;
3079 bool typeIsClass = false;
3080 bool typePtrType = false;
3081 DString type;
3082 Definition *ctx = nullptr;
3083 FileDef *fd = root->fileDef();
3084 SymbolResolver resolver(fd);
3085
3086 AUTO_TRACE("isVarWithConstructor({})",root->name);
3087 if (root->parent()->section.isCompound())
3088 { // inside a class
3089 result=false;
3090 AUTO_TRACE_EXIT("inside class: result={}",result);
3091 return result;
3092 }
3093 else if ((fd != nullptr) && (fd->name().endsWith(".c") || fd->name().endsWith(".h")))
3094 { // inside a .c file
3095 result=false;
3096 AUTO_TRACE_EXIT("inside C file: result={}",result);
3097 return result;
3098 }
3099 if (root->type.empty())
3100 {
3101 result=false;
3102 AUTO_TRACE_EXIT("no type: result={}",result);
3103 return result;
3104 }
3105 if (!root->parent()->name.empty())
3106 {
3108 }
3109 type = root->type;
3110 // remove qualifiers
3111 type.findAndRemoveWord("const");
3112 type.findAndRemoveWord("static");
3113 type.findAndRemoveWord("volatile");
3114 typePtrType = type.find('*')!=DString::npos || type.find('&')!=DString::npos;
3115 if (!typePtrType)
3116 {
3117 typeIsClass = resolver.resolveClass(ctx,type)!=nullptr;
3118 if (size_t ti=type.find('<'); !typeIsClass && ti!=DString::npos)
3119 {
3120 typeIsClass=resolver.resolveClass(ctx,type.left(ti))!=nullptr;
3121 }
3122 }
3123 if (typeIsClass) // now we still have to check if the arguments are
3124 // types or values. Since we do not have complete type info
3125 // we need to rely on heuristics :-(
3126 {
3127 if (root->argList.empty())
3128 {
3129 result=false; // empty arg list -> function prototype.
3130 AUTO_TRACE_EXIT("empty arg list: result={}",result);
3131 return result;
3132 }
3133 for (const Argument &a : root->argList)
3134 {
3135 static const reg::Ex initChars(R"([\d"'&*!^]+)");
3136 reg::Match match;
3137 if (!a.name.empty() || !a.defval.empty())
3138 {
3139 std::string name = a.name.str();
3140 if (reg::search(name,match,initChars) && match.position()==0)
3141 {
3142 result=true;
3143 }
3144 else
3145 {
3146 result=false; // arg has (type,name) pair -> function prototype
3147 }
3148 AUTO_TRACE_EXIT("function prototype: result={}",result);
3149 return result;
3150 }
3151 if (!a.type.empty() &&
3152 (a.type.at(a.type.length()-1)=='*' ||
3153 a.type.at(a.type.length()-1)=='&'))
3154 // type ends with * or & => pointer or reference
3155 {
3156 result=false;
3157 AUTO_TRACE_EXIT("pointer or reference: result={}",result);
3158 return result;
3159 }
3160 if (a.type.empty() || resolver.resolveClass(ctx,a.type)!=nullptr)
3161 {
3162 result=false; // arg type is a known type
3163 AUTO_TRACE_EXIT("known type: result={}",result);
3164 return result;
3165 }
3166 if (checkIfTypedef(ctx,fd,a.type))
3167 {
3168 result=false; // argument is a typedef
3169 AUTO_TRACE_EXIT("typedef: result={}",result);
3170 return result;
3171 }
3172 std::string atype = a.type.str();
3173 if (reg::search(atype,match,initChars) && match.position()==0)
3174 {
3175 result=true; // argument type starts with typical initializer char
3176 AUTO_TRACE_EXIT("argument with init char: result={}",result);
3177 return result;
3178 }
3179 std::string resType=resolveTypeDef(ctx,a.type).str();
3180 if (resType.empty()) resType=atype;
3181 static const reg::Ex idChars(R"(\a\w*)");
3182 if (reg::search(resType,match,idChars) && match.position()==0) // resType starts with identifier
3183 {
3184 resType=match.str();
3185 if (resType=="int" || resType=="long" ||
3186 resType=="float" || resType=="double" ||
3187 resType=="char" || resType=="void" ||
3188 resType=="signed" || resType=="unsigned" ||
3189 resType=="const" || resType=="volatile" )
3190 {
3191 result=false; // type keyword -> function prototype
3192 AUTO_TRACE_EXIT("type keyword: result={}",result);
3193 return result;
3194 }
3195 }
3196 }
3197 result=true;
3198 }
3199
3200 AUTO_TRACE_EXIT("end: result={}",result);
3201 return result;
3202}
3203
3204//--------------------------------------------------------------------------------------
3205
3206/*! Searches for the end of a template in prototype \a s starting from
3207 * character position \a startPos. If the end was found the position
3208 * of the closing > is returned, otherwise -1 is returned.
3209 *
3210 * Handles exotic cases such as
3211 * \code
3212 * Class<(id<0)>
3213 * Class<bits<<2>
3214 * Class<"<">
3215 * Class<'<'>
3216 * Class<(")<")>
3217 * \endcode
3218 */
3219static int findEndOfTemplate(const DString &s,size_t startPos)
3220{
3221 // locate end of template
3222 size_t e=startPos;
3223 int brCount=1;
3224 int roundCount=0;
3225 size_t len = s.length();
3226 bool insideString=false;
3227 bool insideChar=false;
3228 char pc = 0;
3229 while (e<len && brCount!=0)
3230 {
3231 char c=s.at(e);
3232 switch(c)
3233 {
3234 case '<':
3235 if (!insideString && !insideChar)
3236 {
3237 if (e<len-1 && s.at(e+1)=='<')
3238 e++;
3239 else if (roundCount==0)
3240 brCount++;
3241 }
3242 break;
3243 case '>':
3244 if (!insideString && !insideChar)
3245 {
3246 if (e<len-1 && s.at(e+1)=='>')
3247 e++;
3248 else if (roundCount==0)
3249 brCount--;
3250 }
3251 break;
3252 case '(':
3253 if (!insideString && !insideChar)
3254 roundCount++;
3255 break;
3256 case ')':
3257 if (!insideString && !insideChar)
3258 roundCount--;
3259 break;
3260 case '"':
3261 if (!insideChar)
3262 {
3263 if (insideString && pc!='\\')
3264 insideString=false;
3265 else
3266 insideString=true;
3267 }
3268 break;
3269 case '\'':
3270 if (!insideString)
3271 {
3272 if (insideChar && pc!='\\')
3273 insideChar=false;
3274 else
3275 insideChar=true;
3276 }
3277 break;
3278 }
3279 pc = c;
3280 e++;
3281 }
3282 return brCount==0 ? static_cast<int>(e) : -1;
3283}
3284
3285//--------------------------------------------------------------------------------------
3286
3287static void addVariable(const Entry *root,int isFuncPtr=-1)
3288{
3289 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
3290
3291 AUTO_TRACE("VARIABLE_SEC: type='{}' name='{}' args='{}' bodyLine={} endBodyLine={} mGrpId={} relates='{}'",
3292 root->type, root->name, root->args, root->bodyLine, root->endBodyLine, root->mGrpId, root->relates);
3293 //printf("root->parent->name=%s\n",qPrint(root->parent->name));
3294
3295 DString type = root->type;
3296 DString name = root->name;
3297 DString args = root->args;
3298 if (type.empty() && name.find("operator")==DString::npos &&
3299 (name.find('*')!=DString::npos || name.find('&')!=DString::npos))
3300 {
3301 // recover from parse error caused by redundant braces
3302 // like in "int *(var[10]);", which is parsed as
3303 // type="" name="int *" args="(var[10])"
3304
3305 type=name;
3306 std::string sargs = args.str();
3307 static const reg::Ex reName(R"(\a\w*)");
3308 reg::Match match;
3309 if (reg::search(sargs,match,reName))
3310 {
3311 name = match.str(); // e.g. 'var' in '(var[10])'
3312 sargs = match.suffix().str(); // e.g. '[10]) in '(var[10])'
3313 size_t j = sargs.find(')');
3314 if (j!=std::string::npos) args=sargs.substr(0,j); // extract, e.g '[10]' from '[10])'
3315 }
3316 }
3317 else
3318 {
3319 int i=isFuncPtr;
3320 if (i==-1 && (root->spec.isAlias())==0) i=findFunctionPtr(type.str(),root->lang); // for typedefs isFuncPtr is not yet set
3321 AUTO_TRACE_ADD("functionPtr={}",i!=-1?"yes":"no");
3322 if (i>=0) // function pointer
3323 {
3324 size_t ii = i;
3325 size_t ai = type.find('[',ii);
3326 if (ai!=DString::npos && ai>ii) // function pointer array
3327 {
3328 args.prepend(type.mid(ai));
3329 type=type.left(ai);
3330 }
3331 else if (type.find(')',ii)!=DString::npos) // function ptr, not variable like "int (*bla)[10]"
3332 {
3333 type=type.left(type.length()-1);
3334 args.prepend(") ");
3335 }
3336 }
3337 }
3338 AUTO_TRACE_ADD("after correction: type='{}' name='{}' args='{}'",type,name,args);
3339
3340 DString scope;
3341 name=removeRedundantWhiteSpace(name);
3342
3343 // find the scope of this variable
3344 int index = computeQualifiedIndex(name);
3345 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3346 // grouped members are stored with full scope
3347 {
3348 buildScopeFromQualifiedName(name.left(index+2),root->lang,root->tagInfo());
3349 scope=name.left(index);
3350 name=name.mid(index+2);
3351 }
3352 else
3353 {
3354 Entry *p = root->parent();
3355 while (p->section.isScope())
3356 {
3357 DString scopeName = p->name;
3358 if (!scopeName.empty())
3359 {
3360 scope.prepend(scopeName);
3361 break;
3362 }
3363 p=p->parent();
3364 }
3365 }
3366
3367 DString type_s = type;
3368 type=type.stripWhiteSpace();
3369 ClassDefMutable *cd=nullptr;
3370 bool isRelated=false;
3371 bool isMemberOf=false;
3372
3373 DString classScope=stripAnonymousNamespaceScope(scope);
3374 if (root->lang==SrcLangExt::CSharp)
3375 {
3376 classScope=mangleCSharpGenericName(classScope);
3377 }
3378 else
3379 {
3380 classScope=stripTemplateSpecifiersFromScope(classScope,false);
3381 }
3382 DString annScopePrefix=scope.left(scope.length()-classScope.length());
3383
3384
3385 // Look for last :: not part of template specifier
3386 int p=-1;
3387 for (size_t i=0;i<name.length()-1;i++)
3388 {
3389 if (name[i]==':' && name[i+1]==':')
3390 {
3391 p=static_cast<int>(i);
3392 }
3393 else if (name[i]=='<') // skip over template parts,
3394 // i.e. A::B<C::D> => p=1 and
3395 // A<B::C>::D => p=8
3396 {
3397 int e = findEndOfTemplate(name,i+1);
3398 if (e!=-1) i=static_cast<int>(e);
3399 }
3400 }
3401
3402 if (p!=-1) // found it
3403 {
3404 if (isTypeAClassFriend(type))
3405 {
3406 cd=getClassMutable(scope);
3407 if (cd)
3408 {
3409 addVariableToClass(root, // entry
3410 cd, // class to add member to
3411 MemberType::Friend, // type of member
3412 type, // type value as string
3413 name, // name of the member
3414 args, // arguments as string
3415 Protection::Public, // protection
3416 Relationship::Member // related to a class
3417 );
3418 }
3419 }
3420 if (root->bodyLine!=-1 && root->endBodyLine!=-1) // store the body location for later use
3421 {
3422 Doxygen::staticInitMap.emplace(name.str(),BodyInfo{root->startLine,root->bodyLine,root->endBodyLine});
3423 }
3424
3425
3426 AUTO_TRACE_ADD("static variable {} body=[{}..{}]",name,root->bodyLine,root->endBodyLine);
3427 return; /* skip this member, because it is a
3428 * static variable definition (always?), which will be
3429 * found in a class scope as well, but then we know the
3430 * correct protection level, so only then it will be
3431 * inserted in the correct list!
3432 */
3433 }
3434
3435 MemberType mtype = MemberType::Variable;
3436 if (type=="@")
3437 mtype=MemberType::EnumValue;
3438 else if (type_s.startsWith("typedef "))
3439 mtype=MemberType::Typedef;
3440 else if (type_s.startsWith("friend ") || type_s=="friend")
3441 mtype=MemberType::Friend;
3442 else if (root->mtype==MethodTypes::Property)
3443 mtype=MemberType::Property;
3444 else if (root->mtype==MethodTypes::Event)
3445 mtype=MemberType::Event;
3446 else if (type.find("sequence<") != DString::npos)
3447 mtype=sliceOpt ? MemberType::Sequence : MemberType::Typedef;
3448 else if (type.find("dictionary<") != DString::npos)
3449 mtype=sliceOpt ? MemberType::Dictionary : MemberType::Typedef;
3450
3451 if (!root->relates.empty()) // related variable
3452 {
3453 isRelated=true;
3454 isMemberOf=(root->relatesType==RelatesType::MemberOf);
3455 if (getClass(root->relates)==nullptr && !scope.empty())
3456 scope=mergeScopes(scope,root->relates);
3457 else
3458 scope=root->relates;
3459 }
3460
3461 cd=getClassMutable(scope);
3462 if (cd==nullptr && classScope!=scope) cd=getClassMutable(classScope);
3463 if (cd)
3464 {
3465 // if cd is an anonymous (=tag less) scope we insert the member
3466 // into a non-anonymous parent scope as well. This is needed to
3467 // be able to refer to it using \var or \fn
3468
3469 Relationship relationship = isMemberOf ? Relationship::Foreign :
3470 isRelated ? Relationship::Related :
3471 Relationship::Member ;
3472
3473 addVariableToClass(root, // entry
3474 cd, // class to add member to
3475 mtype, // member type
3476 type, // type value as string
3477 name, // name of the member
3478 args, // arguments as string
3479 root->protection,
3480 relationship
3481 );
3482 }
3483 else if (!name.empty()) // global variable
3484 {
3485 addVariableToFile(root,mtype,scope,type,name,args);
3486 }
3487
3488}
3489
3490//----------------------------------------------------------------------
3491// Searches the Entry tree for typedef documentation sections.
3492// If found they are stored in their class or in the global list.
3493static void buildTypedefList(const Entry *root)
3494{
3495 //printf("buildVarList(%s)\n",qPrint(rootNav->name()));
3496 if (!root->name.empty() &&
3497 root->section.isVariable() &&
3498 root->type.find("typedef ")!=DString::npos // its a typedef
3499 )
3500 {
3501 AUTO_TRACE();
3502 DString rname = removeRedundantWhiteSpace(root->name);
3503 DString scope;
3504 int index = computeQualifiedIndex(rname);
3505 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3506 // grouped members are stored with full scope
3507 {
3508 buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
3509 scope=rname.left(index);
3510 rname=rname.mid(index+2);
3511 }
3512 else
3513 {
3514 scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
3515 }
3519 bool found=false;
3520 if (mn) // symbol with the same name already found
3521 {
3522 for (auto &imd : *mn)
3523 {
3524 if (!imd->isTypedef())
3525 continue;
3526
3527 DString rtype = root->type;
3528 rtype.stripPrefix("typedef ");
3529
3530 // merge the typedefs only if they're not both grouped, and both are
3531 // either part of the same class, part of the same namespace, or both
3532 // are global (i.e., neither in a class or a namespace)
3533 bool notBothGrouped = root->groups.empty() || imd->getGroupDef()==nullptr; // see example #100
3534 bool bothSameScope = (!cd && !nd) || (cd && imd->getClassDef() == cd) || (nd && imd->getNamespaceDef() == nd);
3535 //printf("imd->isTypedef()=%d imd->typeString()=%s root->type=%s\n",imd->isTypedef(),
3536 // qPrint(imd->typeString()),qPrint(root->type));
3537 if (notBothGrouped && bothSameScope && imd->typeString()==rtype)
3538 {
3539 MemberDefMutable *md = toMemberDefMutable(imd.get());
3540 if (md)
3541 {
3542 md->setDocumentation(root->doc,root->docFile,root->docLine);
3544 md->setDocsForDefinition(!root->proto);
3545 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3547 md->setRefItems(root->sli);
3548 md->setRequirementReferences(root->rqli);
3549 md->addQualifiers(root->qualifiers);
3550
3551 // merge ingroup specifiers
3552 if (md->getGroupDef()==nullptr && !root->groups.empty())
3553 {
3554 addMemberToGroups(root,md);
3555 }
3556 else if (md->getGroupDef()!=nullptr && root->groups.empty())
3557 {
3558 //printf("existing member is grouped, new member not\n");
3559 }
3560 else if (md->getGroupDef()!=nullptr && !root->groups.empty())
3561 {
3562 //printf("both members are grouped\n");
3563 }
3564 found=true;
3565 break;
3566 }
3567 }
3568 }
3569 }
3570 if (found)
3571 {
3572 AUTO_TRACE_ADD("typedef '{}' already found",rname);
3573 // mark the entry as processed, as we copied everything from it elsewhere
3574 // also, otherwise, due to containing `typedef` it may later get treated
3575 // as a function typedef in filterMemberDocumentation, which is incorrect
3576 root->markAsProcessed();
3577 }
3578 else
3579 {
3580 AUTO_TRACE_ADD("new typedef '{}'",rname);
3581 addVariable(root);
3582 }
3583
3584 }
3585 for (const auto &e : root->children())
3586 if (!e->section.isEnum())
3587 buildTypedefList(e.get());
3588}
3589
3590//----------------------------------------------------------------------
3591// Searches the Entry tree for sequence documentation sections.
3592// If found they are stored in the global list.
3593static void buildSequenceList(const Entry *root)
3594{
3595 if (!root->name.empty() &&
3596 root->section.isVariable() &&
3597 root->type.find("sequence<")!=DString::npos // it's a sequence
3598 )
3599 {
3600 AUTO_TRACE();
3601 addVariable(root);
3602 }
3603 for (const auto &e : root->children())
3604 if (!e->section.isEnum())
3605 buildSequenceList(e.get());
3606}
3607
3608//----------------------------------------------------------------------
3609// Searches the Entry tree for dictionary documentation sections.
3610// If found they are stored in the global list.
3611static void buildDictionaryList(const Entry *root)
3612{
3613 if (!root->name.empty() &&
3614 root->section.isVariable() &&
3615 root->type.find("dictionary<")!=DString::npos // it's a dictionary
3616 )
3617 {
3618 AUTO_TRACE();
3619 addVariable(root);
3620 }
3621 for (const auto &e : root->children())
3622 if (!e->section.isEnum())
3623 buildDictionaryList(e.get());
3624}
3625
3626//----------------------------------------------------------------------
3627// Searches the Entry tree for Variable documentation sections.
3628// If found they are stored in their class or in the global list.
3629
3630static void buildVarList(const Entry *root)
3631{
3632 //printf("buildVarList(%s) section=%08x\n",qPrint(rootNav->name()),rootNav->section());
3633 int isFuncPtr=-1;
3634 if (!root->name.empty() &&
3635 (root->type.empty() || g_compoundKeywords.find(root->type.str())==g_compoundKeywords.end()) &&
3636 (
3637 (root->section.isVariable() && // it's a variable
3638 root->type.find("typedef ")==DString::npos // and not a typedef
3639 ) ||
3640 (root->section.isFunction() && // or maybe a function pointer variable
3641 (isFuncPtr=findFunctionPtr(root->type.str(),root->lang))!=-1
3642 ) ||
3643 (root->section.isFunction() && // class variable initialized by constructor
3645 )
3646 )
3647 ) // documented variable
3648 {
3649 AUTO_TRACE();
3650 addVariable(root,isFuncPtr);
3651 }
3652 for (const auto &e : root->children())
3653 if (!e->section.isEnum())
3654 buildVarList(e.get());
3655}
3656
3657//----------------------------------------------------------------------
3658// Searches the Entry tree for Interface sections (UNO IDL only).
3659// If found they are stored in their service or in the global list.
3660//
3661
3663 const Entry *root,
3664 ClassDefMutable *cd,
3665 DString const& rname)
3666{
3667 FileDef *fd = root->fileDef();
3668 enum MemberType type = root->section.isExportedInterface() ? MemberType::Interface : MemberType::Service;
3669 DString fileName = root->fileName;
3670 if (fileName.empty() && root->tagInfo())
3671 {
3672 fileName = root->tagInfo()->tagName;
3673 }
3674 auto md = createMemberDef(
3675 fileName, root->startLine, root->startColumn, root->type, rname,
3676 "", "", root->protection, root->virt, root->isStatic, Relationship::Member,
3677 type, ArgumentList(), root->argList, root->metaData);
3678 auto mmd = toMemberDefMutable(md.get());
3679 mmd->setTagInfo(root->tagInfo());
3680 mmd->setMemberClass(cd);
3681 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3682 mmd->setDocsForDefinition(false);
3683 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3684 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3685 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3686 mmd->setMemberSpecifiers(root->spec);
3687 mmd->setVhdlSpecifiers(root->vhdlSpec);
3688 mmd->setMemberGroupId(root->mGrpId);
3689 mmd->setTypeConstraints(root->typeConstr);
3690 mmd->setLanguage(root->lang);
3691 mmd->setBodyDef(fd);
3692 mmd->setFileDef(fd);
3693 mmd->addSectionsToDefinition(root->anchors);
3694 DString const def = root->type + " " + rname;
3695 mmd->setDefinition(def);
3697 mmd->addQualifiers(root->qualifiers);
3698
3699 AUTO_TRACE("Interface member: fileName='{}' type='{}' name='{}' mtype='{}' prot={} virt={} state={} proto={} def='{}'",
3700 fileName,root->type,rname,type,root->protection,root->virt,root->isStatic,root->proto,def);
3701
3702 // add member to the class cd
3703 cd->insertMember(md.get());
3704 // also add the member as a "base" (to get nicer diagrams)
3705 // "optional" interface/service get Protected which turns into dashed line
3706 BaseInfo base(rname,
3707 root->spec.isOptional() ? Protection::Protected : Protection::Public, Specifier::Normal);
3708 TemplateNameMap templateNames;
3709 findClassRelation(root,cd,cd,&base,templateNames,DocumentedOnly,true) ||
3710 findClassRelation(root,cd,cd,&base,templateNames,Undocumented,true);
3711 // add file to list of used files
3712 cd->insertUsedFile(fd);
3713
3714 addMemberToGroups(root,md.get());
3716 root->markAsProcessed();
3717 mmd->setRefItems(root->sli);
3718 mmd->setRequirementReferences(root->rqli);
3719
3720 // add member to the global list of all members
3722 mn->push_back(std::move(md));
3723}
3724
3725static void buildInterfaceAndServiceList(const Entry *root)
3726{
3727 if (root->section.isExportedInterface() || root->section.isIncludedService())
3728 {
3729 AUTO_TRACE("Exported interface/included service: type='{}' scope='{}' name='{}' args='{}'"
3730 " relates='{}' relatesType='{}' file='{}' line={} bodyLine={} #tArgLists={}"
3731 " mGrpId={} spec={} proto={} docFile='{}'",
3732 root->type, root->parent()->name, root->name, root->args,
3733 root->relates, root->relatesType, root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(),
3734 root->mGrpId, root->spec, root->proto, root->docFile);
3735
3736 DString rname = removeRedundantWhiteSpace(root->name);
3737
3738 if (!rname.empty())
3739 {
3740 DString scope = root->parent()->name;
3741 ClassDefMutable *cd = getClassMutable(scope);
3742 ASSERT(cd);
3743 if (cd && ((ClassDef::Interface == cd->compoundType()) ||
3744 (ClassDef::Service == cd->compoundType()) ||
3746 {
3748 }
3749 else
3750 {
3751 ASSERT(false); // was checked by scanner.l
3752 }
3753 }
3754 else if (rname.empty())
3755 {
3756 warn(root->fileName,root->startLine,
3757 "Illegal member name found.");
3758 }
3759 }
3760 // can only have these in IDL anyway
3761 switch (root->lang)
3762 {
3763 case SrcLangExt::Unknown: // fall through (root node always is Unknown)
3764 case SrcLangExt::IDL:
3765 for (const auto &e : root->children()) buildInterfaceAndServiceList(e.get());
3766 break;
3767 default:
3768 return; // nothing to do here
3769 }
3770}
3771
3772
3773//----------------------------------------------------------------------
3774// Searches the Entry tree for Function sections.
3775// If found they are stored in their class or in the global list.
3776
3777static void addMethodToClass(const Entry *root,ClassDefMutable *cd,
3778 const DString &rtype,const DString &rname,const DString &rargs,
3779 bool isFriend,
3780 Protection protection,bool stat,Specifier virt,TypeSpecifier spec,
3781 const DString &relates
3782 )
3783{
3784 FileDef *fd=root->fileDef();
3785
3786 DString type = rtype;
3787 DString args = rargs;
3788
3790 name.stripPrefix("::");
3791
3792 MemberType mtype = MemberType::Function;
3793 if (isFriend) mtype=MemberType::Friend;
3794 else if (root->mtype==MethodTypes::Signal) mtype=MemberType::Signal;
3795 else if (root->mtype==MethodTypes::Slot) mtype=MemberType::Slot;
3796 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType::DCOP;
3797
3798 // strip redundant template specifier for constructors
3799 size_t i = DString::npos;
3800 size_t j = DString::npos;
3801 if ((fd==nullptr || fd->getLanguage()==SrcLangExt::Cpp) &&
3802 !name.startsWith("operator ") && // not operator
3803 (i=name.find('<'))!=DString::npos && // containing <
3804 (j=name.find('>'))!=DString::npos && // or >
3805 (j!=i+2 || name.at(i+1)!='=') // but not the C++20 spaceship operator <=>
3806 )
3807 {
3808 name=name.left(i);
3809 }
3810
3811 DString fileName = root->fileName;
3812 if (fileName.empty() && root->tagInfo())
3813 {
3814 fileName = root->tagInfo()->tagName;
3815 }
3816
3817 //printf("root->name='%s; args='%s' root->argList='%s'\n",
3818 // qPrint(root->name),qPrint(args),qPrint(argListToString(root->argList))
3819 // );
3820
3821 // adding class member
3822 Relationship relationship = relates.empty() ? Relationship::Member :
3823 root->relatesType==RelatesType::MemberOf ? Relationship::Foreign :
3824 Relationship::Related ;
3825 auto md = createMemberDef(
3826 fileName,root->startLine,root->startColumn,
3827 type,name,args,root->exception,
3828 protection,virt,
3829 stat && root->relatesType!=RelatesType::MemberOf,
3830 relationship,
3831 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3832 root->argList, root->metaData);
3833 auto mmd = toMemberDefMutable(md.get());
3834 mmd->setTagInfo(root->tagInfo());
3835 mmd->setMemberClass(cd);
3836 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3837 mmd->setDocsForDefinition(!root->proto);
3838 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3839 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3840 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3841 mmd->setMemberSpecifiers(spec);
3842 mmd->setVhdlSpecifiers(root->vhdlSpec);
3843 mmd->setMemberGroupId(root->mGrpId);
3844 mmd->setTypeConstraints(root->typeConstr);
3845 mmd->setLanguage(root->lang);
3846 mmd->setRequiresClause(root->req);
3847 mmd->setId(root->id);
3848 mmd->setBodyDef(fd);
3849 mmd->setFileDef(fd);
3850 mmd->addSectionsToDefinition(root->anchors);
3851 DString def;
3853 SrcLangExt lang = cd->getLanguage();
3854 DString scopeSeparator=getLanguageSpecificSeparator(lang);
3855 if (scopeSeparator!="::")
3856 {
3857 qualScope = substitute(qualScope,"::",scopeSeparator);
3858 }
3859 if (lang==SrcLangExt::PHP)
3860 {
3861 // for PHP we use Class::method and Namespace\method
3862 scopeSeparator="::";
3863 }
3864 if (!relates.empty() || isFriend || Config_getBool(HIDE_SCOPE_NAMES))
3865 {
3866 if (!type.empty())
3867 {
3868 def=type+" "+name; //+optArgs;
3869 }
3870 else
3871 {
3872 def=name; //+optArgs;
3873 }
3874 }
3875 else
3876 {
3877 if (!type.empty())
3878 {
3879 def=type+" "+qualScope+scopeSeparator+name; //+optArgs;
3880 }
3881 else
3882 {
3883 def=qualScope+scopeSeparator+name; //+optArgs;
3884 }
3885 }
3886 def.stripPrefix("friend ");
3887 mmd->setDefinition(def);
3889 mmd->addQualifiers(root->qualifiers);
3890
3891 AUTO_TRACE("function member: type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
3892 type, qualScope, rname, args, root->proto, def);
3893
3894 // add member to the class cd
3895 cd->insertMember(md.get());
3896 // add file to list of used files
3897 cd->insertUsedFile(fd);
3898
3899 addMemberToGroups(root,md.get());
3901 root->markAsProcessed();
3902 mmd->setRefItems(root->sli);
3903 mmd->setRequirementReferences(root->rqli);
3904
3905 // add member to the global list of all members
3906 //printf("Adding member=%s class=%s\n",qPrint(md->name()),qPrint(cd->name()));
3908 mn->push_back(std::move(md));
3909}
3910
3911//------------------------------------------------------------------------------------------
3912
3913static void addGlobalFunction(const Entry *root,const DString &rname,const DString &sc)
3914{
3915 DString scope = sc;
3916
3917 // new global function
3919 auto md = createMemberDef(
3920 root->fileName,root->startLine,root->startColumn,
3921 root->type,name,root->args,root->exception,
3922 root->protection,root->virt,root->isStatic,Relationship::Member,
3923 MemberType::Function,
3924 !root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3925 root->argList,root->metaData);
3926 auto mmd = toMemberDefMutable(md.get());
3927 mmd->setTagInfo(root->tagInfo());
3928 mmd->setLanguage(root->lang);
3929 mmd->setId(root->id);
3930 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3931 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3932 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3933 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
3934 mmd->setDocsForDefinition(!root->proto);
3935 mmd->setTypeConstraints(root->typeConstr);
3936 //md->setBody(root->body);
3937 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3938 FileDef *fd=root->fileDef();
3939 mmd->setBodyDef(fd);
3940 mmd->addSectionsToDefinition(root->anchors);
3941 mmd->setMemberSpecifiers(root->spec);
3942 mmd->setVhdlSpecifiers(root->vhdlSpec);
3943 mmd->setMemberGroupId(root->mGrpId);
3944 mmd->setRequiresClause(root->req);
3945 mmd->setExplicitExternal(root->explicitExternal,root->fileName,root->startLine,root->startColumn);
3946
3947 NamespaceDefMutable *nd = nullptr;
3948 // see if the function is inside a namespace that was not part of
3949 // the name already (in that case nd should be non-zero already)
3950 if (root->parent()->section.isNamespace())
3951 {
3952 //DString nscope=removeAnonymousScopes(root->parent()->name);
3953 DString nscope=root->parent()->name;
3954 if (!nscope.empty())
3955 {
3956 nd = getResolvedNamespaceMutable(nscope);
3957 }
3958 }
3959 else if (root->parent()->section.isGroupDoc() && !scope.empty())
3960 {
3962 }
3963
3964 if (!scope.empty())
3965 {
3967 if (sep!="::")
3968 {
3969 scope = substitute(scope,"::",sep);
3970 }
3971 scope+=sep;
3972 }
3973
3974 if (Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python) scope = "";
3975 DString def;
3976 //DString optArgs = root->argList.empty() ? DString() : root->args;
3977 if (!root->type.empty())
3978 {
3979 def=root->type+" "+scope+name; //+optArgs;
3980 }
3981 else
3982 {
3983 def=scope+name; //+optArgs;
3984 }
3985 AUTO_TRACE("new non-member function type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
3986 root->type,scope,rname,root->args,root->proto,def);
3987 mmd->setDefinition(def);
3989 mmd->addQualifiers(root->qualifiers);
3990
3991 mmd->setRefItems(root->sli);
3992 mmd->setRequirementReferences(root->rqli);
3993 if (nd && !nd->name().empty() && nd->name().at(0)!='@')
3994 {
3995 // add member to namespace
3996 mmd->setNamespace(nd);
3997 nd->insertMember(md.get());
3998 }
3999 if (fd)
4000 {
4001 // add member to the file (we do this even if we have already
4002 // inserted it into the namespace)
4003 mmd->setFileDef(fd);
4004 fd->insertMember(md.get());
4005 }
4006
4007 addMemberToGroups(root,md.get());
4009 if (root->relatesType == RelatesType::Simple) // if this is a relatesalso command,
4010 // allow find Member to pick it up
4011 {
4012 root->markAsProcessed(); // Otherwise we have finished with this entry.
4013 }
4014
4015 // add member to the list of file members
4017 mn->push_back(std::move(md));
4018}
4019
4020//------------------------------------------------------------------------------------------
4021
4022static void buildFunctionList(const Entry *root)
4023{
4024 if (root->section.isFunction())
4025 {
4026 AUTO_TRACE("member function: type='{}' scope='{}' name='{}' args='{}' relates='{}' relatesType='{}'"
4027 " file='{}' line={} bodyLine={} #tArgLists={} mGrpId={}"
4028 " spec={} proto={} docFile='{}'",
4029 root->type, root->parent()->name, root->name, root->args, root->relates, root->relatesType,
4030 root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(), root->mGrpId,
4031 root->spec, root->proto, root->docFile);
4032
4033 bool isFriend=root->type=="friend" || root->type.find("friend ")!=DString::npos;
4034 DString rname = removeRedundantWhiteSpace(root->name);
4035 //printf("rname=%s\n",qPrint(rname));
4036
4037 DString scope;
4038 int index = computeQualifiedIndex(rname);
4039 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
4040 // grouped members are stored with full scope
4041 {
4042 buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
4043 scope=rname.left(index);
4044 rname=rname.mid(index+2);
4045 }
4046 else
4047 {
4048 scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
4049 }
4050 if (!rname.empty() && scope.find('@')==DString::npos)
4051 {
4052 // check if this function's parent is a class
4053 if (root->lang==SrcLangExt::CSharp)
4054 {
4055 scope=mangleCSharpGenericName(scope);
4056 }
4057 else
4058 {
4059 scope=stripTemplateSpecifiersFromScope(scope,false);
4060 }
4061
4062 FileDef *rfd=root->fileDef();
4063
4064 size_t memIndex=rname.rfind("::");
4065
4067 if (cd && scope+"::"==rname.left(scope.length()+2)) // found A::f inside A
4068 {
4069 // strip scope from name
4070 rname=rname.mid(root->parent()->name.length()+2);
4071 }
4072
4073 bool isMember=false;
4074 if (memIndex!=DString::npos)
4075 {
4076 size_t ts=rname.find('<');
4077 size_t te=rname.find('>');
4078 if (memIndex>0 && (ts==DString::npos || te==DString::npos))
4079 {
4080 // note: the following code was replaced by inMember=true to deal with a
4081 // function rname='X::foo' of class X inside a namespace also called X...
4082 // bug id 548175
4083 //nd = Doxygen::namespaceLinkedMap->find(rname.left(memIndex));
4084 //isMember = nd==nullptr;
4085 //if (nd)
4086 //{
4087 // // strip namespace scope from name
4088 // scope=rname.left(memIndex);
4089 // rname=rname.mid(memIndex+2);
4090 //}
4091 isMember = true;
4092 }
4093 else
4094 {
4095 isMember=memIndex<ts || memIndex>te;
4096 }
4097 }
4098
4099 if (!root->parent()->name.empty() && root->parent()->section.isCompound() && cd)
4100 {
4101 AUTO_TRACE_ADD("member '{}' of class '{}'", rname,cd->name());
4102 addMethodToClass(root,cd,root->type,rname,root->args,isFriend,
4103 root->protection,root->isStatic,root->virt,root->spec,root->relates);
4104 }
4105 else if (root->parent()->section.isObjcImpl() && cd)
4106 {
4107 const MemberDef *md = cd->getMemberByName(rname);
4108 if (md)
4109 {
4110 MemberDefMutable *mdm = toMemberDefMutable(const_cast<MemberDef*>(md));
4111 if (mdm)
4112 {
4113 mdm->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
4114 mdm->setBodyDef(root->fileDef());
4115 }
4116 }
4117 }
4118 else if (!root->parent()->section.isCompound() && !root->parent()->section.isObjcImpl() &&
4119 !isMember &&
4120 (root->relates.empty() || root->relatesType==RelatesType::Duplicate) &&
4121 !root->type.startsWith("extern ") && !root->type.startsWith("typedef ")
4122 )
4123 // no member => unrelated function
4124 {
4125 /* check the uniqueness of the function name in the file.
4126 * A file could contain a function prototype and a function definition
4127 * or even multiple function prototypes.
4128 */
4129 bool found=false;
4130 MemberDef *md_found=nullptr;
4132 if (mn)
4133 {
4134 AUTO_TRACE_ADD("function '{}' already found",rname);
4135 for (const auto &imd : *mn)
4136 {
4137 MemberDefMutable *md = toMemberDefMutable(imd.get());
4138 if (md)
4139 {
4140 const NamespaceDef *mnd = md->getNamespaceDef();
4141 NamespaceDef *rnd = nullptr;
4142 //printf("root namespace=%s\n",qPrint(rootNav->parent()->name()));
4143 DString fullScope = scope;
4144 DString parentScope = root->parent()->name;
4145 if (!parentScope.empty() && !leftScopeMatch(parentScope,scope))
4146 {
4147 if (!scope.empty()) fullScope.prepend("::");
4148 fullScope.prepend(parentScope);
4149 }
4150 //printf("fullScope=%s\n",qPrint(fullScope));
4151 rnd = getResolvedNamespace(fullScope);
4152 const FileDef *mfd = md->getFileDef();
4153 DString nsName,rnsName;
4154 if (mnd) nsName = mnd->name();
4155 if (rnd) rnsName = rnd->name();
4156 //printf("matching arguments for %s%s %s%s\n",
4157 // qPrint(md->name()),md->argsString(),qPrint(rname),qPrint(argListToString(root->argList)));
4158 const ArgumentList &mdAl = md->argumentList();
4159 const ArgumentList &mdTempl = md->templateArguments();
4160
4161 // in case of template functions, we need to check if the
4162 // functions have the same number of template parameters
4163 bool sameTemplateArgs = true;
4164 bool matchingReturnTypes = true;
4165 bool sameRequiresClause = true;
4166 if (!mdTempl.empty() && !root->tArgLists.empty())
4167 {
4168 sameTemplateArgs = matchTemplateArguments(mdTempl,root->tArgLists.back());
4169 if (md->typeString()!=removeRedundantWhiteSpace(root->type))
4170 {
4171 matchingReturnTypes = false;
4172 }
4173 if (md->requiresClause()!=root->req)
4174 {
4175 sameRequiresClause = false;
4176 }
4177 }
4178 else if (!mdTempl.empty() || !root->tArgLists.empty())
4179 { // if one has template parameters and the other doesn't then that also counts as a
4180 // difference
4181 sameTemplateArgs = false;
4182 }
4183
4184 bool staticsInDifferentFiles =
4185 root->isStatic && md->isStatic() && root->fileName!=md->getDefFileName();
4186
4187 if (sameTemplateArgs &&
4188 matchingReturnTypes &&
4189 sameRequiresClause &&
4190 !staticsInDifferentFiles &&
4191 matchArguments2(md->getOuterScope(),mfd,md->typeString(),&mdAl,
4192 rnd ? rnd : Doxygen::globalScope,rfd,root->type,&root->argList,
4193 false,root->lang)
4194 )
4195 {
4196 GroupDef *gd=nullptr;
4197 if (!root->groups.empty() && !root->groups.front().groupname.empty())
4198 {
4199 gd = Doxygen::groupLinkedMap->find(root->groups.front().groupname);
4200 }
4201 //printf("match!\n");
4202 //printf("mnd=%p rnd=%p nsName=%s rnsName=%s\n",mnd,rnd,qPrint(nsName),qPrint(rnsName));
4203 // see if we need to create a new member
4204 found=(mnd && rnd && nsName==rnsName) || // members are in the same namespace
4205 ((mnd==nullptr && rnd==nullptr && mfd!=nullptr && // no external reference and
4206 mfd->absFilePath()==root->fileName // prototype in the same file
4207 )
4208 );
4209 // otherwise, allow a duplicate global member with the same argument list
4210 if (!found && gd && gd==md->getGroupDef() && nsName==rnsName)
4211 {
4212 // member is already in the group, so we don't want to add it again.
4213 found=true;
4214 }
4215
4216 AUTO_TRACE_ADD("combining function with prototype found={} in namespace '{}'",found,nsName);
4217
4218 if (found)
4219 {
4220 // merge argument lists
4221 ArgumentList mergedArgList = root->argList;
4222 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.empty());
4223 // merge documentation
4224 if (md->documentation().empty() && !root->doc.empty())
4225 {
4226 if (root->proto)
4227 {
4229 }
4230 else
4231 {
4233 }
4234 }
4235
4236 md->setDocumentation(root->doc,root->docFile,root->docLine);
4238 md->setDocsForDefinition(!root->proto);
4239 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
4240 {
4241 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
4242 md->setBodyDef(rfd);
4243 }
4244
4245 if (md->briefDescription().empty() && !root->brief.empty())
4246 {
4247 md->setArgsString(root->args);
4248 }
4249 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
4250
4252
4254 md->addQualifiers(root->qualifiers);
4255
4256 // merge ingroup specifiers
4257 if (md->getGroupDef()==nullptr && !root->groups.empty())
4258 {
4259 addMemberToGroups(root,md);
4260 }
4261 else if (md->getGroupDef()!=nullptr && root->groups.empty())
4262 {
4263 //printf("existing member is grouped, new member not\n");
4264 }
4265 else if (md->getGroupDef()!=nullptr && !root->groups.empty())
4266 {
4267 //printf("both members are grouped\n");
4268 }
4270
4271 // if md is a declaration and root is the corresponding
4272 // definition, then turn md into a definition.
4273 if (md->isPrototype() && !root->proto)
4274 {
4275 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
4276 md->setPrototype(false,root->fileName,root->startLine,root->startColumn);
4277 }
4278 // if md is already the definition, then add the declaration info
4279 else if (!md->isPrototype() && root->proto)
4280 {
4281 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
4282 }
4283 }
4284 }
4285 }
4286 if (found)
4287 {
4288 md_found = md;
4289 break;
4290 }
4291 }
4292 }
4293 if (!found) /* global function is unique with respect to the file */
4294 {
4295 addGlobalFunction(root,rname,scope);
4296 }
4297 else
4298 {
4299 FileDef *fd=root->fileDef();
4300 if (fd)
4301 {
4302 // add member to the file (we do this even if we have already
4303 // inserted it into the namespace)
4304 fd->insertMember(md_found);
4305 }
4306 }
4307
4308 AUTO_TRACE_ADD("unrelated function type='{}' name='{}' args='{}'",root->type,rname,root->args);
4309 }
4310 else
4311 {
4312 AUTO_TRACE_ADD("function '{}' is not processed",rname);
4313 }
4314 }
4315 else if (rname.empty())
4316 {
4317 warn(root->fileName,root->startLine,
4318 "Illegal member name found."
4319 );
4320 }
4321 }
4322 for (const auto &e : root->children()) buildFunctionList(e.get());
4323}
4324
4325//----------------------------------------------------------------------
4326
4327static void findFriends()
4328{
4329 AUTO_TRACE();
4330 for (const auto &fn : *Doxygen::functionNameLinkedMap) // for each global function name
4331 {
4332 MemberName *mn = Doxygen::memberNameLinkedMap->find(fn->memberName());
4333 if (mn)
4334 { // there are members with the same name
4335 // for each function with that name
4336 for (const auto &ifmd : *fn)
4337 {
4338 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
4339 // for each member with that name
4340 for (const auto &immd : *mn)
4341 {
4342 MemberDefMutable *mmd = toMemberDefMutable(immd.get());
4343 //printf("Checking for matching arguments
4344 // mmd->isRelated()=%d mmd->isFriend()=%d mmd->isFunction()=%d\n",
4345 // mmd->isRelated(),mmd->isFriend(),mmd->isFunction());
4346 if (fmd && mmd &&
4347 (mmd->isFriend() || (mmd->isRelated() && mmd->isFunction())) &&
4348 matchArguments2(mmd->getOuterScope(), mmd->getFileDef(), mmd->typeString(), &mmd->argumentList(),
4349 fmd->getOuterScope(), fmd->getFileDef(), fmd->typeString(), &fmd->argumentList(),
4350 true,mmd->getLanguage()
4351 )
4352
4353 ) // if the member is related and the arguments match then the
4354 // function is actually a friend.
4355 {
4356 AUTO_TRACE_ADD("Merging related global and member '{}' isFriend={} isRelated={} isFunction={}",
4357 mmd->name(),mmd->isFriend(),mmd->isRelated(),mmd->isFunction());
4358 const ArgumentList &mmdAl = mmd->argumentList();
4359 const ArgumentList &fmdAl = fmd->argumentList();
4360 mergeArguments(const_cast<ArgumentList&>(fmdAl),const_cast<ArgumentList&>(mmdAl));
4361
4362 // reset argument lists to add missing default parameters
4363 DString mmdAlStr = argListToString(mmdAl);
4364 DString fmdAlStr = argListToString(fmdAl);
4365 mmd->setArgsString(mmdAlStr);
4366 fmd->setArgsString(fmdAlStr);
4367 mmd->moveDeclArgumentList(std::make_unique<ArgumentList>(mmdAl));
4368 fmd->moveDeclArgumentList(std::make_unique<ArgumentList>(fmdAl));
4369 AUTO_TRACE_ADD("friend args='{}' member args='{}'",argListToString(fmd->argumentList()),argListToString(mmd->argumentList()));
4370
4371 if (!fmd->documentation().empty())
4372 {
4373 mmd->setDocumentation(fmd->documentation(),fmd->docFile(),fmd->docLine());
4374 }
4375 else if (!mmd->documentation().empty())
4376 {
4377 fmd->setDocumentation(mmd->documentation(),mmd->docFile(),mmd->docLine());
4378 }
4379 if (mmd->briefDescription().empty() && !fmd->briefDescription().empty())
4380 {
4381 mmd->setBriefDescription(fmd->briefDescription(),fmd->briefFile(),fmd->briefLine());
4382 }
4383 else if (!mmd->briefDescription().empty() && !fmd->briefDescription().empty())
4384 {
4385 fmd->setBriefDescription(mmd->briefDescription(),mmd->briefFile(),mmd->briefLine());
4386 }
4387 if (!fmd->inbodyDocumentation().empty())
4388 {
4390 }
4391 else if (!mmd->inbodyDocumentation().empty())
4392 {
4394 }
4395 //printf("body mmd %d fmd %d\n",mmd->getStartBodyLine(),fmd->getStartBodyLine());
4396 if (mmd->getStartBodyLine()==-1 && fmd->getStartBodyLine()!=-1)
4397 {
4398 mmd->setBodySegment(fmd->getDefLine(),fmd->getStartBodyLine(),fmd->getEndBodyLine());
4399 mmd->setBodyDef(fmd->getBodyDef());
4400 //mmd->setBodyMember(fmd);
4401 }
4402 else if (mmd->getStartBodyLine()!=-1 && fmd->getStartBodyLine()==-1)
4403 {
4404 fmd->setBodySegment(mmd->getDefLine(),mmd->getStartBodyLine(),mmd->getEndBodyLine());
4405 fmd->setBodyDef(mmd->getBodyDef());
4406 //fmd->setBodyMember(mmd);
4407 }
4409
4411
4412 mmd->addQualifiers(fmd->getQualifiers());
4413 fmd->addQualifiers(mmd->getQualifiers());
4414
4415 }
4416 }
4417 }
4418 }
4419 }
4420}
4421
4422//----------------------------------------------------------------------
4423
4425{
4426 AUTO_TRACE();
4427
4428 // find matching function declaration and definitions.
4429 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4430 {
4431 //printf("memberName=%s count=%zu\n",qPrint(mn->memberName()),mn->size());
4432 /* find a matching function declaration and definition for this function */
4433 for (const auto &imdec : *mn)
4434 {
4435 MemberDefMutable *mdec = toMemberDefMutable(imdec.get());
4436 if (mdec &&
4437 (mdec->isPrototype() ||
4438 (mdec->isVariable() && mdec->isExternal())
4439 ))
4440 {
4441 for (const auto &imdef : *mn)
4442 {
4443 MemberDefMutable *mdef = toMemberDefMutable(imdef.get());
4444 if (mdef && mdec!=mdef &&
4445 mdec->getNamespaceDef()==mdef->getNamespaceDef())
4446 {
4448 }
4449 }
4450 }
4451 }
4452 }
4453}
4454
4455//----------------------------------------------------------------------
4456
4458{
4459 AUTO_TRACE();
4460 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4461 {
4462 MemberDefMutable *mdef=nullptr,*mdec=nullptr;
4463 /* find a matching function declaration and definition for this function */
4464 for (const auto &imd : *mn)
4465 {
4466 MemberDefMutable *md = toMemberDefMutable(imd.get());
4467 if (md)
4468 {
4469 if (md->isPrototype())
4470 mdec=md;
4471 else if (md->isVariable() && md->isExternal())
4472 mdec=md;
4473
4474 if (md->isFunction() && !md->isStatic() && !md->isPrototype())
4475 mdef=md;
4476 else if (md->isVariable() && !md->isExternal() && !md->isStatic())
4477 mdef=md;
4478 }
4479
4480 if (mdef && mdec) break;
4481 }
4482 if (mdef && mdec)
4483 {
4484 const ArgumentList &mdefAl = mdef->argumentList();
4485 const ArgumentList &mdecAl = mdec->argumentList();
4486 if (
4487 matchArguments2(mdef->getOuterScope(),mdef->getFileDef(),mdef->typeString(),const_cast<ArgumentList*>(&mdefAl),
4488 mdec->getOuterScope(),mdec->getFileDef(),mdec->typeString(),const_cast<ArgumentList*>(&mdecAl),
4489 true,mdef->getLanguage()
4490 )
4491 ) /* match found */
4492 {
4493 AUTO_TRACE_ADD("merging references for mdec={} mdef={}",mdec->name(),mdef->name());
4494 mdef->mergeReferences(mdec);
4495 mdec->mergeReferences(mdef);
4496 mdef->mergeReferencedBy(mdec);
4497 mdec->mergeReferencedBy(mdef);
4498 }
4499 }
4500 }
4501}
4502
4503//----------------------------------------------------------------------
4504
4506{
4507 AUTO_TRACE();
4508 // find match between function declaration and definition for
4509 // related functions
4510 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4511 {
4512 /* find a matching function declaration and definition for this function */
4513 // for each global function
4514 for (const auto &imd : *mn)
4515 {
4516 MemberDefMutable *md = toMemberDefMutable(imd.get());
4517 if (md)
4518 {
4519 //printf(" Function '%s'\n",qPrint(md->name()));
4521 if (rmn) // check if there is a member with the same name
4522 {
4523 //printf(" Member name found\n");
4524 // for each member with the same name
4525 for (const auto &irmd : *rmn)
4526 {
4527 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
4528 //printf(" Member found: related='%d'\n",rmd->isRelated());
4529 if (rmd &&
4530 (rmd->isRelated() || rmd->isForeign()) && // related function
4531 matchArguments2( md->getOuterScope(), md->getFileDef(), md->typeString(), &md->argumentList(),
4532 rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmd->argumentList(),
4533 true,md->getLanguage()
4534 )
4535 )
4536 {
4537 AUTO_TRACE_ADD("Found related member '{}'",md->name());
4538 if (rmd->relatedAlso())
4539 md->setRelatedAlso(rmd->relatedAlso());
4540 else if (rmd->isForeign())
4541 md->makeForeign();
4542 else
4543 md->makeRelated();
4544 }
4545 }
4546 }
4547 }
4548 }
4549 }
4550}
4551
4552//----------------------------------------------------------------------
4553
4555{
4556 AUTO_TRACE();
4557 for (const auto &[qualifiedName,bodyInfo] : Doxygen::staticInitMap)
4558 {
4559 size_t i=qualifiedName.rfind("::");
4560 if (i!=std::string::npos)
4561 {
4562 DString scope = qualifiedName.substr(0,i);
4563 DString name = qualifiedName.substr(i+2);
4565 if (mn)
4566 {
4567 for (const auto &imd : *mn)
4568 {
4569 MemberDefMutable *md = toMemberDefMutable(imd.get());
4570 if (md && md->qualifiedName().str()==qualifiedName && md->isVariable())
4571 {
4572 AUTO_TRACE_ADD("found static member {} body [{}..{}]\n",
4573 md->qualifiedName(),bodyInfo.startLine,bodyInfo.endLine);
4574 md->setBodySegment(bodyInfo.defLine,
4575 bodyInfo.startLine,
4576 bodyInfo.endLine);
4577 }
4578 }
4579 }
4580 }
4581 }
4582}
4583
4584//----------------------------------------------------------------------
4585
4586/*! make a dictionary of all template arguments of class cd
4587 * that are part of the base class name.
4588 * Example: A template class A with template arguments <R,S,T>
4589 * that inherits from B<T,T,S> will have T and S in the dictionary.
4590 */
4591static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments,const std::string &name)
4592{
4593 std::map<std::string,int> templateNames;
4594 int count=0;
4595 for (const Argument &arg : templateArguments)
4596 {
4597 static const reg::Ex re(R"(\a[\w:]*)");
4598 reg::Iterator it(name,re);
4600 for (; it!=end ; ++it)
4601 {
4602 const auto &match = *it;
4603 std::string n = match.str();
4604 if (n==arg.name.str())
4605 {
4606 if (templateNames.find(n)==templateNames.end())
4607 {
4608 templateNames.emplace(n,count);
4609 }
4610 }
4611 }
4612 }
4613 return templateNames;
4614}
4615
4616/*! Searches a class from within \a context and \a cd and returns its
4617 * definition if found (otherwise nullptr is returned).
4618 */
4620{
4621 ClassDef *result=nullptr;
4622 if (cd==nullptr)
4623 {
4624 return result;
4625 }
4626 FileDef *fd=cd->getFileDef();
4627 SymbolResolver resolver(fd);
4628 if (context && cd!=context)
4629 {
4630 result = const_cast<ClassDef*>(resolver.resolveClass(context,name,true,true));
4631 }
4632 //printf("1. result=%p\n",result);
4633 if (result==nullptr)
4634 {
4635 result = const_cast<ClassDef*>(resolver.resolveClass(cd,name,true,true));
4636 }
4637 //printf("2. result=%p\n",result);
4638 if (result==nullptr) // try direct class, needed for namespaced classes imported via tag files (see bug624095)
4639 {
4640 result = getClass(name);
4641 }
4642 //printf("3. result=%p\n",result);
4643 //printf("** Trying to find %s within context %s class %s result=%s lookup=%p\n",
4644 // qPrint(name),
4645 // context ? qPrint(context->name()) : "<none>",
4646 // cd ? qPrint(cd->name()) : "<none>",
4647 // result ? qPrint(result->name()) : "<none>",
4648 // Doxygen::classLinkedMap->find(name)
4649 // );
4650 return result;
4651}
4652
4653
4654static void findUsedClassesForClass(const Entry *root,
4655 Definition *context,
4656 ClassDefMutable *masterCd,
4657 ClassDefMutable *instanceCd,
4658 bool isArtificial,
4659 const ArgumentList *actualArgs = nullptr,
4660 const TemplateNameMap &templateNames = TemplateNameMap()
4661 )
4662{
4663 AUTO_TRACE();
4664 const ArgumentList &formalArgs = masterCd->templateArguments();
4665 for (auto &mni : masterCd->memberNameInfoLinkedMap())
4666 {
4667 for (auto &mi : *mni)
4668 {
4669 const MemberDef *md=mi->memberDef();
4670 if (md->isVariable() || md->isObjCProperty()) // for each member variable in this class
4671 {
4672 AUTO_TRACE_ADD("Found variable '{}' in class '{}'",md->name(),masterCd->name());
4673 DString type = normalizeNonTemplateArgumentsInString(md->typeString(),masterCd,formalArgs);
4674 DString typedefValue = md->getLanguage()==SrcLangExt::Java ? type : resolveTypeDef(masterCd,type);
4675 if (!typedefValue.empty())
4676 {
4677 type = typedefValue;
4678 }
4679 int pos=0;
4680 DString usedClassName;
4681 DString templSpec;
4682 bool found=false;
4683 // the type can contain template variables, replace them if present
4684 type = substituteTemplateArgumentsInString(type,formalArgs,actualArgs);
4685
4686 //printf(" template substitution gives=%s\n",qPrint(type));
4687 while (!found && extractClassNameFromType(type,pos,usedClassName,templSpec,root->lang)!=-1)
4688 {
4689 // find the type (if any) that matches usedClassName
4690 SymbolResolver resolver(masterCd->getFileDef());
4691 const ClassDefMutable *typeCd = resolver.resolveClassMutable(masterCd,usedClassName,false,true);
4692 //printf("====> usedClassName=%s -> typeCd=%s\n",
4693 // qPrint(usedClassName),typeCd?qPrint(typeCd->name()):"<none>");
4694 if (typeCd)
4695 {
4696 usedClassName = typeCd->name();
4697 }
4698
4699 // replace any namespace aliases
4700 replaceNamespaceAliases(usedClassName);
4701 // add any template arguments to the class
4702 DString usedName = removeRedundantWhiteSpace(usedClassName+templSpec);
4703 //printf(" usedName=%s usedClassName=%s templSpec=%s\n",qPrint(usedName),qPrint(usedClassName),qPrint(templSpec));
4704
4705 TemplateNameMap formTemplateNames;
4706 if (templateNames.empty())
4707 {
4708 formTemplateNames = getTemplateArgumentsInName(formalArgs,usedName.str());
4709 }
4710 BaseInfo bi(usedName,Protection::Public,Specifier::Normal);
4711 findClassRelation(root,context,instanceCd,&bi,formTemplateNames,TemplateInstances,isArtificial);
4712
4713 for (const Argument &arg : masterCd->templateArguments())
4714 {
4715 if (arg.name==usedName) // type is a template argument
4716 {
4717 ClassDef *usedCd = Doxygen::hiddenClassLinkedMap->find(usedName);
4718 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4719 if (usedCd==nullptr)
4720 {
4721 usedCdm = toClassDefMutable(
4722 Doxygen::hiddenClassLinkedMap->add(usedName,
4724 masterCd->getDefFileName(),masterCd->getDefLine(),
4725 masterCd->getDefColumn(),
4726 usedName,
4727 ClassDef::Class)));
4728 if (usedCdm)
4729 {
4730 //printf("making %s a template argument!!!\n",qPrint(usedCd->name()));
4731 usedCdm->makeTemplateArgument();
4732 usedCdm->setUsedOnly(true);
4733 usedCdm->setLanguage(masterCd->getLanguage());
4734 usedCd = usedCdm;
4735 }
4736 }
4737 if (usedCd)
4738 {
4739 found=true;
4740 AUTO_TRACE_ADD("case 1: adding used class '{}'", usedCd->name());
4741 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4742 if (usedCdm)
4743 {
4744 if (isArtificial) usedCdm->setArtificial(true);
4745 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4746 }
4747 }
4748 }
4749 }
4750
4751 if (!found)
4752 {
4753 ClassDef *usedCd=findClassWithinClassContext(context,masterCd,usedName);
4754 //printf("Looking for used class %s: result=%s master=%s\n",
4755 // qPrint(usedName),usedCd?qPrint(usedCd->name()):"<none>",masterCd?qPrint(masterCd->name()):"<none>");
4756
4757 if (usedCd)
4758 {
4759 found=true;
4760 AUTO_TRACE_ADD("case 2: adding used class '{}'", usedCd->name());
4761 instanceCd->addUsedClass(usedCd,md->name(),md->protection()); // class exists
4762 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4763 if (usedCdm)
4764 {
4765 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4766 }
4767 }
4768 }
4769 }
4770 if (!found && !type.empty()) // used class is not documented in any scope
4771 {
4773 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4774 if (usedCd==nullptr && !Config_getBool(HIDE_UNDOC_RELATIONS))
4775 {
4776 if (type.endsWith("(*") || type.endsWith("(^")) // type is a function pointer
4777 {
4778 type+=md->argsString();
4779 }
4780 AUTO_TRACE_ADD("New undocumented used class '{}'", type);
4781 usedCdm = toClassDefMutable(
4784 masterCd->getDefFileName(),masterCd->getDefLine(),
4785 masterCd->getDefColumn(),
4786 type,ClassDef::Class)));
4787 if (usedCdm)
4788 {
4789 usedCdm->setUsedOnly(true);
4790 usedCdm->setLanguage(masterCd->getLanguage());
4791 usedCd = usedCdm;
4792 }
4793 }
4794 if (usedCd)
4795 {
4796 AUTO_TRACE_ADD("case 3: adding used class '{}'", usedCd->name());
4797 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4798 if (usedCdm)
4799 {
4800 if (isArtificial) usedCdm->setArtificial(true);
4801 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4802 }
4803 }
4804 }
4805 }
4806 }
4807 }
4808}
4809
4811 const Entry *root,
4812 Definition *context,
4813 ClassDefMutable *masterCd,
4814 ClassDefMutable *instanceCd,
4816 bool isArtificial,
4817 const ArgumentList *actualArgs = nullptr,
4818 const TemplateNameMap &templateNames=TemplateNameMap()
4819 )
4820{
4821 AUTO_TRACE("name={}",root->name);
4822 // The base class could ofcouse also be a non-nested class
4823 const ArgumentList &formalArgs = masterCd->templateArguments();
4824 for (const BaseInfo &bi : root->extends)
4825 {
4826 //printf("masterCd=%s bi.name='%s' #actualArgs=%d\n",
4827 // qPrint(masterCd->localName()),qPrint(bi.name),actualArgs ? (int)actualArgs->size() : -1);
4828 TemplateNameMap formTemplateNames;
4829 if (templateNames.empty())
4830 {
4831 formTemplateNames = getTemplateArgumentsInName(formalArgs,bi.name.str());
4832 }
4833 BaseInfo tbi = bi;
4834 tbi.name = substituteTemplateArgumentsInString(bi.name,formalArgs,actualArgs);
4835 //printf("masterCd=%p instanceCd=%p bi->name=%s tbi.name=%s\n",(void*)masterCd,(void*)instanceCd,qPrint(bi.name),qPrint(tbi.name));
4836
4837 if (mode==DocumentedOnly)
4838 {
4839 // find a documented base class in the correct scope
4840 if (!findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,DocumentedOnly,isArtificial))
4841 {
4842 // 1.8.2: decided to show inheritance relations even if not documented,
4843 // we do make them artificial, so they do not appear in the index
4844 //if (!Config_getBool(HIDE_UNDOC_RELATIONS))
4845 bool b = Config_getBool(HIDE_UNDOC_RELATIONS) ? true : isArtificial;
4846 //{
4847 // no documented base class -> try to find an undocumented one
4848 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,Undocumented,b);
4849 //}
4850 }
4851 }
4852 else if (mode==TemplateInstances)
4853 {
4854 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,TemplateInstances,isArtificial);
4855 }
4856 }
4857}
4858
4859//----------------------------------------------------------------------
4860
4861static void findTemplateInstanceRelation(const Entry *root,
4862 Definition *context,
4863 ClassDefMutable *templateClass,const DString &templSpec,
4864 const TemplateNameMap &templateNames,
4865 bool isArtificial)
4866{
4867 AUTO_TRACE("Derived from template '{}' with parameters '{}' isArtificial={}",
4868 templateClass->name(),templSpec,isArtificial);
4869
4870 DString tempArgsStr = tempArgListToString(templateClass->templateArguments(),root->lang,false);
4871 bool existingClass = templSpec==tempArgsStr;
4872 if (existingClass) return; // avoid recursion
4873
4874 bool freshInstance=false;
4875 ClassDefMutable *instanceClass = toClassDefMutable(
4876 templateClass->insertTemplateInstance(
4877 root->fileName,root->startLine,root->startColumn,templSpec,freshInstance));
4878 if (instanceClass)
4879 {
4880 if (freshInstance)
4881 {
4882 instanceClass->setArtificial(true);
4883 instanceClass->setLanguage(root->lang);
4884
4885 AUTO_TRACE_ADD("found fresh instance '{}'",instanceClass->name());
4886 instanceClass->setTemplateBaseClassNames(templateNames);
4887
4888 // search for new template instances caused by base classes of
4889 // instanceClass
4890 auto it_pair = g_classEntries.equal_range(templateClass->name().str());
4891 for (auto it=it_pair.first ; it!=it_pair.second ; ++it)
4892 {
4893 const Entry *templateRoot = it->second;
4894 AUTO_TRACE_ADD("template root found '{}' templSpec='{}'",templateRoot->name,templSpec);
4895 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(root->lang,templSpec);
4896 findBaseClassesForClass(templateRoot,context,templateClass,instanceClass,
4897 TemplateInstances,isArtificial,templArgs.get(),templateNames);
4898
4899 findUsedClassesForClass(templateRoot,context,templateClass,instanceClass,
4900 isArtificial,templArgs.get(),templateNames);
4901 }
4902 }
4903 else
4904 {
4905 AUTO_TRACE_ADD("instance already exists");
4906 }
4907 }
4908}
4909
4910//----------------------------------------------------------------------
4911
4912static void resolveTemplateInstanceInType(const Entry *root,const Definition *scope,const MemberDef *md)
4913{
4914 // For a statement like 'using X = T<A>', add a template instance 'T<A>' as a symbol, so it can
4915 // be used to match arguments (see issue #11111)
4916 AUTO_TRACE();
4917 DString ttype = md->typeString();
4918 ttype.stripPrefix("typedef ");
4919 if (size_t ti=ttype.find('<'); ti!=DString::npos)
4920 {
4921 DString templateClassName = ttype.left(ti);
4922 SymbolResolver resolver(root->fileDef());
4923 ClassDefMutable *baseClass = resolver.resolveClassMutable(scope ? scope : Doxygen::globalScope,
4924 templateClassName, true, true);
4925 AUTO_TRACE_ADD("templateClassName={} baseClass={}",templateClassName,baseClass?baseClass->name():"<none>");
4926 if (baseClass)
4927 {
4928 const ArgumentList &tl = baseClass->templateArguments();
4929 TemplateNameMap templateNames = getTemplateArgumentsInName(tl,templateClassName.str());
4931 baseClass,
4932 ttype.mid(ti),
4933 templateNames,
4934 baseClass->isArtificial());
4935 }
4936 }
4937}
4938
4939//----------------------------------------------------------------------
4940
4941static bool isRecursiveBaseClass(const DString &scope,const DString &name)
4942{
4943 DString n=name;
4944 if (size_t index=n.find('<'); index!=DString::npos)
4945 {
4946 n=n.left(index);
4947 }
4948 bool result = rightScopeMatch(scope,n);
4949 return result;
4950}
4951
4953{
4954 if (name.empty()) return 0;
4955 int l = static_cast<int>(name.length());
4956 if (name[l-1]=='>') // search backward to find the matching <, allowing nested <...> and strings.
4957 {
4958 int count=1;
4959 int i=l-2;
4960 char insideQuote=0;
4961 while (count>0 && i>=0)
4962 {
4963 char c = name[i--];
4964 switch (c)
4965 {
4966 case '>': if (!insideQuote) count++; break;
4967 case '<': if (!insideQuote) count--; break;
4968 case '\'': if (!insideQuote) insideQuote=c;
4969 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
4970 break;
4971 case '"': if (!insideQuote) insideQuote=c;
4972 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
4973 break;
4974 default: break;
4975 }
4976 }
4977 if (i>=0) l=i+1;
4978 }
4979 return l;
4980}
4981
4983 const Entry *root,
4984 Definition *context,
4985 ClassDefMutable *cd,
4986 const BaseInfo *bi,
4987 const TemplateNameMap &templateNames,
4989 bool isArtificial
4990 )
4991{
4992 AUTO_TRACE("name={} base={} isArtificial={} mode={}",cd->name(),bi->name,isArtificial,(int)mode);
4993
4994 DString biName=bi->name;
4995 bool explicitGlobalScope=false;
4996 if (biName.startsWith("::")) // explicit global scope
4997 {
4998 biName=biName.mid(2);
4999 explicitGlobalScope=true;
5000 }
5001
5002 Entry *parentNode=root->parent();
5003 bool lastParent=false;
5004 do // for each parent scope, starting with the largest scope
5005 // (in case of nested classes)
5006 {
5007 DString scopeName= parentNode ? parentNode->name : DString();
5008 int scopeOffset=explicitGlobalScope ? 0 : static_cast<int>(scopeName.length());
5009 do // try all parent scope prefixes, starting with the largest scope
5010 {
5011 //printf("scopePrefix='%s' biName='%s'\n",
5012 // qPrint(scopeName.left(scopeOffset)),qPrint(biName));
5013
5014 DString baseClassName=biName;
5015 if (scopeOffset>0)
5016 {
5017 baseClassName.prepend(scopeName.left(scopeOffset)+"::");
5018 }
5019 if (root->lang==SrcLangExt::CSharp)
5020 {
5021 baseClassName = mangleCSharpGenericName(baseClassName);
5022 }
5023 AUTO_TRACE_ADD("cd='{}' baseClassName='{}'",cd->name(),baseClassName);
5024 SymbolResolver resolver(cd->getFileDef());
5025 ClassDefMutable *baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
5026 baseClassName,
5027 mode==Undocumented,
5028 true
5029 );
5030 const MemberDef *baseClassTypeDef = resolver.getTypedef();
5031 DString templSpec = resolver.getTemplateSpec();
5032 //printf("baseClassName=%s baseClass=%p cd=%p explicitGlobalScope=%d\n",
5033 // qPrint(baseClassName),baseClass,cd,explicitGlobalScope);
5034 //printf(" scope='%s' baseClassName='%s' baseClass=%s templSpec=%s\n",
5035 // cd ? qPrint(cd->name()):"<none>",
5036 // qPrint(baseClassName),
5037 // baseClass?qPrint(baseClass->name()):"<none>",
5038 // qPrint(templSpec)
5039 // );
5040 //if (baseClassName.left(root->name.length())!=root->name ||
5041 // baseClassName.at(root->name.length())!='<'
5042 // ) // Check for base class with the same name.
5043 // // If found then look in the outer scope for a match
5044 // // and prevent recursion.
5045 if (!isRecursiveBaseClass(root->name,baseClassName)
5046 || explicitGlobalScope
5047 // sadly isRecursiveBaseClass always true for UNO IDL ifc/svc members
5048 // (i.e. this is needed for addInterfaceOrServiceToServiceOrSingleton)
5049 || (root->lang==SrcLangExt::IDL &&
5050 (root->section.isExportedInterface() ||
5051 root->section.isIncludedService()))
5052 )
5053 {
5054 AUTO_TRACE_ADD("class relation '{}' inherited/used by '{}' found prot={} virt={} templSpec='{}'",
5055 baseClassName, root->name, bi->prot, bi->virt, templSpec);
5056
5057 int i=findTemplateSpecializationPosition(baseClassName);
5058 size_t si=baseClassName.rfind("::",i);
5059 if (si==DString::npos) si=0;
5060 if (baseClass==nullptr && static_cast<size_t>(i)!=baseClassName.length())
5061 // base class has template specifiers
5062 {
5063 // TODO: here we should try to find the correct template specialization
5064 // but for now, we only look for the unspecialized base class.
5065 int e=findEndOfTemplate(baseClassName,i+1);
5066 //printf("baseClass==0 i=%d e=%d\n",i,e);
5067 if (e!=-1) // end of template was found at e
5068 {
5069 templSpec = removeRedundantWhiteSpace(baseClassName.mid(i,e-i));
5070 baseClassName = baseClassName.left(i)+baseClassName.mid(e);
5071 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
5072 baseClassName,
5073 mode==Undocumented,
5074 true
5075 );
5076 baseClassTypeDef = resolver.getTypedef();
5077 //printf("baseClass=%p -> baseClass=%s templSpec=%s\n",
5078 // baseClass,qPrint(baseClassName),qPrint(templSpec));
5079 }
5080 }
5081 else if (baseClass && !templSpec.empty()) // we have a known class, but also
5082 // know it is a template, so see if
5083 // we can also link to the explicit
5084 // instance (for instance if a class
5085 // derived from a template argument)
5086 {
5087 //printf("baseClass=%s templSpec=%s\n",qPrint(baseClass->name()),qPrint(templSpec));
5088 ClassDefMutable *templClass=getClassMutable(baseClass->name()+templSpec);
5089 if (templClass)
5090 {
5091 // use the template instance instead of the template base.
5092 baseClass = templClass;
5093 templSpec.clear();
5094 }
5095 }
5096
5097 //printf("cd=%p baseClass=%p\n",cd,baseClass);
5098 bool found=baseClass!=nullptr && (baseClass!=cd || mode==TemplateInstances);
5099 AUTO_TRACE_ADD("1. found={}",found);
5100 if (!found && si!=DString::npos)
5101 {
5102 // replace any namespace aliases
5103 replaceNamespaceAliases(baseClassName);
5104 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
5105 baseClassName,
5106 mode==Undocumented,
5107 true
5108 );
5109 baseClassTypeDef = resolver.getTypedef();
5110 found=baseClass!=nullptr && baseClass!=cd;
5111 if (found) templSpec = resolver.getTemplateSpec();
5112 }
5113 AUTO_TRACE_ADD("2. found={}",found);
5114
5115 if (!found)
5116 {
5117 baseClass=toClassDefMutable(findClassWithinClassContext(context,cd,baseClassName));
5118 //printf("findClassWithinClassContext(%s,%s)=%p\n",
5119 // qPrint(cd->name()),qPrint(baseClassName),baseClass);
5120 found = baseClass!=nullptr && baseClass!=cd;
5121
5122 }
5123 AUTO_TRACE_ADD("3. found={}",found);
5124 if (!found)
5125 {
5126 // for PHP the "use A\B as C" construct map class C to A::B, so we lookup
5127 // the class name also in the alias mapping.
5128 auto it = Doxygen::namespaceAliasMap.find(baseClassName.str());
5129 if (it!=Doxygen::namespaceAliasMap.end()) // see if it is indeed a class.
5130 {
5131 baseClass=getClassMutable(it->second.alias);
5132 found = baseClass!=nullptr && baseClass!=cd;
5133 }
5134 }
5135 bool isATemplateArgument = templateNames.find(biName.str())!=templateNames.end();
5136
5137 AUTO_TRACE_ADD("4. found={}",found);
5138 if (found)
5139 {
5140 AUTO_TRACE_ADD("Documented base class '{}' templSpec='{}'",biName,templSpec);
5141 // add base class to this class
5142
5143 // if templSpec is not empty then we should "instantiate"
5144 // the template baseClass. A new ClassDef should be created
5145 // to represent the instance. To be able to add the (instantiated)
5146 // members and documentation of a template class
5147 // (inserted in that template class at a later stage),
5148 // the template should know about its instances.
5149 // the instantiation process, should be done in a recursive way,
5150 // since instantiating a template may introduce new inheritance
5151 // relations.
5152 if (!templSpec.empty() && mode==TemplateInstances)
5153 {
5154 // if baseClass is actually a typedef then we should not
5155 // instantiate it, since typedefs are in a different namespace
5156 // see bug531637 for an example where this would otherwise hang
5157 // Doxygen
5158 if (baseClassTypeDef==nullptr)
5159 {
5160 //printf(" => findTemplateInstanceRelation: %s\n",qPrint(baseClass->name()));
5161 findTemplateInstanceRelation(root,context,baseClass,templSpec,templateNames,baseClass->isArtificial());
5162 }
5163 }
5164 else if (mode==DocumentedOnly || mode==Undocumented)
5165 {
5166 //printf(" => insert base class\n");
5167 DString usedName;
5168 if (baseClassTypeDef)
5169 {
5170 usedName=biName;
5171 //printf("***** usedName=%s templSpec=%s\n",qPrint(usedName),qPrint(templSpec));
5172 }
5173 Protection prot = bi->prot;
5174 if (Config_getBool(SIP_SUPPORT)) prot=Protection::Public;
5175 if (cd!=baseClass && !cd->isSubClass(baseClass) && baseClass->isBaseClass(cd,true,templSpec)==0) // check for recursion, see bug690787
5176 {
5177 AUTO_TRACE_ADD("insertBaseClass name={} prot={} virt={} templSpec={}",usedName,prot,bi->virt,templSpec);
5178 cd->insertBaseClass(baseClass,usedName,prot,bi->virt,templSpec);
5179 // add this class as super class to the base class
5180 baseClass->insertSubClass(cd,prot,bi->virt,templSpec);
5181 }
5182 else
5183 {
5184 warn(root->fileName,root->startLine,
5185 "Detected potential recursive class relation "
5186 "between class {} and base class {}!",
5187 cd->name(),baseClass->name()
5188 );
5189 }
5190 }
5191 return true;
5192 }
5193 else if (mode==Undocumented && (scopeOffset==0 || isATemplateArgument))
5194 {
5195 AUTO_TRACE_ADD("New undocumented base class '{}' baseClassName='{}' templSpec='{}' isArtificial={}",
5196 biName,baseClassName,templSpec,isArtificial);
5197 baseClass=nullptr;
5198 if (isATemplateArgument)
5199 {
5200 baseClass = toClassDefMutable(Doxygen::hiddenClassLinkedMap->find(baseClassName));
5201 if (baseClass==nullptr) // not found (or alias)
5202 {
5203 baseClass= toClassDefMutable(
5204 Doxygen::hiddenClassLinkedMap->add(baseClassName,
5205 createClassDef(root->fileName,root->startLine,root->startColumn,
5206 baseClassName,
5207 ClassDef::Class)));
5208 if (baseClass) // really added (not alias)
5209 {
5210 if (isArtificial) baseClass->setArtificial(true);
5211 baseClass->setLanguage(root->lang);
5212 }
5213 }
5214 }
5215 else
5216 {
5217 baseClass = toClassDefMutable(Doxygen::classLinkedMap->find(baseClassName));
5218 //printf("*** classDDict->find(%s)=%p biName=%s templSpec=%s\n",
5219 // qPrint(baseClassName),baseClass,qPrint(biName),qPrint(templSpec));
5220 if (baseClass==nullptr) // not found (or alias)
5221 {
5222 baseClass = toClassDefMutable(
5223 Doxygen::classLinkedMap->add(baseClassName,
5224 createClassDef(root->fileName,root->startLine,root->startColumn,
5225 baseClassName,
5226 ClassDef::Class)));
5227 if (baseClass) // really added (not alias)
5228 {
5229 if (isArtificial) baseClass->setArtificial(true);
5230 baseClass->setLanguage(root->lang);
5231 si = baseClassName.rfind("::");
5232 if (si!=DString::npos) // class is nested
5233 {
5234 Definition *sd = findScopeFromQualifiedName(Doxygen::globalScope,baseClassName.left(si),nullptr,root->tagInfo());
5235 if (sd==nullptr || sd==Doxygen::globalScope) // outer scope not found
5236 {
5237 baseClass->setArtificial(true); // see bug678139
5238 }
5239 }
5240 }
5241 }
5242 }
5243 if (baseClass)
5244 {
5245 if (biName.endsWith("-p"))
5246 {
5247 biName="<"+biName.left(biName.length()-2)+">";
5248 }
5249 if (!cd->isSubClass(baseClass) && cd!=baseClass && cd->isBaseClass(baseClass,true,templSpec)==0) // check for recursion
5250 {
5251 AUTO_TRACE_ADD("insertBaseClass name={} prot={} virt={} templSpec={}",biName,bi->prot,bi->virt,templSpec);
5252 // add base class to this class
5253 cd->insertBaseClass(baseClass,biName,bi->prot,bi->virt,templSpec);
5254 // add this class as super class to the base class
5255 baseClass->insertSubClass(cd,bi->prot,bi->virt,templSpec);
5256 }
5257 // the undocumented base was found in this file
5258 baseClass->insertUsedFile(root->fileDef());
5259
5260 Definition *scope = buildScopeFromQualifiedName(baseClass->name(),root->lang,nullptr);
5261 if (scope!=baseClass)
5262 {
5263 baseClass->setOuterScope(scope);
5264 }
5265
5266 if (baseClassName.endsWith("-p"))
5267 {
5269 }
5270 return true;
5271 }
5272 else
5273 {
5274 AUTO_TRACE_ADD("Base class '{}' not created (alias?)",biName);
5275 }
5276 }
5277 else
5278 {
5279 AUTO_TRACE_ADD("Base class '{}' not found",biName);
5280 }
5281 }
5282 else
5283 {
5284 if (mode!=TemplateInstances)
5285 {
5286 warn(root->fileName,root->startLine,
5287 "Detected potential recursive class relation "
5288 "between class {} and base class {}!",
5289 root->name,baseClassName
5290 );
5291 }
5292 // for mode==TemplateInstance this case is quite common and
5293 // indicates a relation between a template class and a template
5294 // instance with the same name.
5295 }
5296 if (scopeOffset==0)
5297 {
5298 scopeOffset=-1;
5299 }
5300 else
5301 {
5302 size_t o = scopeName.rfind("::",scopeOffset-1);
5303 scopeOffset = o!=DString::npos ? static_cast<int>(o) : 0;
5304 }
5305 //printf("new scopeOffset='%d'",scopeOffset);
5306 } while (scopeOffset>=0);
5307
5308 if (parentNode==nullptr)
5309 {
5310 lastParent=true;
5311 }
5312 else
5313 {
5314 parentNode=parentNode->parent();
5315 }
5316 } while (lastParent);
5317
5318 return false;
5319}
5320
5321//----------------------------------------------------------------------
5322// Computes the base and super classes for each class in the tree
5323
5324static bool isClassSection(const Entry *root)
5325{
5326 if ( !root->name.empty() )
5327 {
5328 if (root->section.isCompound())
5329 // is it a compound (class, struct, union, interface ...)
5330 {
5331 return true;
5332 }
5333 else if (root->section.isCompoundDoc())
5334 // is it a documentation block with inheritance info.
5335 {
5336 bool hasExtends = !root->extends.empty();
5337 if (hasExtends) return true;
5338 }
5339 }
5340 return false;
5341}
5342
5343
5344/*! Builds a dictionary of all entry nodes in the tree starting with \a root
5345 */
5346static void findClassEntries(const Entry *root)
5347{
5348 if (isClassSection(root))
5349 {
5350 g_classEntries.emplace(root->name.str(),root);
5351 }
5352 for (const auto &e : root->children()) findClassEntries(e.get());
5353}
5354
5355static DString extractClassName(const Entry *root)
5356{
5357 // strip any anonymous scopes first
5360 if (size_t i=bName.find('<'); (root->lang==SrcLangExt::CSharp || root->lang==SrcLangExt::Java) && i!=DString::npos)
5361 {
5362 // a Java/C# generic class looks like a C++ specialization, so we need to strip the
5363 // template part before looking for matches
5364 if (root->lang==SrcLangExt::CSharp)
5365 {
5366 bName = mangleCSharpGenericName(root->name);
5367 }
5368 else
5369 {
5370 bName = bName.left(i);
5371 }
5372 }
5373 return bName;
5374}
5375
5376/*! Using the dictionary build by findClassEntries(), this
5377 * function will look for additional template specialization that
5378 * exists as inheritance relations only. These instances will be
5379 * added to the template they are derived from.
5380 */
5382{
5383 AUTO_TRACE();
5384 ClassDefSet visitedClasses;
5385 for (const auto &[name,root] : g_classEntries)
5386 {
5387 DString bName = extractClassName(root);
5388 ClassDefMutable *cdm = getClassMutable(bName);
5389 if (cdm)
5390 {
5391 findBaseClassesForClass(root,cdm,cdm,cdm,TemplateInstances,false);
5392 }
5393 }
5394}
5395
5397{
5398 AUTO_TRACE("root->name={} cd={}",root->name,cd->name());
5399 size_t i = root->name.find('<');
5400 size_t j = root->name.rfind('>');
5401 size_t k = j!=DString::npos ? root->name.find("::",j+1) : DString::npos; // A<T::B> => ok, A<T>::B => nok
5402 if (i!=DString::npos && j!=DString::npos && k==DString::npos && root->lang!=SrcLangExt::CSharp && root->lang!=SrcLangExt::Java)
5403 {
5404 ClassDefMutable *master = getClassMutable(root->name.left(i));
5405 if (master && master!=cd && !cd->templateMaster())
5406 {
5407 AUTO_TRACE_ADD("class={} master={}",cd->name(),cd->templateMaster()?cd->templateMaster()->name():"<none>",master->name());
5408 cd->setTemplateMaster(master);
5409 master->insertExplicitTemplateInstance(cd,root->name.mid(i));
5410 }
5411 }
5412}
5413
5415{
5416 AUTO_TRACE();
5417 for (const auto &[name,root] : g_classEntries)
5418 {
5419 DString bName = extractClassName(root);
5420 ClassDefMutable *cdm = getClassMutable(bName);
5421 if (cdm)
5422 {
5423 findUsedClassesForClass(root,cdm,cdm,cdm,true);
5425 cdm->addTypeConstraints();
5426 }
5427 }
5428}
5429
5431{
5432 AUTO_TRACE();
5433 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5434 {
5435 if (!nd->hasDocumentation())
5436 {
5437 if ((EntryType::guessSection(nd->getDefFileName()).isHeader() ||
5438 nd->getLanguage() == SrcLangExt::Fortran) && // Fortran doesn't have header files.
5439 !Config_getBool(HIDE_UNDOC_NAMESPACES) // undocumented namespaces are visible
5440 )
5441 {
5442 warn_undoc(nd->getDefFileName(),nd->getDefLine(), "{} {} is not documented.",
5443 nd->getLanguage() == SrcLangExt::Fortran ? "Module" : "Namespace",
5444 nd->name());
5445 }
5446 }
5447 }
5448}
5449
5451{
5452 AUTO_TRACE();
5453 for (const auto &[name,root] : g_classEntries)
5454 {
5455 DString bName = extractClassName(root);
5456 ClassDefMutable *cd = getClassMutable(bName);
5457 if (cd)
5458 {
5459 findBaseClassesForClass(root,cd,cd,cd,DocumentedOnly,false);
5460 }
5461 size_t numMembers = cd ? cd->memberNameInfoLinkedMap().size() : 0;
5462 if ((cd==nullptr || (!cd->hasDocumentation() && !cd->isReference())) && numMembers>0 && !bName.endsWith("::"))
5463 {
5464 if (!root->name.empty() && root->name.find('@')==DString::npos && // normal name
5465 (EntryType::guessSection(root->fileName).isHeader() ||
5466 Config_getBool(EXTRACT_LOCAL_CLASSES)) && // not defined in source file
5467 protectionLevelVisible(root->protection) && // hidden by protection
5468 !Config_getBool(HIDE_UNDOC_CLASSES) // undocumented class are visible
5469 )
5470 warn_undoc(root->fileName,root->startLine, "Compound {} is not documented.", root->name);
5471 }
5472 }
5473}
5474
5476{
5477 AUTO_TRACE();
5478 for (const auto &[name,root] : g_classEntries)
5479 {
5483 // strip any anonymous scopes first
5484 if (cd && !cd->getTemplateInstances().empty())
5485 {
5486 AUTO_TRACE_ADD("Template class '{}'",cd->name());
5487 for (const auto &ti : cd->getTemplateInstances()) // for each template instance
5488 {
5489 ClassDefMutable *tcd=toClassDefMutable(ti.classDef);
5490 if (tcd)
5491 {
5492 AUTO_TRACE_ADD("Template instance '{}'",tcd->name());
5493 DString templSpec = ti.templSpec;
5494 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(tcd->getLanguage(),templSpec);
5495 for (const BaseInfo &bi : root->extends)
5496 {
5497 // check if the base class is a template argument
5498 BaseInfo tbi = bi;
5499 const ArgumentList &tl = cd->templateArguments();
5500 if (!tl.empty())
5501 {
5502 TemplateNameMap baseClassNames = tcd->getTemplateBaseClassNames();
5503 TemplateNameMap templateNames = getTemplateArgumentsInName(tl,bi.name.str());
5504 // for each template name that we inherit from we need to
5505 // substitute the formal with the actual arguments
5506 TemplateNameMap actualTemplateNames;
5507 for (const auto &tn_kv : templateNames)
5508 {
5509 size_t templIndex = tn_kv.second;
5510 Argument actArg;
5511 bool hasActArg=false;
5512 if (templIndex<templArgs->size())
5513 {
5514 actArg=templArgs->at(templIndex);
5515 hasActArg=true;
5516 }
5517 if (hasActArg &&
5518 baseClassNames.find(actArg.type.str())!=baseClassNames.end() &&
5519 actualTemplateNames.find(actArg.type.str())==actualTemplateNames.end()
5520 )
5521 {
5522 actualTemplateNames.emplace(actArg.type.str(),static_cast<int>(templIndex));
5523 }
5524 }
5525
5526 tbi.name = substituteTemplateArgumentsInString(bi.name,tl,templArgs.get());
5527 // find a documented base class in the correct scope
5528 if (!findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,DocumentedOnly,false))
5529 {
5530 // no documented base class -> try to find an undocumented one
5531 findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,Undocumented,true);
5532 }
5533 }
5534 }
5535 }
5536 }
5537 }
5538 }
5539}
5540
5541//-----------------------------------------------------------------------
5542// compute the references (anchors in HTML) for each function in the file
5543
5545{
5546 AUTO_TRACE();
5547 for (const auto &cd : *Doxygen::classLinkedMap)
5548 {
5549 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5550 if (cdm)
5551 {
5552 cdm->computeAnchors();
5553 }
5554 }
5555 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5556 {
5557 for (const auto &fd : *fn)
5558 {
5559 fd->computeAnchors();
5560 }
5561 }
5562 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5563 {
5565 if (ndm)
5566 {
5567 ndm->computeAnchors();
5568 }
5569 }
5570 for (const auto &gd : *Doxygen::groupLinkedMap)
5571 {
5572 gd->computeAnchors();
5573 }
5574}
5575
5576//----------------------------------------------------------------------
5577
5578
5579template<typename Func>
5580static void applyToAllDefinitions(Func func)
5581{
5582 for (const auto &cd : *Doxygen::classLinkedMap)
5583 {
5584 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5585 if (cdm)
5586 {
5587 func(cdm);
5588 }
5589 }
5590
5591 for (const auto &cd : *Doxygen::conceptLinkedMap)
5592 {
5593 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
5594 if (cdm)
5595 {
5596 func(cdm);
5597 }
5598 }
5599
5600 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5601 {
5602 for (const auto &fd : *fn)
5603 {
5604 func(fd.get());
5605 }
5606 }
5607
5608 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5609 {
5611 if (ndm)
5612 {
5613 func(ndm);
5614 }
5615 }
5616
5617 for (const auto &gd : *Doxygen::groupLinkedMap)
5618 {
5619 func(gd.get());
5620 }
5621
5622 for (const auto &pd : *Doxygen::pageLinkedMap)
5623 {
5624 func(pd.get());
5625 }
5626
5627 for (const auto &dd : *Doxygen::dirLinkedMap)
5628 {
5629 func(dd.get());
5630 }
5631
5632 func(&ModuleManager::instance());
5633}
5634
5635//----------------------------------------------------------------------
5636
5638{
5639 AUTO_TRACE();
5640 applyToAllDefinitions([](auto* obj) { obj->addRequirementReferences(); });
5641}
5642
5643//----------------------------------------------------------------------
5644
5646{
5647 AUTO_TRACE();
5648 applyToAllDefinitions([](auto* obj) { obj->addListReferences(); });
5649}
5650
5651
5652//----------------------------------------------------------------------
5653
5655{
5656 AUTO_TRACE();
5658 {
5659 rl->generatePage();
5660 }
5661}
5662
5663//----------------------------------------------------------------------
5664// Copy the documentation in entry 'root' to member definition 'md' and
5665// set the function declaration of the member to 'funcDecl'. If the boolean
5666// over_load is set the standard overload text is added.
5667
5668static void addMemberDocs(const Entry *root,
5669 MemberDefMutable *md, const DString &funcDecl,
5670 const ArgumentList *al,
5671 bool over_load,
5672 TypeSpecifier spec
5673 )
5674{
5675 if (md==nullptr) return;
5676 AUTO_TRACE("scope='{}' name='{}' args='{}' funcDecl='{}' mSpec={}",
5677 root->parent()->name,md->name(),md->argsString(),funcDecl,spec);
5678 if (!root->section.isDoc()) // @fn or @var does not need to specify the complete definition, so don't overwrite it
5679 {
5680 DString fDecl=funcDecl;
5681 // strip extern specifier
5682 fDecl.stripPrefix("extern ");
5683 md->setDefinition(fDecl);
5684 }
5686 md->addQualifiers(root->qualifiers);
5688 const NamespaceDef *nd=md->getNamespaceDef();
5689 DString fullName;
5690 if (cd)
5691 fullName = cd->name();
5692 else if (nd)
5693 fullName = nd->name();
5694
5695 if (!fullName.empty()) fullName+="::";
5696 fullName+=md->name();
5697 FileDef *rfd=root->fileDef();
5698
5699 // TODO determine scope based on root not md
5700 Definition *rscope = md->getOuterScope();
5701
5702 const ArgumentList &mdAl = md->argumentList();
5703 if (al)
5704 {
5705 ArgumentList mergedAl = *al;
5706 //printf("merging arguments (1) docs=%d\n",root->doc.empty());
5707 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedAl,!root->doc.empty());
5708 }
5709 else
5710 {
5711 if (
5712 matchArguments2( md->getOuterScope(), md->getFileDef(),md->typeString(),const_cast<ArgumentList*>(&mdAl),
5713 rscope,rfd,root->type,&root->argList,
5714 true, root->lang
5715 )
5716 )
5717 {
5718 //printf("merging arguments (2)\n");
5719 ArgumentList mergedArgList = root->argList;
5720 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.empty());
5721 }
5722 }
5723 if (over_load) // the \overload keyword was used
5724 {
5726 if (!root->doc.empty())
5727 {
5728 doc+="<p>";
5729 doc+=root->doc;
5730 }
5731 md->setDocumentation(doc,root->docFile,root->docLine);
5733 md->setDocsForDefinition(!root->proto);
5734 }
5735 else
5736 {
5737 //printf("overwrite!\n");
5738 md->setDocumentation(root->doc,root->docFile,root->docLine);
5739 md->setDocsForDefinition(!root->proto);
5740
5741 //printf("overwrite!\n");
5742 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
5743
5744 if (
5745 (md->inbodyDocumentation().empty() ||
5746 !root->parent()->name.empty()
5747 ) && !root->inbodyDocs.empty()
5748 )
5749 {
5751 }
5752 }
5753
5754 //printf("initializer: '%s'(isEmpty=%d) '%s'(isEmpty=%d)\n",
5755 // qPrint(md->initializer()),md->initializer().empty(),
5756 // qPrint(root->initializer),root->initializer.empty()
5757 // );
5758 std::string rootInit = root->initializer.str();
5759 if (md->initializer().empty() && !rootInit.empty())
5760 {
5761 //printf("setInitializer\n");
5762 md->setInitializer(rootInit);
5763 }
5764 if (md->requiresClause().empty() && !root->req.empty())
5765 {
5766 md->setRequiresClause(root->req);
5767 }
5768
5769 md->setMaxInitLines(root->initLines);
5770
5771 if (rfd)
5772 {
5773 if ((md->getStartBodyLine()==-1 && root->bodyLine!=-1)
5774 )
5775 {
5776 //printf("Setting new body segment [%d,%d]\n",root->bodyLine,root->endBodyLine);
5777 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
5778 md->setBodyDef(rfd);
5779 }
5780
5781 md->setRefItems(root->sli);
5782 md->setRequirementReferences(root->rqli);
5783 }
5784
5786 md->addQualifiers(root->qualifiers);
5787
5788 md->mergeMemberSpecifiers(spec);
5790 addMemberToGroups(root,md);
5792 if (cd) cd->insertUsedFile(rfd);
5793 //printf("root->mGrpId=%d\n",root->mGrpId);
5794 if (root->mGrpId!=-1)
5795 {
5796 if (md->getMemberGroupId()!=-1)
5797 {
5798 if (md->getMemberGroupId()!=root->mGrpId)
5799 {
5800 warn(root->fileName,root->startLine,
5801 "member {} belongs to two different groups. The second one found here will be ignored.",
5802 md->name()
5803 );
5804 }
5805 }
5806 else // set group id
5807 {
5808 //printf("setMemberGroupId=%d md=%s\n",root->mGrpId,qPrint(md->name()));
5809 md->setMemberGroupId(root->mGrpId);
5810 }
5811 }
5812 md->addQualifiers(root->qualifiers);
5813}
5814
5815//----------------------------------------------------------------------
5816// find a class definition given the scope name and (optionally) a
5817// template list specifier
5818
5820 const DString &scopeName)
5821{
5822 SymbolResolver resolver(fd);
5823 const ClassDef *tcd = resolver.resolveClass(nd,scopeName,true,true);
5824 //printf("findClassDefinition(fd=%s,ns=%s,scopeName=%s)='%s'\n",
5825 // qPrint(fd?fd->name():""),qPrint(nd?nd->name():""),
5826 // qPrint(scopeName),qPrint(tcd?tcd->name():""));
5827 return tcd;
5828}
5829
5830//----------------------------------------------------------------------------
5831// Returns true, if the entry belongs to the group of the member definition,
5832// otherwise false.
5833
5834static bool isEntryInGroupOfMember(const Entry *root,const MemberDef *md,bool allowNoGroup=false)
5835{
5836 const GroupDef *gd = md->getGroupDef();
5837 if (!gd)
5838 {
5839 return allowNoGroup;
5840 }
5841
5842 for (const auto &g : root->groups)
5843 {
5844 if (g.groupname == gd->name())
5845 {
5846 return true; // matching group
5847 }
5848 }
5849
5850 return false;
5851}
5852
5853//----------------------------------------------------------------------
5854// Adds the documentation contained in 'root' to a global function
5855// with name 'name' and argument list 'args' (for overloading) and
5856// function declaration 'decl' to the corresponding member definition.
5857
5858static bool findGlobalMember(const Entry *root,
5859 const DString &namespaceName,
5860 const DString &type,
5861 const DString &name,
5862 const DString &tempArg,
5863 const DString &,
5864 const DString &decl,
5865 TypeSpecifier /* spec */)
5866{
5867 AUTO_TRACE("namespace='{}' type='{}' name='{}' tempArg='{}' decl='{}'",namespaceName,type,name,tempArg,decl);
5868 DString n=name;
5869 if (n.empty()) return false;
5870 if (n.find("::")!=DString::npos) return false; // skip undefined class members
5871 MemberName *mn=Doxygen::functionNameLinkedMap->find(n+tempArg); // look in function dictionary
5872 if (mn==nullptr)
5873 {
5874 mn=Doxygen::functionNameLinkedMap->find(n); // try without template arguments
5875 }
5876 if (mn) // function name defined
5877 {
5878 AUTO_TRACE_ADD("Found symbol name");
5879 //int count=0;
5880 bool found=false;
5881 for (const auto &md : *mn)
5882 {
5883 // If the entry has groups, then restrict the search to members which are
5884 // in one of the groups of the entry. If md is not associated with a group yet,
5885 // allow this documentation entry to add the group info.
5886 if (!root->groups.empty() && !isEntryInGroupOfMember(root, md.get(), true))
5887 {
5888 continue;
5889 }
5890
5891 const NamespaceDef *nd=nullptr;
5892 if (md->isAlias() && md->getOuterScope() &&
5893 md->getOuterScope()->definitionType()==Definition::TypeNamespace)
5894 {
5895 nd = toNamespaceDef(md->getOuterScope());
5896 }
5897 else
5898 {
5899 nd = md->getNamespaceDef();
5900 }
5901
5902 // special case for strong enums
5903 size_t enumNamePos=0;
5904 if (nd && md->isEnumValue() && (enumNamePos=namespaceName.rfind("::"))!=DString::npos)
5905 { // md part of a strong enum in a namespace?
5906 DString enumName = namespaceName.mid(enumNamePos+2);
5907 if (namespaceName.left(enumNamePos)==nd->name())
5908 {
5910 if (enumMn)
5911 {
5912 for (const auto &emd : *enumMn)
5913 {
5914 found = emd->isStrong() && md->getEnumScope()==emd.get();
5915 if (found)
5916 {
5917 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,nullptr,false,root->spec);
5918 break;
5919 }
5920 }
5921 }
5922 }
5923 if (found)
5924 {
5925 break;
5926 }
5927 }
5928 else if (nd==nullptr && md->isEnumValue()) // md part of global strong enum?
5929 {
5930 MemberName *enumMn=Doxygen::functionNameLinkedMap->find(namespaceName);
5931 if (enumMn)
5932 {
5933 for (const auto &emd : *enumMn)
5934 {
5935 found = emd->isStrong() && md->getEnumScope()==emd.get();
5936 if (found)
5937 {
5938 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,nullptr,false,root->spec);
5939 break;
5940 }
5941 }
5942 }
5943 }
5944
5945 const FileDef *fd=root->fileDef();
5946 //printf("File %s\n",fd ? qPrint(fd->name()) : "<none>");
5948 if (fd)
5949 {
5950 nl = fd->getUsedNamespaces();
5951 }
5952 //printf("NamespaceList %p\n",nl);
5953
5954 // search in the list of namespaces that are imported via a
5955 // using declaration
5956 bool viaUsingDirective = nd && nl.find(nd->qualifiedName())!=nullptr;
5957
5958 if ((namespaceName.empty() && nd==nullptr) || // not in a namespace
5959 (nd && nd->name()==namespaceName) || // or in the same namespace
5960 viaUsingDirective // member in 'using' namespace
5961 )
5962 {
5963 AUTO_TRACE_ADD("Try to add member '{}' to scope '{}'",md->name(),namespaceName);
5964
5965 NamespaceDef *rnd = nullptr;
5966 if (!namespaceName.empty()) rnd = Doxygen::namespaceLinkedMap->find(namespaceName);
5967
5968 const ArgumentList &mdAl = md.get()->argumentList();
5969 bool matching=
5970 (mdAl.empty() && root->argList.empty()) ||
5971 md->isVariable() || md->isTypedef() || /* in case of function pointers */
5972 matchArguments2(md->getOuterScope(),md->getFileDef(),md->typeString(),&mdAl,
5973 rnd ? rnd : Doxygen::globalScope,fd,root->type,&root->argList,
5974 false,root->lang);
5975
5976 // for template members we need to check if the number of
5977 // template arguments is the same, otherwise we are dealing with
5978 // different functions.
5979 if (matching && !root->tArgLists.empty())
5980 {
5981 const ArgumentList &mdTempl = md->templateArguments();
5982 if (root->tArgLists.back().size()!=mdTempl.size())
5983 {
5984 matching=false;
5985 }
5986 }
5987
5988 //printf("%s<->%s\n",
5989 // qPrint(argListToString(md->argumentList())),
5990 // qPrint(argListToString(root->argList)));
5991
5992 // For static members we also check if the comment block was found in
5993 // the same file. This is needed because static members with the same
5994 // name can be in different files. Thus it would be wrong to just
5995 // put the comment block at the first syntactically matching member. If
5996 // the comment block belongs to a group of the static member, then add
5997 // the documentation even if it is in a different file.
5998 if (matching && md->isStatic() &&
5999 md->getDefFileName()!=root->fileName &&
6000 mn->size()>1 &&
6001 !isEntryInGroupOfMember(root,md.get()))
6002 {
6003 matching = false;
6004 }
6005
6006 // for template member we also need to check the return type and requires
6007 if (!md->templateArguments().empty() && !root->tArgLists.empty())
6008 {
6009 //printf("Comparing return types '%s'<->'%s'\n",
6010 // md->typeString(),type);
6011 //printf("%s: Comparing '%s'<=>'%s'\n",qPrint(md->name()),qPrint(md->requiresClause()),qPrint(root->req));
6012 if (md->templateArguments().size()!=root->tArgLists.back().size() ||
6013 md->typeString()!=type ||
6014 md->requiresClause()!=root->req)
6015 {
6016 //printf(" ---> no matching\n");
6017 matching = false;
6018 }
6019 }
6020
6021 if (matching) // add docs to the member
6022 {
6023 AUTO_TRACE_ADD("Match found");
6024 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,&root->argList,false,root->spec);
6025 found=true;
6026 break;
6027 }
6028 }
6029 }
6030 if (!found && root->relatesType!=RelatesType::Duplicate && root->section.isFunction()) // no match
6031 {
6032 DString fullFuncDecl=decl;
6033 if (!root->argList.empty()) fullFuncDecl+=argListToString(root->argList,true);
6034 DString warnMsg = "no matching file member found for \n"+fullFuncDecl;
6035 if (mn->size()>0)
6036 {
6037 warnMsg+="\nPossible candidates:";
6038 for (const auto &md : *mn)
6039 {
6040 warnMsg+="\n '";
6041 warnMsg+=replaceAnonymousScopes(md->declaration());
6042 warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
6043 }
6044 }
6045 warn(root->fileName,root->startLine, "{}", qPrint(warnMsg));
6046 }
6047 }
6048 else // got docs for an undefined member!
6049 {
6050 if (root->type!="friend class" &&
6051 root->type!="friend struct" &&
6052 root->type!="friend union" &&
6053 root->type!="friend" &&
6054 (!Config_getBool(TYPEDEF_HIDES_STRUCT) ||
6055 root->type.find("typedef ")==DString::npos)
6056 )
6057 {
6058 warn(root->fileName,root->startLine,
6059 "documented symbol '{}' was not declared or defined.",qPrint(decl)
6060 );
6061 }
6062 }
6063 return true;
6064}
6065
6067 const ArgumentLists &srcTempArgLists,
6068 const ArgumentLists &dstTempArgLists
6069 )
6070{
6071 auto srcIt = srcTempArgLists.begin();
6072 auto dstIt = dstTempArgLists.begin();
6073 while (srcIt!=srcTempArgLists.end() && dstIt!=dstTempArgLists.end())
6074 {
6075 if ((*srcIt).size()!=(*dstIt).size()) return true;
6076 ++srcIt;
6077 ++dstIt;
6078 }
6079 return false;
6080}
6081
6082static bool scopeIsTemplate(const Definition *d)
6083{
6084 bool result=false;
6085 //printf("> scopeIsTemplate(%s)\n",qPrint(d?d->name():"null"));
6087 {
6088 auto cd = toClassDef(d);
6089 result = cd->templateArguments().hasParameters() || cd->templateMaster()!=nullptr ||
6091 }
6092 //printf("< scopeIsTemplate=%d\n",result);
6093 return result;
6094}
6095
6097 const ArgumentLists &srcTempArgLists,
6098 const ArgumentLists &dstTempArgLists,
6099 const std::string &src
6100 )
6101{
6102 std::string dst;
6103 static const reg::Ex re(R"(\a\w*)");
6104 reg::Iterator it(src,re);
6106 //printf("type=%s\n",qPrint(sa->type));
6107 size_t p=0;
6108 for (; it!=end ; ++it) // for each word in srcType
6109 {
6110 const auto &match = *it;
6111 size_t i = match.position();
6112 size_t l = match.length();
6113 bool found=false;
6114 dst+=src.substr(p,i-p);
6115 std::string name=match.str();
6116
6117 auto srcIt = srcTempArgLists.begin();
6118 auto dstIt = dstTempArgLists.begin();
6119 while (srcIt!=srcTempArgLists.end() && !found)
6120 {
6121 const ArgumentList *tdAli = nullptr;
6122 std::vector<Argument>::const_iterator tdaIt;
6123 if (dstIt!=dstTempArgLists.end())
6124 {
6125 tdAli = &(*dstIt);
6126 tdaIt = tdAli->begin();
6127 ++dstIt;
6128 }
6129
6130 const ArgumentList &tsaLi = *srcIt;
6131 for (auto tsaIt = tsaLi.begin(); tsaIt!=tsaLi.end() && !found; ++tsaIt)
6132 {
6133 Argument tsa = *tsaIt;
6134 const Argument *tda = nullptr;
6135 if (tdAli && tdaIt!=tdAli->end())
6136 {
6137 tda = &(*tdaIt);
6138 ++tdaIt;
6139 }
6140 //if (tda) printf("tsa=%s|%s tda=%s|%s\n",
6141 // qPrint(tsa.type),qPrint(tsa.name),
6142 // qPrint(tda->type),qPrint(tda->name));
6143 if (name==tsa.name.str())
6144 {
6145 if (tda && tda->name.empty())
6146 {
6147 DString tdaName = tda->name;
6148 DString tdaType = tda->type;
6149 int vc=0;
6150 if (tdaType.startsWith("class ")) vc=6;
6151 else if (tdaType.startsWith("typename ")) vc=9;
6152 if (vc>0) // convert type=="class T" to type=="class" name=="T"
6153 {
6154 tdaName = tdaType.mid(vc);
6155 }
6156 if (!tdaName.empty())
6157 {
6158 name=tdaName.str(); // substitute
6159 found=true;
6160 }
6161 }
6162 }
6163 }
6164
6165 //printf(" srcList='%s' dstList='%s faList='%s'\n",
6166 // qPrint(argListToString(srclali.current())),
6167 // qPrint(argListToString(dstlali.current())),
6168 // funcTempArgList ? qPrint(argListToString(funcTempArgList)) : "<none>");
6169 ++srcIt;
6170 }
6171 dst+=name;
6172 p=i+l;
6173 }
6174 dst+=src.substr(p);
6175 //printf(" substituteTemplatesInString(%s)=%s\n",
6176 // qPrint(src),qPrint(dst));
6177 return dst;
6178}
6179
6181 const ArgumentLists &srcTempArgLists,
6182 const ArgumentLists &dstTempArgLists,
6183 const ArgumentList &src,
6184 ArgumentList &dst
6185 )
6186{
6187 auto dstIt = dst.begin();
6188 for (const Argument &sa : src)
6189 {
6190 DString dstType = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.type.str());
6191 DString dstArray = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.array.str());
6192 if (dstIt == dst.end())
6193 {
6194 Argument da = sa;
6195 da.type = dstType;
6196 da.array = dstArray;
6197 dst.push_back(da);
6198 dstIt = dst.end();
6199 }
6200 else
6201 {
6202 Argument da = *dstIt;
6203 da.type = dstType;
6204 da.array = dstArray;
6205 ++dstIt;
6206 }
6207 }
6212 srcTempArgLists,dstTempArgLists,
6213 src.trailingReturnType().str()));
6214 dst.setIsDeleted(src.isDeleted());
6215 dst.setRefQualifier(src.refQualifier());
6216 dst.setNoParameters(src.noParameters());
6217 //printf("substituteTemplatesInArgList: replacing %s with %s\n",
6218 // qPrint(argListToString(src)),qPrint(argListToString(dst))
6219 // );
6220}
6221
6222//-------------------------------------------------------------------------------------------
6223
6224static void addLocalObjCMethod(const Entry *root,
6225 const DString &scopeName,
6226 const DString &funcType,const DString &funcName,const DString &funcArgs,
6227 const DString &exceptions,const DString &funcDecl,
6228 TypeSpecifier spec)
6229{
6230 AUTO_TRACE();
6231 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
6232 ClassDefMutable *cd=nullptr;
6233 if (Config_getBool(EXTRACT_LOCAL_METHODS) && (cd=getClassMutable(scopeName)))
6234 {
6235 AUTO_TRACE_ADD("Local objective C method '{}' scopeName='{}'",root->name,scopeName);
6236 auto md = createMemberDef(
6237 root->fileName,root->startLine,root->startColumn,
6238 funcType,funcName,funcArgs,exceptions,
6239 root->protection,root->virt,root->isStatic,Relationship::Member,
6240 MemberType::Function,ArgumentList(),root->argList,root->metaData);
6241 auto mmd = toMemberDefMutable(md.get());
6242 mmd->setTagInfo(root->tagInfo());
6243 mmd->setLanguage(root->lang);
6244 mmd->setId(root->id);
6245 mmd->makeImplementationDetail();
6246 mmd->setMemberClass(cd);
6247 mmd->setDefinition(funcDecl);
6249 mmd->addQualifiers(root->qualifiers);
6250 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6251 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6252 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6253 mmd->setDocsForDefinition(!root->proto);
6254 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6255 mmd->addSectionsToDefinition(root->anchors);
6256 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6257 FileDef *fd=root->fileDef();
6258 mmd->setBodyDef(fd);
6259 mmd->setMemberSpecifiers(spec);
6260 mmd->setVhdlSpecifiers(root->vhdlSpec);
6261 mmd->setMemberGroupId(root->mGrpId);
6262 cd->insertMember(md.get());
6263 cd->insertUsedFile(fd);
6264 mmd->setRefItems(root->sli);
6265 mmd->setRequirementReferences(root->rqli);
6266
6268 mn->push_back(std::move(md));
6269 }
6270 else
6271 {
6272 // local objective C method found for class without interface
6273 }
6274}
6275
6276//-------------------------------------------------------------------------------------------
6277
6278static void addMemberFunction(const Entry *root,
6279 MemberName *mn,
6280 const DString &scopeName,
6281 const DString &namespaceName,
6282 const DString &className,
6283 const DString &funcTyp,
6284 const DString &funcName,
6285 const DString &funcArgs,
6286 const DString &funcTempList,
6287 const DString &exceptions,
6288 const DString &type,
6289 const DString &args,
6290 bool isFriend,
6291 TypeSpecifier spec,
6292 const DString &relates,
6293 const DString &funcDecl,
6294 bool overloaded,
6295 bool isFunc)
6296{
6297 AUTO_TRACE();
6298 DString funcType = funcTyp;
6299 int count=0;
6300 int noMatchCount=0;
6301 bool memFound=false;
6302 for (const auto &imd : *mn)
6303 {
6304 MemberDefMutable *md = toMemberDefMutable(imd.get());
6305 if (md==nullptr) continue;
6307 if (cd==nullptr) continue;
6308 //AUTO_TRACE_ADD("member definition found, scope needed='{}' scope='{}' args='{}' fileName='{}'",
6309 // scopeName, cd->name(), md->argsString(), root->fileName);
6310 FileDef *fd=root->fileDef();
6311 NamespaceDef *nd=nullptr;
6312 if (!namespaceName.empty()) nd=getResolvedNamespace(namespaceName);
6313
6314 //printf("scopeName %s->%s\n",qPrint(scopeName),
6315 // qPrint(stripTemplateSpecifiersFromScope(scopeName,false)));
6316
6317 // if the member we are searching for is an enum value that is part of
6318 // a "strong" enum, we need to look into the fields of the enum for a match
6319 size_t enumNamePos=0;
6320 if (md->isEnumValue() && (enumNamePos=className.rfind("::"))!=DString::npos)
6321 {
6322 DString enumName = className.mid(enumNamePos+2);
6323 DString fullScope = className.left(enumNamePos);
6324 if (!namespaceName.empty()) fullScope.prepend(namespaceName+"::");
6325 if (fullScope==cd->name())
6326 {
6327 MemberName *enumMn=Doxygen::memberNameLinkedMap->find(enumName);
6328 //printf("enumMn(%s)=%p\n",qPrint(className),(void*)enumMn);
6329 if (enumMn)
6330 {
6331 for (const auto &emd : *enumMn)
6332 {
6333 memFound = emd->isStrong() && md->getEnumScope()==emd.get();
6334 if (memFound)
6335 {
6336 addMemberDocs(root,md,funcDecl,nullptr,overloaded,spec);
6337 count++;
6338 }
6339 if (memFound) break;
6340 }
6341 }
6342 }
6343 }
6344 if (memFound) break;
6345
6346 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
6347 if (tcd==nullptr && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
6348 {
6349 // don't be fooled by anonymous scopes
6350 tcd=cd;
6351 }
6352 //printf("Looking for %s inside nd=%s result=%s cd=%s\n",
6353 // qPrint(scopeName),nd?qPrint(nd->name()):"<none>",tcd?qPrint(tcd->name()):"",qPrint(cd->name()));
6354
6355 if (cd && tcd==cd) // member's classes match
6356 {
6357 AUTO_TRACE_ADD("class definition '{}' found",cd->name());
6358
6359 // get the template parameter lists found at the member declaration
6360 ArgumentLists declTemplArgs = cd->getTemplateParameterLists();
6361 const ArgumentList &templAl = md->templateArguments();
6362 if (!templAl.empty())
6363 {
6364 declTemplArgs.push_back(templAl);
6365 }
6366
6367 // get the template parameter lists found at the member definition
6368 const ArgumentLists &defTemplArgs = root->tArgLists;
6369 //printf("defTemplArgs=%p\n",defTemplArgs);
6370
6371 // do we replace the decl argument lists with the def argument lists?
6372 bool substDone=false;
6373 ArgumentList argList;
6374
6375 /* substitute the occurrences of class template names in the
6376 * argument list before matching
6377 */
6378 const ArgumentList &mdAl = md->argumentList();
6379 if (declTemplArgs.size()>0 && declTemplArgs.size()==defTemplArgs.size())
6380 {
6381 /* the function definition has template arguments
6382 * and the class definition also has template arguments, so
6383 * we must substitute the template names of the class by that
6384 * of the function definition before matching.
6385 */
6386 substituteTemplatesInArgList(declTemplArgs,defTemplArgs,mdAl,argList);
6387
6388 substDone=true;
6389 }
6390 else /* no template arguments, compare argument lists directly */
6391 {
6392 argList = mdAl;
6393 }
6394
6395 bool matching=
6396 md->isVariable() || md->isTypedef() || // needed for function pointers
6398 md->getClassDef(),md->getFileDef(),md->typeString(),&argList,
6399 cd,fd,root->type,&root->argList,
6400 true,root->lang);
6401
6402 AUTO_TRACE_ADD("matching '{}'<=>'{}' className='{}' namespaceName='{}' result={}",
6403 argListToString(argList,true),argListToString(root->argList,true),className,namespaceName,matching);
6404
6405 if (md->getLanguage()==SrcLangExt::ObjC && md->isVariable() && root->section.isFunction())
6406 {
6407 matching = false; // don't match methods and attributes with the same name
6408 }
6409
6410 // for template member we also need to check the return type
6411 if (!md->templateArguments().empty() && !root->tArgLists.empty())
6412 {
6413 DString memType = md->typeString();
6414 memType.stripPrefix("static "); // see bug700696
6415 funcType=substitute(stripTemplateSpecifiersFromScope(funcType,true),
6416 className+"::",""); // see bug700693 & bug732594
6417 memType=substitute(stripTemplateSpecifiersFromScope(memType,true),
6418 className+"::",""); // see bug758900
6419 if (memType=="auto" && !argList.trailingReturnType().empty())
6420 {
6421 memType = argList.trailingReturnType();
6422 memType.stripPrefix(" -> ");
6423 }
6424 if (funcType=="auto" && !root->argList.trailingReturnType().empty())
6425 {
6426 funcType = root->argList.trailingReturnType();
6427 funcType.stripPrefix(" -> ");
6429 substDone=true;
6430 }
6431 AUTO_TRACE_ADD("Comparing return types '{}'<->'{}' #args {}<->{}",
6432 memType,funcType,md->templateArguments().size(),root->tArgLists.back().size());
6433 if (md->templateArguments().size()!=root->tArgLists.back().size() || memType!=funcType)
6434 {
6435 //printf(" ---> no matching\n");
6436 matching = false;
6437 }
6438 }
6439 else if (defTemplArgs.size()>declTemplArgs.size())
6440 {
6441 AUTO_TRACE_ADD("Different number of template arguments {} vs {}",defTemplArgs.size(),declTemplArgs.size());
6442 // avoid matching a non-template function in a template class against a
6443 // template function with the same name and parameters, see issue #10184
6444 substDone = false;
6445 matching = false;
6446 }
6447 bool rootIsUserDoc = root->section.isMemberDoc();
6448 bool classIsTemplate = scopeIsTemplate(md->getClassDef());
6449 bool mdIsTemplate = md->templateArguments().hasParameters();
6450 bool classOrMdIsTemplate = mdIsTemplate || classIsTemplate;
6451 bool rootIsTemplate = !root->tArgLists.empty();
6452 //printf("classIsTemplate=%d mdIsTemplate=%d rootIsTemplate=%d\n",classIsTemplate,mdIsTemplate,rootIsTemplate);
6453 if (!rootIsUserDoc && // don't check out-of-line @fn references, see bug722457
6454 (mdIsTemplate || rootIsTemplate) && // either md or root is a template
6455 ((classOrMdIsTemplate && !rootIsTemplate) || (!classOrMdIsTemplate && rootIsTemplate))
6456 )
6457 {
6458 // Method with template return type does not match method without return type
6459 // even if the parameters are the same. See also bug709052
6460 AUTO_TRACE_ADD("Comparing return types: template v.s. non-template");
6461 matching = false;
6462 }
6463
6464 AUTO_TRACE_ADD("Match results of matchArguments2='{}' substDone='{}'",matching,substDone);
6465
6466 if (substDone) // found a new argument list
6467 {
6468 if (matching) // replace member's argument list
6469 {
6471 md->moveArgumentList(std::make_unique<ArgumentList>(argList));
6472 }
6473 else // no match
6474 {
6475 if (!funcTempList.empty() &&
6476 isSpecialization(declTemplArgs,defTemplArgs))
6477 {
6478 // check if we are dealing with a partial template
6479 // specialization. In this case we add it to the class
6480 // even though the member arguments do not match.
6481
6482 addMethodToClass(root,cd,type,md->name(),args,isFriend,
6483 md->protection(),md->isStatic(),md->virtualness(),spec,relates);
6484 return;
6485 }
6486 }
6487 }
6488 if (matching)
6489 {
6490 addMemberDocs(root,md,funcDecl,nullptr,overloaded,spec);
6491 count++;
6492 memFound=true;
6493 }
6494 }
6495 else if (cd && cd!=tcd) // we did find a class with the same name as cd
6496 // but in a different namespace
6497 {
6498 noMatchCount++;
6499 }
6500
6501 if (memFound) break;
6502 }
6503 if (count==0 && root->parent() && root->parent()->section.isObjcImpl())
6504 {
6505 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
6506 return;
6507 }
6508 if (count==0 && !(isFriend && funcType=="class"))
6509 {
6510 int candidates=0;
6511 const ClassDef *ecd = nullptr, *ucd = nullptr;
6512 MemberDef *emd = nullptr, *umd = nullptr;
6513 //printf("Assume template class\n");
6514 for (const auto &md : *mn)
6515 {
6516 MemberDef *cmd=md.get();
6518 ClassDefMutable *ccd=cdmdm ? cdmdm->getClassDefMutable() : nullptr;
6519 //printf("ccd->name()==%s className=%s\n",qPrint(ccd->name()),qPrint(className));
6520 if (ccd!=nullptr && rightScopeMatch(ccd->name(),className))
6521 {
6522 const ArgumentList &templAl = md->templateArguments();
6523 if (!root->tArgLists.empty() && !templAl.empty() &&
6524 root->tArgLists.back().size()<=templAl.size())
6525 {
6526 AUTO_TRACE_ADD("add template specialization");
6527 addMethodToClass(root,ccd,type,md->name(),args,isFriend,
6528 root->protection,root->isStatic,root->virt,spec,relates);
6529 return;
6530 }
6531 if (argListToString(md->argumentList(),false,false) ==
6532 argListToString(root->argList,false,false))
6533 { // exact argument list match -> remember
6534 ucd = ecd = ccd;
6535 umd = emd = cmd;
6536 AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': exact match",
6537 className,ccd->name(),md->argsString());
6538 }
6539 else // arguments do not match, but member name and scope do -> remember
6540 {
6541 ucd = ccd;
6542 umd = cmd;
6543 AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': no match",
6544 className,ccd->name(),md->argsString());
6545 }
6546 candidates++;
6547 }
6548 }
6549 bool strictProtoMatching = Config_getBool(STRICT_PROTO_MATCHING);
6550 if (!strictProtoMatching)
6551 {
6552 if (candidates==1 && ucd && umd)
6553 {
6554 // we didn't find an actual match on argument lists, but there is only 1 member with this
6555 // name in the same scope, so that has to be the one.
6556 addMemberDocs(root,toMemberDefMutable(umd),funcDecl,nullptr,overloaded,spec);
6557 return;
6558 }
6559 else if (candidates>1 && ecd && emd)
6560 {
6561 // we didn't find a unique match using type resolution,
6562 // but one of the matches has the exact same signature so
6563 // we take that one.
6564 addMemberDocs(root,toMemberDefMutable(emd),funcDecl,nullptr,overloaded,spec);
6565 return;
6566 }
6567 }
6568
6569 DString warnMsg = "no ";
6570 if (noMatchCount>1) warnMsg+="uniquely ";
6571 warnMsg+="matching class member found for \n";
6572
6573 for (const ArgumentList &al : root->tArgLists)
6574 {
6575 warnMsg+=" template ";
6576 warnMsg+=tempArgListToString(al,root->lang);
6577 warnMsg+='\n';
6578 }
6579
6580 DString fullFuncDecl=funcDecl;
6581 if (isFunc) fullFuncDecl+=argListToString(root->argList,true);
6582
6583 warnMsg+=" ";
6584 warnMsg+=fullFuncDecl;
6585
6586 if (candidates>0 || noMatchCount>=1)
6587 {
6588 warnMsg+="\nPossible candidates:";
6589
6590 NamespaceDef *nd=nullptr;
6591 if (!namespaceName.empty()) nd=getResolvedNamespace(namespaceName);
6592 FileDef *fd=root->fileDef();
6593
6594 for (const auto &md : *mn)
6595 {
6596 const ClassDef *cd=md->getClassDef();
6597 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
6598 if (tcd==nullptr && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
6599 {
6600 // don't be fooled by anonymous scopes
6601 tcd=cd;
6602 }
6603 if (cd!=nullptr && (rightScopeMatch(cd->name(),className) || (cd!=tcd)))
6604 {
6605 warnMsg+='\n';
6606 const ArgumentList &templAl = md->templateArguments();
6607 warnMsg+=" '";
6608 if (templAl.hasParameters())
6609 {
6610 warnMsg+="template ";
6611 warnMsg+=tempArgListToString(templAl,root->lang);
6612 warnMsg+='\n';
6613 warnMsg+=" ";
6614 }
6615 if (!md->typeString().empty())
6616 {
6617 warnMsg+=md->typeString();
6618 warnMsg+=' ';
6619 }
6621 if (!qScope.empty())
6622 warnMsg+=qScope+"::"+md->name();
6623 warnMsg+=md->argsString();
6624 warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
6625 }
6626 }
6627 }
6628 warn(root->fileName,root->startLine,"{}",warnMsg);
6629 }
6630}
6631
6632//-------------------------------------------------------------------------------------------
6633
6634static void addMemberSpecialization(const Entry *root,
6635 MemberName *mn,
6636 ClassDefMutable *cd,
6637 const DString &funcType,
6638 const DString &funcName,
6639 const DString &funcArgs,
6640 const DString &funcDecl,
6641 const DString &exceptions,
6642 TypeSpecifier spec
6643 )
6644{
6645 AUTO_TRACE("funcType={} funcName={} funcArgs={} funcDecl={} spec={}",funcType,funcName,funcArgs,funcDecl,spec);
6646 MemberDef *declMd=nullptr;
6647 for (const auto &md : *mn)
6648 {
6649 if (md->getClassDef()==cd)
6650 {
6651 // TODO: we should probably also check for matching arguments
6652 declMd = md.get();
6653 break;
6654 }
6655 }
6656 MemberType mtype=MemberType::Function;
6657 ArgumentList tArgList;
6658 // getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6659 auto md = createMemberDef(
6660 root->fileName,root->startLine,root->startColumn,
6661 funcType,funcName,funcArgs,exceptions,
6662 declMd ? declMd->protection() : root->protection,
6663 root->virt,root->isStatic,Relationship::Member,
6664 mtype,tArgList,root->argList,root->metaData);
6665 auto mmd = toMemberDefMutable(md.get());
6666 //printf("new specialized member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6667 mmd->setTagInfo(root->tagInfo());
6668 mmd->setLanguage(root->lang);
6669 mmd->setId(root->id);
6670 mmd->setMemberClass(cd);
6671 mmd->setTemplateSpecialization(true);
6672 mmd->setTypeConstraints(root->typeConstr);
6673 mmd->setDefinition(funcDecl);
6675 mmd->addQualifiers(root->qualifiers);
6676 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6677 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6678 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6679 mmd->setDocsForDefinition(!root->proto);
6680 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6681 mmd->addSectionsToDefinition(root->anchors);
6682 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6683 FileDef *fd=root->fileDef();
6684 mmd->setBodyDef(fd);
6685 mmd->setMemberSpecifiers(spec);
6686 mmd->setVhdlSpecifiers(root->vhdlSpec);
6687 mmd->setMemberGroupId(root->mGrpId);
6688 cd->insertMember(md.get());
6689 mmd->setRefItems(root->sli);
6690 mmd->setRequirementReferences(root->rqli);
6691
6692 mn->push_back(std::move(md));
6693}
6694
6695//-------------------------------------------------------------------------------------------
6696
6697static void addOverloaded(const Entry *root,MemberName *mn,
6698 const DString &funcType,const DString &funcName,const DString &funcArgs,
6699 const DString &funcDecl,const DString &exceptions,TypeSpecifier spec)
6700{
6701 // for unique overloaded member we allow the class to be
6702 // omitted, this is to be Qt compatible. Using this should
6703 // however be avoided, because it is error prone
6704 bool sameClass=false;
6705 if (mn->size()>0)
6706 {
6707 // check if all members with the same name are also in the same class
6708 sameClass = std::equal(mn->begin()+1,mn->end(),mn->begin(),
6709 [](const auto &md1,const auto &md2)
6710 { return md1->getClassDef()->name()==md2->getClassDef()->name(); });
6711 }
6712 if (sameClass)
6713 {
6714 MemberDefMutable *mdm = toMemberDefMutable(mn->front().get());
6715 ClassDefMutable *cd = mdm ? mdm->getClassDefMutable() : nullptr;
6716 if (cd==nullptr) return;
6717
6718 MemberType mtype = MemberType::Function;
6719 if (root->mtype==MethodTypes::Signal) mtype=MemberType::Signal;
6720 else if (root->mtype==MethodTypes::Slot) mtype=MemberType::Slot;
6721 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType::DCOP;
6722
6723 // new overloaded member function
6724 std::unique_ptr<ArgumentList> tArgList =
6725 getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6726 //printf("new related member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6727 auto md = createMemberDef(
6728 root->fileName,root->startLine,root->startColumn,
6729 funcType,funcName,funcArgs,exceptions,
6730 root->protection,root->virt,root->isStatic,Relationship::Related,
6731 mtype,tArgList ? *tArgList : ArgumentList(),root->argList,root->metaData);
6732 auto mmd = toMemberDefMutable(md.get());
6733 mmd->setTagInfo(root->tagInfo());
6734 mmd->setLanguage(root->lang);
6735 mmd->setId(root->id);
6736 mmd->setTypeConstraints(root->typeConstr);
6737 mmd->setMemberClass(cd);
6738 mmd->setDefinition(funcDecl);
6740 mmd->addQualifiers(root->qualifiers);
6742 doc+="<p>";
6743 doc+=root->doc;
6744 mmd->setDocumentation(doc,root->docFile,root->docLine);
6745 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6746 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6747 mmd->setDocsForDefinition(!root->proto);
6748 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6749 mmd->addSectionsToDefinition(root->anchors);
6750 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6751 FileDef *fd=root->fileDef();
6752 mmd->setBodyDef(fd);
6753 mmd->setMemberSpecifiers(spec);
6754 mmd->setVhdlSpecifiers(root->vhdlSpec);
6755 mmd->setMemberGroupId(root->mGrpId);
6756 cd->insertMember(md.get());
6757 cd->insertUsedFile(fd);
6758 mmd->setRefItems(root->sli);
6759 mmd->setRequirementReferences(root->rqli);
6760
6761 mn->push_back(std::move(md));
6762 }
6763}
6764
6765static void insertMemberAlias(Definition *outerScope,const MemberDef *md)
6766{
6767 if (outerScope && outerScope!=Doxygen::globalScope)
6768 {
6769 auto aliasMd = createMemberDefAlias(outerScope,md);
6770 if (outerScope->definitionType()==Definition::TypeClass)
6771 {
6772 ClassDefMutable *cdm = toClassDefMutable(outerScope);
6773 if (cdm)
6774 {
6775 cdm->insertMember(aliasMd.get());
6776 }
6777 }
6778 else if (outerScope->definitionType()==Definition::TypeNamespace)
6779 {
6780 NamespaceDefMutable *ndm = toNamespaceDefMutable(outerScope);
6781 if (ndm)
6782 {
6783 ndm->insertMember(aliasMd.get());
6784 }
6785 }
6786 else if (outerScope->definitionType()==Definition::TypeFile)
6787 {
6788 toFileDef(outerScope)->insertMember(aliasMd.get());
6789 }
6790 if (aliasMd)
6791 {
6792 Doxygen::functionNameLinkedMap->add(md->name())->push_back(std::move(aliasMd));
6793 }
6794 }
6795}
6796
6797//-------------------------------------------------------------------------------------------
6798
6799/*! This function tries to find a member (in a documented class/file/namespace)
6800 * that corresponds to the function/variable declaration given in \a funcDecl.
6801 *
6802 * The boolean \a overloaded is used to specify whether or not a standard
6803 * overload documentation line should be generated.
6804 *
6805 * The boolean \a isFunc is a hint that indicates that this is a function
6806 * instead of a variable or typedef.
6807 */
6808static void findMember(const Entry *root,
6809 const DString &relates,
6810 const DString &type,
6811 const DString &args,
6812 DString funcDecl,
6813 bool overloaded,
6814 bool isFunc
6815 )
6816{
6817 AUTO_TRACE("root='{}' funcDecl='{}' related='{}' overload={} isFunc={} mGrpId={} #tArgList={} spec={} lang={}",
6818 root->name, funcDecl, relates, overloaded, isFunc, root->mGrpId, root->tArgLists.size(),
6819 root->spec, root->lang);
6820
6821 DString scopeName;
6822 DString className;
6823 DString namespaceName;
6824 DString funcType;
6825 DString funcName;
6826 DString funcArgs;
6827 DString funcTempList;
6828 DString exceptions;
6829 DString funcSpec;
6830 bool isRelated=false;
6831 bool isMemberOf=false;
6832 bool isFriend=false;
6833 bool done=false;
6834 TypeSpecifier spec = root->spec;
6835 while (!done)
6836 {
6837 done=true;
6838 if (funcDecl.stripPrefix("friend ")) // treat friends as related members
6839 {
6840 isFriend=true;
6841 done=false;
6842 }
6843 if (funcDecl.stripPrefix("inline "))
6844 {
6845 spec.setInline(true);
6846 done=false;
6847 }
6848 if (funcDecl.stripPrefix("explicit "))
6849 {
6850 spec.setExplicit(true);
6851 done=false;
6852 }
6853 if (funcDecl.stripPrefix("mutable "))
6854 {
6855 spec.setMutable(true);
6856 done=false;
6857 }
6858 if (funcDecl.stripPrefix("thread_local "))
6859 {
6860 spec.setThreadLocal(true);
6861 done=false;
6862 }
6863 if (funcDecl.stripPrefix("virtual "))
6864 {
6865 done=false;
6866 }
6867 }
6868
6869 // delete any ; from the function declaration
6870 size_t sep=0;
6871 while ((sep=funcDecl.find(';'))!=DString::npos)
6872 {
6873 funcDecl=(funcDecl.left(sep)+funcDecl.mid(sep+1)).stripWhiteSpace();
6874 }
6875
6876 // make sure the first character is a space to simplify searching.
6877 if (!funcDecl.empty() && funcDecl[0]!=' ') funcDecl.prepend(" ");
6878
6879 // remove some superfluous spaces
6880 funcDecl= substitute(
6881 substitute(
6882 substitute(funcDecl,"~ ","~"),
6883 ":: ","::"
6884 ),
6885 " ::","::"
6886 ).stripWhiteSpace();
6887
6888 //printf("funcDecl='%s'\n",qPrint(funcDecl));
6889 if (isFriend && funcDecl.startsWith("class "))
6890 {
6891 //printf("friend class\n");
6892 funcDecl=funcDecl.mid(6);
6893 funcName = funcDecl;
6894 }
6895 else if (isFriend && funcDecl.startsWith("struct "))
6896 {
6897 funcDecl=funcDecl.mid(7);
6898 funcName = funcDecl;
6899 }
6900 else
6901 {
6902 // extract information from the declarations
6903 parseFuncDecl(funcDecl,root->lang,scopeName,funcType,funcName,
6904 funcArgs,funcTempList,exceptions
6905 );
6906 }
6907
6908 // the class name can also be a namespace name, we decide this later.
6909 // if a related class name is specified and the class name could
6910 // not be derived from the function declaration, then use the
6911 // related field.
6912 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}' funcType='{}' funcName='{}' funcArgs='{}'",
6913 scopeName,className,namespaceName,funcType,funcName,funcArgs);
6914 if (!relates.empty())
6915 { // related member, prefix user specified scope
6916 isRelated=true;
6917 isMemberOf=(root->relatesType == RelatesType::MemberOf);
6918 if (getClass(relates)==nullptr && !scopeName.empty())
6919 {
6920 scopeName= mergeScopes(scopeName,relates);
6921 }
6922 else
6923 {
6924 scopeName = relates;
6925 }
6926 }
6927
6928 if (relates.empty() && root->parent() &&
6929 (root->parent()->section.isScope() || root->parent()->section.isObjcImpl()) &&
6930 !root->parent()->name.empty()) // see if we can combine scopeName
6931 // with the scope in which it was found
6932 {
6933 DString joinedName = root->parent()->name+"::"+scopeName;
6934 if (!scopeName.empty() &&
6935 (getClass(joinedName) || Doxygen::namespaceLinkedMap->find(joinedName)))
6936 {
6937 scopeName = joinedName;
6938 }
6939 else
6940 {
6941 scopeName = mergeScopes(root->parent()->name,scopeName);
6942 }
6943 }
6944 else // see if we can prefix a namespace or class that is used from the file
6945 {
6946 FileDef *fd=root->fileDef();
6947 if (fd)
6948 {
6949 for (const auto &fnd : fd->getUsedNamespaces())
6950 {
6951 DString joinedName = fnd->name()+"::"+scopeName;
6952 if (Doxygen::namespaceLinkedMap->find(joinedName))
6953 {
6954 scopeName=joinedName;
6955 break;
6956 }
6957 }
6958 }
6959 }
6961 removeRedundantWhiteSpace(scopeName),false,&funcSpec,DString(),false);
6962
6963 // funcSpec contains the last template specifiers of the given scope.
6964 // If this method does not have any template arguments or they are
6965 // empty while funcSpec is not empty we assume this is a
6966 // specialization of a method. If not, we clear the funcSpec and treat
6967 // this as a normal method of a template class.
6968 if (!(root->tArgLists.size()>0 &&
6969 root->tArgLists.front().size()==0
6970 )
6971 )
6972 {
6973 funcSpec.clear();
6974 }
6975
6976 //namespaceName=removeAnonymousScopes(namespaceName);
6977 if (!Config_getBool(EXTRACT_ANON_NSPACES) && scopeName.find('@')!=DString::npos) return; // skip stuff in anonymous namespace...
6978
6979 // split scope into a namespace and a class part
6980 extractNamespaceName(scopeName,className,namespaceName,true);
6981 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}'",scopeName,className,namespaceName);
6982
6983 //printf("namespaceName='%s' className='%s'\n",qPrint(namespaceName),qPrint(className));
6984 // merge class and namespace scopes again
6985 scopeName.clear();
6986 if (!namespaceName.empty())
6987 {
6988 if (className.empty())
6989 {
6990 scopeName=namespaceName;
6991 }
6992 else if (!relates.empty() || // relates command with explicit scope
6993 !getClass(className)) // class name only exists in a namespace
6994 {
6995 scopeName=namespaceName+"::"+className;
6996 }
6997 else
6998 {
6999 scopeName=className;
7000 }
7001 }
7002 else if (!className.empty())
7003 {
7004 scopeName=className;
7005 }
7006 //printf("new scope='%s'\n",qPrint(scopeName));
7007
7008 DString tempScopeName=scopeName;
7009 ClassDefMutable *cd=getClassMutable(scopeName);
7010 if (cd)
7011 {
7012 if (funcSpec.empty())
7013 {
7014 uint32_t argListIndex=0;
7015 tempScopeName=cd->qualifiedNameWithTemplateParameters(&root->tArgLists,&argListIndex);
7016 }
7017 else
7018 {
7019 tempScopeName=scopeName+funcSpec;
7020 }
7021 }
7022 //printf("scopeName=%s cd=%p root->tArgLists=%p result=%s\n",
7023 // qPrint(scopeName),cd,root->tArgLists,qPrint(tempScopeName));
7024
7025 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
7026 // rebuild the function declaration (needed to get the scope right).
7027 if (!scopeName.empty() && !isRelated && !isFriend && !Config_getBool(HIDE_SCOPE_NAMES) && root->lang!=SrcLangExt::Python)
7028 {
7029 if (!funcType.empty())
7030 {
7031 if (isFunc) // a function -> we use argList for the arguments
7032 {
7033 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcTempList;
7034 }
7035 else
7036 {
7037 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcArgs;
7038 }
7039 }
7040 else
7041 {
7042 if (isFunc) // a function => we use argList for the arguments
7043 {
7044 funcDecl=tempScopeName+"::"+funcName+funcTempList;
7045 }
7046 else // variable => add 'argument' list
7047 {
7048 funcDecl=tempScopeName+"::"+funcName+funcArgs;
7049 }
7050 }
7051 }
7052 else // build declaration without scope
7053 {
7054 if (!funcType.empty()) // but with a type
7055 {
7056 if (isFunc) // function => omit argument list
7057 {
7058 funcDecl=funcType+" "+funcName+funcTempList;
7059 }
7060 else // variable => add 'argument' list
7061 {
7062 funcDecl=funcType+" "+funcName+funcArgs;
7063 }
7064 }
7065 else // no type
7066 {
7067 if (isFunc)
7068 {
7069 funcDecl=funcName+funcTempList;
7070 }
7071 else
7072 {
7073 funcDecl=funcName+funcArgs;
7074 }
7075 }
7076 }
7077
7078 if (funcType=="template class" && !funcTempList.empty())
7079 return; // ignore explicit template instantiations
7080
7081 AUTO_TRACE_ADD("Parse results: namespaceName='{}' className=`{}` funcType='{}' funcSpec='{}' "
7082 " funcName='{}' funcArgs='{}' funcTempList='{}' funcDecl='{}' relates='{}'"
7083 " exceptions='{}' isRelated={} isMemberOf={} isFriend={} isFunc={}",
7084 namespaceName, className, funcType, funcSpec,
7085 funcName, funcArgs, funcTempList, funcDecl, relates,
7086 exceptions, isRelated, isMemberOf, isFriend, isFunc);
7087
7088 if (!funcName.empty()) // function name is valid
7089 {
7090 // check if 'className' is actually a scoped enum, in which case we need to
7091 // process it as a global, see issue #6471
7092 bool strongEnum = false;
7093 MemberName *mn=nullptr;
7094 if (!className.empty() && (mn=Doxygen::functionNameLinkedMap->find(className)))
7095 {
7096 for (const auto &imd : *mn)
7097 {
7098 MemberDefMutable *md = toMemberDefMutable(imd.get());
7099 Definition *mdScope = nullptr;
7100 if (md && md->isEnumerate() && md->isStrong() && (mdScope=md->getOuterScope()) &&
7101 // need filter for the correct scope, see issue #9668
7102 ((namespaceName.empty() && mdScope==Doxygen::globalScope) || (mdScope->name()==namespaceName)))
7103 {
7104 AUTO_TRACE_ADD("'{}' is a strong enum! (namespace={} md->getOuterScope()->name()={})",md->name(),namespaceName,md->getOuterScope()->name());
7105 strongEnum = true;
7106 // pass the scope name name as a 'namespace' to the findGlobalMember function
7107 if (!namespaceName.empty())
7108 {
7109 namespaceName+="::"+className;
7110 }
7111 else
7112 {
7113 namespaceName=className;
7114 }
7115 }
7116 }
7117 }
7118
7119 if (funcName.startsWith("operator ")) // strip class scope from cast operator
7120 {
7121 funcName = substitute(funcName,className+"::","");
7122 }
7123 mn = nullptr;
7124 if (!funcTempList.empty()) // try with member specialization
7125 {
7126 mn=Doxygen::memberNameLinkedMap->find(funcName+funcTempList);
7127 }
7128 if (mn==nullptr) // try without specialization
7129 {
7130 mn=Doxygen::memberNameLinkedMap->find(funcName);
7131 }
7132 if (!isRelated && !strongEnum && mn) // function name already found
7133 {
7134 AUTO_TRACE_ADD("member name exists ({} members with this name)",mn->size());
7135 if (!className.empty()) // class name is valid
7136 {
7137 if (funcSpec.empty()) // not a member specialization
7138 {
7139 addMemberFunction(root,mn,scopeName,namespaceName,className,funcType,funcName,
7140 funcArgs,funcTempList,exceptions,
7141 type,args,isFriend,spec,relates,funcDecl,overloaded,isFunc);
7142 }
7143 else if (cd) // member specialization
7144 {
7145 addMemberSpecialization(root,mn,cd,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
7146 }
7147 else
7148 {
7149 //printf("*** Specialized member %s of unknown scope %s%s found!\n",
7150 // qPrint(scopeName),qPrint(funcName),qPrint(funcArgs));
7151 }
7152 }
7153 else if (overloaded) // check if the function belongs to only one class
7154 {
7155 addOverloaded(root,mn,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
7156 }
7157 else // unrelated function with the same name as a member
7158 {
7159 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
7160 {
7161 DString fullFuncDecl=funcDecl;
7162 if (isFunc) fullFuncDecl+=argListToString(root->argList,true);
7163 warn(root->fileName,root->startLine,
7164 "Cannot determine class for function\n{}",
7165 fullFuncDecl
7166 );
7167 }
7168 }
7169 }
7170 else if (isRelated && !relates.empty())
7171 {
7172 AUTO_TRACE_ADD("related function scopeName='{}' className='{}'",scopeName,className);
7173 if (className.empty()) className=relates;
7174 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
7175 if ((cd=getClassMutable(scopeName)))
7176 {
7177 bool newMember=true; // assume we have a new member
7178 MemberDefMutable *mdDefine=nullptr;
7179 {
7180 mn = Doxygen::functionNameLinkedMap->find(funcName);
7181 if (mn)
7182 {
7183 for (const auto &imd : *mn)
7184 {
7185 MemberDefMutable *md = toMemberDefMutable(imd.get());
7186 if (md && md->isDefine())
7187 {
7188 mdDefine = md;
7189 break;
7190 }
7191 }
7192 }
7193 }
7194
7195 if (mdDefine) // macro definition is already created by the preprocessor and inserted as a file member
7196 {
7197 //printf("moving #define %s into class %s\n",qPrint(mdDefine->name()),qPrint(cd->name()));
7198
7199 // take mdDefine from the Doxygen::functionNameLinkedMap (without deleting the data)
7200 auto mdDefineTaken = Doxygen::functionNameLinkedMap->take(funcName,mdDefine);
7201 // insert it as a class member
7202 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==nullptr)
7203 {
7204 mn=Doxygen::memberNameLinkedMap->add(funcName);
7205 }
7206
7207 if (mdDefine->getFileDef())
7208 {
7209 mdDefine->getFileDef()->removeMember(mdDefine);
7210 }
7211 mdDefine->makeRelated();
7212 mdDefine->setMemberClass(cd);
7213 mdDefine->moveTo(cd);
7214 cd->insertMember(mdDefine);
7215 // also insert the member as an alias in the parent's scope, so it can be referenced also without cd's scope
7216 insertMemberAlias(cd->getOuterScope(),mdDefine);
7217 mn->push_back(std::move(mdDefineTaken));
7218 }
7219 else // normal member, needs to be created and added to the class
7220 {
7221 FileDef *fd=root->fileDef();
7222
7223 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==nullptr)
7224 {
7225 mn=Doxygen::memberNameLinkedMap->add(funcName);
7226 }
7227 else
7228 {
7229 // see if we got another member with matching arguments
7230 MemberDefMutable *rmd_found = nullptr;
7231 for (const auto &irmd : *mn)
7232 {
7233 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
7234 if (rmd)
7235 {
7236 const ArgumentList &rmdAl = rmd->argumentList();
7237
7238 newMember=
7239 className!=rmd->getOuterScope()->name() ||
7240 !matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmdAl,
7241 cd,fd,root->type,&root->argList,
7242 true,root->lang);
7243 if (!newMember)
7244 {
7245 rmd_found = rmd;
7246 }
7247 }
7248 }
7249 if (rmd_found) // member already exists as rmd -> add docs
7250 {
7251 AUTO_TRACE_ADD("addMemberDocs for related member {}",root->name);
7252 addMemberDocs(root,rmd_found,funcDecl,nullptr,overloaded,spec);
7253 newMember=false;
7254 }
7255 }
7256
7257 if (newMember) // need to create a new member
7258 {
7259 MemberType mtype = MemberType::Function;
7260 switch (root->mtype)
7261 {
7262 case MethodTypes::Method: mtype = MemberType::Function; break;
7263 case MethodTypes::Signal: mtype = MemberType::Signal; break;
7264 case MethodTypes::Slot: mtype = MemberType::Slot; break;
7265 case MethodTypes::DCOP: mtype = MemberType::DCOP; break;
7266 case MethodTypes::Property: mtype = MemberType::Property; break;
7267 case MethodTypes::Event: mtype = MemberType::Event; break;
7268 }
7269
7270 //printf("New related name '%s' '%d'\n",qPrint(funcName),
7271 // root->argList ? (int)root->argList->count() : -1);
7272
7273 // first note that we pass:
7274 // (root->tArgLists ? root->tArgLists->last() : nullptr)
7275 // for the template arguments for the new "member."
7276 // this accurately reflects the template arguments of
7277 // the related function, which don't have to do with
7278 // those of the related class.
7279 auto md = createMemberDef(
7280 root->fileName,root->startLine,root->startColumn,
7281 funcType,funcName,funcArgs,exceptions,
7282 root->protection,root->virt,
7283 root->isStatic,
7284 isMemberOf ? Relationship::Foreign : Relationship::Related,
7285 mtype,
7286 (!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList()),
7287 funcArgs.empty() ? ArgumentList() : root->argList,
7288 root->metaData);
7289 auto mmd = toMemberDefMutable(md.get());
7290
7291 // also insert the member as an alias in the parent's scope, so it can be referenced also without cd's scope
7292 insertMemberAlias(cd->getOuterScope(),md.get());
7293
7294 // we still have the problem that
7295 // MemberDef::writeDocumentation() in memberdef.cpp
7296 // writes the template argument list for the class,
7297 // as if this member is a member of the class.
7298 // fortunately, MemberDef::writeDocumentation() has
7299 // a special mechanism that allows us to totally
7300 // override the set of template argument lists that
7301 // are printed. We use that and set it to the
7302 // template argument lists of the related function.
7303 //
7304 mmd->setDefinitionTemplateParameterLists(root->tArgLists);
7305
7306 mmd->setTagInfo(root->tagInfo());
7307
7308 //printf("Related member name='%s' decl='%s' bodyLine='%d'\n",
7309 // qPrint(funcName),qPrint(funcDecl),root->bodyLine);
7310
7311 // try to find the matching line number of the body from the
7312 // global function list
7313 bool found=false;
7314 if (root->bodyLine==-1)
7315 {
7317 if (rmn)
7318 {
7319 const MemberDefMutable *rmd_found=nullptr;
7320 for (const auto &irmd : *rmn)
7321 {
7322 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
7323 if (rmd)
7324 {
7325 const ArgumentList &rmdAl = rmd->argumentList();
7326 // check for matching argument lists
7327 if (
7328 matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmdAl,
7329 cd,fd,root->type,&root->argList,
7330 true,root->lang)
7331 )
7332 {
7333 found=true;
7334 rmd_found = rmd;
7335 break;
7336 }
7337 }
7338 }
7339 if (rmd_found) // member found -> copy line number info
7340 {
7341 mmd->setBodySegment(rmd_found->getDefLine(),rmd_found->getStartBodyLine(),rmd_found->getEndBodyLine());
7342 mmd->setBodyDef(rmd_found->getBodyDef());
7343 //md->setBodyMember(rmd);
7344 }
7345 }
7346 }
7347 if (!found) // line number could not be found or is available in this
7348 // entry
7349 {
7350 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7351 mmd->setBodyDef(fd);
7352 }
7353
7354 //if (root->mGrpId!=-1)
7355 //{
7356 // md->setMemberGroup(memberGroupDict[root->mGrpId]);
7357 //}
7358 mmd->setMemberClass(cd);
7359 mmd->setMemberSpecifiers(spec);
7360 mmd->setVhdlSpecifiers(root->vhdlSpec);
7361 mmd->setDefinition(funcDecl);
7363 mmd->addQualifiers(root->qualifiers);
7364 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7365 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7366 mmd->setDocsForDefinition(!root->proto);
7367 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
7368 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7369 mmd->addSectionsToDefinition(root->anchors);
7370 mmd->setMemberGroupId(root->mGrpId);
7371 mmd->setLanguage(root->lang);
7372 mmd->setId(root->id);
7373 //md->setMemberDefTemplateArguments(root->mtArgList);
7374 cd->insertMember(md.get());
7375 cd->insertUsedFile(fd);
7376 mmd->setRefItems(root->sli);
7377 mmd->setRequirementReferences(root->rqli);
7378 if (root->relatesType==RelatesType::Duplicate) mmd->setRelatedAlso(cd);
7379 addMemberToGroups(root,md.get());
7381 //printf("Adding member=%s\n",qPrint(md->name()));
7382 mn->push_back(std::move(md));
7383 }
7384 if (root->relatesType==RelatesType::Duplicate)
7385 {
7386 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
7387 {
7388 DString fullFuncDecl=funcDecl;
7389 if (isFunc) fullFuncDecl+=argListToString(root->argList,true);
7390 warn(root->fileName,root->startLine,
7391 "Cannot determine file/namespace for relatedalso function\n{}",
7392 fullFuncDecl
7393 );
7394 }
7395 }
7396 }
7397 }
7398 else
7399 {
7400 warn_undoc(root->fileName,root->startLine, "class '{}' for related function '{}' is not documented.", className,funcName);
7401 }
7402 }
7403 else if (root->parent() && root->parent()->section.isObjcImpl())
7404 {
7405 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
7406 }
7407 else // unrelated not overloaded member found
7408 {
7409 bool globMem = findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec);
7410 if (className.empty() && !globMem)
7411 {
7412 warn(root->fileName,root->startLine, "class for member '{}' cannot be found.", funcName);
7413 }
7414 else if (!className.empty() && !globMem)
7415 {
7416 warn(root->fileName,root->startLine,
7417 "member '{}' of class '{}' cannot be found",
7418 funcName,className);
7419 }
7420 }
7421 }
7422 else
7423 {
7424 // this should not be called
7425 warn(root->fileName,root->startLine,"member with no name found.");
7426 }
7427 return;
7428}
7429
7430//----------------------------------------------------------------------
7431// find the members corresponding to the different documentation blocks
7432// that are extracted from the sources.
7433
7434static void filterMemberDocumentation(const Entry *root,const DString &relates)
7435{
7436 AUTO_TRACE("root->type='{}' root->inside='{}' root->name='{}' root->args='{}' section={} root->spec={} root->mGrpId={}",
7437 root->type,root->inside,root->name,root->args,root->section,root->spec,root->mGrpId);
7438 //printf("root->parent()->name=%s\n",qPrint(root->parent()->name));
7439 bool isFunc=true;
7440
7441 DString type = root->type;
7442 DString args = root->args;
7443 int i=-1, l=0;
7444 if ( // detect func variable/typedef to func ptr
7445 (i=findFunctionPtr(type.str(),root->lang,&l))!=-1
7446 )
7447 {
7448 //printf("Fixing function pointer!\n");
7449 // fix type and argument
7450 args.prepend(type.mid(i+l));
7451 type=type.left(i+l);
7452 //printf("Results type=%s,name=%s,args=%s\n",qPrint(type),qPrint(root->name),qPrint(args));
7453 isFunc=false;
7454 }
7455 else if ((type.startsWith("typedef ") && args.find('(')!=DString::npos))
7456 // detect function types marked as functions
7457 {
7458 isFunc=false;
7459 }
7460
7461 //printf("Member %s isFunc=%d\n",qPrint(root->name),isFunc);
7462 if (root->section.isMemberDoc())
7463 {
7464 //printf("Documentation for inline member '%s' found args='%s'\n",
7465 // qPrint(root->name),qPrint(args));
7466 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
7467 if (type.empty())
7468 {
7469 findMember(root,
7470 relates,
7471 type,
7472 args,
7473 root->name + args + root->exception,
7474 false,
7475 isFunc);
7476 }
7477 else
7478 {
7479 findMember(root,
7480 relates,
7481 type,
7482 args,
7483 type + " " + root->name + args + root->exception,
7484 false,
7485 isFunc);
7486 }
7487 }
7488 else if (root->section.isOverloadDoc())
7489 {
7490 //printf("Overloaded member %s found\n",qPrint(root->name));
7491 findMember(root,
7492 relates,
7493 type,
7494 args,
7495 root->name,
7496 true,
7497 isFunc);
7498 }
7499 else if
7500 ((root->section.isFunction() // function
7501 ||
7502 (root->section.isVariable() && // variable
7503 !type.empty() && // with a type
7504 g_compoundKeywords.find(type.str())==g_compoundKeywords.end() // that is not a keyword
7505 // (to skip forward declaration of class etc.)
7506 )
7507 )
7508 )
7509 {
7510 //printf("Documentation for member '%s' found args='%s' excp='%s'\n",
7511 // qPrint(root->name),qPrint(args),qPrint(root->exception));
7512 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
7513 //printf("Inside=%s\n Relates=%s\n",qPrint(root->inside),qPrint(relates));
7514 if (isTypeAClassFriend(type))
7515 {
7516 findMember(root,
7517 relates,
7518 type,
7519 args,
7520 type+" "+root->name,
7521 false,false);
7522
7523 }
7524 else if (!type.empty())
7525 {
7526 findMember(root,
7527 relates,
7528 type,
7529 args,
7530 type+" "+ root->inside + root->name + args + root->exception,
7531 false,isFunc);
7532 }
7533 else
7534 {
7535 findMember(root,
7536 relates,
7537 type,
7538 args,
7539 root->inside + root->name + args + root->exception,
7540 false,isFunc);
7541 }
7542 }
7543 else if (root->section.isDefine() && !relates.empty())
7544 {
7545 findMember(root,
7546 relates,
7547 type,
7548 args,
7549 root->name + args,
7550 false,
7551 !args.empty());
7552 }
7553 else if (root->section.isVariableDoc())
7554 {
7555 //printf("Documentation for variable %s found\n",qPrint(root->name));
7556 //if (!relates.empty()) printf(" Relates %s\n",qPrint(relates));
7557 findMember(root,
7558 relates,
7559 type,
7560 args,
7561 root->name,
7562 false,
7563 false);
7564 }
7565 else if (root->section.isExportedInterface() ||
7566 root->section.isIncludedService())
7567 {
7568 findMember(root,
7569 relates,
7570 type,
7571 args,
7572 type + " " + root->name,
7573 false,
7574 false);
7575 }
7576 else
7577 {
7578 // skip section
7579 //printf("skip section\n");
7580 }
7581}
7582
7583static void findMemberDocumentation(const Entry *root)
7584{
7585 if (root->section.isMemberDoc() ||
7586 root->section.isOverloadDoc() ||
7587 root->section.isFunction() ||
7588 root->section.isVariable() ||
7589 root->section.isVariableDoc() ||
7590 root->section.isDefine() ||
7591 root->section.isIncludedService() ||
7592 root->section.isExportedInterface()
7593 )
7594 {
7595 AUTO_TRACE();
7596 if (root->relatesType==RelatesType::Duplicate && !root->relates.empty())
7597 {
7599 }
7601 }
7602 for (const auto &e : root->children())
7603 {
7604 if (!e->section.isEnum())
7605 {
7606 findMemberDocumentation(e.get());
7607 }
7608 }
7609}
7610
7611//----------------------------------------------------------------------
7612
7613static void findObjCMethodDefinitions(const Entry *root)
7614{
7615 AUTO_TRACE();
7616 for (const auto &objCImpl : root->children())
7617 {
7618 if (objCImpl->section.isObjcImpl())
7619 {
7620 for (const auto &objCMethod : objCImpl->children())
7621 {
7622 if (objCMethod->section.isFunction())
7623 {
7624 //printf(" Found ObjC method definition %s\n",qPrint(objCMethod->name));
7625 findMember(objCMethod.get(),
7626 objCMethod->relates,
7627 objCMethod->type,
7628 objCMethod->args,
7629 objCMethod->type+" "+objCImpl->name+"::"+objCMethod->name+" "+objCMethod->args,
7630 false,true);
7631 objCMethod->section=EntryType::makeEmpty();
7632 }
7633 }
7634 }
7635 }
7636}
7637
7638//----------------------------------------------------------------------
7639// find and add the enumeration to their classes, namespaces or files
7640
7641static void findEnums(const Entry *root)
7642{
7643 if (root->section.isEnum())
7644 {
7645 AUTO_TRACE("name={}",root->name);
7646 ClassDefMutable *cd = nullptr;
7647 FileDef *fd = nullptr;
7648 NamespaceDefMutable *nd = nullptr;
7649 MemberNameLinkedMap *mnsd = nullptr;
7650 bool isGlobal = false;
7651 bool isRelated = false;
7652 bool isMemberOf = false;
7653 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7654
7655 DString name;
7656 DString scope;
7657
7658 if (size_t i = root->name.rfind("::"); i!=DString::npos) // scope is specified
7659 {
7660 scope=root->name.left(i); // extract scope
7661 if (root->lang==SrcLangExt::CSharp)
7662 {
7663 scope = mangleCSharpGenericName(scope);
7664 }
7665 name=root->name.right(root->name.length()-i-2); // extract name
7666 if ((cd=getClassMutable(scope))==nullptr)
7667 {
7669 }
7670 }
7671 else // no scope, check the scope in which the docs where found
7672 {
7673 if (root->parent()->section.isScope() && !root->parent()->name.empty()) // found enum docs inside a compound
7674 {
7675 scope=root->parent()->name;
7676 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7677 }
7678 name=root->name;
7679 }
7680
7681 if (!root->relates.empty())
7682 { // related member, prefix user specified scope
7683 isRelated=true;
7684 isMemberOf=(root->relatesType==RelatesType::MemberOf);
7685 if (getClass(root->relates)==nullptr && !scope.empty())
7686 scope=mergeScopes(scope,root->relates);
7687 else
7688 scope=root->relates;
7689 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7690 }
7691
7692 if (cd && !name.empty()) // found a enum inside a compound
7693 {
7694 //printf("Enum '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7695 fd=nullptr;
7697 isGlobal=false;
7698 }
7699 else if (nd) // found enum inside namespace
7700 {
7702 isGlobal=true;
7703 }
7704 else // found a global enum
7705 {
7706 fd=root->fileDef();
7708 isGlobal=true;
7709 }
7710
7711 if (!name.empty())
7712 {
7713 // new enum type
7714 AUTO_TRACE_ADD("new enum {} at line {} of {}",name,root->bodyLine,root->fileName);
7715 auto md = createMemberDef(
7716 root->fileName,root->startLine,root->startColumn,
7717 DString(),name,DString(),DString(),
7718 root->protection,Specifier::Normal,false,
7719 isMemberOf ? Relationship::Foreign : isRelated ? Relationship::Related : Relationship::Member,
7720 MemberType::Enumeration,
7722 auto mmd = toMemberDefMutable(md.get());
7723 mmd->setTagInfo(root->tagInfo());
7724 mmd->setLanguage(root->lang);
7725 mmd->setId(root->id);
7726 if (!isGlobal) mmd->setMemberClass(cd); else mmd->setFileDef(fd);
7727 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7728 mmd->setBodyDef(root->fileDef());
7729 mmd->setMemberSpecifiers(root->spec);
7730 mmd->setVhdlSpecifiers(root->vhdlSpec);
7731 mmd->setEnumBaseType(root->args);
7732 //printf("Enum %s definition at line %d of %s: protection=%d scope=%s\n",
7733 // qPrint(root->name),root->bodyLine,qPrint(root->fileName),root->protection,cd?qPrint(cd->name()):"<none>");
7734 mmd->addSectionsToDefinition(root->anchors);
7735 mmd->setMemberGroupId(root->mGrpId);
7737 mmd->addQualifiers(root->qualifiers);
7738 //printf("%s::setRefItems(%zu)\n",qPrint(md->name()),root->sli.size());
7739 mmd->setRefItems(root->sli);
7740 mmd->setRequirementReferences(root->rqli);
7741 //printf("found enum %s nd=%p\n",qPrint(md->name()),nd);
7742 bool defSet=false;
7743
7744 DString baseType = root->args;
7745 if (!baseType.empty())
7746 {
7747 baseType.prepend(" : ");
7748 }
7749
7750 if (nd)
7751 {
7752 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python)
7753 {
7754 mmd->setDefinition(name+baseType);
7755 }
7756 else
7757 {
7758 mmd->setDefinition(nd->name()+"::"+name+baseType);
7759 }
7760 //printf("definition=%s\n",md->definition());
7761 defSet=true;
7762 mmd->setNamespace(nd);
7763 nd->insertMember(md.get());
7764 }
7765
7766 // even if we have already added the enum to a namespace, we still
7767 // also want to add it to other appropriate places such as file
7768 // or class.
7769 if (isGlobal && (nd==nullptr || !nd->isAnonymous()))
7770 {
7771 if (!defSet) mmd->setDefinition(name+baseType);
7772 if (fd==nullptr && root->parent())
7773 {
7774 fd=root->parent()->fileDef();
7775 }
7776 if (fd)
7777 {
7778 mmd->setFileDef(fd);
7779 fd->insertMember(md.get());
7780 }
7781 }
7782 else if (cd)
7783 {
7784 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python)
7785 {
7786 mmd->setDefinition(name+baseType);
7787 }
7788 else
7789 {
7790 mmd->setDefinition(cd->name()+"::"+name+baseType);
7791 }
7792 cd->insertMember(md.get());
7793 cd->insertUsedFile(fd);
7794 }
7795 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7796 mmd->setDocsForDefinition(!root->proto);
7797 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7798 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7799
7800 //printf("Adding member=%s\n",qPrint(md->name()));
7801 addMemberToGroups(root,md.get());
7803
7804 MemberName *mn = mnsd->add(name);
7805 mn->push_back(std::move(md));
7806 }
7807 }
7808 else
7809 {
7810 for (const auto &e : root->children()) findEnums(e.get());
7811 }
7812}
7813
7814//----------------------------------------------------------------------
7815
7816static void addEnumValuesToEnums(const Entry *root)
7817{
7818 if (root->section.isEnum())
7819 // non anonymous enumeration
7820 {
7821 AUTO_TRACE("name={}",root->name);
7822 ClassDefMutable *cd = nullptr;
7823 FileDef *fd = nullptr;
7824 NamespaceDefMutable *nd = nullptr;
7825 MemberNameLinkedMap *mnsd = nullptr;
7826 bool isGlobal = false;
7827 bool isRelated = false;
7828 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7829
7830 DString name;
7831 DString scope;
7832
7833 if (size_t i = root->name.rfind("::"); i!=DString::npos) // scope is specified
7834 {
7835 scope=root->name.left(i); // extract scope
7836 if (root->lang==SrcLangExt::CSharp)
7837 {
7838 scope = mangleCSharpGenericName(scope);
7839 }
7840 name=root->name.right(root->name.length()-i-2); // extract name
7841 if ((cd=getClassMutable(scope))==nullptr)
7842 {
7844 }
7845 }
7846 else // no scope, check the scope in which the docs where found
7847 {
7848 if (root->parent()->section.isScope() && !root->parent()->name.empty()) // found enum docs inside a compound
7849 {
7850 scope=root->parent()->name;
7851 if (root->lang==SrcLangExt::CSharp)
7852 {
7853 scope = mangleCSharpGenericName(scope);
7854 }
7855 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7856 }
7857 name=root->name;
7858 }
7859
7860 if (!root->relates.empty())
7861 { // related member, prefix user specified scope
7862 isRelated=true;
7863 if (getClassMutable(root->relates)==nullptr && !scope.empty())
7864 scope=mergeScopes(scope,root->relates);
7865 else
7866 scope=root->relates;
7867 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7868 }
7869
7870 if (cd && !name.empty()) // found a enum inside a compound
7871 {
7872 //printf("Enum in class '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7873 fd=nullptr;
7875 isGlobal=false;
7876 }
7877 else if (nd && !nd->isAnonymous()) // found enum inside namespace
7878 {
7879 //printf("Enum in namespace '%s'::'%s'\n",qPrint(nd->name()),qPrint(name));
7881 isGlobal=true;
7882 }
7883 else // found a global enum
7884 {
7885 fd=root->fileDef();
7886 //printf("Enum in file '%s': '%s'\n",qPrint(fd->name()),qPrint(name));
7888 isGlobal=true;
7889 }
7890
7891 if (!name.empty())
7892 {
7893 //printf("** name=%s\n",qPrint(name));
7894 MemberName *mn = mnsd->find(name); // for all members with this name
7895 if (mn)
7896 {
7897 struct EnumValueInfo
7898 {
7899 EnumValueInfo(const DString &n,std::unique_ptr<MemberDef> &&md) :
7900 name(n), member(std::move(md)) {}
7901 DString name;
7902 std::unique_ptr<MemberDef> member;
7903 };
7904 std::vector< EnumValueInfo > extraMembers;
7905 // for each enum in this list
7906 for (const auto &imd : *mn)
7907 {
7908 MemberDefMutable *md = toMemberDefMutable(imd.get());
7909 // use raw pointer in this loop, since we modify mn and can then invalidate mdp.
7910 if (md && md->isEnumerate() && !root->children().empty())
7911 {
7912 AUTO_TRACE_ADD("enum {} with {} children",md->name(),root->children().size());
7913 for (const auto &e : root->children())
7914 {
7915 SrcLangExt sle = root->lang;
7916 bool isJavaLike = sle==SrcLangExt::CSharp || sle==SrcLangExt::Java || sle==SrcLangExt::XML;
7917 if ( isJavaLike || root->spec.isStrong())
7918 {
7919 if (sle == SrcLangExt::Cpp && e->section.isDefine()) continue;
7920 // Unlike classic C/C++ enums, for C++11, C# & Java enum
7921 // values are only visible inside the enum scope, so we must create
7922 // them here and only add them to the enum
7923 //printf("md->qualifiedName()=%s e->name=%s tagInfo=%p name=%s\n",
7924 // qPrint(md->qualifiedName()),qPrint(e->name),(void*)e->tagInfo(),qPrint(e->name));
7925 DString qualifiedName = root->name;
7926 if (size_t i = qualifiedName.rfind("::"); i!=DString::npos && sle==SrcLangExt::CSharp)
7927 {
7928 qualifiedName = mangleCSharpGenericName(qualifiedName.left(i))+qualifiedName.mid(i);
7929 }
7930 if (isJavaLike)
7931 {
7932 qualifiedName=substitute(qualifiedName,"::",".");
7933 }
7934 if (md->qualifiedName()==qualifiedName) // enum value scope matches that of the enum
7935 {
7936 DString fileName = e->fileName;
7937 if (fileName.empty() && e->tagInfo())
7938 {
7939 fileName = e->tagInfo()->tagName;
7940 }
7941 AUTO_TRACE_ADD("strong enum value {}",e->name);
7942 auto fmd = createMemberDef(
7943 fileName,e->startLine,e->startColumn,
7944 e->type,e->name,e->args,DString(),
7945 e->protection, Specifier::Normal,e->isStatic,Relationship::Member,
7946 MemberType::EnumValue,ArgumentList(),ArgumentList(),e->metaData);
7947 auto fmmd = toMemberDefMutable(fmd.get());
7948 NamespaceDef *mnd = md->getNamespaceDef();
7949 if (md->getClassDef())
7950 fmmd->setMemberClass(md->getClassDef());
7951 else if (mnd && (mnd->isLinkable() || mnd->isAnonymous()))
7952 fmmd->setNamespace(mnd);
7953 else if (md->getFileDef())
7954 fmmd->setFileDef(md->getFileDef());
7955 fmmd->setOuterScope(md->getOuterScope());
7956 fmmd->setTagInfo(e->tagInfo());
7957 fmmd->setLanguage(e->lang);
7958 fmmd->setBodySegment(e->startLine,e->bodyLine,e->endBodyLine);
7959 fmmd->setBodyDef(e->fileDef());
7960 fmmd->setId(e->id);
7961 fmmd->setDocumentation(e->doc,e->docFile,e->docLine);
7962 fmmd->setBriefDescription(e->brief,e->briefFile,e->briefLine);
7963 fmmd->addSectionsToDefinition(e->anchors);
7964 fmmd->setInitializer(e->initializer.str());
7965 fmmd->setMaxInitLines(e->initLines);
7966 fmmd->setMemberGroupId(e->mGrpId);
7967 fmmd->setExplicitExternal(e->explicitExternal,fileName,e->startLine,e->startColumn);
7968 fmmd->setRefItems(e->sli);
7969 fmmd->setRequirementReferences(e->rqli);
7970 fmmd->setAnchor();
7971 md->insertEnumField(fmd.get());
7972 fmmd->setEnumScope(md,true);
7973 extraMembers.emplace_back(e->name,std::move(fmd));
7974 }
7975 }
7976 else
7977 {
7978 AUTO_TRACE_ADD("enum value {}",e->name);
7979 //printf("e->name=%s isRelated=%d\n",qPrint(e->name),isRelated);
7980 MemberName *fmn=nullptr;
7981 MemberNameLinkedMap *emnsd = isRelated ? Doxygen::functionNameLinkedMap : mnsd;
7982 if (!e->name.empty() && (fmn=emnsd->find(e->name)))
7983 // get list of members with the same name as the field
7984 {
7985 for (const auto &ifmd : *fmn)
7986 {
7987 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
7988 if (fmd && fmd->isEnumValue() && fmd->getOuterScope()==md->getOuterScope()) // in same scope
7989 {
7990 //printf("found enum value with same name %s in scope %s\n",
7991 // qPrint(fmd->name()),qPrint(fmd->getOuterScope()->name()));
7992 if (nd && !nd->isAnonymous())
7993 {
7994 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
7995 {
7996 const NamespaceDef *fnd=fmd->getNamespaceDef();
7997 if (fnd==nd) // enum value is inside a namespace
7998 {
7999 md->insertEnumField(fmd);
8000 fmd->setEnumScope(md);
8001 }
8002 }
8003 }
8004 else if (isGlobal)
8005 {
8006 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
8007 {
8008 const FileDef *ffd=fmd->getFileDef();
8009 if (ffd==fd && ffd==md->getFileDef()) // enum value has file scope
8010 {
8011 md->insertEnumField(fmd);
8012 fmd->setEnumScope(md);
8013 }
8014 }
8015 }
8016 else if (isRelated && cd) // reparent enum value to
8017 // match the enum's scope
8018 {
8019 md->insertEnumField(fmd); // add field def to list
8020 fmd->setEnumScope(md); // cross ref with enum name
8021 fmd->setEnumClassScope(cd); // cross ref with enum name
8022 fmd->setOuterScope(cd);
8023 fmd->makeRelated();
8024 cd->insertMember(fmd);
8025 }
8026 else
8027 {
8028 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
8029 {
8030 const ClassDef *fcd=fmd->getClassDef();
8031 if (fcd==cd) // enum value is inside a class
8032 {
8033 //printf("Inserting enum field %s in enum scope %s\n",
8034 // qPrint(fmd->name()),qPrint(md->name()));
8035 md->insertEnumField(fmd); // add field def to list
8036 fmd->setEnumScope(md); // cross ref with enum name
8037 }
8038 }
8039 }
8040 }
8041 }
8042 }
8043 }
8044 }
8045 }
8046 }
8047 // move the newly added members into mn
8048 for (auto &e : extraMembers)
8049 {
8050 MemberName *emn=mnsd->add(e.name);
8051 emn->push_back(std::move(e.member));
8052 }
8053 }
8054 }
8055 }
8056 else
8057 {
8058 for (const auto &e : root->children()) addEnumValuesToEnums(e.get());
8059 }
8060}
8061
8062//----------------------------------------------------------------------
8063
8064static void addEnumDocs(const Entry *root,MemberDefMutable *md)
8065{
8066 AUTO_TRACE();
8067 // documentation outside a compound overrides the documentation inside it
8068 {
8069 md->setDocumentation(root->doc,root->docFile,root->docLine);
8070 md->setDocsForDefinition(!root->proto);
8071 }
8072
8073 // brief descriptions inside a compound override the documentation
8074 // outside it
8075 {
8076 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
8077 }
8078
8079 if (md->inbodyDocumentation().empty() || !root->parent()->name.empty())
8080 {
8082 }
8083
8084 if (root->mGrpId!=-1 && md->getMemberGroupId()==-1)
8085 {
8086 md->setMemberGroupId(root->mGrpId);
8087 }
8088
8090 md->setRefItems(root->sli);
8091 md->setRequirementReferences(root->rqli);
8092
8093 const GroupDef *gd=md->getGroupDef();
8094 if (gd==nullptr && !root->groups.empty()) // member not grouped but out-of-line documentation is
8095 {
8096 addMemberToGroups(root,md);
8097 }
8099}
8100
8101//----------------------------------------------------------------------
8102// Search for the name in the associated groups. If a matching member
8103// definition exists, then add the documentation to it and return true,
8104// otherwise false.
8105
8106static bool tryAddEnumDocsToGroupMember(const Entry *root,const DString &name)
8107{
8108 for (const auto &g : root->groups)
8109 {
8110 const GroupDef *gd = Doxygen::groupLinkedMap->find(g.groupname);
8111 if (gd)
8112 {
8113 MemberList *ml = gd->getMemberList(MemberListType::DecEnumMembers());
8114 if (ml)
8115 {
8116 MemberDefMutable *md = toMemberDefMutable(ml->find(name));
8117 if (md)
8118 {
8119 addEnumDocs(root,md);
8120 return true;
8121 }
8122 }
8123 }
8124 else if (!gd && g.pri == Grouping::GROUPING_INGROUP)
8125 {
8126 warn(root->fileName, root->startLine,
8127 "Found non-existing group '{}' for the command '{}', ignoring command",
8128 g.groupname, Grouping::getGroupPriName( g.pri )
8129 );
8130 }
8131 }
8132
8133 return false;
8134}
8135
8136//----------------------------------------------------------------------
8137// find the documentation blocks for the enumerations
8138
8139static void findEnumDocumentation(const Entry *root)
8140{
8141 if (root->section.isEnumDoc() &&
8142 !root->name.empty() &&
8143 root->name.at(0)!='@' // skip anonymous enums
8144 )
8145 {
8146 DString name;
8147 DString scope;
8148 if (size_t i = root->name.rfind("::"); i!=DString::npos) // scope is specified as part of the name
8149 {
8150 name=root->name.mid(i+2); // extract name
8151 scope=root->name.left(i); // extract scope
8152 //printf("Scope='%s' Name='%s'\n",qPrint(scope),qPrint(name));
8153 }
8154 else // just the name
8155 {
8156 name=root->name;
8157 }
8158 if (root->parent()->section.isScope() && !root->parent()->name.empty()) // found enum docs inside a compound
8159 {
8160 if (!scope.empty()) scope.prepend("::");
8161 scope.prepend(root->parent()->name);
8162 }
8163 const ClassDef *cd = getClass(scope);
8165 const FileDef *fd = root->fileDef();
8166 AUTO_TRACE("Found docs for enum with name '{}' and scope '{}' in context '{}' cd='{}', nd='{}' fd='{}'",
8167 name,scope,root->parent()->name,
8168 cd ? cd->name() : DString("<none>"),
8169 nd ? nd->name() : DString("<none>"),
8170 fd ? fd->name() : DString("<none>"));
8171
8172 if (!name.empty())
8173 {
8174 bool found = tryAddEnumDocsToGroupMember(root, name);
8175 if (!found)
8176 {
8178 if (mn)
8179 {
8180 for (const auto &imd : *mn)
8181 {
8182 MemberDefMutable *md = toMemberDefMutable(imd.get());
8183 if (md && md->isEnumerate())
8184 {
8185 const ClassDef *mcd = md->getClassDef();
8186 const NamespaceDef *mnd = md->getNamespaceDef();
8187 const FileDef *mfd = md->getFileDef();
8188 if (cd && mcd==cd)
8189 {
8190 AUTO_TRACE_ADD("Match found for class scope");
8191 addEnumDocs(root,md);
8192 found = true;
8193 break;
8194 }
8195 else if (cd==nullptr && mcd==nullptr && nd!=nullptr && mnd==nd)
8196 {
8197 AUTO_TRACE_ADD("Match found for namespace scope");
8198 addEnumDocs(root,md);
8199 found = true;
8200 break;
8201 }
8202 else if (cd==nullptr && nd==nullptr && mcd==nullptr && mnd==nullptr && fd==mfd)
8203 {
8204 AUTO_TRACE_ADD("Match found for global scope");
8205 addEnumDocs(root,md);
8206 found = true;
8207 break;
8208 }
8209 }
8210 }
8211 }
8212 }
8213 if (!found)
8214 {
8215 warn(root->fileName,root->startLine, "Documentation for undefined enum '{}' found.", name);
8216 }
8217 }
8218 }
8219 for (const auto &e : root->children()) findEnumDocumentation(e.get());
8220}
8221
8222// search for each enum (member or function) in mnl if it has documented
8223// enum values.
8224static void findDEV(const MemberNameLinkedMap &mnsd)
8225{
8226 // for each member name
8227 for (const auto &mn : mnsd)
8228 {
8229 // for each member definition
8230 for (const auto &imd : *mn)
8231 {
8232 MemberDefMutable *md = toMemberDefMutable(imd.get());
8233 if (md && md->isEnumerate()) // member is an enum
8234 {
8235 int documentedEnumValues=0;
8236 // for each enum value
8237 for (const auto &fmd : md->enumFieldList())
8238 {
8239 if (fmd->isLinkableInProject()) documentedEnumValues++;
8240 }
8241 // at least one enum value is documented
8242 if (documentedEnumValues>0) md->setDocumentedEnumValues(true);
8243 }
8244 }
8245 }
8246}
8247
8248// search for each enum (member or function) if it has documented enum
8249// values.
8255
8256//----------------------------------------------------------------------
8257
8259{
8260 auto &index = Index::instance();
8261 // for each class member name
8262 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8263 {
8264 // for each member definition
8265 for (const auto &md : *mn)
8266 {
8267 index.addClassMemberNameToIndex(md.get());
8268 if (md->getModuleDef())
8269 {
8270 index.addModuleMemberNameToIndex(md.get());
8271 }
8272 }
8273 }
8274 // for each file/namespace function name
8275 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8276 {
8277 // for each member definition
8278 for (const auto &md : *mn)
8279 {
8280 if (md->getNamespaceDef())
8281 {
8282 index.addNamespaceMemberNameToIndex(md.get());
8283 }
8284 else
8285 {
8286 index.addFileMemberNameToIndex(md.get());
8287 }
8288 if (md->getModuleDef())
8289 {
8290 index.addModuleMemberNameToIndex(md.get());
8291 }
8292 }
8293 }
8294
8295 index.sortMemberIndexLists();
8296}
8297
8298//----------------------------------------------------------------------
8299
8300static void addToIndices()
8301{
8302 for (const auto &cd : *Doxygen::classLinkedMap)
8303 {
8304 if (cd->isLinkableInProject())
8305 {
8306 Doxygen::indexList->addIndexItem(cd.get(),nullptr);
8307 if (Doxygen::searchIndex.enabled())
8308 {
8309 Doxygen::searchIndex.setCurrentDoc(cd.get(),cd->anchor(),false);
8310 Doxygen::searchIndex.addWord(cd->localName(),true);
8311 }
8312 }
8313 }
8314
8315 for (const auto &cd : *Doxygen::conceptLinkedMap)
8316 {
8317 if (cd->isLinkableInProject())
8318 {
8319 Doxygen::indexList->addIndexItem(cd.get(),nullptr);
8320 if (Doxygen::searchIndex.enabled())
8321 {
8322 Doxygen::searchIndex.setCurrentDoc(cd.get(),cd->anchor(),false);
8323 Doxygen::searchIndex.addWord(cd->localName(),true);
8324 }
8325 }
8326 }
8327
8328 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8329 {
8330 if (nd->isLinkableInProject())
8331 {
8332 Doxygen::indexList->addIndexItem(nd.get(),nullptr);
8333 if (Doxygen::searchIndex.enabled())
8334 {
8335 Doxygen::searchIndex.setCurrentDoc(nd.get(),nd->anchor(),false);
8336 Doxygen::searchIndex.addWord(nd->localName(),true);
8337 }
8338 }
8339 }
8340
8341 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8342 {
8343 for (const auto &fd : *fn)
8344 {
8345 if (Doxygen::searchIndex.enabled() && fd->isLinkableInProject())
8346 {
8347 Doxygen::searchIndex.setCurrentDoc(fd.get(),fd->anchor(),false);
8348 Doxygen::searchIndex.addWord(fd->localName(),true);
8349 }
8350 }
8351 }
8352
8353 auto addWordsForTitle = [](const Definition *d,const DString &anchor,const DString &title)
8354 {
8356 if (Doxygen::searchIndex.enabled())
8357 {
8358 Doxygen::searchIndex.setCurrentDoc(d,anchor,false);
8359 std::string s = title.str();
8360 static const reg::Ex re(R"(\a[\w-]*)");
8361 reg::Iterator it(s,re);
8363 for (; it!=end ; ++it)
8364 {
8365 const auto &match = *it;
8366 std::string matchStr = match.str();
8367 Doxygen::searchIndex.addWord(matchStr,true);
8368 }
8369 }
8370 };
8371
8372 for (const auto &gd : *Doxygen::groupLinkedMap)
8373 {
8374 if (gd->isLinkableInProject())
8375 {
8376 addWordsForTitle(gd.get(),gd->anchor(),gd->groupTitle());
8377 }
8378 }
8379
8380 for (const auto &pd : *Doxygen::pageLinkedMap)
8381 {
8382 if (pd->isLinkableInProject())
8383 {
8384 addWordsForTitle(pd.get(),pd->anchor(),pd->title());
8385 }
8386 }
8387
8389 {
8390 addWordsForTitle(Doxygen::mainPage.get(),Doxygen::mainPage->anchor(),Doxygen::mainPage->title());
8391 }
8392
8393 auto addMemberToSearchIndex = [](const MemberDef *md)
8394 {
8395 if (Doxygen::searchIndex.enabled())
8396 {
8397 Doxygen::searchIndex.setCurrentDoc(md,md->anchor(),false);
8398 DString ln=md->localName();
8399 DString qn=md->qualifiedName();
8401 if (ln!=qn)
8402 {
8404 if (md->getClassDef())
8405 {
8406 Doxygen::searchIndex.addWord(md->getClassDef()->displayName(),true);
8407 }
8408 if (md->getNamespaceDef())
8409 {
8410 Doxygen::searchIndex.addWord(md->getNamespaceDef()->displayName(),true);
8411 }
8412 }
8413 }
8414 };
8415
8416 auto getScope = [](const MemberDef *md)
8417 {
8418 const Definition *scope = nullptr;
8419 if (md->getGroupDef()) scope = md->getGroupDef();
8420 else if (md->getClassDef()) scope = md->getClassDef();
8421 else if (md->getNamespaceDef()) scope = md->getNamespaceDef();
8422 else if (md->getFileDef()) scope = md->getFileDef();
8423 return scope;
8424 };
8425
8426 auto addMemberToIndices = [addMemberToSearchIndex,getScope](const MemberDef *md)
8427 {
8428 if (md->isLinkableInProject())
8429 {
8430 if (!(md->isEnumerate() && md->isAnonymous()))
8431 {
8432 Doxygen::indexList->addIndexItem(getScope(md),md);
8433 addMemberToSearchIndex(md);
8434 }
8435 if (md->isEnumerate())
8436 {
8437 for (const auto &fmd : md->enumFieldList())
8438 {
8439 Doxygen::indexList->addIndexItem(getScope(fmd),fmd);
8440 addMemberToSearchIndex(fmd);
8441 }
8442 }
8443 }
8444 };
8445
8446 // for each class member name
8447 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8448 {
8449 // for each member definition
8450 for (const auto &md : *mn)
8451 {
8452 addMemberToIndices(md.get());
8453 }
8454 }
8455 // for each file/namespace function name
8456 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8457 {
8458 // for each member definition
8459 for (const auto &md : *mn)
8460 {
8461 addMemberToIndices(md.get());
8462 }
8463 }
8464}
8465
8466//----------------------------------------------------------------------
8467
8469{
8470 // for each member name
8471 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8472 {
8473 // for each member definition
8474 for (const auto &imd : *mn)
8475 {
8476 MemberDefMutable *md = toMemberDefMutable(imd.get());
8477 if (md)
8478 {
8480 }
8481 }
8482 }
8483 // for each member name
8484 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8485 {
8486 // for each member definition
8487 for (const auto &imd : *mn)
8488 {
8489 MemberDefMutable *md = toMemberDefMutable(imd.get());
8490 if (md)
8491 {
8493 }
8494 }
8495 }
8496}
8497
8498// recursive helper function looking for reimplements/implemented
8499// by relations between class cd and direct or indirect base class bcd
8501{
8502 for (const auto &mn : cd->memberNameInfoLinkedMap()) // for each member in class cd with a unique name
8503 {
8504 for (const auto &imd : *mn) // for each member with a given name
8505 {
8506 MemberDefMutable *md = toMemberDefMutable(imd->memberDef());
8507 if (md && (md->isFunction() || md->isCSharpProperty())) // filter on reimplementable members
8508 {
8509 ClassDef *mbcd = bcd->classDef;
8510 if (mbcd && mbcd->isLinkable()) // filter on linkable classes
8511 {
8512 const auto &bmn = mbcd->memberNameInfoLinkedMap();
8513 const auto &bmni = bmn.find(mn->memberName());
8514 if (bmni) // there are base class members with the same name
8515 {
8516 for (const auto &ibmd : *bmni) // for base class member with that name
8517 {
8518 MemberDefMutable *bmd = toMemberDefMutable(ibmd->memberDef());
8519 if (bmd) // not part of an inline namespace
8520 {
8521 auto lang = bmd->getLanguage();
8522 auto compType = mbcd->compoundType();
8523 if (bmd->virtualness()!=Specifier::Normal ||
8524 lang==SrcLangExt::Python ||
8525 lang==SrcLangExt::Java ||
8526 lang==SrcLangExt::PHP ||
8527 compType==ClassDef::Interface ||
8528 compType==ClassDef::Protocol)
8529 {
8530 const ArgumentList &bmdAl = bmd->argumentList();
8531 const ArgumentList &mdAl = md->argumentList();
8532 //printf(" Base argList='%s'\n Super argList='%s'\n",
8533 // qPrint(argListToString(bmdAl)),
8534 // qPrint(argListToString(mdAl))
8535 // );
8536 if (
8537 lang==SrcLangExt::Python ||
8538 matchArguments2(bmd->getOuterScope(),bmd->getFileDef(),bmd->typeString(),&bmdAl,
8539 md->getOuterScope(), md->getFileDef(), md->typeString(),&mdAl,
8540 true,lang
8541 )
8542 )
8543 {
8544 if (lang==SrcLangExt::Python && md->name().startsWith("__")) continue; // private members do not reimplement
8545 //printf("match!\n");
8546 const MemberDef *rmd = md->reimplements();
8547 if (rmd==nullptr) // not already assigned
8548 {
8549 //printf("%s: setting (new) reimplements member %s\n",qPrint(md->qualifiedName()),qPrint(bmd->qualifiedName()));
8550 md->setReimplements(bmd);
8551 }
8552 //printf("%s: add reimplementedBy member %s\n",qPrint(bmd->qualifiedName()),qPrint(md->qualifiedName()));
8553 bmd->insertReimplementedBy(md);
8554 }
8555 else
8556 {
8557 //printf("no match!\n");
8558 }
8559 }
8560 }
8561 }
8562 }
8563 }
8564 }
8565 }
8566 }
8567
8568 // do also for indirect base classes
8569 for (const auto &bbcd : bcd->classDef->baseClasses())
8570 {
8572 }
8573}
8574
8575//----------------------------------------------------------------------
8576// computes the relation between all members. For each member 'm'
8577// the members that override the implementation of 'm' are searched and
8578// the member that 'm' overrides is searched.
8579
8581{
8582 for (const auto &cd : *Doxygen::classLinkedMap)
8583 {
8584 if (cd->isLinkable())
8585 {
8586 for (const auto &bcd : cd->baseClasses())
8587 {
8589 }
8590 }
8591 }
8592}
8593
8594//----------------------------------------------------------------------------
8595
8597{
8598 // for each class
8599 for (const auto &cd : *Doxygen::classLinkedMap)
8600 {
8601 // that is a template
8602 for (const auto &ti : cd->getTemplateInstances())
8603 {
8604 ClassDefMutable *tcdm = toClassDefMutable(ti.classDef);
8605 if (tcdm)
8606 {
8607 tcdm->addMembersToTemplateInstance(cd.get(),cd->templateArguments(),ti.templSpec);
8608 }
8609 }
8610 }
8611}
8612
8613//----------------------------------------------------------------------------
8614
8615static void mergeCategories()
8616{
8617 AUTO_TRACE();
8618 // merge members of categories into the class they extend
8619 for (const auto &cd : *Doxygen::classLinkedMap)
8620 {
8621 if (size_t i=cd->name().find('('); i!=DString::npos) // it is an Objective-C category
8622 {
8623 DString baseName=cd->name().left(i);
8624 ClassDefMutable *baseClass=toClassDefMutable(Doxygen::classLinkedMap->find(baseName));
8625 if (baseClass)
8626 {
8627 AUTO_TRACE_ADD("merging members of category {} into {}",cd->name(),baseClass->name());
8628 baseClass->mergeCategory(cd.get());
8629 }
8630 }
8631 }
8632}
8633
8634// builds the list of all members for each class
8635
8637{
8638 // merge the member list of base classes into the inherited classes.
8639 for (const auto &cd : *Doxygen::classLinkedMap)
8640 {
8641 if (// !cd->isReference() && // not an external class
8642 cd->subClasses().empty() && // is a root of the hierarchy
8643 !cd->baseClasses().empty()) // and has at least one base class
8644 {
8645 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8646 if (cdm)
8647 {
8648 //printf("*** merging members for %s\n",qPrint(cd->name()));
8649 cdm->mergeMembers();
8650 }
8651 }
8652 }
8653 // now sort the member list of all members for all classes.
8654 for (const auto &cd : *Doxygen::classLinkedMap)
8655 {
8656 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8657 if (cdm)
8658 {
8659 cdm->sortAllMembersList();
8660 }
8661 }
8662}
8663
8664//----------------------------------------------------------------------------
8665
8667{
8668 auto processSourceFile = [](FileDef *fd,OutputList &ol,ClangTUParser *parser)
8669 {
8670 bool showSources = fd->generateSourceFile() && !Htags::useHtags; // sources need to be shown in the output
8671 bool parseSources = !fd->isReference() && Doxygen::parseSourcesNeeded; // we needed to parse the sources even if we do not show them
8672 if (showSources)
8673 {
8674 msg("Generating code for file {}...\n",fd->docName());
8675 fd->writeSourceHeader(ol);
8676 fd->writeSourceBody(ol,parser);
8677 fd->writeSourceFooter(ol);
8678 }
8679 else if (parseSources)
8680 {
8681 msg("Parsing code for file {}...\n",fd->docName());
8682 fd->parseSource(parser);
8683 }
8684 };
8685 if (!Doxygen::inputNameLinkedMap->empty())
8686 {
8687#if USE_LIBCLANG
8689 {
8690 StringUnorderedSet processedFiles;
8691
8692 // create a dictionary with files to process
8693 StringUnorderedSet filesToProcess;
8694
8695 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8696 {
8697 for (const auto &fd : *fn)
8698 {
8699 filesToProcess.insert(fd->absFilePath().str());
8700 }
8701 }
8702 // process source files (and their include dependencies)
8703 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8704 {
8705 for (const auto &fd : *fn)
8706 {
8707 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt::Cpp &&
8708 (fd->generateSourceFile() ||
8710 )
8711 )
8712 {
8713 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8714 clangParser->parse();
8715 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8716
8717 for (auto incFile : clangParser->filesInSameTU())
8718 {
8719 if (filesToProcess.find(incFile)!=filesToProcess.end() && // part of input
8720 fd->absFilePath()!=incFile && // not same file
8721 processedFiles.find(incFile)==processedFiles.end()) // not yet marked as processed
8722 {
8723 StringVector moreFiles;
8724 bool ambig = false;
8725 FileDef *ifd=Doxygen::inputNameLinkedMap->findFileDef(incFile,ambig);
8726 if (ifd && !ifd->isReference())
8727 {
8728 processSourceFile(ifd,*g_outputList,clangParser.get());
8729 processedFiles.insert(incFile);
8730 }
8731 }
8732 }
8733 processedFiles.insert(fd->absFilePath().str());
8734 }
8735 }
8736 }
8737 // process remaining files
8738 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8739 {
8740 for (const auto &fd : *fn)
8741 {
8742 if (processedFiles.find(fd->absFilePath().str())==processedFiles.end()) // not yet processed
8743 {
8744 if (fd->getLanguage()==SrcLangExt::Cpp) // C/C++ file, use clang parser
8745 {
8746 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8747 clangParser->parse();
8748 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8749 }
8750 else // non C/C++ file, use built-in parser
8751 {
8752 processSourceFile(fd.get(),*g_outputList,nullptr);
8753 }
8754 }
8755 }
8756 }
8757 }
8758 else
8759#endif
8760 {
8761 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8762 if (numThreads>1)
8763 {
8764 msg("Generating code files using {} threads.\n",numThreads);
8765 struct SourceContext
8766 {
8767 SourceContext(FileDef *fd_,bool gen_,const OutputList &ol_)
8768 : fd(fd_), generateSourceFile(gen_), ol(ol_) {}
8769 FileDef *fd;
8770 bool generateSourceFile;
8771 OutputList ol;
8772 };
8773 ThreadPool threadPool(numThreads);
8774 std::vector< std::future< std::shared_ptr<SourceContext> > > results;
8775 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8776 {
8777 for (const auto &fd : *fn)
8778 {
8779 bool generateSourceFile = fd->generateSourceFile() && !Htags::useHtags;
8780 auto ctx = std::make_shared<SourceContext>(fd.get(),generateSourceFile,*g_outputList);
8781 auto processFile = [ctx]()
8782 {
8783 if (ctx->generateSourceFile)
8784 {
8785 msg("Generating code for file {}...\n",ctx->fd->docName());
8786 }
8787 else
8788 {
8789 msg("Parsing code for file {}...\n",ctx->fd->docName());
8790 }
8791 StringVector filesInSameTu;
8792 ctx->fd->getAllIncludeFilesRecursively(filesInSameTu);
8793 if (ctx->generateSourceFile) // sources need to be shown in the output
8794 {
8795 ctx->fd->writeSourceHeader(ctx->ol);
8796 ctx->fd->writeSourceBody(ctx->ol,nullptr);
8797 ctx->fd->writeSourceFooter(ctx->ol);
8798 }
8799 else if (!ctx->fd->isReference() && Doxygen::parseSourcesNeeded)
8800 // we needed to parse the sources even if we do not show them
8801 {
8802 ctx->fd->parseSource(nullptr);
8803 }
8804 return ctx;
8805 };
8806 results.emplace_back(threadPool.queue(processFile));
8807 }
8808 }
8809 for (auto &f : results)
8810 {
8811 auto ctx = f.get();
8812 }
8813 }
8814 else // single threaded version
8815 {
8816 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8817 {
8818 for (const auto &fd : *fn)
8819 {
8820 StringVector filesInSameTu;
8821 fd->getAllIncludeFilesRecursively(filesInSameTu);
8822 processSourceFile(fd.get(),*g_outputList,nullptr);
8823 }
8824 }
8825 }
8826 }
8827 }
8828}
8829
8830//----------------------------------------------------------------------------
8831
8832static void generateFileDocs()
8833{
8834 if (Index::instance().numDocumentedFiles()==0) return;
8835
8836 if (!Doxygen::inputNameLinkedMap->empty())
8837 {
8838 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8839 if (numThreads>1) // multi threaded processing
8840 {
8841 struct DocContext
8842 {
8843 DocContext(FileDef *fd_,const OutputList &ol_)
8844 : fd(fd_), ol(ol_) {}
8845 FileDef *fd;
8846 OutputList ol;
8847 };
8848 ThreadPool threadPool(numThreads);
8849 std::vector< std::future< std::shared_ptr<DocContext> > > results;
8850 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8851 {
8852 for (const auto &fd : *fn)
8853 {
8854 bool doc = fd->isLinkableInProject();
8855 if (doc)
8856 {
8857 auto ctx = std::make_shared<DocContext>(fd.get(),*g_outputList);
8858 auto processFile = [ctx]() {
8859 msg("Generating docs for file {}...\n",ctx->fd->docName());
8860 ctx->fd->writeDocumentation(ctx->ol);
8861 return ctx;
8862 };
8863 results.emplace_back(threadPool.queue(processFile));
8864 }
8865 }
8866 }
8867 for (auto &f : results)
8868 {
8869 auto ctx = f.get();
8870 }
8871 }
8872 else // single threaded processing
8873 {
8874 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8875 {
8876 for (const auto &fd : *fn)
8877 {
8878 bool doc = fd->isLinkableInProject();
8879 if (doc)
8880 {
8881 msg("Generating docs for file {}...\n",fd->docName());
8882 fd->writeDocumentation(*g_outputList);
8883 }
8884 }
8885 }
8886 }
8887 }
8888}
8889
8890//----------------------------------------------------------------------------
8891
8893{
8894 // add source references for class definitions
8895 for (const auto &cd : *Doxygen::classLinkedMap)
8896 {
8897 const FileDef *fd=cd->getBodyDef();
8898 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8899 {
8900 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),nullptr);
8901 }
8902 }
8903 // add source references for concept definitions
8904 for (const auto &cd : *Doxygen::conceptLinkedMap)
8905 {
8906 const FileDef *fd=cd->getBodyDef();
8907 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8908 {
8909 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),nullptr);
8910 }
8911 }
8912 // add source references for namespace definitions
8913 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8914 {
8915 const FileDef *fd=nd->getBodyDef();
8916 if (fd && nd->isLinkableInProject() && nd->getStartDefLine()!=-1)
8917 {
8918 const_cast<FileDef*>(fd)->addSourceRef(nd->getStartDefLine(),nd.get(),nullptr);
8919 }
8920 }
8921
8922 // add source references for member names
8923 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8924 {
8925 for (const auto &md : *mn)
8926 {
8927 //printf("class member %s: def=%s body=%d link?=%d\n",
8928 // qPrint(md->name()),
8929 // md->getBodyDef()?qPrint(md->getBodyDef()->name()):"<none>",
8930 // md->getStartBodyLine(),md->isLinkableInProject());
8931 const FileDef *fd=md->getBodyDef();
8932 if (fd &&
8933 md->getStartDefLine()!=-1 &&
8934 md->isLinkableInProject() &&
8936 )
8937 {
8938 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8939 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8940 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8941 }
8942 }
8943 }
8944 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8945 {
8946 for (const auto &md : *mn)
8947 {
8948 const FileDef *fd=md->getBodyDef();
8949 //printf("member %s body=[%d,%d] fd=%p link=%d parseSources=%d\n",
8950 // qPrint(md->name()),
8951 // md->getStartBodyLine(),md->getEndBodyLine(),fd,
8952 // md->isLinkableInProject(),
8953 // Doxygen::parseSourcesNeeded);
8954 if (fd &&
8955 md->getStartDefLine()!=-1 &&
8956 md->isLinkableInProject() &&
8958 )
8959 {
8960 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8961 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8962 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8963 }
8964 }
8965 }
8966}
8967
8968//----------------------------------------------------------------------------
8969
8970// add the macro definitions found during preprocessing as file members
8971static void buildDefineList()
8972{
8973 AUTO_TRACE();
8974 for (const auto &s : g_inputFiles)
8975 {
8976 auto it = Doxygen::macroDefinitions.find(s);
8978 {
8979 for (const auto &def : it->second)
8980 {
8981 auto md = createMemberDef(
8982 def.fileName,def.lineNr,def.columnNr,
8983 "#define",def.name,def.args,DString(),
8984 Protection::Public,Specifier::Normal,false,Relationship::Member,MemberType::Define,
8985 ArgumentList(),ArgumentList(),"");
8986 auto mmd = toMemberDefMutable(md.get());
8987
8988 if (!def.args.empty())
8989 {
8990 mmd->moveArgumentList(stringToArgumentList(SrcLangExt::Cpp, def.args));
8991 }
8992 mmd->setInitializer(def.definition);
8993 mmd->setFileDef(def.fileDef);
8994 mmd->setDefinition("#define "+def.name);
8995
8997 if (def.fileDef)
8998 {
8999 const MemberList *defMl = def.fileDef->getMemberList(MemberListType::DocDefineMembers());
9000 if (defMl)
9001 {
9002 const MemberDef *defMd = defMl->findRev(def.name);
9003 if (defMd) // definition already stored
9004 {
9005 mmd->setRedefineCount(defMd->redefineCount()+1);
9006 }
9007 }
9008 def.fileDef->insertMember(md.get());
9009 }
9010 AUTO_TRACE_ADD("adding macro {} with definition {}",def.name,def.definition);
9011 mn->push_back(std::move(md));
9012 }
9013 }
9014 }
9015}
9016
9017//----------------------------------------------------------------------------
9018
9019static void sortMemberLists()
9020{
9021 // sort class member lists
9022 for (const auto &cd : *Doxygen::classLinkedMap)
9023 {
9024 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9025 if (cdm)
9026 {
9027 cdm->sortMemberLists();
9028 }
9029 }
9030
9031 // sort namespace member lists
9032 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9033 {
9035 if (ndm)
9036 {
9037 ndm->sortMemberLists();
9038 }
9039 }
9040
9041 // sort file member lists
9042 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9043 {
9044 for (const auto &fd : *fn)
9045 {
9046 fd->sortMemberLists();
9047 }
9048 }
9049
9050 // sort group member lists
9051 for (const auto &gd : *Doxygen::groupLinkedMap)
9052 {
9053 gd->sortMemberLists();
9054 }
9055
9057}
9058
9059//----------------------------------------------------------------------------
9060
9061static bool isSymbolHidden(const Definition *d)
9062{
9063 bool hidden = d->isHidden();
9064 const Definition *parent = d->getOuterScope();
9065 return parent ? hidden || isSymbolHidden(parent) : hidden;
9066}
9067
9069{
9070 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9071 if (numThreads>1)
9072 {
9073 ThreadPool threadPool(numThreads);
9074 std::vector < std::future< void > > results;
9075 // queue the work
9076 for (const auto &[name,symList] : *Doxygen::symbolMap)
9077 {
9078 for (const auto &def : symList)
9079 {
9081 if (dm && !isSymbolHidden(def) && !def->isArtificial() && def->isLinkableInProject())
9082 {
9083 auto processTooltip = [dm]() {
9084 dm->computeTooltip();
9085 };
9086 results.emplace_back(threadPool.queue(processTooltip));
9087 }
9088 }
9089 }
9090 // wait for the results
9091 for (auto &f : results)
9092 {
9093 f.get();
9094 }
9095 }
9096 else
9097 {
9098 for (const auto &[name,symList] : *Doxygen::symbolMap)
9099 {
9100 for (const auto &def : symList)
9101 {
9103 if (dm && !isSymbolHidden(def) && !def->isArtificial() && def->isLinkableInProject())
9104 {
9105 dm->computeTooltip();
9106 }
9107 }
9108 }
9109 }
9110}
9111
9112//----------------------------------------------------------------------------
9113
9115{
9116 for (const auto &cd : *Doxygen::classLinkedMap)
9117 {
9118 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9119 if (cdm)
9120 {
9121 cdm->setAnonymousEnumType();
9122 }
9123 }
9124}
9125
9126//----------------------------------------------------------------------------
9127
9128static void countMembers()
9129{
9130 for (const auto &cd : *Doxygen::classLinkedMap)
9131 {
9132 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9133 if (cdm)
9134 {
9135 cdm->countMembers();
9136 }
9137 }
9138
9139 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9140 {
9142 if (ndm)
9143 {
9144 ndm->countMembers();
9145 }
9146 }
9147
9148 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9149 {
9150 for (const auto &fd : *fn)
9151 {
9152 fd->countMembers();
9153 }
9154 }
9155
9156 for (const auto &gd : *Doxygen::groupLinkedMap)
9157 {
9158 gd->countMembers();
9159 }
9160
9161 auto &mm = ModuleManager::instance();
9162 mm.countMembers();
9163}
9164
9165
9166//----------------------------------------------------------------------------
9167// generate the documentation for all classes
9168
9169static void generateDocsForClassList(const std::vector<ClassDefMutable*> &classList)
9170{
9171 AUTO_TRACE();
9172 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9173 if (numThreads>1) // multi threaded processing
9174 {
9175 struct DocContext
9176 {
9177 DocContext(ClassDefMutable *cd_,const OutputList &ol_)
9178 : cd(cd_), ol(ol_) {}
9179 ClassDefMutable *cd;
9180 OutputList ol;
9181 };
9182 ThreadPool threadPool(numThreads);
9183 std::vector< std::future< std::shared_ptr<DocContext> > > results;
9184 for (const auto &cd : classList)
9185 {
9186 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
9187 if (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9188 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
9189 )
9190 {
9191 auto ctx = std::make_shared<DocContext>(cd,*g_outputList);
9192 auto processFile = [ctx]()
9193 {
9194 msg("Generating docs for compound {}...\n",ctx->cd->displayName());
9195
9196 // skip external references, anonymous compounds and
9197 // template instances
9198 if (!ctx->cd->isHidden() && !ctx->cd->isEmbeddedInOuterScope() &&
9199 ctx->cd->isLinkableInProject() && !ctx->cd->isImplicitTemplateInstance())
9200 {
9201 ctx->cd->writeDocumentation(ctx->ol);
9202 ctx->cd->writeMemberList(ctx->ol);
9203 }
9204
9205 // even for undocumented classes, the inner classes can be documented.
9206 ctx->cd->writeDocumentationForInnerClasses(ctx->ol);
9207 return ctx;
9208 };
9209 results.emplace_back(threadPool.queue(processFile));
9210 }
9211 }
9212 for (auto &f : results)
9213 {
9214 auto ctx = f.get();
9215 }
9216 }
9217 else // single threaded processing
9218 {
9219 for (const auto &cd : classList)
9220 {
9221 //printf("cd=%s getOuterScope=%p global=%p hidden=%d embeddedInOuterScope=%d\n",
9222 // qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope,cd->isHidden(),cd->isEmbeddedInOuterScope());
9223 if (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9224 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
9225 )
9226 {
9227 // skip external references, anonymous compounds and
9228 // template instances
9229 if ( !cd->isHidden() && !cd->isEmbeddedInOuterScope() &&
9230 cd->isLinkableInProject() && !cd->isImplicitTemplateInstance())
9231 {
9232 msg("Generating docs for compound {}...\n",cd->displayName());
9233
9234 cd->writeDocumentation(*g_outputList);
9235 cd->writeMemberList(*g_outputList);
9236 }
9237 // even for undocumented classes, the inner classes can be documented.
9238 cd->writeDocumentationForInnerClasses(*g_outputList);
9239 }
9240 }
9241 }
9242}
9243
9244static void addClassAndNestedClasses(std::vector<ClassDefMutable*> &list,ClassDefMutable *cd)
9245{
9246 list.push_back(cd);
9247 for (const auto &innerCdi : cd->getClasses())
9248 {
9249 ClassDefMutable *innerCd = toClassDefMutable(innerCdi);
9250 if (innerCd)
9251 {
9252 AUTO_TRACE("innerCd={} isLinkable={} isImplicitTemplateInstance={} protectLevelVisible={} embeddedInOuterScope={}",
9253 innerCd->name(),innerCd->isLinkableInProject(),innerCd->isImplicitTemplateInstance(),protectionLevelVisible(innerCd->protection()),
9254 innerCd->isEmbeddedInOuterScope());
9255 }
9256 if (innerCd && innerCd->isLinkableInProject() && !innerCd->isImplicitTemplateInstance() &&
9257 protectionLevelVisible(innerCd->protection()) &&
9258 !innerCd->isEmbeddedInOuterScope()
9259 )
9260 {
9261 list.push_back(innerCd);
9262 addClassAndNestedClasses(list,innerCd);
9263 }
9264 }
9265}
9266
9268{
9269 std::vector<ClassDefMutable*> classList;
9270 for (const auto &cdi : *Doxygen::classLinkedMap)
9271 {
9272 ClassDefMutable *cd = toClassDefMutable(cdi.get());
9273 if (cd && (cd->getOuterScope()==nullptr ||
9275 {
9276 addClassAndNestedClasses(classList,cd);
9277 }
9278 }
9279 for (const auto &cdi : *Doxygen::hiddenClassLinkedMap)
9280 {
9281 ClassDefMutable *cd = toClassDefMutable(cdi.get());
9282 if (cd && (cd->getOuterScope()==nullptr ||
9284 {
9285 addClassAndNestedClasses(classList,cd);
9286 }
9287 }
9288 generateDocsForClassList(classList);
9289}
9290
9291//----------------------------------------------------------------------------
9292
9294{
9295 for (const auto &cdi : *Doxygen::conceptLinkedMap)
9296 {
9298
9299 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
9300 if (cd &&
9301 (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9302 cd->getOuterScope()==Doxygen::globalScope // only look at global concepts
9303 ) && !cd->isHidden() && cd->isLinkableInProject()
9304 )
9305 {
9306 msg("Generating docs for concept {}...\n",cd->displayName());
9308 }
9309 }
9310}
9311
9312//----------------------------------------------------------------------------
9313
9315{
9316 for (const auto &mn : *Doxygen::memberNameLinkedMap)
9317 {
9318 for (const auto &imd : *mn)
9319 {
9320 MemberDefMutable *md = toMemberDefMutable(imd.get());
9321 //static int count=0;
9322 //printf("%04d Member '%s'\n",count++,qPrint(md->qualifiedName()));
9323 if (md && md->documentation().empty() && md->briefDescription().empty())
9324 { // no documentation yet
9325 const MemberDef *bmd = md->reimplements();
9326 while (bmd && bmd->documentation().empty() &&
9327 bmd->briefDescription().empty()
9328 )
9329 { // search up the inheritance tree for a documentation member
9330 //printf("bmd=%s class=%s\n",qPrint(bmd->name()),qPrint(bmd->getClassDef()->name()));
9331 bmd = bmd->reimplements();
9332 }
9333 if (bmd) // copy the documentation from the reimplemented member
9334 {
9335 md->setInheritsDocsFrom(bmd);
9336 md->setDocumentation(bmd->documentation(),bmd->docFile(),bmd->docLine());
9338 md->setBriefDescription(bmd->briefDescription(),bmd->briefFile(),bmd->briefLine());
9339 md->copyArgumentNames(bmd);
9341 }
9342 }
9343 }
9344 }
9345}
9346
9347//----------------------------------------------------------------------------
9348
9350{
9351 // for each file
9352 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9353 {
9354 for (const auto &fd : *fn)
9355 {
9356 fd->combineUsingRelations();
9357 }
9358 }
9359
9360 // for each namespace
9361 NamespaceDefSet visitedNamespaces;
9362 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9363 {
9365 if (ndm)
9366 {
9367 ndm->combineUsingRelations(visitedNamespaces);
9368 }
9369 }
9370}
9371
9372//----------------------------------------------------------------------------
9373
9375{
9376 // for each class
9377 for (const auto &cd : *Doxygen::classLinkedMap)
9378 {
9379 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9380 if (cdm)
9381 {
9383 }
9384 }
9385 // for each file
9386 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9387 {
9388 for (const auto &fd : *fn)
9389 {
9390 fd->addMembersToMemberGroup();
9391 }
9392 }
9393 // for each namespace
9394 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9395 {
9397 if (ndm)
9398 {
9400 }
9401 }
9402 // for each group
9403 for (const auto &gd : *Doxygen::groupLinkedMap)
9404 {
9405 gd->addMembersToMemberGroup();
9406 }
9408}
9409
9410//----------------------------------------------------------------------------
9411
9413{
9414 // for each class
9415 for (const auto &cd : *Doxygen::classLinkedMap)
9416 {
9417 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9418 if (cdm)
9419 {
9421 }
9422 }
9423 // for each file
9424 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9425 {
9426 for (const auto &fd : *fn)
9427 {
9428 fd->distributeMemberGroupDocumentation();
9429 }
9430 }
9431 // for each namespace
9432 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9433 {
9435 if (ndm)
9436 {
9438 }
9439 }
9440 // for each group
9441 for (const auto &gd : *Doxygen::groupLinkedMap)
9442 {
9443 gd->distributeMemberGroupDocumentation();
9444 }
9446}
9447
9448//----------------------------------------------------------------------------
9449
9451{
9452 // for each class
9453 for (const auto &cd : *Doxygen::classLinkedMap)
9454 {
9455 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9456 if (cdm)
9457 {
9459 }
9460 }
9461 // for each concept
9462 for (const auto &cd : *Doxygen::conceptLinkedMap)
9463 {
9464 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
9465 if (cdm)
9466 {
9468 }
9469 }
9470 // for each file
9471 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9472 {
9473 for (const auto &fd : *fn)
9474 {
9475 fd->findSectionsInDocumentation();
9476 }
9477 }
9478 // for each namespace
9479 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9480 {
9482 if (ndm)
9483 {
9485 }
9486 }
9487 // for each group
9488 for (const auto &gd : *Doxygen::groupLinkedMap)
9489 {
9490 gd->findSectionsInDocumentation();
9491 }
9492 // for each page
9493 for (const auto &pd : *Doxygen::pageLinkedMap)
9494 {
9495 pd->findSectionsInDocumentation();
9496 }
9497 // for each directory
9498 for (const auto &dd : *Doxygen::dirLinkedMap)
9499 {
9500 dd->findSectionsInDocumentation();
9501 }
9503 if (Doxygen::mainPage) Doxygen::mainPage->findSectionsInDocumentation();
9504}
9505
9506//----------------------------------------------------------------------
9507
9508
9510{
9511 // remove all references to classes from the cache
9512 // as there can be new template instances in the inheritance path
9513 // to this class. Optimization: only remove those classes that
9514 // have inheritance instances as direct or indirect sub classes.
9516
9517 // remove all cached typedef resolutions whose target is a
9518 // template class as this may now be a template instance
9519 // for each global function name
9520 for (const auto &fn : *Doxygen::functionNameLinkedMap)
9521 {
9522 // for each function with that name
9523 for (const auto &ifmd : *fn)
9524 {
9525 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
9526 if (fmd && fmd->isTypedefValCached())
9527 {
9528 const ClassDef *cd = fmd->getCachedTypedefVal();
9529 if (cd->isTemplate()) fmd->invalidateTypedefValCache();
9530 }
9531 }
9532 }
9533 // for each class method name
9534 for (const auto &nm : *Doxygen::memberNameLinkedMap)
9535 {
9536 // for each function with that name
9537 for (const auto &imd : *nm)
9538 {
9539 MemberDefMutable *md = toMemberDefMutable(imd.get());
9540 if (md && md->isTypedefValCached())
9541 {
9542 const ClassDef *cd = md->getCachedTypedefVal();
9543 if (cd->isTemplate()) md->invalidateTypedefValCache();
9544 }
9545 }
9546 }
9547}
9548
9549//----------------------------------------------------------------------------
9550
9552{
9553 // Remove all unresolved references to classes from the cache.
9554 // This is needed before resolving the inheritance relations, since
9555 // it would otherwise not find the inheritance relation
9556 // for C in the example below, as B::I was already found to be unresolvable
9557 // (which is correct if you ignore the inheritance relation between A and B).
9558 //
9559 // class A { class I {} };
9560 // class B : public A {};
9561 // class C : public B::I {};
9563
9564 // for each class method name
9565 for (const auto &nm : *Doxygen::memberNameLinkedMap)
9566 {
9567 // for each function with that name
9568 for (const auto &imd : *nm)
9569 {
9570 MemberDefMutable *md = toMemberDefMutable(imd.get());
9571 if (md)
9572 {
9574 }
9575 }
9576 }
9577
9578}
9579
9580//----------------------------------------------------------------------------
9581// Returns true if the entry and member definition have equal file names,
9582// otherwise false.
9583
9584static bool haveEqualFileNames(const Entry *root, const MemberDef *md)
9585{
9586 if (const FileDef *fd = md->getFileDef())
9587 {
9588 return fd->absFilePath() == root->fileName;
9589 }
9590 return false;
9591}
9592
9593//----------------------------------------------------------------------------
9594
9595static void addDefineDoc(const Entry *root, MemberDefMutable *md)
9596{
9597 md->setDocumentation(root->doc,root->docFile,root->docLine);
9598 md->setDocsForDefinition(!root->proto);
9599 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9600 if (md->inbodyDocumentation().empty())
9601 {
9603 }
9604 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
9605 {
9606 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
9607 md->setBodyDef(root->fileDef());
9608 }
9610 md->setMaxInitLines(root->initLines);
9612 md->setRefItems(root->sli);
9613 md->setRequirementReferences(root->rqli);
9614 md->addQualifiers(root->qualifiers);
9615 if (root->mGrpId!=-1) md->setMemberGroupId(root->mGrpId);
9616 addMemberToGroups(root,md);
9618}
9619
9620//----------------------------------------------------------------------------
9621
9623{
9624 if ((root->section.isDefineDoc() || root->section.isDefine()) && !root->name.empty())
9625 {
9626 //printf("found define '%s' '%s' brief='%s' doc='%s'\n",
9627 // qPrint(root->name),qPrint(root->args),qPrint(root->brief),qPrint(root->doc));
9628
9629 if (root->tagInfo() && !root->name.empty()) // define read from a tag file
9630 {
9631 auto md = createMemberDef(root->tagInfo()->tagName,1,1,
9632 "#define",root->name,root->args,DString(),
9633 Protection::Public,Specifier::Normal,false,Relationship::Member,MemberType::Define,
9634 ArgumentList(),ArgumentList(),"");
9635 auto mmd = toMemberDefMutable(md.get());
9636 mmd->setTagInfo(root->tagInfo());
9637 mmd->setLanguage(root->lang);
9638 mmd->addQualifiers(root->qualifiers);
9639 //printf("Searching for '%s' fd=%p\n",qPrint(filePathName),fd);
9640 mmd->setFileDef(root->parent()->fileDef());
9641 //printf("Adding member=%s\n",qPrint(md->name()));
9643 mn->push_back(std::move(md));
9644 }
9646 if (mn)
9647 {
9648 int count=0;
9649 for (const auto &md : *mn)
9650 {
9651 if (md->memberType()==MemberType::Define) count++;
9652 }
9653 if (count==1)
9654 {
9655 for (const auto &imd : *mn)
9656 {
9657 MemberDefMutable *md = toMemberDefMutable(imd.get());
9658 if (md && md->memberType()==MemberType::Define)
9659 {
9660 addDefineDoc(root,md);
9661 }
9662 }
9663 }
9664 else if (count>1 &&
9665 (!root->doc.empty() ||
9666 !root->brief.empty() ||
9667 root->bodyLine!=-1
9668 )
9669 )
9670 // multiple defines don't know where to add docs
9671 // but maybe they are in different files together with their documentation
9672 {
9673 for (const auto &imd : *mn)
9674 {
9675 MemberDefMutable *md = toMemberDefMutable(imd.get());
9676 if (md && md->memberType()==MemberType::Define)
9677 {
9678 if (haveEqualFileNames(root, md) || isEntryInGroupOfMember(root, md))
9679 // doc and define in the same file or group assume they belong together.
9680 {
9681 addDefineDoc(root,md);
9682 }
9683 }
9684 }
9685 //warn("define {} found in the following files:\n",root->name);
9686 //warn("Cannot determine where to add the documentation found "
9687 // "at line {} of file {}. \n",
9688 // root->startLine,root->fileName);
9689 }
9690 }
9691 else if (!root->doc.empty() || !root->brief.empty()) // define not found
9692 {
9693 bool preEnabled = Config_getBool(ENABLE_PREPROCESSING);
9694 if (preEnabled)
9695 {
9696 warn(root->fileName,root->startLine,"documentation for unknown define {} found.",root->name);
9697 }
9698 else
9699 {
9700 warn(root->fileName,root->startLine, "found documented #define {} but ignoring it because ENABLE_PREPROCESSING is NO.", root->name);
9701 }
9702 }
9703 }
9704 for (const auto &e : root->children()) findDefineDocumentation(e.get());
9705}
9706
9707//----------------------------------------------------------------------------
9708
9709static void findDirDocumentation(const Entry *root)
9710{
9711 if (root->section.isDirDoc())
9712 {
9713 DString normalizedName = root->name;
9714 normalizedName = substitute(normalizedName,"\\","/");
9715 //printf("root->docFile=%s normalizedName=%s\n",
9716 // qPrint(root->docFile),qPrint(normalizedName));
9717 if (root->docFile==normalizedName) // current dir?
9718 {
9719 if (size_t lastSlashPos=normalizedName.rfind('/'); lastSlashPos!=DString::npos) // strip file name
9720 {
9721 normalizedName=normalizedName.left(lastSlashPos);
9722 }
9723 }
9724 if (normalizedName.at(normalizedName.length()-1)!='/')
9725 {
9726 normalizedName+='/';
9727 }
9728 DirDef *matchingDir=nullptr;
9729 for (const auto &dir : *Doxygen::dirLinkedMap)
9730 {
9731 //printf("Dir: %s<->%s\n",qPrint(dir->name()),qPrint(normalizedName));
9732 if (dir->name().right(normalizedName.length())==normalizedName)
9733 {
9734 if (matchingDir)
9735 {
9736 warn(root->fileName,root->startLine,
9737 "\\dir command matches multiple directories.\n"
9738 " Applying the command for directory {}\n"
9739 " Ignoring the command for directory {}",
9740 matchingDir->name(),dir->name()
9741 );
9742 }
9743 else
9744 {
9745 matchingDir=dir.get();
9746 }
9747 }
9748 }
9749 if (matchingDir)
9750 {
9751 //printf("Match for with dir %s #anchor=%zu\n",qPrint(matchingDir->name()),root->anchors.size());
9752 matchingDir->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9753 matchingDir->setDocumentation(root->doc,root->docFile,root->docLine);
9754 matchingDir->setRefItems(root->sli);
9755 matchingDir->setRequirementReferences(root->rqli);
9756 matchingDir->addSectionsToDefinition(root->anchors);
9757 root->commandOverrides.apply_directoryGraph([&](bool b) { matchingDir->overrideDirectoryGraph(b); });
9758 addDirToGroups(root,matchingDir);
9759 }
9760 else
9761 {
9762 warn(root->fileName,root->startLine,"No matching directory found for command \\dir {}",normalizedName);
9763 }
9764 }
9765 for (const auto &e : root->children()) findDirDocumentation(e.get());
9766}
9767
9768//----------------------------------------------------------------------------
9770{
9771 if (root->section.isRequirementDoc())
9772 {
9774 }
9775 for (const auto &e : root->children()) buildRequirementsList(e.get());
9776}
9777
9778//----------------------------------------------------------------------------
9779// create a (sorted) list of separate documentation pages
9780
9781static void buildPageList(Entry *root)
9782{
9783 if (root->section.isPageDoc())
9784 {
9785 if (!root->name.empty())
9786 {
9787 addRelatedPage(root);
9788 }
9789 }
9790 else if (root->section.isMainpageDoc())
9791 {
9792 DString title=root->args.stripWhiteSpace();
9793 if (title.empty()) title=theTranslator->trMainPage();
9794 //DString name = Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9795 DString name = "index";
9796 addRefItem(root->sli,
9797 name,
9798 theTranslator->trPage(true,true),
9799 name,
9800 title,
9801 DString(),nullptr
9802 );
9803 }
9804 for (const auto &e : root->children()) buildPageList(e.get());
9805}
9806
9807// search for the main page defined in this project
9808static void findMainPage(Entry *root)
9809{
9810 if (root->section.isMainpageDoc())
9811 {
9812 if (Doxygen::mainPage==nullptr && root->tagInfo()==nullptr)
9813 {
9814 //printf("mainpage: docLine=%d startLine=%d\n",root->docLine,root->startLine);
9815 //printf("Found main page! \n======\n%s\n=======\n",qPrint(root->doc));
9816 DString title=root->args.stripWhiteSpace();
9817 if (title.empty()) title = Config_getString(PROJECT_NAME);
9818 //DString indexName=Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9819 DString indexName="index";
9821 indexName, root->brief+root->doc+root->inbodyDocs,title);
9822 //setFileNameForSections(root->anchors,"index",Doxygen::mainPage);
9823 Doxygen::mainPage->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9824 Doxygen::mainPage->setBodySegment(root->startLine,root->startLine,-1);
9825 Doxygen::mainPage->setFileName(indexName);
9826 Doxygen::mainPage->setLocalToc(root->localToc);
9828
9830 if (si)
9831 {
9832 if (!si->ref().empty()) // we are from a tag file
9833 {
9834 // a page name is a label as well! but should no be double either
9836 Doxygen::mainPage->name(),
9837 indexName,
9838 root->startLine,
9839 Doxygen::mainPage->title(),
9841 0); // level 0
9842 }
9843 else if (si->lineNr() != -1)
9844 {
9845 warn(root->fileName,root->startLine,"multiple use of section label '{}' for main page, (first occurrence: {}, line {})",
9846 Doxygen::mainPage->name(),si->fileName(),si->lineNr());
9847 }
9848 else
9849 {
9850 warn(root->fileName,root->startLine,"multiple use of section label '{}' for main page, (first occurrence: {})",
9851 Doxygen::mainPage->name(),si->fileName());
9852 }
9853 }
9854 else
9855 {
9856 // a page name is a label as well! but should no be double either
9858 Doxygen::mainPage->name(),
9859 indexName,
9860 root->startLine,
9861 Doxygen::mainPage->title(),
9863 0); // level 0
9864 }
9865 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9866 }
9867 else if (root->tagInfo()==nullptr)
9868 {
9869 warn(root->fileName,root->startLine,
9870 "found more than one \\mainpage comment block! (first occurrence: {}, line {}), Skipping current block!",
9871 Doxygen::mainPage->docFile(),Doxygen::mainPage->getStartBodyLine());
9872 }
9873 }
9874 for (const auto &e : root->children()) findMainPage(e.get());
9875}
9876
9877// search for the main page imported via tag files and add only the section labels
9878static void findMainPageTagFiles(Entry *root)
9879{
9880 if (root->section.isMainpageDoc())
9881 {
9882 if (Doxygen::mainPage && root->tagInfo())
9883 {
9884 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9885 }
9886 }
9887 for (const auto &e : root->children()) findMainPageTagFiles(e.get());
9888}
9889
9890static void computePageRelations(Entry *root)
9891{
9892 if ((root->section.isPageDoc() || root->section.isMainpageDoc()) && !root->name.empty())
9893 {
9894 PageDef *pd = root->section.isPageDoc() ?
9896 Doxygen::mainPage.get();
9897 if (pd)
9898 {
9899 for (const BaseInfo &bi : root->extends)
9900 {
9902 if (pd==subPd)
9903 {
9904 term("page defined {} with label {} is a direct "
9905 "subpage of itself! Please remove this cyclic dependency.\n",
9906 warn_line(pd->docFile(),pd->docLine()),pd->name());
9907 }
9908 else if (subPd)
9909 {
9910 pd->addInnerCompound(subPd);
9911 //printf("*** Added subpage relation: %s->%s\n",
9912 // qPrint(pd->name()),qPrint(subPd->name()));
9913 }
9914 }
9915 }
9916 }
9917 for (const auto &e : root->children()) computePageRelations(e.get());
9918}
9919
9921{
9922 for (const auto &pd : *Doxygen::pageLinkedMap)
9923 {
9924 Definition *ppd = pd->getOuterScope();
9925 while (ppd)
9926 {
9927 if (ppd==pd.get())
9928 {
9929 term("page defined {} with label {} is a subpage "
9930 "of itself! Please remove this cyclic dependency.\n",
9931 warn_line(pd->docFile(),pd->docLine()),pd->name());
9932 }
9933 ppd=ppd->getOuterScope();
9934 }
9935 }
9936}
9937
9938//----------------------------------------------------------------------------
9939
9941{
9942 for (const auto &si : SectionManager::instance())
9943 {
9944 //printf("si->label='%s' si->definition=%s si->fileName='%s'\n",
9945 // qPrint(si->label),si->definition?qPrint(si->definition->name()):"<none>",
9946 // qPrint(si->fileName));
9947 PageDef *pd=nullptr;
9948
9949 // hack: the items of a todo/test/bug/deprecated list are all fragments from
9950 // different files, so the resulting section's all have the wrong file
9951 // name (not from the todo/test/bug/deprecated list, but from the file in
9952 // which they are defined). We correct this here by looking at the
9953 // generated section labels!
9955 {
9956 DString label="_"+rl->listName(); // "_todo", "_test", ...
9957 if (si->label().left(label.length())==label)
9958 {
9959 si->setFileName(rl->listName());
9960 si->setGenerated(true);
9961 break;
9962 }
9963 }
9964
9965 //printf("start: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
9966 if (!si->generated())
9967 {
9968 // if this section is in a page and the page is in a group, then we
9969 // have to adjust the link file name to point to the group.
9970 if (!si->fileName().empty() &&
9971 (pd=Doxygen::pageLinkedMap->find(si->fileName())) &&
9972 pd->getGroupDef())
9973 {
9974 si->setFileName(pd->getGroupDef()->getOutputFileBase());
9975 }
9976
9977 if (si->definition())
9978 {
9979 // TODO: there should be one function in Definition that returns
9980 // the file to link to, so we can avoid the following tests.
9981 const GroupDef *gd=nullptr;
9982 if (si->definition()->definitionType()==Definition::TypeMember)
9983 {
9984 gd = (toMemberDef(si->definition()))->getGroupDef();
9985 }
9986
9987 if (gd)
9988 {
9989 si->setFileName(gd->getOutputFileBase());
9990 }
9991 else
9992 {
9993 //si->fileName=si->definition->getOutputFileBase();
9994 //printf("Setting si->fileName to %s\n",qPrint(si->fileName));
9995 }
9996 }
9997 }
9998 //printf("end: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
9999 }
10000}
10001
10002
10003
10004//----------------------------------------------------------------------------
10005// generate all separate documentation pages
10006
10007
10008static void generatePageDocs()
10009{
10010 //printf("documentedPages=%d real=%d\n",documentedPages,Doxygen::pageLinkedMap->count());
10011 if (Index::instance().numDocumentedPages()==0) return;
10012 for (const auto &pd : *Doxygen::pageLinkedMap)
10013 {
10014 if (!pd->getGroupDef() && !pd->isReference())
10015 {
10016 msg("Generating docs for page {}...\n",pd->name());
10017 pd->writeDocumentation(*g_outputList);
10018 }
10019 }
10020}
10021
10022//----------------------------------------------------------------------------
10023// create a (sorted) list & dictionary of example pages
10024
10025static void buildExampleList(Entry *root)
10026{
10027 if ((root->section.isExample() || root->section.isExampleLineno()) && !root->name.empty())
10028 {
10029 if (Doxygen::exampleLinkedMap->find(root->name))
10030 {
10031 warn(root->fileName,root->startLine,"Example {} was already documented. Ignoring documentation found here.",root->name);
10032 }
10033 else
10034 {
10036 createPageDef(root->fileName,root->startLine,
10037 root->name,root->brief+root->doc+root->inbodyDocs,root->args));
10038 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
10039 pd->setFileName(convertNameToFile(pd->name()+"-example",false,true));
10041 pd->setLanguage(root->lang);
10042 pd->setShowLineNo(root->section.isExampleLineno());
10043
10044 //we don't add example to groups
10045 //addExampleToGroups(root,pd);
10046 }
10047 }
10048 for (const auto &e : root->children()) buildExampleList(e.get());
10049}
10050
10051//----------------------------------------------------------------------------
10052// prints the Entry tree (for debugging)
10053
10054void printNavTree(Entry *root,int indent)
10055{
10057 {
10058 DString indentStr;
10059 indentStr.fill(' ',indent);
10060 Debug::print(Debug::Entries,0,"{}{} at {}:{} (sec={}, spec={})\n",
10061 indentStr.empty()?"":indentStr,
10062 root->name.empty()?"<empty>":root->name,
10063 root->fileName,root->startLine,
10064 root->section.to_string(),
10065 root->spec.to_string());
10066 for (const auto &e : root->children())
10067 {
10068 printNavTree(e.get(),indent+2);
10069 }
10070 }
10071}
10072
10073
10074//----------------------------------------------------------------------------
10075// prints the Sections tree (for debugging)
10076
10078{
10080 {
10081 for (const auto &si : SectionManager::instance())
10082 {
10083 Debug::print(Debug::Sections,0,"Section = {}, file = {}, title = {}, type = {}, ref = {}\n",
10084 si->label(),si->fileName(),si->title(),si->type().level(),si->ref());
10085 }
10086 }
10087}
10088
10089
10090//----------------------------------------------------------------------------
10091// generate the example documentation
10092
10094{
10096 for (const auto &pd : *Doxygen::exampleLinkedMap)
10097 {
10098 msg("Generating docs for example {}...\n",pd->name());
10099 SrcLangExt lang = getLanguageFromFileName(pd->name(), SrcLangExt::Unknown);
10100 if (lang != SrcLangExt::Unknown)
10101 {
10102 DString ext = getFileNameExtension(pd->name());
10103 auto intf = Doxygen::parserManager->getCodeParser(ext);
10104 intf->resetCodeParserState();
10105 }
10106 DString n=pd->getOutputFileBase();
10107 startFile(*g_outputList,n,false,n,pd->name());
10109 g_outputList->docify(pd->name());
10112 DString lineNoOptStr;
10113 if (pd->showLineNo())
10114 {
10115 lineNoOptStr="{lineno}";
10116 }
10117 g_outputList->generateDoc(pd->docFile(), // file
10118 pd->docLine(), // startLine
10119 pd.get(), // context
10120 nullptr, // memberDef
10121 (pd->briefDescription().empty()?"":pd->briefDescription()+"\n\n")+
10122 pd->documentation()+"\n\n\\include"+lineNoOptStr+" "+pd->name(), // docs
10123 DocOptions()
10124 .setIndexWords(true)
10125 .setExample(pd->name()));
10126 endFile(*g_outputList); // contains g_outputList->endContents()
10127 }
10129}
10130
10131//----------------------------------------------------------------------------
10132// generate module pages
10133
10135{
10136 for (const auto &gd : *Doxygen::groupLinkedMap)
10137 {
10138 if (!gd->isReference())
10139 {
10140 gd->writeDocumentation(*g_outputList);
10141 }
10142 }
10143}
10144
10145//----------------------------------------------------------------------------
10146// generate module pages
10147
10149{
10150 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10151 if (numThreads>1) // multi threaded processing
10152 {
10153 struct DocContext
10154 {
10155 DocContext(ClassDefMutable *cdm_,const OutputList &ol_)
10156 : cdm(cdm_), ol(ol_) {}
10157 ClassDefMutable *cdm;
10158 OutputList ol;
10159 };
10160 ThreadPool threadPool(numThreads);
10161 std::vector< std::future< std::shared_ptr<DocContext> > > results;
10162 // for each class in the namespace...
10163 for (const auto &cd : classList)
10164 {
10166 if (cdm)
10167 {
10168 auto ctx = std::make_shared<DocContext>(cdm,*g_outputList);
10169 auto processFile = [ctx]()
10170 {
10171 if ( ( ctx->cdm->isLinkableInProject() &&
10172 !ctx->cdm->isImplicitTemplateInstance()
10173 ) // skip external references, anonymous compounds and
10174 // template instances and nested classes
10175 && !ctx->cdm->isHidden() && !ctx->cdm->isEmbeddedInOuterScope()
10176 )
10177 {
10178 msg("Generating docs for compound {}...\n",ctx->cdm->displayName());
10179 ctx->cdm->writeDocumentation(ctx->ol);
10180 ctx->cdm->writeMemberList(ctx->ol);
10181 }
10182 ctx->cdm->writeDocumentationForInnerClasses(ctx->ol);
10183 return ctx;
10184 };
10185 results.emplace_back(threadPool.queue(processFile));
10186 }
10187 }
10188 // wait for the results
10189 for (auto &f : results)
10190 {
10191 auto ctx = f.get();
10192 }
10193 }
10194 else // single threaded processing
10195 {
10196 // for each class in the namespace...
10197 for (const auto &cd : classList)
10198 {
10200 if (cdm)
10201 {
10202 if ( ( cd->isLinkableInProject() &&
10203 !cd->isImplicitTemplateInstance()
10204 ) // skip external references, anonymous compounds and
10205 // template instances and nested classes
10206 && !cd->isHidden() && !cd->isEmbeddedInOuterScope()
10207 )
10208 {
10209 msg("Generating docs for compound {}...\n",cd->displayName());
10210
10213 }
10215 }
10216 }
10217 }
10218}
10219
10221{
10222 // for each concept in the namespace...
10223 for (const auto &cd : conceptList)
10224 {
10226 if ( cdm && cd->isLinkableInProject() && !cd->isHidden())
10227 {
10228 msg("Generating docs for concept {}...\n",cd->name());
10230 }
10231 }
10232}
10233
10235{
10236 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
10237
10238 //writeNamespaceIndex(*g_outputList);
10239
10240 // for each namespace...
10241 for (const auto &nd : *Doxygen::namespaceLinkedMap)
10242 {
10243 if (nd->isLinkableInProject())
10244 {
10246 if (ndm)
10247 {
10248 msg("Generating docs for namespace {}\n",nd->displayName());
10250 }
10251 }
10252
10253 generateNamespaceClassDocs(nd->getClasses());
10254 if (sliceOpt)
10255 {
10256 generateNamespaceClassDocs(nd->getInterfaces());
10257 generateNamespaceClassDocs(nd->getStructs());
10258 generateNamespaceClassDocs(nd->getExceptions());
10259 }
10260 generateNamespaceConceptDocs(nd->getConcepts());
10261 }
10262}
10263
10265{
10266 std::string oldDir = Dir::currentDirPath();
10267 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
10270 {
10271 err("failed to run html help compiler on {}\n", HtmlHelp::hhpFileName);
10272 }
10273 Dir::setCurrent(oldDir);
10274}
10275
10277{
10278 DString args = Qhp::qhpFileName + " -o \"" + Qhp::getQchFileName() + "\"";
10279 std::string oldDir = Dir::currentDirPath();
10280 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
10281
10282 DString qhgLocation=Config_getString(QHG_LOCATION);
10283 if (Debug::isFlagSet(Debug::Qhp)) // produce info for debugging
10284 {
10285 // run qhelpgenerator -v and extract the Qt version used
10286 DString cmd=qhgLocation+ " -v 2>&1";
10287 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
10288 FILE *f=Portable::popen(cmd,"r");
10289 if (!f)
10290 {
10291 err("could not execute {}\n",qhgLocation);
10292 }
10293 else
10294 {
10295 const size_t bufSize = 1024;
10296 char inBuf[bufSize+1];
10297 size_t numRead=fread(inBuf,1,bufSize,f);
10298 inBuf[numRead] = '\0';
10299 Debug::print(Debug::Qhp,0,"{}",inBuf);
10301
10302 int qtVersion=0;
10303 static const reg::Ex versionReg(R"(Qt (\d+)\.(\d+)\.(\d+))");
10304 reg::Match match;
10305 std::string s = inBuf;
10306 if (reg::search(s,match,versionReg))
10307 {
10308 qtVersion = 10000*DString(match[1].str()).toInt() +
10309 100*DString(match[2].str()).toInt() +
10310 DString(match[3].str()).toInt();
10311 }
10312 if (qtVersion>0 && (qtVersion<60000 || qtVersion >= 60205))
10313 {
10314 // dump the output of qhelpgenerator -c file.qhp
10315 // Qt<6 or Qt>=6.2.5 or higher, see https://bugreports.qt.io/browse/QTBUG-101070
10316 cmd=qhgLocation+ " -c " + Qhp::qhpFileName + " 2>&1";
10317 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
10318 f=Portable::popen(cmd,"r");
10319 if (!f)
10320 {
10321 err("could not execute {}\n",qhgLocation);
10322 }
10323 else
10324 {
10325 std::string output;
10326 while ((numRead=fread(inBuf,1,bufSize,f))>0)
10327 {
10328 inBuf[numRead] = '\0';
10329 output += inBuf;
10330 }
10332 Debug::print(Debug::Qhp,0,"{}",output);
10333 }
10334 }
10335 }
10336 }
10337
10338 if (Portable::system(qhgLocation, args, false))
10339 {
10340 err("failed to run qhelpgenerator on {}\n",Qhp::qhpFileName);
10341 }
10342 Dir::setCurrent(oldDir);
10343}
10344
10345//----------------------------------------------------------------------------
10346
10348{
10349 // check dot path
10350 DString dotPath = Config_getString(DOT_PATH);
10351 if (!dotPath.empty())
10352 {
10353 FileInfo fi(dotPath.str());
10354 if (!(fi.exists() && fi.isFile()) )// not an existing user specified path + exec
10355 {
10356 dotPath = dotPath+"/dot"+Portable::commandExtension();
10357 FileInfo dp(dotPath.str());
10358 if (!dp.exists() || !dp.isFile())
10359 {
10360 warn_uncond("the dot tool could not be found as '{}'\n",dotPath);
10361 dotPath = "dot";
10362 dotPath += Portable::commandExtension();
10363 }
10364 }
10365#if defined(_WIN32) // convert slashes
10366 size_t l=dotPath.length();
10367 for (size_t i=0;i<l;i++) if (dotPath.at(i)=='/') dotPath.at(i)='\\';
10368#endif
10369 }
10370 else
10371 {
10372 dotPath = "dot";
10373 dotPath += Portable::commandExtension();
10374 }
10375 Doxygen::verifiedDotPath = dotPath;
10377}
10378
10379//----------------------------------------------------------------------------
10380
10381/*! Generate a template version of the configuration file.
10382 * If the \a shortList parameter is true a configuration file without
10383 * comments will be generated.
10384 */
10385static void generateConfigFile(const DString &configFile,bool shortList,
10386 bool updateOnly=false)
10387{
10388 std::ofstream f;
10389 bool fileOpened=openOutputFile(configFile,f);
10390 bool writeToStdout=configFile=="-";
10391 if (fileOpened)
10392 {
10393 TextStream t(&f);
10394 Config::writeTemplate(t,shortList,updateOnly);
10395 if (!writeToStdout)
10396 {
10397 if (!updateOnly)
10398 {
10399 msg("\n\nConfiguration file '{}' created.\n\n",configFile);
10400 msg("Now edit the configuration file and enter\n\n");
10401 if (configFile!="Doxyfile" && configFile!="doxyfile")
10402 msg(" doxygen {}\n\n",configFile);
10403 else
10404 msg(" doxygen\n\n");
10405 msg("to generate the documentation for your project\n\n");
10406 }
10407 else
10408 {
10409 msg("\n\nConfiguration file '{}' updated.\n\n",configFile);
10410 }
10411 }
10412 }
10413 else
10414 {
10415 term("Cannot open file {} for writing\n",configFile);
10416 }
10417}
10418
10420{
10421 std::ofstream f;
10422 bool fileOpened=openOutputFile("-",f);
10423 if (fileOpened)
10424 {
10425 TextStream t(&f);
10426 Config::compareDoxyfile(t,diffList);
10427 }
10428 else
10429 {
10430 term("Cannot open stdout for writing\n");
10431 }
10432}
10433
10434//----------------------------------------------------------------------------
10435// read and parse a tag file
10436
10437static void readTagFile(const std::shared_ptr<Entry> &root,const DString &tagLine)
10438{
10439 DString fileName;
10440 DString destName;
10441 if (size_t eqPos = tagLine.find('='); eqPos!=DString::npos) // tag command contains a destination
10442 {
10443 fileName = tagLine.left(eqPos).stripWhiteSpace();
10444 destName = tagLine.mid(eqPos+1).stripWhiteSpace();
10445 if (fileName.empty() || destName.empty()) return;
10446 //printf("insert tagDestination %s->%s\n",qPrint(fi.fileName()),qPrint(destName));
10447 }
10448 else
10449 {
10450 fileName = tagLine;
10451 }
10452
10453 FileInfo fi(fileName.str());
10454 if (!fi.exists() || !fi.isFile())
10455 {
10456 err("Tag file '{}' does not exist or is not a file. Skipping it...\n",fileName);
10457 return;
10458 }
10459
10460 if (Doxygen::tagFileSet.find(fi.absFilePath()) != Doxygen::tagFileSet.end()) return;
10461
10462 Doxygen::tagFileSet.emplace(fi.absFilePath());
10463
10464 if (!destName.empty())
10465 {
10466 Doxygen::tagDestinationMap.emplace(fi.absFilePath(), destName.str());
10467 msg("Reading tag file '{}', location '{}'...\n",fileName,destName);
10468 }
10469 else
10470 {
10471 msg("Reading tag file '{}'...\n",fileName);
10472 }
10473
10474 parseTagFile(root,fi.absFilePath().c_str());
10475}
10476
10477//----------------------------------------------------------------------------
10479{
10480 StringVector latexExtraStyleSheet = Config_getList(LATEX_EXTRA_STYLESHEET);
10481 for (const auto &sheet : latexExtraStyleSheet)
10482 {
10483 std::string fileName = sheet;
10484 if (!fileName.empty())
10485 {
10486 FileInfo fi(fileName);
10487 if (!fi.exists())
10488 {
10489 err("Style sheet '{}' specified by LATEX_EXTRA_STYLESHEET does not exist!\n",fileName);
10490 }
10491 else if (fi.isDir())
10492 {
10493 err("Style sheet '{}' specified by LATEX_EXTRA_STYLESHEET is a directory, it has to be a file!\n", fileName);
10494 }
10495 else
10496 {
10497 DString destFileName = Config_getString(LATEX_OUTPUT)+"/"+fi.fileName();
10499 {
10500 destFileName += LATEX_STYLE_EXTENSION;
10501 }
10502 copyFile(fileName, destFileName);
10503 }
10504 }
10505 }
10506}
10507
10508//----------------------------------------------------------------------------
10509static void copyStyleSheet()
10510{
10511 DString htmlStyleSheet = Config_getString(HTML_STYLESHEET);
10512 if (!htmlStyleSheet.empty())
10513 {
10514 if (!htmlStyleSheet.startsWith("http:") && !htmlStyleSheet.startsWith("https:"))
10515 {
10516 FileInfo fi(htmlStyleSheet.str());
10517 if (!fi.exists())
10518 {
10519 err("Style sheet '{}' specified by HTML_STYLESHEET does not exist!\n",htmlStyleSheet);
10520 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
10521 }
10522 else if (fi.isDir())
10523 {
10524 err("Style sheet '{}' specified by HTML_STYLESHEET is a directory, it has to be a file!\n",htmlStyleSheet);
10525 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
10526 }
10527 else
10528 {
10529 DString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
10530 copyFile(htmlStyleSheet,destFileName);
10531 }
10532 }
10533 }
10534 StringVector htmlExtraStyleSheet = Config_getList(HTML_EXTRA_STYLESHEET);
10535 for (const auto &sheet : htmlExtraStyleSheet)
10536 {
10537 DString fileName(sheet);
10538 if (!fileName.empty() && !fileName.startsWith("http:") && !fileName.startsWith("https:"))
10539 {
10540 FileInfo fi(fileName.str());
10541 if (!fi.exists())
10542 {
10543 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET does not exist!\n",fileName);
10544 }
10545 else if (fi.fileName()=="doxygen.css" || fi.fileName()=="tabs.css" || fi.fileName()=="navtree.css")
10546 {
10547 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET is already a built-in stylesheet. Please use a different name\n",fi.fileName());
10548 }
10549 else if (fi.isDir())
10550 {
10551 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET is a directory, it has to be a file!\n",fileName);
10552 }
10553 else
10554 {
10555 DString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
10556 copyFile(fileName, destFileName);
10557 }
10558 }
10559 }
10560}
10561
10562static void copyLogo(const DString &outputOption, bool toIndex)
10563{
10564 DString projectLogo = projectLogoFile();
10565 if (!projectLogo.empty())
10566 {
10567 FileInfo fi(projectLogo.str());
10568 if (!fi.exists())
10569 {
10570 err("Project logo '{}' specified by PROJECT_LOGO does not exist!\n",projectLogo);
10571 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
10572 }
10573 else if (fi.isDir())
10574 {
10575 err("Project logo '{}' specified by PROJECT_LOGO is a directory, it has to be a file!\n",projectLogo);
10576 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
10577 }
10578 else
10579 {
10580 DString destFileName = outputOption+"/"+fi.fileName();
10581 copyFile(projectLogo,destFileName);
10582 if (toIndex) Doxygen::indexList->addImageFile(fi.fileName());
10583 }
10584 }
10585}
10586
10587static void copyIcon(const DString &outputOption, bool toIndex)
10588{
10589 DString projectIcon = Config_getString(PROJECT_ICON);
10590 if (!projectIcon.empty())
10591 {
10592 FileInfo fi(projectIcon.str());
10593 if (!fi.exists())
10594 {
10595 err("Project icon '{}' specified by PROJECT_ICON does not exist!\n",projectIcon);
10596 projectIcon = Config_updateString(PROJECT_ICON,""); // revert to the default
10597 }
10598 else if (fi.isDir())
10599 {
10600 err("Project icon '{}' specified by PROJECT_ICON is a directory, it has to be a file!\n",projectIcon);
10601 projectIcon = Config_updateString(PROJECT_ICON,""); // revert to the default
10602 }
10603 else
10604 {
10605 DString destFileName = outputOption+"/"+fi.fileName();
10606 copyFile(projectIcon,destFileName);
10607 if (toIndex) Doxygen::indexList->addImageFile(fi.fileName());
10608 }
10609 }
10610}
10611
10612static inline void copyExtraFiles(StringVector files,const DString &filesOption,const DString &outputOption, bool toIndex)
10613{
10614 for (const auto &fileName : files)
10615 {
10616 if (!fileName.empty())
10617 {
10618 FileInfo fi(fileName);
10619 if (!fi.exists())
10620 {
10621 err("Extra file '{}' specified in {} does not exist!\n", fileName,filesOption);
10622 }
10623 else if (fi.isDir())
10624 {
10625 err("Extra file '{}' specified in {} is a directory, it has to be a file!\n", fileName,filesOption);
10626 }
10627 else
10628 {
10629 DString destFileName = outputOption+"/"+fi.fileName();
10630 copyFile(fileName, destFileName);
10631 if (toIndex) Doxygen::indexList->addImageFile(fi.fileName());
10632 }
10633 }
10634 }
10635}
10636
10637//----------------------------------------------------------------------------
10638
10640{
10641 for (const auto &fn : *Doxygen::inputNameLinkedMap)
10642 {
10643 struct FileEntry
10644 {
10645 FileEntry(const DString &p,FileDef *fd) : path(p), fileDef(fd) {}
10646 DString path;
10647 FileDef *fileDef;
10648 };
10649
10650 // collect the entry for which to compute the longest common prefix (LCP) of the path
10651 std::vector<FileEntry> fileEntries;
10652 for (const auto &fd : *fn)
10653 {
10654 if (!fd->isReference()) // skip external references
10655 {
10656 fileEntries.emplace_back(fd->getPath(),fd.get());
10657 }
10658 }
10659
10660 size_t size = fileEntries.size();
10661
10662 if (size==1) // name if unique, so diskname is simply the name
10663 {
10664 FileDef *fd = fileEntries[0].fileDef;
10665 fd->setDiskName(fn->fileName());
10666 }
10667 else if (size>1) // multiple occurrences of the same file name
10668 {
10669 // sort the array
10670 std::stable_sort(fileEntries.begin(),
10671 fileEntries.end(),
10672 [](const FileEntry &fe1,const FileEntry &fe2)
10673 { return dstricmp_sort(fe1.path,fe2.path)<0; }
10674 );
10675
10676 // since the entries are sorted, the common prefix of the whole array is same
10677 // as the common prefix between the first and last entry
10678 const FileEntry &first = fileEntries[0];
10679 const FileEntry &last = fileEntries[size-1];
10680 int first_path_size = static_cast<int>(first.path.size())-1; // -1 to skip trailing slash
10681 int last_path_size = static_cast<int>(last.path.size())-1; // -1 to skip trailing slash
10682 int j=0;
10683 int i=0;
10684 for (i=0;i<first_path_size && i<last_path_size;i++)
10685 {
10686 if (first.path[i]=='/') j=i;
10687 if (first.path[i]!=last.path[i]) break;
10688 }
10689 if (i==first_path_size && i<last_path_size && last.path[i]=='/')
10690 {
10691 // case first='some/path' and last='some/path/more' => match is 'some/path'
10692 j=first_path_size;
10693 }
10694 else if (i==last_path_size && i<first_path_size && first.path[i]=='/')
10695 {
10696 // case first='some/path/more' and last='some/path' => match is 'some/path'
10697 j=last_path_size;
10698 }
10699
10700 // add non-common part of the path to the name
10701 for (auto &fileEntry : fileEntries)
10702 {
10703 DString prefix = fileEntry.path.right(fileEntry.path.length()-j-1);
10704 fileEntry.fileDef->setName(prefix+fn->fileName());
10705 //printf("!!!!!!!! non unique disk name=%s:%s\n",qPrint(prefix),fn->fileName());
10706 fileEntry.fileDef->setDiskName(prefix+fn->fileName());
10707 }
10708 }
10709 }
10710}
10711
10712
10713
10714//----------------------------------------------------------------------------
10715
10716static std::unique_ptr<OutlineParserInterface> getParserForFile(const DString &fn)
10717{
10718 DString fileName=fn;
10719 DString extension;
10720 size_t sep = fileName.rfind('/');
10721 size_t ei = fileName.rfind('.');
10722 if (ei!=DString::npos && (sep==DString::npos || ei>sep)) // matches dir/file.ext but not dir.1/file
10723 {
10724 extension=fileName.mid(ei);
10725 }
10726 else
10727 {
10728 extension = ".no_extension";
10729 }
10730
10731 return Doxygen::parserManager->getOutlineParser(extension);
10732}
10733
10734static std::shared_ptr<Entry> parseFile(OutlineParserInterface &parser,
10735 FileDef *fd,const DString &fn,
10736 ClangTUParser *clangParser,bool newTU)
10737{
10738 DString fileName=fn;
10739 AUTO_TRACE("fileName={}",fileName);
10740 DString extension;
10741 if (size_t ei = fileName.rfind('.'); ei!=DString::npos)
10742 {
10743 extension=fileName.mid(ei);
10744 }
10745 else
10746 {
10747 extension = ".no_extension";
10748 }
10749
10750 FileInfo fi(fileName.str());
10751 std::string preBuf;
10752
10753 if (Config_getBool(ENABLE_PREPROCESSING) &&
10754 parser.needsPreprocessing(extension))
10755 {
10756 Preprocessor preprocessor;
10757 StringVector includePath = Config_getList(INCLUDE_PATH);
10758 for (const auto &s : includePath)
10759 {
10760 std::string absPath = FileInfo(s).absFilePath();
10761 preprocessor.addSearchDir(absPath);
10762 }
10763 std::string inBuf;
10764 msg("Preprocessing {}...\n",fn);
10765 readInputFile(fileName,inBuf);
10766 addTerminalCharIfMissing(inBuf,'\n');
10767 preprocessor.processFile(fileName,inBuf,preBuf);
10768 }
10769 else // no preprocessing
10770 {
10771 msg("Reading {}...\n",fn);
10772 readInputFile(fileName,preBuf);
10773 addTerminalCharIfMissing(preBuf,'\n');
10774 }
10775
10776 std::string convBuf;
10777 convBuf.reserve(preBuf.size()+1024);
10778
10779 // convert multi-line C++ comments to C style comments
10780 convertCppComments(preBuf,convBuf,fileName.str());
10781
10782 std::shared_ptr<Entry> fileRoot = std::make_shared<Entry>();
10783 // use language parse to parse the file
10784 if (clangParser)
10785 {
10786 if (newTU) clangParser->parse();
10787 clangParser->switchToFile(fd);
10788 }
10789 parser.parseInput(fileName,convBuf.data(),fileRoot,clangParser);
10790 fileRoot->setFileDef(fd);
10791 return fileRoot;
10792}
10793
10794//! parse the list of input files
10795static void parseFilesMultiThreading(const std::shared_ptr<Entry> &root)
10796{
10797 AUTO_TRACE();
10798#if USE_LIBCLANG
10800 {
10801 StringUnorderedSet processedFiles;
10802
10803 // create a dictionary with files to process
10804 StringUnorderedSet filesToProcess;
10805 for (const auto &s : g_inputFiles)
10806 {
10807 filesToProcess.insert(s);
10808 }
10809
10810 std::mutex processedFilesLock;
10811 // process source files (and their include dependencies)
10812 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10813 msg("Processing input using {} threads.\n",numThreads);
10814 ThreadPool threadPool(numThreads);
10815 using FutureType = std::vector< std::shared_ptr<Entry> >;
10816 std::vector< std::future< FutureType > > results;
10817 for (const auto &s : g_inputFiles)
10818 {
10819 bool ambig = false;
10820 DString qs = s;
10822 ASSERT(fd!=nullptr);
10823 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt::Cpp) // this is a source file
10824 {
10825 // lambda representing the work to executed by a thread
10826 auto processFile = [qs,&filesToProcess,&processedFilesLock,&processedFiles]() {
10827 bool ambig_l = false;
10828 std::vector< std::shared_ptr<Entry> > roots;
10829 FileDef *fd_l = Doxygen::inputNameLinkedMap->findFileDef(qs,ambig_l);
10830 auto clangParser = ClangParser::instance()->createTUParser(fd_l);
10831 auto parser = getParserForFile(qs);
10832 auto fileRoot { parseFile(*parser.get(),fd_l,qs,clangParser.get(),true) };
10833 roots.push_back(fileRoot);
10834
10835 // Now process any include files in the same translation unit
10836 // first. When libclang is used this is much more efficient.
10837 for (auto incFile : clangParser->filesInSameTU())
10838 {
10839 DString qincFile = incFile;
10840 if (filesToProcess.find(incFile)!=filesToProcess.end())
10841 {
10842 bool needsToBeProcessed = false;
10843 {
10844 std::lock_guard<std::mutex> lock(processedFilesLock);
10845 needsToBeProcessed = processedFiles.find(incFile)==processedFiles.end();
10846 if (needsToBeProcessed) processedFiles.insert(incFile);
10847 }
10848 if (qincFile!=qs && needsToBeProcessed)
10849 {
10850 FileDef *ifd=Doxygen::inputNameLinkedMap->findFileDef(qincFile,ambig_l);
10851 if (ifd && !ifd->isReference())
10852 {
10853 //printf(" Processing %s in same translation unit as %s\n",incFile,qPrint(s));
10854 fileRoot = parseFile(*parser.get(),ifd,qincFile,clangParser.get(),false);
10855 roots.push_back(fileRoot);
10856 }
10857 }
10858 }
10859 }
10860 return roots;
10861 };
10862 // dispatch the work and collect the future results
10863 results.emplace_back(threadPool.queue(processFile));
10864 }
10865 }
10866 // synchronize with the Entry result lists produced and add them to the root
10867 for (auto &f : results)
10868 {
10869 auto l = f.get();
10870 for (auto &e : l)
10871 {
10872 root->moveToSubEntryAndKeep(e);
10873 }
10874 }
10875 // process remaining files
10876 results.clear();
10877 for (const auto &s : g_inputFiles)
10878 {
10879 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
10880 {
10881 // lambda representing the work to executed by a thread
10882 auto processFile = [s]() {
10883 bool ambig = false;
10884 DString qs = s;
10885 std::vector< std::shared_ptr<Entry> > roots;
10887 auto parser { getParserForFile(qs) };
10888 bool useClang = getLanguageFromFileName(qs)==SrcLangExt::Cpp;
10889 if (useClang)
10890 {
10891 auto clangParser = ClangParser::instance()->createTUParser(fd);
10892 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
10893 roots.push_back(fileRoot);
10894 }
10895 else
10896 {
10897 auto fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10898 roots.push_back(fileRoot);
10899 }
10900 return roots;
10901 };
10902 results.emplace_back(threadPool.queue(processFile));
10903 }
10904 }
10905 // synchronize with the Entry result lists produced and add them to the root
10906 for (auto &f : results)
10907 {
10908 auto l = f.get();
10909 for (auto &e : l)
10910 {
10911 root->moveToSubEntryAndKeep(e);
10912 }
10913 }
10914 }
10915 else // normal processing
10916#endif
10917 {
10918 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10919 msg("Processing input using {} threads.\n",numThreads);
10920 ThreadPool threadPool(numThreads);
10921 using FutureType = std::shared_ptr<Entry>;
10922 std::vector< std::future< FutureType > > results;
10923 for (const auto &s : g_inputFiles)
10924 {
10925 // lambda representing the work to executed by a thread
10926 auto processFile = [s]() {
10927 bool ambig = false;
10928 DString qs = s;
10930 auto parser = getParserForFile(qs);
10931 auto fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10932 return fileRoot;
10933 };
10934 // dispatch the work and collect the future results
10935 results.emplace_back(threadPool.queue(processFile));
10936 }
10937 // synchronize with the Entry results produced and add them to the root
10938 for (auto &f : results)
10939 {
10940 root->moveToSubEntryAndKeep(f.get());
10941 }
10942 }
10943}
10944
10945//! parse the list of input files
10946static void parseFilesSingleThreading(const std::shared_ptr<Entry> &root)
10947{
10948 AUTO_TRACE();
10949#if USE_LIBCLANG
10951 {
10952 StringUnorderedSet processedFiles;
10953
10954 // create a dictionary with files to process
10955 StringUnorderedSet filesToProcess;
10956 for (const auto &s : g_inputFiles)
10957 {
10958 filesToProcess.insert(s);
10959 }
10960
10961 // process source files (and their include dependencies)
10962 for (const auto &s : g_inputFiles)
10963 {
10964 bool ambig = false;
10965 DString qs =s;
10967 ASSERT(fd!=nullptr);
10968 if (fd->isSource() && !fd->isReference() && getLanguageFromFileName(qs)==SrcLangExt::Cpp) // this is a source file
10969 {
10970 auto clangParser = ClangParser::instance()->createTUParser(fd);
10971 auto parser { getParserForFile(qs) };
10972 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
10973 root->moveToSubEntryAndKeep(fileRoot);
10974 processedFiles.insert(s);
10975
10976 // Now process any include files in the same translation unit
10977 // first. When libclang is used this is much more efficient.
10978 for (auto incFile : clangParser->filesInSameTU())
10979 {
10980 //printf(" file %s\n",qPrint(incFile));
10981 if (filesToProcess.find(incFile)!=filesToProcess.end() && // file need to be processed
10982 processedFiles.find(incFile)==processedFiles.end()) // and is not processed already
10983 {
10984 FileDef *ifd=Doxygen::inputNameLinkedMap->findFileDef(incFile,ambig);
10985 if (ifd && !ifd->isReference())
10986 {
10987 //printf(" Processing %s in same translation unit as %s\n",qPrint(incFile),qPrint(qs));
10988 fileRoot = parseFile(*parser.get(),ifd,incFile,clangParser.get(),false);
10989 root->moveToSubEntryAndKeep(fileRoot);
10990 processedFiles.insert(incFile);
10991 }
10992 }
10993 }
10994 }
10995 }
10996 // process remaining files
10997 for (const auto &s : g_inputFiles)
10998 {
10999 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
11000 {
11001 bool ambig = false;
11002 DString qs = s;
11004 if (getLanguageFromFileName(qs)==SrcLangExt::Cpp) // not yet processed
11005 {
11006 auto clangParser = ClangParser::instance()->createTUParser(fd);
11007 auto parser { getParserForFile(qs) };
11008 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
11009 root->moveToSubEntryAndKeep(fileRoot);
11010 }
11011 else
11012 {
11013 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(qs) };
11014 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
11015 root->moveToSubEntryAndKeep(fileRoot);
11016 }
11017 processedFiles.insert(s);
11018 }
11019 }
11020 }
11021 else // normal processing
11022#endif
11023 {
11024 for (const auto &s : g_inputFiles)
11025 {
11026 bool ambig = false;
11027 DString qs = s;
11029 ASSERT(fd!=nullptr);
11030 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(qs) };
11031 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
11032 root->moveToSubEntryAndKeep(std::move(fileRoot));
11033 }
11034 }
11035}
11036
11037// resolves a path that may include symlinks, if a recursive symlink is
11038// found an empty string is returned.
11039static std::string resolveSymlink(const std::string &path)
11040{
11041 size_t sepPos=0;
11042 size_t oldPos=0;
11043 StringUnorderedSet nonSymlinks;
11044 StringUnorderedSet known;
11045 DString result(path);
11046 DString oldPrefix = "/";
11047 do
11048 {
11049#if defined(_WIN32)
11050 // UNC path, skip server and share name
11051 if (sepPos==0 && (result.startsWith("//") || result.startsWith("\\\\")))
11052 sepPos = result.find('/',2);
11053 if (sepPos!=DString::npos)
11054 sepPos = result.find('/',sepPos+1);
11055#else
11056 sepPos = result.find('/',sepPos+1);
11057#endif
11058 DString prefix = sepPos==DString::npos ? result : result.left(sepPos);
11059 if (nonSymlinks.find(prefix.str())==nonSymlinks.end())
11060 {
11061 FileInfo fi(prefix.str());
11062 if (fi.isSymLink())
11063 {
11064 DString target = fi.readLink();
11065 bool isRelative = FileInfo(target.str()).isRelative();
11066 if (isRelative)
11067 {
11068 target = Dir::cleanDirPath(oldPrefix.str()+"/"+target.str());
11069 }
11070 if (sepPos!=DString::npos)
11071 {
11072 if (fi.isDir() && !target.empty() && target.at(target.length()-1)!='/')
11073 {
11074 target+='/';
11075 }
11076 target+=result.mid(sepPos);
11077 }
11078 result = Dir::cleanDirPath(target.str());
11079 if (known.find(result.str())!=known.end()) return std::string(); // recursive symlink!
11080 known.insert(result.str());
11081 if (isRelative)
11082 {
11083 sepPos = oldPos;
11084 }
11085 else // link to absolute path
11086 {
11087 sepPos = 0;
11088 oldPrefix = "/";
11089 }
11090 }
11091 else
11092 {
11093 nonSymlinks.insert(prefix.str());
11094 oldPrefix = prefix;
11095 }
11096 oldPos = sepPos;
11097 }
11098 }
11099 while (sepPos!=DString::npos);
11100 return Dir::cleanDirPath(result.str());
11101}
11102
11104
11105//----------------------------------------------------------------------------
11106// Read all files matching at least one pattern in 'patList' in the
11107// directory represented by 'fi'.
11108// The directory is read iff the recursiveFlag is set.
11109// The contents of all files is append to the input string
11110
11111static void readDir(FileInfo *fi,
11112 FileNameLinkedMap *fnMap,
11113 StringUnorderedSet *exclSet,
11114 const StringVector *patList,
11115 const StringVector *exclPatList,
11116 StringVector *resultList,
11117 StringUnorderedSet *resultSet,
11118 bool errorIfNotExist,
11119 bool recursive,
11120 StringUnorderedSet *killSet,
11121 StringUnorderedSet *paths
11122 )
11123{
11124 std::string dirName = fi->absFilePath();
11125 if (paths && !dirName.empty())
11126 {
11127 paths->insert(dirName);
11128 }
11129 //printf("%s isSymLink()=%d\n",qPrint(dirName),fi->isSymLink());
11130 if (fi->isSymLink())
11131 {
11132 dirName = resolveSymlink(dirName);
11133 if (dirName.empty())
11134 {
11135 //printf("RECURSIVE SYMLINK: %s\n",qPrint(dirName));
11136 return; // recursive symlink
11137 }
11138 }
11139
11140 if (g_pathsVisited.find(dirName)!=g_pathsVisited.end())
11141 {
11142 //printf("PATH ALREADY VISITED: %s\n",qPrint(dirName));
11143 return; // already visited path
11144 }
11145 g_pathsVisited.insert(dirName);
11146
11147 Dir dir(dirName);
11148 msg("Searching for files in directory {}\n", fi->absFilePath());
11149 //printf("killSet=%p count=%d\n",killSet,killSet ? (int)killSet->count() : -1);
11150
11151 StringVector dirResultList;
11152
11153 bool caseSenseNames = useCaseSenseNames();
11154
11155 for (const auto &dirEntry : dir.iterator())
11156 {
11157 FileInfo cfi(dirEntry.path());
11158 auto checkPatterns = [&]() -> bool
11159 {
11160 return (patList==nullptr || cfi.match(*patList,caseSenseNames)) &&
11161 (exclPatList==nullptr || !cfi.match(*exclPatList,caseSenseNames)) &&
11162 (killSet==nullptr || killSet->find(cfi.absFilePath())==killSet->end());
11163 };
11164
11165 if (exclSet==nullptr || exclSet->find(cfi.absFilePath())==exclSet->end())
11166 { // file should not be excluded
11167 //printf("killSet->find(%s)\n",qPrint(cfi->absFilePath()));
11168 if (Config_getBool(EXCLUDE_SYMLINKS) && cfi.isSymLink())
11169 {
11170 }
11171 else if (!cfi.exists() || !cfi.isReadable())
11172 {
11173 if (errorIfNotExist && checkPatterns())
11174 {
11175 warn_uncond("source '{}' is not a readable file or directory... skipping.\n",cfi.absFilePath());
11176 }
11177 }
11178 else if (cfi.isFile() && checkPatterns())
11179 {
11180 std::string name=cfi.fileName();
11181 std::string path=cfi.dirPath()+"/";
11182 std::string fullName=path+name;
11183 if (fnMap)
11184 {
11185 auto fd = createFileDef(path,name);
11186 FileName *fn=nullptr;
11187 if (!name.empty())
11188 {
11189 fn = fnMap->add(name);
11190 fn->push_back(std::move(fd));
11191 }
11192 }
11193 dirResultList.push_back(fullName);
11194 if (resultSet) resultSet->insert(fullName);
11195 if (killSet) killSet->insert(fullName);
11196 }
11197 else if (recursive &&
11198 cfi.isDir() &&
11199 (exclPatList==nullptr || !cfi.match(*exclPatList,caseSenseNames)) &&
11200 cfi.fileName().at(0)!='.') // skip "." ".." and ".dir"
11201 {
11202 FileInfo acfi(cfi.absFilePath());
11203 readDir(&acfi,fnMap,exclSet,
11204 patList,exclPatList,&dirResultList,resultSet,errorIfNotExist,
11205 recursive,killSet,paths);
11206 }
11207 }
11208 }
11209 if (resultList && !dirResultList.empty())
11210 {
11211 // sort the resulting list to make the order platform independent.
11212 std::stable_sort(dirResultList.begin(),
11213 dirResultList.end(),
11214 [](const auto &f1,const auto &f2) { return dstricmp_sort(f1.c_str(),f2.c_str())<0; });
11215
11216 // append the sorted results to resultList
11217 resultList->insert(resultList->end(), dirResultList.begin(), dirResultList.end());
11218 }
11219}
11220
11221
11222//----------------------------------------------------------------------------
11223// read a file or all files in a directory and append their contents to the
11224// input string. The names of the files are appended to the 'fiList' list.
11225
11227 FileNameLinkedMap *fnMap,
11228 StringUnorderedSet *exclSet,
11229 const StringVector *patList,
11230 const StringVector *exclPatList,
11231 StringVector *resultList,
11232 StringUnorderedSet *resultSet,
11233 bool recursive,
11234 bool errorIfNotExist,
11235 StringUnorderedSet *killSet,
11236 StringUnorderedSet *paths
11237 )
11238{
11239 //printf("killSet count=%d\n",killSet ? (int)killSet->size() : -1);
11240 // strip trailing slashes
11241 if (s.empty()) return;
11242
11243 g_pathsVisited.clear();
11244
11245 FileInfo fi(s.str());
11246 //printf("readFileOrDirectory(%s)\n",s);
11247 {
11248 if (exclSet==nullptr || exclSet->find(fi.absFilePath())==exclSet->end())
11249 {
11250 if (Config_getBool(EXCLUDE_SYMLINKS) && fi.isSymLink())
11251 {
11252 }
11253 else if (!fi.exists() || !fi.isReadable())
11254 {
11255 if (errorIfNotExist)
11256 {
11257 warn_uncond("source '{}' is not a readable file or directory... skipping.\n",s);
11258 }
11259 }
11260 else if (fi.isFile())
11261 {
11262 std::string dirPath = fi.dirPath(true);
11263 std::string filePath = fi.absFilePath();
11264 if (paths && !dirPath.empty())
11265 {
11266 paths->insert(dirPath);
11267 }
11268 //printf("killSet.find(%s)=%d\n",qPrint(fi.absFilePath()),killSet.find(fi.absFilePath())!=killSet.end());
11269 if (killSet==nullptr || killSet->find(filePath)==killSet->end())
11270 {
11271 std::string name=fi.fileName();
11272 if (fnMap)
11273 {
11274 auto fd = createFileDef(dirPath+"/",name);
11275 if (!name.empty())
11276 {
11277 FileName *fn = fnMap->add(name);
11278 fn->push_back(std::move(fd));
11279 }
11280 }
11281 if (resultList || resultSet)
11282 {
11283 if (resultList) resultList->push_back(filePath);
11284 if (resultSet) resultSet->insert(filePath);
11285 }
11286
11287 if (killSet) killSet->insert(fi.absFilePath());
11288 }
11289 }
11290 else if (fi.isDir()) // readable dir
11291 {
11292 readDir(&fi,fnMap,exclSet,patList,
11293 exclPatList,resultList,resultSet,errorIfNotExist,
11294 recursive,killSet,paths);
11295 }
11296 }
11297 }
11298}
11299
11300//----------------------------------------------------------------------------
11301
11303{
11304 DString anchor;
11306 {
11307 MemberDef *md = toMemberDef(d);
11308 anchor=":"+md->anchor();
11309 }
11310 DString scope;
11311 DString fn = d->getOutputFileBase();
11314 {
11315 scope = fn;
11316 }
11317 t << "REPLACE INTO symbols (symbol_id,scope_id,name,file,line) VALUES('"
11318 << fn+anchor << "','"
11319 << scope << "','"
11320 << d->name() << "','"
11321 << d->getDefFileName() << "','"
11322 << d->getDefLine()
11323 << "');\n";
11324}
11325
11326static void dumpSymbolMap()
11327{
11328 std::ofstream f = Portable::openOutputStream("symbols.sql");
11329 if (f.is_open())
11330 {
11331 TextStream t(&f);
11332 for (const auto &[name,symList] : *Doxygen::symbolMap)
11333 {
11334 for (const auto &def : symList)
11335 {
11336 dumpSymbol(t,def);
11337 }
11338 }
11339 }
11340}
11341
11342// print developer options of Doxygen
11343static void devUsage()
11344{
11346 msg("Developer parameters:\n");
11347 msg(" -m dump symbol map\n");
11348 msg(" -b making messages output unbuffered\n");
11349 msg(" -c <file> process input file as a comment block and produce HTML output\n");
11350#if ENABLE_TRACING
11351 msg(" -t [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout)\n");
11352 msg(" -t_time [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout),\n"
11353 " and include time and thread information\n");
11354#endif
11355 msg(" -d <level> enable a debug level, such as (multiple invocations of -d are possible):\n");
11357}
11358
11359
11360//----------------------------------------------------------------------------
11361// print the version of Doxygen
11362
11363static void version(const bool extended)
11364{
11366 DString versionString = getFullVersion();
11367 msg("{}\n",versionString);
11368 if (extended)
11369 {
11370 DString extVers;
11371 if (!extVers.empty()) extVers+= ", ";
11372 extVers += "sqlite3 ";
11373 extVers += sqlite3_libversion();
11374#if USE_LIBCLANG
11375 if (!extVers.empty()) extVers+= ", ";
11376 extVers += "clang support ";
11377 extVers += CLANG_VERSION_STRING;
11378#endif
11379 if (!extVers.empty())
11380 {
11381 if (size_t lastComma = extVers.rfind(','); lastComma != DString::npos)
11382 {
11383 extVers = extVers.replace(lastComma,1," and");
11384 }
11385 msg(" with {}.\n",extVers);
11386 }
11387 }
11388}
11389
11390//----------------------------------------------------------------------------
11391// print the usage of Doxygen
11392
11393static void usage(const DString &name,const DString &versionString)
11394{
11396 msg("Doxygen version {0}\nCopyright Dimitri van Heesch 1997-2025\n\n"
11397 "You can use Doxygen in a number of ways:\n\n"
11398 "1) Use Doxygen to generate a template configuration file*:\n"
11399 " {1} [-s] -g [configName]\n\n"
11400 "2) Use Doxygen to update an old configuration file*:\n"
11401 " {1} [-s] -u [configName]\n\n"
11402 "3) Use Doxygen to generate documentation using an existing "
11403 "configuration file*:\n"
11404 " {1} [configName]\n\n"
11405 "4) Use Doxygen to generate a template file controlling the layout of the\n"
11406 " generated documentation:\n"
11407 " {1} -l [layoutFileName]\n\n"
11408 " In case layoutFileName is omitted DoxygenLayout.xml will be used as filename.\n"
11409 " If - is used for layoutFileName Doxygen will write to standard output.\n\n"
11410 "5) Use Doxygen to generate a template style sheet file for RTF, HTML or Latex.\n"
11411 " RTF: {1} -w rtf styleSheetFile\n"
11412 " HTML: {1} -w html headerFile footerFile styleSheetFile [configFile]\n"
11413 " LaTeX: {1} -w latex headerFile footerFile styleSheetFile [configFile]\n\n"
11414 "6) Use Doxygen to generate a rtf extensions file\n"
11415 " {1} -e rtf extensionsFile\n\n"
11416 " If - is used for extensionsFile Doxygen will write to standard output.\n\n"
11417 "7) Use Doxygen to compare the used configuration file with the template configuration file\n"
11418 " {1} -x [configFile]\n\n"
11419 " Use Doxygen to compare the used configuration file with the template configuration file\n"
11420 " without replacing the environment variables or CMake type replacement variables\n"
11421 " {1} -x_noenv [configFile]\n\n"
11422 "8) Use Doxygen to show a list of built-in emojis.\n"
11423 " {1} -f emoji outputFileName\n\n"
11424 " If - is used for outputFileName Doxygen will write to standard output.\n\n"
11425 "*) If -s is specified the comments of the configuration items in the config file will be omitted.\n"
11426 " If configName is omitted 'Doxyfile' will be used as a default.\n"
11427 " If - is used for configFile Doxygen will write / read the configuration to /from standard output / input.\n\n"
11428 "If -q is used for a Doxygen documentation run, Doxygen will see this as if QUIET=YES has been set.\n\n"
11429 "-v print version string, -V print extended version information\n"
11430 "-h,-? prints usage help information\n"
11431 "{1} -d prints additional usage flags for debugging purposes\n",versionString,name);
11432}
11433
11434//----------------------------------------------------------------------------
11435// read the argument of option 'c' from the comment argument list and
11436// update the option index 'optInd'.
11437
11438static const char *getArg(int argc,char **argv,int &optInd)
11439{
11440 char *s=nullptr;
11441 if (dstrlen(&argv[optInd][2])>0)
11442 s=&argv[optInd][2];
11443 else if (optInd+1<argc && argv[optInd+1][0]!='-')
11444 s=argv[++optInd];
11445 return s;
11446}
11447
11448//----------------------------------------------------------------------------
11449
11450/** @brief /dev/null outline parser */
11452{
11453 public:
11454 void parseInput(const DString &/* file */, const char * /* buf */,const std::shared_ptr<Entry> &, ClangTUParser*) override {}
11455 bool needsPreprocessing(const DString &) const override { return false; }
11456 void parsePrototype(const DString &) override {}
11457};
11458
11459
11460template<class T> std::function< std::unique_ptr<T>() > make_parser_factory()
11461{
11462 return []() { return std::make_unique<T>(); };
11463}
11464
11466{
11467 initResources();
11468 DString lang = Portable::getenv("LC_ALL");
11469 if (!lang.empty()) Portable::setenv("LANG",lang);
11470 std::setlocale(LC_ALL,"");
11471 std::setlocale(LC_CTYPE,"C"); // to get isspace(0xA0)==0, needed for UTF-8
11472 std::setlocale(LC_NUMERIC,"C");
11473
11475
11499
11500 // register any additional parsers here...
11501
11503
11504#if USE_LIBCLANG
11506#endif
11515 Doxygen::pageLinkedMap = new PageLinkedMap; // all doc pages
11516 Doxygen::exampleLinkedMap = new PageLinkedMap; // all examples
11517 //Doxygen::tagDestinationDict.setAutoDelete(true);
11519
11520 // initialization of these globals depends on
11521 // configuration switches so we need to postpone these
11522 Doxygen::globalScope = nullptr;
11532
11533}
11534
11567
11568void readConfiguration(int argc, char **argv)
11569{
11570 DString versionString = getFullVersion();
11571
11572 // helper that calls \a func to write to file \a fileName via a TextStream
11573 auto writeFile = [](const char *fileName,std::function<void(TextStream&)> func) -> bool
11574 {
11575 std::ofstream f;
11576 if (openOutputFile(fileName,f))
11577 {
11578 TextStream t(&f);
11579 func(t);
11580 return true;
11581 }
11582 return false;
11583 };
11584
11585
11586 /**************************************************************************
11587 * Handle arguments *
11588 **************************************************************************/
11589
11590 int optInd=1;
11591 DString configName;
11592 DString traceName;
11593 bool genConfig=false;
11594 bool shortList=false;
11595 bool traceTiming=false;
11597 bool updateConfig=false;
11598 bool quiet = false;
11599 while (optInd<argc && argv[optInd][0]=='-' &&
11600 (isalpha(argv[optInd][1]) || argv[optInd][1]=='?' ||
11601 argv[optInd][1]=='-')
11602 )
11603 {
11604 switch(argv[optInd][1])
11605 {
11606 case 'g':
11607 {
11608 genConfig=true;
11609 }
11610 break;
11611 case 'l':
11612 {
11613 DString layoutName;
11614 if (optInd+1>=argc)
11615 {
11616 layoutName="DoxygenLayout.xml";
11617 }
11618 else
11619 {
11620 layoutName=argv[optInd+1];
11621 }
11622 writeDefaultLayoutFile(layoutName);
11624 exit(0);
11625 }
11626 break;
11627 case 'c':
11628 if (optInd+1>=argc) // no file name given
11629 {
11630 msg("option \"-c\" is missing the file name to read\n");
11631 devUsage();
11633 exit(1);
11634 }
11635 else
11636 {
11637 g_commentFileName=argv[optInd+1];
11638 optInd++;
11639 }
11640 g_singleComment=true;
11641 quiet=true;
11642 break;
11643 case 'd':
11644 {
11645 DString debugLabel=getArg(argc,argv,optInd);
11646 if (debugLabel.empty())
11647 {
11648 devUsage();
11650 exit(0);
11651 }
11652 int retVal = Debug::setFlagStr(debugLabel);
11653 if (!retVal)
11654 {
11655 msg("option \"-d\" has unknown debug specifier: \"{}\".\n",debugLabel);
11656 devUsage();
11658 exit(1);
11659 }
11660 }
11661 break;
11662 case 't':
11663 {
11664#if ENABLE_TRACING
11665 if (!strcmp(argv[optInd]+1,"t_time"))
11666 {
11667 traceTiming = true;
11668 }
11669 else if (!strcmp(argv[optInd]+1,"t"))
11670 {
11671 traceTiming = false;
11672 }
11673 else
11674 {
11675 err("option should be \"-t\" or \"-t_time\", found: \"{}\".\n",argv[optInd]);
11677 exit(1);
11678 }
11679 if (optInd+1>=argc || argv[optInd+1][0] == '-') // no file name given
11680 {
11681 traceName="stdout";
11682 }
11683 else
11684 {
11685 traceName=argv[optInd+1];
11686 optInd++;
11687 }
11688#else
11689 err("support for option \"-t\" has not been compiled in (use a debug build or a release build with tracing enabled).\n");
11691 exit(1);
11692#endif
11693 }
11694 break;
11695 case 'x':
11696 if (!strcmp(argv[optInd]+1,"x_noenv")) diffList=Config::CompareMode::CompressedNoEnv;
11697 else if (!strcmp(argv[optInd]+1,"x")) diffList=Config::CompareMode::Compressed;
11698 else
11699 {
11700 err("option should be \"-x\" or \"-x_noenv\", found: \"{}\".\n",argv[optInd]);
11702 exit(1);
11703 }
11704 break;
11705 case 's':
11706 shortList=true;
11707 break;
11708 case 'u':
11709 updateConfig=true;
11710 break;
11711 case 'e':
11712 {
11713 DString formatName=getArg(argc,argv,optInd);
11714 if (formatName.empty())
11715 {
11716 err("option \"-e\" is missing format specifier rtf.\n");
11718 exit(1);
11719 }
11720 if (dstricmp(formatName.data(),"rtf")==0)
11721 {
11722 if (optInd+1>=argc)
11723 {
11724 err("option \"-e rtf\" is missing an extensions file name\n");
11726 exit(1);
11727 }
11728 writeFile(argv[optInd+1],RTFGenerator::writeExtensionsFile);
11730 exit(0);
11731 }
11732 err("option \"-e\" has invalid format specifier.\n");
11734 exit(1);
11735 }
11736 break;
11737 case 'f':
11738 {
11739 DString listName=getArg(argc,argv,optInd);
11740 if (listName.empty())
11741 {
11742 err("option \"-f\" is missing list specifier.\n");
11744 exit(1);
11745 }
11746 if (dstricmp(listName.data(),"emoji")==0)
11747 {
11748 if (optInd+1>=argc)
11749 {
11750 err("option \"-f emoji\" is missing an output file name\n");
11752 exit(1);
11753 }
11754 writeFile(argv[optInd+1],[](TextStream &t) { EmojiEntityMapper::instance().writeEmojiFile(t); });
11756 exit(0);
11757 }
11758 err("option \"-f\" has invalid list specifier.\n");
11760 exit(1);
11761 }
11762 break;
11763 case 'w':
11764 {
11765 DString formatName=getArg(argc,argv,optInd);
11766 if (formatName.empty())
11767 {
11768 err("option \"-w\" is missing format specifier rtf, html or latex\n");
11770 exit(1);
11771 }
11772 if (dstricmp(formatName.data(),"rtf")==0)
11773 {
11774 if (optInd+1>=argc)
11775 {
11776 err("option \"-w rtf\" is missing a style sheet file name\n");
11778 exit(1);
11779 }
11780 if (!writeFile(argv[optInd+1],RTFGenerator::writeStyleSheetFile))
11781 {
11782 err("error opening RTF style sheet file {}!\n",argv[optInd+1]);
11784 exit(1);
11785 }
11787 exit(0);
11788 }
11789 else if (dstricmp(formatName.data(),"html")==0)
11790 {
11791 Config::init();
11792 if (optInd+4<argc || FileInfo("Doxyfile").exists() || FileInfo("doxyfile").exists())
11793 // explicit config file mentioned or default found on disk
11794 {
11795 DString df = optInd+4<argc ? argv[optInd+4] : (FileInfo("Doxyfile").exists() ? DString("Doxyfile") : DString("doxyfile"));
11796 if (!Config::parse(df)) // parse the config file
11797 {
11798 err("error opening or reading configuration file {}!\n",argv[optInd+4]);
11800 exit(1);
11801 }
11802 }
11803 if (optInd+3>=argc)
11804 {
11805 err("option \"-w html\" does not have enough arguments\n");
11807 exit(1);
11808 }
11809 Config::postProcess(true);
11812 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11813 writeFile(argv[optInd+1],[&](TextStream &t) { HtmlGenerator::writeHeaderFile(t,argv[optInd+3]); });
11814 writeFile(argv[optInd+2],HtmlGenerator::writeFooterFile);
11815 writeFile(argv[optInd+3],HtmlGenerator::writeStyleSheetFile);
11817 exit(0);
11818 }
11819 else if (dstricmp(formatName.data(),"latex")==0)
11820 {
11821 Config::init();
11822 if (optInd+4<argc || FileInfo("Doxyfile").exists() || FileInfo("doxyfile").exists())
11823 {
11824 DString df = optInd+4<argc ? argv[optInd+4] : (FileInfo("Doxyfile").exists() ? DString("Doxyfile") : DString("doxyfile"));
11825 if (!Config::parse(df))
11826 {
11827 err("error opening or reading configuration file {}!\n",argv[optInd+4]);
11829 exit(1);
11830 }
11831 }
11832 if (optInd+3>=argc)
11833 {
11834 err("option \"-w latex\" does not have enough arguments\n");
11836 exit(1);
11837 }
11838 Config::postProcess(true);
11841 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11842 writeFile(argv[optInd+1],LatexGenerator::writeHeaderFile);
11843 writeFile(argv[optInd+2],LatexGenerator::writeFooterFile);
11844 writeFile(argv[optInd+3],LatexGenerator::writeStyleSheetFile);
11846 exit(0);
11847 }
11848 else
11849 {
11850 err("Illegal format specifier \"{}\": should be one of rtf, html or latex\n",formatName);
11852 exit(1);
11853 }
11854 }
11855 break;
11856 case 'm':
11857 g_dumpSymbolMap = true;
11858 break;
11859 case 'v':
11860 version(false);
11862 exit(0);
11863 break;
11864 case 'V':
11865 version(true);
11867 exit(0);
11868 break;
11869 case '-':
11870 if (dstrcmp(&argv[optInd][2],"help")==0)
11871 {
11872 usage(argv[0],versionString);
11873 exit(0);
11874 }
11875 else if (dstrcmp(&argv[optInd][2],"version")==0)
11876 {
11877 version(false);
11879 exit(0);
11880 }
11881 else if ((dstrcmp(&argv[optInd][2],"Version")==0) ||
11882 (dstrcmp(&argv[optInd][2],"VERSION")==0))
11883 {
11884 version(true);
11886 exit(0);
11887 }
11888 else
11889 {
11890 err("Unknown option \"-{}\"\n",&argv[optInd][1]);
11891 usage(argv[0],versionString);
11892 exit(1);
11893 }
11894 break;
11895 case 'b':
11896 setvbuf(stdout,nullptr,_IONBF,0);
11897 break;
11898 case 'q':
11899 quiet = true;
11900 break;
11901 case 'h':
11902 case '?':
11903 usage(argv[0],versionString);
11904 exit(0);
11905 break;
11906 default:
11907 err("Unknown option \"-{:c}\"\n",argv[optInd][1]);
11908 usage(argv[0],versionString);
11909 exit(1);
11910 }
11911 optInd++;
11912 }
11913
11914 /**************************************************************************
11915 * Parse or generate the config file *
11916 **************************************************************************/
11917
11918 initTracing(traceName.data(),traceTiming);
11919 TRACE("Doxygen version used: {}",getFullVersion());
11920 Config::init();
11921
11922 FileInfo configFileInfo1("Doxyfile"),configFileInfo2("doxyfile");
11923 if (optInd>=argc)
11924 {
11925 if (configFileInfo1.exists())
11926 {
11927 configName="Doxyfile";
11928 }
11929 else if (configFileInfo2.exists())
11930 {
11931 configName="doxyfile";
11932 }
11933 else if (genConfig)
11934 {
11935 configName="Doxyfile";
11936 }
11937 else
11938 {
11939 err("Doxyfile not found and no input file specified!\n");
11940 usage(argv[0],versionString);
11941 exit(1);
11942 }
11943 }
11944 else
11945 {
11946 FileInfo fi(argv[optInd]);
11947 if (fi.exists() || dstrcmp(argv[optInd],"-")==0 || genConfig)
11948 {
11949 configName=argv[optInd];
11950 }
11951 else
11952 {
11953 err("configuration file {} not found!\n",argv[optInd]);
11954 usage(argv[0],versionString);
11955 exit(1);
11956 }
11957 }
11958
11959 if (genConfig)
11960 {
11961 generateConfigFile(configName,shortList);
11963 exit(0);
11964 }
11965
11966 if (!Config::parse(configName,updateConfig,diffList))
11967 {
11968 err("could not open or read configuration file {}!\n",configName);
11970 exit(1);
11971 }
11972
11973 if (diffList!=Config::CompareMode::Full)
11974 {
11976 compareDoxyfile(diffList);
11978 exit(0);
11979 }
11980
11981 if (updateConfig)
11982 {
11984 generateConfigFile(configName,shortList,true);
11986 exit(0);
11987 }
11988
11989 /* Perlmod wants to know the path to the config file.*/
11990 FileInfo configFileInfo(configName.str());
11991 setPerlModDoxyfile(configFileInfo.absFilePath());
11992
11993 /* handle -q option */
11994 if (quiet) Config_updateBool(QUIET,true);
11995}
11996
11997/** check and resolve config options */
11999{
12000 AUTO_TRACE();
12001
12002 Config::postProcess(false);
12006}
12007
12008/** adjust globals that depend on configuration settings. */
12010{
12011 AUTO_TRACE();
12012 Doxygen::globalNamespaceDef = createNamespaceDef("<globalScope>",1,1,"<globalScope>");
12023
12024 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
12025
12026 /* Set the global html file extension. */
12027 Doxygen::htmlFileExtension = Config_getString(HTML_FILE_EXTENSION);
12028
12029
12031 Config_getBool(CALLER_GRAPH) ||
12032 Config_getBool(REFERENCES_RELATION) ||
12033 Config_getBool(REFERENCED_BY_RELATION);
12034
12035 /**************************************************************************
12036 * Add custom extension mappings
12037 **************************************************************************/
12038
12039 StringVector extMaps = Config_getList(EXTENSION_MAPPING);
12040 for (const auto &mapping : extMaps)
12041 {
12042 DString mapStr = mapping;
12043 if (size_t i=mapStr.find('='); i==DString::npos)
12044 {
12045 continue;
12046 }
12047 else
12048 {
12049 DString ext = mapStr.left(i).stripWhiteSpace().lower();
12050 DString language = mapStr.mid(i+1).stripWhiteSpace().lower();
12051 if (ext.empty() || language.empty())
12052 {
12053 continue;
12054 }
12055
12056 if (!updateLanguageMapping(ext,language))
12057 {
12058 err("Failed to map file extension '{}' to unsupported language '{}'.\n"
12059 "Check the EXTENSION_MAPPING setting in the config file.\n",
12060 ext,language);
12061 }
12062 else
12063 {
12064 msg("Adding custom extension mapping: '{}' will be treated as language '{}'\n",
12065 ext,language);
12066 }
12067 }
12068 }
12069 // create input file exncodings
12070
12071 // check INPUT_ENCODING
12072 void *cd = portable_iconv_open("UTF-8",Config_getString(INPUT_ENCODING).data());
12073 if (cd==reinterpret_cast<void *>(-1))
12074 {
12075 term("unsupported character conversion: '{}'->'UTF-8': {}\n"
12076 "Check the 'INPUT_ENCODING' setting in the config file!\n",
12077 Config_getString(INPUT_ENCODING),strerror(errno));
12078 }
12079 else
12080 {
12082 }
12083
12084 // check and split INPUT_FILE_ENCODING
12085 StringVector fileEncod = Config_getList(INPUT_FILE_ENCODING);
12086 for (const auto &mapping : fileEncod)
12087 {
12088 DString mapStr = mapping;
12089 if (size_t i=mapStr.find('='); i==DString::npos)
12090 {
12091 continue;
12092 }
12093 else
12094 {
12095 DString pattern = mapStr.left(i).stripWhiteSpace().lower();
12096 DString encoding = mapStr.mid(i+1).stripWhiteSpace().lower();
12097 if (pattern.empty() || encoding.empty())
12098 {
12099 continue;
12100 }
12101 cd = portable_iconv_open("UTF-8",encoding.data());
12102 if (cd==reinterpret_cast<void *>(-1))
12103 {
12104 term("unsupported character conversion: '{}'->'UTF-8': {}\n"
12105 "Check the 'INPUT_FILE_ENCODING' setting in the config file!\n",
12106 encoding,strerror(errno));
12107 }
12108 else
12109 {
12111 }
12112
12113 Doxygen::inputFileEncodingList.emplace_back(pattern, encoding);
12114 }
12115 }
12116
12117 // add predefined macro name to a dictionary
12118 StringVector expandAsDefinedList = Config_getList(EXPAND_AS_DEFINED);
12119 for (const auto &s : expandAsDefinedList)
12120 {
12122 }
12123
12124 // read aliases and store them in a dictionary
12125 readAliases();
12126
12127 // store number of spaces in a tab into Doxygen::spaces
12128 int tabSize = Config_getInt(TAB_SIZE);
12129 Doxygen::spaces.resize(tabSize);
12130 for (int sp=0; sp<tabSize; sp++) Doxygen::spaces.at(sp)=' ';
12131 Doxygen::spaces.at(tabSize)='\0';
12132}
12133
12134#ifdef HAS_SIGNALS
12135static void stopDoxygen(int)
12136{
12137 signal(SIGINT,SIG_DFL); // Re-register signal handler for default action
12138 Dir thisDir;
12139 msg("Cleaning up...\n");
12140 if (!Doxygen::filterDBFileName.empty())
12141 {
12142 thisDir.remove(Doxygen::filterDBFileName.str());
12143 }
12144 killpg(0,SIGINT);
12146 exitTracing();
12147 exit(1);
12148}
12149#endif
12150
12151static void writeTagFile()
12152{
12153 DString generateTagFile = Config_getString(GENERATE_TAGFILE);
12154 if (generateTagFile.empty()) return;
12155
12156 std::ofstream f = Portable::openOutputStream(generateTagFile);
12157 if (!f.is_open())
12158 {
12159 err("cannot open tag file {} for writing\n", generateTagFile);
12160 return;
12161 }
12162 TextStream tagFile(&f);
12163 tagFile << "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>\n";
12164 tagFile << "<tagfile doxygen_version=\"" << getDoxygenVersion() << "\"";
12165 std::string gitVersion = getGitVersion();
12166 if (!gitVersion.empty())
12167 {
12168 tagFile << " doxygen_gitid=\"" << gitVersion << "\"";
12169 }
12170 tagFile << ">\n";
12171
12172 // for each file
12173 for (const auto &fn : *Doxygen::inputNameLinkedMap)
12174 {
12175 for (const auto &fd : *fn)
12176 {
12177 if (fd->isLinkableInProject()) fd->writeTagFile(tagFile);
12178 }
12179 }
12180 // for each class
12181 for (const auto &cd : *Doxygen::classLinkedMap)
12182 {
12183 ClassDefMutable *cdm = toClassDefMutable(cd.get());
12184 if (cdm && cdm->isLinkableInProject())
12185 {
12186 cdm->writeTagFile(tagFile);
12187 }
12188 }
12189 // for each concept
12190 for (const auto &cd : *Doxygen::conceptLinkedMap)
12191 {
12192 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
12193 if (cdm && cdm->isLinkableInProject())
12194 {
12195 cdm->writeTagFile(tagFile);
12196 }
12197 }
12198 // for each namespace
12199 for (const auto &nd : *Doxygen::namespaceLinkedMap)
12200 {
12202 if (ndm && nd->isLinkableInProject())
12203 {
12204 ndm->writeTagFile(tagFile);
12205 }
12206 }
12207 // for each group
12208 for (const auto &gd : *Doxygen::groupLinkedMap)
12209 {
12210 if (gd->isLinkableInProject()) gd->writeTagFile(tagFile);
12211 }
12212 // for each module
12213 for (const auto &mod : ModuleManager::instance().modules())
12214 {
12215 if (mod->isLinkableInProject()) mod->writeTagFile(tagFile);
12216 }
12217 // for each page
12218 for (const auto &pd : *Doxygen::pageLinkedMap)
12219 {
12220 if (pd->isLinkableInProject()) pd->writeTagFile(tagFile);
12221 }
12222 // for requirements
12224 // for each directory
12225 for (const auto &dd : *Doxygen::dirLinkedMap)
12226 {
12227 if (dd->isLinkableInProject()) dd->writeTagFile(tagFile);
12228 }
12229 if (Doxygen::mainPage) Doxygen::mainPage->writeTagFile(tagFile);
12230
12231 tagFile << "</tagfile>\n";
12232}
12233
12234static void exitDoxygen() noexcept
12235{
12236 if (!g_successfulRun) // premature exit
12237 {
12238 Dir thisDir;
12239 msg("Exiting...\n");
12240 if (!Doxygen::filterDBFileName.empty())
12241 {
12242 thisDir.remove(Doxygen::filterDBFileName.str());
12243 }
12244 }
12245}
12246
12247static DString createOutputDirectory(const DString &baseDirName,
12248 const DString &formatDirName,
12249 const char *defaultDirName)
12250{
12251 DString result = formatDirName;
12252 if (result.empty())
12253 {
12254 result = baseDirName + defaultDirName;
12255 }
12256 else if (formatDirName[0]!='/' && (formatDirName.length()==1 || formatDirName[1]!=':'))
12257 {
12258 result.prepend(baseDirName+"/");
12259 }
12260 Dir formatDir(result.str());
12261 if (!formatDir.exists() && !formatDir.mkdir(result.str()))
12262 {
12263 term("Could not create output directory {}\n", result);
12264 }
12265 return result;
12266}
12267
12269{
12270 StringUnorderedSet killSet;
12271
12272 StringVector exclPatterns = Config_getList(EXCLUDE_PATTERNS);
12273 bool alwaysRecursive = Config_getBool(RECURSIVE);
12274 StringUnorderedSet excludeNameSet;
12275
12276 // gather names of all files in the include path
12277 g_s.begin("Searching for include files...\n");
12278 killSet.clear();
12279 StringVector includePathList = Config_getList(INCLUDE_PATH);
12280 for (const auto &s : includePathList)
12281 {
12282 size_t plSize = Config_getList(INCLUDE_FILE_PATTERNS).size();
12283 StringVector pl = plSize==0 ? Config_getList(FILE_PATTERNS) :
12284 Config_getList(INCLUDE_FILE_PATTERNS);
12285 readFileOrDirectory(s, // s
12287 nullptr, // exclSet
12288 &pl, // patList
12289 &exclPatterns, // exclPatList
12290 nullptr, // resultList
12291 nullptr, // resultSet
12292 false, // INCLUDE_PATH isn't recursive
12293 true, // errorIfNotExist
12294 &killSet); // killSet
12295 }
12296 g_s.end();
12297
12298 g_s.begin("Searching for example files...\n");
12299 killSet.clear();
12300 StringVector examplePathList = Config_getList(EXAMPLE_PATH);
12301 for (const auto &s : examplePathList)
12302 {
12303 StringVector patterns = Config_getList(EXAMPLE_PATTERNS);
12304 readFileOrDirectory(s, // s
12306 nullptr, // exclSet
12307 &patterns, // patList
12308 nullptr, // exclPatList
12309 nullptr, // resultList
12310 nullptr, // resultSet
12311 (alwaysRecursive || Config_getBool(EXAMPLE_RECURSIVE)), // recursive
12312 true, // errorIfNotExist
12313 &killSet); // killSet
12314 }
12315 g_s.end();
12316
12317 g_s.begin("Searching for images...\n");
12318 killSet.clear();
12319 StringVector imagePathList=Config_getList(IMAGE_PATH);
12320 for (const auto &s : imagePathList)
12321 {
12322 readFileOrDirectory(s, // s
12324 nullptr, // exclSet
12325 nullptr, // patList
12326 nullptr, // exclPatList
12327 nullptr, // resultList
12328 nullptr, // resultSet
12329 alwaysRecursive, // recursive
12330 true, // errorIfNotExist
12331 &killSet); // killSet
12332 }
12333 g_s.end();
12334
12335 g_s.begin("Searching for dot files...\n");
12336 killSet.clear();
12337 StringVector dotFileList=Config_getList(DOTFILE_DIRS);
12338 for (const auto &s : dotFileList)
12339 {
12340 readFileOrDirectory(s, // s
12342 nullptr, // exclSet
12343 nullptr, // patList
12344 nullptr, // exclPatList
12345 nullptr, // resultList
12346 nullptr, // resultSet
12347 alwaysRecursive, // recursive
12348 true, // errorIfNotExist
12349 &killSet); // killSet
12350 }
12351 g_s.end();
12352
12353 g_s.begin("Searching for msc files...\n");
12354 killSet.clear();
12355 StringVector mscFileList=Config_getList(MSCFILE_DIRS);
12356 for (const auto &s : mscFileList)
12357 {
12358 readFileOrDirectory(s, // s
12360 nullptr, // exclSet
12361 nullptr, // patList
12362 nullptr, // exclPatList
12363 nullptr, // resultList
12364 nullptr, // resultSet
12365 alwaysRecursive, // recursive
12366 true, // errorIfNotExist
12367 &killSet); // killSet
12368 }
12369 g_s.end();
12370
12371 g_s.begin("Searching for dia files...\n");
12372 killSet.clear();
12373 StringVector diaFileList=Config_getList(DIAFILE_DIRS);
12374 for (const auto &s : diaFileList)
12375 {
12376 readFileOrDirectory(s, // s
12378 nullptr, // exclSet
12379 nullptr, // patList
12380 nullptr, // exclPatList
12381 nullptr, // resultList
12382 nullptr, // resultSet
12383 alwaysRecursive, // recursive
12384 true, // errorIfNotExist
12385 &killSet); // killSet
12386 }
12387 g_s.end();
12388
12389 g_s.begin("Searching for plantuml files...\n");
12390 killSet.clear();
12391 StringVector plantUmlFileList=Config_getList(PLANTUMLFILE_DIRS);
12392 for (const auto &s : plantUmlFileList)
12393 {
12394 readFileOrDirectory(s, // s
12396 nullptr, // exclSet
12397 nullptr, // patList
12398 nullptr, // exclPatList
12399 nullptr, // resultList
12400 nullptr, // resultSet
12401 alwaysRecursive, // recursive
12402 true, // errorIfNotExist
12403 &killSet); // killSet
12404 }
12405 g_s.end();
12406
12407 g_s.begin("Searching for mermaid files...\n");
12408 killSet.clear();
12409 StringVector mermaidFileList=Config_getList(MERMAIDFILE_DIRS);
12410 for (const auto &s : mermaidFileList)
12411 {
12412 readFileOrDirectory(s, // s
12414 nullptr, // exclSet
12415 nullptr, // patList
12416 nullptr, // exclPatList
12417 nullptr, // resultList
12418 nullptr, // resultSet
12419 alwaysRecursive, // recursive
12420 true, // errorIfNotExist
12421 &killSet); // killSet
12422 }
12423 g_s.end();
12424
12425 g_s.begin("Searching for files to exclude\n");
12426 StringVector excludeList = Config_getList(EXCLUDE);
12427 for (const auto &s : excludeList)
12428 {
12429 StringVector filePatterns = Config_getList(FILE_PATTERNS);
12430 readFileOrDirectory(s, // s
12431 nullptr, // fnDict
12432 nullptr, // exclSet
12433 &filePatterns, // patList
12434 nullptr, // exclPatList
12435 nullptr, // resultList
12436 &excludeNameSet, // resultSet
12437 alwaysRecursive, // recursive
12438 false); // errorIfNotExist
12439 }
12440 g_s.end();
12441
12442 /**************************************************************************
12443 * Determine Input Files *
12444 **************************************************************************/
12445
12446 g_s.begin("Searching INPUT for files to process...\n");
12447 killSet.clear();
12448 Doxygen::inputPaths.clear();
12449 StringVector inputList=Config_getList(INPUT);
12450 for (const auto &s : inputList)
12451 {
12452 DString path = s;
12453 size_t l = path.length();
12454 if (l>0)
12455 {
12456 // strip trailing slashes
12457 if (path.at(l-1)=='\\' || path.at(l-1)=='/') path=path.left(l-1);
12458
12459 StringVector filePatterns = Config_getList(FILE_PATTERNS);
12461 path, // s
12463 &excludeNameSet, // exclSet
12464 &filePatterns, // patList
12465 &exclPatterns, // exclPatList
12466 &g_inputFiles, // resultList
12467 nullptr, // resultSet
12468 alwaysRecursive, // recursive
12469 true, // errorIfNotExist
12470 &killSet, // killSet
12471 &Doxygen::inputPaths); // paths
12472 }
12473 }
12474
12475 // Sort the FileDef objects by full path to get a predictable ordering over multiple runs
12476 for (auto &fileName : *Doxygen::inputNameLinkedMap)
12477 {
12478 if (fileName->size()>1)
12479 {
12480 std::stable_sort(fileName->begin(),fileName->end(),[](const auto &f1,const auto &f2)
12481 {
12482 return dstricmp_sort(f1->absFilePath(),f2->absFilePath())<0;
12483 });
12484 }
12485 }
12486 std::stable_sort(Doxygen::inputNameLinkedMap->begin(),
12488 [](const auto &f1,const auto &f2)
12489 {
12490 return dstricmp_sort(f1->front()->absFilePath(),f2->front()->absFilePath())<0;
12491 });
12492 if (Doxygen::inputNameLinkedMap->empty())
12493 {
12494 warn_uncond("No files to be processed, please check your settings, in particular INPUT, FILE_PATTERNS, and RECURSIVE\n");
12495 }
12496 g_s.end();
12497}
12498
12499
12501{
12502 if (Config_getBool(MARKDOWN_SUPPORT))
12503 {
12504 DString mdfileAsMainPage = Config_getString(USE_MDFILE_AS_MAINPAGE);
12505 if (mdfileAsMainPage.empty()) return;
12506 FileInfo fi(mdfileAsMainPage.data());
12507 if (!fi.exists())
12508 {
12509 warn_uncond("Specified markdown mainpage '{}' does not exist\n",mdfileAsMainPage);
12510 return;
12511 }
12512 bool ambig = false;
12513 if (Doxygen::inputNameLinkedMap->findFileDef(fi.absFilePath(),ambig)==nullptr)
12514 {
12515 warn_uncond("Specified markdown mainpage '{}' has not been defined as input file\n",mdfileAsMainPage);
12516 return;
12517 }
12518 }
12519}
12520
12522{
12523 AUTO_TRACE();
12524 std::atexit(exitDoxygen);
12525
12526 Portable::correctPath(Config_getList(EXTERNAL_TOOL_PATH));
12527
12528#if USE_LIBCLANG
12529 Doxygen::clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING);
12530#endif
12531
12532 // we would like to show the versionString earlier, but we first have to handle the configuration file
12533 // to know the value of the QUIET setting.
12534 DString versionString = getFullVersion();
12535 msg("Doxygen version used: {}\n",versionString);
12536
12538
12539 /**************************************************************************
12540 * Make sure the output directory exists
12541 **************************************************************************/
12542 DString outputDirectory = Config_getString(OUTPUT_DIRECTORY);
12543 if (!g_singleComment)
12544 {
12545 if (outputDirectory.empty())
12546 {
12547 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,Dir::currentDirPath());
12548 }
12549 else
12550 {
12551 Dir dir(outputDirectory.str());
12552 if (!dir.exists())
12553 {
12555 if (!dir.mkdir(outputDirectory.str()))
12556 {
12557 term("tag OUTPUT_DIRECTORY: Output directory '{}' does not "
12558 "exist and cannot be created\n",outputDirectory);
12559 }
12560 else
12561 {
12562 msg("Notice: Output directory '{}' does not exist. "
12563 "I have created it for you.\n", outputDirectory);
12564 }
12565 dir.setPath(outputDirectory.str());
12566 }
12567 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,dir.absPath());
12568 }
12569 }
12570 AUTO_TRACE_ADD("outputDirectory={}",outputDirectory);
12571
12572 /**************************************************************************
12573 * Initialize global lists and dictionaries
12574 **************************************************************************/
12575
12576#ifdef HAS_SIGNALS
12577 signal(SIGINT, stopDoxygen);
12578#endif
12579
12580 uint32_t pid = Portable::pid();
12581 Doxygen::filterDBFileName.sprintf("doxygen_filterdb_%d.tmp",pid);
12582 Doxygen::filterDBFileName.prepend(outputDirectory+"/");
12583
12584 /**************************************************************************
12585 * Check/create output directories *
12586 **************************************************************************/
12587
12588 bool generateHtml = Config_getBool(GENERATE_HTML);
12589 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
12590 bool generateXml = Config_getBool(GENERATE_XML);
12591 bool generateLatex = Config_getBool(GENERATE_LATEX);
12592 bool generateRtf = Config_getBool(GENERATE_RTF);
12593 bool generateMan = Config_getBool(GENERATE_MAN);
12594 bool generateSql = Config_getBool(GENERATE_SQLITE3);
12595 DString htmlOutput;
12596 DString docbookOutput;
12597 DString xmlOutput;
12598 DString latexOutput;
12599 DString rtfOutput;
12600 DString manOutput;
12601 DString sqlOutput;
12602
12603 if (!g_singleComment)
12604 {
12605 if (generateHtml)
12606 {
12607 htmlOutput = createOutputDirectory(outputDirectory,Config_getString(HTML_OUTPUT),"/html");
12608 Config_updateString(HTML_OUTPUT,htmlOutput);
12609
12610 DString sitemapUrl = Config_getString(SITEMAP_URL);
12611 bool generateSitemap = !sitemapUrl.empty();
12612 if (generateSitemap && !sitemapUrl.endsWith("/"))
12613 {
12614 Config_updateString(SITEMAP_URL,sitemapUrl+"/");
12615 }
12616
12617 // add HTML indexers that are enabled
12618 bool generateHtmlHelp = Config_getBool(GENERATE_HTMLHELP);
12619 bool generateEclipseHelp = Config_getBool(GENERATE_ECLIPSEHELP);
12620 bool generateQhp = Config_getBool(GENERATE_QHP);
12621 bool generateTreeView = Config_getBool(GENERATE_TREEVIEW);
12622 bool generateDocSet = Config_getBool(GENERATE_DOCSET);
12623 if (generateEclipseHelp) Doxygen::indexList->addIndex<EclipseHelp>();
12624 if (generateHtmlHelp) Doxygen::indexList->addIndex<HtmlHelp>();
12625 if (generateQhp) Doxygen::indexList->addIndex<Qhp>();
12626 if (generateSitemap) Doxygen::indexList->addIndex<Sitemap>();
12627 if (generateTreeView) Doxygen::indexList->addIndex<FTVHelp>(true);
12628 if (generateDocSet) Doxygen::indexList->addIndex<DocSets>();
12631 }
12632
12633 if (generateDocbook)
12634 {
12635 docbookOutput = createOutputDirectory(outputDirectory,Config_getString(DOCBOOK_OUTPUT),"/docbook");
12636 Config_updateString(DOCBOOK_OUTPUT,docbookOutput);
12637 }
12638
12639 if (generateXml)
12640 {
12641 xmlOutput = createOutputDirectory(outputDirectory,Config_getString(XML_OUTPUT),"/xml");
12642 Config_updateString(XML_OUTPUT,xmlOutput);
12643 }
12644
12645 if (generateLatex)
12646 {
12647 latexOutput = createOutputDirectory(outputDirectory,Config_getString(LATEX_OUTPUT), "/latex");
12648 Config_updateString(LATEX_OUTPUT,latexOutput);
12649 }
12650
12651 if (generateRtf)
12652 {
12653 rtfOutput = createOutputDirectory(outputDirectory,Config_getString(RTF_OUTPUT),"/rtf");
12654 Config_updateString(RTF_OUTPUT,rtfOutput);
12655 }
12656
12657 if (generateMan)
12658 {
12659 manOutput = createOutputDirectory(outputDirectory,Config_getString(MAN_OUTPUT),"/man");
12660 Config_updateString(MAN_OUTPUT,manOutput);
12661 }
12662
12663 if (generateSql)
12664 {
12665 sqlOutput = createOutputDirectory(outputDirectory,Config_getString(SQLITE3_OUTPUT),"/sqlite3");
12666 Config_updateString(SQLITE3_OUTPUT,sqlOutput);
12667 }
12668 }
12669
12670 if (Config_getBool(HAVE_DOT))
12671 {
12672 DString curFontPath = Config_getString(DOT_FONTPATH);
12673 if (curFontPath.empty())
12674 {
12675 Portable::getenv("DOTFONTPATH");
12676 DString newFontPath = ".";
12677 if (!curFontPath.empty())
12678 {
12679 newFontPath+=Portable::pathListSeparator();
12680 newFontPath+=curFontPath;
12681 }
12682 Portable::setenv("DOTFONTPATH",qPrint(newFontPath));
12683 }
12684 else
12685 {
12686 Portable::setenv("DOTFONTPATH",qPrint(curFontPath));
12687 }
12688 }
12689
12690 /**************************************************************************
12691 * Handle layout file *
12692 **************************************************************************/
12693
12695 DString layoutFileName = Config_getString(LAYOUT_FILE);
12696 bool defaultLayoutUsed = false;
12697 if (layoutFileName.empty())
12698 {
12699 layoutFileName = Config_updateString(LAYOUT_FILE,"DoxygenLayout.xml");
12700 defaultLayoutUsed = true;
12701 }
12702 AUTO_TRACE_ADD("defaultLayoutUsed={}, layoutFileName={}",defaultLayoutUsed,layoutFileName);
12703
12704 FileInfo fi(layoutFileName.str());
12705 if (fi.exists())
12706 {
12707 msg("Parsing layout file {}...\n",layoutFileName);
12708 LayoutDocManager::instance().parse(layoutFileName);
12709 }
12710 else if (!defaultLayoutUsed)
12711 {
12712 warn_uncond("failed to open layout file '{}' for reading! Using default settings.\n",layoutFileName);
12713 }
12714 printLayout();
12715
12716 /**************************************************************************
12717 * Read and preprocess input *
12718 **************************************************************************/
12719
12720 // prevent search in the output directories
12721 StringVector exclPatterns = Config_getList(EXCLUDE_PATTERNS);
12722 if (generateHtml) exclPatterns.push_back(htmlOutput.str());
12723 if (generateDocbook) exclPatterns.push_back(docbookOutput.str());
12724 if (generateXml) exclPatterns.push_back(xmlOutput.str());
12725 if (generateLatex) exclPatterns.push_back(latexOutput.str());
12726 if (generateRtf) exclPatterns.push_back(rtfOutput.str());
12727 if (generateMan) exclPatterns.push_back(manOutput.str());
12728 Config_updateList(EXCLUDE_PATTERNS,exclPatterns);
12729
12730 if (!g_singleComment)
12731 {
12733
12735 }
12736
12737 // Notice: the order of the function calls below is very important!
12738
12739 if (generateHtml && !Config_getBool(USE_MATHJAX))
12740 {
12742 }
12743 if (generateRtf)
12744 {
12746 }
12747 if (generateDocbook)
12748 {
12750 }
12751
12753
12754 /**************************************************************************
12755 * Handle Tag Files *
12756 **************************************************************************/
12757
12758 std::shared_ptr<Entry> root = std::make_shared<Entry>();
12759
12760 if (!g_singleComment)
12761 {
12762 msg("Reading and parsing tag files\n");
12763 StringVector tagFileList = Config_getList(TAGFILES);
12764 for (const auto &s : tagFileList)
12765 {
12766 readTagFile(root,s.c_str());
12767 }
12768 }
12769
12770 /**************************************************************************
12771 * Parse source files *
12772 **************************************************************************/
12773
12774 addSTLSupport(root);
12775
12776 g_s.begin("Parsing files\n");
12777 if (g_singleComment)
12778 {
12779 //printf("Parsing comment %s\n",qPrint(g_commentFileName));
12780 if (g_commentFileName=="-")
12781 {
12782 std::string text = fileToString(g_commentFileName).str();
12783 addTerminalCharIfMissing(text,'\n');
12784 generateHtmlForComment("stdin.md",text);
12785 }
12786 else if (FileInfo(g_commentFileName.str()).isFile())
12787 {
12788 std::string text;
12790 addTerminalCharIfMissing(text,'\n');
12792 }
12793 else
12794 {
12795 }
12797 exit(0);
12798 }
12799 else
12800 {
12801 if (Config_getInt(NUM_PROC_THREADS)==1)
12802 {
12804 }
12805 else
12806 {
12808 }
12809 }
12810 g_s.end();
12811
12812 /**************************************************************************
12813 * Gather information *
12814 **************************************************************************/
12815
12816 g_s.begin("Building macro definition list...\n");
12818 g_s.end();
12819
12820 g_s.begin("Building group list...\n");
12821 buildGroupList(root.get());
12822 organizeSubGroups(root.get());
12823 g_s.end();
12824
12825 g_s.begin("Building directory list...\n");
12827 findDirDocumentation(root.get());
12828 g_s.end();
12829
12830 g_s.begin("Building namespace list...\n");
12831 buildNamespaceList(root.get());
12832 findUsingDirectives(root.get());
12833 g_s.end();
12834
12835 g_s.begin("Building file list...\n");
12836 buildFileList(root.get());
12837 g_s.end();
12838
12839 g_s.begin("Building class list...\n");
12840 buildClassList(root.get());
12841 g_s.end();
12842
12843 g_s.begin("Building concept list...\n");
12844 buildConceptList(root.get());
12845 g_s.end();
12846
12847 // build list of using declarations here (global list)
12848 buildListOfUsingDecls(root.get());
12849 g_s.end();
12850
12851 g_s.begin("Computing nesting relations for classes...\n");
12853 g_s.end();
12854 // 1.8.2-20121111: no longer add nested classes to the group as well
12855 //distributeClassGroupRelations();
12856
12857 // calling buildClassList may result in cached relations that
12858 // become invalid after resolveClassNestingRelations(), that's why
12859 // we need to clear the cache here
12861 // we don't need the list of using declaration anymore
12862 g_usingDeclarations.clear();
12863
12864 g_s.begin("Associating documentation with classes...\n");
12865 buildClassDocList(root.get());
12866 g_s.end();
12867
12868 g_s.begin("Associating documentation with concepts...\n");
12869 buildConceptDocList(root.get());
12871 g_s.end();
12872
12873 g_s.begin("Associating documentation with modules...\n");
12874 findModuleDocumentation(root.get());
12875 g_s.end();
12876
12877 g_s.begin("Building example list...\n");
12878 buildExampleList(root.get());
12879 g_s.end();
12880
12881 g_s.begin("Searching for enumerations...\n");
12882 findEnums(root.get());
12883 g_s.end();
12884
12885 // Since buildVarList calls isVarWithConstructor
12886 // and this calls getResolvedClass we need to process
12887 // typedefs first so the relations between classes via typedefs
12888 // are properly resolved. See bug 536385 for an example.
12889 g_s.begin("Searching for documented typedefs...\n");
12890 buildTypedefList(root.get());
12891 g_s.end();
12892
12893 if (Config_getBool(OPTIMIZE_OUTPUT_SLICE))
12894 {
12895 g_s.begin("Searching for documented sequences...\n");
12896 buildSequenceList(root.get());
12897 g_s.end();
12898
12899 g_s.begin("Searching for documented dictionaries...\n");
12900 buildDictionaryList(root.get());
12901 g_s.end();
12902 }
12903
12904 g_s.begin("Searching for members imported via using declarations...\n");
12905 // this should be after buildTypedefList in order to properly import
12906 // used typedefs
12907 findUsingDeclarations(root.get(),true); // do for python packages first
12908 findUsingDeclarations(root.get(),false); // then the rest
12909 g_s.end();
12910
12911 g_s.begin("Searching for included using directives...\n");
12913 g_s.end();
12914
12915 g_s.begin("Searching for documented variables...\n");
12916 buildVarList(root.get());
12917 g_s.end();
12918
12919 g_s.begin("Building interface member list...\n");
12920 buildInterfaceAndServiceList(root.get()); // UNO IDL
12921
12922 g_s.begin("Building member list...\n"); // using class info only !
12923 buildFunctionList(root.get());
12924 g_s.end();
12925
12926 g_s.begin("Searching for friends...\n");
12927 findFriends();
12928 g_s.end();
12929
12930 g_s.begin("Searching for documented defines...\n");
12931 findDefineDocumentation(root.get());
12932 g_s.end();
12933
12934 g_s.begin("Computing class inheritance relations...\n");
12935 findClassEntries(root.get());
12937 g_s.end();
12938
12939 g_s.begin("Computing class usage relations...\n");
12941 g_s.end();
12942
12943 g_s.begin("Flushing cached template relations that have become invalid...\n");
12945 g_s.end();
12946
12947 g_s.begin("Warn for undocumented namespaces...\n");
12949 g_s.end();
12950
12951 g_s.begin("Computing class relations...\n");
12954 if (Config_getBool(OPTIMIZE_OUTPUT_VHDL))
12955 {
12957 }
12959 g_classEntries.clear();
12960 g_s.end();
12961
12962 g_s.begin("Add enum values to enums...\n");
12963 addEnumValuesToEnums(root.get());
12964 findEnumDocumentation(root.get());
12965 g_s.end();
12966
12967 g_s.begin("Searching for member function documentation...\n");
12968 findObjCMethodDefinitions(root.get());
12969 findMemberDocumentation(root.get()); // may introduce new members !
12970 findUsingDeclImports(root.get()); // may introduce new members !
12971 g_usingClassMap.clear();
12975 g_s.end();
12976
12977 // moved to after finding and copying documentation,
12978 // as this introduces new members see bug 722654
12979 g_s.begin("Creating members for template instances...\n");
12981 g_s.end();
12982
12983 g_s.begin("Searching for tag less structs...\n");
12985 g_s.end();
12986
12987 g_s.begin("Building page list...\n");
12988 buildPageList(root.get());
12989 g_s.end();
12990
12991 g_s.begin("Building requirements list...\n");
12992 buildRequirementsList(root.get());
12993 g_s.end();
12994
12995 g_s.begin("Search for main page...\n");
12996 findMainPage(root.get());
12997 findMainPageTagFiles(root.get());
12998 g_s.end();
12999
13000 g_s.begin("Computing page relations...\n");
13001 computePageRelations(root.get());
13003 g_s.end();
13004
13005 g_s.begin("Determining the scope of groups...\n");
13006 findGroupScope(root.get());
13007 g_s.end();
13008
13009 g_s.begin("Computing module relations...\n");
13010 auto &mm = ModuleManager::instance();
13011 mm.resolvePartitions();
13012 mm.resolveImports();
13013 mm.collectExportedSymbols();
13014 g_s.end();
13015
13016 auto memberNameComp = [](const MemberNameLinkedMap::Ptr &n1,const MemberNameLinkedMap::Ptr &n2)
13017 {
13018 return dstricmp_sort(n1->memberName().data()+getPrefixIndex(n1->memberName()),
13019 n2->memberName().data()+getPrefixIndex(n2->memberName())
13020 )<0;
13021 };
13022
13023 auto classComp = [](const ClassLinkedMap::Ptr &c1,const ClassLinkedMap::Ptr &c2)
13024 {
13025 if (Config_getBool(SORT_BY_SCOPE_NAME))
13026 {
13027 return dstricmp_sort(c1->name(), c2->name())<0;
13028 }
13029 else
13030 {
13031 int i = dstricmp_sort(c1->className(), c2->className());
13032 return i==0 ? dstricmp_sort(c1->name(), c2->name())<0 : i<0;
13033 }
13034 };
13035
13036 auto namespaceComp = [](const NamespaceLinkedMap::Ptr &n1,const NamespaceLinkedMap::Ptr &n2)
13037 {
13038 return dstricmp_sort(n1->name(),n2->name())<0;
13039 };
13040
13041 auto conceptComp = [](const ConceptLinkedMap::Ptr &c1,const ConceptLinkedMap::Ptr &c2)
13042 {
13043 return dstricmp_sort(c1->name(),c2->name())<0;
13044 };
13045
13046 g_s.begin("Sorting lists...\n");
13047 std::stable_sort(Doxygen::memberNameLinkedMap->begin(),
13049 memberNameComp);
13050 std::stable_sort(Doxygen::functionNameLinkedMap->begin(),
13052 memberNameComp);
13053 std::stable_sort(Doxygen::hiddenClassLinkedMap->begin(),
13055 classComp);
13056 std::stable_sort(Doxygen::classLinkedMap->begin(),
13058 classComp);
13059 std::stable_sort(Doxygen::conceptLinkedMap->begin(),
13061 conceptComp);
13062 std::stable_sort(Doxygen::namespaceLinkedMap->begin(),
13064 namespaceComp);
13065 g_s.end();
13066
13067 g_s.begin("Determining which enums are documented\n");
13069 g_s.end();
13070
13071 g_s.begin("Computing member relations...\n");
13074 g_s.end();
13075
13076 g_s.begin("Building full member lists recursively...\n");
13078 g_s.end();
13079
13080 g_s.begin("Adding members to member groups.\n");
13082 g_s.end();
13083
13084 if (Config_getBool(DISTRIBUTE_GROUP_DOC))
13085 {
13086 g_s.begin("Distributing member group documentation.\n");
13088 g_s.end();
13089 }
13090
13091 g_s.begin("Computing member references...\n");
13093 g_s.end();
13094
13095 if (Config_getBool(INHERIT_DOCS))
13096 {
13097 g_s.begin("Inheriting documentation...\n");
13099 g_s.end();
13100 }
13101
13102
13103 // compute the shortest possible names of all files
13104 // without losing the uniqueness of the file names.
13105 g_s.begin("Generating disk names...\n");
13107 g_s.end();
13108
13109 g_s.begin("Adding source references...\n");
13111 g_s.end();
13112
13113 g_s.begin("Adding xrefitems...\n");
13116 g_s.end();
13117
13118 g_s.begin("Adding requirements...\n");
13121 g_s.end();
13122
13123 g_s.begin("Sorting member lists...\n");
13125 g_s.end();
13126
13127 g_s.begin("Setting anonymous enum type...\n");
13129 g_s.end();
13130
13131 g_s.begin("Computing dependencies between directories...\n");
13133 g_s.end();
13134
13135 g_s.begin("Generating citations page...\n");
13137 g_s.end();
13138
13139 g_s.begin("Counting data structures...\n");
13141 g_s.end();
13142
13143 g_s.begin("Resolving user defined references...\n");
13145 g_s.end();
13146
13147 g_s.begin("Finding anchors and sections in the documentation...\n");
13149 g_s.end();
13150
13151 g_s.begin("Transferring function references...\n");
13153 g_s.end();
13154
13155 g_s.begin("Combining using relations...\n");
13157 g_s.end();
13158
13160 g_s.begin("Adding members to index pages...\n");
13162 addToIndices();
13163 g_s.end();
13164
13165 g_s.begin("Correcting members for VHDL...\n");
13167 g_s.end();
13168
13169 g_s.begin("Computing tooltip texts...\n");
13171 g_s.end();
13172
13173 if (Config_getBool(SORT_GROUP_NAMES))
13174 {
13175 std::stable_sort(Doxygen::groupLinkedMap->begin(),
13177 [](const auto &g1,const auto &g2)
13178 { return g1->groupTitle() < g2->groupTitle(); });
13179
13180 for (const auto &gd : *Doxygen::groupLinkedMap)
13181 {
13182 gd->sortSubGroups();
13183 }
13184 }
13185
13186 printNavTree(root.get(),0);
13188}
13189
13191{
13192 AUTO_TRACE();
13193 /**************************************************************************
13194 * Initialize output generators *
13195 **************************************************************************/
13196
13197 /// add extra languages for which we can only produce syntax highlighted code
13199
13200 //// dump all symbols
13201 if (g_dumpSymbolMap)
13202 {
13203 dumpSymbolMap();
13204 exit(0);
13205 }
13206
13207 bool generateHtml = Config_getBool(GENERATE_HTML);
13208 bool generateLatex = Config_getBool(GENERATE_LATEX);
13209 bool generateMan = Config_getBool(GENERATE_MAN);
13210 bool generateRtf = Config_getBool(GENERATE_RTF);
13211 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
13212
13213
13215 if (generateHtml)
13216 {
13220 }
13221 if (generateLatex)
13222 {
13225 }
13226 if (generateDocbook)
13227 {
13230 }
13231 if (generateMan)
13232 {
13235 }
13236 if (generateRtf)
13237 {
13240 }
13241 if (Config_getBool(USE_HTAGS))
13242 {
13243 Htags::useHtags = true;
13244 DString htmldir = Config_getString(HTML_OUTPUT);
13245 if (!Htags::execute(htmldir))
13246 err("USE_HTAGS is YES but htags(1) failed. \n");
13247 else if (!Htags::loadFilemap(htmldir))
13248 err("htags(1) ended normally but failed to load the filemap. \n");
13249 }
13250
13251 /**************************************************************************
13252 * Generate documentation *
13253 **************************************************************************/
13254
13255 g_s.begin("Generating style sheet...\n");
13256 //printf("writing style info\n");
13257 g_outputList->writeStyleInfo(0); // write first part
13258 g_s.end();
13259
13260 bool searchEngine = Config_getBool(SEARCHENGINE);
13261 bool serverBasedSearch = Config_getBool(SERVER_BASED_SEARCH);
13262
13263 g_s.begin("Generating search indices...\n");
13264 if (searchEngine && !serverBasedSearch && generateHtml)
13265 {
13267 }
13268
13269 // generate search indices (need to do this before writing other HTML
13270 // pages as these contain a drop down menu with options depending on
13271 // what categories we find in this function.
13272 if (generateHtml && searchEngine)
13273 {
13274 DString searchDirName = Config_getString(HTML_OUTPUT)+"/search";
13275 Dir searchDir(searchDirName.str());
13276 if (!searchDir.exists() && !searchDir.mkdir(searchDirName.str()))
13277 {
13278 term("Could not create search results directory '{}' $PWD='{}'\n",
13279 searchDirName,Dir::currentDirPath());
13280 }
13281 HtmlGenerator::writeSearchData(searchDirName);
13282 if (!serverBasedSearch) // client side search index
13283 {
13285 }
13286 }
13287 g_s.end();
13288
13289 // copy static stuff
13290 if (generateHtml)
13291 {
13293 copyLogo(Config_getString(HTML_OUTPUT),true);
13294 copyIcon(Config_getString(HTML_OUTPUT),true);
13295 copyExtraFiles(Config_getList(HTML_EXTRA_FILES),"HTML_EXTRA_FILES",Config_getString(HTML_OUTPUT),true);
13296 }
13297 if (generateLatex)
13298 {
13300 copyLogo(Config_getString(LATEX_OUTPUT),false);
13301 copyIcon(Config_getString(LATEX_OUTPUT),false);
13302 copyExtraFiles(Config_getList(LATEX_EXTRA_FILES),"LATEX_EXTRA_FILES",Config_getString(LATEX_OUTPUT),false);
13303 }
13304 if (generateDocbook)
13305 {
13306 copyLogo(Config_getString(DOCBOOK_OUTPUT),false);
13307 copyIcon(Config_getString(DOCBOOK_OUTPUT),false);
13308 }
13309 if (generateRtf)
13310 {
13311 copyLogo(Config_getString(RTF_OUTPUT),false);
13312 copyIcon(Config_getString(RTF_OUTPUT),false);
13313 copyExtraFiles(Config_getList(RTF_EXTRA_FILES),"RTF_EXTRA_FILES",Config_getString(RTF_OUTPUT),false);
13314 }
13315
13317 if (fm.hasFormulas() && generateHtml
13318 && !Config_getBool(USE_MATHJAX))
13319 {
13320 g_s.begin("Generating images for formulas in HTML...\n");
13321 fm.generateImages(Config_getString(HTML_OUTPUT), true, Config_getEnum(HTML_FORMULA_FORMAT)==HTML_FORMULA_FORMAT_t::svg ?
13323 g_s.end();
13324 }
13325 if (fm.hasFormulas() && generateRtf)
13326 {
13327 g_s.begin("Generating images for formulas in RTF...\n");
13329 g_s.end();
13330 }
13331
13332 if (fm.hasFormulas() && generateDocbook)
13333 {
13334 g_s.begin("Generating images for formulas in Docbook...\n");
13336 g_s.end();
13337 }
13338
13339 g_s.begin("Generating example documentation...\n");
13341 g_s.end();
13342
13343 g_s.begin("Generating file sources...\n");
13345 g_s.end();
13346
13347 g_s.begin("Counting members...\n");
13348 // needs to be done after generating the sources
13349 // but before generating the compound documentation, see bug #12233
13350 countMembers();
13351 g_s.end();
13352
13353 g_s.begin("Generating file documentation...\n");
13355 g_s.end();
13356
13357 g_s.begin("Generating page documentation...\n");
13359 g_s.end();
13360
13361 g_s.begin("Generating group documentation...\n");
13363 g_s.end();
13364
13365 g_s.begin("Generating class documentation...\n");
13367 g_s.end();
13368
13369 g_s.begin("Generating concept documentation...\n");
13371 g_s.end();
13372
13373 g_s.begin("Generating module documentation...\n");
13375 g_s.end();
13376
13377 g_s.begin("Generating namespace documentation...\n");
13379 g_s.end();
13380
13381 if (Config_getBool(GENERATE_LEGEND))
13382 {
13383 g_s.begin("Generating graph info page...\n");
13385 g_s.end();
13386 }
13387
13388 g_s.begin("Generating directory documentation...\n");
13390 g_s.end();
13391
13392 if (g_outputList->size()>0)
13393 {
13395 }
13396
13397 g_s.begin("finalizing index lists...\n");
13399 g_s.end();
13400
13401 g_s.begin("writing tag file...\n");
13402 writeTagFile();
13403 g_s.end();
13404
13405 if (Config_getBool(GENERATE_XML))
13406 {
13407 g_s.begin("Generating XML output...\n");
13409 generateXML();
13411 g_s.end();
13412 }
13413 if (Config_getBool(GENERATE_SQLITE3))
13414 {
13415 g_s.begin("Generating SQLITE3 output...\n");
13417 g_s.end();
13418 }
13419
13420 if (Config_getBool(GENERATE_AUTOGEN_DEF))
13421 {
13422 g_s.begin("Generating AutoGen DEF output...\n");
13423 generateDEF();
13424 g_s.end();
13425 }
13426 if (Config_getBool(GENERATE_PERLMOD))
13427 {
13428 g_s.begin("Generating Perl module output...\n");
13430 g_s.end();
13431 }
13432 if (generateHtml && searchEngine && serverBasedSearch)
13433 {
13434 g_s.begin("Generating search index\n");
13435 if (Doxygen::searchIndex.kind()==SearchIndexIntf::Internal) // write own search index
13436 {
13438 Doxygen::searchIndex.write(Config_getString(HTML_OUTPUT)+"/search/search.idx");
13439 }
13440 else // write data for external search index
13441 {
13443 DString searchDataFile = Config_getString(SEARCHDATA_FILE);
13444 if (searchDataFile.empty())
13445 {
13446 searchDataFile="searchdata.xml";
13447 }
13448 if (!Portable::isAbsolutePath(searchDataFile.data()))
13449 {
13450 searchDataFile.prepend(Config_getString(OUTPUT_DIRECTORY)+"/");
13451 }
13452 Doxygen::searchIndex.write(searchDataFile);
13453 }
13454 g_s.end();
13455 }
13456
13457 if (generateRtf)
13458 {
13459 g_s.begin("Combining RTF output...\n");
13460 if (!RTFGenerator::preProcessFileInplace(Config_getString(RTF_OUTPUT),"refman.rtf"))
13461 {
13462 err("An error occurred during post-processing the RTF files!\n");
13463 }
13464 g_s.end();
13465 }
13466
13467 if (PlantumlManager::instance().needToRun())
13468 {
13469 g_s.begin("Running plantuml with JAVA...\n");
13471 g_s.end();
13472 }
13473
13474 if (MermaidManager::instance().needToRun())
13475 {
13476 g_s.begin("Running mermaid (mmdc)...\n");
13478 g_s.end();
13479 }
13480
13481 if (Config_getBool(HAVE_DOT) && DotManager::instance()->needToRun())
13482 {
13483 g_s.begin("Running dot...\n");
13485 g_s.end();
13486 }
13487
13488 if (generateHtml &&
13489 Config_getBool(GENERATE_HTMLHELP) &&
13490 !Config_getString(HHC_LOCATION).empty())
13491 {
13492 g_s.begin("Running html help compiler...\n");
13494 g_s.end();
13495 }
13496
13497 if ( generateHtml &&
13498 Config_getBool(GENERATE_QHP) &&
13499 !Config_getString(QHG_LOCATION).empty())
13500 {
13501 g_s.begin("Running qhelpgenerator...\n");
13503 g_s.end();
13504 }
13505
13508
13510
13512 {
13513
13514 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
13515 if (numThreads<1) numThreads=1;
13516 msg("Total elapsed time: {:.6f} seconds\n(of which an average of {:.6f} seconds per thread waiting for external tools to finish)\n",
13517 (static_cast<double>(Debug::elapsedTime())),
13518 Portable::getSysElapsedTime()/static_cast<double>(numThreads)
13519 );
13520 g_s.print();
13521
13523 msg("finished...\n");
13525 }
13526 else
13527 {
13528 msg("finished...\n");
13529 }
13530
13531
13532 /**************************************************************************
13533 * Start cleaning up *
13534 **************************************************************************/
13535
13537
13539 Dir thisDir;
13540 thisDir.remove(Doxygen::filterDBFileName.str());
13542 exitTracing();
13544 delete Doxygen::clangUsrMap;
13545 g_successfulRun=true;
13546
13547 //dumpDocNodeSizes();
13548}
void readAliases()
Definition aliases.cpp:162
constexpr auto prefix
Definition anchor.cpp:47
This class contains the information about the argument of a function or template.
Definition arguments.h:27
DString defval
Definition arguments.h:47
DString array
Definition arguments.h:46
DString name
Definition arguments.h:45
DString type
Definition arguments.h:43
This class represents an function or template argument list.
Definition arguments.h:66
RefQualifierType refQualifier() const
Definition arguments.h:117
bool noParameters() const
Definition arguments.h:118
bool pureSpecifier() const
Definition arguments.h:114
iterator end()
Definition arguments.h:95
bool hasParameters() const
Definition arguments.h:77
DString trailingReturnType() const
Definition arguments.h:115
bool isDeleted() const
Definition arguments.h:116
size_t size() const
Definition arguments.h:101
void setPureSpecifier(bool b)
Definition arguments.h:122
bool constSpecifier() const
Definition arguments.h:112
void setTrailingReturnType(const DString &s)
Definition arguments.cpp:40
void push_back(const Argument &a)
Definition arguments.h:103
bool empty() const
Definition arguments.h:100
void setConstSpecifier(bool b)
Definition arguments.h:120
void setRefQualifier(RefQualifierType t)
Definition arguments.h:127
void setIsDeleted(bool b)
Definition arguments.h:126
iterator begin()
Definition arguments.h:94
bool volatileSpecifier() const
Definition arguments.h:113
void setNoParameters(bool b)
Definition arguments.h:128
void setVolatileSpecifier(bool b)
Definition arguments.h:121
static CitationManager & instance()
Definition cite.cpp:90
void clear()
clears the database
Definition cite.cpp:115
void generatePage()
Generate the citations page.
Definition cite.cpp:336
std::unique_ptr< ClangTUParser > createTUParser(const FileDef *fd) const
static ClangParser * instance()
Returns the one and only instance of the class.
Clang parser object for a single translation unit, which consists of a source file and the directly o...
Definition clangparser.h:25
void switchToFile(const FileDef *fd)
Switches to another file within the translation unit started with start().
void parse()
Parse the file given at construction time as a translation unit This file should already be preproces...
A abstract class representing of a compound symbol.
Definition classdef.h:100
virtual DString requiresClause() const =0
virtual const MemberDef * getMemberByName(const DString &) const =0
Returns the member with the given name.
virtual const ArgumentList & templateArguments() const =0
Returns the template arguments of this class.
virtual void writeDocumentation(OutputList &ol) const =0
virtual void writeMemberList(OutputList &ol) const =0
virtual bool isTemplate() const =0
Returns true if this class is a template.
virtual const BaseClassList & baseClasses() const =0
Returns the list of base classes from which this class directly inherits.
virtual int isBaseClass(const ClassDef *bcd, bool followInstances, const DString &templSpec=DString()) const =0
Returns true iff bcd is a direct or indirect base class of this class.
virtual const TemplateInstanceList & getTemplateInstances() const =0
Returns a sorted dictionary with all template instances found for this template class.
virtual ArgumentLists getTemplateParameterLists() const =0
Returns the template parameter lists that form the template declaration of this class.
virtual Protection protection() const =0
Return the protection level (Public,Protected,Private) in which this compound was found.
virtual DString compoundTypeString() const =0
Returns the type of compound as a string.
virtual MemberList * getMemberList(MemberListType lt) const =0
Returns the members in the list identified by lt.
virtual std::unique_ptr< ClassDef > deepCopy(const DString &name) const =0
virtual bool isForwardDeclared() const =0
Returns true if this class represents a forward declaration of a template class.
virtual bool isSubClass(ClassDef *bcd, int level=0) const =0
Returns true iff bcd is a direct or indirect sub class of this class.
virtual void moveTo(Definition *)=0
virtual const TemplateNameMap & getTemplateBaseClassNames() const =0
virtual DString qualifiedNameWithTemplateParameters(const ArgumentLists *actualParams=nullptr, uint32_t *actualParamIndex=nullptr) const =0
virtual bool isEmbeddedInOuterScope() const =0
virtual const MemberNameInfoLinkedMap & memberNameInfoLinkedMap() const =0
Returns a dictionary of all members.
virtual bool isImplicitTemplateInstance() const =0
virtual const ClassDef * templateMaster() const =0
Returns the template master of which this class is an instance.
CompoundType
The various compound types.
Definition classdef.h:105
@ Singleton
Definition classdef.h:113
@ Interface
Definition classdef.h:108
@ Exception
Definition classdef.h:111
virtual CompoundType compoundType() const =0
Returns the type of compound this is, i.e. class/struct/union/...
virtual bool containsOverload(const MemberDef *md) const =0
virtual ClassLinkedRefMap getClasses() const =0
returns the classes nested into this class
virtual FileDef * getFileDef() const =0
Returns the file in which this compound's definition can be found.
virtual void writeTagFile(TextStream &) const =0
virtual void writeDocumentationForInnerClasses(OutputList &ol) const =0
virtual void computeAnchors()=0
virtual void addTypeConstraints()=0
virtual void overrideCollaborationGraph(bool e)=0
virtual void addUsedClass(ClassDef *cd, const DString &accessName, Protection prot)=0
virtual void insertExplicitTemplateInstance(ClassDef *instance, const DString &spec)=0
virtual void countMembers()=0
virtual void addMembersToMemberGroup()=0
virtual void setPrimaryConstructorParams(const ArgumentList &list)=0
virtual void setTemplateBaseClassNames(const TemplateNameMap &templateNames)=0
virtual ClassDef * insertTemplateInstance(const DString &fileName, int startLine, size_t startColumn, const DString &templSpec, bool &freshInstance)=0
virtual void setClassName(const DString &name)=0
virtual void setFileDef(FileDef *fd)=0
virtual void reclassifyMember(MemberDefMutable *md, MemberType t)=0
virtual void setTemplateArguments(const ArgumentList &al)=0
virtual void setTemplateMaster(const ClassDef *tm)=0
virtual void makeTemplateArgument(bool b=true)=0
virtual void mergeCategory(ClassDef *category)=0
virtual void addQualifiers(const StringVector &qualifiers)=0
virtual void setClassSpecifier(TypeSpecifier spec)=0
virtual void insertUsedFile(const FileDef *)=0
virtual void setTagLessReference(const ClassDef *cd)=0
virtual void setUsedOnly(bool b)=0
virtual void sortMemberLists()=0
virtual void setProtection(Protection p)=0
virtual void setTypeConstraints(const ArgumentList &al)=0
virtual void overrideInheritanceGraph(CLASS_GRAPH_t e)=0
virtual void setAnonymousEnumType()=0
virtual void setCompoundType(CompoundType t)=0
virtual void distributeMemberGroupDocumentation()=0
virtual void setMetaData(const DString &md)=0
virtual void findSectionsInDocumentation()=0
virtual void insertMember(MemberDef *)=0
virtual void sortAllMembersList()=0
virtual void mergeMembers()=0
virtual void setIsStatic(bool b)=0
virtual void addMembersToTemplateInstance(const ClassDef *cd, const ArgumentList &templateArguments, const DString &templSpec)=0
virtual void insertSubClass(ClassDef *, Protection p, Specifier s, const DString &t=DString())=0
virtual void addUsedByClass(ClassDef *cd, const DString &accessName, Protection prot)=0
virtual void setSubGrouping(bool enabled)=0
virtual void setRequiresClause(const DString &req)=0
virtual void insertBaseClass(ClassDef *, const DString &name, Protection p, Specifier s, const DString &t=DString())=0
virtual void addDocPart(const DString &doc, int lineNr, size_t colNr)=0
virtual void addCodePart(const DString &code, int lineNr, size_t colNr)=0
virtual void setFileDef(FileDef *fd)=0
virtual void writeTagFile(TextStream &)=0
virtual void writeDocumentation(OutputList &ol)=0
virtual void setGroupId(int id)=0
virtual void setInitializer(const DString &init)=0
virtual void findSectionsInDocumentation()=0
virtual void setTemplateArguments(const ArgumentList &al)=0
A String class for use with Doxygen wrapping std::string and adding some additional functionality off...
Definition dstring.h:84
void clear()
Definition dstring.h:214
DString & setNum(short n)
Definition dstring.h:552
void resize(size_t newlen)
Definition dstring.h:209
size_t rfind(char c, size_t pos=npos) const
Definition dstring.h:244
DString fill(char c, size_t len)
Fills a string with a predefined character.
Definition dstring.h:278
DString mid(size_t index, size_t len=npos) const
Definition dstring.h:318
DString lower() const
Definition dstring.h:326
bool empty() const
Returns true iff the string is empty (std::string compatible alias for isEmpty()).
Definition dstring.h:148
DString & replace(size_t index, size_t len, const char *s)
Definition dstring.cpp:154
DString substr(size_t pos=0, size_t count=npos) const
Returns a substring of length count starting at pos.
Definition dstring.h:223
static constexpr size_t npos
value used to indicate 'not found' or 'to the end of the string', matching std::string::npos
Definition dstring.h:178
char & at(size_t i)
Returns a reference to the character at index i.
Definition dstring.h:686
bool findAndRemoveWord(const char *word)
removes occurrences of whole word from this string, while keeping internal spaces and reducing multip...
Definition dstring.cpp:660
DString right(size_t len) const
Definition dstring.h:311
size_t size() const
Returns the length of the string, not counting the 0-terminator.
Definition dstring.h:154
DString & prepend(const char *s)
Definition dstring.h:515
int contains(char c, bool cs=true) const
Definition dstring.cpp:85
size_t find(char c, size_t pos=0) const
Definition dstring.h:239
DString & sprintf(const char *format,...)
Definition dstring.cpp:34
int toInt(bool *ok=nullptr, int base=10) const
Definition dstring.cpp:191
@ ExplicitSize
Definition dstring.h:131
DString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
Definition dstring.h:337
DString left(size_t len) const
Definition dstring.h:306
const std::string & str() const
Definition dstring.h:645
bool stripPrefix(const DString &prefix)
Definition dstring.h:290
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition dstring.h:157
bool startsWith(const char *s) const
Definition dstring.h:600
bool endsWith(const char *s) const
Definition dstring.h:617
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition dstring.h:151
@ ExtCmd
Definition debug.h:37
@ Sections
Definition debug.h:49
@ Time
Definition debug.h:36
@ Qhp
Definition debug.h:45
@ Entries
Definition debug.h:48
static void printFlags()
Definition debug.cpp:137
static void clearFlag(const DebugMask mask)
Definition debug.cpp:122
static bool isFlagSet(const DebugMask mask)
Definition debug.cpp:132
static void print(DebugMask mask, int prio, fmt::format_string< Args... > fmt, Args &&... args)
Definition debug.h:78
static double elapsedTime()
Definition debug.cpp:200
static void startTimer()
Definition debug.cpp:195
static bool setFlagStr(const DString &label)
Definition debug.cpp:103
static void setFlag(const DebugMask mask)
Definition debug.cpp:117
The common base class of all entity definitions found in the sources.
Definition definition.h:77
virtual DString briefDescription(bool abbreviate=false) const =0
virtual DString inbodyFile() const =0
virtual int getEndBodyLine() const =0
virtual DString briefFile() const =0
virtual SrcLangExt getLanguage() const =0
Returns the programming language this definition was written in.
virtual int docLine() const =0
virtual DString getDefFileName() const =0
virtual DString documentation() const =0
virtual bool isLinkable() const =0
virtual DString inbodyDocumentation() const =0
virtual int getDefLine() const =0
virtual DefType definitionType() const =0
virtual const DString & name() const =0
virtual const DString & localName() const =0
virtual int inbodyLine() const =0
virtual const FileDef * getBodyDef() const =0
virtual int briefLine() const =0
virtual bool hasDocumentation() const =0
virtual bool isLinkableInProject() const =0
virtual size_t getDefColumn() const =0
virtual bool isAnonymous() const =0
virtual DString displayName(bool includeScope=true) const =0
virtual DString qualifiedName() const =0
virtual bool isHidden() const =0
virtual DString anchor() const =0
virtual int getStartDefLine() const =0
virtual const GroupList & partOfGroups() const =0
virtual bool isArtificial() const =0
virtual Definition * getOuterScope() const =0
virtual DString docFile() const =0
virtual int getStartBodyLine() const =0
virtual bool isReference() const =0
virtual const Definition * findInnerCompound(const DString &name) const =0
virtual DString getOutputFileBase() const =0
virtual void mergeReferencedBy(const Definition *other)=0
virtual void setName(const DString &name)=0
virtual void setExported(bool b)=0
virtual void setBodySegment(int defLine, int bls, int ble)=0
virtual void setHidden(bool b)=0
virtual void setReference(const DString &r)=0
virtual void mergeReferences(const Definition *other)=0
virtual void setInbodyDocumentation(const DString &d, const DString &docFile, int docLine)=0
virtual void setDocumentation(const DString &d, const DString &docFile, int docLine, bool stripWhiteSpace=true)=0
virtual void addInnerCompound(Definition *d)=0
virtual void setDefFile(const DString &df, int defLine, size_t defColumn)=0
virtual void addSectionsToDefinition(const std::vector< const SectionInfo * > &anchorList)=0
virtual void setLanguage(SrcLangExt lang)=0
virtual void setOuterScope(Definition *d)=0
virtual void setArtificial(bool b)=0
virtual void setBriefDescription(const DString &b, const DString &briefFile, int briefLine)=0
virtual void makePartOfGroup(GroupDef *gd)=0
virtual void setBodyDef(const FileDef *fd)=0
virtual void setRequirementReferences(const RequirementRefs &rqli)=0
virtual void setId(const DString &name)=0
virtual void setRefItems(const RefItemVector &sli)=0
virtual void computeTooltip()=0
A model of a directory symbol.
Definition dirdef.h:108
virtual void overrideDirectoryGraph(bool e)=0
Class representing a directory in the file system.
Definition dir.h:73
static std::string currentDirPath()
Definition dir.cpp:348
std::string absPath() const
Definition dir.cpp:370
bool mkdir(const std::string &path, bool acceptsAbsPath=true) const
Definition dir.cpp:301
void setPath(const std::string &path)
Definition dir.cpp:235
bool remove(const std::string &path, bool acceptsAbsPath=true) const
Definition dir.cpp:320
DirIterator iterator() const
Definition dir.cpp:245
static std::string cleanDirPath(const std::string &path)
Definition dir.cpp:363
static bool setCurrent(const std::string &path)
Definition dir.cpp:356
bool exists() const
Definition dir.cpp:263
A linked map of directories.
Definition dirdef.h:173
A class that generates docset files.
Definition docsets.h:35
static void init()
bool run()
Definition dot.cpp:119
static DotManager * instance()
Definition dot.cpp:78
static NamespaceLinkedMap * namespaceLinkedMap
Definition doxygen.h:108
static ConceptLinkedMap * conceptLinkedMap
Definition doxygen.h:90
static bool suppressDocWarnings
Definition doxygen.h:123
static FileNameLinkedMap * plantUmlFileNameLinkedMap
Definition doxygen.h:102
static bool parseSourcesNeeded
Definition doxygen.h:116
static StringUnorderedSet inputPaths
Definition doxygen.h:96
static std::unique_ptr< PageDef > mainPage
Definition doxygen.h:93
static bool clangAssistedParsing
Definition doxygen.h:129
static StringUnorderedSet expandAsDefinedSet
Definition doxygen.h:112
static FileNameLinkedMap * inputNameLinkedMap
Definition doxygen.h:97
static DString filterDBFileName
Definition doxygen.h:124
static ParserManager * parserManager
Definition doxygen.h:122
static InputFileEncodingList inputFileEncodingList
Definition doxygen.h:131
static DString verifiedDotPath
Definition doxygen.h:130
static ClassLinkedMap * classLinkedMap
Definition doxygen.h:88
static MemberNameLinkedMap * functionNameLinkedMap
Definition doxygen.h:105
static PageLinkedMap * exampleLinkedMap
Definition doxygen.h:91
static FileNameLinkedMap * dotFileNameLinkedMap
Definition doxygen.h:99
static NamespaceDefMutable * globalScope
Definition doxygen.h:114
static FileNameLinkedMap * imageNameLinkedMap
Definition doxygen.h:98
static FileNameLinkedMap * mscFileNameLinkedMap
Definition doxygen.h:100
static FileNameLinkedMap * mermaidFileNameLinkedMap
Definition doxygen.h:103
static MemberGroupInfoMap memberGroupInfoMap
Definition doxygen.h:111
static IndexList * indexList
Definition doxygen.h:125
static StaticInitMap staticInitMap
Definition doxygen.h:134
static StringMap tagDestinationMap
Definition doxygen.h:109
static std::mutex countFlowKeywordsMutex
Definition doxygen.h:132
static ClassLinkedMap * hiddenClassLinkedMap
Definition doxygen.h:89
static FileNameLinkedMap * diaFileNameLinkedMap
Definition doxygen.h:101
static DString spaces
Definition doxygen.h:126
static PageLinkedMap * pageLinkedMap
Definition doxygen.h:92
static bool generatingXmlOutput
Definition doxygen.h:127
static std::unique_ptr< NamespaceDef > globalNamespaceDef
Definition doxygen.h:113
static DString htmlFileExtension
Definition doxygen.h:115
static DefinesPerFileList macroDefinitions
Definition doxygen.h:128
static DirLinkedMap * dirLinkedMap
Definition doxygen.h:120
static NamespaceAliasInfoMap namespaceAliasMap
Definition doxygen.h:106
static MemberNameLinkedMap * memberNameLinkedMap
Definition doxygen.h:104
static SymbolMap< Definition > * symbolMap
Definition doxygen.h:118
static StringUnorderedSet tagFileSet
Definition doxygen.h:110
static FileNameLinkedMap * includeNameLinkedMap
Definition doxygen.h:94
static FileNameLinkedMap * exampleNameLinkedMap
Definition doxygen.h:95
static SearchIndexIntf searchIndex
Definition doxygen.h:117
static DirRelationLinkedMap dirRelations
Definition doxygen.h:121
static std::mutex addExampleMutex
Definition doxygen.h:133
static ClangUsrMap * clangUsrMap
Definition doxygen.h:119
static GroupLinkedMap * groupLinkedMap
Definition doxygen.h:107
Generator for Eclipse help files.
Definition eclipsehelp.h:42
static EmojiEntityMapper & instance()
Returns the one and only instance of the Emoji entity mapper.
Definition emoji.cpp:1981
void writeEmojiFile(TextStream &t)
Writes the list of supported emojis to the given file.
Definition emoji.cpp:2002
Represents an unstructured piece of information, about an entity found in the sources.
Definition entry.h:115
TextStream initializer
initial value (for variables)
Definition entry.h:197
VhdlSpecifier vhdlSpec
VHDL specifiers.
Definition entry.h:182
bool subGrouping
automatically group class members?
Definition entry.h:187
RequirementRefs rqli
references to requirements
Definition entry.h:227
const std::vector< std::shared_ptr< Entry > > & children() const
Definition entry.h:138
bool proto
prototype ?
Definition entry.h:186
GroupDocType groupDocType
Definition entry.h:231
int docLine
line number at which the documentation was found
Definition entry.h:201
DString includeName
include name (3 arg of \class)
Definition entry.h:199
DString bitfields
member's bit fields
Definition entry.h:192
ArgumentList typeConstr
where clause (C#) for type constraints
Definition entry.h:215
void markAsProcessed() const
Definition entry.h:166
int endBodyLine
line number where the definition ends
Definition entry.h:218
DString write
property write accessor
Definition entry.h:212
bool exported
is the symbol exported from a C++20 module
Definition entry.h:188
const TagInfo * tagInfo() const
Definition entry.h:176
DString includeFile
include file (2 arg of \class, must be unique)
Definition entry.h:198
DString fileName
file this entry was extracted from
Definition entry.h:223
ArgumentLists tArgLists
template argument declarations
Definition entry.h:195
DString docFile
file in which the documentation was found
Definition entry.h:202
LocalToc localToc
Definition entry.h:233
MethodTypes mtype
signal, slot, (dcop) method, or property?
Definition entry.h:180
@ GROUPDOC_NORMAL
defgroup
Definition entry.h:120
DString args
member argument string
Definition entry.h:191
SrcLangExt lang
programming language in which this entry was found
Definition entry.h:228
Entry * parent() const
Definition entry.h:133
DString inside
name of the class in which documents are found
Definition entry.h:213
DString doc
documentation block (partly parsed)
Definition entry.h:200
DString req
C++20 requires clause.
Definition entry.h:235
bool explicitExternal
explicitly defined as external?
Definition entry.h:185
DString brief
brief description (doc block)
Definition entry.h:203
std::vector< const SectionInfo * > anchors
list of anchors defined in this entry
Definition entry.h:222
RelatesType relatesType
how relates is handled
Definition entry.h:210
std::vector< Grouping > groups
list of groups this entry belongs to
Definition entry.h:221
CommandOverrides commandOverrides
store info for commands whose default can be overridden
Definition entry.h:189
int startLine
start line of entry in the source
Definition entry.h:224
size_t startColumn
start column of entry in the source
Definition entry.h:225
ArgumentList argList
member arguments as a list
Definition entry.h:194
DString type
member type
Definition entry.h:172
int inbodyLine
line number at which the body doc was found
Definition entry.h:207
EntryType section
entry type (see Sections);
Definition entry.h:171
int bodyLine
line number of the body in the source
Definition entry.h:216
DString exception
throw specification
Definition entry.h:214
DString relates
related class (doc block)
Definition entry.h:209
int mGrpId
member group id
Definition entry.h:219
std::vector< BaseInfo > extends
list of base classes
Definition entry.h:220
DString inbodyFile
file in which the body doc was found
Definition entry.h:208
Specifier virt
virtualness of the entry
Definition entry.h:190
DString metaData
Slice metadata.
Definition entry.h:234
std::vector< std::string > qualifiers
qualifiers specified with the qualifier command
Definition entry.h:236
DString name
member name
Definition entry.h:173
RefItemVector sli
special lists (test/todo/bug/deprecated/..) this entry is in
Definition entry.h:226
Protection protection
class protection
Definition entry.h:179
bool artificial
Artificially introduced item.
Definition entry.h:230
bool hidden
does this represent an entity that is hidden from the output
Definition entry.h:229
DString inbodyDocs
documentation inside the body of a function
Definition entry.h:206
int briefLine
line number at which the brief desc. was found
Definition entry.h:204
FileDef * fileDef() const
Definition entry.h:168
DString id
libclang id
Definition entry.h:232
int initLines
define/variable initializer lines to show
Definition entry.h:183
bool isStatic
static ?
Definition entry.h:184
TypeSpecifier spec
class/member specifiers
Definition entry.h:181
DString briefFile
file in which the brief desc. was found
Definition entry.h:205
DString read
property read accessor
Definition entry.h:211
Wrapper class for the Entry type.
Definition types.h:856
constexpr bool isCompoundDoc() const noexcept
Definition types.h:866
constexpr bool isFile() const noexcept
Definition types.h:865
constexpr bool isDoc() const noexcept
Definition types.h:867
ENTRY_TYPES constexpr bool isCompound() const noexcept
Definition types.h:863
std::string to_string() const
Definition types.h:868
static EntryType guessSection(const DString &name)
Definition types.cpp:22
constexpr bool isScope() const noexcept
Definition types.h:864
A class that generates a dynamic tree view side panel.
Definition ftvhelp.h:41
A model of a file symbol.
Definition filedef.h:97
virtual void addUsingDeclaration(const Definition *d)=0
virtual void removeMember(MemberDef *md)=0
virtual void insertClass(ClassDef *cd)=0
virtual void setDiskName(const DString &name)=0
virtual void insertConcept(ConceptDef *cd)=0
virtual void overrideIncludeGraph(bool e)=0
virtual void writeSourceHeader(OutputList &ol)=0
virtual bool generateSourceFile() const =0
virtual DString absFilePath() const =0
virtual const DString & docName() const =0
virtual const LinkedRefMap< NamespaceDef > & getUsedNamespaces() const =0
virtual bool isSource() const =0
virtual void parseSource(ClangTUParser *clangParser)=0
virtual void getAllIncludeFilesRecursively(StringVector &incFiles) const =0
virtual void writeSourceFooter(OutputList &ol)=0
virtual void writeSourceBody(OutputList &ol, ClangTUParser *clangParser)=0
virtual void addUsingDirective(NamespaceDef *nd)=0
virtual void overrideIncludedByGraph(bool e)=0
virtual void insertMember(MemberDef *md)=0
virtual void insertNamespace(NamespaceDef *nd)=0
Minimal replacement for QFileInfo.
Definition fileinfo.h:26
std::string readLink() const
Definition fileinfo.cpp:88
bool isRelative() const
Definition fileinfo.cpp:62
bool isSymLink() const
Definition fileinfo.cpp:81
bool exists() const
Definition fileinfo.cpp:34
bool match(const PatternList &patList, bool caseSenseNames, PatternElem *elem=nullptr, PatternGet getter=[](const std::string &s){ return s;}) const
Match the file name against a list of patterns.
Definition fileinfo.h:60
std::string fileName() const
Definition fileinfo.cpp:122
bool isReadable() const
Definition fileinfo.cpp:48
bool isDir() const
Definition fileinfo.cpp:74
bool isFile() const
Definition fileinfo.cpp:67
std::string dirPath(bool absPath=true) const
Definition fileinfo.cpp:141
std::string absFilePath() const
Definition fileinfo.cpp:105
Class representing all files with a certain base name.
Definition filename.h:31
Ordered dictionary of FileName objects.
Definition filename.h:70
DString showFileDefMatches(const DString &n) const
Returns a list of file definitions in fnMap that match the file name n.
Definition filename.cpp:131
FileDef * findFileDef(const DString &n, bool &ambig) const
Returns the file definition in fnMap that matches the file name n.
Definition filename.cpp:38
bool hasFormulas() const
Definition formula.cpp:720
void initFromRepository(const DString &dir)
Definition formula.cpp:60
void checkRepositories()
Definition formula.cpp:173
static FormulaManager & instance()
Definition formula.cpp:54
void generateImages(const DString &outputDir, bool toIndex, Format format, HighDPI hd=HighDPI::Off)
Definition formula.cpp:635
A model of a group of symbols.
Definition groupdef.h:48
virtual DString groupTitle() const =0
virtual void overrideGroupGraph(bool e)=0
virtual bool addClass(ClassDef *def)=0
virtual bool containsFile(const FileDef *def) const =0
virtual bool addNamespace(NamespaceDef *def)=0
virtual void setGroupScope(Definition *d)=0
virtual void addFile(FileDef *def)=0
virtual void setGroupTitle(const DString &newtitle)=0
virtual MemberList * getMemberList(MemberListType lt) const =0
virtual bool hasGroupTitle() const =0
Generator for HTML output.
Definition htmlgen.h:93
static void writeSearchData(const DString &dir)
Definition htmlgen.cpp:1455
static void init()
Definition htmlgen.cpp:1275
static void writeSearchPage()
Definition htmlgen.cpp:3272
static void writeHeaderFile(TextStream &t, const DString &cssname)
Definition htmlgen.cpp:1623
static void writeFooterFile(TextStream &t)
Definition htmlgen.cpp:1629
static void writeTabData()
Additional initialization after indices have been created.
Definition htmlgen.cpp:1439
static void writeExternalSearchPage()
Definition htmlgen.cpp:3371
static void writeStyleSheetFile(TextStream &t)
Definition htmlgen.cpp:1617
A class that generated the HTML Help specific files.
Definition htmlhelp.h:37
static const DString hhpFileName
Definition htmlhelp.h:90
static Index & instance()
Definition index.cpp:110
void countDataStructures()
Definition index.cpp:266
A list of index interfaces.
Definition indexlist.h:65
void initialize()
Definition indexlist.h:101
void addIndexItem(const Definition *context, const MemberDef *md, const DString &sectionAnchor=DString(), const DString &title=DString())
Definition indexlist.h:118
void addIndex(As &&... args)
Add an index generator to the list, using a syntax similar to std::make_unique<T>().
Definition indexlist.h:98
void addImageFile(const DString &name)
Definition indexlist.h:124
void finalize()
Definition indexlist.h:104
Generator for LaTeX output.
Definition latexgen.h:93
static void writeFooterFile(TextStream &t)
Definition latexgen.cpp:698
static void writeStyleSheetFile(TextStream &t)
Definition latexgen.cpp:704
static void writeHeaderFile(TextStream &t)
Definition latexgen.cpp:692
static void init()
Definition latexgen.cpp:634
void parse(const DString &fileName, const char *data=nullptr)
Parses a user provided layout.
Definition layout.cpp:1470
static LayoutDocManager & instance()
Returns a reference to this singleton.
Definition layout.cpp:1437
void clear()
Definition linkedmap.h:212
std::unique_ptr< RefList > Ptr
Definition linkedmap.h:38
size_t size() const
Definition linkedmap.h:210
T * add(const char *k, Args &&... args)
Definition linkedmap.h:90
const T * find(const std::string &key) const
Definition linkedmap.h:47
Container class representing a vector of objects with keys.
Definition linkedmap.h:232
const T * find(const std::string &key) const
Definition linkedmap.h:243
bool empty() const
Definition linkedmap.h:374
Generator for Man page output.
Definition mangen.h:69
static void init()
Definition mangen.cpp:275
A model of a class/file/namespace member symbol.
Definition memberdef.h:45
virtual DString requiresClause() const =0
virtual bool isFriend() const =0
virtual DString argsString() const =0
virtual bool isForeign() const =0
virtual bool isRelated() const =0
virtual DString definition() const =0
virtual const ClassDef * getCachedTypedefVal() const =0
virtual const ClassDef * getClassDef() const =0
virtual DString excpString() const =0
virtual const ArgumentList & templateArguments() const =0
virtual const DString & initializer() const =0
virtual GroupDef * getGroupDef()=0
virtual bool isCSharpProperty() const =0
virtual bool isTypedef() const =0
virtual const MemberVector & enumFieldList() const =0
virtual void moveTo(Definition *)=0
virtual const FileDef * getFileDef() const =0
virtual const ArgumentList & argumentList() const =0
virtual bool isStrongEnumValue() const =0
virtual VhdlSpecifier getVhdlSpecifiers() const =0
virtual bool isFunction() const =0
virtual bool isExternal() const =0
virtual int getMemberGroupId() const =0
virtual DString bitfieldString() const =0
virtual bool isStatic() const =0
virtual const MemberDef * reimplements() const =0
virtual StringVector getQualifiers() const =0
virtual bool isTypedefValCached() const =0
virtual bool isDocsForDefinition() const =0
virtual bool isDefine() const =0
virtual const NamespaceDef * getNamespaceDef() const =0
virtual bool isObjCProperty() const =0
virtual Protection protection() const =0
virtual TypeSpecifier getMemberSpecifiers() const =0
virtual bool isEnumerate() const =0
virtual MemberType memberType() const =0
virtual ClassDef * relatedAlso() const =0
virtual bool isVariable() const =0
virtual bool isStrong() const =0
virtual DString typeString() const =0
virtual Specifier virtualness(int count=0) const =0
virtual int redefineCount() const =0
virtual int initializerLines() const =0
virtual const MemberDef * getEnumScope() const =0
virtual bool isEnumValue() const =0
virtual bool isPrototype() const =0
virtual void setRequiresClause(const DString &req)=0
virtual void setClassDefOfAnonymousType(const ClassDef *cd)=0
virtual void setMemberClass(ClassDef *cd)=0
virtual void setProtection(Protection p)=0
virtual void setDefinition(const DString &d)=0
virtual void setMemberGroupId(int id)=0
virtual void setDocumentedEnumValues(bool value)=0
virtual void setMemberSpecifiers(TypeSpecifier s)=0
virtual ClassDefMutable * getClassDefMutable()=0
virtual void setArgsString(const DString &as)=0
virtual void setEnumScope(MemberDef *md, bool livesInsideEnum=false)=0
virtual void setDefinitionTemplateParameterLists(const ArgumentLists &lists)=0
virtual void invalidateTypedefValCache()=0
virtual void setVhdlSpecifiers(VhdlSpecifier s)=0
virtual void setEnumClassScope(ClassDef *cd)=0
virtual void setInitializer(const DString &i)=0
virtual void setMaxInitLines(int lines)=0
virtual void setInheritsDocsFrom(const MemberDef *md)=0
virtual void setPrototype(bool p, const DString &df, int line, size_t column)=0
virtual void setRelatedAlso(ClassDef *cd)=0
virtual void overrideReferencesRelation(bool e)=0
virtual void makeForeign()=0
virtual void overrideReferencedByRelation(bool e)=0
virtual void setDocsForDefinition(bool b)=0
virtual void overrideCallGraph(bool e)=0
virtual void overrideInlineSource(bool e)=0
virtual void setBitfields(const DString &s)=0
virtual void copyArgumentNames(const MemberDef *bmd)=0
virtual void overrideEnumValues(bool e)=0
virtual void mergeMemberSpecifiers(TypeSpecifier s)=0
virtual void addQualifiers(const StringVector &qualifiers)=0
virtual void setExplicitExternal(bool b, const DString &df, int line, size_t column)=0
virtual void insertEnumField(MemberDef *md)=0
virtual void moveDeclArgumentList(std::unique_ptr< ArgumentList > al)=0
virtual void setDeclFile(const DString &df, int line, size_t column)=0
virtual void setAnonymousEnumType(const MemberDef *md)=0
virtual void overrideCallerGraph(bool e)=0
virtual void setReimplements(MemberDef *md)=0
virtual void invalidateCachedArgumentTypes()=0
virtual void moveArgumentList(std::unique_ptr< ArgumentList > al)=0
virtual void makeRelated()=0
virtual void insertReimplementedBy(MemberDef *md)=0
A list of MemberDef objects as shown in documentation sections.
Definition memberlist.h:126
Wrapper class for the MemberListType type.
Definition types.h:346
Ptr & front()
Definition membername.h:49
size_t size() const
Definition membername.h:46
iterator begin()
Definition membername.h:35
iterator end()
Definition membername.h:36
void push_back(Ptr &&p)
Definition membername.h:52
Ordered dictionary of MemberName objects.
Definition membername.h:61
MemberName::Ptr take(const DString &key, const MemberDef *value)
Definition membername.h:63
const MemberDef * findRev(const DString &name) const
Definition memberlist.h:107
const MemberDef * find(const DString &name) const
Definition memberlist.h:94
void run()
Run mmdc tool for all collected diagrams.
Definition mermaid.cpp:255
static MermaidManager & instance()
Definition mermaid.cpp:38
void sortMemberLists()
static ModuleManager & instance()
void addDocs(const Entry *root)
void addConceptToModule(const Entry *root, ConceptDef *cd)
void addClassToModule(const Entry *root, ClassDef *cd)
void addMemberToModule(const Entry *root, MemberDef *md)
void writeDocumentation(OutputList &ol)
void addMembersToMemberGroup()
void findSectionsInDocumentation()
void distributeMemberGroupDocumentation()
An abstract interface of a namespace symbol.
virtual const LinkedRefMap< NamespaceDef > & getUsedNamespaces() const =0
virtual bool isInline() const =0
virtual void insertUsedFile(FileDef *fd)=0
virtual void findSectionsInDocumentation()=0
virtual void countMembers()=0
virtual void addUsingDirective(NamespaceDef *nd)=0
virtual void setFileName(const DString &fn)=0
virtual void insertMember(MemberDef *md)=0
virtual void setMetaData(const DString &m)=0
virtual void addUsingDeclaration(const Definition *d)=0
virtual void distributeMemberGroupDocumentation()=0
virtual void writeTagFile(TextStream &)=0
virtual void writeDocumentation(OutputList &ol)=0
virtual void setInline(bool isInline)=0
virtual void computeAnchors()=0
virtual void combineUsingRelations(NamespaceDefSet &visitedNamespace)=0
virtual void sortMemberLists()=0
virtual void addMembersToMemberGroup()=0
/dev/null outline parser
void parseInput(const DString &, const char *, const std::shared_ptr< Entry > &, ClangTUParser *) override
Parses a single input file with the goal to build an Entry tree.
void parsePrototype(const DString &) override
Callback function called by the comment block scanner.
bool needsPreprocessing(const DString &) const override
Returns true if the language identified by extension needs the C preprocessor to be run before feed t...
Abstract interface for outline parsers.
Definition parserintf.h:41
virtual bool needsPreprocessing(const DString &extension) const =0
Returns true if the language identified by extension needs the C preprocessor to be run before feed t...
virtual void parseInput(const DString &fileName, const char *fileBuf, const std::shared_ptr< Entry > &root, ClangTUParser *clangParser)=0
Parses a single input file with the goal to build an Entry tree.
Class representing a list of output generators that are written to in parallel.
Definition outputlist.h:311
void disable(OutputType o)
void add()
Definition outputlist.h:348
void enable(OutputType o)
size_t size() const
Definition outputlist.h:357
void docify(const DString &s)
Definition outputlist.h:433
void writeStyleInfo(int part)
Definition outputlist.h:391
void generateDoc(const DString &fileName, int startLine, const Definition *ctx, const MemberDef *md, const DString &docStr, const DocOptions &options)
void cleanup()
Definition outputlist.h:748
void startContents()
Definition outputlist.h:614
A model of a page symbol.
Definition pagedef.h:27
virtual void setLocalToc(const LocalToc &tl)=0
virtual void setFileName(const DString &name)=0
virtual void setShowLineNo(bool)=0
virtual void setPageScope(Definition *)=0
virtual const GroupDef * getGroupDef() const =0
Manages programming language parsers.
Definition parserintf.h:182
std::unique_ptr< CodeParserInterface > getCodeParser(const DString &extension)
Gets the interface to the parser associated with a given extension.
Definition parserintf.h:253
void registerParser(const DString &name, const OutlineParserFactory &outlineParserFactory, const CodeParserFactory &codeParserFactory)
Registers an additional parser.
Definition parserintf.h:215
std::unique_ptr< OutlineParserInterface > getOutlineParser(const DString &extension)
Gets the interface to the parser associated with a given extension.
Definition parserintf.h:244
static PlantumlManager & instance()
Definition plantuml.cpp:236
void run()
Run plant UML tool for all images.
Definition plantuml.cpp:432
void processFile(const DString &fileName, const std::string &input, std::string &output)
Definition pre.l:4302
void addSearchDir(const DString &dir)
Definition pre.l:4284
Definition qhp.h:29
static DString getQchFileName()
Definition qhp.cpp:430
static const DString qhpFileName
Definition qhp.h:49
Generator for RTF output.
Definition rtfgen.h:80
static void init()
Definition rtfgen.cpp:464
static bool preProcessFileInplace(const DString &path, const DString &name)
This is an API to a VERY brittle RTF preprocessor that combines nested RTF files.
Definition rtfgen.cpp:2467
static void writeStyleSheetFile(TextStream &t)
Definition rtfgen.cpp:397
static void writeExtensionsFile(TextStream &t)
Definition rtfgen.cpp:412
static RefListManager & instance()
Definition reflist.h:120
static RequirementManager & instance()
void writeTagFile(TextStream &tagFile)
void addRequirement(Entry *e)
Abstract proxy interface for non-javascript based search indices.
void addWord(const DString &word, bool hiPriority)
void setCurrentDoc(const Definition *ctx, const DString &anchor, bool isSourceFile)
void write(const DString &file)
class that provide information about a section.
Definition section.h:58
DString fileName() const
Definition section.h:74
int lineNr() const
Definition section.h:73
DString ref() const
Definition section.h:72
SectionInfo * replace(const DString &label, const DString &fileName, int lineNr, const DString &title, SectionType type, int level, const DString &ref=DString())
Definition section.h:157
SectionInfo * add(const SectionInfo &si)
Definition section.h:139
static SectionManager & instance()
returns a reference to the singleton
Definition section.h:179
static constexpr int Page
Definition section.h:31
std::vector< stat > stats
Definition doxygen.cpp:273
void print()
Definition doxygen.cpp:250
void begin(const char *name)
Definition doxygen.cpp:237
void end()
Definition doxygen.cpp:243
std::chrono::steady_clock::time_point startTime
Definition doxygen.cpp:274
static void showCacheUsage()
Show usage of the type lookup cache.
static void clearTypeLookupCache(ClearScope scope)
ClassDefMutable * resolveClassMutable(const Definition *scope, const DString &name, bool mayBeUnlinkable=false, bool mayBeHidden=false)
Wrapper around resolveClass that returns a mutable interface to the class object or a nullptr if the ...
const ClassDef * resolveClass(const Definition *scope, const DString &name, bool maybeUnlinkable=false, bool mayBeHidden=false)
Find the class definition matching name within the scope set.
const Definition * resolveSymbol(const Definition *scope, const DString &name, const DString &args=DString(), bool checkCV=false, bool insideCode=false, bool onlyLinkable=false)
Find the symbool definition matching name within the scope set.
DString getTemplateSpec() const
In case a call to resolveClass() points to a template specialization, the template part is return via...
const MemberDef * getTypedef() const
In case a call to resolveClass() resolves to a type member (e.g. an enum) this method will return it.
This struct is used to capture the tag file information for an Entry.
Definition entry.h:101
DString tagName
Definition entry.h:103
DString fileName
Definition entry.h:104
Text streaming class that buffers data.
Definition textstream.h:36
std::string str() const
Return the contents of the buffer as a std::string object.
Definition textstream.h:232
Class managing a pool of worker threads.
Definition threadpool.h:48
auto queue(F &&f, Args &&... args) -> std::future< decltype(f(args...))>
Queue the callable function f for the threads to execute.
Definition threadpool.h:77
virtual DString trOverloadText()=0
virtual DString trPage(bool first_capital, bool singular)=0
virtual DString trMainPage()=0
Wrapper class for a number of boolean properties.
Definition types.h:694
std::string to_string() const
Definition types.h:738
static void correctMemberProperties(MemberDefMutable *md)
static void computeVhdlComponentRelations()
ClassDefMutable * toClassDefMutable(Definition *d)
ClassDef * getClass(const DString &n)
std::unique_ptr< ClassDef > createClassDefAlias(const Definition *newScope, const ClassDef *cd)
Definition classdef.cpp:803
std::unique_ptr< ClassDef > createClassDef(const DString &fileName, int startLine, size_t startColumn, const DString &name, ClassDef::CompoundType ct, const DString &ref, const DString &fName, bool isSymbol, bool isJavaEnum)
Factory method to create a new ClassDef object.
Definition classdef.cpp:577
ClassDef * toClassDef(Definition *d)
std::unordered_set< const ClassDef * > ClassDefSet
Definition classdef.h:91
std::map< std::string, int > TemplateNameMap
Definition classdef.h:89
ClassDefMutable * getClassMutable(const DString &key)
Definition classdef.h:461
Class representing a regular expression.
Definition regex.h:39
Class to iterate through matches.
Definition regex.h:239
Object representing the matching results.
Definition regex.h:154
First pass comment processing.
void convertCppComments(const std::string &inBuf, std::string &outBuf, const std::string &fn)
Converts the comments in a file.
ConceptDefMutable * toConceptDefMutable(Definition *d)
std::unique_ptr< ConceptDef > createConceptDef(const DString &fileName, int startLine, size_t startColumn, const DString &name, const DString &tagRef, const DString &tagFile)
ConceptDefMutable * getConceptMutable(const DString &key)
Definition conceptdef.h:106
#define Config_getInt(name)
Definition config.h:34
#define Config_getList(name)
Definition config.h:38
#define Config_updateString(name, value)
Definition config.h:39
#define Config_updateBool(name, value)
Definition config.h:40
#define Config_getBool(name)
Definition config.h:33
#define Config_getString(name)
Definition config.h:32
#define Config_updateList(name,...)
Definition config.h:43
#define Config_getEnum(name)
Definition config.h:35
std::set< std::string > StringSet
Definition containers.h:31
std::unordered_set< std::string > StringUnorderedSet
Definition containers.h:29
std::map< std::string, std::string > StringMap
Definition containers.h:30
std::vector< std::string > StringVector
Definition containers.h:33
void parseFuncDecl(const DString &decl, const SrcLangExt lang, DString &clName, DString &type, DString &name, DString &args, DString &funcTempList, DString &exceptions)
Definition declinfo.l:318
std::unique_ptr< ArgumentList > stringToArgumentList(SrcLangExt lang, const DString &argsString, DString *extraTypeChars=nullptr)
Definition defargs.l:821
void generateDEF()
Definition defgen.cpp:463
std::unordered_map< std::string, DefineList > DefinesPerFileList
Definition define.h:50
Definition * toDefinition(DefinitionMutable *dm)
DefinitionMutable * toDefinitionMutable(Definition *d)
DirIterator begin(DirIterator it) noexcept
Definition dir.cpp:176
DirIterator end(const DirIterator &) noexcept
Definition dir.cpp:181
void buildDirectories()
Definition dirdef.cpp:1098
void computeDirDependencies()
Definition dirdef.cpp:1172
void generateDirDocs(OutputList &ol)
Definition dirdef.cpp:1189
#define AUTO_TRACE_ADD(...)
Definition docnode.cpp:54
#define AUTO_TRACE(...)
Definition docnode.cpp:53
#define AUTO_TRACE_EXIT(...)
Definition docnode.cpp:55
constexpr DocNodeVariant * parent(DocNodeVariant *n)
returns the parent node of a given node n or nullptr if the node has no parent.
Definition docnode.h:1335
static void findInheritedTemplateInstances()
Definition doxygen.cpp:5381
void printNavTree(Entry *root, int indent)
static void addClassToContext(const Entry *root)
Definition doxygen.cpp:942
static void makeTemplateInstanceRelation(const Entry *root, ClassDefMutable *cd)
Definition doxygen.cpp:5396
static StringUnorderedSet g_pathsVisited(1009)
static void buildGroupList(const Entry *root)
Definition doxygen.cpp:435
static void insertMemberAlias(Definition *outerScope, const MemberDef *md)
Definition doxygen.cpp:6765
static void findUsingDeclarations(const Entry *root, bool filterPythonPackages)
Definition doxygen.cpp:2174
static void flushCachedTemplateRelations()
Definition doxygen.cpp:9509
static void copyLatexStyleSheet()
static void generateDocsForClassList(const std::vector< ClassDefMutable * > &classList)
Definition doxygen.cpp:9169
static int findFunctionPtr(const std::string &type, SrcLangExt lang, int *pLength=nullptr)
Definition doxygen.cpp:3016
static bool isSpecialization(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists)
Definition doxygen.cpp:6066
static void computeTemplateClassRelations()
Definition doxygen.cpp:5475
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:8224
static void runQHelpGenerator()
static void addConceptToContext(const Entry *root)
Definition doxygen.cpp:1167
static void addRelatedPage(Entry *root)
Definition doxygen.cpp:333
void initDoxygen()
static void addIncludeFile(DefMutable *cd, FileDef *ifd, const Entry *root)
Definition doxygen.cpp:592
static StringVector g_inputFiles
Definition doxygen.cpp:191
void printSectionsTree()
class Statistics g_s
static void generateXRefPages()
Definition doxygen.cpp:5654
static Definition * buildScopeFromQualifiedName(const DString &name_, SrcLangExt lang, const TagInfo *tagInfo)
Definition doxygen.cpp:716
static void findUsingDeclImports(const Entry *root)
Definition doxygen.cpp:2327
static void copyStyleSheet()
FindBaseClassRelation_Mode
Definition doxygen.cpp:290
@ Undocumented
Definition doxygen.cpp:293
@ TemplateInstances
Definition doxygen.cpp:291
@ DocumentedOnly
Definition doxygen.cpp:292
void distributeClassGroupRelations()
Definition doxygen.cpp:1498
static void generateGroupDocs()
static void findDirDocumentation(const Entry *root)
Definition doxygen.cpp:9709
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:1691
static bool findClassRelation(const Entry *root, Definition *context, ClassDefMutable *cd, const BaseInfo *bi, const TemplateNameMap &templateNames, FindBaseClassRelation_Mode mode, bool isArtificial)
Definition doxygen.cpp:4982
static void resolveTemplateInstanceInType(const Entry *root, const Definition *scope, const MemberDef *md)
Definition doxygen.cpp:4912
static void organizeSubGroupsFiltered(const Entry *root, bool additional)
Definition doxygen.cpp:475
static void warnUndocumentedNamespaces()
Definition doxygen.cpp:5430
static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments, const std::string &name)
Definition doxygen.cpp:4591
static void buildConceptList(const Entry *root)
Definition doxygen.cpp:1328
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:6634
static void resolveClassNestingRelations()
Definition doxygen.cpp:1383
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:2764
static void findClassEntries(const Entry *root)
Definition doxygen.cpp:5346
static void vhdlCorrectMemberProperties()
Definition doxygen.cpp:8468
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:6278
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:6180
static void copyLogo(const DString &outputOption, bool toIndex)
static void computeMemberReferences()
Definition doxygen.cpp:5544
static void transferRelatedFunctionDocumentation()
Definition doxygen.cpp:4505
static void addMembersToMemberGroup()
Definition doxygen.cpp:9374
static void findMainPageTagFiles(Entry *root)
Definition doxygen.cpp:9878
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:6224
static void distributeConceptGroups()
Definition doxygen.cpp:1350
static NamespaceDef * findUsedNamespace(const LinkedRefMap< NamespaceDef > &unl, const DString &name)
Definition doxygen.cpp:2004
static void transferFunctionDocumentation()
Definition doxygen.cpp:4424
static void setAnonymousEnumType()
Definition doxygen.cpp:9114
static void sortMemberLists()
Definition doxygen.cpp:9019
static void findMember(const Entry *root, const DString &relates, const DString &type, const DString &args, DString funcDecl, bool overloaded, bool isFunc)
Definition doxygen.cpp:6808
static void createTemplateInstanceMembers()
Definition doxygen.cpp:8596
void transferStaticInstanceInitializers()
Definition doxygen.cpp:4554
static void findObjCMethodDefinitions(const Entry *root)
Definition doxygen.cpp:7613
static void addMemberDocs(const Entry *root, MemberDefMutable *md, const DString &funcDecl, const ArgumentList *al, bool over_load, TypeSpecifier spec)
Definition doxygen.cpp:5668
static void dumpSymbolMap()
static void buildTypedefList(const Entry *root)
Definition doxygen.cpp:3493
static void findGroupScope(const Entry *root)
Definition doxygen.cpp:450
static void generateFileDocs()
Definition doxygen.cpp:8832
static int findEndOfTemplate(const DString &s, size_t startPos)
Definition doxygen.cpp:3219
static void findDefineDocumentation(Entry *root)
Definition doxygen.cpp:9622
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:4654
void parseInput()
static void findMemberDocumentation(const Entry *root)
Definition doxygen.cpp:7583
static void distributeMemberGroupDocumentation()
Definition doxygen.cpp:9412
static void generateNamespaceClassDocs(const ClassLinkedRefMap &classList)
static void addEnumValuesToEnums(const Entry *root)
Definition doxygen.cpp:7816
static void generatePageDocs()
static void resolveUserReferences()
Definition doxygen.cpp:9940
static void buildRequirementsList(Entry *root)
Definition doxygen.cpp:9769
static void compareDoxyfile(Config::CompareMode diffList)
static void addPageToContext(PageDef *pd, Entry *root)
Definition doxygen.cpp:314
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:3630
static void copyIcon(const DString &outputOption, bool toIndex)
static void buildSequenceList(const Entry *root)
Definition doxygen.cpp:3593
static void generateFileSources()
Definition doxygen.cpp:8666
static void copyExtraFiles(StringVector files, const DString &filesOption, const DString &outputOption, bool toIndex)
static void generateClassDocs()
Definition doxygen.cpp:9267
static int findTemplateSpecializationPosition(const DString &name)
Definition doxygen.cpp:4952
static void buildNamespaceList(const Entry *root)
Definition doxygen.cpp:1835
static void findIncludedUsingDirectives()
Definition doxygen.cpp:2577
static void addDefineDoc(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:9595
static void countMembers()
Definition doxygen.cpp:9128
void clearAll()
Definition doxygen.cpp:205
static void devUsage()
static ClassDef * findClassWithinClassContext(Definition *context, ClassDef *cd, const DString &name)
Definition doxygen.cpp:4619
static void organizeSubGroups(const Entry *root)
Definition doxygen.cpp:494
static void applyMemberOverrideOptions(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:2266
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:6697
static void findFriends()
Definition doxygen.cpp:4327
static void findEnums(const Entry *root)
Definition doxygen.cpp:7641
static void generateConfigFile(const DString &configFile, bool shortList, bool updateOnly=false)
static DString g_commentFileName
Definition doxygen.cpp:196
static void dumpSymbol(TextStream &t, Definition *d)
static void addClassAndNestedClasses(std::vector< ClassDefMutable * > &list, ClassDefMutable *cd)
Definition doxygen.cpp:9244
static void addEnumDocs(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:8064
static void addListReferences()
Definition doxygen.cpp:5645
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:5858
static bool isClassSection(const Entry *root)
Definition doxygen.cpp:5324
static void buildGroupListFiltered(const Entry *root, bool additional, bool includeExternal)
Definition doxygen.cpp:364
static void runHtmlHelpCompiler()
static void addMembersToIndex()
Definition doxygen.cpp:8258
static bool g_dumpSymbolMap
Definition doxygen.cpp:195
static void version(const bool extended)
static OutputList * g_outputList
Definition doxygen.cpp:192
static DString extractClassName(const Entry *root)
Definition doxygen.cpp:5355
static void findMainPage(Entry *root)
Definition doxygen.cpp:9808
static ClassDef::CompoundType convertToCompoundType(EntryType section, TypeSpecifier specifier)
Definition doxygen.cpp:900
DString stripTemplateSpecifiers(const DString &s)
Definition doxygen.cpp:689
static void findUsingDirectives(const Entry *root)
Definition doxygen.cpp:2017
static void addInterfaceOrServiceToServiceOrSingleton(const Entry *root, ClassDefMutable *cd, DString const &rname)
Definition doxygen.cpp:3662
static bool g_successfulRun
Definition doxygen.cpp:194
static bool tryAddEnumDocsToGroupMember(const Entry *root, const DString &name)
Definition doxygen.cpp:8106
static void addSourceReferences()
Definition doxygen.cpp:8892
static void associateVariableWithAnonymousEnumType(const MemberDef *md, const Container *cd, const MemberDef *enumTypeMember, MemberListType mlFilter)
Definition doxygen.cpp:1532
static void createUsingMemberImportForClass(const Entry *root, ClassDefMutable *cd, const MemberDef *md, const DString &fileName, const DString &memName)
Definition doxygen.cpp:2278
static DString substituteTemplatesInString(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists, const std::string &src)
Definition doxygen.cpp:6096
std::function< std::unique_ptr< T >() > make_parser_factory()
static void buildExampleList(Entry *root)
static void inheritDocumentation()
Definition doxygen.cpp:9314
static void flushUnresolvedRelations()
Definition doxygen.cpp:9551
static bool isSymbolHidden(const Definition *d)
Definition doxygen.cpp:9061
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:4861
static void findDocumentedEnumValues()
Definition doxygen.cpp:8250
static void findTagLessClasses()
Definition doxygen.cpp:1783
static void generateDiskNames()
static void addToIndices()
Definition doxygen.cpp:8300
static void computeClassRelations()
Definition doxygen.cpp:5450
static void buildFunctionList(const Entry *root)
Definition doxygen.cpp:4022
static void checkPageRelations()
Definition doxygen.cpp:9920
static void addGlobalFunction(const Entry *root, const DString &rname, const DString &sc)
Definition doxygen.cpp:3913
static void findModuleDocumentation(const Entry *root)
Definition doxygen.cpp:1318
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:2594
static void readTagFile(const std::shared_ptr< Entry > &root, const DString &tagLine)
static void findEnumDocumentation(const Entry *root)
Definition doxygen.cpp:8139
static void computePageRelations(Entry *root)
Definition doxygen.cpp:9890
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:3777
void initResources()
static bool isVarWithConstructor(const Entry *root)
Definition doxygen.cpp:3076
static StringSet g_usingDeclarations
Definition doxygen.cpp:193
static void buildDictionaryList(const Entry *root)
Definition doxygen.cpp:3611
static bool haveEqualFileNames(const Entry *root, const MemberDef *md)
Definition doxygen.cpp:9584
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:1153
static void buildPageList(Entry *root)
Definition doxygen.cpp:9781
static void writeTagFile()
static void addRequirementReferences()
Definition doxygen.cpp:5637
static void computeVerifiedDotPath()
static bool g_singleComment
Definition doxygen.cpp:197
static void findSectionsInDocumentation()
Definition doxygen.cpp:9450
static void mergeCategories()
Definition doxygen.cpp:8615
static const StringUnorderedSet g_compoundKeywords
Definition doxygen.cpp:202
static bool scopeIsTemplate(const Definition *d)
Definition doxygen.cpp:6082
static void buildFileList(const Entry *root)
Definition doxygen.cpp:506
static void buildClassList(const Entry *root)
Definition doxygen.cpp:1143
static void usage(const DString &name, const DString &versionString)
static void findUsedTemplateInstances()
Definition doxygen.cpp:5414
static void computeTooltipTexts()
Definition doxygen.cpp:9068
static void addVariable(const Entry *root, int isFuncPtr=-1)
Definition doxygen.cpp:3287
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:4810
static void parseFilesSingleThreading(const std::shared_ptr< Entry > &root)
parse the list of input files
static void buildCompleteMemberLists()
Definition doxygen.cpp:8636
static const ClassDef * findClassDefinition(FileDef *fd, NamespaceDef *nd, const DString &scopeName)
Definition doxygen.cpp:5819
static void filterMemberDocumentation(const Entry *root, const DString &relates)
Definition doxygen.cpp:7434
static void generateConceptDocs()
Definition doxygen.cpp:9293
static bool isRecursiveBaseClass(const DString &scope, const DString &name)
Definition doxygen.cpp:4941
static std::unique_ptr< OutlineParserInterface > getParserForFile(const DString &fn)
static void combineUsingRelations()
Definition doxygen.cpp:9349
static const char * getArg(int argc, char **argv, int &optInd)
std::unique_ptr< ArgumentList > getTemplateArgumentsFromName(const DString &name, const ArgumentLists &tArgLists)
Definition doxygen.cpp:870
static ClassDefMutable * createTagLessInstance(const Definition *root, const ClassDef *templ, const DString &fieldName)
Definition doxygen.cpp:1559
static void checkMarkdownMainfile()
static std::unordered_map< std::string, std::vector< ClassDefMutable * > > g_usingClassMap
Definition doxygen.cpp:2325
static void buildConceptDocList(const Entry *root)
Definition doxygen.cpp:1338
static bool isEntryInGroupOfMember(const Entry *root, const MemberDef *md, bool allowNoGroup=false)
Definition doxygen.cpp:5834
static void applyToAllDefinitions(Func func)
Definition doxygen.cpp:5580
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:4457
static void buildDefineList()
Definition doxygen.cpp:8971
static void buildInterfaceAndServiceList(const Entry *root)
Definition doxygen.cpp:3725
static Definition * findScopeFromQualifiedName(NamespaceDefMutable *startScope, const DString &n, FileDef *fileScope, const TagInfo *tagInfo)
Definition doxygen.cpp:785
static void computeMemberRelations()
Definition doxygen.cpp:8580
static void buildListOfUsingDecls(const Entry *root)
Definition doxygen.cpp:2161
static void computeMemberRelationsForBaseClass(const ClassDef *cd, const BaseClassDef *bcd)
Definition doxygen.cpp:8500
static std::multimap< std::string, const Entry * > g_classEntries
Definition doxygen.cpp:190
std::vector< InputFileEncoding > InputFileEncodingList
Definition doxygen.h:73
std::unordered_map< std::string, BodyInfo > StaticInitMap
Definition doxygen.h:77
std::unordered_map< std::string, NamespaceAliasInfo > NamespaceAliasInfoMap
Definition doxygen.h:79
std::unordered_map< std::string, const Definition * > ClangUsrMap
Definition doxygen.h:75
DString substitute(const DString &s, const DString &src, const DString &dst)
substitute all occurrences of src in s by dst
Definition dstring.cpp:485
int dstricmp(const char *s1, const char *s2)
Definition dstring.cpp:444
uint32_t dstrlen(const char *str)
Returns the length of string str, or 0 if a null pointer is passed.
Definition dstring.h:39
int dstricmp_sort(const char *str1, const char *str2)
Definition dstring.h:67
int dstrcmp(const char *str1, const char *str2)
Definition dstring.h:50
const char * qPrint(const char *s)
Definition dstring.h:783
FileDef * toFileDef(Definition *d)
Definition filedef.cpp:1973
std::unique_ptr< FileDef > createFileDef(const DString &p, const DString &n, const DString &ref, const DString &dn)
Definition filedef.cpp:267
std::unordered_set< const FileDef * > FileDefSet
Definition filedef.h:41
void addNamespaceToGroups(const Entry *root, NamespaceDef *nd)
void addGroupToGroups(const Entry *root, GroupDef *subGroup)
void addClassToGroups(const Entry *root, ClassDef *cd)
void addDirToGroups(const Entry *root, DirDef *dd)
std::unique_ptr< GroupDef > createGroupDef(const DString &fileName, int line, const DString &name, const DString &title, const DString &refFileName)
Definition groupdef.cpp:180
void addConceptToGroups(const Entry *root, ConceptDef *cd)
void addMemberToGroups(const Entry *root, MemberDef *md)
void writeGraphInfo(OutputList &ol)
Definition index.cpp:4097
void endTitle(OutputList &ol, const DString &fileName, const DString &name)
Definition index.cpp:398
void writeIndexHierarchy(OutputList &ol)
Definition index.cpp:5811
void endFile(OutputList &ol, bool skipNavIndex, bool skipEndContents, const DString &navPath)
Definition index.cpp:431
void startTitle(OutputList &ol, const DString &fileName, const DefinitionMutable *def)
Definition index.cpp:388
void startFile(OutputList &ol, const DString &name, bool isSource, const DString &manName, const DString &title, HighlightedItem hli, bool additionalIndices, const DString &altSidebarName, int hierarchyLevel, const DString &allMembersFile)
Definition index.cpp:405
Translator * theTranslator
Definition language.cpp:76
void setTranslator(OUTPUT_LANGUAGE_t langName)
Definition language.cpp:78
#define LATEX_STYLE_EXTENSION
Definition latexgen.h:21
void writeDefaultLayoutFile(const DString &fileName)
Definition layout.cpp:1734
void printLayout()
Definition layout.cpp:1822
std::unique_ptr< MemberDef > createMemberDef(const DString &defFileName, int defLine, size_t defColumn, const DString &type, const DString &name, const DString &args, const DString &excp, Protection prot, Specifier virt, bool stat, Relationship related, MemberType t, const ArgumentList &tal, const ArgumentList &al, const DString &metaData)
Factory method to create a new instance of a MemberDef.
std::unique_ptr< MemberDef > createMemberDefAlias(const Definition *newScope, const MemberDef *aliasMd)
MemberDefMutable * toMemberDefMutable(Definition *d)
void mergeMemberOverrideOptions(MemberDefMutable *md1, MemberDefMutable *md2)
void combineDeclarationAndDefinition(MemberDefMutable *mdec, MemberDefMutable *mdef)
MemberDef * toMemberDef(Definition *d)
std::unordered_map< int, std::unique_ptr< MemberGroupInfo > > MemberGroupInfoMap
#define DOX_NOGROUP
Definition membergroup.h:26
void initWarningFormat()
Definition message.cpp:233
void warn_flush()
Definition message.cpp:226
DString warn_line(const DString &file, int line)
Definition message.cpp:211
void finishWarnExit()
Definition message.cpp:291
#define warn_undoc(file, line, fmt,...)
Definition message.h:102
#define warn_uncond(fmt,...)
Definition message.h:122
#define warn(file, line, fmt,...)
Definition message.h:97
#define msg(fmt,...)
Definition message.h:94
#define err(fmt,...)
Definition message.h:127
#define ASSERT(x)
Definition message.h:142
#define term(fmt,...)
Definition message.h:137
void postProcess(bool clearHeaderAndFooter, CompareMode compareMode=CompareMode::Full)
CompareMode
Definition config.h:54
void checkAndCorrect(bool quiet, const bool check)
void compareDoxyfile(TextStream &t, CompareMode compareMode)
void writeTemplate(TextStream &t, bool shortList, bool updateOnly=false)
void deinit()
void init()
void updateObsolete()
bool parse(const DString &fileName, bool update=false, CompareMode compareMode=CompareMode::Full)
void correctPath(const StringVector &list)
Correct a possible wrong PATH variable.
Definition portable.cpp:532
FILE * popen(const DString &name, const DString &type)
Definition portable.cpp:495
double getSysElapsedTime()
Definition portable.cpp:113
void setenv(const DString &variable, const DString &value)
Definition portable.cpp:302
uint32_t pid()
Definition portable.cpp:264
int system(const DString &command, const DString &args, bool commandHasConsole=true)
Definition portable.cpp:121
int pclose(FILE *stream)
Definition portable.cpp:504
DString getenv(const DString &variable)
Definition portable.cpp:337
bool isAbsolutePath(const DString &fileName)
Definition portable.cpp:513
DString pathListSeparator()
Definition portable.cpp:399
std::ofstream openOutputStream(const DString &name, bool append=false)
Definition portable.cpp:681
const char * commandExtension()
Definition portable.cpp:477
void setShortDir()
Definition portable.cpp:569
DString trunc(const DString &s, size_t numChars=15)
Definition trace.h:56
std::unique_ptr< NamespaceDef > createNamespaceDef(const DString &defFileName, int defLine, size_t defColumn, const DString &name, const DString &ref, const DString &refFile, const DString &type, bool isPublished)
Factory method to create new NamespaceDef instance.
std::unique_ptr< NamespaceDef > createNamespaceDefAlias(const Definition *newScope, const NamespaceDef *nd)
Factory method to create an alias of an existing namespace.
void replaceNamespaceAliases(DString &name)
NamespaceDef * getResolvedNamespace(const DString &name)
NamespaceDef * toNamespaceDef(Definition *d)
NamespaceDefMutable * toNamespaceDefMutable(Definition *d)
NamespaceDefMutable * getResolvedNamespaceMutable(const DString &key)
std::unordered_set< const NamespaceDef * > NamespaceDefSet
bool search(std::string_view str, Match &match, const Ex &re, size_t pos)
Search in a given string str starting at position pos for a match against regular expression re.
Definition regex.cpp:850
std::unique_ptr< PageDef > createPageDef(const DString &f, int l, const DString &n, const DString &d, const DString &t)
Definition pagedef.cpp:89
void generatePerlMod()
void setPerlModDoxyfile(const DString &qs)
Portable versions of functions that are platform dependent.
int portable_iconv_close(void *cd)
void * portable_iconv_open(const char *tocode, const char *fromcode)
void addRefItem(const RefItemVector &sli, const DString &key, const DString &prefix, const DString &name, const DString &title, const DString &args, const Definition *scope)
Definition reflist.h:134
void initSearchIndexer()
void finalizeSearchIndexer()
void createJavaScriptSearchIndex()
void writeJavaScriptSearchIndex()
Javascript based search engine.
void generateHtmlForComment(const std::string &fn, const std::string &text)
Helper for implemented the -c option of doxygen, which produces HTML output for a given doxygen forma...
void generateSqlite3()
void addSTLSupport(std::shared_ptr< Entry > &root)
Add stub entries for the most used classes in the standard template library.
Some helper functions for std::string.
void addTerminalCharIfMissing(std::string &s, char c)
Definition stringutil.h:87
Class that contains information about an inheritance relation.
Definition classdef.h:51
ClassDef * classDef
Class definition that this relation inherits from.
Definition classdef.h:56
This class stores information about an inheritance relation.
Definition entry.h:88
Protection prot
inheritance type
Definition entry.h:93
Specifier virt
virtualness
Definition entry.h:94
DString name
the name of the base class
Definition entry.h:92
Data associated with description found in the body.
Definition definition.h:64
Helper class to pass options when calling OutputList::generateDoc().
Definition docoptions.h:24
Grouping info.
Definition types.h:227
DString groupname
name of the group
Definition types.h:257
static constexpr const char * getGroupPriName(GroupPri_t priority) noexcept
Definition types.h:240
@ GROUPING_INGROUP
membership in group was defined by @ingroup
Definition types.h:236
GroupPri_t pri
priority of this definition
Definition types.h:258
static bool execute(const DString &htmldir)
Definition htags.cpp:41
static bool loadFilemap(const DString &htmldir)
Definition htags.cpp:110
static bool useHtags
Definition htags.h:23
stat(const char *n, double el)
Definition doxygen.cpp:271
const char * name
Definition doxygen.cpp:268
void parseTagFile(const std::shared_ptr< Entry > &root, const char *fullName)
void exitTracing()
Definition trace.cpp:55
void initTracing(const DString &logFile, bool timing)
Definition trace.cpp:25
#define TRACE(...)
Definition trace.h:77
MemberType
Definition types.h:569
bool isTypeAClassFriend(const DString &type)
Definition types.h:920
Protection
Definition types.h:32
SrcLangExt
Definition types.h:207
Relationship
Definition types.h:167
Specifier
Definition types.h:80
bool useCaseSenseNames()
Returns true if the names of the symbols can be case sensitive.
Definition util.cpp:2679
DString substituteTemplateArgumentsInString(const DString &nm, const ArgumentList &formalArgs, const ArgumentList *actualArgs)
Definition util.cpp:3524
bool protectionLevelVisible(Protection prot)
Definition util.cpp:4671
DString stripFromIncludePath(const DString &path)
Definition util.cpp:227
DString mergeScopes(const DString &leftScope, const DString &rightScope)
Definition util.cpp:3753
DString filterTitle(const DString &title)
Definition util.cpp:4454
bool matchTemplateArguments(const ArgumentList &srcAl, const ArgumentList &dstAl)
Definition util.cpp:1833
void addCodeOnlyMappings()
Definition util.cpp:4162
bool rightScopeMatch(const DString &scope, const DString &name)
Definition util.cpp:729
bool checkIfTypedef(const Definition *scope, const FileDef *fileScope, const DString &n)
Definition util.cpp:4271
DString replaceAnonymousScopes(const DString &s, const DString &replacement)
Definition util.cpp:155
void cleanupInlineGraphs()
Definition util.cpp:5383
int computeQualifiedIndex(const DString &name)
Return the index of the last :: in the string name that is still before the first <.
Definition util.cpp:5278
DString removeRedundantWhiteSpace(const DString &s)
Definition util.cpp:426
bool checkExtension(const DString &fName, const DString &ext)
Definition util.cpp:3926
DString convertNameToFile(const DString &name, bool allowDots, bool allowUnderscore)
Definition util.cpp:2863
bool leftScopeMatch(const DString &scope, const DString &name)
Definition util.cpp:740
DString tempArgListToString(const ArgumentList &al, SrcLangExt lang, bool includeDefault)
Definition util.cpp:903
DString getFileNameExtension(const DString &fn)
Definition util.cpp:4210
DString resolveTypeDef(const Definition *context, const DString &qualifiedName, const Definition **typedefContext)
Definition util.cpp:232
bool readInputFile(const DString &fileName, std::string &contents, bool filter, bool isSourceCode)
read a file name fileName and optionally filter and transcode it
Definition util.cpp:4374
DString normalizeNonTemplateArgumentsInString(const DString &name, const Definition *context, const ArgumentList &formalArgs)
Definition util.cpp:3465
SrcLangExt getLanguageFromFileName(const DString &fileName, SrcLangExt defLang)
Definition util.cpp:4168
bool matchArguments2(const Definition *srcScope, const FileDef *srcFileScope, const DString &srcReturnType, const ArgumentList *srcAl, const Definition *dstScope, const FileDef *dstFileScope, const DString &dstReturnType, const ArgumentList *dstAl, bool checkCV, SrcLangExt lang)
Definition util.cpp:1589
void initDefaultExtensionMapping()
Definition util.cpp:4095
void addHtmlExtensionIfMissing(DString &fName)
Definition util.cpp:3931
DString fileToString(const DString &name, bool filter, bool isSourceCode)
Definition util.cpp:1053
void extractNamespaceName(const DString &scopeName, DString &className, DString &namespaceName, bool allowEmptyClass)
Definition util.cpp:3026
DString stripTemplateSpecifiersFromScope(const DString &fullName, bool parentOnly, DString *pLastScopeStripped, DString scopeName, bool allowArtificial)
Definition util.cpp:3686
DString argListToString(const ArgumentList &al, bool useCanonicalType, bool showDefVals)
Definition util.cpp:859
DString projectLogoFile()
Definition util.cpp:2580
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:4619
int getPrefixIndex(const DString &name)
Definition util.cpp:2651
DString getLanguageSpecificSeparator(SrcLangExt lang, bool classScope)
Definition util.cpp:4629
bool updateLanguageMapping(const DString &extension, const DString &language)
Definition util.cpp:4063
DString mangleCSharpGenericName(const DString &name)
Definition util.cpp:5319
void mergeArguments(ArgumentList &srcAl, ArgumentList &dstAl, bool forceNameOverwrite)
Definition util.cpp:1689
int getScopeFragment(const DString &s, int p, int *l)
Definition util.cpp:3798
int extractClassNameFromType(const DString &type, int &pos, DString &name, DString &templSpec, SrcLangExt lang)
Definition util.cpp:3380
bool openOutputFile(const DString &outFile, std::ofstream &f)
Definition util.cpp:4971
DString stripAnonymousNamespaceScope(const DString &s)
Definition util.cpp:167
A bunch of utility functions.
void generateXML()
Definition xmlgen.cpp:2316