Doxygen
Loading...
Searching...
No Matches
doxygen.cpp
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * Copyright (C) 1997-2015 by Dimitri van Heesch.
4 *
5 * Permission to use, copy, modify, and distribute this software and its
6 * documentation under the terms of the GNU General Public License is hereby
7 * granted. No representations are made about the suitability of this software
8 * for any purpose. It is provided "as is" without express or implied warranty.
9 * See the GNU General Public License for more details.
10 *
11 * Documents produced by Doxygen are derivative works derived from the
12 * input used in their production; they are not affected by this license.
13 *
14 */
15
16// own header
17#include "doxygen.h"
18
19// standard includes
20#include <algorithm>
21#include <cerrno>
22#include <chrono>
23#include <cinttypes>
24#include <clocale>
25#include <cstdio>
26#include <cstdlib>
27#include <locale>
28#include <memory>
29#include <sys/stat.h>
30#include <unordered_map>
31
32// other includes
33#include "aliases.h"
34#include "arguments.h"
35#include "cite.h"
36#include "clangparser.h"
37#include "classlist.h"
38#include "cmdmapper.h"
39#include "code.h"
40#include "commentcnv.h"
41#include "conceptdef.h"
42#include "config.h"
43#include "debug.h"
44#include "declinfo.h"
45#include "defargs.h"
46#include "defgen.h"
47#include "dir.h"
48#include "dirdef.h"
49#include "docbookgen.h"
50#include "docparser.h"
51#include "docsets.h"
52#include "dot.h"
53#include "eclipsehelp.h"
54#include "emoji.h"
55#include "entry.h"
56#include "fileinfo.h"
57#include "filename.h"
58#include "fileparser.h"
59#include "formula.h"
60#include "fortrancode.h"
61#include "fortranscanner.h"
62#include "ftvhelp.h"
63#include "groupdef.h"
64#include "htags.h"
65#include "htmlgen.h"
66#include "htmlhelp.h"
67#include "index.h"
68#include "indexlist.h"
69#include "language.h"
70#include "latexgen.h"
71#include "layout.h"
72#include "lexcode.h"
73#include "lexscanner.h"
74#include "mangen.h"
75#include "markdown.h"
76#include "membergroup.h"
77#include "memberlist.h"
78#include "membername.h"
79#include "mermaid.h"
80#include "message.h"
81#include "moduledef.h"
82#include "msc.h"
83#include "namespacedef.h"
84#include "outputlist.h"
85#include "pagedef.h"
86#include "parserintf.h"
87#include "perlmodgen.h"
88#include "plantuml.h"
89#include "portable.h"
90#include "pre.h"
91#include "pycode.h"
92#include "pyscanner.h"
93#include "qhp.h"
94#include "reflist.h"
95#include "regex.h"
96#include "requirement.h"
97#include "rtfgen.h"
98#include "scanner.h"
99#include "searchindex_js.h"
100#include "settings.h"
101#include "singlecomment.h"
102#include "sitemap.h"
103#include "sqlcode.h"
104#include "sqlite3gen.h"
105#include "stlsupport.h"
106#include "stringutil.h"
107#include "symbolresolver.h"
108#include "tagreader.h"
109#include "threadpool.h"
110#include "trace.h"
111#include "util.h"
112#include "version.h"
113#include "vhdlcode.h"
114#include "vhdldocgen.h"
115#include "vhdljjparser.h"
116#include "xmlcode.h"
117#include "xmlgen.h"
118
119#include <sqlite3.h>
120
121#if USE_LIBCLANG
122#if defined(__GNUC__)
123#pragma GCC diagnostic push
124#pragma GCC diagnostic ignored "-Wshadow"
125#endif
126#include <clang/Basic/Version.h>
127#if defined(__GNUC__)
128#pragma GCC diagnostic pop
129#endif
130#endif
131
132// provided by the generated file resources.cpp
133extern void initResources();
134
135#if !defined(_WIN32) || defined(__CYGWIN__)
136#include <signal.h>
137#define HAS_SIGNALS
138#endif
139
140// globally accessible variables
152FileNameLinkedMap *Doxygen::includeNameLinkedMap = nullptr; // include names
158FileNameLinkedMap *Doxygen::plantUmlFileNameLinkedMap = nullptr;// plantuml files
159FileNameLinkedMap *Doxygen::mermaidFileNameLinkedMap = nullptr; // mermaid files
161StringMap Doxygen::tagDestinationMap; // all tag locations
162StringUnorderedSet Doxygen::tagFileSet; // all tag file names
163StringUnorderedSet Doxygen::expandAsDefinedSet; // all macros that should be expanded
164MemberGroupInfoMap Doxygen::memberGroupInfoMap; // dictionary of the member groups heading
165std::unique_ptr<PageDef> Doxygen::mainPage;
166std::unique_ptr<NamespaceDef> Doxygen::globalNamespaceDef;
168bool Doxygen::parseSourcesNeeded = false;
186std::mutex Doxygen::addExampleMutex;
188
189// locally accessible globals
190static std::multimap< std::string, const Entry* > g_classEntries;
192static OutputList *g_outputList = nullptr; // list of output generating objects
193static StringSet g_usingDeclarations; // used classes
194static bool g_successfulRun = false;
195static bool g_dumpSymbolMap = false;
197static bool g_singleComment=false;
198
199
200
201// keywords recognized as compounds
203{ "template class", "template struct", "class", "struct", "union", "interface", "exception" };
204
232
234{
235 public:
237 void begin(const char *name)
238 {
239 msg("{}", name);
240 stats.emplace_back(name,0);
241 startTime = std::chrono::steady_clock::now();
242 }
243 void end()
244 {
245 std::chrono::steady_clock::time_point endTime = std::chrono::steady_clock::now();
246 stats.back().elapsed = static_cast<double>(std::chrono::duration_cast<
247 std::chrono::microseconds>(endTime - startTime).count())/1000000.0;
248 warn_flush();
249 }
250 void print()
251 {
252 bool restore=false;
254 {
256 restore=true;
257 }
258 msg("----------------------\n");
259 for (const auto &s : stats)
260 {
261 msg("Spent {:.6f} seconds in {}",s.elapsed,s.name);
262 }
263 if (restore) Debug::setFlag(Debug::Time);
264 }
265 private:
266 struct stat
267 {
268 const char *name;
269 double elapsed;
270 //stat() : name(nullptr),elapsed(0) {}
271 stat(const char *n, double el) : name(n),elapsed(el) {}
272 };
273 std::vector<stat> stats;
274 std::chrono::steady_clock::time_point startTime;
276
277
278static void addMemberDocs(const Entry *root,MemberDefMutable *md, const DString &funcDecl,
279 const ArgumentList *al,bool over_load,TypeSpecifier spec);
280static void findMember(const Entry *root,
281 const DString &relates,
282 const DString &type,
283 const DString &args,
284 DString funcDecl,
285 bool overloaded,
286 bool isFunc
287 );
288
295
296
297static bool findClassRelation(
298 const Entry *root,
299 Definition *context,
300 ClassDefMutable *cd,
301 const BaseInfo *bi,
302 const TemplateNameMap &templateNames,
303 /*bool insertUndocumented*/
305 bool isArtificial
306 );
307
308//----------------------------------------------------------------------
309
310template<class Def>
311static void addIncludeFile(Def *def,FileDef *ifd,const Entry *root)
312{
313 AUTO_TRACE();
314 if (
315 (!root->doc.stripWhiteSpace().empty() ||
316 !root->brief.stripWhiteSpace().empty() ||
317 Config_getBool(EXTRACT_ALL)
318 ) && root->protection!=Protection::Private
319 )
320 {
321 AUTO_TRACE_ADD("includefile={}",root->includeFile);
322 //printf(">>>>>> includeFile=%s\n",qPrint(root->includeFile));
323
324 bool local=Config_getBool(FORCE_LOCAL_INCLUDES);
325 DString includeFile = root->includeFile;
326 if (!includeFile.empty() && includeFile.at(0)=='"')
327 {
328 local = true;
329 includeFile=includeFile.mid(1,includeFile.length()-2);
330 }
331 else if (!includeFile.empty() && includeFile.at(0)=='<')
332 {
333 local = false;
334 includeFile=includeFile.mid(1,includeFile.length()-2);
335 }
336
337 bool ambig = false;
338 FileDef *fd=nullptr;
339 // see if we need to include a verbatim copy of the header file
340 //printf("root->includeFile=%s\n",qPrint(root->includeFile));
341 if (!includeFile.empty() &&
342 (fd=Doxygen::inputNameLinkedMap->findFileDef(includeFile,ambig))==nullptr
343 )
344 { // explicit request
345 DString text;
346 text.sprintf("the name '%s' supplied as "
347 "the argument of the \\class, \\struct, \\union, or \\headerfile command ",
348 qPrint(includeFile)
349 );
350 if (ambig) // name is ambiguous
351 {
352 text+="matches the following input files:\n";
354 text+="\n";
355 text+="Please use a more specific name by "
356 "including a (larger) part of the path!";
357 }
358 else // name is not an input file
359 {
360 text+="is not an input file";
361 }
362 warn(root->fileName,root->startLine, "{}", text);
363 }
364 else if (includeFile.empty() && ifd &&
365 // see if the file extension makes sense
366 EntryType::guessSection(ifd->name()).isHeader())
367 { // implicit assumption
368 fd=ifd;
369 }
370
371 // if a file is found, we mark it as a source file.
372 if (fd)
373 {
374 DString iName = !root->includeName.empty() ?
375 root->includeName : includeFile;
376 if (!iName.empty()) // user specified include file
377 {
378 if (iName.at(0)=='<') local=false; // explicit override
379 else if (iName.at(0)=='"') local=true;
380 if (iName.at(0)=='"' || iName.at(0)=='<')
381 {
382 iName=iName.mid(1,iName.length()-2); // strip quotes or brackets
383 }
384 if (iName.empty())
385 {
386 iName=fd->name();
387 }
388 }
389 else if (!Config_getList(STRIP_FROM_INC_PATH).empty())
390 {
392 }
393 else // use name of the file containing the class definition
394 {
395 iName=fd->name();
396 }
397 AUTO_TRACE_ADD("iName={}",iName);
398 if (fd->generateSourceFile()) // generate code for header
399 {
400 def->setIncludeFile(fd,iName,local,!root->includeName.empty());
401 }
402 else // put #include in the class documentation without link
403 {
404 def->setIncludeFile(nullptr,iName,local,true);
405 }
406 }
407 }
408}
409
410
411//----------------------------------------------------------------------------
412
414 FileDef *fileScope,const TagInfo *tagInfo);
415static void resolveTemplateInstanceInType(const Entry *root,const Definition *scope,const MemberDef *md);
416
417static void addPageToContext(PageDef *pd,Entry *root)
418{
419 if (root->parent()) // add the page to it's scope
420 {
421 DString scope = root->parent()->name;
422 if (root->parent()->section.isPackageDoc())
423 {
424 scope=substitute(scope,".","::");
425 }
426 scope = stripAnonymousNamespaceScope(scope);
427 scope+="::"+pd->name();
429 if (d)
430 {
431 pd->setPageScope(d);
432 }
433 }
434}
435
436static void addRelatedPage(Entry *root)
437{
438 GroupDef *gd=nullptr;
439 for (const Grouping &g : root->groups)
440 {
441 if (!g.groupname.empty() && (gd=Doxygen::groupLinkedMap->find(g.groupname))) break;
442 }
443 //printf("---> addRelatedPage() %s gd=%p\n",qPrint(root->name),gd);
444 DString doc=root->doc+root->inbodyDocs;
445
446 PageDef *pd = addRelatedPage(root->name, // name
447 root->args, // ptitle
448 doc, // doc
449 root->docFile, // fileName
450 root->docLine, // docLine
451 root->startLine, // startLine
452 root->sli, // sli
453 gd, // gd
454 root->tagInfo(), // tagInfo
455 false, // xref
456 root->lang // lang
457 );
458 if (pd)
459 {
460 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
462 pd->setLocalToc(root->localToc);
463 addPageToContext(pd,root);
464 }
465}
466
467static void buildGroupListFiltered(const Entry *root,bool additional, bool includeExternal)
468{
469 if (root->section.isGroupDoc() && !root->name.empty() &&
470 ((!includeExternal && root->tagInfo()==nullptr) ||
471 ( includeExternal && root->tagInfo()!=nullptr))
472 )
473 {
474 AUTO_TRACE("additional={} includeExternal={}",additional,includeExternal);
475 if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
476 (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
477 {
479 AUTO_TRACE_ADD("Processing group '{}':'{}' gd={}", root->type,root->name,(void*)gd);
480
481 if (gd)
482 {
483 if ( !gd->hasGroupTitle() )
484 {
485 gd->setGroupTitle( root->type );
486 }
487 else if ( root->type.length() > 0 && root->name != root->type && gd->groupTitle() != root->type )
488 {
489 warn( root->fileName,root->startLine,
490 "group {}: ignoring title \"{}\" that does not match old title \"{}\"",
491 root->name, root->type, gd->groupTitle() );
492 }
493 gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
494 gd->setDocumentation( root->doc, root->docFile, root->docLine );
495 gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
497 gd->setRefItems(root->sli);
499 gd->setLanguage(root->lang);
501 {
502 root->commandOverrides.apply_groupGraph([&](bool b) { gd->overrideGroupGraph(b); });
503 }
504 addIncludeFile(gd,nullptr,root);
505 }
506 else
507 {
508 if (root->tagInfo())
509 {
510 gd = Doxygen::groupLinkedMap->add(root->name,
511 std::unique_ptr<GroupDef>(
512 createGroupDef(root->fileName,root->startLine,root->name,root->type,root->tagInfo()->fileName)));
513 gd->setReference(root->tagInfo()->tagName);
514 }
515 else
516 {
517 gd = Doxygen::groupLinkedMap->add(root->name,
518 std::unique_ptr<GroupDef>(
519 createGroupDef(root->fileName,root->startLine,root->name,root->type)));
520 }
521 gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
522 // allow empty docs for group
523 gd->setDocumentation(!root->doc.empty() ? root->doc : DString(" "),root->docFile,root->docLine,false);
524 gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
526 gd->setRefItems(root->sli);
528 gd->setLanguage(root->lang);
529 addIncludeFile(gd,nullptr,root);
531 {
532 root->commandOverrides.apply_groupGraph([&](bool b) { gd->overrideGroupGraph(b); });
533 }
534 }
535 }
536 }
537 for (const auto &e : root->children()) buildGroupListFiltered(e.get(),additional,includeExternal);
538}
539
540static void buildGroupList(const Entry *root)
541{
542 // --- first process only local groups
543 // first process the @defgroups blocks
544 buildGroupListFiltered(root,false,false);
545 // then process the @addtogroup, @weakgroup blocks
546 buildGroupListFiltered(root,true,false);
547
548 // --- then also process external groups
549 // first process the @defgroups blocks
550 buildGroupListFiltered(root,false,true);
551 // then process the @addtogroup, @weakgroup blocks
552 buildGroupListFiltered(root,true,true);
553}
554
555static void findGroupScope(const Entry *root)
556{
557 if (root->section.isGroupDoc() && !root->name.empty() &&
558 root->parent() && !root->parent()->name.empty())
559 {
561 if (gd)
562 {
563 DString scope = root->parent()->name;
564 if (root->parent()->section.isPackageDoc())
565 {
566 scope=substitute(scope,".","::");
567 }
568 scope = stripAnonymousNamespaceScope(scope);
569 scope+="::"+gd->name();
571 if (d)
572 {
573 gd->setGroupScope(d);
574 }
575 }
576 }
577 for (const auto &e : root->children()) findGroupScope(e.get());
578}
579
580static void organizeSubGroupsFiltered(const Entry *root,bool additional)
581{
582 if (root->section.isGroupDoc() && !root->name.empty())
583 {
584 AUTO_TRACE("additional={}",additional);
585 if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
586 (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
587 {
589 if (gd)
590 {
591 AUTO_TRACE_ADD("adding {} to group {}",root->name,gd->name());
592 addGroupToGroups(root,gd);
593 }
594 }
595 }
596 for (const auto &e : root->children()) organizeSubGroupsFiltered(e.get(),additional);
597}
598
599static void organizeSubGroups(const Entry *root)
600{
601 //printf("Defining groups\n");
602 // first process the @defgroups blocks
603 organizeSubGroupsFiltered(root,false);
604 //printf("Additional groups\n");
605 // then process the @addtogroup, @weakgroup blocks
606 organizeSubGroupsFiltered(root,true);
607}
608
609//----------------------------------------------------------------------
610
611static void buildFileList(const Entry *root)
612{
613 if ((root->section.isFileDoc() || (root->section.isFile() && Config_getBool(EXTRACT_ALL))) &&
614 !root->name.empty() && !root->tagInfo() // skip any file coming from tag files
615 )
616 {
617 bool ambig = false;
619 if (!fd || ambig)
620 {
621 bool save_ambig = ambig;
622 // use the directory of the file to see if the described file is in the same
623 // directory as the describing file.
624 DString fn = root->fileName;
625 size_t newIndex=fn.rfind('/');
626 if (newIndex==DString::npos)
627 {
628 fn = root->name;
629 }
630 else
631 {
632 fn = fn.left(newIndex)+"/"+root->name;
633 }
635 if (!fd) ambig = save_ambig;
636 }
637 //printf("**************** root->name=%s fd=%p\n",qPrint(root->name),(void*)fd);
638 if (fd && !ambig)
639 {
640 //printf("Adding documentation!\n");
641 // using false in setDocumentation is small hack to make sure a file
642 // is documented even if a \file command is used without further
643 // documentation
644 fd->setDocumentation(root->doc,root->docFile,root->docLine,false);
645 fd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
647 fd->setRefItems(root->sli);
649 addIncludeFile(fd,fd,root);
650 root->commandOverrides.apply_includeGraph ([&](bool b) { fd->overrideIncludeGraph(b); });
651 root->commandOverrides.apply_includedByGraph([&](bool b) { fd->overrideIncludedByGraph(b); });
652 for (const Grouping &g : root->groups)
653 {
654 GroupDef *gd=nullptr;
656 {
657 if (!gd->containsFile(fd))
658 {
659 gd->addFile(fd);
660 fd->makePartOfGroup(gd);
661 //printf("File %s: in group %s\n",qPrint(fd->name()),qPrint(gd->name()));
662 }
663 }
664 else if (!gd && g.pri == Grouping::GROUPING_INGROUP)
665 {
666 warn(root->fileName, root->startLine,
667 "Found non-existing group '{}' for the command '{}', ignoring command",
669 );
670 }
671 }
672 }
673 else
674 {
675 DString text(4096, DString::ExplicitSize);
676 text.sprintf("the name '%s' supplied as "
677 "the argument in the \\file statement ",
678 qPrint(root->name));
679 if (ambig) // name is ambiguous
680 {
681 text+="matches the following input files:\n";
683 text+="\n";
684 text+="Please use a more specific name by "
685 "including a (larger) part of the path!";
686 }
687 else // name is not an input file
688 {
689 text+="is not an input file";
690 }
691 warn(root->fileName,root->startLine,"{}", text);
692 }
693 }
694 for (const auto &e : root->children()) buildFileList(e.get());
695}
696
698{
699 size_t l = s.length();
700 int count=0;
701 int round=0;
702 DString result;
703 for (size_t i=0;i<l;i++)
704 {
705 char c=s.at(i);
706 if (c=='(') round++;
707 else if (c==')' && round>0) round--;
708 else if (c=='<' && round==0) count++;
709 if (count==0)
710 {
711 result+=c;
712 }
713 if (c=='>' && round==0 && count>0) count--;
714 }
715 //printf("stripTemplateSpecifiers(%s)=%s\n",qPrint(s),qPrint(result));
716 return result;
717}
718
719/*! returns the Definition object belonging to the first \a level levels of
720 * full qualified name \a name. Creates an artificial scope if the scope is
721 * not found and set the parent/child scope relation if the scope is found.
722 */
723[[maybe_unused]]
724static Definition *buildScopeFromQualifiedName(const DString &name_,SrcLangExt lang,const TagInfo *tagInfo)
725{
726 DString name = stripTemplateSpecifiers(name_);
727 name.stripPrefix("::");
728 int level = name.contains("::");
729 //printf("buildScopeFromQualifiedName(%s) level=%d\n",qPrint(name),level);
730 int i=0, p=0, l=0;
732 DString fullScope;
733 while (i<level)
734 {
735 int idx=getScopeFragment(name,p,&l);
736 if (idx==-1) return prevScope;
737 DString nsName = name.mid(idx,l);
738 if (nsName.empty()) return prevScope;
739 if (!fullScope.empty()) fullScope+="::";
740 fullScope+=nsName;
742 DefinitionMutable *innerScope = toDefinitionMutable(nd);
743 ClassDef *cd=nullptr;
744 if (nd==nullptr) cd = getClass(fullScope);
745 if (nd==nullptr && cd) // scope is a class
746 {
747 innerScope = toDefinitionMutable(cd);
748 }
749 else if (nd==nullptr && cd==nullptr && fullScope.find('<')==DString::npos) // scope is not known and could be a namespace!
750 {
751 // introduce bogus namespace
752 //printf("++ adding dummy namespace %s to %s tagInfo=%p\n",qPrint(nsName),qPrint(prevScope->name()),(void*)tagInfo);
753 NamespaceDefMutable *newNd=
755 Doxygen::namespaceLinkedMap->add(fullScope,
757 "[generated]",1,1,fullScope,
758 tagInfo?tagInfo->tagName:DString(),
759 tagInfo?tagInfo->fileName:DString())));
760 if (newNd)
761 {
762 newNd->setLanguage(lang);
763 newNd->setArtificial(true);
764 // add namespace to the list
765 innerScope = newNd;
766 }
767 }
768 else // scope is a namespace
769 {
770 }
771 if (innerScope)
772 {
773 // make the parent/child scope relation
774 DefinitionMutable *prevScopeMutable = toDefinitionMutable(prevScope);
775 if (prevScopeMutable)
776 {
777 prevScopeMutable->addInnerCompound(toDefinition(innerScope));
778 }
779 innerScope->setOuterScope(prevScope);
780 }
781 else // current scope is a class, so return only the namespace part...
782 {
783 return prevScope;
784 }
785 // proceed to the next scope fragment
786 p=idx+l+2;
787 prevScope=toDefinition(innerScope);
788 i++;
789 }
790 return prevScope;
791}
792
794 FileDef *fileScope,const TagInfo *tagInfo)
795{
796 //printf("<findScopeFromQualifiedName(%s,%s)\n",startScope ? qPrint(startScope->name()) : 0, qPrint(n));
797 Definition *resultScope=toDefinition(startScope);
798 if (resultScope==nullptr) resultScope=Doxygen::globalScope;
800 int l1 = 0;
801 int i1 = getScopeFragment(scope,0,&l1);
802 if (i1==-1)
803 {
804 //printf(">no fragments!\n");
805 return resultScope;
806 }
807 int p=i1+l1,l2=0,i2=0;
808 while ((i2=getScopeFragment(scope,p,&l2))!=-1)
809 {
810 DString nestedNameSpecifier = scope.mid(i1,l1);
811 Definition *orgScope = resultScope;
812 //printf(" nestedNameSpecifier=%s\n",qPrint(nestedNameSpecifier));
813 resultScope = const_cast<Definition*>(resultScope->findInnerCompound(nestedNameSpecifier));
814 //printf(" resultScope=%p\n",resultScope);
815 if (resultScope==nullptr)
816 {
817 if (orgScope==Doxygen::globalScope && fileScope && !fileScope->getUsedNamespaces().empty())
818 // also search for used namespaces
819 {
820 for (const auto &nd : fileScope->getUsedNamespaces())
821 {
823 if (mnd)
824 {
825 resultScope = findScopeFromQualifiedName(toNamespaceDefMutable(nd),n,fileScope,tagInfo);
826 if (resultScope!=nullptr) break;
827 }
828 }
829 if (resultScope)
830 {
831 // for a nested class A::I in used namespace N, we get
832 // N::A::I while looking for A, so we should compare
833 // resultScope->name() against scope.left(i2+l2)
834 //printf(" -> result=%s scope=%s\n",qPrint(resultScope->name()),qPrint(scope));
835 if (rightScopeMatch(resultScope->name(),scope.left(i2+l2)))
836 {
837 break;
838 }
839 goto nextFragment;
840 }
841 }
842
843 // also search for used classes. Complication: we haven't been able
844 // to put them in the right scope yet, because we are still resolving
845 // the scope relations!
846 // Therefore loop through all used classes and see if there is a right
847 // scope match between the used class and nestedNameSpecifier.
848 for (const auto &usedName : g_usingDeclarations)
849 {
850 //printf("Checking using class %s\n",qPrint(usedName));
851 if (rightScopeMatch(usedName,nestedNameSpecifier))
852 {
853 // ui.currentKey() is the fully qualified name of nestedNameSpecifier
854 // so use this instead.
855 DString fqn = usedName + scope.mid(p);
856 resultScope = buildScopeFromQualifiedName(fqn,startScope->getLanguage(),nullptr);
857 //printf("Creating scope from fqn=%s result %p\n",qPrint(fqn),resultScope);
858 if (resultScope)
859 {
860 //printf("> Match! resultScope=%s\n",qPrint(resultScope->name()));
861 return resultScope;
862 }
863 }
864 }
865
866 //printf("> name %s not found in scope %s\n",qPrint(nestedNameSpecifier),qPrint(orgScope->name()));
867 return nullptr;
868 }
869 nextFragment:
870 i1=i2;
871 l1=l2;
872 p=i2+l2;
873 }
874 //printf(">findScopeFromQualifiedName scope %s\n",qPrint(resultScope->name()));
875 return resultScope;
876}
877
878std::unique_ptr<ArgumentList> getTemplateArgumentsFromName(
879 const DString &name,
880 const ArgumentLists &tArgLists)
881{
882 // for each scope fragment, check if it is a template and advance through
883 // the list if so.
884 size_t i=0, p=0;
885 auto alIt = tArgLists.begin();
886 while ((i=name.find("::",p))!=DString::npos && alIt!=tArgLists.end())
887 {
888 NamespaceDef *nd = Doxygen::namespaceLinkedMap->find(name.left(i));
889 if (nd==nullptr)
890 {
891 ClassDef *cd = getClass(name.left(i));
892 if (cd)
893 {
894 if (!cd->templateArguments().empty())
895 {
896 ++alIt;
897 }
898 }
899 }
900 p=i+2;
901 }
902 return alIt!=tArgLists.end() ?
903 std::make_unique<ArgumentList>(*alIt) :
904 std::unique_ptr<ArgumentList>();
905}
906
907static
909{
911
912 if (specifier.isStruct())
914 else if (specifier.isUnion())
915 sec=ClassDef::Union;
916 else if (specifier.isCategory())
918 else if (specifier.isInterface())
920 else if (specifier.isProtocol())
922 else if (specifier.isException())
924 else if (specifier.isService())
926 else if (specifier.isSingleton())
928
929 if (section.isUnionDoc())
930 sec=ClassDef::Union;
931 else if (section.isStructDoc())
933 else if (section.isInterfaceDoc())
935 else if (section.isProtocolDoc())
937 else if (section.isCategoryDoc())
939 else if (section.isExceptionDoc())
941 else if (section.isServiceDoc())
943 else if (section.isSingletonDoc())
945
946 return sec;
947}
948
949
950static void addClassToContext(const Entry *root)
951{
952 AUTO_TRACE("name={}",root->name);
953 FileDef *fd = root->fileDef();
954
955 DString scName;
956 if (root->parent()->section.isScope())
957 {
958 scName=root->parent()->name;
959 }
960 // name without parent's scope
961 DString fullName = root->name;
962
963 // strip off any template parameters (but not those for specializations)
964 if (size_t idx=fullName.find('>'); idx!=DString::npos && root->lang==SrcLangExt::CSharp) // mangle A<S,T>::N as A-2-g::N
965 {
966 fullName = mangleCSharpGenericName(fullName.left(idx+1))+fullName.mid(idx+1);
967 }
968 fullName=stripTemplateSpecifiersFromScope(fullName);
969
970 // name with scope (if not present already)
971 DString qualifiedName = fullName;
972 if (!scName.empty() && !leftScopeMatch(scName,fullName))
973 {
974 qualifiedName.prepend(scName+"::");
975 }
976
977 // see if we already found the class before
978 ClassDefMutable *cd = getClassMutable(qualifiedName);
979
980 AUTO_TRACE_ADD("Found class with name '{}', qualifiedName '{}'", cd ? cd->name() : root->name, qualifiedName);
981
982 if (cd)
983 {
984 fullName=cd->name();
985 AUTO_TRACE_ADD("Existing class '{}'",cd->name());
986 //if (cd->templateArguments()==0)
987 //{
988 // //printf("existing ClassDef tempArgList=%p specScope=%s\n",root->tArgList,qPrint(root->scopeSpec));
989 // cd->setTemplateArguments(tArgList);
990 //}
991
992 cd->setDocumentation(root->doc,root->docFile,root->docLine);
993 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
994 root->commandOverrides.apply_collaborationGraph([&](bool b ) { cd->overrideCollaborationGraph(b); });
995 root->commandOverrides.apply_inheritanceGraph ([&](CLASS_GRAPH_t gt) { cd->overrideInheritanceGraph(gt); });
996
997 if (!root->spec.isForwardDecl() && cd->isForwardDeclared())
998 {
999 cd->setDefFile(root->fileName,root->startLine,root->startColumn);
1000 if (root->bodyLine!=-1)
1001 {
1002 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1003 cd->setBodyDef(fd);
1004 }
1005 }
1006
1007 if (cd->templateArguments().empty() || (cd->isForwardDeclared() && !root->spec.isForwardDecl()))
1008 {
1009 // this happens if a template class declared with @class is found
1010 // before the actual definition or if a forward declaration has different template
1011 // parameter names.
1012 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(cd->name(),root->tArgLists);
1013 if (tArgList)
1014 {
1015 cd->setTemplateArguments(*tArgList);
1016 }
1017 }
1018 if (cd->requiresClause().empty() && !root->req.empty())
1019 {
1020 cd->setRequiresClause(root->req);
1021 }
1022
1024
1025 cd->setMetaData(root->metaData);
1026 }
1027 else // new class
1028 {
1030
1031 DString className;
1032 DString namespaceName;
1033 extractNamespaceName(fullName,className,namespaceName);
1034
1035 AUTO_TRACE_ADD("New class: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1036 fullName, namespaceName, className, Trace::trunc(root->brief), Trace::trunc(root->doc));
1037
1038 DString tagName;
1039 DString refFileName;
1040 const TagInfo *tagInfo = root->tagInfo();
1041 if (tagInfo)
1042 {
1043 tagName = tagInfo->tagName;
1044 refFileName = tagInfo->fileName;
1045 if (fullName.find("::")!=DString::npos)
1046 // symbols imported via tag files may come without the parent scope,
1047 // so we artificially create it here
1048 {
1049 buildScopeFromQualifiedName(fullName,root->lang,tagInfo);
1050 }
1051 }
1052 std::unique_ptr<ArgumentList> tArgList;
1053 size_t i=0;
1054 if ((root->lang==SrcLangExt::CSharp || root->lang==SrcLangExt::Java) &&
1055 (i=fullName.find('<'))!=DString::npos)
1056 {
1057 // a Java/C# generic class looks like a C++ specialization, so we need to split the
1058 // name and template arguments here
1059 tArgList = stringToArgumentList(root->lang,fullName.mid(i));
1060 if (i!=DString::npos && root->lang==SrcLangExt::CSharp) // in C# A, A<T>, and A<T,S> are different classes, so we need some way to disguish them using this name mangling
1061 // A -> A
1062 // A<T> -> A-1-g
1063 // A<T,S> -> A-2-g
1064 {
1065 fullName=mangleCSharpGenericName(fullName);
1066 }
1067 else
1068 {
1069 fullName=fullName.left(i);
1070 }
1071 }
1072 else
1073 {
1074 tArgList = getTemplateArgumentsFromName(fullName,root->tArgLists);
1075 }
1076 // add class to the list
1077 cd = toClassDefMutable(
1078 Doxygen::classLinkedMap->add(fullName,
1079 createClassDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1080 fullName,sec,tagName,refFileName,true,root->spec.isEnum()) ));
1081 if (cd)
1082 {
1083 AUTO_TRACE_ADD("New class '{}' type={} #tArgLists={} tagInfo={} hidden={} artificial={}",
1084 fullName,cd->compoundTypeString(),root->tArgLists.size(),
1085 fmt::ptr(tagInfo),root->hidden,root->artificial);
1086 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1087 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1088 cd->setLanguage(root->lang);
1089 cd->setId(root->id);
1090 cd->setHidden(root->hidden);
1091 cd->setArtificial(root->artificial);
1092 cd->setClassSpecifier(root->spec);
1093 if (root->lang==SrcLangExt::CSharp && !root->args.empty())
1094 {
1096 }
1097 cd->addQualifiers(root->qualifiers);
1098 cd->setTypeConstraints(root->typeConstr);
1099 root->commandOverrides.apply_collaborationGraph([&](bool b ) { cd->overrideCollaborationGraph(b); });
1100 root->commandOverrides.apply_inheritanceGraph ([&](CLASS_GRAPH_t gt) { cd->overrideInheritanceGraph(gt); });
1101
1102 if (tArgList)
1103 {
1104 cd->setTemplateArguments(*tArgList);
1105 }
1106 cd->setRequiresClause(root->req);
1107 cd->setProtection(root->protection);
1108 cd->setIsStatic(root->isStatic);
1109
1110 // file definition containing the class cd
1111 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1112 cd->setBodyDef(fd);
1113
1114 cd->setMetaData(root->metaData);
1115
1116 cd->insertUsedFile(fd);
1117 }
1118 else
1119 {
1120 AUTO_TRACE_ADD("Class {} not added, already exists as alias", fullName);
1121 }
1122 }
1123
1124 if (cd)
1125 {
1127 if (!root->subGrouping) cd->setSubGrouping(false);
1128 if (!root->spec.isForwardDecl())
1129 {
1130 if (cd->hasDocumentation())
1131 {
1132 addIncludeFile(cd,fd,root);
1133 }
1134 if (fd && root->section.isCompound())
1135 {
1136 AUTO_TRACE_ADD("Inserting class {} in file {} (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1137 cd->setFileDef(fd);
1138 fd->insertClass(cd);
1139 }
1140 }
1141 addClassToGroups(root,cd);
1143 cd->setRefItems(root->sli);
1144 cd->setRequirementReferences(root->rqli);
1145 }
1146}
1147
1148//----------------------------------------------------------------------
1149// build a list of all classes mentioned in the documentation
1150// and all classes that have a documentation block before their definition.
1151static void buildClassList(const Entry *root)
1152{
1153 if ((root->section.isCompound() || root->section.isObjcImpl()) && !root->name.empty())
1154 {
1155 AUTO_TRACE();
1156 addClassToContext(root);
1157 }
1158 for (const auto &e : root->children()) buildClassList(e.get());
1159}
1160
1161static void buildClassDocList(const Entry *root)
1162{
1163 if ((root->section.isCompoundDoc()) && !root->name.empty())
1164 {
1165 AUTO_TRACE();
1166 addClassToContext(root);
1167 }
1168 for (const auto &e : root->children()) buildClassDocList(e.get());
1169}
1170
1171//----------------------------------------------------------------------
1172// build a list of all classes mentioned in the documentation
1173// and all classes that have a documentation block before their definition.
1174
1175static void addConceptToContext(const Entry *root)
1176{
1177 AUTO_TRACE();
1178 FileDef *fd = root->fileDef();
1179
1180 DString scName;
1181 if (root->parent()->section.isScope())
1182 {
1183 scName=root->parent()->name;
1184 }
1185
1186 // name with scope (if not present already)
1187 DString qualifiedName = root->name;
1188 if (!scName.empty() && !leftScopeMatch(qualifiedName,scName))
1189 {
1190 qualifiedName.prepend(scName+"::");
1191 }
1192
1193 // see if we already found the concept before
1194 ConceptDefMutable *cd = getConceptMutable(qualifiedName);
1195
1196 AUTO_TRACE_ADD("Found concept with name '{}' (qualifiedName='{}')", cd ? cd->name() : root->name, qualifiedName);
1197
1198 if (cd)
1199 {
1200 qualifiedName=cd->name();
1201 AUTO_TRACE_ADD("Existing concept '{}'",cd->name());
1202
1203 cd->setDocumentation(root->doc,root->docFile,root->docLine);
1204 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1205
1206 addIncludeFile(cd,fd,root);
1207 }
1208 else // new concept
1209 {
1210 DString className;
1211 DString namespaceName;
1212 extractNamespaceName(qualifiedName,className,namespaceName);
1213
1214 AUTO_TRACE_ADD("New concept: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1215 qualifiedName,namespaceName,className,root->brief,root->doc);
1216
1217 DString tagName;
1218 DString refFileName;
1219 const TagInfo *tagInfo = root->tagInfo();
1220 if (tagInfo)
1221 {
1222 tagName = tagInfo->tagName;
1223 refFileName = tagInfo->fileName;
1224 if (qualifiedName.find("::")!=DString::npos)
1225 // symbols imported via tag files may come without the parent scope,
1226 // so we artificially create it here
1227 {
1228 buildScopeFromQualifiedName(qualifiedName,root->lang,tagInfo);
1229 }
1230 }
1231 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(qualifiedName,root->tArgLists);
1232 // add concept to the list
1234 Doxygen::conceptLinkedMap->add(qualifiedName,
1235 createConceptDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1236 qualifiedName,tagName,refFileName)));
1237 if (cd)
1238 {
1239 AUTO_TRACE_ADD("New concept '{}' #tArgLists={} tagInfo={}",
1240 qualifiedName,root->tArgLists.size(),fmt::ptr(tagInfo));
1241 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1242 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1243 cd->setLanguage(root->lang);
1244 cd->setId(root->id);
1245 cd->setHidden(root->hidden);
1246 cd->setGroupId(root->mGrpId);
1247 if (tArgList)
1248 {
1249 cd->setTemplateArguments(*tArgList);
1250 }
1251 cd->setInitializer(root->initializer.str());
1252 // file definition containing the class cd
1253 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1254 cd->setBodyDef(fd);
1256 cd->setRefItems(root->sli);
1257 cd->setRequirementReferences(root->rqli);
1258 addIncludeFile(cd,fd,root);
1259
1260 // also add namespace to the correct structural context
1261 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,qualifiedName,nullptr,tagInfo);
1263 {
1265 if (dm)
1266 {
1267 dm->addInnerCompound(cd);
1268 }
1269 cd->setOuterScope(d);
1270 }
1271 for (const auto &ce : root->children())
1272 {
1273 //printf("Concept %s has child %s\n",qPrint(root->name),qPrint(ce->section.to_string()));
1274 if (ce->section.isConceptDocPart())
1275 {
1276 cd->addSectionsToDefinition(ce->anchors);
1277 cd->setRefItems(ce->sli);
1278 cd->setRequirementReferences(ce->rqli);
1279 if (!ce->brief.empty())
1280 {
1281 cd->addDocPart(ce->brief,ce->startLine,ce->startColumn);
1282 //printf(" brief=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->brief),ce->startLine,ce->startColumn);
1283 }
1284 if (!ce->doc.empty())
1285 {
1286 cd->addDocPart(ce->doc,ce->startLine,ce->startColumn);
1287 //printf(" doc=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->doc),ce->startLine,ce->startColumn);
1288 }
1289 }
1290 else if (ce->section.isConceptCodePart())
1291 {
1292 cd->addCodePart(ce->initializer.str(),ce->startLine,ce->startColumn);
1293 //printf(" code=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->initializer.str()),ce->startLine,ce->startColumn);
1294 }
1295 }
1296 }
1297 else
1298 {
1299 AUTO_TRACE_ADD("Concept '{}' not added, already exists (as alias)", qualifiedName);
1300 }
1301 }
1302
1303 if (cd)
1304 {
1306 for (const auto &ce : root->children())
1307 {
1308 if (ce->section.isConceptDocPart())
1309 {
1310 cd->addSectionsToDefinition(ce->anchors);
1311 }
1312 }
1313 if (fd)
1314 {
1315 AUTO_TRACE_ADD("Inserting concept '{}' in file '{}' (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1316 cd->setFileDef(fd);
1317 fd->insertConcept(cd);
1318 }
1319 addConceptToGroups(root,cd);
1321 cd->setRefItems(root->sli);
1322 cd->setRequirementReferences(root->rqli);
1323 }
1324}
1325
1326static void findModuleDocumentation(const Entry *root)
1327{
1328 if (root->section.isModuleDoc())
1329 {
1330 AUTO_TRACE();
1332 }
1333 for (const auto &e : root->children()) findModuleDocumentation(e.get());
1334}
1335
1336static void buildConceptList(const Entry *root)
1337{
1338 if (root->section.isConcept())
1339 {
1340 AUTO_TRACE();
1341 addConceptToContext(root);
1342 }
1343 for (const auto &e : root->children()) buildConceptList(e.get());
1344}
1345
1346static void buildConceptDocList(const Entry *root)
1347{
1348 if (root->section.isConceptDoc())
1349 {
1350 AUTO_TRACE();
1351 addConceptToContext(root);
1352 }
1353 for (const auto &e : root->children()) buildConceptDocList(e.get());
1354}
1355
1356// This routine is to allow @ingroup X @{ concept A; concept B; @} to work
1357// (same also works for variable and functions because of logic in MemberGroup::insertMember)
1359{
1360 AUTO_TRACE();
1361 for (const auto &cd : *Doxygen::conceptLinkedMap)
1362 {
1363 if (cd->groupId()!=DOX_NOGROUP)
1364 {
1365 for (const auto &ocd : *Doxygen::conceptLinkedMap)
1366 {
1367 if (cd!=ocd && cd->groupId()==ocd->groupId() &&
1368 !cd->partOfGroups().empty() && ocd->partOfGroups().empty())
1369 {
1370 ConceptDefMutable *ocdm = toConceptDefMutable(ocd.get());
1371 if (ocdm)
1372 {
1373 for (const auto &gd : cd->partOfGroups())
1374 {
1375 if (gd)
1376 {
1377 AUTO_TRACE_ADD("making concept '{}' part of group '{}'",ocdm->name(),gd->name());
1378 ocdm->makePartOfGroup(gd);
1379 gd->addConcept(ocd.get());
1380 }
1381 }
1382 }
1383 }
1384 }
1385 }
1386 }
1387}
1388
1389//----------------------------------------------------------------------
1390
1392{
1393 ClassDefSet visitedClasses;
1394
1395 bool done=false;
1396 //int iteration=0;
1397 while (!done)
1398 {
1399 done=true;
1400 //++iteration;
1401 struct ClassAlias
1402 {
1403 ClassAlias(const DString &name,std::unique_ptr<ClassDef> cd,DefinitionMutable *ctx) :
1404 aliasFullName(name),aliasCd(std::move(cd)), aliasContext(ctx) {}
1405 DString aliasFullName;
1406 std::unique_ptr<ClassDef> aliasCd;
1407 DefinitionMutable *aliasContext;
1408 };
1409 std::vector<ClassAlias> aliases;
1410 for (const auto &icd : *Doxygen::classLinkedMap)
1411 {
1412 ClassDefMutable *cd = toClassDefMutable(icd.get());
1413 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1414 {
1415 DString name = stripAnonymousNamespaceScope(icd->name());
1416 //printf("processing=%s, iteration=%d\n",qPrint(cd->name()),iteration);
1417 // also add class to the correct structural context
1419 name,icd->getFileDef(),nullptr);
1420 if (d)
1421 {
1422 //printf("****** adding %s to scope %s in iteration %d\n",qPrint(cd->name()),qPrint(d->name()),iteration);
1424 if (dm)
1425 {
1426 dm->addInnerCompound(cd);
1427 }
1428 cd->setOuterScope(d);
1429
1430 // for inline namespace add an alias of the class to the outer scope
1432 {
1434 //printf("nd->isInline()=%d\n",nd->isInline());
1435 if (nd && nd->isInline())
1436 {
1437 d = d->getOuterScope();
1438 if (d)
1439 {
1440 dm = toDefinitionMutable(d);
1441 if (dm)
1442 {
1443 auto aliasCd = createClassDefAlias(d,cd);
1444 DString aliasFullName = d->qualifiedName()+"::"+aliasCd->localName();
1445 aliases.emplace_back(aliasFullName,std::move(aliasCd),dm);
1446 //printf("adding %s to %s as %s\n",qPrint(aliasCd->name()),qPrint(d->name()),qPrint(aliasFullName));
1447 }
1448 }
1449 }
1450 else
1451 {
1452 break;
1453 }
1454 }
1455
1456 visitedClasses.insert(icd.get());
1457 done=false;
1458 }
1459 //else
1460 //{
1461 // printf("****** ignoring %s: scope not (yet) found in iteration %d\n",qPrint(cd->name()),iteration);
1462 //}
1463 }
1464 }
1465 // add aliases
1466 for (auto &alias : aliases)
1467 {
1468 ClassDef *aliasCd = Doxygen::classLinkedMap->add(alias.aliasFullName,std::move(alias.aliasCd));
1469 if (aliasCd)
1470 {
1471 alias.aliasContext->addInnerCompound(aliasCd);
1472 }
1473 }
1474 }
1475
1476 //give warnings for unresolved compounds
1477 for (const auto &icd : *Doxygen::classLinkedMap)
1478 {
1479 ClassDefMutable *cd = toClassDefMutable(icd.get());
1480 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1481 {
1483 /// create the scope artificially
1484 // anyway, so we can at least relate scopes properly.
1485 Definition *d = buildScopeFromQualifiedName(name,cd->getLanguage(),nullptr);
1486 if (d && d!=cd && !cd->getDefFileName().empty())
1487 // avoid recursion in case of redundant scopes, i.e: namespace N { class N::C {}; }
1488 // for this case doxygen assumes the existence of a namespace N::N in which C is to be found!
1489 // also avoid warning for stuff imported via a tagfile.
1490 {
1492 if (dm)
1493 {
1494 dm->addInnerCompound(cd);
1495 }
1496 cd->setOuterScope(d);
1497 // A specialization can only be written for a template that has been declared before, so a missing
1498 // scope means that declaration is outside the input - e.g. `template<> struct std::hash<MyClass>`.
1499 if (cd->localName().find('<')==DString::npos)
1500 {
1501 warn(cd->getDefFileName(),cd->getDefLine(),
1502 "Incomplete input: scope for class {} not found!{}",name,
1503 name.startsWith("std::") ? " Try enabling BUILTIN_STL_SUPPORT." : ""
1504 );
1505 }
1506 }
1507 }
1508 }
1509}
1510
1512{
1513 //bool inlineGroupedClasses = Config_getBool(INLINE_GROUPED_CLASSES);
1514 //if (!inlineGroupedClasses) return;
1515 //printf("** distributeClassGroupRelations()\n");
1516
1517 ClassDefSet visitedClasses;
1518 for (const auto &cd : *Doxygen::classLinkedMap)
1519 {
1520 //printf("Checking %s\n",qPrint(cd->name()));
1521 // distribute the group to nested classes as well
1522 if (visitedClasses.find(cd.get())==visitedClasses.end() && !cd->partOfGroups().empty())
1523 {
1524 //printf(" Candidate for merging\n");
1525 GroupDef *gd = cd->partOfGroups().front();
1526 for (auto &ncd : cd->getClasses())
1527 {
1529 if (ncdm && ncdm->partOfGroups().empty())
1530 {
1531 //printf(" Adding %s to group '%s'\n",qPrint(ncd->name()),
1532 // gd->groupTitle());
1533 ncdm->makePartOfGroup(gd);
1534 gd->addClass(ncdm);
1535 }
1536 }
1537 visitedClasses.insert(cd.get()); // only visit every class once
1538 }
1539 }
1540}
1541
1542//----------------------------------------------------------------------
1543
1544template<typename Container>
1546 const Container *cd,
1547 const MemberDef *enumTypeMember,
1548 MemberListType mlFilter)
1549{
1550 if (md && md->isEnumerate() && md->name().startsWith("@")) // anonymous enum type
1551 {
1552 MemberList *eiml = cd->getMemberList(mlFilter);
1553 if (eiml)
1554 {
1555 for (const auto &eimd : *eiml)
1556 {
1557 DString vtype = eimd->typeString();
1558 if (vtype.find(md->name())!=DString::npos)
1559 {
1561 if (mimd)
1562 {
1563 mimd->setAnonymousEnumType(enumTypeMember);
1564 break;
1565 }
1566 }
1567 }
1568 }
1569 }
1570}
1571
1572static ClassDefMutable *createTagLessInstance(const Definition *root,const ClassDef *templ,const DString &fieldName,bool hasNamedVariable)
1573{
1574 DString fullName;
1575 if (Config_getEnum(TAGLESS_NAMESTYLE)==TAGLESS_NAMESTYLE_t::flat)
1576 {
1577 // "flat" style: refer to the anonymous compound via its flattened scope and
1578 // member names, e.g. 'struct A { struct { int x; } y; };' yields 'A.y'.
1579 // This matches the pre-1.18 naming and is friendlier for consumers of the
1580 // XML output that cannot handle type names starting with '[' (e.g. mkdoxy).
1581 fullName = removeAnonymousScopes(templ->name());
1582 if (fullName.endsWith("::")) fullName = fullName.left(fullName.length()-2);
1583 // add the field name as a suffix to make the name unique again; the
1584 // separator is only added when there is a flattened scope left (a
1585 // file-scope anonymous compound has none and would otherwise get a
1586 // leading '.').
1587 if (!fullName.empty()) fullName += ".";
1588 fullName += fieldName;
1589 }
1590 else // "marker" style (the default)
1591 {
1592 DString n = templ->name();
1593 // replace e.g. X::@1343:@4343::Y -> X::[struct]::Y
1594 if (size_t sn = n.find('@'); sn!=DString::npos)
1595 {
1596 const char *p = n.data()+sn;
1597 char c;
1598 while ((c=*p))
1599 {
1600 if (!isdigit(c) && c!='@' && c!=':') break;
1601 p++;
1602 }
1603 n = n.left(sn)+"["+templ->compoundTypeString().str()+"]"+p;
1604 }
1605 // add field name to the class name to make it unique again, e.g. X::[struct]::Y.m
1606 fullName = n+"."+fieldName;
1607 }
1608
1609 //printf("** adding class %s based on %s in %s\n",qPrint(fullName),qPrint(templ->name()),qPrint(root->name()));
1611 Doxygen::classLinkedMap->add(fullName,
1613 templ->getDefLine(),
1614 templ->getDefColumn(),
1615 fullName,
1616 templ->compoundType())));
1617 if (cd)
1618 {
1619 //printf("cd->name()=%s displayName=%s\n",qPrint(cd->name()),qPrint(cd->displayName()));
1620 cd->setDocumentation(templ->documentation(),templ->docFile(),templ->docLine()); // copy docs to definition
1621 cd->setBriefDescription(templ->briefDescription(),templ->briefFile(),templ->briefLine());
1622 cd->setLanguage(templ->getLanguage());
1623 cd->setBodySegment(templ->getDefLine(),templ->getStartBodyLine(),templ->getEndBodyLine());
1624 cd->setBodyDef(templ->getBodyDef());
1625
1626 if (hasNamedVariable)
1627 {
1628 cd->setHasNamedVariable(true);
1629 if (root!=Doxygen::globalScope)
1630 {
1631 DefinitionMutable *outerScope = toDefinitionMutable(const_cast<Definition*>(root));
1632 if (root && root->definitionType()==Definition::TypeFile)
1633 {
1634 FileDef *fd = toFileDef(const_cast<Definition*>(root));
1635 fd->insertClass(cd);
1636 cd->setFileDef(fd);
1638 }
1639 else if (outerScope)
1640 {
1641 outerScope->addInnerCompound(cd);
1642 cd->setOuterScope(const_cast<Definition*>(root));
1643 }
1644 }
1645 }
1646 else
1647 {
1648 cd->setArtificial(true);
1649 }
1650
1651 for (auto &gd : root->partOfGroups())
1652 {
1653 cd->makePartOfGroup(gd);
1654 gd->addClass(cd);
1655 }
1656
1657 auto addMember = [&](const MemberDef *md) -> MemberDefMutable*
1658 {
1659 auto newMd = createMemberDef(md->getDefFileName(),md->getDefLine(),md->getDefColumn(),
1660 md->typeString(),md->name(),md->argsString(),md->excpString(),
1661 md->protection(),md->virtualness(),md->isStatic(),Relationship::Member,
1662 md->memberType(),
1663 ArgumentList(),ArgumentList(),"");
1664 MemberDefMutable *imd = toMemberDefMutable(newMd.get());
1665 imd->setDefinition(md->definition());
1666 imd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
1667 imd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
1668 imd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
1669 imd->setMemberSpecifiers(md->getMemberSpecifiers());
1670 imd->setId(md->id());
1671 imd->addQualifiers(md->getQualifiers());
1672 imd->setVhdlSpecifiers(md->getVhdlSpecifiers());
1673 imd->setMemberGroupId(md->getMemberGroupId());
1674 imd->setInitializer(md->initializer());
1675 imd->setRequiresClause(md->requiresClause());
1676 imd->setMaxInitLines(md->initializerLines());
1677 imd->setBitfields(md->bitfieldString());
1678 imd->setLanguage(md->getLanguage());
1679 imd->setAnchor();
1680 MemberName *mn = Doxygen::memberNameLinkedMap->add(md->name());
1681 mn->push_back(std::move(newMd));
1682 return imd;
1683 };
1684
1685 static auto memberListPairs = std::array<std::pair<MemberListType,MemberListType>,3>{{
1686 { MemberListType::PubAttribs(), MemberListType::PubTypes() },
1687 { MemberListType::ProAttribs(), MemberListType::ProTypes() },
1688 { MemberListType::PriAttribs(), MemberListType::PriTypes() }
1689 }};
1690 for (auto [attribs,types] : memberListPairs)
1691 {
1692 MemberList *ml = templ->getMemberList(attribs);
1693 if (ml)
1694 {
1695 for (const auto &md : *ml)
1696 {
1697 //printf(" Member attribute %s def=%s\n",qPrint(md->name()),qPrint(md->definition()));
1698 MemberDefMutable *imd = addMember(md);
1699 if (!hasNamedVariable && root) // we have an anonymous struct/union without associated member variable,
1700 // so we also need to create a new member to show in the outer scope's detailed section.
1701 // e.g. struct S { union { int a; int b; }; }; needs S::a and S::b as members
1702 {
1703 MemberDefMutable *dimd = addMember(md);
1704 // link the detailed member to the original member
1707 if (root && root->definitionType()==Definition::TypeClass && dimd)
1708 {
1709 ClassDefMutable *cm = toClassDefMutable(const_cast<Definition*>(root));
1710 if (cm)
1711 {
1712 //printf(" Member attribute %s added to class %s\n",qPrint(md->name()),qPrint(cm->name()));
1713 dimd->setMemberClass(cm);
1714 cm->insertMember(dimd);
1715 associateVariableWithAnonymousEnumType(md,cm,dimd,attribs);
1716 }
1717 }
1718 else if (root && root->definitionType()==Definition::TypeFile && dimd)
1719 {
1720 FileDef *fd = toFileDef(const_cast<Definition*>(root));
1721 if (fd)
1722 {
1723 //printf(" Member attribute %s added to file %s\n",qPrint(md->name()),qPrint(fd->name()));
1724 dimd->setOuterScope(fd);
1725 dimd->setFileDef(fd);
1726 fd->insertMember(dimd);
1727 associateVariableWithAnonymousEnumType(md,fd,dimd,attribs);
1728 }
1729 }
1730 else if (root && root->definitionType()==Definition::TypeNamespace && dimd)
1731 {
1732 NamespaceDefMutable *ndm = toNamespaceDefMutable(const_cast<Definition*>(root));
1733 if (ndm)
1734 {
1735 //printf(" Member attribute %s added to namespace %s\n",qPrint(md->name()),qPrint(ndm->name()));
1736 dimd->setOuterScope(ndm);
1737 dimd->setNamespace(ndm);
1738 ndm->insertMember(dimd);
1739 associateVariableWithAnonymousEnumType(md,ndm,dimd,attribs);
1740 }
1741 }
1742 }
1743
1744 imd->setMemberClass(cd);
1745 cd->insertMember(imd);
1746 associateVariableWithAnonymousEnumType(md,cd,imd,attribs);
1747 }
1748 }
1749 ml = templ->getMemberList(types);
1750 if (ml)
1751 {
1752 for (const auto &md : *ml)
1753 {
1754 //printf(" Member type %s def=%s\n",qPrint(md->name()),qPrint(md->definition()));
1755 MemberDefMutable *mdm = addMember(md);
1756 mdm->setMemberClass(cd);
1757 cd->insertMember(mdm);
1759 associateVariableWithAnonymousEnumType(md,cd,mdm,attribs);
1760 if (md->isEnumerate() && md->name().startsWith("@")) // anonymous enum type
1761 {
1762 for (const auto &emd : md->enumFieldList())
1763 {
1764 //printf(" enum field %s\n",qPrint(emd->name()));
1765 MemberDefMutable *emdm = addMember(emd);
1766 mdm->insertEnumField(emdm);
1767 emdm->setEnumScope(md);
1768 emdm->setMemberClass(cd);
1769 cd->insertMember(emdm);
1771 associateVariableWithAnonymousEnumType(md,cd,emdm,attribs);
1772 }
1773 }
1774 }
1775 }
1776 }
1777 }
1778 return cd;
1779}
1780
1781template<typename Container, typename TagContainer>
1782static void processTagLessClasses(const Definition *root,
1783 const Container *def,
1784 const TagContainer *tagParent,
1785 MemberListType varFilter,
1786 MemberListType typeFilter,
1787 const DString &prefix,int count);
1788
1789template<typename Container, typename TagContainer>
1791 const Container *def,
1792 const TagContainer *tagParent,
1793 MemberListType varFilter,
1794 MemberListType typeFilter,
1795 const DString &prefix,int count)
1796{
1797 AUTO_TRACE("count={} name={}\n",count,def->name());
1798 if (tagParent)
1799 {
1800 MemberList *ml = def->getMemberList(varFilter);
1801 if (ml)
1802 {
1803 int pos=0;
1804 // make a copy of the member list, since we may add new members to the list while iterating over it
1805 std::vector<MemberDef *> members;
1806 for (const auto &md : *ml)
1807 {
1808 members.push_back(md);
1809 }
1810 for (const auto &md : members)
1811 {
1812 MemberDefMutable *pmdm = nullptr;
1813 if (tagParent!=def)
1814 {
1815 // associate the variable of the anonymous type with the type member
1816 MemberList *pml = tagParent->getMemberList(varFilter);
1817 if (pml)
1818 {
1819 for (const auto &pmd : *pml)
1820 {
1821 if (pmd->name()==md->name())
1822 {
1823 pmdm = toMemberDefMutable(pmd);
1824 }
1825 }
1826 }
1827 }
1828 else
1829 {
1830 pmdm = toMemberDefMutable(md);
1831 }
1832 bool hasNamedVariable = pmdm==nullptr || !pmdm->isAnonymous();
1833
1834 DString type = md->typeString();
1835 //printf(" member %s: type='%s' outerScope='%s'\n",qPrint(md->name()),qPrint(type),qPrint(md->getOuterScope()?md->getOuterScope()->name():"<null>"));
1836 if ((def->definitionType()!=Definition::TypeFile || md->getOuterScope()==Doxygen::globalScope) && // parse namespace members only if def is a namespace
1837 (type.find("::@")!=DString::npos || type.find(" @")!=DString::npos)) // member of tag less struct/union
1838 {
1839 // make a copy of the class list, since we may add new members to the list while iterating over it
1840 std::vector<const ClassDef *> candidates;
1841 for (const auto &icd : def->getClasses())
1842 {
1843 candidates.push_back(icd);
1844 }
1845 for (const auto &icd : candidates)
1846 {
1847 //printf(" comparing '%s'<->'%s'\n",qPrint(type),qPrint(icd->name()));
1848 if (type.find(icd->name())!=DString::npos) // matching tag less struct/union
1849 {
1850 DString name = md->name();
1851 if (md->isAnonymous()) name = "__unnamed" + DString().setNum(pos++)+"__"; // TODO: remove dead code
1852 if (!prefix.empty()) name.prepend(prefix+".");
1853 //printf(" found %s in scope %s\n",qPrint(name),qPrint(def->name()));
1854 ClassDefMutable *ncd = createTagLessInstance(root,icd,name,hasNamedVariable);
1855 if (ncd)
1856 {
1857 processTagLessClasses(ncd,icd,ncd,MemberListType::PubAttribs(),MemberListType::PubTypes(),name,count+1);
1858 //printf(" addTagged %s to %s\n",qPrint(ncd->name()),qPrint(tagParent->name()));
1859 ncd->setTaglessParent(icd);
1860 if (pmdm)
1861 {
1862 pmdm->setClassDefOfAnonymousType(ncd);
1863 }
1864 }
1865 }
1866 else
1867 {
1868 //printf(" no match for %s in %s\n",qPrint(icd->name()),qPrint(type));
1869 }
1870 }
1871 }
1872 }
1873 }
1874 // associate the variable of the anonymous enum type with the type member
1875 ml = def->getMemberList(typeFilter);
1876 if (ml)
1877 {
1878 for (const auto &md : *ml)
1879 {
1880 MemberListType mlFilter = def->definitionType()==Definition::TypeClass ? varFilter : MemberListType::DecVarMembers();
1881 associateVariableWithAnonymousEnumType(md,def,md,mlFilter);
1882 }
1883 }
1884 }
1885}
1886
1887/** Look through the members of class \a cd and its public members.
1888 * If there is a member m of a tag less struct/union,
1889 * then we create a duplicate of the struct/union with the name of the
1890 * member to identify it.
1891 * So if cd has name S, then the tag less struct/union will get name S.m
1892 * Since tag less structs can be nested we need to call this function
1893 * recursively. Later on we need to patch the member types so we keep
1894 * track of the hierarchy of classes we create.
1895 */
1896template<typename Container, typename TagContainer>
1897static void processTagLessClasses(const Definition *root,
1898 const Container *def,
1899 const TagContainer *tagParent,
1900 MemberListType varFilter,
1901 MemberListType typeFilter,
1902 const DString &prefix,int count)
1903{
1904 if (varFilter==MemberListType::PubAttribs()) // also handle protected and private members
1905 {
1906 processTagLessClassesForList(root,def,tagParent,MemberListType::PubAttribs(),MemberListType::PubTypes(),prefix,count);
1907 processTagLessClassesForList(root,def,tagParent,MemberListType::ProAttribs(),MemberListType::ProTypes(),prefix,count);
1908 processTagLessClassesForList(root,def,tagParent,MemberListType::PriAttribs(),MemberListType::PriTypes(),prefix,count);
1909 }
1910 else
1911 {
1912 processTagLessClassesForList(root,def,tagParent,varFilter,typeFilter,prefix,count);
1913 }
1914}
1915
1916template<typename Container>
1917static void findTagLessClasses(std::set<const Definition *> &candidates,const Container *cd)
1918{
1919 for (const auto &icd : cd->getClasses())
1920 {
1921 if (icd->name().find('@')==DString::npos) // process all non-anonymous inner classes
1922 {
1923 findTagLessClasses(candidates,icd);
1924 }
1925 }
1926
1927 candidates.insert(cd);
1928}
1929
1931{
1932 std::set<const Definition *> candidates;
1933 for (auto &cd : *Doxygen::classLinkedMap)
1934 {
1935 Definition *scope = cd->getOuterScope();
1936 //printf(" scope=%s for class %s\n",qPrint(scope?scope->name():"<null>"),qPrint(cd->name()));
1937 if (scope && scope->definitionType()==Definition::TypeNamespace) // class that is not nested
1938 {
1939 const NamespaceDef *nd = toNamespaceDef(scope);
1940 if (nd && nd==Doxygen::globalScope) // class at global namespace
1941 {
1942 const FileDef *fd = cd->getFileDef();
1943 if (fd)
1944 {
1945 findTagLessClasses(candidates,fd);
1946 }
1947 }
1948 else if (nd) // class in a namespace
1949 {
1950 findTagLessClasses(candidates,nd);
1951 }
1952 }
1953 }
1954
1955 // since processTagLessClasses is potentially adding classes to Doxygen::classLinkedMap
1956 // we need to call it outside of the loop above, otherwise the iterator gets invalidated!
1957 for (const auto &d : candidates)
1958 {
1959 //printf("------ processing tag-less classes for %s\n",qPrint(d->name()));
1960 if (d->definitionType()==Definition::TypeNamespace)
1961 {
1962 const NamespaceDef *nd = toNamespaceDef(d);
1963 processTagLessClasses(nd,nd,nd,MemberListType::DecVarMembers(),MemberListType::DecEnumMembers(),"",0);
1964 }
1965 else if (d->definitionType()==Definition::TypeFile)
1966 {
1967 const FileDef *fd = toFileDef(d);
1968 processTagLessClasses(fd,fd,fd,MemberListType::DecVarMembers(),MemberListType::DecEnumMembers(),"",0);
1969 }
1970 else if (d->definitionType()==Definition::TypeClass)
1971 {
1972 const ClassDef *cd = toClassDef(d);
1973 processTagLessClasses(cd,cd,cd,MemberListType::PubAttribs(),MemberListType::PubTypes(),"",0);
1974 }
1975 }
1976}
1977
1978
1979//----------------------------------------------------------------------
1980// build a list of all namespaces mentioned in the documentation
1981// and all namespaces that have a documentation block before their definition.
1982static void buildNamespaceList(const Entry *root)
1983{
1984 if (
1985 (root->section.isNamespace() ||
1986 root->section.isNamespaceDoc() ||
1987 root->section.isPackageDoc()
1988 ) &&
1989 !root->name.empty()
1990 )
1991 {
1992 AUTO_TRACE("name={}",root->name);
1993
1994 DString fName = root->name;
1995 if (root->section.isPackageDoc())
1996 {
1997 fName=substitute(fName,".","::");
1998 }
1999
2000 DString fullName = stripAnonymousNamespaceScope(fName);
2001 if (!fullName.empty())
2002 {
2003 AUTO_TRACE_ADD("Found namespace {} in {} at line {}",root->name,root->fileName,root->startLine);
2005 if (ndi) // existing namespace
2006 {
2008 if (nd) // non-inline namespace
2009 {
2010 AUTO_TRACE_ADD("Existing namespace");
2011 nd->setDocumentation(root->doc,root->docFile,root->docLine);
2012 nd->setName(fullName); // change name to match docs
2014 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2015 if (nd->getLanguage()==SrcLangExt::Unknown)
2016 {
2017 nd->setLanguage(root->lang);
2018 }
2019 if (root->tagInfo()==nullptr && nd->isReference() && !(root->doc.empty() && root->brief.empty()))
2020 // if we previously found namespace nd in a tag file and now we find a
2021 // documented namespace with the same name in the project, then remove
2022 // the tag file reference
2023 {
2024 nd->setReference("");
2025 nd->setFileName(fullName);
2026 }
2027 nd->setMetaData(root->metaData);
2028
2029 // file definition containing the namespace nd
2030 FileDef *fd=root->fileDef();
2031 if (nd->isArtificial())
2032 {
2033 nd->setArtificial(false); // found namespace explicitly, so cannot be artificial
2034 nd->setDefFile(root->fileName,root->startLine,root->startColumn);
2035 }
2036 // insert the namespace in the file definition
2037 if (fd) fd->insertNamespace(nd);
2038 addNamespaceToGroups(root,nd);
2039 nd->setRefItems(root->sli);
2040 nd->setRequirementReferences(root->rqli);
2041 addIncludeFile(nd,fd,root);
2042 }
2043 }
2044 else // fresh namespace
2045 {
2046 DString tagName;
2047 DString tagFileName;
2048 const TagInfo *tagInfo = root->tagInfo();
2049 if (tagInfo)
2050 {
2051 tagName = tagInfo->tagName;
2052 tagFileName = tagInfo->fileName;
2053 }
2054 AUTO_TRACE_ADD("new namespace {} lang={} tagName={}",fullName,root->lang,tagName);
2055 // add namespace to the list
2057 Doxygen::namespaceLinkedMap->add(fullName,
2058 createNamespaceDef(tagInfo?tagName:root->fileName,root->startLine,
2059 root->startColumn,fullName,tagName,tagFileName,
2060 root->type,root->spec.isPublished())));
2061 if (nd)
2062 {
2063 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
2064 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2066 nd->setHidden(root->hidden);
2067 nd->setArtificial(root->artificial);
2068 nd->setLanguage(root->lang);
2069 nd->setId(root->id);
2070 nd->setMetaData(root->metaData);
2071 nd->setInline(root->spec.isInline());
2072 nd->setExported(root->exported);
2073
2074 addNamespaceToGroups(root,nd);
2075 nd->setRefItems(root->sli);
2076 nd->setRequirementReferences(root->rqli);
2077
2078 // file definition containing the namespace nd
2079 FileDef *fd=root->fileDef();
2080 // insert the namespace in the file definition
2081 if (fd) fd->insertNamespace(nd);
2082
2083 // the empty string test is needed for extract all case
2084 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2085 nd->insertUsedFile(fd);
2086 nd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2087 nd->setBodyDef(fd);
2088 addIncludeFile(nd,fd,root);
2089
2090 // also add namespace to the correct structural context
2091 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,fullName,nullptr,tagInfo);
2092 AUTO_TRACE_ADD("adding namespace {} to context {}",nd->name(),d ? d->name() : DString("<none>"));
2093 if (d==nullptr) // we didn't find anything, create the scope artificially
2094 // anyway, so we can at least relate scopes properly.
2095 {
2096 d = buildScopeFromQualifiedName(fullName,nd->getLanguage(),tagInfo);
2098 if (dm)
2099 {
2100 dm->addInnerCompound(nd);
2101 }
2102 nd->setOuterScope(d);
2103 // TODO: Due to the order in which the tag file is written
2104 // a nested class can be found before its parent!
2105 }
2106 else
2107 {
2109 if (dm)
2110 {
2111 dm->addInnerCompound(nd);
2112 }
2113 nd->setOuterScope(d);
2114 // in case of d is an inline namespace, alias insert nd in the part scope of d.
2116 {
2117 NamespaceDef *pnd = toNamespaceDef(d);
2118 if (pnd && pnd->isInline())
2119 {
2120 d = d->getOuterScope();
2121 if (d)
2122 {
2123 dm = toDefinitionMutable(d);
2124 if (dm)
2125 {
2126 auto aliasNd = createNamespaceDefAlias(d,nd);
2127 dm->addInnerCompound(aliasNd.get());
2128 DString aliasName = aliasNd->name();
2129 AUTO_TRACE_ADD("adding alias {} to {}",aliasName,d->name());
2130 Doxygen::namespaceLinkedMap->add(aliasName,std::move(aliasNd));
2131 }
2132 }
2133 else
2134 {
2135 break;
2136 }
2137 }
2138 else
2139 {
2140 break;
2141 }
2142 }
2143 }
2144 }
2145 }
2146 }
2147 }
2148 for (const auto &e : root->children()) buildNamespaceList(e.get());
2149}
2150
2151//----------------------------------------------------------------------
2152
2154 const DString &name)
2155{
2156 NamespaceDef *usingNd =nullptr;
2157 for (auto &und : unl)
2158 {
2159 DString uScope=und->name()+"::";
2160 usingNd = getResolvedNamespace(uScope+name);
2161 if (usingNd!=nullptr) break;
2162 }
2163 return usingNd;
2164}
2165
2166static void findUsingDirectives(const Entry *root)
2167{
2168 if (root->section.isUsingDir())
2169 {
2170 AUTO_TRACE("Found using directive {} at line {} of {}",root->name,root->startLine,root->fileName);
2171 DString name=substitute(root->name,".","::");
2172 if (name.endsWith("::"))
2173 {
2174 name=name.left(name.length()-2);
2175 }
2176 if (!name.empty())
2177 {
2178 NamespaceDef *usingNd = nullptr;
2179 NamespaceDefMutable *nd = nullptr;
2180 FileDef *fd = root->fileDef();
2181 DString nsName;
2182
2183 // see if the using statement was found inside a namespace or inside
2184 // the global file scope.
2185 if (root->parent() && root->parent()->section.isNamespace() &&
2186 (fd==nullptr || fd->getLanguage()!=SrcLangExt::Java) // not a .java file
2187 )
2188 {
2189 nsName=stripAnonymousNamespaceScope(root->parent()->name);
2190 if (!nsName.empty())
2191 {
2192 nd = getResolvedNamespaceMutable(nsName);
2193 }
2194 }
2195
2196 // find the scope in which the 'using' namespace is defined by prepending
2197 // the possible scopes in which the using statement was found, starting
2198 // with the most inner scope and going to the most outer scope (i.e.
2199 // file scope).
2200 int scopeOffset = static_cast<int>(nsName.length());
2201 do
2202 {
2203 DString scope=scopeOffset>0 ?
2204 nsName.left(scopeOffset)+"::" : DString();
2205 usingNd = getResolvedNamespace(scope+name);
2206 //printf("Trying with scope='%s' usingNd=%p\n",(scope+qPrint(name)),usingNd);
2207 if (scopeOffset==0)
2208 {
2209 scopeOffset=-1;
2210 }
2211 else
2212 {
2213 size_t o = nsName.rfind("::",scopeOffset-1);
2214 scopeOffset = o!=DString::npos ? static_cast<int>(o) : 0;
2215 }
2216 } while (scopeOffset>=0 && usingNd==nullptr);
2217
2218 if (usingNd==nullptr && nd) // not found, try used namespaces in this scope
2219 // or in one of the parent namespace scopes
2220 {
2221 const NamespaceDefMutable *pnd = nd;
2222 while (pnd && usingNd==nullptr)
2223 {
2224 // also try with one of the used namespaces found earlier
2226
2227 // goto the parent
2228 Definition *s = pnd->getOuterScope();
2230 {
2232 }
2233 else
2234 {
2235 pnd = nullptr;
2236 }
2237 }
2238 }
2239 if (usingNd==nullptr && fd) // still nothing, also try used namespace in the
2240 // global scope
2241 {
2242 usingNd = findUsedNamespace(fd->getUsedNamespaces(),name);
2243 }
2244
2245 //printf("%s -> %s\n",qPrint(name),usingNd?qPrint(usingNd->name()):"<none>");
2246
2247 // add the namespace the correct scope
2248 if (usingNd)
2249 {
2250 //printf("using fd=%p nd=%p\n",fd,nd);
2251 if (nd)
2252 {
2253 //printf("Inside namespace %s\n",qPrint(nd->name()));
2254 nd->addUsingDirective(usingNd);
2255 }
2256 else if (fd)
2257 {
2258 //printf("Inside file %s\n",qPrint(fd->name()));
2259 fd->addUsingDirective(usingNd);
2260 }
2261 }
2262 else // unknown namespace, but add it anyway.
2263 {
2264 AUTO_TRACE_ADD("new unknown namespace {} lang={} hidden={}",name,root->lang,root->hidden);
2265 // add namespace to the list
2268 createNamespaceDef(root->fileName,root->startLine,root->startColumn,name)));
2269 if (nd)
2270 {
2271 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
2272 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2274 nd->setHidden(root->hidden);
2275 nd->setArtificial(true);
2276 nd->setLanguage(root->lang);
2277 nd->setId(root->id);
2278 nd->setMetaData(root->metaData);
2279 nd->setInline(root->spec.isInline());
2280 nd->setExported(root->exported);
2281
2282 for (const Grouping &g : root->groups)
2283 {
2284 GroupDef *gd=nullptr;
2286 gd->addNamespace(nd);
2287 }
2288
2289 // insert the namespace in the file definition
2290 if (fd)
2291 {
2292 fd->insertNamespace(nd);
2293 fd->addUsingDirective(nd);
2294 }
2295
2296 // the empty string test is needed for extract all case
2297 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2298 nd->insertUsedFile(fd);
2299 nd->setRefItems(root->sli);
2300 nd->setRequirementReferences(root->rqli);
2301 }
2302 }
2303 }
2304 }
2305 for (const auto &e : root->children()) findUsingDirectives(e.get());
2306}
2307
2308//----------------------------------------------------------------------
2309
2310static void buildListOfUsingDecls(const Entry *root)
2311{
2312 if (root->section.isUsingDecl() &&
2313 !root->parent()->section.isCompound() // not a class/struct member
2314 )
2315 {
2316 DString name = substitute(root->name,".","::");
2317 g_usingDeclarations.insert(name.str());
2318 }
2319 for (const auto &e : root->children()) buildListOfUsingDecls(e.get());
2320}
2321
2322
2323static void findUsingDeclarations(const Entry *root,bool filterPythonPackages)
2324{
2325 if (root->section.isUsingDecl() &&
2326 !root->parent()->section.isCompound() && // not a class/struct member
2327 (!filterPythonPackages || (root->lang==SrcLangExt::Python && root->fileName.endsWith("__init__.py")))
2328 )
2329 {
2330 AUTO_TRACE("Found using declaration '{}' at line {} of {} inside section {}",
2331 root->name,root->startLine,root->fileName,root->parent()->section);
2332 if (!root->name.empty())
2333 {
2334 const Definition *usingDef = nullptr;
2335 NamespaceDefMutable *nd = nullptr;
2336 FileDef *fd = root->fileDef();
2337 DString scName;
2338
2339 // see if the using statement was found inside a namespace or inside
2340 // the global file scope.
2341 if (root->parent()->section.isNamespace())
2342 {
2343 scName=root->parent()->name;
2344 if (!scName.empty())
2345 {
2346 nd = getResolvedNamespaceMutable(scName);
2347 }
2348 }
2349
2350 // Assume the using statement was used to import a class.
2351 // Find the scope in which the 'using' namespace is defined by prepending
2352 // the possible scopes in which the using statement was found, starting
2353 // with the most inner scope and going to the most outer scope (i.e.
2354 // file scope).
2355
2356 DString name = substitute(root->name,".","::"); //Java/C# scope->internal
2357
2358 SymbolResolver resolver;
2359 const Definition *scope = nd;
2360 if (nd==nullptr) scope = fd;
2361 usingDef = resolver.resolveSymbol(scope,name);
2362
2363 //printf("usingDef(scope=%s,name=%s)=%s\n",qPrint(nd?nd->qualifiedName():""),qPrint(name),usingDef?qPrint(usingDef->qualifiedName()):"nullptr");
2364
2365 if (!usingDef)
2366 {
2367 usingDef = getClass(name); // try direct lookup, this is needed to get
2368 // builtin STL classes to properly resolve, e.g.
2369 // vector -> std::vector
2370 }
2371 if (!usingDef)
2372 {
2373 usingDef = Doxygen::hiddenClassLinkedMap->find(name); // check if it is already hidden
2374 }
2375#if 0
2376 if (!usingDef)
2377 {
2378 AUTO_TRACE_ADD("New using class '{}' (sec={})! #tArgLists={}",
2379 name,root->section,root->tArgLists.size());
2382 createClassDef( "<using>",1,1, name, ClassDef::Class)));
2383 if (usingCd)
2384 {
2385 usingCd->setArtificial(true);
2386 usingCd->setLanguage(root->lang);
2387 usingDef = usingCd;
2388 }
2389 }
2390#endif
2391 else
2392 {
2393 AUTO_TRACE_ADD("Found used type '{}' in scope='{}'",
2394 usingDef->name(), nd ? nd->name(): fd ? fd->name() : DString("<unknown>"));
2395 }
2396
2397 if (usingDef)
2398 {
2399 if (nd)
2400 {
2401 nd->addUsingDeclaration(usingDef);
2402 }
2403 else if (fd)
2404 {
2405 fd->addUsingDeclaration(usingDef);
2406 }
2407 }
2408 }
2409 }
2410 for (const auto &e : root->children()) findUsingDeclarations(e.get(),filterPythonPackages);
2411}
2412
2413//----------------------------------------------------------------------
2414
2416{
2417 root->commandOverrides.apply_callGraph ([&](bool b) { md->overrideCallGraph(b); });
2418 root->commandOverrides.apply_callerGraph ([&](bool b) { md->overrideCallerGraph(b); });
2419 root->commandOverrides.apply_referencedByRelation([&](bool b) { md->overrideReferencedByRelation(b); });
2420 root->commandOverrides.apply_referencesRelation ([&](bool b) { md->overrideReferencesRelation(b); });
2421 root->commandOverrides.apply_inlineSource ([&](bool b) { md->overrideInlineSource(b); });
2422 root->commandOverrides.apply_enumValues ([&](bool b) { md->overrideEnumValues(b); });
2423}
2424
2425//----------------------------------------------------------------------
2426
2428 const DString &fileName,const DString &memName)
2429{
2430 AUTO_TRACE("creating new member {} for class {}",memName,cd->name());
2431 const ArgumentList &templAl = md->templateArguments();
2432 const ArgumentList &al = md->argumentList();
2433 auto newMd = createMemberDef(
2434 fileName,root->startLine,root->startColumn,
2435 md->typeString(),memName,md->argsString(),
2436 md->excpString(),root->protection,root->virt,
2437 md->isStatic(),Relationship::Member,md->memberType(),
2438 templAl,al,root->metaData
2439 );
2440 auto newMmd = toMemberDefMutable(newMd.get());
2441 newMmd->setMemberClass(cd);
2442 cd->insertMember(newMd.get());
2443 if (!root->doc.empty() || !root->brief.empty())
2444 {
2445 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2446 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2447 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2448 }
2449 else
2450 {
2451 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2452 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2453 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2454 }
2455 newMmd->setDefinition(md->definition());
2456 applyMemberOverrideOptions(root,newMmd);
2457 newMmd->addQualifiers(root->qualifiers);
2458 newMmd->setBitfields(md->bitfieldString());
2459 newMmd->addSectionsToDefinition(root->anchors);
2460 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2461 newMmd->setBodyDef(md->getBodyDef());
2462 newMmd->setInitializer(md->initializer());
2463 newMmd->setRequiresClause(md->requiresClause());
2464 newMmd->setMaxInitLines(md->initializerLines());
2465 newMmd->setMemberGroupId(root->mGrpId);
2466 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2467 newMmd->setVhdlSpecifiers(md->getVhdlSpecifiers());
2468 newMmd->setLanguage(root->lang);
2469 newMmd->setId(root->id);
2471 mn->push_back(std::move(newMd));
2472}
2473
2474static std::unordered_map<std::string,std::vector<ClassDefMutable*>> g_usingClassMap;
2475
2476static void findUsingDeclImports(const Entry *root)
2477{
2478 if (root->section.isUsingDecl() &&
2479 root->parent()->section.isCompound() // in a class/struct member
2480 )
2481 {
2482 AUTO_TRACE("Found using declaration '{}' inside section {}", root->name, root->parent()->section);
2483 DString fullName=removeRedundantWhiteSpace(root->parent()->name);
2484 fullName=stripAnonymousNamespaceScope(fullName);
2485 fullName=stripTemplateSpecifiersFromScope(fullName);
2486 ClassDefMutable *cd = getClassMutable(fullName);
2487 if (cd)
2488 {
2489 AUTO_TRACE_ADD("found class '{}'",cd->name());
2490 size_t i=root->name.rfind("::");
2491 if (i!=DString::npos)
2492 {
2493 DString scope=root->name.left(i);
2494 DString memName=root->name.mid(i+2);
2495 SymbolResolver resolver;
2496 const ClassDef *bcd = resolver.resolveClass(cd,scope); // todo: file in fileScope parameter
2497 AUTO_TRACE_ADD("name={} scope={} bcd={}",scope,cd?cd->name():"<none>",bcd?bcd->name():"<none>");
2498 if (bcd && bcd!=cd)
2499 {
2500 AUTO_TRACE_ADD("found class '{}' memName='{}'",bcd->name(),memName);
2502 const MemberNameInfo *mni = mnlm.find(memName);
2503 if (mni)
2504 {
2505 for (auto &mi : *mni)
2506 {
2507 const MemberDef *md = mi->memberDef();
2508 if (md && md->protection()!=Protection::Private)
2509 {
2510 AUTO_TRACE_ADD("found member '{}'",mni->memberName());
2511 DString fileName = root->fileName;
2512 if (fileName.empty() && root->tagInfo())
2513 {
2514 fileName = root->tagInfo()->tagName;
2515 }
2516 if (!cd->containsOverload(md))
2517 {
2518 createUsingMemberImportForClass(root,cd,md,fileName,memName);
2519 // also insert the member into copies of the class
2520 auto it = g_usingClassMap.find(cd->qualifiedName().str());
2521 if (it != g_usingClassMap.end())
2522 {
2523 for (const auto &copyCd : it->second)
2524 {
2525 createUsingMemberImportForClass(root,copyCd,md,fileName,memName);
2526 }
2527 }
2528 }
2529 }
2530 }
2531 }
2532 }
2533 }
2534 }
2535 }
2536 else if (root->section.isUsingDecl() &&
2537 (root->parent()->section.isNamespace() || root->parent()->section.isEmpty()) && // namespace or global member
2538 root->lang==SrcLangExt::Cpp // do we also want this for e.g. Fortran? (see test case 095)
2539 )
2540 {
2541 AUTO_TRACE("Found using declaration '{}' inside section {}", root->name, root->parent()->section);
2542 Definition *scope = nullptr;
2543 NamespaceDefMutable *nd = nullptr;
2544 FileDef *fd = root->parent()->fileDef();
2545 if (!root->parent()->name.empty())
2546 {
2547 DString fullName=removeRedundantWhiteSpace(root->parent()->name);
2548 fullName=stripAnonymousNamespaceScope(fullName);
2550 scope = nd;
2551 }
2552 else
2553 {
2554 scope = fd;
2555 }
2556 if (scope)
2557 {
2558 AUTO_TRACE_ADD("found scope '{}'",scope->name());
2559 SymbolResolver resolver;
2560 const Definition *def = resolver.resolveSymbol(root->name.startsWith("::") ? nullptr : scope,root->name);
2561 if (def && def->definitionType()==Definition::TypeMember)
2562 {
2563 size_t i=root->name.rfind("::");
2564 DString memName;
2565 if (i!=DString::npos)
2566 {
2567 memName = root->name.right(root->name.length()-i-2);
2568 }
2569 else
2570 {
2571 memName = root->name;
2572 }
2573 const MemberDef *md = toMemberDef(def);
2574 AUTO_TRACE_ADD("found member '{}' for name '{}'",md->qualifiedName(),root->name);
2575 DString fileName = root->fileName;
2576 if (fileName.empty() && root->tagInfo())
2577 {
2578 fileName = root->tagInfo()->tagName;
2579 }
2580 const ArgumentList &templAl = md->templateArguments();
2581 const ArgumentList &al = md->argumentList();
2582
2583 auto newMd = createMemberDef(
2584 fileName,root->startLine,root->startColumn,
2585 md->typeString(),memName,md->argsString(),
2586 md->excpString(),root->protection,root->virt,
2587 md->isStatic(),Relationship::Member,md->memberType(),
2588 templAl,al,root->metaData
2589 );
2590 auto newMmd = toMemberDefMutable(newMd.get());
2591 if (nd)
2592 {
2593 newMmd->setNamespace(nd);
2594 nd->insertMember(newMd.get());
2595 }
2596 if (fd)
2597 {
2598 newMmd->setFileDef(fd);
2599 fd->insertMember(newMd.get());
2600 }
2601 if (!root->doc.empty() || !root->brief.empty())
2602 {
2603 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2604 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2605 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2606 }
2607 else
2608 {
2609 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2610 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2611 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2612 }
2613 newMmd->setDefinition(md->definition());
2614 applyMemberOverrideOptions(root,newMmd);
2615 newMmd->addQualifiers(root->qualifiers);
2616 newMmd->setBitfields(md->bitfieldString());
2617 newMmd->addSectionsToDefinition(root->anchors);
2618 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2619 newMmd->setBodyDef(md->getBodyDef());
2620 newMmd->setInitializer(md->initializer());
2621 newMmd->setRequiresClause(md->requiresClause());
2622 newMmd->setMaxInitLines(md->initializerLines());
2623 newMmd->setMemberGroupId(root->mGrpId);
2624 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2625 newMmd->setVhdlSpecifiers(md->getVhdlSpecifiers());
2626 newMmd->setLanguage(root->lang);
2627 newMmd->setId(root->id);
2629 mn->push_back(std::move(newMd));
2630#if 0 // insert an alias instead of a copy
2631 const MemberDef *md = toMemberDef(def);
2632 AUTO_TRACE_ADD("found member '{}' for name '{}'",md->qualifiedName(),root->name);
2633 auto aliasMd = createMemberDefAlias(nd,md);
2634 DString aliasFullName = nd->qualifiedName()+"::"+aliasMd->localName();
2635 if (nd && aliasMd.get())
2636 {
2637 nd->insertMember(aliasMd.get());
2638 }
2639 if (fd && aliasMd.get())
2640 {
2641 fd->insertMember(aliasMd.get());
2642 }
2643 MemberName *mn = Doxygen::memberNameLinkedMap->add(aliasFullName);
2644 mn->push_back(std::move(aliasMd));
2645#endif
2646 }
2647 else if (def && def->definitionType()==Definition::TypeClass)
2648 {
2649 const ClassDef *cd = toClassDef(def);
2650 DString copyFullName;
2651 if (nd==nullptr)
2652 {
2653 copyFullName = cd->localName();
2654 }
2655 else
2656 {
2657 copyFullName = nd->qualifiedName()+"::"+cd->localName();
2658 }
2659 if (Doxygen::classLinkedMap->find(copyFullName)==nullptr)
2660 {
2662 Doxygen::classLinkedMap->add(copyFullName,
2663 cd->deepCopy(copyFullName)));
2664 AUTO_TRACE_ADD("found class '{}' for name '{}' copy '{}' obj={}",cd->qualifiedName(),root->name,copyFullName,(void*)ncdm);
2665 g_usingClassMap[cd->qualifiedName().str()].push_back(ncdm);
2666 if (ncdm)
2667 {
2668 if (nd) ncdm->moveTo(nd);
2669 if ((!root->doc.empty() || !root->brief.empty())) // use docs at using statement
2670 {
2671 ncdm->setDocumentation(root->doc,root->docFile,root->docLine);
2672 ncdm->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2673 }
2674 else // use docs from used class
2675 {
2676 ncdm->setDocumentation(cd->documentation(),cd->docFile(),cd->docLine());
2678 }
2679 if (nd)
2680 {
2681 nd->addInnerCompound(ncdm);
2682 nd->addUsingDeclaration(ncdm);
2683 }
2684 if (fd)
2685 {
2686 if (ncdm) ncdm->setFileDef(fd);
2687 fd->insertClass(ncdm);
2688 fd->addUsingDeclaration(ncdm);
2689 }
2690 }
2691 }
2692#if 0 // insert an alias instead of a copy
2693 auto aliasCd = createClassDefAlias(nd,cd);
2694 DString aliasFullName;
2695 if (nd==nullptr)
2696 {
2697 aliasFullName = aliasCd->localName();
2698 }
2699 else
2700 {
2701 aliasFullName = nd->qualifiedName()+"::"+aliasCd->localName();
2702 }
2703 AUTO_TRACE_ADD("found class '{}' for name '{}' aliasFullName='{}'",cd->qualifiedName(),root->name,aliasFullName);
2704 auto acd = Doxygen::classLinkedMap->add(aliasFullName,std::move(aliasCd));
2705 if (nd && acd)
2706 {
2707 nd->addInnerCompound(acd);
2708 }
2709 if (fd && acd)
2710 {
2711 fd->insertClass(acd);
2712 }
2713#endif
2714 }
2715 else if (scope)
2716 {
2717 AUTO_TRACE_ADD("no symbol with name '{}' in scope {}",root->name,scope->name());
2718 }
2719 }
2720 }
2721 for (const auto &e : root->children()) findUsingDeclImports(e.get());
2722}
2723
2724//----------------------------------------------------------------------
2725
2727{
2728 FileDefSet visitedFiles;
2729 // then recursively add using directives found in #include files
2730 // to files that have not been visited.
2731 for (const auto &fn : *Doxygen::inputNameLinkedMap)
2732 {
2733 for (const auto &fd : *fn)
2734 {
2735 //printf("----- adding using directives for file %s\n",qPrint(fd->name()));
2736 fd->addIncludedUsingDirectives(visitedFiles);
2737 }
2738 }
2739}
2740
2741//----------------------------------------------------------------------
2742
2744 const Entry *root,
2745 ClassDefMutable *cd,
2746 MemberType mtype,
2747 const DString &type,
2748 const DString &name,
2749 const DString &args,
2750 Protection prot,
2751 Relationship related)
2752{
2754 DString scopeSeparator="::";
2755 SrcLangExt lang = cd->getLanguage();
2756 if (lang==SrcLangExt::Java || lang==SrcLangExt::CSharp)
2757 {
2758 qualScope = substitute(qualScope,"::",".");
2759 scopeSeparator=".";
2760 }
2761 AUTO_TRACE("class variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} init='{}'",
2762 root->fileName, type, qualScope, name, args, root->protection, mtype, lang, root->initializer.str());
2763
2764 DString def;
2765 if (!type.empty())
2766 {
2767 if (related!=Relationship::Member || mtype==MemberType::Friend || Config_getBool(HIDE_SCOPE_NAMES))
2768 {
2769 if (root->spec.isAlias()) // turn 'typedef B A' into 'using A'
2770 {
2771 if (lang==SrcLangExt::Python)
2772 {
2773 def="type "+name+args;
2774 }
2775 else
2776 {
2777 def="using "+name;
2778 }
2779 }
2780 else
2781 {
2782 def=type+" "+name+args;
2783 }
2784 }
2785 else
2786 {
2787 if (root->spec.isAlias()) // turn 'typedef B C::A' into 'using C::A'
2788 {
2789 if (lang==SrcLangExt::Python)
2790 {
2791 def="type "+qualScope+scopeSeparator+name+args;
2792 }
2793 else
2794 {
2795 def="using "+qualScope+scopeSeparator+name;
2796 }
2797 }
2798 else
2799 {
2800 def=type+" "+qualScope+scopeSeparator+name+args;
2801 }
2802 }
2803 }
2804 else
2805 {
2806 if (Config_getBool(HIDE_SCOPE_NAMES))
2807 {
2808 def=name+args;
2809 }
2810 else
2811 {
2812 def=qualScope+scopeSeparator+name+args;
2813 }
2814 }
2815 def.stripPrefix("static ");
2816
2817 // see if the member is already found in the same scope
2818 // (this may be the case for a static member that is initialized
2819 // outside the class)
2821 if (mn)
2822 {
2823 for (const auto &imd : *mn)
2824 {
2825 //printf("md->getClassDef()=%p cd=%p type=[%s] md->typeString()=[%s]\n",
2826 // md->getClassDef(),cd,qPrint(type),md->typeString());
2827 MemberDefMutable *md = toMemberDefMutable(imd.get());
2828 if (md &&
2829 md->getClassDef()==cd &&
2830 ((lang==SrcLangExt::Python && type.empty() && !md->typeString().empty()) ||
2832 // member already in the scope
2833 {
2834
2835 if (root->lang==SrcLangExt::ObjC &&
2836 root->mtype==MethodTypes::Property &&
2837 md->memberType()==MemberType::Variable)
2838 { // Objective-C 2.0 property
2839 // turn variable into a property
2840 md->setProtection(root->protection);
2841 cd->reclassifyMember(md,MemberType::Property);
2842 }
2843 addMemberDocs(root,md,def,nullptr,false,root->spec);
2844 AUTO_TRACE_ADD("Member already found!");
2845 return md;
2846 }
2847 }
2848 }
2849
2850 DString fileName = root->fileName;
2851 if (fileName.empty() && root->tagInfo())
2852 {
2853 fileName = root->tagInfo()->tagName;
2854 }
2855
2856 // new member variable, typedef or enum value
2857 auto md = createMemberDef(
2858 fileName,root->startLine,root->startColumn,
2859 type,name,args,root->exception,
2860 prot,Specifier::Normal,root->isStatic,related,
2861 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2862 ArgumentList(), root->metaData);
2863 auto mmd = toMemberDefMutable(md.get());
2864 mmd->setTagInfo(root->tagInfo());
2865 mmd->setMemberClass(cd); // also sets outer scope (i.e. getOuterScope())
2866 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2867 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2868 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2869 mmd->setDefinition(def);
2870 mmd->setBitfields(root->bitfields);
2871 mmd->addSectionsToDefinition(root->anchors);
2872 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2873 mmd->setInitializer(root->initializer.str());
2874 mmd->setMaxInitLines(root->initLines);
2875 mmd->setMemberGroupId(root->mGrpId);
2876 mmd->setMemberSpecifiers(root->spec);
2877 mmd->setVhdlSpecifiers(root->vhdlSpec);
2878 mmd->setReadAccessor(root->read);
2879 mmd->setWriteAccessor(root->write);
2881 mmd->setHidden(root->hidden);
2882 mmd->setArtificial(root->artificial);
2883 mmd->setLanguage(root->lang);
2884 mmd->setId(root->id);
2885 addMemberToGroups(root,md.get());
2887 mmd->setBodyDef(root->fileDef());
2888 mmd->addQualifiers(root->qualifiers);
2889 addIncludeFile(mmd,root->fileDef(),root);
2890
2891 AUTO_TRACE_ADD("Adding new member '{}' to class '{}'",name,cd->name());
2892 cd->insertMember(md.get());
2893 mmd->setRefItems(root->sli);
2894 mmd->setRequirementReferences(root->rqli);
2895
2896 cd->insertUsedFile(root->fileDef());
2897 root->markAsProcessed();
2898
2899 if (mtype==MemberType::Typedef)
2900 {
2901 resolveTemplateInstanceInType(root,cd,md.get());
2902 }
2903
2904 // add the member to the global list
2905 MemberDef *result = md.get();
2907 mn->push_back(std::move(md));
2908
2909 return result;
2910}
2911
2912//----------------------------------------------------------------------
2913
2915 const Entry *root,
2916 MemberType mtype,
2917 const DString &scope,
2918 const DString &type,
2919 const DString &name,
2920 const DString &args)
2921{
2922 AUTO_TRACE("global variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} init='{}'",
2923 root->fileName, type, scope, name, args, root->protection, mtype, root->lang, root->initializer.str());
2924
2925 FileDef *fd = root->fileDef();
2926
2927 // see if we have a typedef that should hide a struct or union
2928 if (mtype==MemberType::Typedef && Config_getBool(TYPEDEF_HIDES_STRUCT))
2929 {
2930 DString ttype = type;
2931 ttype.stripPrefix("typedef ");
2932 if (ttype.stripPrefix("struct ") || ttype.stripPrefix("union "))
2933 {
2934 static const reg::Ex re(R"(\a\w*)");
2935 reg::Match match;
2936 const std::string &typ = ttype.str();
2937 if (reg::search(typ,match,re))
2938 {
2939 DString typeValue = match.str();
2940 ClassDefMutable *cd = getClassMutable(typeValue);
2941 if (cd)
2942 {
2943 // this typedef should hide compound name cd, so we
2944 // change the name that is displayed from cd.
2945 cd->setClassName(name);
2946 cd->setDocumentation(root->doc,root->docFile,root->docLine);
2947 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2948 return nullptr;
2949 }
2950 }
2951 }
2952 }
2953
2954 // see if the function is inside a namespace
2955 NamespaceDefMutable *nd = nullptr;
2956 if (!scope.empty())
2957 {
2958 if (scope.find('@')!=DString::npos) return nullptr; // anonymous scope!
2959 nd = getResolvedNamespaceMutable(scope);
2960 }
2961 DString def;
2962
2963 // determine the definition of the global variable
2964 if (nd && !nd->isAnonymous() &&
2965 !Config_getBool(HIDE_SCOPE_NAMES)
2966 )
2967 // variable is inside a namespace, so put the scope before the name
2968 {
2969 SrcLangExt lang = nd->getLanguage();
2971
2972 if (!type.empty())
2973 {
2974 if (root->spec.isAlias()) // turn 'typedef B NS::A' into 'using NS::A'
2975 {
2976 if (lang==SrcLangExt::Python)
2977 {
2978 def="type "+nd->name()+sep+name+args;
2979 }
2980 else
2981 {
2982 def="using "+nd->name()+sep+name;
2983 }
2984 }
2985 else // normal member
2986 {
2987 def=type+" "+nd->name()+sep+name+args;
2988 }
2989 }
2990 else
2991 {
2992 def=nd->name()+sep+name+args;
2993 }
2994 }
2995 else
2996 {
2997 if (!type.empty() && !root->name.empty())
2998 {
2999 if (name.at(0)=='@') // dummy variable representing anonymous union
3000 {
3001 def=type;
3002 }
3003 else
3004 {
3005 if (root->spec.isAlias()) // turn 'typedef B A' into 'using A'
3006 {
3007 if (root->lang==SrcLangExt::Python)
3008 {
3009 def="type "+root->name+args;
3010 }
3011 else
3012 {
3013 def="using "+root->name;
3014 }
3015 }
3016 else // normal member
3017 {
3018 def=type+" "+name+args;
3019 }
3020 }
3021 }
3022 else
3023 {
3024 def=name+args;
3025 }
3026 }
3027 def.stripPrefix("static ");
3028
3030 if (mn)
3031 {
3032 //DString nscope=removeAnonymousScopes(scope);
3033 //NamespaceDef *nd=nullptr;
3034 //if (!nscope.empty())
3035 if (!scope.empty())
3036 {
3037 nd = getResolvedNamespaceMutable(scope);
3038 }
3039 for (const auto &imd : *mn)
3040 {
3041 MemberDefMutable *md = toMemberDefMutable(imd.get());
3042 if (md &&
3043 ((nd==nullptr && md->getNamespaceDef()==nullptr && md->getFileDef() &&
3044 root->fileName==md->getFileDef()->absFilePath()
3045 ) // both variable names in the same file
3046 || (nd!=nullptr && md->getNamespaceDef()==nd) // both in same namespace
3047 )
3048 && !md->isDefine() // function style #define's can be "overloaded" by typedefs or variables
3049 && !md->isEnumerate() // in C# an enum value and enum can have the same name
3050 )
3051 // variable already in the scope
3052 {
3053 bool isPHPArray = md->getLanguage()==SrcLangExt::PHP &&
3054 md->argsString()!=args &&
3055 args.find('[')!=DString::npos;
3056 bool staticsInDifferentFiles =
3057 root->isStatic && md->isStatic() &&
3058 root->fileName!=md->getDefFileName();
3059
3060 if (md->getFileDef() &&
3061 !isPHPArray && // not a php array
3062 !staticsInDifferentFiles
3063 )
3064 // not a php array variable
3065 {
3066 AUTO_TRACE_ADD("variable already found: scope='{}'",md->getOuterScope()->name());
3067 addMemberDocs(root,md,def,nullptr,false,root->spec);
3068 md->setRefItems(root->sli);
3069 md->setRequirementReferences(root->rqli);
3070 // if md is a variable forward declaration and root is the definition that
3071 // turn md into the definition
3072 if (!root->explicitExternal && md->isExternal())
3073 {
3074 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
3075 md->setExplicitExternal(false,root->fileName,root->startLine,root->startColumn);
3076 }
3077 // if md is the definition and root point at a declaration, then add the
3078 // declaration info
3079 else if (root->explicitExternal && !md->isExternal())
3080 {
3081 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
3082 }
3083 return md;
3084 }
3085 }
3086 }
3087 }
3088
3089 DString fileName = root->fileName;
3090 if (fileName.empty() && root->tagInfo())
3091 {
3092 fileName = root->tagInfo()->tagName;
3093 }
3094
3095 AUTO_TRACE_ADD("new variable, namespace='{}'",nd?nd->name():DString("<global>"));
3096 // new global variable, enum value or typedef
3097 auto md = createMemberDef(
3098 fileName,root->startLine,root->startColumn,
3099 type,name,args,DString(),
3100 root->protection, Specifier::Normal,root->isStatic,Relationship::Member,
3101 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3102 root->argList, root->metaData);
3103 auto mmd = toMemberDefMutable(md.get());
3104 mmd->setTagInfo(root->tagInfo());
3105 mmd->setMemberSpecifiers(root->spec);
3106 mmd->setVhdlSpecifiers(root->vhdlSpec);
3107 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3108 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3109 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3110 mmd->addSectionsToDefinition(root->anchors);
3111 mmd->setInitializer(root->initializer.str());
3112 mmd->setMaxInitLines(root->initLines);
3113 mmd->setMemberGroupId(root->mGrpId);
3114 mmd->setDefinition(def);
3115 mmd->setLanguage(root->lang);
3116 mmd->setId(root->id);
3118 mmd->setExplicitExternal(root->explicitExternal,fileName,root->startLine,root->startColumn);
3119 mmd->addQualifiers(root->qualifiers);
3120 //md->setOuterScope(fd);
3121 if (!root->explicitExternal)
3122 {
3123 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3124 mmd->setBodyDef(fd);
3125 }
3126 addMemberToGroups(root,md.get());
3127 addIncludeFile(mmd,fd,root);
3129
3130 mmd->setRefItems(root->sli);
3131 mmd->setRequirementReferences(root->rqli);
3132 if (nd && !nd->isAnonymous())
3133 {
3134 mmd->setNamespace(nd);
3135 nd->insertMember(md.get());
3136 }
3137
3138 // add member to the file (we do this even if we have already inserted
3139 // it into the namespace.
3140 if (fd)
3141 {
3142 mmd->setFileDef(fd);
3143 fd->insertMember(md.get());
3144 }
3145
3146 root->markAsProcessed();
3147
3148 if (mtype==MemberType::Typedef)
3149 {
3150 resolveTemplateInstanceInType(root,nd,md.get());
3151 }
3152
3153 // add member definition to the list of globals
3154 MemberDef *result = md.get();
3156 mn->push_back(std::move(md));
3157
3158
3159
3160 return result;
3161}
3162
3163/*! See if the return type string \a type is that of a function pointer
3164 * \returns -1 if this is not a function pointer variable or
3165 * the index at which the closing brace of (...*name) was found.
3166 */
3167static int findFunctionPtr(const std::string &type,SrcLangExt lang, int *pLength=nullptr)
3168{
3169 AUTO_TRACE("type='{}' lang={}",type,lang);
3170 if (lang == SrcLangExt::Fortran || lang == SrcLangExt::VHDL)
3171 {
3172 return -1; // Fortran and VHDL do not have function pointers
3173 }
3174
3175 static const reg::Ex re(R"(\‍([^)]*[*&^][^)]*\))");
3176 reg::Match match;
3177 size_t i=std::string::npos;
3178 size_t l=0;
3179 if (reg::search(type,match,re)) // contains (...*...) or (...&...) or (...^...)
3180 {
3181 i = match.position();
3182 l = match.length();
3183 }
3184 if (i!=std::string::npos)
3185 {
3186 size_t di = type.find("decltype(");
3187 if (di!=std::string::npos && di<i)
3188 {
3189 i = std::string::npos;
3190 }
3191 }
3192 size_t bb=type.find('<');
3193 size_t be=type.rfind('>');
3194 bool templFp = false;
3195 if (be!=std::string::npos) {
3196 size_t cc_ast = type.find("::*");
3197 size_t cc_amp = type.find("::&");
3198 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>::*)'
3199 }
3200
3201 if (!type.empty() && // return type is non-empty
3202 i!=std::string::npos && // contains (...*...)
3203 type.find("operator")==std::string::npos && // not an operator
3204 (type.find(")(")==std::string::npos || type.find("typedef ")!=std::string::npos) &&
3205 // not a function pointer return type
3206 (!((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
3207 )
3208 {
3209 if (pLength) *pLength=static_cast<int>(l);
3210 //printf("findFunctionPtr=%d\n",(int)i);
3211 AUTO_TRACE_EXIT("result={}",i);
3212 return static_cast<int>(i);
3213 }
3214 else
3215 {
3216 //printf("findFunctionPtr=%d\n",-1);
3217 AUTO_TRACE_EXIT("result=-1");
3218 return -1;
3219 }
3220}
3221
3222//--------------------------------------------------------------------------------------
3223
3224/*! Returns true iff \a type is a class within scope \a context.
3225 * Used to detect variable declarations that look like function prototypes.
3226 */
3227static bool isVarWithConstructor(const Entry *root)
3228{
3229 bool result = false;
3230 bool typeIsClass = false;
3231 bool typePtrType = false;
3232 DString type;
3233 Definition *ctx = nullptr;
3234 FileDef *fd = root->fileDef();
3235 SymbolResolver resolver(fd);
3236
3237 AUTO_TRACE("isVarWithConstructor({})",root->name);
3238 if (root->parent()->section.isCompound())
3239 { // inside a class
3240 result=false;
3241 AUTO_TRACE_EXIT("inside class: result={}",result);
3242 return result;
3243 }
3244 else if ((fd != nullptr) && (fd->name().endsWith(".c") || fd->name().endsWith(".h")))
3245 { // inside a .c file
3246 result=false;
3247 AUTO_TRACE_EXIT("inside C file: result={}",result);
3248 return result;
3249 }
3250 if (root->type.empty())
3251 {
3252 result=false;
3253 AUTO_TRACE_EXIT("no type: result={}",result);
3254 return result;
3255 }
3256 if (!root->parent()->name.empty())
3257 {
3259 }
3260 type = root->type;
3261 // remove qualifiers
3262 type.findAndRemoveWord("const");
3263 type.findAndRemoveWord("static");
3264 type.findAndRemoveWord("volatile");
3265 typePtrType = type.find('*')!=DString::npos || type.find('&')!=DString::npos;
3266 if (!typePtrType)
3267 {
3268 typeIsClass = resolver.resolveClass(ctx,type)!=nullptr;
3269 if (size_t ti=type.find('<'); !typeIsClass && ti!=DString::npos)
3270 {
3271 typeIsClass=resolver.resolveClass(ctx,type.left(ti))!=nullptr;
3272 }
3273 }
3274 if (typeIsClass) // now we still have to check if the arguments are
3275 // types or values. Since we do not have complete type info
3276 // we need to rely on heuristics :-(
3277 {
3278 if (root->argList.empty())
3279 {
3280 result=false; // empty arg list -> function prototype.
3281 AUTO_TRACE_EXIT("empty arg list: result={}",result);
3282 return result;
3283 }
3284 for (const Argument &a : root->argList)
3285 {
3286 static const reg::Ex initChars(R"([\d"'&*!^]+)");
3287 reg::Match match;
3288 if (!a.name.empty() || !a.defval.empty())
3289 {
3290 std::string name = a.name.str();
3291 if (reg::search(name,match,initChars) && match.position()==0)
3292 {
3293 result=true;
3294 }
3295 else
3296 {
3297 result=false; // arg has (type,name) pair -> function prototype
3298 }
3299 AUTO_TRACE_EXIT("function prototype: result={}",result);
3300 return result;
3301 }
3302 if (!a.type.empty() &&
3303 (a.type.at(a.type.length()-1)=='*' ||
3304 a.type.at(a.type.length()-1)=='&'))
3305 // type ends with * or & => pointer or reference
3306 {
3307 result=false;
3308 AUTO_TRACE_EXIT("pointer or reference: result={}",result);
3309 return result;
3310 }
3311 if (a.type.empty() || resolver.resolveClass(ctx,a.type)!=nullptr)
3312 {
3313 result=false; // arg type is a known type
3314 AUTO_TRACE_EXIT("known type: result={}",result);
3315 return result;
3316 }
3317 if (checkIfTypedef(ctx,fd,a.type))
3318 {
3319 result=false; // argument is a typedef
3320 AUTO_TRACE_EXIT("typedef: result={}",result);
3321 return result;
3322 }
3323 std::string atype = a.type.str();
3324 if (reg::search(atype,match,initChars) && match.position()==0)
3325 {
3326 result=true; // argument type starts with typical initializer char
3327 AUTO_TRACE_EXIT("argument with init char: result={}",result);
3328 return result;
3329 }
3330 std::string resType=resolveTypeDef(ctx,a.type).str();
3331 if (resType.empty()) resType=atype;
3332 static const reg::Ex idChars(R"(\a\w*)");
3333 if (reg::search(resType,match,idChars) && match.position()==0) // resType starts with identifier
3334 {
3335 resType=match.str();
3336 if (resType=="int" || resType=="long" ||
3337 resType=="float" || resType=="double" ||
3338 resType=="char" || resType=="void" ||
3339 resType=="signed" || resType=="unsigned" ||
3340 resType=="const" || resType=="volatile" )
3341 {
3342 result=false; // type keyword -> function prototype
3343 AUTO_TRACE_EXIT("type keyword: result={}",result);
3344 return result;
3345 }
3346 }
3347 }
3348 result=true;
3349 }
3350
3351 AUTO_TRACE_EXIT("end: result={}",result);
3352 return result;
3353}
3354
3355//--------------------------------------------------------------------------------------
3356
3357/*! Searches for the end of a template in prototype \a s starting from
3358 * character position \a startPos. If the end was found the position
3359 * of the closing > is returned, otherwise -1 is returned.
3360 *
3361 * Handles exotic cases such as
3362 * \code
3363 * Class<(id<0)>
3364 * Class<bits<<2>
3365 * Class<"<">
3366 * Class<'<'>
3367 * Class<(")<")>
3368 * \endcode
3369 */
3370static int findEndOfTemplate(const DString &s,size_t startPos)
3371{
3372 // locate end of template
3373 size_t e=startPos;
3374 int brCount=1;
3375 int roundCount=0;
3376 size_t len = s.length();
3377 bool insideString=false;
3378 bool insideChar=false;
3379 char pc = 0;
3380 while (e<len && brCount!=0)
3381 {
3382 char c=s.at(e);
3383 switch(c)
3384 {
3385 case '<':
3386 if (!insideString && !insideChar)
3387 {
3388 if (e<len-1 && s.at(e+1)=='<')
3389 e++;
3390 else if (roundCount==0)
3391 brCount++;
3392 }
3393 break;
3394 case '>':
3395 if (!insideString && !insideChar)
3396 {
3397 if (e<len-1 && s.at(e+1)=='>')
3398 e++;
3399 else if (roundCount==0)
3400 brCount--;
3401 }
3402 break;
3403 case '(':
3404 if (!insideString && !insideChar)
3405 roundCount++;
3406 break;
3407 case ')':
3408 if (!insideString && !insideChar)
3409 roundCount--;
3410 break;
3411 case '"':
3412 if (!insideChar)
3413 {
3414 if (insideString && pc!='\\')
3415 insideString=false;
3416 else
3417 insideString=true;
3418 }
3419 break;
3420 case '\'':
3421 if (!insideString)
3422 {
3423 if (insideChar && pc!='\\')
3424 insideChar=false;
3425 else
3426 insideChar=true;
3427 }
3428 break;
3429 }
3430 pc = c;
3431 e++;
3432 }
3433 return brCount==0 ? static_cast<int>(e) : -1;
3434}
3435
3436//--------------------------------------------------------------------------------------
3437
3438static void addVariable(const Entry *root,int isFuncPtr=-1)
3439{
3440 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
3441
3442 AUTO_TRACE("VARIABLE_SEC: type='{}' name='{}' args='{}' bodyLine={} endBodyLine={} mGrpId={} relates='{}'",
3443 root->type, root->name, root->args, root->bodyLine, root->endBodyLine, root->mGrpId, root->relates);
3444 //printf("root->parent->name=%s\n",qPrint(root->parent->name));
3445
3446 DString type = root->type;
3447 DString name = root->name;
3448 DString args = root->args;
3449 if (type.empty() && name.find("operator")==DString::npos &&
3450 (name.find('*')!=DString::npos || name.find('&')!=DString::npos))
3451 {
3452 // recover from parse error caused by redundant braces
3453 // like in "int *(var[10]);", which is parsed as
3454 // type="" name="int *" args="(var[10])"
3455
3456 type=name;
3457 std::string sargs = args.str();
3458 static const reg::Ex reName(R"(\a\w*)");
3459 reg::Match match;
3460 if (reg::search(sargs,match,reName))
3461 {
3462 name = match.str(); // e.g. 'var' in '(var[10])'
3463 sargs = match.suffix().str(); // e.g. '[10]) in '(var[10])'
3464 size_t j = sargs.find(')');
3465 if (j!=std::string::npos) args=sargs.substr(0,j); // extract, e.g '[10]' from '[10])'
3466 }
3467 }
3468 else
3469 {
3470 int i=isFuncPtr;
3471 if (i==-1 && (root->spec.isAlias())==0) i=findFunctionPtr(type.str(),root->lang); // for typedefs isFuncPtr is not yet set
3472 AUTO_TRACE_ADD("functionPtr={}",i!=-1?"yes":"no");
3473 if (i>=0) // function pointer
3474 {
3475 size_t ii = i;
3476 size_t ai = type.find('[',ii);
3477 if (ai!=DString::npos && ai>ii) // function pointer array
3478 {
3479 args.prepend(type.mid(ai));
3480 type=type.left(ai);
3481 }
3482 else if (type.find(')',ii)!=DString::npos) // function ptr, not variable like "int (*bla)[10]"
3483 {
3484 type=type.left(type.length()-1);
3485 args.prepend(") ");
3486 }
3487 }
3488 }
3489 AUTO_TRACE_ADD("after correction: type='{}' name='{}' args='{}'",type,name,args);
3490
3491 DString scope;
3492 name=removeRedundantWhiteSpace(name);
3493
3494 // find the scope of this variable
3495 int index = computeQualifiedIndex(name);
3496 // grouped members are stored with full scope
3497 bool qualifiedGroupMember = index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo();
3498 // A specialization of a variable template that lives in a namespace, i.e.
3499 // `template<class T> constexpr bool std::ranges::enable_view<MyView<T>> = true;`.
3500 // Its scope has to be spelled out, so it must not be mistaken for the definition of a static
3501 // data member, which is declared in its class as well and is therefore picked up from there.
3502 bool variableTemplateSpecialization = index!=-1 && !root->tArgLists.empty() &&
3503 name.mid(index+2).find('<')!=DString::npos &&
3504 getClass(name.left(index))==nullptr;
3505 if (qualifiedGroupMember || variableTemplateSpecialization)
3506 {
3507 Definition *d = buildScopeFromQualifiedName(name.left(index+2),root->lang,root->tagInfo());
3508 if (variableTemplateSpecialization)
3509 // the scope may have been made up for this specialization, but it holds a documented member
3510 // now, which is only reachable through a page of its own
3511 {
3512 for (Definition *s=d; s && s!=Doxygen::globalScope; s=s->getOuterScope())
3513 {
3515 if (ndm && ndm->isArtificial()) ndm->setArtificial(false);
3516 }
3517 }
3518 scope=name.left(index);
3519 name=name.mid(index+2);
3520 }
3521 else
3522 {
3523 Entry *p = root->parent();
3524 while (p->section.isScope())
3525 {
3526 DString scopeName = p->name;
3527 if (!scopeName.empty())
3528 {
3529 scope.prepend(scopeName);
3530 break;
3531 }
3532 p=p->parent();
3533 }
3534 }
3535
3536 DString type_s = type;
3537 type=type.stripWhiteSpace();
3538 ClassDefMutable *cd=nullptr;
3539 bool isRelated=false;
3540 bool isMemberOf=false;
3541
3542 DString classScope=stripAnonymousNamespaceScope(scope);
3543 if (root->lang==SrcLangExt::CSharp)
3544 {
3545 classScope=mangleCSharpGenericName(classScope);
3546 }
3547 else
3548 {
3549 classScope=stripTemplateSpecifiersFromScope(classScope,false);
3550 }
3551 DString annScopePrefix=scope.left(scope.length()-classScope.length());
3552
3553
3554 // Look for last :: not part of template specifier
3555 int p=-1;
3556 for (size_t i=0;i<name.length()-1;i++)
3557 {
3558 if (name[i]==':' && name[i+1]==':')
3559 {
3560 p=static_cast<int>(i);
3561 }
3562 else if (name[i]=='<') // skip over template parts,
3563 // i.e. A::B<C::D> => p=1 and
3564 // A<B::C>::D => p=8
3565 {
3566 int e = findEndOfTemplate(name,i+1);
3567 if (e!=-1) i=static_cast<int>(e);
3568 }
3569 }
3570
3571 if (p!=-1) // found it
3572 {
3573 if (isTypeAClassFriend(type))
3574 {
3575 cd=getClassMutable(scope);
3576 if (cd)
3577 {
3578 addVariableToClass(root, // entry
3579 cd, // class to add member to
3580 MemberType::Friend, // type of member
3581 type, // type value as string
3582 name, // name of the member
3583 args, // arguments as string
3584 Protection::Public, // protection
3585 Relationship::Member // related to a class
3586 );
3587 }
3588 }
3589 if (root->bodyLine!=-1 && root->endBodyLine!=-1) // store the body location for later use
3590 {
3591 Doxygen::staticInitMap.emplace(name.str(),BodyInfo{root->startLine,root->bodyLine,root->endBodyLine});
3592 }
3593
3594
3595 AUTO_TRACE_ADD("static variable {} body=[{}..{}]",name,root->bodyLine,root->endBodyLine);
3596 return; /* skip this member, because it is a
3597 * static variable definition (always?), which will be
3598 * found in a class scope as well, but then we know the
3599 * correct protection level, so only then it will be
3600 * inserted in the correct list!
3601 */
3602 }
3603
3604 MemberType mtype = MemberType::Variable;
3605 if (type=="@")
3606 mtype=MemberType::EnumValue;
3607 else if (type_s.startsWith("typedef "))
3608 mtype=MemberType::Typedef;
3609 else if (type_s.startsWith("friend ") || type_s=="friend")
3610 mtype=MemberType::Friend;
3611 else if (root->mtype==MethodTypes::Property)
3612 mtype=MemberType::Property;
3613 else if (root->mtype==MethodTypes::Event)
3614 mtype=MemberType::Event;
3615 else if (type.find("sequence<") != DString::npos)
3616 mtype=sliceOpt ? MemberType::Sequence : MemberType::Typedef;
3617 else if (type.find("dictionary<") != DString::npos)
3618 mtype=sliceOpt ? MemberType::Dictionary : MemberType::Typedef;
3619
3620 if (!root->relates.empty()) // related variable
3621 {
3622 isRelated=true;
3623 isMemberOf=(root->relatesType==RelatesType::MemberOf);
3624 if (getClass(root->relates)==nullptr && !scope.empty())
3625 scope=mergeScopes(scope,root->relates);
3626 else
3627 scope=root->relates;
3628 }
3629
3630 cd=getClassMutable(scope);
3631 if (cd==nullptr && classScope!=scope) cd=getClassMutable(classScope);
3632 if (cd)
3633 {
3634 Relationship relationship = isMemberOf ? Relationship::Foreign :
3635 isRelated ? Relationship::Related :
3636 Relationship::Member ;
3637
3638 addVariableToClass(root, // entry
3639 cd, // class to add member to
3640 mtype, // member type
3641 type, // type value as string
3642 name, // name of the member
3643 args, // arguments as string
3644 root->protection,
3645 relationship
3646 );
3647
3648 }
3649 else if (!name.empty()) // global variable
3650 {
3651 addVariableToFile(root,mtype,scope,type,name,args);
3652 }
3653
3654}
3655
3656//----------------------------------------------------------------------
3657// Searches the Entry tree for typedef documentation sections.
3658// If found they are stored in their class or in the global list.
3659static void buildTypedefList(const Entry *root)
3660{
3661 //printf("buildVarList(%s)\n",qPrint(rootNav->name()));
3662 if (!root->name.empty() &&
3663 root->section.isVariable() &&
3664 root->type.find("typedef ")!=DString::npos // its a typedef
3665 )
3666 {
3667 AUTO_TRACE();
3668 DString rname = removeRedundantWhiteSpace(root->name);
3669 DString scope;
3670 int index = computeQualifiedIndex(rname);
3671 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3672 // grouped members are stored with full scope
3673 {
3674 buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
3675 scope=rname.left(index);
3676 rname=rname.mid(index+2);
3677 }
3678 else
3679 {
3680 scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
3681 }
3685 bool found=false;
3686 if (mn) // symbol with the same name already found
3687 {
3688 for (auto &imd : *mn)
3689 {
3690 if (!imd->isTypedef())
3691 continue;
3692
3693 DString rtype = root->type;
3694 rtype.stripPrefix("typedef ");
3695
3696 // merge the typedefs only if they're not both grouped, and both are
3697 // either part of the same class, part of the same namespace, or both
3698 // are global (i.e., neither in a class or a namespace)
3699 bool notBothGrouped = root->groups.empty() || imd->getGroupDef()==nullptr; // see example #100
3700 bool bothSameScope = (!cd && !nd) || (cd && imd->getClassDef() == cd) || (nd && imd->getNamespaceDef() == nd);
3701 //printf("imd->isTypedef()=%d imd->typeString()=%s root->type=%s\n",imd->isTypedef(),
3702 // qPrint(imd->typeString()),qPrint(root->type));
3703 if (notBothGrouped && bothSameScope && imd->typeString()==rtype)
3704 {
3705 MemberDefMutable *md = toMemberDefMutable(imd.get());
3706 if (md)
3707 {
3708 md->setDocumentation(root->doc,root->docFile,root->docLine);
3710 md->setDocsForDefinition(!root->proto);
3711 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3713 md->setRefItems(root->sli);
3714 md->setRequirementReferences(root->rqli);
3715 md->addQualifiers(root->qualifiers);
3716
3717 // merge ingroup specifiers
3718 if (md->getGroupDef()==nullptr && !root->groups.empty())
3719 {
3720 addMemberToGroups(root,md);
3721 }
3722 else if (md->getGroupDef()!=nullptr && root->groups.empty())
3723 {
3724 //printf("existing member is grouped, new member not\n");
3725 }
3726 else if (md->getGroupDef()!=nullptr && !root->groups.empty())
3727 {
3728 //printf("both members are grouped\n");
3729 }
3730 found=true;
3731 break;
3732 }
3733 }
3734 }
3735 }
3736 if (found)
3737 {
3738 AUTO_TRACE_ADD("typedef '{}' already found",rname);
3739 // mark the entry as processed, as we copied everything from it elsewhere
3740 // also, otherwise, due to containing `typedef` it may later get treated
3741 // as a function typedef in filterMemberDocumentation, which is incorrect
3742 root->markAsProcessed();
3743 }
3744 else
3745 {
3746 AUTO_TRACE_ADD("new typedef '{}'",rname);
3747 addVariable(root);
3748 }
3749
3750 }
3751 for (const auto &e : root->children())
3752 if (!e->section.isEnum())
3753 buildTypedefList(e.get());
3754}
3755
3756//----------------------------------------------------------------------
3757// Searches the Entry tree for sequence documentation sections.
3758// If found they are stored in the global list.
3759static void buildSequenceList(const Entry *root)
3760{
3761 if (!root->name.empty() &&
3762 root->section.isVariable() &&
3763 root->type.find("sequence<")!=DString::npos // it's a sequence
3764 )
3765 {
3766 AUTO_TRACE();
3767 addVariable(root);
3768 }
3769 for (const auto &e : root->children())
3770 if (!e->section.isEnum())
3771 buildSequenceList(e.get());
3772}
3773
3774//----------------------------------------------------------------------
3775// Searches the Entry tree for dictionary documentation sections.
3776// If found they are stored in the global list.
3777static void buildDictionaryList(const Entry *root)
3778{
3779 if (!root->name.empty() &&
3780 root->section.isVariable() &&
3781 root->type.find("dictionary<")!=DString::npos // it's a dictionary
3782 )
3783 {
3784 AUTO_TRACE();
3785 addVariable(root);
3786 }
3787 for (const auto &e : root->children())
3788 if (!e->section.isEnum())
3789 buildDictionaryList(e.get());
3790}
3791
3792//----------------------------------------------------------------------
3793// Searches the Entry tree for Variable documentation sections.
3794// If found they are stored in their class or in the global list.
3795
3796static void buildVarList(const Entry *root)
3797{
3798 //printf("buildVarList(%s) section=%08x\n",qPrint(rootNav->name()),rootNav->section());
3799 int isFuncPtr=-1;
3800 if (!root->name.empty() &&
3801 (root->type.empty() || g_compoundKeywords.find(root->type.str())==g_compoundKeywords.end()) &&
3802 (
3803 (root->section.isVariable() && // it's a variable
3804 root->type.find("typedef ")==DString::npos // and not a typedef
3805 ) ||
3806 (root->section.isFunction() && // or maybe a function pointer variable
3807 (isFuncPtr=findFunctionPtr(root->type.str(),root->lang))!=-1
3808 ) ||
3809 (root->section.isFunction() && // class variable initialized by constructor
3811 )
3812 )
3813 ) // documented variable
3814 {
3815 AUTO_TRACE();
3816 addVariable(root,isFuncPtr);
3817 }
3818 for (const auto &e : root->children())
3819 if (!e->section.isEnum())
3820 buildVarList(e.get());
3821}
3822
3823//----------------------------------------------------------------------
3824// Searches the Entry tree for Interface sections (UNO IDL only).
3825// If found they are stored in their service or in the global list.
3826//
3827
3829 const Entry *root,
3830 ClassDefMutable *cd,
3831 DString const& rname)
3832{
3833 FileDef *fd = root->fileDef();
3834 enum MemberType type = root->section.isExportedInterface() ? MemberType::Interface : MemberType::Service;
3835 DString fileName = root->fileName;
3836 if (fileName.empty() && root->tagInfo())
3837 {
3838 fileName = root->tagInfo()->tagName;
3839 }
3840 auto md = createMemberDef(
3841 fileName, root->startLine, root->startColumn, root->type, rname,
3842 "", "", root->protection, root->virt, root->isStatic, Relationship::Member,
3843 type, ArgumentList(), root->argList, root->metaData);
3844 auto mmd = toMemberDefMutable(md.get());
3845 mmd->setTagInfo(root->tagInfo());
3846 mmd->setMemberClass(cd);
3847 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3848 mmd->setDocsForDefinition(false);
3849 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3850 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3851 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3852 mmd->setMemberSpecifiers(root->spec);
3853 mmd->setVhdlSpecifiers(root->vhdlSpec);
3854 mmd->setMemberGroupId(root->mGrpId);
3855 mmd->setTypeConstraints(root->typeConstr);
3856 mmd->setLanguage(root->lang);
3857 mmd->setBodyDef(fd);
3858 mmd->setFileDef(fd);
3859 mmd->addSectionsToDefinition(root->anchors);
3860 DString const def = root->type + " " + rname;
3861 mmd->setDefinition(def);
3863 mmd->addQualifiers(root->qualifiers);
3864 addIncludeFile(mmd,fd,root);
3865
3866 AUTO_TRACE("Interface member: fileName='{}' type='{}' name='{}' mtype='{}' prot={} virt={} state={} proto={} def='{}'",
3867 fileName,root->type,rname,type,root->protection,root->virt,root->isStatic,root->proto,def);
3868
3869 // add member to the class cd
3870 cd->insertMember(md.get());
3871 // also add the member as a "base" (to get nicer diagrams)
3872 // "optional" interface/service get Protected which turns into dashed line
3873 BaseInfo base(rname,
3874 root->spec.isOptional() ? Protection::Protected : Protection::Public, Specifier::Normal);
3875 TemplateNameMap templateNames;
3876 findClassRelation(root,cd,cd,&base,templateNames,DocumentedOnly,true) ||
3877 findClassRelation(root,cd,cd,&base,templateNames,Undocumented,true);
3878 // add file to list of used files
3879 cd->insertUsedFile(fd);
3880
3881 addMemberToGroups(root,md.get());
3883 root->markAsProcessed();
3884 mmd->setRefItems(root->sli);
3885 mmd->setRequirementReferences(root->rqli);
3886
3887 // add member to the global list of all members
3889 mn->push_back(std::move(md));
3890}
3891
3892static void buildInterfaceAndServiceList(const Entry *root)
3893{
3894 if (root->section.isExportedInterface() || root->section.isIncludedService())
3895 {
3896 AUTO_TRACE("Exported interface/included service: type='{}' scope='{}' name='{}' args='{}'"
3897 " relates='{}' relatesType='{}' file='{}' line={} bodyLine={} #tArgLists={}"
3898 " mGrpId={} spec={} proto={} docFile='{}'",
3899 root->type, root->parent()->name, root->name, root->args,
3900 root->relates, root->relatesType, root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(),
3901 root->mGrpId, root->spec, root->proto, root->docFile);
3902
3903 DString rname = removeRedundantWhiteSpace(root->name);
3904
3905 if (!rname.empty())
3906 {
3907 DString scope = root->parent()->name;
3908 ClassDefMutable *cd = getClassMutable(scope);
3909 ASSERT(cd);
3910 if (cd && ((ClassDef::Interface == cd->compoundType()) ||
3911 (ClassDef::Service == cd->compoundType()) ||
3913 {
3915 }
3916 else
3917 {
3918 ASSERT(false); // was checked by scanner.l
3919 }
3920 }
3921 else if (rname.empty())
3922 {
3923 warn(root->fileName,root->startLine,
3924 "Illegal member name found.");
3925 }
3926 }
3927 // can only have these in IDL anyway
3928 switch (root->lang)
3929 {
3930 case SrcLangExt::Unknown: // fall through (root node always is Unknown)
3931 case SrcLangExt::IDL:
3932 for (const auto &e : root->children()) buildInterfaceAndServiceList(e.get());
3933 break;
3934 default:
3935 return; // nothing to do here
3936 }
3937}
3938
3939
3940//----------------------------------------------------------------------
3941// Searches the Entry tree for Function sections.
3942// If found they are stored in their class or in the global list.
3943
3944static void addMethodToClass(const Entry *root,ClassDefMutable *cd,
3945 const DString &rtype,const DString &rname,const DString &rargs,
3946 bool isFriend,
3947 Protection protection,bool stat,Specifier virt,TypeSpecifier spec,
3948 const DString &relates
3949 )
3950{
3951 FileDef *fd=root->fileDef();
3952
3953 DString type = rtype;
3954 DString args = rargs;
3955
3957 name.stripPrefix("::");
3958
3959 MemberType mtype = MemberType::Function;
3960 if (isFriend) mtype=MemberType::Friend;
3961 else if (root->mtype==MethodTypes::Signal) mtype=MemberType::Signal;
3962 else if (root->mtype==MethodTypes::Slot) mtype=MemberType::Slot;
3963 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType::DCOP;
3964
3965 // strip redundant template specifier for constructors
3966 size_t i = DString::npos;
3967 size_t j = DString::npos;
3968 if ((fd==nullptr || fd->getLanguage()==SrcLangExt::Cpp) &&
3969 !name.startsWith("operator ") && // not operator
3970 (i=name.find('<'))!=DString::npos && // containing <
3971 (j=name.find('>'))!=DString::npos && // or >
3972 (j!=i+2 || name.at(i+1)!='=') // but not the C++20 spaceship operator <=>
3973 )
3974 {
3975 name=name.left(i);
3976 }
3977
3978 DString fileName = root->fileName;
3979 if (fileName.empty() && root->tagInfo())
3980 {
3981 fileName = root->tagInfo()->tagName;
3982 }
3983
3984 //printf("root->name='%s; args='%s' root->argList='%s'\n",
3985 // qPrint(root->name),qPrint(args),qPrint(argListToString(root->argList))
3986 // );
3987
3988 // adding class member
3989 Relationship relationship = relates.empty() ? Relationship::Member :
3990 root->relatesType==RelatesType::MemberOf ? Relationship::Foreign :
3991 Relationship::Related ;
3992 auto md = createMemberDef(
3993 fileName,root->startLine,root->startColumn,
3994 type,name,args,root->exception,
3995 protection,virt,
3996 stat && root->relatesType!=RelatesType::MemberOf,
3997 relationship,
3998 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3999 root->argList, root->metaData);
4000 auto mmd = toMemberDefMutable(md.get());
4001 mmd->setTagInfo(root->tagInfo());
4002 mmd->setMemberClass(cd);
4003 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
4004 mmd->setDocsForDefinition(!root->proto);
4005 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
4006 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
4007 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
4008 mmd->setMemberSpecifiers(spec);
4009 mmd->setVhdlSpecifiers(root->vhdlSpec);
4010 mmd->setMemberGroupId(root->mGrpId);
4011 mmd->setTypeConstraints(root->typeConstr);
4012 mmd->setLanguage(root->lang);
4013 mmd->setRequiresClause(root->req);
4014 mmd->setId(root->id);
4015 mmd->setBodyDef(fd);
4016 mmd->setFileDef(fd);
4017 mmd->addSectionsToDefinition(root->anchors);
4018 addIncludeFile(mmd,fd,root);
4019 DString def;
4021 SrcLangExt lang = cd->getLanguage();
4022 DString scopeSeparator=getLanguageSpecificSeparator(lang);
4023 if (scopeSeparator!="::")
4024 {
4025 qualScope = substitute(qualScope,"::",scopeSeparator);
4026 }
4027 if (lang==SrcLangExt::PHP)
4028 {
4029 // for PHP we use Class::method and Namespace\method
4030 scopeSeparator="::";
4031 }
4032 if (!relates.empty() || isFriend || Config_getBool(HIDE_SCOPE_NAMES))
4033 {
4034 if (!type.empty())
4035 {
4036 def=type+" "+name; //+optArgs;
4037 }
4038 else
4039 {
4040 def=name; //+optArgs;
4041 }
4042 }
4043 else
4044 {
4045 if (!type.empty())
4046 {
4047 def=type+" "+qualScope+scopeSeparator+name; //+optArgs;
4048 }
4049 else
4050 {
4051 def=qualScope+scopeSeparator+name; //+optArgs;
4052 }
4053 }
4054 def.stripPrefix("friend ");
4055 mmd->setDefinition(def);
4057 mmd->addQualifiers(root->qualifiers);
4058
4059 AUTO_TRACE("function member: type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
4060 type, qualScope, rname, args, root->proto, def);
4061
4062 // add member to the class cd
4063 cd->insertMember(md.get());
4064 // add file to list of used files
4065 cd->insertUsedFile(fd);
4066
4067 addMemberToGroups(root,md.get());
4069 root->markAsProcessed();
4070 mmd->setRefItems(root->sli);
4071 mmd->setRequirementReferences(root->rqli);
4072
4073 // add member to the global list of all members
4074 //printf("Adding member=%s class=%s\n",qPrint(md->name()),qPrint(cd->name()));
4076 mn->push_back(std::move(md));
4077}
4078
4079//------------------------------------------------------------------------------------------
4080
4081static void addGlobalFunction(const Entry *root,const DString &rname,const DString &sc)
4082{
4083 DString scope = sc;
4084
4085 // new global function
4087 auto md = createMemberDef(
4088 root->fileName,root->startLine,root->startColumn,
4089 root->type,name,root->args,root->exception,
4090 root->protection,root->virt,root->isStatic,Relationship::Member,
4091 MemberType::Function,
4092 !root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
4093 root->argList,root->metaData);
4094 auto mmd = toMemberDefMutable(md.get());
4095 mmd->setTagInfo(root->tagInfo());
4096 mmd->setLanguage(root->lang);
4097 mmd->setId(root->id);
4098 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
4099 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
4100 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
4101 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
4102 mmd->setDocsForDefinition(!root->proto);
4103 mmd->setTypeConstraints(root->typeConstr);
4104 //md->setBody(root->body);
4105 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
4106 FileDef *fd=root->fileDef();
4107 mmd->setBodyDef(fd);
4108 mmd->addSectionsToDefinition(root->anchors);
4109 mmd->setMemberSpecifiers(root->spec);
4110 mmd->setVhdlSpecifiers(root->vhdlSpec);
4111 mmd->setMemberGroupId(root->mGrpId);
4112 mmd->setRequiresClause(root->req);
4113 mmd->setExplicitExternal(root->explicitExternal,root->fileName,root->startLine,root->startColumn);
4114 addIncludeFile(mmd,fd,root);
4115
4116 NamespaceDefMutable *nd = nullptr;
4117 // see if the function is inside a namespace that was not part of
4118 // the name already (in that case nd should be non-zero already)
4119 if (root->parent()->section.isNamespace())
4120 {
4121 //DString nscope=removeAnonymousScopes(root->parent()->name);
4122 DString nscope=root->parent()->name;
4123 if (!nscope.empty())
4124 {
4125 nd = getResolvedNamespaceMutable(nscope);
4126 }
4127 }
4128 else if (root->parent()->section.isGroupDoc() && !scope.empty())
4129 {
4131 }
4132
4133 if (!scope.empty())
4134 {
4136 if (sep!="::")
4137 {
4138 scope = substitute(scope,"::",sep);
4139 }
4140 scope+=sep;
4141 }
4142
4143 if (Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python) scope = "";
4144 DString def;
4145 //DString optArgs = root->argList.empty() ? DString() : root->args;
4146 if (!root->type.empty())
4147 {
4148 def=root->type+" "+scope+name; //+optArgs;
4149 }
4150 else
4151 {
4152 def=scope+name; //+optArgs;
4153 }
4154 AUTO_TRACE("new non-member function type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
4155 root->type,scope,rname,root->args,root->proto,def);
4156 mmd->setDefinition(def);
4158 mmd->addQualifiers(root->qualifiers);
4159
4160 mmd->setRefItems(root->sli);
4161 mmd->setRequirementReferences(root->rqli);
4162 if (nd && !nd->name().empty() && nd->name().at(0)!='@')
4163 {
4164 // add member to namespace
4165 mmd->setNamespace(nd);
4166 nd->insertMember(md.get());
4167 }
4168 if (fd)
4169 {
4170 // add member to the file (we do this even if we have already
4171 // inserted it into the namespace)
4172 mmd->setFileDef(fd);
4173 fd->insertMember(md.get());
4174 }
4175
4176 addMemberToGroups(root,md.get());
4178 if (root->relatesType == RelatesType::Simple) // if this is a relatesalso command,
4179 // allow find Member to pick it up
4180 {
4181 root->markAsProcessed(); // Otherwise we have finished with this entry.
4182 }
4183
4184 // add member to the list of file members
4186 mn->push_back(std::move(md));
4187}
4188
4189//------------------------------------------------------------------------------------------
4190
4191static void buildFunctionList(const Entry *root)
4192{
4193 if (root->section.isFunction())
4194 {
4195 AUTO_TRACE("member function: type='{}' scope='{}' name='{}' args='{}' relates='{}' relatesType='{}'"
4196 " file='{}' line={} bodyLine={} #tArgLists={} mGrpId={}"
4197 " spec={} proto={} docFile='{}'",
4198 root->type, root->parent()->name, root->name, root->args, root->relates, root->relatesType,
4199 root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(), root->mGrpId,
4200 root->spec, root->proto, root->docFile);
4201
4202 bool isFriend=root->type=="friend" || root->type.find("friend ")!=DString::npos;
4203 DString rname = removeRedundantWhiteSpace(root->name);
4204 //printf("rname=%s\n",qPrint(rname));
4205
4206 DString scope;
4207 int index = computeQualifiedIndex(rname);
4208 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
4209 // grouped members are stored with full scope
4210 {
4211 buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
4212 scope=rname.left(index);
4213 rname=rname.mid(index+2);
4214 }
4215 else
4216 {
4217 scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
4218 }
4219 if (!rname.empty() && scope.find('@')==DString::npos)
4220 {
4221 // check if this function's parent is a class
4222 if (root->lang==SrcLangExt::CSharp)
4223 {
4224 scope=mangleCSharpGenericName(scope);
4225 }
4226 else
4227 {
4228 scope=stripTemplateSpecifiersFromScope(scope,false);
4229 }
4230
4231 FileDef *rfd=root->fileDef();
4232
4233 size_t memIndex=rname.rfind("::");
4234
4236 if (cd && scope+"::"==rname.left(scope.length()+2)) // found A::f inside A
4237 {
4238 // strip scope from name
4239 rname=rname.mid(root->parent()->name.length()+2);
4240 }
4241
4242 bool isMember=false;
4243 if (memIndex!=DString::npos)
4244 {
4245 size_t ts=rname.find('<');
4246 size_t te=rname.find('>');
4247 if (memIndex>0 && (ts==DString::npos || te==DString::npos))
4248 {
4249 // note: the following code was replaced by inMember=true to deal with a
4250 // function rname='X::foo' of class X inside a namespace also called X...
4251 // bug id 548175
4252 //nd = Doxygen::namespaceLinkedMap->find(rname.left(memIndex));
4253 //isMember = nd==nullptr;
4254 //if (nd)
4255 //{
4256 // // strip namespace scope from name
4257 // scope=rname.left(memIndex);
4258 // rname=rname.mid(memIndex+2);
4259 //}
4260 isMember = true;
4261 }
4262 else
4263 {
4264 isMember=memIndex<ts || memIndex>te;
4265 }
4266 }
4267
4268 if (!root->parent()->name.empty() && root->parent()->section.isCompound() && cd)
4269 {
4270 AUTO_TRACE_ADD("member '{}' of class '{}'", rname,cd->name());
4271 addMethodToClass(root,cd,root->type,rname,root->args,isFriend,
4272 root->protection,root->isStatic,root->virt,root->spec,root->relates);
4273 }
4274 else if (root->parent()->section.isObjcImpl() && cd)
4275 {
4276 const MemberDef *md = cd->getMemberByName(rname);
4277 if (md)
4278 {
4279 MemberDefMutable *mdm = toMemberDefMutable(const_cast<MemberDef*>(md));
4280 if (mdm)
4281 {
4282 mdm->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
4283 mdm->setBodyDef(root->fileDef());
4284 }
4285 }
4286 }
4287 else if (!root->parent()->section.isCompound() && !root->parent()->section.isObjcImpl() &&
4288 !isMember &&
4289 (root->relates.empty() || root->relatesType==RelatesType::Duplicate) &&
4290 !root->type.startsWith("extern ") && !root->type.startsWith("typedef ")
4291 )
4292 // no member => unrelated function
4293 {
4294 /* check the uniqueness of the function name in the file.
4295 * A file could contain a function prototype and a function definition
4296 * or even multiple function prototypes.
4297 */
4298 bool found=false;
4299 MemberDef *md_found=nullptr;
4301 if (mn)
4302 {
4303 AUTO_TRACE_ADD("function '{}' already found",rname);
4304 for (const auto &imd : *mn)
4305 {
4306 MemberDefMutable *md = toMemberDefMutable(imd.get());
4307 if (md)
4308 {
4309 const NamespaceDef *mnd = md->getNamespaceDef();
4310 NamespaceDef *rnd = nullptr;
4311 //printf("root namespace=%s\n",qPrint(rootNav->parent()->name()));
4312 DString fullScope = scope;
4313 DString parentScope = root->parent()->name;
4314 if (!parentScope.empty() && !leftScopeMatch(parentScope,scope))
4315 {
4316 if (!scope.empty()) fullScope.prepend("::");
4317 fullScope.prepend(parentScope);
4318 }
4319 //printf("fullScope=%s\n",qPrint(fullScope));
4320 rnd = getResolvedNamespace(fullScope);
4321 const FileDef *mfd = md->getFileDef();
4322 DString nsName,rnsName;
4323 if (mnd) nsName = mnd->name();
4324 if (rnd) rnsName = rnd->name();
4325 //printf("matching arguments for %s%s %s%s\n",
4326 // qPrint(md->name()),md->argsString(),qPrint(rname),qPrint(argListToString(root->argList)));
4327 const ArgumentList &mdAl = md->argumentList();
4328 const ArgumentList &mdTempl = md->templateArguments();
4329
4330 // in case of template functions, we need to check if the
4331 // functions have the same number of template parameters
4332 bool sameTemplateArgs = true;
4333 bool matchingReturnTypes = true;
4334 bool sameRequiresClause = true;
4335 if (!mdTempl.empty() && !root->tArgLists.empty())
4336 {
4337 sameTemplateArgs = matchTemplateArguments(mdTempl,root->tArgLists.back());
4338 if (md->typeString()!=removeRedundantWhiteSpace(root->type))
4339 {
4340 matchingReturnTypes = false;
4341 }
4342 if (md->requiresClause()!=root->req)
4343 {
4344 sameRequiresClause = false;
4345 }
4346 }
4347 else if (!mdTempl.empty() || !root->tArgLists.empty())
4348 { // if one has template parameters and the other doesn't then that also counts as a
4349 // difference
4350 sameTemplateArgs = false;
4351 }
4352
4353 bool staticsInDifferentFiles =
4354 root->isStatic && md->isStatic() && root->fileName!=md->getDefFileName();
4355
4356 if (sameTemplateArgs &&
4357 matchingReturnTypes &&
4358 sameRequiresClause &&
4359 !staticsInDifferentFiles &&
4360 matchArguments2(md->getOuterScope(),mfd,md->typeString(),&mdAl,
4361 rnd ? rnd : Doxygen::globalScope,rfd,root->type,&root->argList,
4362 false,root->lang)
4363 )
4364 {
4365 GroupDef *gd=nullptr;
4366 if (!root->groups.empty() && !root->groups.front().groupname.empty())
4367 {
4368 gd = Doxygen::groupLinkedMap->find(root->groups.front().groupname);
4369 }
4370 //printf("match!\n");
4371 //printf("mnd=%p rnd=%p nsName=%s rnsName=%s\n",mnd,rnd,qPrint(nsName),qPrint(rnsName));
4372 // see if we need to create a new member
4373 found=(mnd && rnd && nsName==rnsName) || // members are in the same namespace
4374 ((mnd==nullptr && rnd==nullptr && mfd!=nullptr && // no external reference and
4375 mfd->absFilePath()==root->fileName // prototype in the same file
4376 )
4377 );
4378 // otherwise, allow a duplicate global member with the same argument list
4379 if (!found && gd && gd==md->getGroupDef() && nsName==rnsName)
4380 {
4381 // member is already in the group, so we don't want to add it again.
4382 found=true;
4383 }
4384
4385 AUTO_TRACE_ADD("combining function with prototype found={} in namespace '{}'",found,nsName);
4386
4387 if (found)
4388 {
4389 // merge argument lists
4390 ArgumentList mergedArgList = root->argList;
4391 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.empty());
4392 // merge documentation
4393 if (md->documentation().empty() && !root->doc.empty())
4394 {
4395 if (root->proto)
4396 {
4398 }
4399 else
4400 {
4402 }
4403 }
4404
4405 md->setDocumentation(root->doc,root->docFile,root->docLine);
4407 md->setDocsForDefinition(!root->proto);
4408 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
4409 {
4410 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
4411 md->setBodyDef(rfd);
4412 }
4413
4414 if (md->briefDescription().empty() && !root->brief.empty())
4415 {
4416 md->setArgsString(root->args);
4417 }
4418 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
4419
4421
4423 md->addQualifiers(root->qualifiers);
4424
4425 // merge ingroup specifiers
4426 if (md->getGroupDef()==nullptr && !root->groups.empty())
4427 {
4428 addMemberToGroups(root,md);
4429 }
4430 else if (md->getGroupDef()!=nullptr && root->groups.empty())
4431 {
4432 //printf("existing member is grouped, new member not\n");
4433 }
4434 else if (md->getGroupDef()!=nullptr && !root->groups.empty())
4435 {
4436 //printf("both members are grouped\n");
4437 }
4439
4440 // if md is a declaration and root is the corresponding
4441 // definition, then turn md into a definition.
4442 if (md->isPrototype() && !root->proto)
4443 {
4444 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
4445 md->setPrototype(false,root->fileName,root->startLine,root->startColumn);
4446 }
4447 // if md is already the definition, then add the declaration info
4448 else if (!md->isPrototype() && root->proto)
4449 {
4450 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
4451 }
4452 }
4453 }
4454 }
4455 if (found)
4456 {
4457 md_found = md;
4458 break;
4459 }
4460 }
4461 }
4462 if (!found) /* global function is unique with respect to the file */
4463 {
4464 addGlobalFunction(root,rname,scope);
4465 }
4466 else
4467 {
4468 FileDef *fd=root->fileDef();
4469 if (fd)
4470 {
4471 // add member to the file (we do this even if we have already
4472 // inserted it into the namespace)
4473 fd->insertMember(md_found);
4474 }
4475 }
4476
4477 AUTO_TRACE_ADD("unrelated function type='{}' name='{}' args='{}'",root->type,rname,root->args);
4478 }
4479 else
4480 {
4481 AUTO_TRACE_ADD("function '{}' is not processed",rname);
4482 }
4483 }
4484 else if (rname.empty())
4485 {
4486 warn(root->fileName,root->startLine,
4487 "Illegal member name found."
4488 );
4489 }
4490 }
4491 for (const auto &e : root->children()) buildFunctionList(e.get());
4492}
4493
4494//----------------------------------------------------------------------
4495
4496static void findFriends()
4497{
4498 AUTO_TRACE();
4499 for (const auto &fn : *Doxygen::functionNameLinkedMap) // for each global function name
4500 {
4501 MemberName *mn = Doxygen::memberNameLinkedMap->find(fn->memberName());
4502 if (mn)
4503 { // there are members with the same name
4504 // for each function with that name
4505 for (const auto &ifmd : *fn)
4506 {
4507 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
4508 // for each member with that name
4509 for (const auto &immd : *mn)
4510 {
4511 MemberDefMutable *mmd = toMemberDefMutable(immd.get());
4512 //printf("Checking for matching arguments
4513 // mmd->isRelated()=%d mmd->isFriend()=%d mmd->isFunction()=%d\n",
4514 // mmd->isRelated(),mmd->isFriend(),mmd->isFunction());
4515 if (fmd && mmd &&
4516 (mmd->isFriend() || (mmd->isRelated() && mmd->isFunction())) &&
4517 matchArguments2(mmd->getOuterScope(), mmd->getFileDef(), mmd->typeString(), &mmd->argumentList(),
4518 fmd->getOuterScope(), fmd->getFileDef(), fmd->typeString(), &fmd->argumentList(),
4519 true,mmd->getLanguage()
4520 )
4521
4522 ) // if the member is related and the arguments match then the
4523 // function is actually a friend.
4524 {
4525 AUTO_TRACE_ADD("Merging related global and member '{}' isFriend={} isRelated={} isFunction={}",
4526 mmd->name(),mmd->isFriend(),mmd->isRelated(),mmd->isFunction());
4527 const ArgumentList &mmdAl = mmd->argumentList();
4528 const ArgumentList &fmdAl = fmd->argumentList();
4529 mergeArguments(const_cast<ArgumentList&>(fmdAl),const_cast<ArgumentList&>(mmdAl));
4530
4531 // reset argument lists to add missing default parameters
4532 DString mmdAlStr = argListToString(mmdAl);
4533 DString fmdAlStr = argListToString(fmdAl);
4534 mmd->setArgsString(mmdAlStr);
4535 fmd->setArgsString(fmdAlStr);
4536 mmd->moveDeclArgumentList(std::make_unique<ArgumentList>(mmdAl));
4537 fmd->moveDeclArgumentList(std::make_unique<ArgumentList>(fmdAl));
4538 AUTO_TRACE_ADD("friend args='{}' member args='{}'",argListToString(fmd->argumentList()),argListToString(mmd->argumentList()));
4539
4540 if (!fmd->documentation().empty())
4541 {
4542 mmd->setDocumentation(fmd->documentation(),fmd->docFile(),fmd->docLine());
4543 }
4544 else if (!mmd->documentation().empty())
4545 {
4546 fmd->setDocumentation(mmd->documentation(),mmd->docFile(),mmd->docLine());
4547 }
4548 if (mmd->briefDescription().empty() && !fmd->briefDescription().empty())
4549 {
4550 mmd->setBriefDescription(fmd->briefDescription(),fmd->briefFile(),fmd->briefLine());
4551 }
4552 else if (!mmd->briefDescription().empty() && !fmd->briefDescription().empty())
4553 {
4554 fmd->setBriefDescription(mmd->briefDescription(),mmd->briefFile(),mmd->briefLine());
4555 }
4556 if (!fmd->inbodyDocumentation().empty())
4557 {
4559 }
4560 else if (!mmd->inbodyDocumentation().empty())
4561 {
4563 }
4564 //printf("body mmd %d fmd %d\n",mmd->getStartBodyLine(),fmd->getStartBodyLine());
4565 if (mmd->getStartBodyLine()==-1 && fmd->getStartBodyLine()!=-1)
4566 {
4567 mmd->setBodySegment(fmd->getDefLine(),fmd->getStartBodyLine(),fmd->getEndBodyLine());
4568 mmd->setBodyDef(fmd->getBodyDef());
4569 //mmd->setBodyMember(fmd);
4570 }
4571 else if (mmd->getStartBodyLine()!=-1 && fmd->getStartBodyLine()==-1)
4572 {
4573 fmd->setBodySegment(mmd->getDefLine(),mmd->getStartBodyLine(),mmd->getEndBodyLine());
4574 fmd->setBodyDef(mmd->getBodyDef());
4575 //fmd->setBodyMember(mmd);
4576 }
4578
4580
4581 mmd->addQualifiers(fmd->getQualifiers());
4582 fmd->addQualifiers(mmd->getQualifiers());
4583
4584 }
4585 }
4586 }
4587 }
4588 }
4589}
4590
4591//----------------------------------------------------------------------
4592
4594{
4595 AUTO_TRACE();
4596
4597 // find matching function declaration and definitions.
4598 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4599 {
4600 //printf("memberName=%s count=%zu\n",qPrint(mn->memberName()),mn->size());
4601 /* find a matching function declaration and definition for this function */
4602 for (const auto &imdec : *mn)
4603 {
4604 MemberDefMutable *mdec = toMemberDefMutable(imdec.get());
4605 if (mdec &&
4606 (mdec->isPrototype() ||
4607 (mdec->isVariable() && mdec->isExternal())
4608 ))
4609 {
4610 for (const auto &imdef : *mn)
4611 {
4612 MemberDefMutable *mdef = toMemberDefMutable(imdef.get());
4613 if (mdef && mdec!=mdef &&
4614 mdec->getNamespaceDef()==mdef->getNamespaceDef())
4615 {
4617 }
4618 }
4619 }
4620 }
4621 }
4622}
4623
4624//----------------------------------------------------------------------
4625
4627{
4628 AUTO_TRACE();
4629 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4630 {
4631 MemberDefMutable *mdef=nullptr,*mdec=nullptr;
4632 /* find a matching function declaration and definition for this function */
4633 for (const auto &imd : *mn)
4634 {
4635 MemberDefMutable *md = toMemberDefMutable(imd.get());
4636 if (md)
4637 {
4638 if (md->isPrototype())
4639 mdec=md;
4640 else if (md->isVariable() && md->isExternal())
4641 mdec=md;
4642
4643 if (md->isFunction() && !md->isStatic() && !md->isPrototype())
4644 mdef=md;
4645 else if (md->isVariable() && !md->isExternal() && !md->isStatic())
4646 mdef=md;
4647 }
4648
4649 if (mdef && mdec) break;
4650 }
4651 if (mdef && mdec)
4652 {
4653 const ArgumentList &mdefAl = mdef->argumentList();
4654 const ArgumentList &mdecAl = mdec->argumentList();
4655 if (
4656 matchArguments2(mdef->getOuterScope(),mdef->getFileDef(),mdef->typeString(),const_cast<ArgumentList*>(&mdefAl),
4657 mdec->getOuterScope(),mdec->getFileDef(),mdec->typeString(),const_cast<ArgumentList*>(&mdecAl),
4658 true,mdef->getLanguage()
4659 )
4660 ) /* match found */
4661 {
4662 AUTO_TRACE_ADD("merging references for mdec={} mdef={}",mdec->name(),mdef->name());
4663 mdef->mergeReferences(mdec);
4664 mdec->mergeReferences(mdef);
4665 mdef->mergeReferencedBy(mdec);
4666 mdec->mergeReferencedBy(mdef);
4667 }
4668 }
4669 }
4670}
4671
4672//----------------------------------------------------------------------
4673
4675{
4676 AUTO_TRACE();
4677 // find match between function declaration and definition for
4678 // related functions
4679 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4680 {
4681 /* find a matching function declaration and definition for this function */
4682 // for each global function
4683 for (const auto &imd : *mn)
4684 {
4685 MemberDefMutable *md = toMemberDefMutable(imd.get());
4686 if (md)
4687 {
4688 //printf(" Function '%s'\n",qPrint(md->name()));
4690 if (rmn) // check if there is a member with the same name
4691 {
4692 //printf(" Member name found\n");
4693 // for each member with the same name
4694 for (const auto &irmd : *rmn)
4695 {
4696 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
4697 //printf(" Member found: related='%d'\n",rmd->isRelated());
4698 if (rmd &&
4699 (rmd->isRelated() || rmd->isForeign()) && // related function
4700 matchArguments2( md->getOuterScope(), md->getFileDef(), md->typeString(), &md->argumentList(),
4701 rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmd->argumentList(),
4702 true,md->getLanguage()
4703 )
4704 )
4705 {
4706 AUTO_TRACE_ADD("Found related member '{}'",md->name());
4707 if (rmd->relatedAlso())
4708 md->setRelatedAlso(rmd->relatedAlso());
4709 else if (rmd->isForeign())
4710 md->makeForeign();
4711 else
4712 md->makeRelated();
4713 }
4714 }
4715 }
4716 }
4717 }
4718 }
4719}
4720
4721//----------------------------------------------------------------------
4722
4724{
4725 AUTO_TRACE();
4726 for (const auto &[qualifiedName,bodyInfo] : Doxygen::staticInitMap)
4727 {
4728 size_t i=qualifiedName.rfind("::");
4729 if (i!=std::string::npos)
4730 {
4731 DString scope = qualifiedName.substr(0,i);
4732 DString name = qualifiedName.substr(i+2);
4734 if (mn)
4735 {
4736 for (const auto &imd : *mn)
4737 {
4738 MemberDefMutable *md = toMemberDefMutable(imd.get());
4739 if (md && md->qualifiedName().str()==qualifiedName && md->isVariable())
4740 {
4741 AUTO_TRACE_ADD("found static member {} body [{}..{}]\n",
4742 md->qualifiedName(),bodyInfo.startLine,bodyInfo.endLine);
4743 md->setBodySegment(bodyInfo.defLine,
4744 bodyInfo.startLine,
4745 bodyInfo.endLine);
4746 }
4747 }
4748 }
4749 }
4750 }
4751}
4752
4753//----------------------------------------------------------------------
4754
4755/*! make a dictionary of all template arguments of class cd
4756 * that are part of the base class name.
4757 * Example: A template class A with template arguments <R,S,T>
4758 * that inherits from B<T,T,S> will have T and S in the dictionary.
4759 */
4760static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments,const std::string &name)
4761{
4762 std::map<std::string,int> templateNames;
4763 int count=0;
4764 for (const Argument &arg : templateArguments)
4765 {
4766 static const reg::Ex re(R"(\a[\w:]*)");
4767 reg::Iterator it(name,re);
4769 for (; it!=end ; ++it)
4770 {
4771 const auto &match = *it;
4772 std::string n = match.str();
4773 if (n==arg.name.str())
4774 {
4775 if (templateNames.find(n)==templateNames.end())
4776 {
4777 templateNames.emplace(n,count);
4778 }
4779 }
4780 }
4781 }
4782 return templateNames;
4783}
4784
4785/*! Searches a class from within \a context and \a cd and returns its
4786 * definition if found (otherwise nullptr is returned).
4787 */
4789{
4790 ClassDef *result=nullptr;
4791 if (cd==nullptr)
4792 {
4793 return result;
4794 }
4795 FileDef *fd=cd->getFileDef();
4796 SymbolResolver resolver(fd);
4797 if (context && cd!=context)
4798 {
4799 result = const_cast<ClassDef*>(resolver.resolveClass(context,name,true,true));
4800 }
4801 //printf("1. result=%p\n",result);
4802 if (result==nullptr)
4803 {
4804 result = const_cast<ClassDef*>(resolver.resolveClass(cd,name,true,true));
4805 }
4806 //printf("2. result=%p\n",result);
4807 if (result==nullptr) // try direct class, needed for namespaced classes imported via tag files (see bug624095)
4808 {
4809 result = getClass(name);
4810 }
4811 //printf("3. result=%p\n",result);
4812 //printf("** Trying to find %s within context %s class %s result=%s lookup=%p\n",
4813 // qPrint(name),
4814 // context ? qPrint(context->name()) : "<none>",
4815 // cd ? qPrint(cd->name()) : "<none>",
4816 // result ? qPrint(result->name()) : "<none>",
4817 // Doxygen::classLinkedMap->find(name)
4818 // );
4819 return result;
4820}
4821
4822
4823static void findUsedClassesForClass(const Entry *root,
4824 Definition *context,
4825 ClassDefMutable *masterCd,
4826 ClassDefMutable *instanceCd,
4827 bool isArtificial,
4828 const ArgumentList *actualArgs = nullptr,
4829 const TemplateNameMap &templateNames = TemplateNameMap()
4830 )
4831{
4832 AUTO_TRACE();
4833 const ArgumentList &formalArgs = masterCd->templateArguments();
4834 for (auto &mni : masterCd->memberNameInfoLinkedMap())
4835 {
4836 for (auto &mi : *mni)
4837 {
4838 const MemberDef *md=mi->memberDef();
4839 if (md->isVariable() || md->isObjCProperty()) // for each member variable in this class
4840 {
4841 AUTO_TRACE_ADD("Found variable '{}' in class '{}'",md->name(),masterCd->name());
4842 DString type = normalizeNonTemplateArgumentsInString(md->typeString(),masterCd,formalArgs);
4843 DString typedefValue = md->getLanguage()==SrcLangExt::Java ? type : resolveTypeDef(masterCd,type);
4844 if (!typedefValue.empty())
4845 {
4846 type = typedefValue;
4847 }
4848 int pos=0;
4849 DString usedClassName;
4850 DString templSpec;
4851 bool found=false;
4852 // the type can contain template variables, replace them if present
4853 type = substituteTemplateArgumentsInString(type,formalArgs,actualArgs);
4854
4855 //printf(" template substitution gives=%s\n",qPrint(type));
4856 while (!found && extractClassNameFromType(type,pos,usedClassName,templSpec,root->lang)!=-1)
4857 {
4858 // find the type (if any) that matches usedClassName
4859 SymbolResolver resolver(masterCd->getFileDef());
4860 const ClassDefMutable *typeCd = resolver.resolveClassMutable(masterCd,usedClassName,false,true);
4861 //printf("====> usedClassName=%s -> typeCd=%s\n",
4862 // qPrint(usedClassName),typeCd?qPrint(typeCd->name()):"<none>");
4863 if (typeCd)
4864 {
4865 usedClassName = typeCd->name();
4866 }
4867
4868 // replace any namespace aliases
4869 replaceNamespaceAliases(usedClassName);
4870 // add any template arguments to the class
4871 DString usedName = removeRedundantWhiteSpace(usedClassName+templSpec);
4872 //printf(" usedName=%s usedClassName=%s templSpec=%s\n",qPrint(usedName),qPrint(usedClassName),qPrint(templSpec));
4873
4874 TemplateNameMap formTemplateNames;
4875 if (templateNames.empty())
4876 {
4877 formTemplateNames = getTemplateArgumentsInName(formalArgs,usedName.str());
4878 }
4879 BaseInfo bi(usedName,Protection::Public,Specifier::Normal);
4880 findClassRelation(root,context,instanceCd,&bi,formTemplateNames,TemplateInstances,isArtificial);
4881
4882 for (const Argument &arg : masterCd->templateArguments())
4883 {
4884 if (arg.name==usedName) // type is a template argument
4885 {
4886 ClassDef *usedCd = Doxygen::hiddenClassLinkedMap->find(usedName);
4887 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4888 if (usedCd==nullptr)
4889 {
4890 usedCdm = toClassDefMutable(
4891 Doxygen::hiddenClassLinkedMap->add(usedName,
4893 masterCd->getDefFileName(),masterCd->getDefLine(),
4894 masterCd->getDefColumn(),
4895 usedName,
4896 ClassDef::Class)));
4897 if (usedCdm)
4898 {
4899 //printf("making %s a template argument!!!\n",qPrint(usedCd->name()));
4900 usedCdm->makeTemplateArgument();
4901 usedCdm->setUsedOnly(true);
4902 usedCdm->setLanguage(masterCd->getLanguage());
4903 usedCd = usedCdm;
4904 }
4905 }
4906 if (usedCd)
4907 {
4908 found=true;
4909 AUTO_TRACE_ADD("case 1: adding used class '{}'", usedCd->name());
4910 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4911 if (usedCdm)
4912 {
4913 if (isArtificial) usedCdm->setArtificial(true);
4914 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4915 }
4916 }
4917 }
4918 }
4919
4920 if (!found)
4921 {
4922 ClassDef *usedCd=findClassWithinClassContext(context,masterCd,usedName);
4923 //printf("Looking for used class %s: result=%s master=%s\n",
4924 // qPrint(usedName),usedCd?qPrint(usedCd->name()):"<none>",masterCd?qPrint(masterCd->name()):"<none>");
4925
4926 if (usedCd)
4927 {
4928 found=true;
4929 AUTO_TRACE_ADD("case 2: adding used class '{}'", usedCd->name());
4930 instanceCd->addUsedClass(usedCd,md->name(),md->protection()); // class exists
4931 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4932 if (usedCdm)
4933 {
4934 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4935 }
4936 }
4937 }
4938 }
4939 if (!found && !type.empty()) // used class is not documented in any scope
4940 {
4942 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4943 if (usedCd==nullptr && !Config_getBool(HIDE_UNDOC_RELATIONS))
4944 {
4945 if (type.endsWith("(*") || type.endsWith("(^")) // type is a function pointer
4946 {
4947 type+=md->argsString();
4948 }
4949 AUTO_TRACE_ADD("New undocumented used class '{}'", type);
4950 usedCdm = toClassDefMutable(
4953 masterCd->getDefFileName(),masterCd->getDefLine(),
4954 masterCd->getDefColumn(),
4955 type,ClassDef::Class)));
4956 if (usedCdm)
4957 {
4958 usedCdm->setUsedOnly(true);
4959 usedCdm->setLanguage(masterCd->getLanguage());
4960 usedCd = usedCdm;
4961 }
4962 }
4963 if (usedCd)
4964 {
4965 AUTO_TRACE_ADD("case 3: adding used class '{}'", usedCd->name());
4966 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4967 if (usedCdm)
4968 {
4969 if (isArtificial) usedCdm->setArtificial(true);
4970 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4971 }
4972 }
4973 }
4974 }
4975 }
4976 }
4977}
4978
4980 const Entry *root,
4981 Definition *context,
4982 ClassDefMutable *masterCd,
4983 ClassDefMutable *instanceCd,
4985 bool isArtificial,
4986 const ArgumentList *actualArgs = nullptr,
4987 const TemplateNameMap &templateNames=TemplateNameMap()
4988 )
4989{
4990 AUTO_TRACE("name={}",root->name);
4991 // The base class could ofcouse also be a non-nested class
4992 const ArgumentList &formalArgs = masterCd->templateArguments();
4993 for (const BaseInfo &bi : root->extends)
4994 {
4995 //printf("masterCd=%s bi.name='%s' #actualArgs=%d\n",
4996 // qPrint(masterCd->localName()),qPrint(bi.name),actualArgs ? (int)actualArgs->size() : -1);
4997 TemplateNameMap formTemplateNames;
4998 if (templateNames.empty())
4999 {
5000 formTemplateNames = getTemplateArgumentsInName(formalArgs,bi.name.str());
5001 }
5002 BaseInfo tbi = bi;
5003 tbi.name = substituteTemplateArgumentsInString(bi.name,formalArgs,actualArgs);
5004 //printf("masterCd=%p instanceCd=%p bi->name=%s tbi.name=%s\n",(void*)masterCd,(void*)instanceCd,qPrint(bi.name),qPrint(tbi.name));
5005
5006 if (mode==DocumentedOnly)
5007 {
5008 // find a documented base class in the correct scope
5009 if (!findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,DocumentedOnly,isArtificial))
5010 {
5011 // 1.8.2: decided to show inheritance relations even if not documented,
5012 // we do make them artificial, so they do not appear in the index
5013 //if (!Config_getBool(HIDE_UNDOC_RELATIONS))
5014 bool b = Config_getBool(HIDE_UNDOC_RELATIONS) ? true : isArtificial;
5015 //{
5016 // no documented base class -> try to find an undocumented one
5017 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,Undocumented,b);
5018 //}
5019 }
5020 }
5021 else if (mode==TemplateInstances)
5022 {
5023 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,TemplateInstances,isArtificial);
5024 }
5025 }
5026}
5027
5028//----------------------------------------------------------------------
5029
5030static void findTemplateInstanceRelation(const Entry *root,
5031 Definition *context,
5032 ClassDefMutable *templateClass,const DString &templSpec,
5033 const TemplateNameMap &templateNames,
5034 bool isArtificial)
5035{
5036 AUTO_TRACE("Derived from template '{}' with parameters '{}' isArtificial={}",
5037 templateClass->name(),templSpec,isArtificial);
5038
5039 DString tempArgsStr = tempArgListToString(templateClass->templateArguments(),root->lang,false);
5040 bool existingClass = templSpec==tempArgsStr;
5041 if (existingClass) return; // avoid recursion
5042
5043 bool freshInstance=false;
5044 ClassDefMutable *instanceClass = toClassDefMutable(
5045 templateClass->insertTemplateInstance(
5046 root->fileName,root->startLine,root->startColumn,templSpec,freshInstance));
5047 if (instanceClass)
5048 {
5049 if (freshInstance)
5050 {
5051 instanceClass->setArtificial(true);
5052 instanceClass->setLanguage(root->lang);
5053
5054 AUTO_TRACE_ADD("found fresh instance '{}'",instanceClass->name());
5055 instanceClass->setTemplateBaseClassNames(templateNames);
5056
5057 // search for new template instances caused by base classes of
5058 // instanceClass
5059 auto it_pair = g_classEntries.equal_range(templateClass->name().str());
5060 for (auto it=it_pair.first ; it!=it_pair.second ; ++it)
5061 {
5062 const Entry *templateRoot = it->second;
5063 AUTO_TRACE_ADD("template root found '{}' templSpec='{}'",templateRoot->name,templSpec);
5064 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(root->lang,templSpec);
5065 findBaseClassesForClass(templateRoot,context,templateClass,instanceClass,
5066 TemplateInstances,isArtificial,templArgs.get(),templateNames);
5067
5068 findUsedClassesForClass(templateRoot,context,templateClass,instanceClass,
5069 isArtificial,templArgs.get(),templateNames);
5070 }
5071 }
5072 else
5073 {
5074 AUTO_TRACE_ADD("instance already exists");
5075 }
5076 }
5077}
5078
5079//----------------------------------------------------------------------
5080
5081static void resolveTemplateInstanceInType(const Entry *root,const Definition *scope,const MemberDef *md)
5082{
5083 // For a statement like 'using X = T<A>', add a template instance 'T<A>' as a symbol, so it can
5084 // be used to match arguments (see issue #11111)
5085 AUTO_TRACE();
5086 DString ttype = md->typeString();
5087 ttype.stripPrefix("typedef ");
5088 if (size_t ti=ttype.find('<'); ti!=DString::npos)
5089 {
5090 DString templateClassName = ttype.left(ti);
5091 SymbolResolver resolver(root->fileDef());
5092 ClassDefMutable *baseClass = resolver.resolveClassMutable(scope ? scope : Doxygen::globalScope,
5093 templateClassName, true, true);
5094 AUTO_TRACE_ADD("templateClassName={} baseClass={}",templateClassName,baseClass?baseClass->name():"<none>");
5095 if (baseClass)
5096 {
5097 const ArgumentList &tl = baseClass->templateArguments();
5098 TemplateNameMap templateNames = getTemplateArgumentsInName(tl,templateClassName.str());
5100 baseClass,
5101 ttype.mid(ti),
5102 templateNames,
5103 baseClass->isArtificial());
5104 }
5105 }
5106}
5107
5108//----------------------------------------------------------------------
5109
5110static bool isRecursiveBaseClass(const DString &scope,const DString &name)
5111{
5112 DString n=name;
5113 if (size_t index=n.find('<'); index!=DString::npos)
5114 {
5115 n=n.left(index);
5116 }
5117 bool result = rightScopeMatch(scope,n);
5118 return result;
5119}
5120
5122{
5123 if (name.empty()) return 0;
5124 int l = static_cast<int>(name.length());
5125 if (name[l-1]=='>') // search backward to find the matching <, allowing nested <...> and strings.
5126 {
5127 int count=1;
5128 int i=l-2;
5129 char insideQuote=0;
5130 while (count>0 && i>=0)
5131 {
5132 char c = name[i--];
5133 switch (c)
5134 {
5135 case '>': if (!insideQuote) count++; break;
5136 case '<': if (!insideQuote) count--; break;
5137 case '\'': if (!insideQuote) insideQuote=c;
5138 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
5139 break;
5140 case '"': if (!insideQuote) insideQuote=c;
5141 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
5142 break;
5143 default: break;
5144 }
5145 }
5146 if (i>=0) l=i+1;
5147 }
5148 return l;
5149}
5150
5152 const Entry *root,
5153 Definition *context,
5154 ClassDefMutable *cd,
5155 const BaseInfo *bi,
5156 const TemplateNameMap &templateNames,
5158 bool isArtificial
5159 )
5160{
5161 AUTO_TRACE("name={} base={} isArtificial={} mode={}",cd->name(),bi->name,isArtificial,(int)mode);
5162
5163 DString biName=bi->name;
5164 bool explicitGlobalScope=false;
5165 if (biName.startsWith("::")) // explicit global scope
5166 {
5167 biName=biName.mid(2);
5168 explicitGlobalScope=true;
5169 }
5170
5171 Entry *parentNode=root->parent();
5172 bool lastParent=false;
5173 do // for each parent scope, starting with the largest scope
5174 // (in case of nested classes)
5175 {
5176 DString scopeName= parentNode ? parentNode->name : DString();
5177 int scopeOffset=explicitGlobalScope ? 0 : static_cast<int>(scopeName.length());
5178 do // try all parent scope prefixes, starting with the largest scope
5179 {
5180 //printf("scopePrefix='%s' biName='%s'\n",
5181 // qPrint(scopeName.left(scopeOffset)),qPrint(biName));
5182
5183 DString baseClassName=biName;
5184 if (scopeOffset>0)
5185 {
5186 baseClassName.prepend(scopeName.left(scopeOffset)+"::");
5187 }
5188 if (root->lang==SrcLangExt::CSharp)
5189 {
5190 baseClassName = mangleCSharpGenericName(baseClassName);
5191 }
5192 AUTO_TRACE_ADD("cd='{}' baseClassName='{}'",cd->name(),baseClassName);
5193 SymbolResolver resolver(cd->getFileDef());
5194 ClassDefMutable *baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
5195 baseClassName,
5196 mode==Undocumented,
5197 true
5198 );
5199 const MemberDef *baseClassTypeDef = resolver.getTypedef();
5200 DString templSpec = resolver.getTemplateSpec();
5201 //printf("baseClassName=%s baseClass=%p cd=%p explicitGlobalScope=%d\n",
5202 // qPrint(baseClassName),baseClass,cd,explicitGlobalScope);
5203 //printf(" scope='%s' baseClassName='%s' baseClass=%s templSpec=%s\n",
5204 // cd ? qPrint(cd->name()):"<none>",
5205 // qPrint(baseClassName),
5206 // baseClass?qPrint(baseClass->name()):"<none>",
5207 // qPrint(templSpec)
5208 // );
5209 //if (baseClassName.left(root->name.length())!=root->name ||
5210 // baseClassName.at(root->name.length())!='<'
5211 // ) // Check for base class with the same name.
5212 // // If found then look in the outer scope for a match
5213 // // and prevent recursion.
5214 if (!isRecursiveBaseClass(root->name,baseClassName)
5215 || explicitGlobalScope
5216 // sadly isRecursiveBaseClass always true for UNO IDL ifc/svc members
5217 // (i.e. this is needed for addInterfaceOrServiceToServiceOrSingleton)
5218 || (root->lang==SrcLangExt::IDL &&
5219 (root->section.isExportedInterface() ||
5220 root->section.isIncludedService()))
5221 )
5222 {
5223 AUTO_TRACE_ADD("class relation '{}' inherited/used by '{}' found prot={} virt={} templSpec='{}'",
5224 baseClassName, root->name, bi->prot, bi->virt, templSpec);
5225
5226 int i=findTemplateSpecializationPosition(baseClassName);
5227 size_t si=baseClassName.rfind("::",i);
5228 if (si==DString::npos) si=0;
5229 if (baseClass==nullptr && static_cast<size_t>(i)!=baseClassName.length())
5230 // base class has template specifiers
5231 {
5232 // TODO: here we should try to find the correct template specialization
5233 // but for now, we only look for the unspecialized base class.
5234 int e=findEndOfTemplate(baseClassName,i+1);
5235 //printf("baseClass==0 i=%d e=%d\n",i,e);
5236 if (e!=-1) // end of template was found at e
5237 {
5238 templSpec = removeRedundantWhiteSpace(baseClassName.mid(i,e-i));
5239 baseClassName = baseClassName.left(i)+baseClassName.mid(e);
5240 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
5241 baseClassName,
5242 mode==Undocumented,
5243 true
5244 );
5245 baseClassTypeDef = resolver.getTypedef();
5246 //printf("baseClass=%p -> baseClass=%s templSpec=%s\n",
5247 // baseClass,qPrint(baseClassName),qPrint(templSpec));
5248 }
5249 }
5250 else if (baseClass && !templSpec.empty()) // we have a known class, but also
5251 // know it is a template, so see if
5252 // we can also link to the explicit
5253 // instance (for instance if a class
5254 // derived from a template argument)
5255 {
5256 //printf("baseClass=%s templSpec=%s\n",qPrint(baseClass->name()),qPrint(templSpec));
5257 ClassDefMutable *templClass=getClassMutable(baseClass->name()+templSpec);
5258 if (templClass)
5259 {
5260 // use the template instance instead of the template base.
5261 baseClass = templClass;
5262 templSpec.clear();
5263 }
5264 }
5265
5266 //printf("cd=%p baseClass=%p\n",cd,baseClass);
5267 bool found=baseClass!=nullptr && (baseClass!=cd || mode==TemplateInstances);
5268 AUTO_TRACE_ADD("1. found={}",found);
5269 if (!found && si!=DString::npos)
5270 {
5271 // replace any namespace aliases
5272 replaceNamespaceAliases(baseClassName);
5273 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
5274 baseClassName,
5275 mode==Undocumented,
5276 true
5277 );
5278 baseClassTypeDef = resolver.getTypedef();
5279 found=baseClass!=nullptr && baseClass!=cd;
5280 if (found) templSpec = resolver.getTemplateSpec();
5281 }
5282 AUTO_TRACE_ADD("2. found={}",found);
5283
5284 if (!found)
5285 {
5286 baseClass=toClassDefMutable(findClassWithinClassContext(context,cd,baseClassName));
5287 //printf("findClassWithinClassContext(%s,%s)=%p\n",
5288 // qPrint(cd->name()),qPrint(baseClassName),baseClass);
5289 found = baseClass!=nullptr && baseClass!=cd;
5290
5291 }
5292 AUTO_TRACE_ADD("3. found={}",found);
5293 if (!found)
5294 {
5295 // for PHP the "use A\B as C" construct map class C to A::B, so we lookup
5296 // the class name also in the alias mapping.
5297 auto it = Doxygen::namespaceAliasMap.find(baseClassName.str());
5298 if (it!=Doxygen::namespaceAliasMap.end()) // see if it is indeed a class.
5299 {
5300 baseClass=getClassMutable(it->second.alias);
5301 found = baseClass!=nullptr && baseClass!=cd;
5302 }
5303 }
5304 bool isATemplateArgument = templateNames.find(biName.str())!=templateNames.end();
5305
5306 AUTO_TRACE_ADD("4. found={}",found);
5307 if (found)
5308 {
5309 AUTO_TRACE_ADD("Documented base class '{}' templSpec='{}'",biName,templSpec);
5310 // add base class to this class
5311
5312 // if templSpec is not empty then we should "instantiate"
5313 // the template baseClass. A new ClassDef should be created
5314 // to represent the instance. To be able to add the (instantiated)
5315 // members and documentation of a template class
5316 // (inserted in that template class at a later stage),
5317 // the template should know about its instances.
5318 // the instantiation process, should be done in a recursive way,
5319 // since instantiating a template may introduce new inheritance
5320 // relations.
5321 if (!templSpec.empty() && mode==TemplateInstances)
5322 {
5323 // if baseClass is actually a typedef then we should not
5324 // instantiate it, since typedefs are in a different namespace
5325 // see bug531637 for an example where this would otherwise hang
5326 // Doxygen
5327 if (baseClassTypeDef==nullptr)
5328 {
5329 //printf(" => findTemplateInstanceRelation: %s\n",qPrint(baseClass->name()));
5330 findTemplateInstanceRelation(root,context,baseClass,templSpec,templateNames,baseClass->isArtificial());
5331 }
5332 }
5333 else if (mode==DocumentedOnly || mode==Undocumented)
5334 {
5335 //printf(" => insert base class\n");
5336 DString usedName;
5337 if (baseClassTypeDef)
5338 {
5339 usedName=biName;
5340 //printf("***** usedName=%s templSpec=%s\n",qPrint(usedName),qPrint(templSpec));
5341 }
5342 Protection prot = bi->prot;
5343 if (Config_getBool(SIP_SUPPORT)) prot=Protection::Public;
5344 if (cd!=baseClass && !cd->isSubClass(baseClass) && baseClass->isBaseClass(cd,true,templSpec)==0) // check for recursion, see bug690787
5345 {
5346 AUTO_TRACE_ADD("insertBaseClass name={} prot={} virt={} templSpec={}",usedName,prot,bi->virt,templSpec);
5347 cd->insertBaseClass(baseClass,usedName,prot,bi->virt,templSpec);
5348 // add this class as super class to the base class
5349 baseClass->insertSubClass(cd,prot,bi->virt,templSpec);
5350 }
5351 else
5352 {
5353 warn(root->fileName,root->startLine,
5354 "Detected potential recursive class relation "
5355 "between class {} and base class {}!",
5356 cd->name(),baseClass->name()
5357 );
5358 }
5359 }
5360 return true;
5361 }
5362 else if (mode==Undocumented && (scopeOffset==0 || isATemplateArgument))
5363 {
5364 AUTO_TRACE_ADD("New undocumented base class '{}' baseClassName='{}' templSpec='{}' isArtificial={}",
5365 biName,baseClassName,templSpec,isArtificial);
5366 baseClass=nullptr;
5367 if (isATemplateArgument)
5368 {
5369 baseClass = toClassDefMutable(Doxygen::hiddenClassLinkedMap->find(baseClassName));
5370 if (baseClass==nullptr) // not found (or alias)
5371 {
5372 baseClass= toClassDefMutable(
5373 Doxygen::hiddenClassLinkedMap->add(baseClassName,
5374 createClassDef(root->fileName,root->startLine,root->startColumn,
5375 baseClassName,
5376 ClassDef::Class)));
5377 if (baseClass) // really added (not alias)
5378 {
5379 if (isArtificial) baseClass->setArtificial(true);
5380 baseClass->setLanguage(root->lang);
5381 }
5382 }
5383 }
5384 else
5385 {
5386 baseClass = toClassDefMutable(Doxygen::classLinkedMap->find(baseClassName));
5387 //printf("*** classDDict->find(%s)=%p biName=%s templSpec=%s\n",
5388 // qPrint(baseClassName),baseClass,qPrint(biName),qPrint(templSpec));
5389 if (baseClass==nullptr) // not found (or alias)
5390 {
5391 baseClass = toClassDefMutable(
5392 Doxygen::classLinkedMap->add(baseClassName,
5393 createClassDef(root->fileName,root->startLine,root->startColumn,
5394 baseClassName,
5395 ClassDef::Class)));
5396 if (baseClass) // really added (not alias)
5397 {
5398 if (isArtificial) baseClass->setArtificial(true);
5399 baseClass->setLanguage(root->lang);
5400 si = baseClassName.rfind("::");
5401 if (si!=DString::npos) // class is nested
5402 {
5403 Definition *sd = findScopeFromQualifiedName(Doxygen::globalScope,baseClassName.left(si),nullptr,root->tagInfo());
5404 if (sd==nullptr || sd==Doxygen::globalScope) // outer scope not found
5405 {
5406 baseClass->setArtificial(true); // see bug678139
5407 }
5408 }
5409 }
5410 }
5411 }
5412 if (baseClass)
5413 {
5414 if (biName.endsWith("-p"))
5415 {
5416 biName="<"+biName.left(biName.length()-2)+">";
5417 }
5418 if (!cd->isSubClass(baseClass) && cd!=baseClass && cd->isBaseClass(baseClass,true,templSpec)==0) // check for recursion
5419 {
5420 AUTO_TRACE_ADD("insertBaseClass name={} prot={} virt={} templSpec={}",biName,bi->prot,bi->virt,templSpec);
5421 // add base class to this class
5422 cd->insertBaseClass(baseClass,biName,bi->prot,bi->virt,templSpec);
5423 // add this class as super class to the base class
5424 baseClass->insertSubClass(cd,bi->prot,bi->virt,templSpec);
5425 }
5426 // the undocumented base was found in this file
5427 baseClass->insertUsedFile(root->fileDef());
5428
5429 Definition *scope = buildScopeFromQualifiedName(baseClass->name(),root->lang,nullptr);
5430 if (scope!=baseClass)
5431 {
5432 baseClass->setOuterScope(scope);
5433 }
5434
5435 if (baseClassName.endsWith("-p"))
5436 {
5438 }
5439 return true;
5440 }
5441 else
5442 {
5443 AUTO_TRACE_ADD("Base class '{}' not created (alias?)",biName);
5444 }
5445 }
5446 else
5447 {
5448 AUTO_TRACE_ADD("Base class '{}' not found",biName);
5449 }
5450 }
5451 else
5452 {
5453 if (mode!=TemplateInstances)
5454 {
5455 warn(root->fileName,root->startLine,
5456 "Detected potential recursive class relation "
5457 "between class {} and base class {}!",
5458 root->name,baseClassName
5459 );
5460 }
5461 // for mode==TemplateInstance this case is quite common and
5462 // indicates a relation between a template class and a template
5463 // instance with the same name.
5464 }
5465 if (scopeOffset==0)
5466 {
5467 scopeOffset=-1;
5468 }
5469 else
5470 {
5471 size_t o = scopeName.rfind("::",scopeOffset-1);
5472 scopeOffset = o!=DString::npos ? static_cast<int>(o) : 0;
5473 }
5474 //printf("new scopeOffset='%d'",scopeOffset);
5475 } while (scopeOffset>=0);
5476
5477 if (parentNode==nullptr)
5478 {
5479 lastParent=true;
5480 }
5481 else
5482 {
5483 parentNode=parentNode->parent();
5484 }
5485 } while (lastParent);
5486
5487 return false;
5488}
5489
5490//----------------------------------------------------------------------
5491// Computes the base and super classes for each class in the tree
5492
5493static bool isClassSection(const Entry *root)
5494{
5495 if ( !root->name.empty() )
5496 {
5497 if (root->section.isCompound())
5498 // is it a compound (class, struct, union, interface ...)
5499 {
5500 return true;
5501 }
5502 else if (root->section.isCompoundDoc())
5503 // is it a documentation block with inheritance info.
5504 {
5505 bool hasExtends = !root->extends.empty();
5506 if (hasExtends) return true;
5507 }
5508 }
5509 return false;
5510}
5511
5512
5513/*! Builds a dictionary of all entry nodes in the tree starting with \a root
5514 */
5515static void findClassEntries(const Entry *root)
5516{
5517 if (isClassSection(root))
5518 {
5519 g_classEntries.emplace(root->name.str(),root);
5520 }
5521 for (const auto &e : root->children()) findClassEntries(e.get());
5522}
5523
5524static DString extractClassName(const Entry *root)
5525{
5526 // strip any anonymous scopes first
5529 if (size_t i=bName.find('<'); (root->lang==SrcLangExt::CSharp || root->lang==SrcLangExt::Java) && i!=DString::npos)
5530 {
5531 // a Java/C# generic class looks like a C++ specialization, so we need to strip the
5532 // template part before looking for matches
5533 if (root->lang==SrcLangExt::CSharp)
5534 {
5535 bName = mangleCSharpGenericName(root->name);
5536 }
5537 else
5538 {
5539 bName = bName.left(i);
5540 }
5541 }
5542 return bName;
5543}
5544
5545/*! Using the dictionary build by findClassEntries(), this
5546 * function will look for additional template specialization that
5547 * exists as inheritance relations only. These instances will be
5548 * added to the template they are derived from.
5549 */
5551{
5552 AUTO_TRACE();
5553 ClassDefSet visitedClasses;
5554 for (const auto &[name,root] : g_classEntries)
5555 {
5556 DString bName = extractClassName(root);
5557 ClassDefMutable *cdm = getClassMutable(bName);
5558 if (cdm)
5559 {
5560 findBaseClassesForClass(root,cdm,cdm,cdm,TemplateInstances,false);
5561 }
5562 }
5563}
5564
5566{
5567 AUTO_TRACE("root->name={} cd={}",root->name,cd->name());
5568 size_t i = root->name.find('<');
5569 size_t j = root->name.rfind('>');
5570 size_t k = j!=DString::npos ? root->name.find("::",j+1) : DString::npos; // A<T::B> => ok, A<T>::B => nok
5571 if (i!=DString::npos && j!=DString::npos && k==DString::npos && root->lang!=SrcLangExt::CSharp && root->lang!=SrcLangExt::Java)
5572 {
5573 ClassDefMutable *master = getClassMutable(root->name.left(i));
5574 if (master && master!=cd && !cd->templateMaster())
5575 {
5576 AUTO_TRACE_ADD("class={} master={}",cd->name(),cd->templateMaster()?cd->templateMaster()->name():"<none>",master->name());
5577 cd->setTemplateMaster(master);
5578 master->insertExplicitTemplateInstance(cd,root->name.mid(i));
5579 }
5580 }
5581}
5582
5584{
5585 AUTO_TRACE();
5586 for (const auto &[name,root] : g_classEntries)
5587 {
5588 DString bName = extractClassName(root);
5589 ClassDefMutable *cdm = getClassMutable(bName);
5590 if (cdm)
5591 {
5592 findUsedClassesForClass(root,cdm,cdm,cdm,true);
5594 cdm->addTypeConstraints();
5595 }
5596 }
5597}
5598
5600{
5601 AUTO_TRACE();
5602 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5603 {
5604 if (!nd->hasDocumentation())
5605 {
5606 if ((EntryType::guessSection(nd->getDefFileName()).isHeader() ||
5607 nd->getLanguage() == SrcLangExt::Fortran) && // Fortran doesn't have header files.
5608 !Config_getBool(HIDE_UNDOC_NAMESPACES) // undocumented namespaces are visible
5609 )
5610 {
5611 warn_undoc(nd->getDefFileName(),nd->getDefLine(), "{} {} is not documented.",
5612 nd->getLanguage() == SrcLangExt::Fortran ? "Module" : "Namespace",
5613 nd->name());
5614 }
5615 }
5616 }
5617}
5618
5620{
5621 AUTO_TRACE();
5622 for (const auto &[name,root] : g_classEntries)
5623 {
5624 DString bName = extractClassName(root);
5625 ClassDefMutable *cd = getClassMutable(bName);
5626 if (cd)
5627 {
5628 findBaseClassesForClass(root,cd,cd,cd,DocumentedOnly,false);
5629 }
5630 size_t numMembers = cd ? cd->memberNameInfoLinkedMap().size() : 0;
5631 if ((cd==nullptr || (!cd->hasDocumentation() && !cd->isReference())) && numMembers>0 && !bName.endsWith("::"))
5632 {
5633 if (!root->name.empty() && root->name.find('@')==DString::npos && // normal name
5634 (EntryType::guessSection(root->fileName).isHeader() ||
5635 Config_getBool(EXTRACT_LOCAL_CLASSES)) && // not defined in source file
5636 protectionLevelVisible(root->protection) && // hidden by protection
5637 !Config_getBool(HIDE_UNDOC_CLASSES) // undocumented class are visible
5638 )
5639 warn_undoc(root->fileName,root->startLine, "Compound {} is not documented.", root->name);
5640 }
5641 }
5642}
5643
5645{
5646 AUTO_TRACE();
5647 for (const auto &[name,root] : g_classEntries)
5648 {
5652 // strip any anonymous scopes first
5653 if (cd && !cd->getTemplateInstances().empty())
5654 {
5655 AUTO_TRACE_ADD("Template class '{}'",cd->name());
5656 for (const auto &ti : cd->getTemplateInstances()) // for each template instance
5657 {
5658 ClassDefMutable *tcd=toClassDefMutable(ti.classDef);
5659 if (tcd && tcd->isImplicitTemplateInstance()) // (partial) specializations have their own base classes
5660 {
5661 AUTO_TRACE_ADD("Template instance '{}'",tcd->name());
5662 DString templSpec = ti.templSpec;
5663 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(tcd->getLanguage(),templSpec);
5664 for (const BaseInfo &bi : root->extends)
5665 {
5666 // check if the base class is a template argument
5667 BaseInfo tbi = bi;
5668 const ArgumentList &tl = cd->templateArguments();
5669 if (!tl.empty())
5670 {
5671 TemplateNameMap baseClassNames = tcd->getTemplateBaseClassNames();
5672 TemplateNameMap templateNames = getTemplateArgumentsInName(tl,bi.name.str());
5673 // for each template name that we inherit from we need to
5674 // substitute the formal with the actual arguments
5675 TemplateNameMap actualTemplateNames;
5676 for (const auto &tn_kv : templateNames)
5677 {
5678 size_t templIndex = tn_kv.second;
5679 Argument actArg;
5680 bool hasActArg=false;
5681 if (templIndex<templArgs->size())
5682 {
5683 actArg=templArgs->at(templIndex);
5684 hasActArg=true;
5685 }
5686 if (hasActArg &&
5687 baseClassNames.find(actArg.type.str())!=baseClassNames.end() &&
5688 actualTemplateNames.find(actArg.type.str())==actualTemplateNames.end()
5689 )
5690 {
5691 actualTemplateNames.emplace(actArg.type.str(),static_cast<int>(templIndex));
5692 }
5693 }
5694
5695 tbi.name = substituteTemplateArgumentsInString(bi.name,tl,templArgs.get());
5696 // find a documented base class in the correct scope
5697 if (!findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,DocumentedOnly,false))
5698 {
5699 // no documented base class -> try to find an undocumented one
5700 findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,Undocumented,true);
5701 }
5702 }
5703 }
5704 }
5705 }
5706 }
5707 }
5708}
5709
5710//-----------------------------------------------------------------------
5711// compute the references (anchors in HTML) for each function in the file
5712
5714{
5715 AUTO_TRACE();
5716 for (const auto &cd : *Doxygen::classLinkedMap)
5717 {
5718 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5719 if (cdm)
5720 {
5721 cdm->computeAnchors();
5722 }
5723 }
5724 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5725 {
5726 for (const auto &fd : *fn)
5727 {
5728 fd->computeAnchors();
5729 }
5730 }
5731 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5732 {
5734 if (ndm)
5735 {
5736 ndm->computeAnchors();
5737 }
5738 }
5739 for (const auto &gd : *Doxygen::groupLinkedMap)
5740 {
5741 gd->computeAnchors();
5742 }
5743}
5744
5745//----------------------------------------------------------------------
5746
5747
5748template<typename Func>
5749static void applyToAllDefinitions(Func func)
5750{
5751 for (const auto &cd : *Doxygen::classLinkedMap)
5752 {
5753 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5754 if (cdm)
5755 {
5756 func(cdm);
5757 }
5758 }
5759
5760 for (const auto &cd : *Doxygen::conceptLinkedMap)
5761 {
5762 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
5763 if (cdm)
5764 {
5765 func(cdm);
5766 }
5767 }
5768
5769 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5770 {
5771 for (const auto &fd : *fn)
5772 {
5773 func(fd.get());
5774 }
5775 }
5776
5777 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5778 {
5780 if (ndm)
5781 {
5782 func(ndm);
5783 }
5784 }
5785
5786 for (const auto &gd : *Doxygen::groupLinkedMap)
5787 {
5788 func(gd.get());
5789 }
5790
5791 for (const auto &pd : *Doxygen::pageLinkedMap)
5792 {
5793 func(pd.get());
5794 }
5795
5796 for (const auto &dd : *Doxygen::dirLinkedMap)
5797 {
5798 func(dd.get());
5799 }
5800
5801 func(&ModuleManager::instance());
5802}
5803
5804//----------------------------------------------------------------------
5805
5807{
5808 AUTO_TRACE();
5809 applyToAllDefinitions([](auto* obj) { obj->addRequirementReferences(); });
5810}
5811
5812//----------------------------------------------------------------------
5813
5815{
5816 AUTO_TRACE();
5817 applyToAllDefinitions([](auto* obj) { obj->addListReferences(); });
5818}
5819
5820
5821//----------------------------------------------------------------------
5822
5824{
5825 AUTO_TRACE();
5827 {
5828 rl->generatePage();
5829 }
5830}
5831
5832//----------------------------------------------------------------------
5833// Copy the documentation in entry 'root' to member definition 'md' and
5834// set the function declaration of the member to 'funcDecl'. If the boolean
5835// over_load is set the standard overload text is added.
5836
5837static void addMemberDocs(const Entry *root,
5838 MemberDefMutable *md, const DString &funcDecl,
5839 const ArgumentList *al,
5840 bool over_load,
5841 TypeSpecifier spec
5842 )
5843{
5844 if (md==nullptr) return;
5845 AUTO_TRACE("scope='{}' name='{}' args='{}' funcDecl='{}' mSpec={}",
5846 root->parent()->name,md->name(),md->argsString(),funcDecl,spec);
5847 if (!root->section.isDoc()) // @fn or @var does not need to specify the complete definition, so don't overwrite it
5848 {
5849 DString fDecl=funcDecl;
5850 // strip extern specifier
5851 fDecl.stripPrefix("extern ");
5852 md->setDefinition(fDecl);
5853 }
5855 md->addQualifiers(root->qualifiers);
5857 const NamespaceDef *nd=md->getNamespaceDef();
5858 DString fullName;
5859 if (cd)
5860 fullName = cd->name();
5861 else if (nd)
5862 fullName = nd->name();
5863
5864 if (!fullName.empty()) fullName+="::";
5865 fullName+=md->name();
5866 FileDef *rfd=root->fileDef();
5867
5868 // TODO determine scope based on root not md
5869 Definition *rscope = md->getOuterScope();
5870
5871 const ArgumentList &mdAl = md->argumentList();
5872 if (al)
5873 {
5874 ArgumentList mergedAl = *al;
5875 //printf("merging arguments (1) docs=%d\n",root->doc.empty());
5876 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedAl,!root->doc.empty());
5877 }
5878 else
5879 {
5880 if (
5881 matchArguments2( md->getOuterScope(), md->getFileDef(),md->typeString(),const_cast<ArgumentList*>(&mdAl),
5882 rscope,rfd,root->type,&root->argList,
5883 true, root->lang
5884 )
5885 )
5886 {
5887 //printf("merging arguments (2)\n");
5888 ArgumentList mergedArgList = root->argList;
5889 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.empty());
5890 }
5891 }
5892 if (over_load) // the \overload keyword was used
5893 {
5895 if (!root->doc.empty())
5896 {
5897 doc+="<p>";
5898 doc+=root->doc;
5899 }
5900 md->setDocumentation(doc,root->docFile,root->docLine);
5902 md->setDocsForDefinition(!root->proto);
5903 }
5904 else
5905 {
5906 //printf("overwrite!\n");
5907 md->setDocumentation(root->doc,root->docFile,root->docLine);
5908 md->setDocsForDefinition(!root->proto);
5909
5910 //printf("overwrite!\n");
5911 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
5912
5913 if (
5914 (md->inbodyDocumentation().empty() ||
5915 !root->parent()->name.empty()
5916 ) && !root->inbodyDocs.empty()
5917 )
5918 {
5920 }
5921 }
5922
5923 //printf("initializer: '%s'(isEmpty=%d) '%s'(isEmpty=%d)\n",
5924 // qPrint(md->initializer()),md->initializer().empty(),
5925 // qPrint(root->initializer),root->initializer.empty()
5926 // );
5927 std::string rootInit = root->initializer.str();
5928 if (md->initializer().empty() && !rootInit.empty())
5929 {
5930 //printf("setInitializer\n");
5931 md->setInitializer(rootInit);
5932 }
5933 if (md->requiresClause().empty() && !root->req.empty())
5934 {
5935 md->setRequiresClause(root->req);
5936 }
5937
5938 md->setMaxInitLines(root->initLines);
5939
5940 if (rfd)
5941 {
5942 if ((md->getStartBodyLine()==-1 && root->bodyLine!=-1)
5943 )
5944 {
5945 //printf("Setting new body segment [%d,%d]\n",root->bodyLine,root->endBodyLine);
5946 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
5947 md->setBodyDef(rfd);
5948 }
5949
5950 md->setRefItems(root->sli);
5951 md->setRequirementReferences(root->rqli);
5952 }
5953
5955 md->addQualifiers(root->qualifiers);
5956
5957 md->mergeMemberSpecifiers(spec);
5959 addMemberToGroups(root,md);
5960 addIncludeFile(md,rfd,root);
5962 if (cd) cd->insertUsedFile(rfd);
5963 //printf("root->mGrpId=%d\n",root->mGrpId);
5964 if (root->mGrpId!=-1)
5965 {
5966 if (md->getMemberGroupId()!=-1)
5967 {
5968 if (md->getMemberGroupId()!=root->mGrpId)
5969 {
5970 warn(root->fileName,root->startLine,
5971 "member {} belongs to two different groups. The second one found here will be ignored.",
5972 md->name()
5973 );
5974 }
5975 }
5976 else // set group id
5977 {
5978 //printf("setMemberGroupId=%d md=%s\n",root->mGrpId,qPrint(md->name()));
5979 md->setMemberGroupId(root->mGrpId);
5980 }
5981 }
5982 md->addQualifiers(root->qualifiers);
5983}
5984
5985//----------------------------------------------------------------------
5986// find a class definition given the scope name and (optionally) a
5987// template list specifier
5988
5990 const DString &scopeName)
5991{
5992 SymbolResolver resolver(fd);
5993 const ClassDef *tcd = resolver.resolveClass(nd,scopeName,true,true);
5994 //printf("findClassDefinition(fd=%s,ns=%s,scopeName=%s)='%s'\n",
5995 // qPrint(fd?fd->name():""),qPrint(nd?nd->name():""),
5996 // qPrint(scopeName),qPrint(tcd?tcd->name():""));
5997 return tcd;
5998}
5999
6000//----------------------------------------------------------------------------
6001// Returns true, if the entry belongs to the group of the member definition,
6002// otherwise false.
6003
6004static bool isEntryInGroupOfMember(const Entry *root,const MemberDef *md,bool allowNoGroup=false)
6005{
6006 const GroupDef *gd = md->getGroupDef();
6007 if (!gd)
6008 {
6009 return allowNoGroup;
6010 }
6011
6012 for (const auto &g : root->groups)
6013 {
6014 if (g.groupname == gd->name())
6015 {
6016 return true; // matching group
6017 }
6018 }
6019
6020 return false;
6021}
6022
6023//----------------------------------------------------------------------
6024// Adds the documentation contained in 'root' to a global function
6025// with name 'name' and argument list 'args' (for overloading) and
6026// function declaration 'decl' to the corresponding member definition.
6027
6028static bool findGlobalMember(const Entry *root,
6029 const DString &namespaceName,
6030 const DString &type,
6031 const DString &name,
6032 const DString &tempArg,
6033 const DString &,
6034 const DString &decl,
6035 TypeSpecifier /* spec */)
6036{
6037 AUTO_TRACE("namespace='{}' type='{}' name='{}' tempArg='{}' decl='{}'",namespaceName,type,name,tempArg,decl);
6038 DString n=name;
6039 if (n.empty()) return false;
6040 if (n.find("::")!=DString::npos) return false; // skip undefined class members
6041 MemberName *mn=Doxygen::functionNameLinkedMap->find(n+tempArg); // look in function dictionary
6042 if (mn==nullptr)
6043 {
6044 mn=Doxygen::functionNameLinkedMap->find(n); // try without template arguments
6045 }
6046 if (mn) // function name defined
6047 {
6048 AUTO_TRACE_ADD("Found symbol name");
6049 //int count=0;
6050 bool found=false;
6051 for (const auto &md : *mn)
6052 {
6053 // If the entry has groups, then restrict the search to members which are
6054 // in one of the groups of the entry. If md is not associated with a group yet,
6055 // allow this documentation entry to add the group info.
6056 if (!root->groups.empty() && !isEntryInGroupOfMember(root, md.get(), true))
6057 {
6058 continue;
6059 }
6060
6061 const NamespaceDef *nd=nullptr;
6062 if (md->isAlias() && md->getOuterScope() &&
6063 md->getOuterScope()->definitionType()==Definition::TypeNamespace)
6064 {
6065 nd = toNamespaceDef(md->getOuterScope());
6066 }
6067 else
6068 {
6069 nd = md->getNamespaceDef();
6070 }
6071
6072 // special case for strong enums
6073 size_t enumNamePos=0;
6074 if (nd && md->isEnumValue() && (enumNamePos=namespaceName.rfind("::"))!=DString::npos)
6075 { // md part of a strong enum in a namespace?
6076 DString enumName = namespaceName.mid(enumNamePos+2);
6077 if (namespaceName.left(enumNamePos)==nd->name())
6078 {
6080 if (enumMn)
6081 {
6082 for (const auto &emd : *enumMn)
6083 {
6084 found = emd->isStrong() && md->getEnumScope()==emd.get();
6085 if (found)
6086 {
6087 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,nullptr,false,root->spec);
6088 break;
6089 }
6090 }
6091 }
6092 }
6093 if (found)
6094 {
6095 break;
6096 }
6097 }
6098 else if (nd==nullptr && md->isEnumValue()) // md part of global strong enum?
6099 {
6100 MemberName *enumMn=Doxygen::functionNameLinkedMap->find(namespaceName);
6101 if (enumMn)
6102 {
6103 for (const auto &emd : *enumMn)
6104 {
6105 found = emd->isStrong() && md->getEnumScope()==emd.get();
6106 if (found)
6107 {
6108 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,nullptr,false,root->spec);
6109 break;
6110 }
6111 }
6112 }
6113 }
6114
6115 const FileDef *fd=root->fileDef();
6116 //printf("File %s\n",fd ? qPrint(fd->name()) : "<none>");
6118 if (fd)
6119 {
6120 nl = fd->getUsedNamespaces();
6121 }
6122 //printf("NamespaceList %p\n",nl);
6123
6124 // search in the list of namespaces that are imported via a
6125 // using declaration
6126 bool viaUsingDirective = nd && nl.find(nd->qualifiedName())!=nullptr;
6127
6128 if ((namespaceName.empty() && nd==nullptr) || // not in a namespace
6129 (nd && nd->name()==namespaceName) || // or in the same namespace
6130 viaUsingDirective // member in 'using' namespace
6131 )
6132 {
6133 AUTO_TRACE_ADD("Try to add member '{}' to scope '{}'",md->name(),namespaceName);
6134
6135 NamespaceDef *rnd = nullptr;
6136 if (!namespaceName.empty()) rnd = Doxygen::namespaceLinkedMap->find(namespaceName);
6137
6138 const ArgumentList &mdAl = md.get()->argumentList();
6139 bool matching=
6140 (mdAl.empty() && root->argList.empty()) ||
6141 md->isVariable() || md->isTypedef() || /* in case of function pointers */
6142 matchArguments2(md->getOuterScope(),md->getFileDef(),md->typeString(),&mdAl,
6143 rnd ? rnd : Doxygen::globalScope,fd,root->type,&root->argList,
6144 false,root->lang);
6145
6146 // for template members we need to check if the number of
6147 // template arguments is the same, otherwise we are dealing with
6148 // different functions.
6149 if (matching && !root->tArgLists.empty())
6150 {
6151 const ArgumentList &mdTempl = md->templateArguments();
6152 if (root->tArgLists.back().size()!=mdTempl.size())
6153 {
6154 matching=false;
6155 }
6156 }
6157
6158 //printf("%s<->%s\n",
6159 // qPrint(argListToString(md->argumentList())),
6160 // qPrint(argListToString(root->argList)));
6161
6162 // For static members we also check if the comment block was found in
6163 // the same file. This is needed because static members with the same
6164 // name can be in different files. Thus it would be wrong to just
6165 // put the comment block at the first syntactically matching member. If
6166 // the comment block belongs to a group of the static member, then add
6167 // the documentation even if it is in a different file.
6168 if (matching && md->isStatic() &&
6169 md->getDefFileName()!=root->fileName &&
6170 mn->size()>1 &&
6171 !isEntryInGroupOfMember(root,md.get()))
6172 {
6173 matching = false;
6174 }
6175
6176 // for template member we also need to check the return type and requires
6177 if (!md->templateArguments().empty() && !root->tArgLists.empty())
6178 {
6179 //printf("Comparing return types '%s'<->'%s'\n",
6180 // md->typeString(),type);
6181 //printf("%s: Comparing '%s'<=>'%s'\n",qPrint(md->name()),qPrint(md->requiresClause()),qPrint(root->req));
6182 if (md->templateArguments().size()!=root->tArgLists.back().size() ||
6183 md->typeString()!=type ||
6184 md->requiresClause()!=root->req)
6185 {
6186 //printf(" ---> no matching\n");
6187 matching = false;
6188 }
6189 }
6190
6191 if (matching) // add docs to the member
6192 {
6193 AUTO_TRACE_ADD("Match found");
6194 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,&root->argList,false,root->spec);
6195 found=true;
6196 break;
6197 }
6198 }
6199 }
6200 if (!found && root->relatesType!=RelatesType::Duplicate && root->section.isFunction()) // no match
6201 {
6202 DString fullFuncDecl=decl;
6203 if (!root->argList.empty()) fullFuncDecl+=argListToString(root->argList,true);
6204 DString warnMsg = "no matching file member found for \n"+fullFuncDecl;
6205 if (mn->size()>0)
6206 {
6207 warnMsg+="\nPossible candidates:";
6208 for (const auto &md : *mn)
6209 {
6210 warnMsg+="\n '";
6211 warnMsg+=replaceAnonymousScopes(md->declaration());
6212 warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
6213 }
6214 }
6215 warn(root->fileName,root->startLine, "{}", qPrint(warnMsg));
6216 }
6217 }
6218 else // got docs for an undefined member!
6219 {
6220 if (root->type!="friend class" &&
6221 root->type!="friend struct" &&
6222 root->type!="friend union" &&
6223 root->type!="friend" &&
6224 (!Config_getBool(TYPEDEF_HIDES_STRUCT) ||
6225 root->type.find("typedef ")==DString::npos)
6226 )
6227 {
6228 warn(root->fileName,root->startLine,
6229 "documented symbol '{}' was not declared or defined.",qPrint(decl)
6230 );
6231 }
6232 }
6233 return true;
6234}
6235
6237 const ArgumentLists &srcTempArgLists,
6238 const ArgumentLists &dstTempArgLists
6239 )
6240{
6241 auto srcIt = srcTempArgLists.begin();
6242 auto dstIt = dstTempArgLists.begin();
6243 while (srcIt!=srcTempArgLists.end() && dstIt!=dstTempArgLists.end())
6244 {
6245 if ((*srcIt).size()!=(*dstIt).size()) return true;
6246 ++srcIt;
6247 ++dstIt;
6248 }
6249 return false;
6250}
6251
6252static bool scopeIsTemplate(const Definition *d)
6253{
6254 bool result=false;
6255 //printf("> scopeIsTemplate(%s)\n",qPrint(d?d->name():"null"));
6257 {
6258 auto cd = toClassDef(d);
6259 result = cd->templateArguments().hasParameters() || cd->templateMaster()!=nullptr ||
6261 }
6262 //printf("< scopeIsTemplate=%d\n",result);
6263 return result;
6264}
6265
6267 const ArgumentLists &srcTempArgLists,
6268 const ArgumentLists &dstTempArgLists,
6269 const std::string &src
6270 )
6271{
6272 std::string dst;
6273 static const reg::Ex re(R"(\a\w*)");
6274 reg::Iterator it(src,re);
6276 //printf("type=%s\n",qPrint(sa->type));
6277 size_t p=0;
6278 for (; it!=end ; ++it) // for each word in srcType
6279 {
6280 const auto &match = *it;
6281 size_t i = match.position();
6282 size_t l = match.length();
6283 bool found=false;
6284 dst+=src.substr(p,i-p);
6285 std::string name=match.str();
6286
6287 auto srcIt = srcTempArgLists.begin();
6288 auto dstIt = dstTempArgLists.begin();
6289 while (srcIt!=srcTempArgLists.end() && !found)
6290 {
6291 const ArgumentList *tdAli = nullptr;
6292 std::vector<Argument>::const_iterator tdaIt;
6293 if (dstIt!=dstTempArgLists.end())
6294 {
6295 tdAli = &(*dstIt);
6296 tdaIt = tdAli->begin();
6297 ++dstIt;
6298 }
6299
6300 const ArgumentList &tsaLi = *srcIt;
6301 for (auto tsaIt = tsaLi.begin(); tsaIt!=tsaLi.end() && !found; ++tsaIt)
6302 {
6303 Argument tsa = *tsaIt;
6304 const Argument *tda = nullptr;
6305 if (tdAli && tdaIt!=tdAli->end())
6306 {
6307 tda = &(*tdaIt);
6308 ++tdaIt;
6309 }
6310 //if (tda) printf("tsa=%s|%s tda=%s|%s\n",
6311 // qPrint(tsa.type),qPrint(tsa.name),
6312 // qPrint(tda->type),qPrint(tda->name));
6313 if (name==tsa.name.str())
6314 {
6315 if (tda && tda->name.empty())
6316 {
6317 DString tdaName = tda->name;
6318 DString tdaType = tda->type;
6319 int vc=0;
6320 if (tdaType.startsWith("class ")) vc=6;
6321 else if (tdaType.startsWith("typename ")) vc=9;
6322 if (vc>0) // convert type=="class T" to type=="class" name=="T"
6323 {
6324 tdaName = tdaType.mid(vc);
6325 }
6326 if (!tdaName.empty())
6327 {
6328 name=tdaName.str(); // substitute
6329 found=true;
6330 }
6331 }
6332 }
6333 }
6334
6335 //printf(" srcList='%s' dstList='%s faList='%s'\n",
6336 // qPrint(argListToString(srclali.current())),
6337 // qPrint(argListToString(dstlali.current())),
6338 // funcTempArgList ? qPrint(argListToString(funcTempArgList)) : "<none>");
6339 ++srcIt;
6340 }
6341 dst+=name;
6342 p=i+l;
6343 }
6344 dst+=src.substr(p);
6345 //printf(" substituteTemplatesInString(%s)=%s\n",
6346 // qPrint(src),qPrint(dst));
6347 return dst;
6348}
6349
6351 const ArgumentLists &srcTempArgLists,
6352 const ArgumentLists &dstTempArgLists,
6353 const ArgumentList &src,
6354 ArgumentList &dst
6355 )
6356{
6357 auto dstIt = dst.begin();
6358 for (const Argument &sa : src)
6359 {
6360 DString dstType = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.type.str());
6361 DString dstArray = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.array.str());
6362 if (dstIt == dst.end())
6363 {
6364 Argument da = sa;
6365 da.type = dstType;
6366 da.array = dstArray;
6367 dst.push_back(da);
6368 dstIt = dst.end();
6369 }
6370 else
6371 {
6372 Argument da = *dstIt;
6373 da.type = dstType;
6374 da.array = dstArray;
6375 ++dstIt;
6376 }
6377 }
6382 srcTempArgLists,dstTempArgLists,
6383 src.trailingReturnType().str()));
6384 dst.setIsDeleted(src.isDeleted());
6385 dst.setRefQualifier(src.refQualifier());
6386 dst.setNoParameters(src.noParameters());
6387 //printf("substituteTemplatesInArgList: replacing %s with %s\n",
6388 // qPrint(argListToString(src)),qPrint(argListToString(dst))
6389 // );
6390}
6391
6392//-------------------------------------------------------------------------------------------
6393
6394static void addLocalObjCMethod(const Entry *root,
6395 const DString &scopeName,
6396 const DString &funcType,const DString &funcName,const DString &funcArgs,
6397 const DString &exceptions,const DString &funcDecl,
6398 TypeSpecifier spec)
6399{
6400 AUTO_TRACE();
6401 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
6402 ClassDefMutable *cd=nullptr;
6403 if (Config_getBool(EXTRACT_LOCAL_METHODS) && (cd=getClassMutable(scopeName)))
6404 {
6405 AUTO_TRACE_ADD("Local objective C method '{}' scopeName='{}'",root->name,scopeName);
6406 auto md = createMemberDef(
6407 root->fileName,root->startLine,root->startColumn,
6408 funcType,funcName,funcArgs,exceptions,
6409 root->protection,root->virt,root->isStatic,Relationship::Member,
6410 MemberType::Function,ArgumentList(),root->argList,root->metaData);
6411 auto mmd = toMemberDefMutable(md.get());
6412 mmd->setTagInfo(root->tagInfo());
6413 mmd->setLanguage(root->lang);
6414 mmd->setId(root->id);
6415 mmd->makeImplementationDetail();
6416 mmd->setMemberClass(cd);
6417 mmd->setDefinition(funcDecl);
6419 mmd->addQualifiers(root->qualifiers);
6420 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6421 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6422 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6423 mmd->setDocsForDefinition(!root->proto);
6424 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6425 mmd->addSectionsToDefinition(root->anchors);
6426 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6427 FileDef *fd=root->fileDef();
6428 mmd->setBodyDef(fd);
6429 mmd->setMemberSpecifiers(spec);
6430 mmd->setVhdlSpecifiers(root->vhdlSpec);
6431 mmd->setMemberGroupId(root->mGrpId);
6432 cd->insertMember(md.get());
6433 cd->insertUsedFile(fd);
6434 mmd->setRefItems(root->sli);
6435 mmd->setRequirementReferences(root->rqli);
6436 addIncludeFile(mmd,fd,root);
6437
6439 mn->push_back(std::move(md));
6440 }
6441 else
6442 {
6443 // local objective C method found for class without interface
6444 }
6445}
6446
6447//-------------------------------------------------------------------------------------------
6448
6449static void addMemberFunction(const Entry *root,
6450 MemberName *mn,
6451 const DString &scopeName,
6452 const DString &namespaceName,
6453 const DString &className,
6454 const DString &funcTyp,
6455 const DString &funcName,
6456 const DString &funcArgs,
6457 const DString &funcTempList,
6458 const DString &exceptions,
6459 const DString &type,
6460 const DString &args,
6461 bool isFriend,
6462 TypeSpecifier spec,
6463 const DString &relates,
6464 const DString &funcDecl,
6465 bool overloaded,
6466 bool isFunc)
6467{
6468 AUTO_TRACE();
6469 DString funcType = funcTyp;
6470 int count=0;
6471 int noMatchCount=0;
6472 bool memFound=false;
6473 for (const auto &imd : *mn)
6474 {
6475 MemberDefMutable *md = toMemberDefMutable(imd.get());
6476 if (md==nullptr) continue;
6478 if (cd==nullptr) continue;
6479 //AUTO_TRACE_ADD("member definition found, scope needed='{}' scope='{}' args='{}' fileName='{}'",
6480 // scopeName, cd->name(), md->argsString(), root->fileName);
6481 FileDef *fd=root->fileDef();
6482 NamespaceDef *nd=nullptr;
6483 if (!namespaceName.empty()) nd=getResolvedNamespace(namespaceName);
6484
6485 //printf("scopeName %s->%s\n",qPrint(scopeName),
6486 // qPrint(stripTemplateSpecifiersFromScope(scopeName,false)));
6487
6488 // if the member we are searching for is an enum value that is part of
6489 // a "strong" enum, we need to look into the fields of the enum for a match
6490 size_t enumNamePos=0;
6491 if (md->isEnumValue() && (enumNamePos=className.rfind("::"))!=DString::npos)
6492 {
6493 DString enumName = className.mid(enumNamePos+2);
6494 DString fullScope = className.left(enumNamePos);
6495 if (!namespaceName.empty()) fullScope.prepend(namespaceName+"::");
6496 if (fullScope==cd->name())
6497 {
6498 MemberName *enumMn=Doxygen::memberNameLinkedMap->find(enumName);
6499 //printf("enumMn(%s)=%p\n",qPrint(className),(void*)enumMn);
6500 if (enumMn)
6501 {
6502 for (const auto &emd : *enumMn)
6503 {
6504 memFound = emd->isStrong() && md->getEnumScope()==emd.get();
6505 if (memFound)
6506 {
6507 addMemberDocs(root,md,funcDecl,nullptr,overloaded,spec);
6508 count++;
6509 }
6510 if (memFound) break;
6511 }
6512 }
6513 }
6514 }
6515 if (memFound) break;
6516
6517 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
6518 if (tcd==nullptr && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
6519 {
6520 // don't be fooled by anonymous scopes
6521 tcd=cd;
6522 }
6523 //printf("Looking for %s inside nd=%s result=%s cd=%s\n",
6524 // qPrint(scopeName),nd?qPrint(nd->name()):"<none>",tcd?qPrint(tcd->name()):"",qPrint(cd->name()));
6525
6526 if (cd && tcd==cd) // member's classes match
6527 {
6528 AUTO_TRACE_ADD("class definition '{}' found",cd->name());
6529
6530 // get the template parameter lists found at the member declaration
6531 ArgumentLists declTemplArgs = cd->getTemplateParameterLists();
6532 const ArgumentList &templAl = md->templateArguments();
6533 if (!templAl.empty())
6534 {
6535 declTemplArgs.push_back(templAl);
6536 }
6537
6538 // get the template parameter lists found at the member definition
6539 const ArgumentLists &defTemplArgs = root->tArgLists;
6540 //printf("defTemplArgs=%p\n",defTemplArgs);
6541
6542 // do we replace the decl argument lists with the def argument lists?
6543 bool substDone=false;
6544 ArgumentList argList;
6545
6546 /* substitute the occurrences of class template names in the
6547 * argument list before matching
6548 */
6549 const ArgumentList &mdAl = md->argumentList();
6550 if (declTemplArgs.size()>0 && declTemplArgs.size()==defTemplArgs.size())
6551 {
6552 /* the function definition has template arguments
6553 * and the class definition also has template arguments, so
6554 * we must substitute the template names of the class by that
6555 * of the function definition before matching.
6556 */
6557 substituteTemplatesInArgList(declTemplArgs,defTemplArgs,mdAl,argList);
6558
6559 substDone=true;
6560 }
6561 else /* no template arguments, compare argument lists directly */
6562 {
6563 argList = mdAl;
6564 }
6565
6566 bool matching=
6567 md->isVariable() || md->isTypedef() || // needed for function pointers
6569 md->getClassDef(),md->getFileDef(),md->typeString(),&argList,
6570 cd,fd,root->type,&root->argList,
6571 true,root->lang);
6572
6573 AUTO_TRACE_ADD("matching '{}'<=>'{}' className='{}' namespaceName='{}' result={}",
6574 argListToString(argList,true),argListToString(root->argList,true),className,namespaceName,matching);
6575
6576 if (md->getLanguage()==SrcLangExt::ObjC && md->isVariable() && root->section.isFunction())
6577 {
6578 matching = false; // don't match methods and attributes with the same name
6579 }
6580
6581 // for template member we also need to check the return type
6582 if (!md->templateArguments().empty() && !root->tArgLists.empty())
6583 {
6584 DString memType = md->typeString();
6585 memType.stripPrefix("static "); // see bug700696
6586 // a member defined outside its namespace may use the namespace-qualified
6587 // name for its return type ("N::C" instead of "C"), while the in-class
6588 // declaration uses the unqualified name, so strip the leading namespace
6589 // before comparing the return types, see issue #12379
6590 funcType=substitute(stripTemplateSpecifiersFromScope(funcType,true),
6591 className+"::",""); // see bug700693 & bug732594
6592 memType=substitute(stripTemplateSpecifiersFromScope(memType,true),
6593 className+"::",""); // see bug758900
6594 if (!namespaceName.empty())
6595 {
6596 funcType.stripPrefix(namespaceName+"::");
6597 memType.stripPrefix(namespaceName+"::");
6598 }
6599 if (memType=="auto" && !argList.trailingReturnType().empty())
6600 {
6601 memType = argList.trailingReturnType();
6602 memType.stripPrefix(" -> ");
6603 }
6604 if (funcType=="auto" && !root->argList.trailingReturnType().empty())
6605 {
6606 funcType = root->argList.trailingReturnType();
6607 funcType.stripPrefix(" -> ");
6609 substDone=true;
6610 }
6611 AUTO_TRACE_ADD("Comparing return types '{}'<->'{}' #args {}<->{}",
6612 memType,funcType,md->templateArguments().size(),root->tArgLists.back().size());
6613 if (md->templateArguments().size()!=root->tArgLists.back().size() || memType!=funcType)
6614 {
6615 //printf(" ---> no matching\n");
6616 matching = false;
6617 }
6618 }
6619 else if (defTemplArgs.size()>declTemplArgs.size())
6620 {
6621 AUTO_TRACE_ADD("Different number of template arguments {} vs {}",defTemplArgs.size(),declTemplArgs.size());
6622 // avoid matching a non-template function in a template class against a
6623 // template function with the same name and parameters, see issue #10184
6624 substDone = false;
6625 matching = false;
6626 }
6627 bool rootIsUserDoc = root->section.isMemberDoc();
6628 bool classIsTemplate = scopeIsTemplate(md->getClassDef());
6629 bool mdIsTemplate = md->templateArguments().hasParameters();
6630 bool classOrMdIsTemplate = mdIsTemplate || classIsTemplate;
6631 bool rootIsTemplate = !root->tArgLists.empty();
6632 //printf("classIsTemplate=%d mdIsTemplate=%d rootIsTemplate=%d\n",classIsTemplate,mdIsTemplate,rootIsTemplate);
6633 if (!rootIsUserDoc && // don't check out-of-line @fn references, see bug722457
6634 (mdIsTemplate || rootIsTemplate) && // either md or root is a template
6635 ((classOrMdIsTemplate && !rootIsTemplate) || (!classOrMdIsTemplate && rootIsTemplate))
6636 )
6637 {
6638 // Method with template return type does not match method without return type
6639 // even if the parameters are the same. See also bug709052
6640 AUTO_TRACE_ADD("Comparing return types: template v.s. non-template");
6641 matching = false;
6642 }
6643
6644 AUTO_TRACE_ADD("Match results of matchArguments2='{}' substDone='{}'",matching,substDone);
6645
6646 if (substDone) // found a new argument list
6647 {
6648 if (matching) // replace member's argument list
6649 {
6651 md->moveArgumentList(std::make_unique<ArgumentList>(argList));
6652 }
6653 else // no match
6654 {
6655 if (!funcTempList.empty() &&
6656 isSpecialization(declTemplArgs,defTemplArgs))
6657 {
6658 // check if we are dealing with a partial template
6659 // specialization. In this case we add it to the class
6660 // even though the member arguments do not match.
6661
6662 addMethodToClass(root,cd,type,md->name(),args,isFriend,
6663 md->protection(),md->isStatic(),md->virtualness(),spec,relates);
6664 return;
6665 }
6666 }
6667 }
6668 if (matching)
6669 {
6670 addMemberDocs(root,md,funcDecl,nullptr,overloaded,spec);
6671 count++;
6672 memFound=true;
6673 }
6674 }
6675 else if (cd && cd!=tcd) // we did find a class with the same name as cd
6676 // but in a different namespace
6677 {
6678 noMatchCount++;
6679 }
6680
6681 if (memFound) break;
6682 }
6683 if (count==0 && root->parent() && root->parent()->section.isObjcImpl())
6684 {
6685 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
6686 return;
6687 }
6688 if (count==0 && !(isFriend && funcType=="class"))
6689 {
6690 int candidates=0;
6691 const ClassDef *ecd = nullptr, *ucd = nullptr;
6692 MemberDef *emd = nullptr, *umd = nullptr;
6693 //printf("Assume template class\n");
6694 for (const auto &md : *mn)
6695 {
6696 MemberDef *cmd=md.get();
6698 ClassDefMutable *ccd=cdmdm ? cdmdm->getClassDefMutable() : nullptr;
6699 //printf("ccd->name()==%s className=%s\n",qPrint(ccd->name()),qPrint(className));
6700 if (ccd!=nullptr && rightScopeMatch(ccd->name(),className))
6701 {
6702 const ArgumentList &templAl = md->templateArguments();
6703 if (!root->tArgLists.empty() && !templAl.empty() &&
6704 root->tArgLists.back().size()<=templAl.size())
6705 {
6706 AUTO_TRACE_ADD("add template specialization");
6707 addMethodToClass(root,ccd,type,md->name(),args,isFriend,
6708 root->protection,root->isStatic,root->virt,spec,relates);
6709 return;
6710 }
6711 if (argListToString(md->argumentList(),false,false) ==
6712 argListToString(root->argList,false,false))
6713 { // exact argument list match -> remember
6714 ucd = ecd = ccd;
6715 umd = emd = cmd;
6716 AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': exact match",
6717 className,ccd->name(),md->argsString());
6718 }
6719 else // arguments do not match, but member name and scope do -> remember
6720 {
6721 ucd = ccd;
6722 umd = cmd;
6723 AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': no match",
6724 className,ccd->name(),md->argsString());
6725 }
6726 candidates++;
6727 }
6728 }
6729 bool strictProtoMatching = Config_getBool(STRICT_PROTO_MATCHING);
6730 if (!strictProtoMatching)
6731 {
6732 if (candidates==1 && ucd && umd)
6733 {
6734 // we didn't find an actual match on argument lists, but there is only 1 member with this
6735 // name in the same scope, so that has to be the one.
6736 addMemberDocs(root,toMemberDefMutable(umd),funcDecl,nullptr,overloaded,spec);
6737 return;
6738 }
6739 else if (candidates>1 && ecd && emd)
6740 {
6741 // we didn't find a unique match using type resolution,
6742 // but one of the matches has the exact same signature so
6743 // we take that one.
6744 addMemberDocs(root,toMemberDefMutable(emd),funcDecl,nullptr,overloaded,spec);
6745 return;
6746 }
6747 }
6748
6749 DString warnMsg = "no ";
6750 if (noMatchCount>1) warnMsg+="uniquely ";
6751 warnMsg+="matching class member found for \n";
6752
6753 for (const ArgumentList &al : root->tArgLists)
6754 {
6755 warnMsg+=" template ";
6756 warnMsg+=tempArgListToString(al,root->lang);
6757 warnMsg+='\n';
6758 }
6759
6760 DString fullFuncDecl=funcDecl;
6761 if (isFunc) fullFuncDecl+=argListToString(root->argList,true);
6762
6763 warnMsg+=" ";
6764 warnMsg+=fullFuncDecl;
6765
6766 if (candidates>0 || noMatchCount>=1)
6767 {
6768 warnMsg+="\nPossible candidates:";
6769
6770 NamespaceDef *nd=nullptr;
6771 if (!namespaceName.empty()) nd=getResolvedNamespace(namespaceName);
6772 FileDef *fd=root->fileDef();
6773
6774 for (const auto &md : *mn)
6775 {
6776 const ClassDef *cd=md->getClassDef();
6777 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
6778 if (tcd==nullptr && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
6779 {
6780 // don't be fooled by anonymous scopes
6781 tcd=cd;
6782 }
6783 if (cd!=nullptr && (rightScopeMatch(cd->name(),className) || (cd!=tcd)))
6784 {
6785 warnMsg+='\n';
6786 const ArgumentList &templAl = md->templateArguments();
6787 warnMsg+=" '";
6788 if (templAl.hasParameters())
6789 {
6790 warnMsg+="template ";
6791 warnMsg+=tempArgListToString(templAl,root->lang);
6792 warnMsg+='\n';
6793 warnMsg+=" ";
6794 }
6795 if (!md->typeString().empty())
6796 {
6797 warnMsg+=md->typeString();
6798 warnMsg+=' ';
6799 }
6801 if (!qScope.empty())
6802 warnMsg+=qScope+"::"+md->name();
6803 warnMsg+=md->argsString();
6804 warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
6805 }
6806 }
6807 }
6808 warn(root->fileName,root->startLine,"{}",warnMsg);
6809 }
6810}
6811
6812//-------------------------------------------------------------------------------------------
6813
6814static void addMemberSpecialization(const Entry *root,
6815 MemberName *mn,
6816 ClassDefMutable *cd,
6817 const DString &funcType,
6818 const DString &funcName,
6819 const DString &funcArgs,
6820 const DString &funcDecl,
6821 const DString &exceptions,
6822 TypeSpecifier spec
6823 )
6824{
6825 AUTO_TRACE("funcType={} funcName={} funcArgs={} funcDecl={} spec={}",funcType,funcName,funcArgs,funcDecl,spec);
6826 MemberDef *declMd=nullptr;
6827 for (const auto &md : *mn)
6828 {
6829 if (md->getClassDef()==cd)
6830 {
6831 // TODO: we should probably also check for matching arguments
6832 declMd = md.get();
6833 break;
6834 }
6835 }
6836 MemberType mtype=MemberType::Function;
6837 ArgumentList tArgList;
6838 // getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6839 auto md = createMemberDef(
6840 root->fileName,root->startLine,root->startColumn,
6841 funcType,funcName,funcArgs,exceptions,
6842 declMd ? declMd->protection() : root->protection,
6843 root->virt,root->isStatic,Relationship::Member,
6844 mtype,tArgList,root->argList,root->metaData);
6845 auto mmd = toMemberDefMutable(md.get());
6846 //printf("new specialized member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6847 mmd->setTagInfo(root->tagInfo());
6848 mmd->setLanguage(root->lang);
6849 mmd->setId(root->id);
6850 mmd->setMemberClass(cd);
6851 mmd->setTemplateSpecialization(true);
6852 mmd->setTypeConstraints(root->typeConstr);
6853 mmd->setDefinition(funcDecl);
6855 mmd->addQualifiers(root->qualifiers);
6856 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6857 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6858 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6859 mmd->setDocsForDefinition(!root->proto);
6860 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6861 mmd->addSectionsToDefinition(root->anchors);
6862 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6863 FileDef *fd=root->fileDef();
6864 mmd->setBodyDef(fd);
6865 mmd->setMemberSpecifiers(spec);
6866 mmd->setVhdlSpecifiers(root->vhdlSpec);
6867 mmd->setMemberGroupId(root->mGrpId);
6868 cd->insertMember(md.get());
6869 mmd->setRefItems(root->sli);
6870 mmd->setRequirementReferences(root->rqli);
6871 addIncludeFile(mmd,fd,root);
6872
6873 mn->push_back(std::move(md));
6874}
6875
6876//-------------------------------------------------------------------------------------------
6877
6878static void addOverloaded(const Entry *root,MemberName *mn,
6879 const DString &funcType,const DString &funcName,const DString &funcArgs,
6880 const DString &funcDecl,const DString &exceptions,TypeSpecifier spec)
6881{
6882 // for unique overloaded member we allow the class to be
6883 // omitted, this is to be Qt compatible. Using this should
6884 // however be avoided, because it is error prone
6885 bool sameClass=false;
6886 if (mn->size()>0)
6887 {
6888 // check if all members with the same name are also in the same class
6889 sameClass = std::equal(mn->begin()+1,mn->end(),mn->begin(),
6890 [](const auto &md1,const auto &md2)
6891 { return md1->getClassDef()->name()==md2->getClassDef()->name(); });
6892 }
6893 if (sameClass)
6894 {
6895 MemberDefMutable *mdm = toMemberDefMutable(mn->front().get());
6896 ClassDefMutable *cd = mdm ? mdm->getClassDefMutable() : nullptr;
6897 if (cd==nullptr) return;
6898
6899 MemberType mtype = MemberType::Function;
6900 if (root->mtype==MethodTypes::Signal) mtype=MemberType::Signal;
6901 else if (root->mtype==MethodTypes::Slot) mtype=MemberType::Slot;
6902 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType::DCOP;
6903
6904 // new overloaded member function
6905 std::unique_ptr<ArgumentList> tArgList =
6906 getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6907 //printf("new related member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6908 auto md = createMemberDef(
6909 root->fileName,root->startLine,root->startColumn,
6910 funcType,funcName,funcArgs,exceptions,
6911 root->protection,root->virt,root->isStatic,Relationship::Related,
6912 mtype,tArgList ? *tArgList : ArgumentList(),root->argList,root->metaData);
6913 auto mmd = toMemberDefMutable(md.get());
6914 mmd->setTagInfo(root->tagInfo());
6915 mmd->setLanguage(root->lang);
6916 mmd->setId(root->id);
6917 mmd->setTypeConstraints(root->typeConstr);
6918 mmd->setMemberClass(cd);
6919 mmd->setDefinition(funcDecl);
6921 mmd->addQualifiers(root->qualifiers);
6923 doc+="<p>";
6924 doc+=root->doc;
6925 mmd->setDocumentation(doc,root->docFile,root->docLine);
6926 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6927 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6928 mmd->setDocsForDefinition(!root->proto);
6929 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6930 mmd->addSectionsToDefinition(root->anchors);
6931 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6932 FileDef *fd=root->fileDef();
6933 mmd->setBodyDef(fd);
6934 mmd->setMemberSpecifiers(spec);
6935 mmd->setVhdlSpecifiers(root->vhdlSpec);
6936 mmd->setMemberGroupId(root->mGrpId);
6937 cd->insertMember(md.get());
6938 cd->insertUsedFile(fd);
6939 mmd->setRefItems(root->sli);
6940 mmd->setRequirementReferences(root->rqli);
6941 addIncludeFile(mmd,fd,root);
6942
6943 mn->push_back(std::move(md));
6944 }
6945}
6946
6947static void insertMemberAlias(Definition *outerScope,const MemberDef *md)
6948{
6949 if (outerScope && outerScope!=Doxygen::globalScope)
6950 {
6951 auto aliasMd = createMemberDefAlias(outerScope,md);
6952 if (outerScope->definitionType()==Definition::TypeClass)
6953 {
6954 ClassDefMutable *cdm = toClassDefMutable(outerScope);
6955 if (cdm)
6956 {
6957 cdm->insertMember(aliasMd.get());
6958 }
6959 }
6960 else if (outerScope->definitionType()==Definition::TypeNamespace)
6961 {
6962 NamespaceDefMutable *ndm = toNamespaceDefMutable(outerScope);
6963 if (ndm)
6964 {
6965 ndm->insertMember(aliasMd.get());
6966 }
6967 }
6968 else if (outerScope->definitionType()==Definition::TypeFile)
6969 {
6970 toFileDef(outerScope)->insertMember(aliasMd.get());
6971 }
6972 if (aliasMd)
6973 {
6974 Doxygen::functionNameLinkedMap->add(md->name())->push_back(std::move(aliasMd));
6975 }
6976 }
6977}
6978
6979//-------------------------------------------------------------------------------------------
6980
6981/*! This function tries to find a member (in a documented class/file/namespace)
6982 * that corresponds to the function/variable declaration given in \a funcDecl.
6983 *
6984 * The boolean \a overloaded is used to specify whether or not a standard
6985 * overload documentation line should be generated.
6986 *
6987 * The boolean \a isFunc is a hint that indicates that this is a function
6988 * instead of a variable or typedef.
6989 */
6990static void findMember(const Entry *root,
6991 const DString &relates,
6992 const DString &type,
6993 const DString &args,
6994 DString funcDecl,
6995 bool overloaded,
6996 bool isFunc
6997 )
6998{
6999 AUTO_TRACE("root='{}' funcDecl='{}' related='{}' overload={} isFunc={} mGrpId={} #tArgList={} spec={} lang={}",
7000 root->name, funcDecl, relates, overloaded, isFunc, root->mGrpId, root->tArgLists.size(),
7001 root->spec, root->lang);
7002
7003 DString scopeName;
7004 DString className;
7005 DString namespaceName;
7006 DString funcType;
7007 DString funcName;
7008 DString funcArgs;
7009 DString funcTempList;
7010 DString exceptions;
7011 DString funcSpec;
7012 bool isRelated=false;
7013 bool isMemberOf=false;
7014 bool isFriend=false;
7015 bool done=false;
7016 TypeSpecifier spec = root->spec;
7017 while (!done)
7018 {
7019 done=true;
7020 if (funcDecl.stripPrefix("friend ")) // treat friends as related members
7021 {
7022 isFriend=true;
7023 done=false;
7024 }
7025 if (funcDecl.stripPrefix("inline "))
7026 {
7027 spec.setInline(true);
7028 done=false;
7029 }
7030 if (funcDecl.stripPrefix("explicit "))
7031 {
7032 spec.setExplicit(true);
7033 done=false;
7034 }
7035 if (funcDecl.stripPrefix("mutable "))
7036 {
7037 spec.setMutable(true);
7038 done=false;
7039 }
7040 if (funcDecl.stripPrefix("thread_local "))
7041 {
7042 spec.setThreadLocal(true);
7043 done=false;
7044 }
7045 if (funcDecl.stripPrefix("virtual "))
7046 {
7047 done=false;
7048 }
7049 }
7050
7051 // delete any ; from the function declaration
7052 size_t sep=0;
7053 while ((sep=funcDecl.find(';'))!=DString::npos)
7054 {
7055 funcDecl=(funcDecl.left(sep)+funcDecl.mid(sep+1)).stripWhiteSpace();
7056 }
7057
7058 // make sure the first character is a space to simplify searching.
7059 if (!funcDecl.empty() && funcDecl[0]!=' ') funcDecl.prepend(" ");
7060
7061 // remove some superfluous spaces
7062 funcDecl= substitute(
7063 substitute(
7064 substitute(funcDecl,"~ ","~"),
7065 ":: ","::"
7066 ),
7067 " ::","::"
7068 ).stripWhiteSpace();
7069
7070 //printf("funcDecl='%s'\n",qPrint(funcDecl));
7071 if (isFriend && funcDecl.startsWith("class "))
7072 {
7073 //printf("friend class\n");
7074 funcDecl=funcDecl.mid(6);
7075 funcName = funcDecl;
7076 }
7077 else if (isFriend && funcDecl.startsWith("struct "))
7078 {
7079 funcDecl=funcDecl.mid(7);
7080 funcName = funcDecl;
7081 }
7082 else
7083 {
7084 // extract information from the declarations
7085 parseFuncDecl(funcDecl,root->lang,scopeName,funcType,funcName,
7086 funcArgs,funcTempList,exceptions
7087 );
7088 }
7089
7090 // the class name can also be a namespace name, we decide this later.
7091 // if a related class name is specified and the class name could
7092 // not be derived from the function declaration, then use the
7093 // related field.
7094 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}' funcType='{}' funcName='{}' funcArgs='{}'",
7095 scopeName,className,namespaceName,funcType,funcName,funcArgs);
7096 if (!relates.empty())
7097 { // related member, prefix user specified scope
7098 isRelated=true;
7099 isMemberOf=(root->relatesType == RelatesType::MemberOf);
7100 if (getClass(relates)==nullptr && !scopeName.empty())
7101 {
7102 scopeName= mergeScopes(scopeName,relates);
7103 }
7104 else
7105 {
7106 scopeName = relates;
7107 }
7108 }
7109
7110 if (relates.empty() && root->parent() &&
7111 (root->parent()->section.isScope() || root->parent()->section.isObjcImpl()) &&
7112 !root->parent()->name.empty()) // see if we can combine scopeName
7113 // with the scope in which it was found
7114 {
7115 DString joinedName = root->parent()->name+"::"+scopeName;
7116 if (!scopeName.empty() &&
7117 (getClass(joinedName) || Doxygen::namespaceLinkedMap->find(joinedName)))
7118 {
7119 scopeName = joinedName;
7120 }
7121 else
7122 {
7123 scopeName = mergeScopes(root->parent()->name,scopeName);
7124 }
7125 }
7126 else // see if we can prefix a namespace or class that is used from the file
7127 {
7128 FileDef *fd=root->fileDef();
7129 if (fd)
7130 {
7131 for (const auto &fnd : fd->getUsedNamespaces())
7132 {
7133 DString joinedName = fnd->name()+"::"+scopeName;
7134 if (Doxygen::namespaceLinkedMap->find(joinedName))
7135 {
7136 scopeName=joinedName;
7137 break;
7138 }
7139 }
7140 }
7141 }
7143 removeRedundantWhiteSpace(scopeName),false,&funcSpec,DString(),false);
7144
7145 // funcSpec contains the last template specifiers of the given scope.
7146 // If this method does not have any template arguments or they are
7147 // empty while funcSpec is not empty we assume this is a
7148 // specialization of a method. If not, we clear the funcSpec and treat
7149 // this as a normal method of a template class.
7150 if (!(root->tArgLists.size()>0 &&
7151 root->tArgLists.front().size()==0
7152 )
7153 )
7154 {
7155 funcSpec.clear();
7156 }
7157
7158 //namespaceName=removeAnonymousScopes(namespaceName);
7159 if (!Config_getBool(EXTRACT_ANON_NSPACES) && scopeName.find('@')!=DString::npos) return; // skip stuff in anonymous namespace...
7160
7161 // split scope into a namespace and a class part
7162 extractNamespaceName(scopeName,className,namespaceName,true);
7163 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}'",scopeName,className,namespaceName);
7164
7165 //printf("namespaceName='%s' className='%s'\n",qPrint(namespaceName),qPrint(className));
7166 // merge class and namespace scopes again
7167 scopeName.clear();
7168 if (!namespaceName.empty())
7169 {
7170 if (className.empty())
7171 {
7172 scopeName=namespaceName;
7173 }
7174 else if (!relates.empty() || // relates command with explicit scope
7175 !getClass(className)) // class name only exists in a namespace
7176 {
7177 scopeName=namespaceName+"::"+className;
7178 }
7179 else
7180 {
7181 scopeName=className;
7182 }
7183 }
7184 else if (!className.empty())
7185 {
7186 scopeName=className;
7187 }
7188 //printf("new scope='%s'\n",qPrint(scopeName));
7189
7190 DString tempScopeName=scopeName;
7191 ClassDefMutable *cd=getClassMutable(scopeName);
7192 if (cd)
7193 {
7194 if (funcSpec.empty())
7195 {
7196 uint32_t argListIndex=0;
7197 tempScopeName=cd->qualifiedNameWithTemplateParameters(&root->tArgLists,&argListIndex);
7198 }
7199 else
7200 {
7201 tempScopeName=scopeName+funcSpec;
7202 }
7203 }
7204 //printf("scopeName=%s cd=%p root->tArgLists=%p result=%s\n",
7205 // qPrint(scopeName),cd,root->tArgLists,qPrint(tempScopeName));
7206
7207 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
7208 // rebuild the function declaration (needed to get the scope right).
7209 if (!scopeName.empty() && !isRelated && !isFriend && !Config_getBool(HIDE_SCOPE_NAMES) && root->lang!=SrcLangExt::Python)
7210 {
7211 if (!funcType.empty())
7212 {
7213 if (isFunc) // a function -> we use argList for the arguments
7214 {
7215 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcTempList;
7216 }
7217 else
7218 {
7219 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcArgs;
7220 }
7221 }
7222 else
7223 {
7224 if (isFunc) // a function => we use argList for the arguments
7225 {
7226 funcDecl=tempScopeName+"::"+funcName+funcTempList;
7227 }
7228 else // variable => add 'argument' list
7229 {
7230 funcDecl=tempScopeName+"::"+funcName+funcArgs;
7231 }
7232 }
7233 }
7234 else // build declaration without scope
7235 {
7236 if (!funcType.empty()) // but with a type
7237 {
7238 if (isFunc) // function => omit argument list
7239 {
7240 funcDecl=funcType+" "+funcName+funcTempList;
7241 }
7242 else // variable => add 'argument' list
7243 {
7244 funcDecl=funcType+" "+funcName+funcArgs;
7245 }
7246 }
7247 else // no type
7248 {
7249 if (isFunc)
7250 {
7251 funcDecl=funcName+funcTempList;
7252 }
7253 else
7254 {
7255 funcDecl=funcName+funcArgs;
7256 }
7257 }
7258 }
7259
7260 if (funcType=="template class" && !funcTempList.empty())
7261 return; // ignore explicit template instantiations
7262
7263 AUTO_TRACE_ADD("Parse results: namespaceName='{}' className=`{}` funcType='{}' funcSpec='{}' "
7264 " funcName='{}' funcArgs='{}' funcTempList='{}' funcDecl='{}' relates='{}'"
7265 " exceptions='{}' isRelated={} isMemberOf={} isFriend={} isFunc={}",
7266 namespaceName, className, funcType, funcSpec,
7267 funcName, funcArgs, funcTempList, funcDecl, relates,
7268 exceptions, isRelated, isMemberOf, isFriend, isFunc);
7269
7270 if (!funcName.empty()) // function name is valid
7271 {
7272 // check if 'className' is actually a scoped enum, in which case we need to
7273 // process it as a global, see issue #6471
7274 bool strongEnum = false;
7275 MemberName *mn=nullptr;
7276 if (!className.empty() && (mn=Doxygen::functionNameLinkedMap->find(className)))
7277 {
7278 for (const auto &imd : *mn)
7279 {
7280 MemberDefMutable *md = toMemberDefMutable(imd.get());
7281 Definition *mdScope = nullptr;
7282 if (md && md->isEnumerate() && md->isStrong() && (mdScope=md->getOuterScope()) &&
7283 // need filter for the correct scope, see issue #9668
7284 ((namespaceName.empty() && mdScope==Doxygen::globalScope) || (mdScope->name()==namespaceName)))
7285 {
7286 AUTO_TRACE_ADD("'{}' is a strong enum! (namespace={} md->getOuterScope()->name()={})",md->name(),namespaceName,md->getOuterScope()->name());
7287 strongEnum = true;
7288 // pass the scope name name as a 'namespace' to the findGlobalMember function
7289 if (!namespaceName.empty())
7290 {
7291 namespaceName+="::"+className;
7292 }
7293 else
7294 {
7295 namespaceName=className;
7296 }
7297 }
7298 }
7299 }
7300
7301 if (funcName.startsWith("operator ")) // strip class scope from cast operator
7302 {
7303 funcName = substitute(funcName,className+"::","");
7304 }
7305 mn = nullptr;
7306 if (!funcTempList.empty()) // try with member specialization
7307 {
7308 mn=Doxygen::memberNameLinkedMap->find(funcName+funcTempList);
7309 }
7310 if (mn==nullptr) // try without specialization
7311 {
7312 mn=Doxygen::memberNameLinkedMap->find(funcName);
7313 }
7314 if (!isRelated && !strongEnum && mn) // function name already found
7315 {
7316 AUTO_TRACE_ADD("member name exists ({} members with this name)",mn->size());
7317 if (!className.empty()) // class name is valid
7318 {
7319 if (funcSpec.empty()) // not a member specialization
7320 {
7321 addMemberFunction(root,mn,scopeName,namespaceName,className,funcType,funcName,
7322 funcArgs,funcTempList,exceptions,
7323 type,args,isFriend,spec,relates,funcDecl,overloaded,isFunc);
7324 }
7325 else if (cd) // member specialization
7326 {
7327 addMemberSpecialization(root,mn,cd,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
7328 }
7329 else
7330 {
7331 //printf("*** Specialized member %s of unknown scope %s%s found!\n",
7332 // qPrint(scopeName),qPrint(funcName),qPrint(funcArgs));
7333 }
7334 }
7335 else if (overloaded) // check if the function belongs to only one class
7336 {
7337 addOverloaded(root,mn,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
7338 }
7339 else // unrelated function with the same name as a member
7340 {
7341 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
7342 {
7343 DString fullFuncDecl=funcDecl;
7344 if (isFunc) fullFuncDecl+=argListToString(root->argList,true);
7345 warn(root->fileName,root->startLine,
7346 "Cannot determine class for function\n{}",
7347 fullFuncDecl
7348 );
7349 }
7350 }
7351 }
7352 else if (isRelated && !relates.empty())
7353 {
7354 AUTO_TRACE_ADD("related function scopeName='{}' className='{}'",scopeName,className);
7355 if (className.empty()) className=relates;
7356 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
7357 if ((cd=getClassMutable(scopeName)))
7358 {
7359 bool newMember=true; // assume we have a new member
7360 MemberDefMutable *mdDefine=nullptr;
7361 {
7362 mn = Doxygen::functionNameLinkedMap->find(funcName);
7363 if (mn)
7364 {
7365 for (const auto &imd : *mn)
7366 {
7367 MemberDefMutable *md = toMemberDefMutable(imd.get());
7368 if (md && md->isDefine())
7369 {
7370 mdDefine = md;
7371 break;
7372 }
7373 }
7374 }
7375 }
7376
7377 if (mdDefine) // macro definition is already created by the preprocessor and inserted as a file member
7378 {
7379 //printf("moving #define %s into class %s\n",qPrint(mdDefine->name()),qPrint(cd->name()));
7380
7381 // take mdDefine from the Doxygen::functionNameLinkedMap (without deleting the data)
7382 auto mdDefineTaken = Doxygen::functionNameLinkedMap->take(funcName,mdDefine);
7383 // insert it as a class member
7384 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==nullptr)
7385 {
7386 mn=Doxygen::memberNameLinkedMap->add(funcName);
7387 }
7388
7389 if (mdDefine->getFileDef())
7390 {
7391 mdDefine->getFileDef()->removeMember(mdDefine);
7392 }
7393 mdDefine->makeRelated();
7394 mdDefine->setMemberClass(cd);
7395 mdDefine->moveTo(cd);
7396 cd->insertMember(mdDefine);
7397 // also insert the member as an alias in the parent's scope, so it can be referenced also without cd's scope
7398 insertMemberAlias(cd->getOuterScope(),mdDefine);
7399 mn->push_back(std::move(mdDefineTaken));
7400 }
7401 else // normal member, needs to be created and added to the class
7402 {
7403 FileDef *fd=root->fileDef();
7404
7405 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==nullptr)
7406 {
7407 mn=Doxygen::memberNameLinkedMap->add(funcName);
7408 }
7409 else
7410 {
7411 // see if we got another member with matching arguments
7412 MemberDefMutable *rmd_found = nullptr;
7413 for (const auto &irmd : *mn)
7414 {
7415 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
7416 if (rmd)
7417 {
7418 const ArgumentList &rmdAl = rmd->argumentList();
7419
7420 newMember=
7421 className!=rmd->getOuterScope()->name() ||
7422 !matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmdAl,
7423 cd,fd,root->type,&root->argList,
7424 true,root->lang);
7425 if (!newMember)
7426 {
7427 rmd_found = rmd;
7428 }
7429 }
7430 }
7431 if (rmd_found) // member already exists as rmd -> add docs
7432 {
7433 AUTO_TRACE_ADD("addMemberDocs for related member {}",root->name);
7434 addMemberDocs(root,rmd_found,funcDecl,nullptr,overloaded,spec);
7435 newMember=false;
7436 }
7437 }
7438
7439 if (newMember) // need to create a new member
7440 {
7441 MemberType mtype = MemberType::Function;
7442 switch (root->mtype)
7443 {
7444 case MethodTypes::Method: mtype = MemberType::Function; break;
7445 case MethodTypes::Signal: mtype = MemberType::Signal; break;
7446 case MethodTypes::Slot: mtype = MemberType::Slot; break;
7447 case MethodTypes::DCOP: mtype = MemberType::DCOP; break;
7448 case MethodTypes::Property: mtype = MemberType::Property; break;
7449 case MethodTypes::Event: mtype = MemberType::Event; break;
7450 }
7451
7452 //printf("New related name '%s' '%d'\n",qPrint(funcName),
7453 // root->argList ? (int)root->argList->count() : -1);
7454
7455 // first note that we pass:
7456 // (root->tArgLists ? root->tArgLists->last() : nullptr)
7457 // for the template arguments for the new "member."
7458 // this accurately reflects the template arguments of
7459 // the related function, which don't have to do with
7460 // those of the related class.
7461 auto md = createMemberDef(
7462 root->fileName,root->startLine,root->startColumn,
7463 funcType,funcName,funcArgs,exceptions,
7464 root->protection,root->virt,
7465 root->isStatic,
7466 isMemberOf ? Relationship::Foreign : Relationship::Related,
7467 mtype,
7468 (!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList()),
7469 funcArgs.empty() ? ArgumentList() : root->argList,
7470 root->metaData);
7471 auto mmd = toMemberDefMutable(md.get());
7472
7473 // also insert the member as an alias in the parent's scope, so it can be referenced also without cd's scope
7474 insertMemberAlias(cd->getOuterScope(),md.get());
7475
7476 // we still have the problem that
7477 // MemberDef::writeDocumentation() in memberdef.cpp
7478 // writes the template argument list for the class,
7479 // as if this member is a member of the class.
7480 // fortunately, MemberDef::writeDocumentation() has
7481 // a special mechanism that allows us to totally
7482 // override the set of template argument lists that
7483 // are printed. We use that and set it to the
7484 // template argument lists of the related function.
7485 //
7486 mmd->setDefinitionTemplateParameterLists(root->tArgLists);
7487
7488 mmd->setTagInfo(root->tagInfo());
7489
7490 addIncludeFile(mmd,root->fileDef(),root);
7491 //printf("Related member name='%s' decl='%s' bodyLine='%d'\n",
7492 // qPrint(funcName),qPrint(funcDecl),root->bodyLine);
7493
7494 // try to find the matching line number of the body from the
7495 // global function list
7496 bool found=false;
7497 if (root->bodyLine==-1)
7498 {
7500 if (rmn)
7501 {
7502 const MemberDefMutable *rmd_found=nullptr;
7503 for (const auto &irmd : *rmn)
7504 {
7505 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
7506 if (rmd)
7507 {
7508 const ArgumentList &rmdAl = rmd->argumentList();
7509 // check for matching argument lists
7510 if (
7511 matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmdAl,
7512 cd,fd,root->type,&root->argList,
7513 true,root->lang)
7514 )
7515 {
7516 found=true;
7517 rmd_found = rmd;
7518 break;
7519 }
7520 }
7521 }
7522 if (rmd_found) // member found -> copy line number info
7523 {
7524 mmd->setBodySegment(rmd_found->getDefLine(),rmd_found->getStartBodyLine(),rmd_found->getEndBodyLine());
7525 mmd->setBodyDef(rmd_found->getBodyDef());
7526 //md->setBodyMember(rmd);
7527 }
7528 }
7529 }
7530 if (!found) // line number could not be found or is available in this
7531 // entry
7532 {
7533 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7534 mmd->setBodyDef(fd);
7535 }
7536
7537 //if (root->mGrpId!=-1)
7538 //{
7539 // md->setMemberGroup(memberGroupDict[root->mGrpId]);
7540 //}
7541 mmd->setMemberClass(cd);
7542 mmd->setMemberSpecifiers(spec);
7543 mmd->setVhdlSpecifiers(root->vhdlSpec);
7544 mmd->setDefinition(funcDecl);
7546 mmd->addQualifiers(root->qualifiers);
7547 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7548 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7549 mmd->setDocsForDefinition(!root->proto);
7550 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
7551 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7552 mmd->addSectionsToDefinition(root->anchors);
7553 mmd->setMemberGroupId(root->mGrpId);
7554 mmd->setLanguage(root->lang);
7555 mmd->setId(root->id);
7556 //md->setMemberDefTemplateArguments(root->mtArgList);
7557 cd->insertMember(md.get());
7558 cd->insertUsedFile(fd);
7559 mmd->setRefItems(root->sli);
7560 mmd->setRequirementReferences(root->rqli);
7561 if (root->relatesType==RelatesType::Duplicate) mmd->setRelatedAlso(cd);
7562 addMemberToGroups(root,md.get());
7564 //printf("Adding member=%s\n",qPrint(md->name()));
7565 mn->push_back(std::move(md));
7566 }
7567 if (root->relatesType==RelatesType::Duplicate)
7568 {
7569 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
7570 {
7571 DString fullFuncDecl=funcDecl;
7572 if (isFunc) fullFuncDecl+=argListToString(root->argList,true);
7573 warn(root->fileName,root->startLine,
7574 "Cannot determine file/namespace for relatedalso function\n{}",
7575 fullFuncDecl
7576 );
7577 }
7578 }
7579 }
7580 }
7581 else
7582 {
7583 warn_undoc(root->fileName,root->startLine, "class '{}' for related function '{}' is not documented.", className,funcName);
7584 }
7585 }
7586 else if (root->parent() && root->parent()->section.isObjcImpl())
7587 {
7588 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
7589 }
7590 else // unrelated not overloaded member found
7591 {
7592 bool globMem = findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec);
7593 if (className.empty() && !globMem)
7594 {
7595 warn(root->fileName,root->startLine, "class for member '{}' cannot be found.", funcName);
7596 }
7597 else if (!className.empty() && !globMem)
7598 {
7599 warn(root->fileName,root->startLine,
7600 "member '{}' of class '{}' cannot be found",
7601 funcName,className);
7602 }
7603 }
7604 }
7605 else
7606 {
7607 // this should not be called
7608 warn(root->fileName,root->startLine,"member with no name found.");
7609 }
7610 return;
7611}
7612
7613//----------------------------------------------------------------------
7614// find the members corresponding to the different documentation blocks
7615// that are extracted from the sources.
7616
7617static void filterMemberDocumentation(const Entry *root,const DString &relates)
7618{
7619 AUTO_TRACE("root->type='{}' root->inside='{}' root->name='{}' root->args='{}' section={} root->spec={} root->mGrpId={}",
7620 root->type,root->inside,root->name,root->args,root->section,root->spec,root->mGrpId);
7621 //printf("root->parent()->name=%s\n",qPrint(root->parent()->name));
7622 bool isFunc=true;
7623
7624 DString type = root->type;
7625 DString args = root->args;
7626 int i=-1, l=0;
7627 if ( // detect func variable/typedef to func ptr
7628 (i=findFunctionPtr(type.str(),root->lang,&l))!=-1
7629 )
7630 {
7631 //printf("Fixing function pointer!\n");
7632 // fix type and argument
7633 args.prepend(type.mid(i+l));
7634 type=type.left(i+l);
7635 //printf("Results type=%s,name=%s,args=%s\n",qPrint(type),qPrint(root->name),qPrint(args));
7636 isFunc=false;
7637 }
7638 else if ((type.startsWith("typedef ") && args.find('(')!=DString::npos))
7639 // detect function types marked as functions
7640 {
7641 isFunc=false;
7642 }
7643
7644 //printf("Member %s isFunc=%d\n",qPrint(root->name),isFunc);
7645 if (root->section.isMemberDoc())
7646 {
7647 //printf("Documentation for inline member '%s' found args='%s'\n",
7648 // qPrint(root->name),qPrint(args));
7649 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
7650 if (type.empty())
7651 {
7652 findMember(root,
7653 relates,
7654 type,
7655 args,
7656 root->name + args + root->exception,
7657 false,
7658 isFunc);
7659 }
7660 else
7661 {
7662 findMember(root,
7663 relates,
7664 type,
7665 args,
7666 type + " " + root->name + args + root->exception,
7667 false,
7668 isFunc);
7669 }
7670 }
7671 else if (root->section.isOverloadDoc())
7672 {
7673 //printf("Overloaded member %s found\n",qPrint(root->name));
7674 findMember(root,
7675 relates,
7676 type,
7677 args,
7678 root->name,
7679 true,
7680 isFunc);
7681 }
7682 else if
7683 ((root->section.isFunction() // function
7684 ||
7685 (root->section.isVariable() && // variable
7686 !type.empty() && // with a type
7687 g_compoundKeywords.find(type.str())==g_compoundKeywords.end() // that is not a keyword
7688 // (to skip forward declaration of class etc.)
7689 )
7690 )
7691 )
7692 {
7693 //printf("Documentation for member '%s' found args='%s' excp='%s'\n",
7694 // qPrint(root->name),qPrint(args),qPrint(root->exception));
7695 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
7696 //printf("Inside=%s\n Relates=%s\n",qPrint(root->inside),qPrint(relates));
7697 if (isTypeAClassFriend(type))
7698 {
7699 findMember(root,
7700 relates,
7701 type,
7702 args,
7703 type+" "+root->name,
7704 false,false);
7705
7706 }
7707 else if (!type.empty())
7708 {
7709 findMember(root,
7710 relates,
7711 type,
7712 args,
7713 type+" "+ root->inside + root->name + args + root->exception,
7714 false,isFunc);
7715 }
7716 else
7717 {
7718 findMember(root,
7719 relates,
7720 type,
7721 args,
7722 root->inside + root->name + args + root->exception,
7723 false,isFunc);
7724 }
7725 }
7726 else if (root->section.isDefine() && !relates.empty())
7727 {
7728 findMember(root,
7729 relates,
7730 type,
7731 args,
7732 root->name + args,
7733 false,
7734 !args.empty());
7735 }
7736 else if (root->section.isVariableDoc())
7737 {
7738 //printf("Documentation for variable %s found\n",qPrint(root->name));
7739 //if (!relates.empty()) printf(" Relates %s\n",qPrint(relates));
7740 findMember(root,
7741 relates,
7742 type,
7743 args,
7744 root->name,
7745 false,
7746 false);
7747 }
7748 else if (root->section.isExportedInterface() ||
7749 root->section.isIncludedService())
7750 {
7751 findMember(root,
7752 relates,
7753 type,
7754 args,
7755 type + " " + root->name,
7756 false,
7757 false);
7758 }
7759 else
7760 {
7761 // skip section
7762 //printf("skip section\n");
7763 }
7764}
7765
7766static void findMemberDocumentation(const Entry *root)
7767{
7768 if (root->section.isMemberDoc() ||
7769 root->section.isOverloadDoc() ||
7770 root->section.isFunction() ||
7771 root->section.isVariable() ||
7772 root->section.isVariableDoc() ||
7773 root->section.isDefine() ||
7774 root->section.isIncludedService() ||
7775 root->section.isExportedInterface()
7776 )
7777 {
7778 AUTO_TRACE();
7779 if (root->relatesType==RelatesType::Duplicate && !root->relates.empty())
7780 {
7782 }
7784 }
7785 for (const auto &e : root->children())
7786 {
7787 if (!e->section.isEnum())
7788 {
7789 findMemberDocumentation(e.get());
7790 }
7791 }
7792}
7793
7794//----------------------------------------------------------------------
7795
7796static void findObjCMethodDefinitions(const Entry *root)
7797{
7798 AUTO_TRACE();
7799 for (const auto &objCImpl : root->children())
7800 {
7801 if (objCImpl->section.isObjcImpl())
7802 {
7803 for (const auto &objCMethod : objCImpl->children())
7804 {
7805 if (objCMethod->section.isFunction())
7806 {
7807 //printf(" Found ObjC method definition %s\n",qPrint(objCMethod->name));
7808 findMember(objCMethod.get(),
7809 objCMethod->relates,
7810 objCMethod->type,
7811 objCMethod->args,
7812 objCMethod->type+" "+objCImpl->name+"::"+objCMethod->name+" "+objCMethod->args,
7813 false,true);
7814 objCMethod->section=EntryType::makeEmpty();
7815 }
7816 }
7817 }
7818 }
7819}
7820
7821//----------------------------------------------------------------------
7822// find and add the enumeration to their classes, namespaces or files
7823
7824static void findEnums(const Entry *root)
7825{
7826 if (root->section.isEnum())
7827 {
7828 AUTO_TRACE("name={}",root->name);
7829 ClassDefMutable *cd = nullptr;
7830 FileDef *fd = nullptr;
7831 NamespaceDefMutable *nd = nullptr;
7832 MemberNameLinkedMap *mnsd = nullptr;
7833 bool isGlobal = false;
7834 bool isRelated = false;
7835 bool isMemberOf = false;
7836 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7837
7838 DString name;
7839 DString scope;
7840
7841 if (size_t i = root->name.rfind("::"); i!=DString::npos) // scope is specified
7842 {
7843 scope=root->name.left(i); // extract scope
7844 if (root->lang==SrcLangExt::CSharp)
7845 {
7846 scope = mangleCSharpGenericName(scope);
7847 }
7848 name=root->name.right(root->name.length()-i-2); // extract name
7849 if ((cd=getClassMutable(scope))==nullptr)
7850 {
7852 }
7853 }
7854 else // no scope, check the scope in which the docs where found
7855 {
7856 if (root->parent()->section.isScope() && !root->parent()->name.empty()) // found enum docs inside a compound
7857 {
7858 scope=root->parent()->name;
7859 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7860 }
7861 name=root->name;
7862 }
7863
7864 if (!root->relates.empty())
7865 { // related member, prefix user specified scope
7866 isRelated=true;
7867 isMemberOf=(root->relatesType==RelatesType::MemberOf);
7868 if (getClass(root->relates)==nullptr && !scope.empty())
7869 scope=mergeScopes(scope,root->relates);
7870 else
7871 scope=root->relates;
7872 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7873 }
7874
7875 if (cd && !name.empty()) // found a enum inside a compound
7876 {
7877 //printf("Enum '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7878 fd=nullptr;
7880 isGlobal=false;
7881 }
7882 else if (nd) // found enum inside namespace
7883 {
7885 isGlobal=true;
7886 }
7887 else // found a global enum
7888 {
7889 fd=root->fileDef();
7891 isGlobal=true;
7892 }
7893
7894 if (!name.empty())
7895 {
7896 // new enum type
7897 AUTO_TRACE_ADD("new enum {} at line {} of {}",name,root->bodyLine,root->fileName);
7898 auto md = createMemberDef(
7899 root->fileName,root->startLine,root->startColumn,
7900 DString(),name,DString(),DString(),
7901 root->protection,Specifier::Normal,false,
7902 isMemberOf ? Relationship::Foreign : isRelated ? Relationship::Related : Relationship::Member,
7903 MemberType::Enumeration,
7905 auto mmd = toMemberDefMutable(md.get());
7906 mmd->setTagInfo(root->tagInfo());
7907 mmd->setLanguage(root->lang);
7908 mmd->setId(root->id);
7909 if (!isGlobal) mmd->setMemberClass(cd); else mmd->setFileDef(fd);
7910 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7911 mmd->setBodyDef(root->fileDef());
7912 mmd->setMemberSpecifiers(root->spec);
7913 mmd->setVhdlSpecifiers(root->vhdlSpec);
7914 mmd->setEnumBaseType(root->args);
7915 //printf("Enum %s definition at line %d of %s: protection=%d scope=%s\n",
7916 // qPrint(root->name),root->bodyLine,qPrint(root->fileName),root->protection,cd?qPrint(cd->name()):"<none>");
7917 mmd->addSectionsToDefinition(root->anchors);
7918 mmd->setMemberGroupId(root->mGrpId);
7920 mmd->addQualifiers(root->qualifiers);
7921 //printf("%s::setRefItems(%zu)\n",qPrint(md->name()),root->sli.size());
7922 mmd->setRefItems(root->sli);
7923 mmd->setRequirementReferences(root->rqli);
7924 //printf("found enum %s nd=%p\n",qPrint(md->name()),nd);
7925 bool defSet=false;
7926 addIncludeFile(mmd,root->fileDef(),root);
7927
7928 DString baseType = root->args;
7929 if (!baseType.empty())
7930 {
7931 baseType.prepend(" : ");
7932 }
7933
7934 if (nd)
7935 {
7936 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python)
7937 {
7938 mmd->setDefinition(name+baseType);
7939 }
7940 else
7941 {
7942 mmd->setDefinition(nd->name()+"::"+name+baseType);
7943 }
7944 //printf("definition=%s\n",md->definition());
7945 defSet=true;
7946 mmd->setNamespace(nd);
7947 nd->insertMember(md.get());
7948 }
7949
7950 // even if we have already added the enum to a namespace, we still
7951 // also want to add it to other appropriate places such as file
7952 // or class.
7953 if (isGlobal && (nd==nullptr || !nd->isAnonymous()))
7954 {
7955 if (!defSet) mmd->setDefinition(name+baseType);
7956 if (fd==nullptr && root->parent())
7957 {
7958 fd=root->parent()->fileDef();
7959 }
7960 if (fd)
7961 {
7962 mmd->setFileDef(fd);
7963 fd->insertMember(md.get());
7964 }
7965 }
7966 else if (cd)
7967 {
7968 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python)
7969 {
7970 mmd->setDefinition(name+baseType);
7971 }
7972 else
7973 {
7974 mmd->setDefinition(cd->name()+"::"+name+baseType);
7975 }
7976 cd->insertMember(md.get());
7977 cd->insertUsedFile(fd);
7978 }
7979 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7980 mmd->setDocsForDefinition(!root->proto);
7981 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7982 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7983
7984 //printf("Adding member=%s\n",qPrint(md->name()));
7985 addMemberToGroups(root,md.get());
7987
7988 MemberName *mn = mnsd->add(name);
7989 mn->push_back(std::move(md));
7990 }
7991 }
7992 else
7993 {
7994 for (const auto &e : root->children()) findEnums(e.get());
7995 }
7996}
7997
7998//----------------------------------------------------------------------
7999
8000static void addEnumValuesToEnums(const Entry *root)
8001{
8002 if (root->section.isEnum())
8003 // non anonymous enumeration
8004 {
8005 AUTO_TRACE("name={}",root->name);
8006 ClassDefMutable *cd = nullptr;
8007 FileDef *fd = nullptr;
8008 NamespaceDefMutable *nd = nullptr;
8009 MemberNameLinkedMap *mnsd = nullptr;
8010 bool isGlobal = false;
8011 bool isRelated = false;
8012 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
8013
8014 DString name;
8015 DString scope;
8016
8017 if (size_t i = root->name.rfind("::"); i!=DString::npos) // scope is specified
8018 {
8019 scope=root->name.left(i); // extract scope
8020 if (root->lang==SrcLangExt::CSharp)
8021 {
8022 scope = mangleCSharpGenericName(scope);
8023 }
8024 name=root->name.right(root->name.length()-i-2); // extract name
8025 if ((cd=getClassMutable(scope))==nullptr)
8026 {
8028 }
8029 }
8030 else // no scope, check the scope in which the docs where found
8031 {
8032 if (root->parent()->section.isScope() && !root->parent()->name.empty()) // found enum docs inside a compound
8033 {
8034 scope=root->parent()->name;
8035 if (root->lang==SrcLangExt::CSharp)
8036 {
8037 scope = mangleCSharpGenericName(scope);
8038 }
8039 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
8040 }
8041 name=root->name;
8042 }
8043
8044 if (!root->relates.empty())
8045 { // related member, prefix user specified scope
8046 isRelated=true;
8047 if (getClassMutable(root->relates)==nullptr && !scope.empty())
8048 scope=mergeScopes(scope,root->relates);
8049 else
8050 scope=root->relates;
8051 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
8052 }
8053
8054 if (cd && !name.empty()) // found a enum inside a compound
8055 {
8056 //printf("Enum in class '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
8057 fd=nullptr;
8059 isGlobal=false;
8060 }
8061 else if (nd && !nd->isAnonymous()) // found enum inside namespace
8062 {
8063 //printf("Enum in namespace '%s'::'%s'\n",qPrint(nd->name()),qPrint(name));
8065 isGlobal=true;
8066 }
8067 else // found a global enum
8068 {
8069 fd=root->fileDef();
8070 //printf("Enum in file '%s': '%s'\n",qPrint(fd->name()),qPrint(name));
8072 isGlobal=true;
8073 }
8074
8075 if (!name.empty())
8076 {
8077 //printf("** name=%s\n",qPrint(name));
8078 MemberName *mn = mnsd->find(name); // for all members with this name
8079 if (mn)
8080 {
8081 struct EnumValueInfo
8082 {
8083 EnumValueInfo(const DString &n,std::unique_ptr<MemberDef> &&md) :
8084 name(n), member(std::move(md)) {}
8085 DString name;
8086 std::unique_ptr<MemberDef> member;
8087 };
8088 std::vector< EnumValueInfo > extraMembers;
8089 // for each enum in this list
8090 for (const auto &imd : *mn)
8091 {
8092 MemberDefMutable *md = toMemberDefMutable(imd.get());
8093 // use raw pointer in this loop, since we modify mn and can then invalidate mdp.
8094 if (md && md->isEnumerate() && !root->children().empty())
8095 {
8096 AUTO_TRACE_ADD("enum {} with {} children",md->name(),root->children().size());
8097 for (const auto &e : root->children())
8098 {
8099 SrcLangExt sle = root->lang;
8100 bool isJavaLike = sle==SrcLangExt::CSharp || sle==SrcLangExt::Java || sle==SrcLangExt::XML;
8101 if ( isJavaLike || root->spec.isStrong())
8102 {
8103 if (sle == SrcLangExt::Cpp && e->section.isDefine()) continue;
8104 // Unlike classic C/C++ enums, for C++11, C# & Java enum
8105 // values are only visible inside the enum scope, so we must create
8106 // them here and only add them to the enum
8107 //printf("md->qualifiedName()=%s e->name=%s tagInfo=%p name=%s\n",
8108 // qPrint(md->qualifiedName()),qPrint(e->name),(void*)e->tagInfo(),qPrint(e->name));
8109 DString qualifiedName = root->name;
8110 if (size_t i = qualifiedName.rfind("::"); i!=DString::npos && sle==SrcLangExt::CSharp)
8111 {
8112 qualifiedName = mangleCSharpGenericName(qualifiedName.left(i))+qualifiedName.mid(i);
8113 }
8114 if (isJavaLike)
8115 {
8116 qualifiedName=substitute(qualifiedName,"::",".");
8117 }
8118 if (md->qualifiedName()==qualifiedName) // enum value scope matches that of the enum
8119 {
8120 DString fileName = e->fileName;
8121 if (fileName.empty() && e->tagInfo())
8122 {
8123 fileName = e->tagInfo()->tagName;
8124 }
8125 AUTO_TRACE_ADD("strong enum value {}",e->name);
8126 auto fmd = createMemberDef(
8127 fileName,e->startLine,e->startColumn,
8128 e->type,e->name,e->args,DString(),
8129 e->protection, Specifier::Normal,e->isStatic,Relationship::Member,
8130 MemberType::EnumValue,ArgumentList(),ArgumentList(),e->metaData);
8131 auto fmmd = toMemberDefMutable(fmd.get());
8132 NamespaceDef *mnd = md->getNamespaceDef();
8133 if (md->getClassDef())
8134 fmmd->setMemberClass(md->getClassDef());
8135 else if (mnd && (mnd->isLinkable() || mnd->isAnonymous()))
8136 fmmd->setNamespace(mnd);
8137 else if (md->getFileDef())
8138 fmmd->setFileDef(md->getFileDef());
8139 fmmd->setOuterScope(md->getOuterScope());
8140 fmmd->setTagInfo(e->tagInfo());
8141 fmmd->setLanguage(e->lang);
8142 fmmd->setBodySegment(e->startLine,e->bodyLine,e->endBodyLine);
8143 fmmd->setBodyDef(e->fileDef());
8144 fmmd->setId(e->id);
8145 fmmd->setDocumentation(e->doc,e->docFile,e->docLine);
8146 fmmd->setBriefDescription(e->brief,e->briefFile,e->briefLine);
8147 fmmd->addSectionsToDefinition(e->anchors);
8148 fmmd->setInitializer(e->initializer.str());
8149 fmmd->setMaxInitLines(e->initLines);
8150 fmmd->setMemberGroupId(e->mGrpId);
8151 fmmd->setExplicitExternal(e->explicitExternal,fileName,e->startLine,e->startColumn);
8152 fmmd->setRefItems(e->sli);
8153 fmmd->setRequirementReferences(e->rqli);
8154 fmmd->setAnchor();
8155 md->insertEnumField(fmd.get());
8156 fmmd->setEnumScope(md,true);
8157 extraMembers.emplace_back(e->name,std::move(fmd));
8158 }
8159 }
8160 else
8161 {
8162 AUTO_TRACE_ADD("enum value {}",e->name);
8163 //printf("e->name=%s isRelated=%d\n",qPrint(e->name),isRelated);
8164 MemberName *fmn=nullptr;
8165 MemberNameLinkedMap *emnsd = isRelated ? Doxygen::functionNameLinkedMap : mnsd;
8166 if (!e->name.empty() && (fmn=emnsd->find(e->name)))
8167 // get list of members with the same name as the field
8168 {
8169 for (const auto &ifmd : *fmn)
8170 {
8171 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
8172 if (fmd && fmd->isEnumValue() && fmd->getOuterScope()==md->getOuterScope()) // in same scope
8173 {
8174 //printf("found enum value with same name %s in scope %s\n",
8175 // qPrint(fmd->name()),qPrint(fmd->getOuterScope()->name()));
8176 if (nd && !nd->isAnonymous())
8177 {
8178 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
8179 {
8180 const NamespaceDef *fnd=fmd->getNamespaceDef();
8181 if (fnd==nd) // enum value is inside a namespace
8182 {
8183 md->insertEnumField(fmd);
8184 fmd->setEnumScope(md);
8185 }
8186 }
8187 }
8188 else if (isGlobal)
8189 {
8190 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
8191 {
8192 const FileDef *ffd=fmd->getFileDef();
8193 if (ffd==fd && ffd==md->getFileDef()) // enum value has file scope
8194 {
8195 md->insertEnumField(fmd);
8196 fmd->setEnumScope(md);
8197 }
8198 }
8199 }
8200 else if (isRelated && cd) // reparent enum value to
8201 // match the enum's scope
8202 {
8203 md->insertEnumField(fmd); // add field def to list
8204 fmd->setEnumScope(md); // cross ref with enum name
8205 fmd->setEnumClassScope(cd); // cross ref with enum name
8206 fmd->setOuterScope(cd);
8207 fmd->makeRelated();
8208 cd->insertMember(fmd);
8209 }
8210 else
8211 {
8212 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
8213 {
8214 const ClassDef *fcd=fmd->getClassDef();
8215 if (fcd==cd) // enum value is inside a class
8216 {
8217 //printf("Inserting enum field %s in enum scope %s\n",
8218 // qPrint(fmd->name()),qPrint(md->name()));
8219 md->insertEnumField(fmd); // add field def to list
8220 fmd->setEnumScope(md); // cross ref with enum name
8221 }
8222 }
8223 }
8224 }
8225 }
8226 }
8227 }
8228 }
8229 }
8230 }
8231 // move the newly added members into mn
8232 for (auto &e : extraMembers)
8233 {
8234 MemberName *emn=mnsd->add(e.name);
8235 emn->push_back(std::move(e.member));
8236 }
8237 }
8238 }
8239 }
8240 else
8241 {
8242 for (const auto &e : root->children()) addEnumValuesToEnums(e.get());
8243 }
8244}
8245
8246//----------------------------------------------------------------------
8247
8248static void addEnumDocs(const Entry *root,MemberDefMutable *md)
8249{
8250 AUTO_TRACE();
8251 // documentation outside a compound overrides the documentation inside it
8252 {
8253 md->setDocumentation(root->doc,root->docFile,root->docLine);
8254 md->setDocsForDefinition(!root->proto);
8255 }
8256
8257 // brief descriptions inside a compound override the documentation
8258 // outside it
8259 {
8260 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
8261 }
8262
8263 if (md->inbodyDocumentation().empty() || !root->parent()->name.empty())
8264 {
8266 }
8267
8268 if (root->mGrpId!=-1 && md->getMemberGroupId()==-1)
8269 {
8270 md->setMemberGroupId(root->mGrpId);
8271 }
8272
8274 md->setRefItems(root->sli);
8275 md->setRequirementReferences(root->rqli);
8276
8277 const GroupDef *gd=md->getGroupDef();
8278 if (gd==nullptr && !root->groups.empty()) // member not grouped but out-of-line documentation is
8279 {
8280 addMemberToGroups(root,md);
8281 }
8283}
8284
8285//----------------------------------------------------------------------
8286// Search for the name in the associated groups. If a matching member
8287// definition exists, then add the documentation to it and return true,
8288// otherwise false.
8289
8290static bool tryAddEnumDocsToGroupMember(const Entry *root,const DString &name)
8291{
8292 for (const auto &g : root->groups)
8293 {
8294 const GroupDef *gd = Doxygen::groupLinkedMap->find(g.groupname);
8295 if (gd)
8296 {
8297 MemberList *ml = gd->getMemberList(MemberListType::DecEnumMembers());
8298 if (ml)
8299 {
8300 MemberDefMutable *md = toMemberDefMutable(ml->find(name));
8301 if (md)
8302 {
8303 addEnumDocs(root,md);
8304 return true;
8305 }
8306 }
8307 }
8308 else if (!gd && g.pri == Grouping::GROUPING_INGROUP)
8309 {
8310 warn(root->fileName, root->startLine,
8311 "Found non-existing group '{}' for the command '{}', ignoring command",
8312 g.groupname, Grouping::getGroupPriName( g.pri )
8313 );
8314 }
8315 }
8316
8317 return false;
8318}
8319
8320//----------------------------------------------------------------------
8321// find the documentation blocks for the enumerations
8322
8323static void findEnumDocumentation(const Entry *root)
8324{
8325 if (root->section.isEnumDoc() &&
8326 !root->name.empty() &&
8327 root->name.at(0)!='@' // skip anonymous enums
8328 )
8329 {
8330 DString name;
8331 DString scope;
8332 if (size_t i = root->name.rfind("::"); i!=DString::npos) // scope is specified as part of the name
8333 {
8334 name=root->name.mid(i+2); // extract name
8335 scope=root->name.left(i); // extract scope
8336 //printf("Scope='%s' Name='%s'\n",qPrint(scope),qPrint(name));
8337 }
8338 else // just the name
8339 {
8340 name=root->name;
8341 }
8342 if (root->parent()->section.isScope() && !root->parent()->name.empty()) // found enum docs inside a compound
8343 {
8344 if (!scope.empty()) scope.prepend("::");
8345 scope.prepend(root->parent()->name);
8346 }
8347 const ClassDef *cd = getClass(scope);
8349 const FileDef *fd = root->fileDef();
8350 AUTO_TRACE("Found docs for enum with name '{}' and scope '{}' in context '{}' cd='{}', nd='{}' fd='{}'",
8351 name,scope,root->parent()->name,
8352 cd ? cd->name() : DString("<none>"),
8353 nd ? nd->name() : DString("<none>"),
8354 fd ? fd->name() : DString("<none>"));
8355
8356 if (!name.empty())
8357 {
8358 bool found = tryAddEnumDocsToGroupMember(root, name);
8359 if (!found)
8360 {
8362 if (mn)
8363 {
8364 for (const auto &imd : *mn)
8365 {
8366 MemberDefMutable *md = toMemberDefMutable(imd.get());
8367 if (md && md->isEnumerate())
8368 {
8369 const ClassDef *mcd = md->getClassDef();
8370 const NamespaceDef *mnd = md->getNamespaceDef();
8371 const FileDef *mfd = md->getFileDef();
8372 if (cd && mcd==cd)
8373 {
8374 AUTO_TRACE_ADD("Match found for class scope");
8375 addEnumDocs(root,md);
8376 found = true;
8377 break;
8378 }
8379 else if (cd==nullptr && mcd==nullptr && nd!=nullptr && mnd==nd)
8380 {
8381 AUTO_TRACE_ADD("Match found for namespace scope");
8382 addEnumDocs(root,md);
8383 found = true;
8384 break;
8385 }
8386 else if (cd==nullptr && nd==nullptr && mcd==nullptr && mnd==nullptr && fd==mfd)
8387 {
8388 AUTO_TRACE_ADD("Match found for global scope");
8389 addEnumDocs(root,md);
8390 found = true;
8391 break;
8392 }
8393 }
8394 }
8395 }
8396 }
8397 if (!found)
8398 {
8399 warn(root->fileName,root->startLine, "Documentation for undefined enum '{}' found.", name);
8400 }
8401 }
8402 }
8403 for (const auto &e : root->children()) findEnumDocumentation(e.get());
8404}
8405
8406// search for each enum (member or function) in mnl if it has documented
8407// enum values.
8408static void findDEV(const MemberNameLinkedMap &mnsd)
8409{
8410 // for each member name
8411 for (const auto &mn : mnsd)
8412 {
8413 // for each member definition
8414 for (const auto &imd : *mn)
8415 {
8416 MemberDefMutable *md = toMemberDefMutable(imd.get());
8417 if (md && md->isEnumerate()) // member is an enum
8418 {
8419 int documentedEnumValues=0;
8420 // for each enum value
8421 for (const auto &fmd : md->enumFieldList())
8422 {
8423 if (fmd->isLinkableInProject()) documentedEnumValues++;
8424 }
8425 // at least one enum value is documented
8426 if (documentedEnumValues>0) md->setDocumentedEnumValues(true);
8427 }
8428 }
8429 }
8430}
8431
8432// search for each enum (member or function) if it has documented enum
8433// values.
8439
8440//----------------------------------------------------------------------
8441
8443{
8444 auto &index = Index::instance();
8445 // for each class member name
8446 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8447 {
8448 // for each member definition
8449 for (const auto &md : *mn)
8450 {
8451 index.addClassMemberNameToIndex(md.get());
8452 if (md->getModuleDef())
8453 {
8454 index.addModuleMemberNameToIndex(md.get());
8455 }
8456 }
8457 }
8458 // for each file/namespace function name
8459 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8460 {
8461 // for each member definition
8462 for (const auto &md : *mn)
8463 {
8464 if (md->getNamespaceDef())
8465 {
8466 index.addNamespaceMemberNameToIndex(md.get());
8467 }
8468 else
8469 {
8470 index.addFileMemberNameToIndex(md.get());
8471 }
8472 if (md->getModuleDef())
8473 {
8474 index.addModuleMemberNameToIndex(md.get());
8475 }
8476 }
8477 }
8478
8479 index.sortMemberIndexLists();
8480}
8481
8482//----------------------------------------------------------------------
8483
8484static void addToIndices()
8485{
8486 for (const auto &cd : *Doxygen::classLinkedMap)
8487 {
8488 if (cd->isLinkableInProject())
8489 {
8490 Doxygen::indexList->addIndexItem(cd.get(),nullptr);
8491 if (Doxygen::searchIndex.enabled())
8492 {
8493 Doxygen::searchIndex.setCurrentDoc(cd.get(),cd->anchor(),false);
8494 Doxygen::searchIndex.addWord(cd->localName(),true);
8495 }
8496 }
8497 }
8498
8499 for (const auto &cd : *Doxygen::conceptLinkedMap)
8500 {
8501 if (cd->isLinkableInProject())
8502 {
8503 Doxygen::indexList->addIndexItem(cd.get(),nullptr);
8504 if (Doxygen::searchIndex.enabled())
8505 {
8506 Doxygen::searchIndex.setCurrentDoc(cd.get(),cd->anchor(),false);
8507 Doxygen::searchIndex.addWord(cd->localName(),true);
8508 }
8509 }
8510 }
8511
8512 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8513 {
8514 if (nd->isLinkableInProject())
8515 {
8516 Doxygen::indexList->addIndexItem(nd.get(),nullptr);
8517 if (Doxygen::searchIndex.enabled())
8518 {
8519 Doxygen::searchIndex.setCurrentDoc(nd.get(),nd->anchor(),false);
8520 Doxygen::searchIndex.addWord(nd->localName(),true);
8521 }
8522 }
8523 }
8524
8525 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8526 {
8527 for (const auto &fd : *fn)
8528 {
8529 if (Doxygen::searchIndex.enabled() && fd->isLinkableInProject())
8530 {
8531 Doxygen::searchIndex.setCurrentDoc(fd.get(),fd->anchor(),false);
8532 Doxygen::searchIndex.addWord(fd->localName(),true);
8533 }
8534 }
8535 }
8536
8537 auto addWordsForTitle = [](const Definition *d,const DString &anchor,const DString &title)
8538 {
8540 if (Doxygen::searchIndex.enabled())
8541 {
8542 Doxygen::searchIndex.setCurrentDoc(d,anchor,false);
8543 std::string s = title.str();
8544 static const reg::Ex re(R"(\a[\w-]*)");
8545 reg::Iterator it(s,re);
8547 for (; it!=end ; ++it)
8548 {
8549 const auto &match = *it;
8550 std::string matchStr = match.str();
8551 Doxygen::searchIndex.addWord(matchStr,true);
8552 }
8553 }
8554 };
8555
8556 for (const auto &gd : *Doxygen::groupLinkedMap)
8557 {
8558 if (gd->isLinkableInProject())
8559 {
8560 addWordsForTitle(gd.get(),gd->anchor(),gd->groupTitle());
8561 }
8562 }
8563
8564 for (const auto &pd : *Doxygen::pageLinkedMap)
8565 {
8566 if (pd->isLinkableInProject())
8567 {
8568 addWordsForTitle(pd.get(),pd->anchor(),pd->title());
8569 }
8570 }
8571
8573 {
8574 addWordsForTitle(Doxygen::mainPage.get(),Doxygen::mainPage->anchor(),Doxygen::mainPage->title());
8575 }
8576
8577 auto addMemberToSearchIndex = [](const MemberDef *md)
8578 {
8579 if (Doxygen::searchIndex.enabled())
8580 {
8581 Doxygen::searchIndex.setCurrentDoc(md,md->anchor(),false);
8582 DString ln=md->localName();
8583 DString qn=md->qualifiedName();
8585 if (ln!=qn)
8586 {
8588 if (md->getClassDef())
8589 {
8590 Doxygen::searchIndex.addWord(md->getClassDef()->displayName(),true);
8591 }
8592 if (md->getNamespaceDef())
8593 {
8594 Doxygen::searchIndex.addWord(md->getNamespaceDef()->displayName(),true);
8595 }
8596 }
8597 }
8598 };
8599
8600 auto getScope = [](const MemberDef *md)
8601 {
8602 const Definition *scope = nullptr;
8603 if (md->getGroupDef()) scope = md->getGroupDef();
8604 else if (md->getClassDef()) scope = md->getClassDef();
8605 else if (md->getNamespaceDef()) scope = md->getNamespaceDef();
8606 else if (md->getFileDef()) scope = md->getFileDef();
8607 return scope;
8608 };
8609
8610 auto addMemberToIndices = [addMemberToSearchIndex,getScope](const MemberDef *md)
8611 {
8612 if (md->isLinkableInProject())
8613 {
8614 if (!(md->isEnumerate() && md->isAnonymous()))
8615 {
8616 Doxygen::indexList->addIndexItem(getScope(md),md);
8617 addMemberToSearchIndex(md);
8618 }
8619 if (md->isEnumerate())
8620 {
8621 for (const auto &fmd : md->enumFieldList())
8622 {
8623 Doxygen::indexList->addIndexItem(getScope(fmd),fmd);
8624 addMemberToSearchIndex(fmd);
8625 }
8626 }
8627 }
8628 };
8629
8630 // for each class member name
8631 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8632 {
8633 // for each member definition
8634 for (const auto &md : *mn)
8635 {
8636 addMemberToIndices(md.get());
8637 }
8638 }
8639 // for each file/namespace function name
8640 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8641 {
8642 // for each member definition
8643 for (const auto &md : *mn)
8644 {
8645 addMemberToIndices(md.get());
8646 }
8647 }
8648}
8649
8650//----------------------------------------------------------------------
8651
8653{
8654 // for each member name
8655 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8656 {
8657 // for each member definition
8658 for (const auto &imd : *mn)
8659 {
8660 MemberDefMutable *md = toMemberDefMutable(imd.get());
8661 if (md)
8662 {
8664 }
8665 }
8666 }
8667 // for each member name
8668 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8669 {
8670 // for each member definition
8671 for (const auto &imd : *mn)
8672 {
8673 MemberDefMutable *md = toMemberDefMutable(imd.get());
8674 if (md)
8675 {
8677 }
8678 }
8679 }
8680}
8681
8682// recursive helper function looking for reimplements/implemented
8683// by relations between class cd and direct or indirect base class bcd
8684static void computeMemberRelationsForBaseClass(const ClassDef *cd,const BaseClassDef *bcd,const ArgumentList *actualArgs)
8685{
8686 for (const auto &mn : cd->memberNameInfoLinkedMap()) // for each member in class cd with a unique name
8687 {
8688 for (const auto &imd : *mn) // for each member with a given name
8689 {
8690 MemberDefMutable *md = toMemberDefMutable(imd->memberDef());
8691 if (md && (md->isFunction() || md->isCSharpProperty())) // filter on reimplementable members
8692 {
8693 ClassDef *mbcd = bcd->classDef;
8694 if (mbcd && mbcd->isLinkable()) // filter on linkable classes
8695 {
8696 const auto &bmn = mbcd->memberNameInfoLinkedMap();
8697 const auto &bmni = bmn.find(mn->memberName());
8698 if (bmni) // there are base class members with the same name
8699 {
8700 for (const auto &ibmd : *bmni) // for base class member with that name
8701 {
8702 MemberDefMutable *bmd = toMemberDefMutable(ibmd->memberDef());
8703 if (bmd) // not part of an inline namespace
8704 {
8705 auto lang = bmd->getLanguage();
8706 auto compType = mbcd->compoundType();
8707 if (bmd->virtualness()!=Specifier::Normal ||
8708 lang==SrcLangExt::Python ||
8709 lang==SrcLangExt::Java ||
8710 lang==SrcLangExt::PHP ||
8711 compType==ClassDef::Interface ||
8712 compType==ClassDef::Protocol)
8713 {
8714 const ArgumentList &bmdAl = bmd->argumentList();
8715 const ArgumentList &mdAl = md->argumentList();
8716 const ClassDef *baseClass = bcd->classDef;
8717 const ClassDef *baseTemplate = baseClass->templateMaster() ? baseClass->templateMaster() : baseClass;
8718 const ClassDef *memberClass = bmd->getClassDef();
8719 bool instantiateBaseMember = actualArgs &&
8720 (memberClass==baseClass || memberClass==baseTemplate) &&
8721 !baseTemplate->templateArguments().empty();
8722 bool useDerivedScope = false;
8723 ArgumentList instantiatedBmdAl;
8724 DString instantiatedBmdType = bmd->typeString();
8725 if (instantiateBaseMember)
8726 {
8727 instantiatedBmdAl = bmdAl;
8728 for (Argument &arg : instantiatedBmdAl)
8729 {
8731 arg.type,baseTemplate->templateArguments(),actualArgs);
8732 useDerivedScope = useDerivedScope || substitutedType!=arg.type;
8733 arg.type = substitutedType;
8734 }
8735 DString substitutedTrailingReturnType = substituteTemplateArgumentsInString(
8736 instantiatedBmdAl.trailingReturnType(),baseTemplate->templateArguments(),actualArgs);
8737 useDerivedScope = useDerivedScope ||
8738 substitutedTrailingReturnType!=instantiatedBmdAl.trailingReturnType();
8739 instantiatedBmdAl.setTrailingReturnType(substitutedTrailingReturnType);
8741 instantiatedBmdType,baseTemplate->templateArguments(),actualArgs);
8742 useDerivedScope = useDerivedScope || substitutedType!=instantiatedBmdType;
8743 instantiatedBmdType = substitutedType;
8744 }
8745 //printf(" Base argList='%s'\n Super argList='%s'\n",
8746 // qPrint(argListToString(bmdAl)),
8747 // qPrint(argListToString(mdAl))
8748 // );
8749 if (
8750 lang==SrcLangExt::Python ||
8751 matchArguments2(useDerivedScope ? cd : bmd->getOuterScope(),
8752 useDerivedScope ? cd->getFileDef() : bmd->getFileDef(),
8753 instantiatedBmdType,instantiateBaseMember ? &instantiatedBmdAl : &bmdAl,
8754 md->getOuterScope(), md->getFileDef(), md->typeString(),&mdAl,
8755 true,lang
8756 )
8757 )
8758 {
8759 if (lang==SrcLangExt::Python && md->name().startsWith("__")) continue; // private members do not reimplement
8760 //printf("match!\n");
8761 const MemberDef *rmd = md->reimplements();
8762 if (rmd==nullptr) // not already assigned
8763 {
8764 //printf("%s: setting (new) reimplements member %s\n",qPrint(md->qualifiedName()),qPrint(bmd->qualifiedName()));
8765 md->setReimplements(bmd);
8766 }
8767 //printf("%s: add reimplementedBy member %s\n",qPrint(bmd->qualifiedName()),qPrint(md->qualifiedName()));
8768 bmd->insertReimplementedBy(md);
8769 }
8770 else
8771 {
8772 //printf("no match!\n");
8773 }
8774 }
8775 }
8776 }
8777 }
8778 }
8779 }
8780 }
8781 }
8782
8783 // do also for indirect base classes
8784 for (const auto &bbcd : bcd->classDef->baseClasses())
8785 {
8786 DString templSpec = bbcd.templSpecifiers;
8787 const ClassDef *baseClass = bcd->classDef;
8788 const ClassDef *baseTemplate = baseClass->templateMaster() ? baseClass->templateMaster() : baseClass;
8789 if (actualArgs)
8790 {
8791 templSpec = substituteTemplateArgumentsInString(templSpec,baseTemplate->templateArguments(),actualArgs);
8792 }
8793 auto nextActualArgs = stringToArgumentList(baseClass->getLanguage(),templSpec);
8794 computeMemberRelationsForBaseClass(cd,&bbcd,nextActualArgs.get());
8795 }
8796}
8797
8798//----------------------------------------------------------------------
8799// computes the relation between all members. For each member 'm'
8800// the members that override the implementation of 'm' are searched and
8801// the member that 'm' overrides is searched.
8802
8804{
8805 for (const auto &cd : *Doxygen::classLinkedMap)
8806 {
8807 if (cd->isLinkable())
8808 {
8809 for (const auto &bcd : cd->baseClasses())
8810 {
8811 auto actualArgs = stringToArgumentList(cd->getLanguage(),bcd.templSpecifiers);
8812 computeMemberRelationsForBaseClass(cd.get(),&bcd,actualArgs.get());
8813 }
8814 }
8815 }
8816}
8817
8818//----------------------------------------------------------------------------
8819
8821{
8822 // for each class
8823 for (const auto &cd : *Doxygen::classLinkedMap)
8824 {
8825 // that is a template
8826 for (const auto &ti : cd->getTemplateInstances())
8827 {
8828 ClassDefMutable *tcdm = toClassDefMutable(ti.classDef);
8829 if (tcdm && tcdm->isImplicitTemplateInstance()) // (partial) specializations have their own members
8830 {
8831 tcdm->addMembersToTemplateInstance(cd.get(),cd->templateArguments(),ti.templSpec);
8832 }
8833 }
8834 }
8835}
8836
8837//----------------------------------------------------------------------------
8838
8839static void mergeCategories()
8840{
8841 AUTO_TRACE();
8842 // merge members of categories into the class they extend
8843 for (const auto &cd : *Doxygen::classLinkedMap)
8844 {
8845 if (size_t i=cd->name().find('('); i!=DString::npos) // it is an Objective-C category
8846 {
8847 DString baseName=cd->name().left(i);
8848 ClassDefMutable *baseClass=toClassDefMutable(Doxygen::classLinkedMap->find(baseName));
8849 if (baseClass)
8850 {
8851 AUTO_TRACE_ADD("merging members of category {} into {}",cd->name(),baseClass->name());
8852 baseClass->mergeCategory(cd.get());
8853 }
8854 }
8855 }
8856}
8857
8858// builds the list of all members for each class
8859
8861{
8862 // merge the member list of base classes into the inherited classes.
8863 for (const auto &cd : *Doxygen::classLinkedMap)
8864 {
8865 if (// !cd->isReference() && // not an external class
8866 cd->subClasses().empty() && // is a root of the hierarchy
8867 !cd->baseClasses().empty()) // and has at least one base class
8868 {
8869 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8870 if (cdm)
8871 {
8872 //printf("*** merging members for %s\n",qPrint(cd->name()));
8873 cdm->mergeMembers();
8874 }
8875 }
8876 }
8877 // now sort the member list of all members for all classes.
8878 for (const auto &cd : *Doxygen::classLinkedMap)
8879 {
8880 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8881 if (cdm)
8882 {
8883 cdm->sortAllMembersList();
8884 }
8885 }
8886}
8887
8888//----------------------------------------------------------------------------
8889
8891{
8892 auto processSourceFile = [](FileDef *fd,OutputList &ol,ClangTUParser *parser)
8893 {
8894 bool showSources = fd->generateSourceFile() && !Htags::useHtags; // sources need to be shown in the output
8895 bool parseSources = !fd->isReference() && Doxygen::parseSourcesNeeded; // we needed to parse the sources even if we do not show them
8896 if (showSources)
8897 {
8898 msg("Generating code for file {}...\n",fd->docName());
8899 fd->writeSourceHeader(ol);
8900 fd->writeSourceBody(ol,parser);
8901 fd->writeSourceFooter(ol);
8902 }
8903 else if (parseSources)
8904 {
8905 msg("Parsing code for file {}...\n",fd->docName());
8906 fd->parseSource(parser);
8907 }
8908 };
8909 if (!Doxygen::inputNameLinkedMap->empty())
8910 {
8911#if USE_LIBCLANG
8913 {
8914 StringUnorderedSet processedFiles;
8915
8916 // create a dictionary with files to process
8917 StringUnorderedSet filesToProcess;
8918
8919 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8920 {
8921 for (const auto &fd : *fn)
8922 {
8923 filesToProcess.insert(fd->absFilePath().str());
8924 }
8925 }
8926 // process source files (and their include dependencies)
8927 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8928 {
8929 for (const auto &fd : *fn)
8930 {
8931 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt::Cpp &&
8932 (fd->generateSourceFile() ||
8934 )
8935 )
8936 {
8937 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8938 clangParser->parse();
8939 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8940
8941 for (auto incFile : clangParser->filesInSameTU())
8942 {
8943 if (filesToProcess.find(incFile)!=filesToProcess.end() && // part of input
8944 fd->absFilePath()!=incFile && // not same file
8945 processedFiles.find(incFile)==processedFiles.end()) // not yet marked as processed
8946 {
8947 StringVector moreFiles;
8948 bool ambig = false;
8949 FileDef *ifd=Doxygen::inputNameLinkedMap->findFileDef(incFile,ambig);
8950 if (ifd && !ifd->isReference())
8951 {
8952 processSourceFile(ifd,*g_outputList,clangParser.get());
8953 processedFiles.insert(incFile);
8954 }
8955 }
8956 }
8957 processedFiles.insert(fd->absFilePath().str());
8958 }
8959 }
8960 }
8961 // process remaining files
8962 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8963 {
8964 for (const auto &fd : *fn)
8965 {
8966 if (processedFiles.find(fd->absFilePath().str())==processedFiles.end()) // not yet processed
8967 {
8968 if (fd->getLanguage()==SrcLangExt::Cpp) // C/C++ file, use clang parser
8969 {
8970 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8971 clangParser->parse();
8972 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8973 }
8974 else // non C/C++ file, use built-in parser
8975 {
8976 processSourceFile(fd.get(),*g_outputList,nullptr);
8977 }
8978 }
8979 }
8980 }
8981 }
8982 else
8983#endif
8984 {
8985 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8986 if (numThreads>1)
8987 {
8988 msg("Generating code files using {} threads.\n",numThreads);
8989 struct SourceContext
8990 {
8991 SourceContext(FileDef *fd_,bool gen_,const OutputList &ol_)
8992 : fd(fd_), generateSourceFile(gen_), ol(ol_) {}
8993 FileDef *fd;
8994 bool generateSourceFile;
8995 OutputList ol;
8996 };
8997 ThreadPool threadPool(numThreads);
8998 std::vector< std::future< std::shared_ptr<SourceContext> > > results;
8999 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9000 {
9001 for (const auto &fd : *fn)
9002 {
9003 bool generateSourceFile = fd->generateSourceFile() && !Htags::useHtags;
9004 auto ctx = std::make_shared<SourceContext>(fd.get(),generateSourceFile,*g_outputList);
9005 auto processFile = [ctx]()
9006 {
9007 if (ctx->generateSourceFile)
9008 {
9009 msg("Generating code for file {}...\n",ctx->fd->docName());
9010 }
9011 else
9012 {
9013 msg("Parsing code for file {}...\n",ctx->fd->docName());
9014 }
9015 StringVector filesInSameTu;
9016 ctx->fd->getAllIncludeFilesRecursively(filesInSameTu);
9017 if (ctx->generateSourceFile) // sources need to be shown in the output
9018 {
9019 ctx->fd->writeSourceHeader(ctx->ol);
9020 ctx->fd->writeSourceBody(ctx->ol,nullptr);
9021 ctx->fd->writeSourceFooter(ctx->ol);
9022 }
9023 else if (!ctx->fd->isReference() && Doxygen::parseSourcesNeeded)
9024 // we needed to parse the sources even if we do not show them
9025 {
9026 ctx->fd->parseSource(nullptr);
9027 }
9028 return ctx;
9029 };
9030 results.emplace_back(threadPool.queue(processFile));
9031 }
9032 }
9033 for (auto &f : results)
9034 {
9035 auto ctx = f.get();
9036 }
9037 }
9038 else // single threaded version
9039 {
9040 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9041 {
9042 for (const auto &fd : *fn)
9043 {
9044 StringVector filesInSameTu;
9045 fd->getAllIncludeFilesRecursively(filesInSameTu);
9046 processSourceFile(fd.get(),*g_outputList,nullptr);
9047 }
9048 }
9049 }
9050 }
9051 }
9052}
9053
9054//----------------------------------------------------------------------------
9055
9056static void generateFileDocs()
9057{
9058 if (Index::instance().numDocumentedFiles()==0) return;
9059
9060 if (!Doxygen::inputNameLinkedMap->empty())
9061 {
9062 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9063 if (numThreads>1) // multi threaded processing
9064 {
9065 struct DocContext
9066 {
9067 DocContext(FileDef *fd_,const OutputList &ol_)
9068 : fd(fd_), ol(ol_) {}
9069 FileDef *fd;
9070 OutputList ol;
9071 };
9072 ThreadPool threadPool(numThreads);
9073 std::vector< std::future< std::shared_ptr<DocContext> > > results;
9074 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9075 {
9076 for (const auto &fd : *fn)
9077 {
9078 bool doc = fd->isLinkableInProject();
9079 if (doc)
9080 {
9081 auto ctx = std::make_shared<DocContext>(fd.get(),*g_outputList);
9082 auto processFile = [ctx]() {
9083 msg("Generating docs for file {}...\n",ctx->fd->docName());
9084 ctx->fd->writeDocumentation(ctx->ol);
9085 return ctx;
9086 };
9087 results.emplace_back(threadPool.queue(processFile));
9088 }
9089 }
9090 }
9091 for (auto &f : results)
9092 {
9093 auto ctx = f.get();
9094 }
9095 }
9096 else // single threaded processing
9097 {
9098 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9099 {
9100 for (const auto &fd : *fn)
9101 {
9102 bool doc = fd->isLinkableInProject();
9103 if (doc)
9104 {
9105 msg("Generating docs for file {}...\n",fd->docName());
9106 fd->writeDocumentation(*g_outputList);
9107 }
9108 }
9109 }
9110 }
9111 }
9112}
9113
9114//----------------------------------------------------------------------------
9115
9117{
9118 // add source references for class definitions
9119 for (const auto &cd : *Doxygen::classLinkedMap)
9120 {
9121 const FileDef *fd=cd->getBodyDef();
9122 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
9123 {
9124 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),nullptr);
9125 }
9126 }
9127 // add source references for concept definitions
9128 for (const auto &cd : *Doxygen::conceptLinkedMap)
9129 {
9130 const FileDef *fd=cd->getBodyDef();
9131 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
9132 {
9133 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),nullptr);
9134 }
9135 }
9136 // add source references for namespace definitions
9137 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9138 {
9139 const FileDef *fd=nd->getBodyDef();
9140 if (fd && nd->isLinkableInProject() && nd->getStartDefLine()!=-1)
9141 {
9142 const_cast<FileDef*>(fd)->addSourceRef(nd->getStartDefLine(),nd.get(),nullptr);
9143 }
9144 }
9145
9146 // add source references for member names
9147 for (const auto &mn : *Doxygen::memberNameLinkedMap)
9148 {
9149 for (const auto &md : *mn)
9150 {
9151 //printf("class member %s: def=%s body=%d link?=%d\n",
9152 // qPrint(md->name()),
9153 // md->getBodyDef()?qPrint(md->getBodyDef()->name()):"<none>",
9154 // md->getStartBodyLine(),md->isLinkableInProject());
9155 const FileDef *fd=md->getBodyDef();
9156 if (fd &&
9157 md->getStartDefLine()!=-1 &&
9158 md->isLinkableInProject() &&
9160 )
9161 {
9162 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
9163 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
9164 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
9165 }
9166 }
9167 }
9168 for (const auto &mn : *Doxygen::functionNameLinkedMap)
9169 {
9170 for (const auto &md : *mn)
9171 {
9172 const FileDef *fd=md->getBodyDef();
9173 //printf("member %s body=[%d,%d] fd=%p link=%d parseSources=%d\n",
9174 // qPrint(md->name()),
9175 // md->getStartBodyLine(),md->getEndBodyLine(),fd,
9176 // md->isLinkableInProject(),
9177 // Doxygen::parseSourcesNeeded);
9178 if (fd &&
9179 md->getStartDefLine()!=-1 &&
9180 md->isLinkableInProject() &&
9182 )
9183 {
9184 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
9185 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
9186 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
9187 }
9188 }
9189 }
9190}
9191
9192//----------------------------------------------------------------------------
9193
9194// add the macro definitions found during preprocessing as file members
9195static void buildDefineList()
9196{
9197 AUTO_TRACE();
9198 for (const auto &s : g_inputFiles)
9199 {
9200 auto it = Doxygen::macroDefinitions.find(s);
9202 {
9203 for (const auto &def : it->second)
9204 {
9205 auto md = createMemberDef(
9206 def.fileName,def.lineNr,def.columnNr,
9207 "#define",def.name,def.args,DString(),
9208 Protection::Public,Specifier::Normal,false,Relationship::Member,MemberType::Define,
9209 ArgumentList(),ArgumentList(),"");
9210 auto mmd = toMemberDefMutable(md.get());
9211
9212 if (!def.args.empty())
9213 {
9214 mmd->moveArgumentList(stringToArgumentList(SrcLangExt::Cpp, def.args));
9215 }
9216 mmd->setInitializer(def.definition);
9217 mmd->setFileDef(def.fileDef);
9218 mmd->setDefinition("#define "+def.name);
9219
9221 if (def.fileDef)
9222 {
9223 const MemberList *defMl = def.fileDef->getMemberList(MemberListType::DocDefineMembers());
9224 if (defMl)
9225 {
9226 const MemberDef *defMd = defMl->findRev(def.name);
9227 if (defMd) // definition already stored
9228 {
9229 mmd->setRedefineCount(defMd->redefineCount()+1);
9230 }
9231 }
9232 def.fileDef->insertMember(md.get());
9233 }
9234 AUTO_TRACE_ADD("adding macro {} with definition {}",def.name,def.definition);
9235 mn->push_back(std::move(md));
9236 }
9237 }
9238 }
9239}
9240
9241//----------------------------------------------------------------------------
9242
9243static void sortMemberLists()
9244{
9245 // sort class member lists
9246 for (const auto &cd : *Doxygen::classLinkedMap)
9247 {
9248 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9249 if (cdm)
9250 {
9251 cdm->sortMemberLists();
9252 }
9253 }
9254
9255 // sort namespace member lists
9256 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9257 {
9259 if (ndm)
9260 {
9261 ndm->sortMemberLists();
9262 }
9263 }
9264
9265 // sort file member lists
9266 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9267 {
9268 for (const auto &fd : *fn)
9269 {
9270 fd->sortMemberLists();
9271 }
9272 }
9273
9274 // sort group member lists
9275 for (const auto &gd : *Doxygen::groupLinkedMap)
9276 {
9277 gd->sortMemberLists();
9278 }
9279
9281}
9282
9283//----------------------------------------------------------------------------
9284
9285static bool isSymbolHidden(const Definition *d)
9286{
9287 bool hidden = d->isHidden();
9288 const Definition *parent = d->getOuterScope();
9289 return parent ? hidden || isSymbolHidden(parent) : hidden;
9290}
9291
9293{
9294 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9295 if (numThreads>1)
9296 {
9297 ThreadPool threadPool(numThreads);
9298 std::vector < std::future< void > > results;
9299 // queue the work
9300 for (const auto &[name,symList] : *Doxygen::symbolMap)
9301 {
9302 for (const auto &def : symList)
9303 {
9305 if (dm && !isSymbolHidden(def) && !def->isArtificial() && def->isLinkableInProject())
9306 {
9307 auto processTooltip = [dm]() {
9308 dm->computeTooltip();
9309 };
9310 results.emplace_back(threadPool.queue(processTooltip));
9311 }
9312 }
9313 }
9314 // wait for the results
9315 for (auto &f : results)
9316 {
9317 f.get();
9318 }
9319 }
9320 else
9321 {
9322 for (const auto &[name,symList] : *Doxygen::symbolMap)
9323 {
9324 for (const auto &def : symList)
9325 {
9327 if (dm && !isSymbolHidden(def) && !def->isArtificial() && def->isLinkableInProject())
9328 {
9329 dm->computeTooltip();
9330 }
9331 }
9332 }
9333 }
9334}
9335
9336//----------------------------------------------------------------------------
9337
9339{
9340 for (const auto &cd : *Doxygen::classLinkedMap)
9341 {
9342 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9343 if (cdm)
9344 {
9345 cdm->setAnonymousEnumType();
9346 }
9347 }
9348}
9349
9350//----------------------------------------------------------------------------
9351
9352static void countMembers()
9353{
9354 for (const auto &cd : *Doxygen::classLinkedMap)
9355 {
9356 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9357 if (cdm)
9358 {
9359 cdm->countMembers();
9360 }
9361 }
9362
9363 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9364 {
9366 if (ndm)
9367 {
9368 ndm->countMembers();
9369 }
9370 }
9371
9372 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9373 {
9374 for (const auto &fd : *fn)
9375 {
9376 fd->countMembers();
9377 }
9378 }
9379
9380 for (const auto &gd : *Doxygen::groupLinkedMap)
9381 {
9382 gd->countMembers();
9383 }
9384
9385 auto &mm = ModuleManager::instance();
9386 mm.countMembers();
9387}
9388
9389
9390//----------------------------------------------------------------------------
9391// generate the documentation for all classes
9392
9393static void generateDocsForClassList(const std::vector<ClassDefMutable*> &classList)
9394{
9395 AUTO_TRACE();
9396 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9397 if (numThreads>1) // multi threaded processing
9398 {
9399 struct DocContext
9400 {
9401 DocContext(ClassDefMutable *cd_,const OutputList &ol_)
9402 : cd(cd_), ol(ol_) {}
9403 ClassDefMutable *cd;
9404 OutputList ol;
9405 };
9406 ThreadPool threadPool(numThreads);
9407 std::vector< std::future< std::shared_ptr<DocContext> > > results;
9408 for (const auto &cd : classList)
9409 {
9410 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
9411 if (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9412 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
9413 )
9414 {
9415 auto ctx = std::make_shared<DocContext>(cd,*g_outputList);
9416 auto processFile = [ctx]()
9417 {
9418 msg("Generating docs for compound {}...\n",ctx->cd->displayName());
9419
9420 // skip external references, anonymous compounds and
9421 // template instances
9422 if (!ctx->cd->isHidden() && !ctx->cd->isEmbeddedInOuterScope() &&
9423 ctx->cd->isLinkableInProject() && !ctx->cd->isImplicitTemplateInstance())
9424 {
9425 ctx->cd->writeDocumentation(ctx->ol);
9426 ctx->cd->writeMemberList(ctx->ol);
9427 }
9428
9429 // even for undocumented classes, the inner classes can be documented.
9430 ctx->cd->writeDocumentationForInnerClasses(ctx->ol);
9431 return ctx;
9432 };
9433 results.emplace_back(threadPool.queue(processFile));
9434 }
9435 }
9436 for (auto &f : results)
9437 {
9438 auto ctx = f.get();
9439 }
9440 }
9441 else // single threaded processing
9442 {
9443 for (const auto &cd : classList)
9444 {
9445 //printf("cd=%s getOuterScope=%p global=%p hidden=%d embeddedInOuterScope=%d\n",
9446 // qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope,cd->isHidden(),cd->isEmbeddedInOuterScope());
9447 if (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9448 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
9449 )
9450 {
9451 // skip external references, anonymous compounds and
9452 // template instances
9453 if ( !cd->isHidden() && !cd->isEmbeddedInOuterScope() &&
9454 cd->isLinkableInProject() && !cd->isImplicitTemplateInstance())
9455 {
9456 msg("Generating docs for compound {}...\n",cd->displayName());
9457
9458 cd->writeDocumentation(*g_outputList);
9459 cd->writeMemberList(*g_outputList);
9460 }
9461 // even for undocumented classes, the inner classes can be documented.
9462 cd->writeDocumentationForInnerClasses(*g_outputList);
9463 }
9464 }
9465 }
9466}
9467
9468static void addClassAndNestedClasses(std::vector<ClassDefMutable*> &list,ClassDefMutable *cd)
9469{
9470 list.push_back(cd);
9471 for (const auto &innerCdi : cd->getClasses())
9472 {
9473 ClassDefMutable *innerCd = toClassDefMutable(innerCdi);
9474 if (innerCd)
9475 {
9476 AUTO_TRACE("innerCd={} isLinkable={} isImplicitTemplateInstance={} protectLevelVisible={} embeddedInOuterScope={}",
9477 innerCd->name(),innerCd->isLinkableInProject(),innerCd->isImplicitTemplateInstance(),protectionLevelVisible(innerCd->protection()),
9478 innerCd->isEmbeddedInOuterScope());
9479 }
9480 if (innerCd && innerCd->isLinkableInProject() && !innerCd->isImplicitTemplateInstance() &&
9481 protectionLevelVisible(innerCd->protection()) &&
9482 !innerCd->isEmbeddedInOuterScope()
9483 )
9484 {
9485 list.push_back(innerCd);
9486 addClassAndNestedClasses(list,innerCd);
9487 }
9488 }
9489}
9490
9492{
9493 std::vector<ClassDefMutable*> classList;
9494 for (const auto &cdi : *Doxygen::classLinkedMap)
9495 {
9496 ClassDefMutable *cd = toClassDefMutable(cdi.get());
9497 if (cd && (cd->getOuterScope()==nullptr ||
9499 {
9500 addClassAndNestedClasses(classList,cd);
9501 }
9502 }
9503 for (const auto &cdi : *Doxygen::hiddenClassLinkedMap)
9504 {
9505 ClassDefMutable *cd = toClassDefMutable(cdi.get());
9506 if (cd && (cd->getOuterScope()==nullptr ||
9508 {
9509 addClassAndNestedClasses(classList,cd);
9510 }
9511 }
9512 generateDocsForClassList(classList);
9513}
9514
9515//----------------------------------------------------------------------------
9516
9518{
9519 for (const auto &cdi : *Doxygen::conceptLinkedMap)
9520 {
9522
9523 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
9524 if (cd &&
9525 (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9526 cd->getOuterScope()==Doxygen::globalScope // only look at global concepts
9527 ) && !cd->isHidden() && cd->isLinkableInProject()
9528 )
9529 {
9530 msg("Generating docs for concept {}...\n",cd->displayName());
9532 }
9533 }
9534}
9535
9536//----------------------------------------------------------------------------
9537
9539{
9540 for (const auto &mn : *Doxygen::memberNameLinkedMap)
9541 {
9542 for (const auto &imd : *mn)
9543 {
9544 MemberDefMutable *md = toMemberDefMutable(imd.get());
9545 //static int count=0;
9546 //printf("%04d Member '%s'\n",count++,qPrint(md->qualifiedName()));
9547 if (md && md->documentation().empty() && md->briefDescription().empty())
9548 { // no documentation yet
9549 const MemberDef *bmd = md->reimplements();
9550 while (bmd && bmd->documentation().empty() &&
9551 bmd->briefDescription().empty()
9552 )
9553 { // search up the inheritance tree for a documentation member
9554 //printf("bmd=%s class=%s\n",qPrint(bmd->name()),qPrint(bmd->getClassDef()->name()));
9555 bmd = bmd->reimplements();
9556 }
9557 if (bmd) // copy the documentation from the reimplemented member
9558 {
9559 md->setInheritsDocsFrom(bmd);
9560 md->setDocumentation(bmd->documentation(),bmd->docFile(),bmd->docLine());
9562 md->setBriefDescription(bmd->briefDescription(),bmd->briefFile(),bmd->briefLine());
9563 md->copyArgumentNames(bmd);
9565 }
9566 }
9567 }
9568 }
9569}
9570
9571//----------------------------------------------------------------------------
9572
9574{
9575 // for each file
9576 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9577 {
9578 for (const auto &fd : *fn)
9579 {
9580 fd->combineUsingRelations();
9581 }
9582 }
9583
9584 // for each namespace
9585 NamespaceDefSet visitedNamespaces;
9586 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9587 {
9589 if (ndm)
9590 {
9591 ndm->combineUsingRelations(visitedNamespaces);
9592 }
9593 }
9594}
9595
9596//----------------------------------------------------------------------------
9597
9599{
9600 // for each class
9601 for (const auto &cd : *Doxygen::classLinkedMap)
9602 {
9603 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9604 if (cdm)
9605 {
9607 }
9608 }
9609 // for each file
9610 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9611 {
9612 for (const auto &fd : *fn)
9613 {
9614 fd->addMembersToMemberGroup();
9615 }
9616 }
9617 // for each namespace
9618 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9619 {
9621 if (ndm)
9622 {
9624 }
9625 }
9626 // for each group
9627 for (const auto &gd : *Doxygen::groupLinkedMap)
9628 {
9629 gd->addMembersToMemberGroup();
9630 }
9632}
9633
9634//----------------------------------------------------------------------------
9635
9637{
9638 // for each class
9639 for (const auto &cd : *Doxygen::classLinkedMap)
9640 {
9641 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9642 if (cdm)
9643 {
9645 }
9646 }
9647 // for each file
9648 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9649 {
9650 for (const auto &fd : *fn)
9651 {
9652 fd->distributeMemberGroupDocumentation();
9653 }
9654 }
9655 // for each namespace
9656 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9657 {
9659 if (ndm)
9660 {
9662 }
9663 }
9664 // for each group
9665 for (const auto &gd : *Doxygen::groupLinkedMap)
9666 {
9667 gd->distributeMemberGroupDocumentation();
9668 }
9670}
9671
9672//----------------------------------------------------------------------------
9673
9675{
9676 // for each class
9677 for (const auto &cd : *Doxygen::classLinkedMap)
9678 {
9679 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9680 if (cdm)
9681 {
9683 }
9684 }
9685 // for each concept
9686 for (const auto &cd : *Doxygen::conceptLinkedMap)
9687 {
9688 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
9689 if (cdm)
9690 {
9692 }
9693 }
9694 // for each file
9695 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9696 {
9697 for (const auto &fd : *fn)
9698 {
9699 fd->findSectionsInDocumentation();
9700 }
9701 }
9702 // for each namespace
9703 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9704 {
9706 if (ndm)
9707 {
9709 }
9710 }
9711 // for each group
9712 for (const auto &gd : *Doxygen::groupLinkedMap)
9713 {
9714 gd->findSectionsInDocumentation();
9715 }
9716 // for each page
9717 for (const auto &pd : *Doxygen::pageLinkedMap)
9718 {
9719 pd->findSectionsInDocumentation();
9720 }
9721 // for each directory
9722 for (const auto &dd : *Doxygen::dirLinkedMap)
9723 {
9724 dd->findSectionsInDocumentation();
9725 }
9727 if (Doxygen::mainPage) Doxygen::mainPage->findSectionsInDocumentation();
9728}
9729
9730//----------------------------------------------------------------------
9731
9732
9734{
9735 // remove all references to classes from the cache
9736 // as there can be new template instances in the inheritance path
9737 // to this class. Optimization: only remove those classes that
9738 // have inheritance instances as direct or indirect sub classes.
9740
9741 // remove all cached typedef resolutions whose target is a
9742 // template class as this may now be a template instance
9743 // for each global function name
9744 for (const auto &fn : *Doxygen::functionNameLinkedMap)
9745 {
9746 // for each function with that name
9747 for (const auto &ifmd : *fn)
9748 {
9749 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
9750 if (fmd && fmd->isTypedefValCached())
9751 {
9752 const ClassDef *cd = fmd->getCachedTypedefVal();
9753 if (cd->isTemplate()) fmd->invalidateTypedefValCache();
9754 }
9755 }
9756 }
9757 // for each class method name
9758 for (const auto &nm : *Doxygen::memberNameLinkedMap)
9759 {
9760 // for each function with that name
9761 for (const auto &imd : *nm)
9762 {
9763 MemberDefMutable *md = toMemberDefMutable(imd.get());
9764 if (md && md->isTypedefValCached())
9765 {
9766 const ClassDef *cd = md->getCachedTypedefVal();
9767 if (cd->isTemplate()) md->invalidateTypedefValCache();
9768 }
9769 }
9770 }
9771}
9772
9773//----------------------------------------------------------------------------
9774
9776{
9777 // Remove all unresolved references to classes from the cache.
9778 // This is needed before resolving the inheritance relations, since
9779 // it would otherwise not find the inheritance relation
9780 // for C in the example below, as B::I was already found to be unresolvable
9781 // (which is correct if you ignore the inheritance relation between A and B).
9782 //
9783 // class A { class I {} };
9784 // class B : public A {};
9785 // class C : public B::I {};
9787
9788 // for each class method name
9789 for (const auto &nm : *Doxygen::memberNameLinkedMap)
9790 {
9791 // for each function with that name
9792 for (const auto &imd : *nm)
9793 {
9794 MemberDefMutable *md = toMemberDefMutable(imd.get());
9795 if (md)
9796 {
9798 }
9799 }
9800 }
9801
9802}
9803
9804//----------------------------------------------------------------------------
9805// Returns true if the entry and member definition have equal file names,
9806// otherwise false.
9807
9808static bool haveEqualFileNames(const Entry *root, const MemberDef *md)
9809{
9810 if (const FileDef *fd = md->getFileDef())
9811 {
9812 return fd->absFilePath() == root->fileName;
9813 }
9814 return false;
9815}
9816
9817//----------------------------------------------------------------------------
9818
9819static void addDefineDoc(const Entry *root, MemberDefMutable *md)
9820{
9821 md->setDocumentation(root->doc,root->docFile,root->docLine);
9822 md->setDocsForDefinition(!root->proto);
9823 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9824 if (md->inbodyDocumentation().empty())
9825 {
9827 }
9828 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
9829 {
9830 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
9831 md->setBodyDef(root->fileDef());
9832 }
9834 md->setMaxInitLines(root->initLines);
9836 md->setRefItems(root->sli);
9837 md->setRequirementReferences(root->rqli);
9838 md->addQualifiers(root->qualifiers);
9839 if (root->mGrpId!=-1) md->setMemberGroupId(root->mGrpId);
9840 addMemberToGroups(root,md);
9842}
9843
9844//----------------------------------------------------------------------------
9845
9847{
9848 if ((root->section.isDefineDoc() || root->section.isDefine()) && !root->name.empty())
9849 {
9850 //printf("found define '%s' '%s' brief='%s' doc='%s'\n",
9851 // qPrint(root->name),qPrint(root->args),qPrint(root->brief),qPrint(root->doc));
9852
9853 if (root->tagInfo() && !root->name.empty()) // define read from a tag file
9854 {
9855 auto md = createMemberDef(root->tagInfo()->tagName,1,1,
9856 "#define",root->name,root->args,DString(),
9857 Protection::Public,Specifier::Normal,false,Relationship::Member,MemberType::Define,
9858 ArgumentList(),ArgumentList(),"");
9859 auto mmd = toMemberDefMutable(md.get());
9860 mmd->setTagInfo(root->tagInfo());
9861 mmd->setLanguage(root->lang);
9862 mmd->addQualifiers(root->qualifiers);
9863 //printf("Searching for '%s' fd=%p\n",qPrint(filePathName),fd);
9864 mmd->setFileDef(root->parent()->fileDef());
9865 //printf("Adding member=%s\n",qPrint(md->name()));
9867 mn->push_back(std::move(md));
9868 }
9870 if (mn)
9871 {
9872 int count=0;
9873 for (const auto &md : *mn)
9874 {
9875 if (md->memberType()==MemberType::Define) count++;
9876 }
9877 if (count==1)
9878 {
9879 for (const auto &imd : *mn)
9880 {
9881 MemberDefMutable *md = toMemberDefMutable(imd.get());
9882 if (md && md->memberType()==MemberType::Define)
9883 {
9884 addDefineDoc(root,md);
9885 }
9886 }
9887 }
9888 else if (count>1 &&
9889 (!root->doc.empty() ||
9890 !root->brief.empty() ||
9891 root->bodyLine!=-1
9892 )
9893 )
9894 // multiple defines don't know where to add docs
9895 // but maybe they are in different files together with their documentation
9896 {
9897 for (const auto &imd : *mn)
9898 {
9899 MemberDefMutable *md = toMemberDefMutable(imd.get());
9900 if (md && md->memberType()==MemberType::Define)
9901 {
9902 if (haveEqualFileNames(root, md) || isEntryInGroupOfMember(root, md))
9903 // doc and define in the same file or group assume they belong together.
9904 {
9905 addDefineDoc(root,md);
9906 }
9907 }
9908 }
9909 //warn("define {} found in the following files:\n",root->name);
9910 //warn("Cannot determine where to add the documentation found "
9911 // "at line {} of file {}. \n",
9912 // root->startLine,root->fileName);
9913 }
9914 }
9915 else if (!root->doc.empty() || !root->brief.empty()) // define not found
9916 {
9917 bool preEnabled = Config_getBool(ENABLE_PREPROCESSING);
9918 if (preEnabled)
9919 {
9920 warn(root->fileName,root->startLine,"documentation for unknown define {} found.",root->name);
9921 }
9922 else
9923 {
9924 warn(root->fileName,root->startLine, "found documented #define {} but ignoring it because ENABLE_PREPROCESSING is NO.", root->name);
9925 }
9926 }
9927 }
9928 for (const auto &e : root->children()) findDefineDocumentation(e.get());
9929}
9930
9931//----------------------------------------------------------------------------
9932
9933static void findDirDocumentation(const Entry *root)
9934{
9935 if (root->section.isDirDoc())
9936 {
9937 DString normalizedName = root->name;
9938 normalizedName = substitute(normalizedName,"\\","/");
9939 //printf("root->docFile=%s normalizedName=%s\n",
9940 // qPrint(root->docFile),qPrint(normalizedName));
9941 if (root->docFile==normalizedName) // current dir?
9942 {
9943 if (size_t lastSlashPos=normalizedName.rfind('/'); lastSlashPos!=DString::npos) // strip file name
9944 {
9945 normalizedName=normalizedName.left(lastSlashPos);
9946 }
9947 }
9948 if (normalizedName.at(normalizedName.length()-1)!='/')
9949 {
9950 normalizedName+='/';
9951 }
9952 DirDef *matchingDir=nullptr;
9953 for (const auto &dir : *Doxygen::dirLinkedMap)
9954 {
9955 //printf("Dir: %s<->%s\n",qPrint(dir->name()),qPrint(normalizedName));
9956 if (dir->name().right(normalizedName.length())==normalizedName)
9957 {
9958 if (matchingDir)
9959 {
9960 warn(root->fileName,root->startLine,
9961 "\\dir command matches multiple directories.\n"
9962 " Applying the command for directory {}\n"
9963 " Ignoring the command for directory {}",
9964 matchingDir->name(),dir->name()
9965 );
9966 }
9967 else
9968 {
9969 matchingDir=dir.get();
9970 }
9971 }
9972 }
9973 if (matchingDir)
9974 {
9975 //printf("Match for with dir %s #anchor=%zu\n",qPrint(matchingDir->name()),root->anchors.size());
9976 matchingDir->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9977 matchingDir->setDocumentation(root->doc,root->docFile,root->docLine);
9978 matchingDir->setRefItems(root->sli);
9979 matchingDir->setRequirementReferences(root->rqli);
9980 matchingDir->addSectionsToDefinition(root->anchors);
9981 root->commandOverrides.apply_directoryGraph([&](bool b) { matchingDir->overrideDirectoryGraph(b); });
9982 addDirToGroups(root,matchingDir);
9983 }
9984 else
9985 {
9986 warn(root->fileName,root->startLine,"No matching directory found for command \\dir {}",normalizedName);
9987 }
9988 }
9989 for (const auto &e : root->children()) findDirDocumentation(e.get());
9990}
9991
9992//----------------------------------------------------------------------------
9994{
9995 if (root->section.isRequirementDoc())
9996 {
9998 }
9999 for (const auto &e : root->children()) buildRequirementsList(e.get());
10000}
10001
10002//----------------------------------------------------------------------------
10003// create a (sorted) list of separate documentation pages
10004
10005static void buildPageList(Entry *root)
10006{
10007 if (root->section.isPageDoc())
10008 {
10009 if (!root->name.empty())
10010 {
10011 addRelatedPage(root);
10012 }
10013 }
10014 else if (root->section.isMainpageDoc())
10015 {
10016 DString title=root->args.stripWhiteSpace();
10017 if (title.empty()) title=theTranslator->trMainPage();
10018 //DString name = Config_getBool(GENERATE_TREEVIEW)?"main":"index";
10019 DString name = "index";
10020 addRefItem(root->sli,
10021 name,
10022 theTranslator->trPage(true,true),
10023 name,
10024 title,
10025 DString(),nullptr
10026 );
10027 }
10028 for (const auto &e : root->children()) buildPageList(e.get());
10029}
10030
10031// search for the main page defined in this project
10032static void findMainPage(Entry *root)
10033{
10034 if (root->section.isMainpageDoc())
10035 {
10036 if (Doxygen::mainPage==nullptr && root->tagInfo()==nullptr)
10037 {
10038 //printf("mainpage: docLine=%d startLine=%d\n",root->docLine,root->startLine);
10039 //printf("Found main page! \n======\n%s\n=======\n",qPrint(root->doc));
10040 DString title=root->args.stripWhiteSpace();
10041 if (title.empty()) title = Config_getString(PROJECT_NAME);
10042 //DString indexName=Config_getBool(GENERATE_TREEVIEW)?"main":"index";
10043 DString indexName="index";
10045 indexName, root->brief+root->doc+root->inbodyDocs,title);
10046 //setFileNameForSections(root->anchors,"index",Doxygen::mainPage);
10047 Doxygen::mainPage->setBriefDescription(root->brief,root->briefFile,root->briefLine);
10048 Doxygen::mainPage->setBodySegment(root->startLine,root->startLine,-1);
10049 Doxygen::mainPage->setFileName(indexName);
10050 Doxygen::mainPage->setLocalToc(root->localToc);
10052
10054 if (si)
10055 {
10056 if (!si->ref().empty()) // we are from a tag file
10057 {
10058 // a page name is a label as well! but should no be double either
10060 Doxygen::mainPage->name(),
10061 indexName,
10062 root->startLine,
10063 Doxygen::mainPage->title(),
10065 0); // level 0
10066 }
10067 else if (si->lineNr() != -1)
10068 {
10069 warn(root->fileName,root->startLine,"multiple use of section label '{}' for main page, (first occurrence: {}, line {})",
10070 Doxygen::mainPage->name(),si->fileName(),si->lineNr());
10071 }
10072 else
10073 {
10074 warn(root->fileName,root->startLine,"multiple use of section label '{}' for main page, (first occurrence: {})",
10075 Doxygen::mainPage->name(),si->fileName());
10076 }
10077 }
10078 else
10079 {
10080 // a page name is a label as well! but should no be double either
10082 Doxygen::mainPage->name(),
10083 indexName,
10084 root->startLine,
10085 Doxygen::mainPage->title(),
10087 0); // level 0
10088 }
10089 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
10090 }
10091 else if (root->tagInfo()==nullptr)
10092 {
10093 warn(root->fileName,root->startLine,
10094 "found more than one \\mainpage comment block! (first occurrence: {}, line {}), Skipping current block!",
10095 Doxygen::mainPage->docFile(),Doxygen::mainPage->getStartBodyLine());
10096 }
10097 }
10098 for (const auto &e : root->children()) findMainPage(e.get());
10099}
10100
10101// search for the main page imported via tag files and add only the section labels
10102static void findMainPageTagFiles(Entry *root)
10103{
10104 if (root->section.isMainpageDoc())
10105 {
10106 if (Doxygen::mainPage && root->tagInfo())
10107 {
10108 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
10109 }
10110 }
10111 for (const auto &e : root->children()) findMainPageTagFiles(e.get());
10112}
10113
10114static void computePageRelations(Entry *root)
10115{
10116 if ((root->section.isPageDoc() || root->section.isMainpageDoc()) && !root->name.empty())
10117 {
10118 PageDef *pd = root->section.isPageDoc() ?
10120 Doxygen::mainPage.get();
10121 if (pd)
10122 {
10123 for (const BaseInfo &bi : root->extends)
10124 {
10126 if (pd==subPd)
10127 {
10128 term("page defined {} with label {} is a direct "
10129 "subpage of itself! Please remove this cyclic dependency.\n",
10130 warn_line(pd->docFile(),pd->docLine()),pd->name());
10131 }
10132 else if (subPd)
10133 {
10134 pd->addInnerCompound(subPd);
10135 //printf("*** Added subpage relation: %s->%s\n",
10136 // qPrint(pd->name()),qPrint(subPd->name()));
10137 }
10138 }
10139 }
10140 }
10141 for (const auto &e : root->children()) computePageRelations(e.get());
10142}
10143
10145{
10146 for (const auto &pd : *Doxygen::pageLinkedMap)
10147 {
10148 Definition *ppd = pd->getOuterScope();
10149 while (ppd)
10150 {
10151 if (ppd==pd.get())
10152 {
10153 term("page defined {} with label {} is a subpage "
10154 "of itself! Please remove this cyclic dependency.\n",
10155 warn_line(pd->docFile(),pd->docLine()),pd->name());
10156 }
10157 ppd=ppd->getOuterScope();
10158 }
10159 }
10160}
10161
10162//----------------------------------------------------------------------------
10163
10165{
10166 for (const auto &si : SectionManager::instance())
10167 {
10168 //printf("si->label='%s' si->definition=%s si->fileName='%s'\n",
10169 // qPrint(si->label),si->definition?qPrint(si->definition->name()):"<none>",
10170 // qPrint(si->fileName));
10171 PageDef *pd=nullptr;
10172
10173 // hack: the items of a todo/test/bug/deprecated list are all fragments from
10174 // different files, so the resulting section's all have the wrong file
10175 // name (not from the todo/test/bug/deprecated list, but from the file in
10176 // which they are defined). We correct this here by looking at the
10177 // generated section labels!
10179 {
10180 DString label="_"+rl->listName(); // "_todo", "_test", ...
10181 if (si->label().left(label.length())==label)
10182 {
10183 si->setFileName(rl->listName());
10184 si->setGenerated(true);
10185 break;
10186 }
10187 }
10188
10189 //printf("start: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
10190 if (!si->generated())
10191 {
10192 // if this section is in a page and the page is in a group, then we
10193 // have to adjust the link file name to point to the group.
10194 if (!si->fileName().empty() &&
10195 (pd=Doxygen::pageLinkedMap->find(si->fileName())) &&
10196 pd->getGroupDef())
10197 {
10198 si->setFileName(pd->getGroupDef()->getOutputFileBase());
10199 }
10200
10201 if (si->definition())
10202 {
10203 // TODO: there should be one function in Definition that returns
10204 // the file to link to, so we can avoid the following tests.
10205 const GroupDef *gd=nullptr;
10206 if (si->definition()->definitionType()==Definition::TypeMember)
10207 {
10208 gd = (toMemberDef(si->definition()))->getGroupDef();
10209 }
10210
10211 if (gd)
10212 {
10213 si->setFileName(gd->getOutputFileBase());
10214 }
10215 else
10216 {
10217 //si->fileName=si->definition->getOutputFileBase();
10218 //printf("Setting si->fileName to %s\n",qPrint(si->fileName));
10219 }
10220 }
10221 }
10222 //printf("end: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
10223 }
10224}
10225
10226
10227
10228//----------------------------------------------------------------------------
10229// generate all separate documentation pages
10230
10231
10232static void generatePageDocs()
10233{
10234 //printf("documentedPages=%d real=%d\n",documentedPages,Doxygen::pageLinkedMap->count());
10235 if (Index::instance().numDocumentedPages()==0) return;
10236 for (const auto &pd : *Doxygen::pageLinkedMap)
10237 {
10238 if (!pd->getGroupDef() && !pd->isReference())
10239 {
10240 msg("Generating docs for page {}...\n",pd->name());
10241 pd->writeDocumentation(*g_outputList);
10242 }
10243 }
10244}
10245
10246//----------------------------------------------------------------------------
10247// create a (sorted) list & dictionary of example pages
10248
10249static void buildExampleList(Entry *root)
10250{
10251 if ((root->section.isExample() || root->section.isExampleLineno()) && !root->name.empty())
10252 {
10253 if (Doxygen::exampleLinkedMap->find(root->name))
10254 {
10255 warn(root->fileName,root->startLine,"Example {} was already documented. Ignoring documentation found here.",root->name);
10256 }
10257 else
10258 {
10260 createPageDef(root->fileName,root->startLine,
10261 root->name,root->brief+root->doc+root->inbodyDocs,root->args));
10262 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
10263 pd->setFileName(convertNameToFile(pd->name()+"-example",false,true));
10265 pd->setLanguage(root->lang);
10266 pd->setShowLineNo(root->section.isExampleLineno());
10267
10268 //we don't add example to groups
10269 //addExampleToGroups(root,pd);
10270 }
10271 }
10272 for (const auto &e : root->children()) buildExampleList(e.get());
10273}
10274
10275//----------------------------------------------------------------------------
10276// prints the Entry tree (for debugging)
10277
10278void printNavTree(Entry *root,int indent)
10279{
10281 {
10282 DString indentStr;
10283 indentStr.fill(' ',indent);
10284 Debug::print(Debug::Entries,0,"{}{} at {}:{} (sec={}, spec={})\n",
10285 indentStr.empty()?"":indentStr,
10286 root->name.empty()?"<empty>":root->name,
10287 root->fileName,root->startLine,
10288 root->section.to_string(),
10289 root->spec.to_string());
10290 for (const auto &e : root->children())
10291 {
10292 printNavTree(e.get(),indent+2);
10293 }
10294 }
10295}
10296
10297
10298//----------------------------------------------------------------------------
10299// prints the Sections tree (for debugging)
10300
10302{
10304 {
10305 for (const auto &si : SectionManager::instance())
10306 {
10307 Debug::print(Debug::Sections,0,"Section = {}, file = {}, title = {}, type = {}, ref = {}\n",
10308 si->label(),si->fileName(),si->title(),si->type().level(),si->ref());
10309 }
10310 }
10311}
10312
10313
10314//----------------------------------------------------------------------------
10315// generate the example documentation
10316
10318{
10320 for (const auto &pd : *Doxygen::exampleLinkedMap)
10321 {
10322 msg("Generating docs for example {}...\n",pd->name());
10323 SrcLangExt lang = getLanguageFromFileName(pd->name(), SrcLangExt::Unknown);
10324 if (lang != SrcLangExt::Unknown)
10325 {
10326 DString ext = getFileNameExtension(pd->name());
10327 auto intf = Doxygen::parserManager->getCodeParser(ext);
10328 intf->resetCodeParserState();
10329 }
10330 DString n=pd->getOutputFileBase();
10331 startFile(*g_outputList,n,false,n,pd->name());
10333 g_outputList->docify(pd->name());
10336 DString lineNoOptStr;
10337 if (pd->showLineNo())
10338 {
10339 lineNoOptStr="{lineno}";
10340 }
10341 g_outputList->generateDoc(pd->docFile(), // file
10342 pd->docLine(), // startLine
10343 pd.get(), // context
10344 nullptr, // memberDef
10345 (pd->briefDescription().empty()?"":pd->briefDescription()+"\n\n")+
10346 pd->documentation()+"\n\n\\include"+lineNoOptStr+" "+pd->name(), // docs
10347 DocOptions()
10348 .setIndexWords(true)
10349 .setExample(pd->name()));
10350 endFile(*g_outputList); // contains g_outputList->endContents()
10351 }
10353}
10354
10355//----------------------------------------------------------------------------
10356// generate module pages
10357
10359{
10360 for (const auto &gd : *Doxygen::groupLinkedMap)
10361 {
10362 if (!gd->isReference())
10363 {
10364 gd->writeDocumentation(*g_outputList);
10365 }
10366 }
10367}
10368
10369//----------------------------------------------------------------------------
10370// generate module pages
10371
10373{
10374 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10375 if (numThreads>1) // multi threaded processing
10376 {
10377 struct DocContext
10378 {
10379 DocContext(ClassDefMutable *cdm_,const OutputList &ol_)
10380 : cdm(cdm_), ol(ol_) {}
10381 ClassDefMutable *cdm;
10382 OutputList ol;
10383 };
10384 ThreadPool threadPool(numThreads);
10385 std::vector< std::future< std::shared_ptr<DocContext> > > results;
10386 // for each class in the namespace...
10387 for (const auto &cd : classList)
10388 {
10390 if (cdm)
10391 {
10392 auto ctx = std::make_shared<DocContext>(cdm,*g_outputList);
10393 auto processFile = [ctx]()
10394 {
10395 if ( ( ctx->cdm->isLinkableInProject() &&
10396 !ctx->cdm->isImplicitTemplateInstance()
10397 ) // skip external references, anonymous compounds and
10398 // template instances and nested classes
10399 && !ctx->cdm->isHidden() && !ctx->cdm->isEmbeddedInOuterScope()
10400 )
10401 {
10402 msg("Generating docs for compound {}...\n",ctx->cdm->displayName());
10403 ctx->cdm->writeDocumentation(ctx->ol);
10404 ctx->cdm->writeMemberList(ctx->ol);
10405 }
10406 ctx->cdm->writeDocumentationForInnerClasses(ctx->ol);
10407 return ctx;
10408 };
10409 results.emplace_back(threadPool.queue(processFile));
10410 }
10411 }
10412 // wait for the results
10413 for (auto &f : results)
10414 {
10415 auto ctx = f.get();
10416 }
10417 }
10418 else // single threaded processing
10419 {
10420 // for each class in the namespace...
10421 for (const auto &cd : classList)
10422 {
10424 if (cdm)
10425 {
10426 if ( ( cd->isLinkableInProject() &&
10427 !cd->isImplicitTemplateInstance()
10428 ) // skip external references, anonymous compounds and
10429 // template instances and nested classes
10430 && !cd->isHidden() && !cd->isEmbeddedInOuterScope()
10431 )
10432 {
10433 msg("Generating docs for compound {}...\n",cd->displayName());
10434
10437 }
10439 }
10440 }
10441 }
10442}
10443
10445{
10446 // for each concept in the namespace...
10447 for (const auto &cd : conceptList)
10448 {
10450 if ( cdm && cd->isLinkableInProject() && !cd->isHidden())
10451 {
10452 msg("Generating docs for concept {}...\n",cd->name());
10454 }
10455 }
10456}
10457
10459{
10460 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
10461
10462 //writeNamespaceIndex(*g_outputList);
10463
10464 // for each namespace...
10465 for (const auto &nd : *Doxygen::namespaceLinkedMap)
10466 {
10467 if (nd->isLinkableInProject())
10468 {
10470 if (ndm)
10471 {
10472 msg("Generating docs for namespace {}\n",nd->displayName());
10474 }
10475 }
10476
10477 generateNamespaceClassDocs(nd->getClasses());
10478 if (sliceOpt)
10479 {
10480 generateNamespaceClassDocs(nd->getInterfaces());
10481 generateNamespaceClassDocs(nd->getStructs());
10482 generateNamespaceClassDocs(nd->getExceptions());
10483 }
10484 generateNamespaceConceptDocs(nd->getConcepts());
10485 }
10486}
10487
10489{
10490 std::string oldDir = Dir::currentDirPath();
10491 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
10494 {
10495 err("failed to run html help compiler on {}\n", HtmlHelp::hhpFileName);
10496 }
10497 Dir::setCurrent(oldDir);
10498}
10499
10501{
10502 DString args = Qhp::qhpFileName + " -o \"" + Qhp::getQchFileName() + "\"";
10503 std::string oldDir = Dir::currentDirPath();
10504 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
10505
10506 DString qhgLocation=Config_getString(QHG_LOCATION);
10507 if (Debug::isFlagSet(Debug::Qhp)) // produce info for debugging
10508 {
10509 // run qhelpgenerator -v and extract the Qt version used
10510 DString cmd=qhgLocation+ " -v 2>&1";
10511 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
10512 FILE *f=Portable::popen(cmd,"r");
10513 if (!f)
10514 {
10515 err("could not execute {}\n",qhgLocation);
10516 }
10517 else
10518 {
10519 const size_t bufSize = 1024;
10520 char inBuf[bufSize+1];
10521 size_t numRead=fread(inBuf,1,bufSize,f);
10522 inBuf[numRead] = '\0';
10523 Debug::print(Debug::Qhp,0,"{}",inBuf);
10525
10526 int qtVersion=0;
10527 static const reg::Ex versionReg(R"(Qt (\d+)\.(\d+)\.(\d+))");
10528 reg::Match match;
10529 std::string s = inBuf;
10530 if (reg::search(s,match,versionReg))
10531 {
10532 qtVersion = 10000*DString(match[1].str()).toInt() +
10533 100*DString(match[2].str()).toInt() +
10534 DString(match[3].str()).toInt();
10535 }
10536 if (qtVersion>0 && (qtVersion<60000 || qtVersion >= 60205))
10537 {
10538 // dump the output of qhelpgenerator -c file.qhp
10539 // Qt<6 or Qt>=6.2.5 or higher, see https://bugreports.qt.io/browse/QTBUG-101070
10540 cmd=qhgLocation+ " -c " + Qhp::qhpFileName + " 2>&1";
10541 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
10542 f=Portable::popen(cmd,"r");
10543 if (!f)
10544 {
10545 err("could not execute {}\n",qhgLocation);
10546 }
10547 else
10548 {
10549 std::string output;
10550 while ((numRead=fread(inBuf,1,bufSize,f))>0)
10551 {
10552 inBuf[numRead] = '\0';
10553 output += inBuf;
10554 }
10556 Debug::print(Debug::Qhp,0,"{}",output);
10557 }
10558 }
10559 }
10560 }
10561
10562 if (Portable::system(qhgLocation, args, false))
10563 {
10564 err("failed to run qhelpgenerator on {}\n",Qhp::qhpFileName);
10565 }
10566 Dir::setCurrent(oldDir);
10567}
10568
10569//----------------------------------------------------------------------------
10570
10572{
10573 // check dot path
10574 DString dotPath = Config_getString(DOT_PATH);
10575 if (!dotPath.empty())
10576 {
10577 FileInfo fi(dotPath.str());
10578 if (!(fi.exists() && fi.isFile()) )// not an existing user specified path + exec
10579 {
10580 dotPath = dotPath+"/dot"+Portable::commandExtension();
10581 FileInfo dp(dotPath.str());
10582 if (!dp.exists() || !dp.isFile())
10583 {
10584 warn_uncond("the dot tool could not be found as '{}'\n",dotPath);
10585 dotPath = "dot";
10586 dotPath += Portable::commandExtension();
10587 }
10588 else if (dp.isRelative())
10589 {
10590 dotPath = fi.dirPath() + "/" + dotPath;
10591 }
10592 }
10593#if defined(_WIN32) // convert slashes
10594 size_t l=dotPath.length();
10595 for (size_t i=0;i<l;i++) if (dotPath.at(i)=='/') dotPath.at(i)='\\';
10596#endif
10597 }
10598 else
10599 {
10600 dotPath = "dot";
10601 dotPath += Portable::commandExtension();
10602 }
10603 Doxygen::verifiedDotPath = dotPath;
10605}
10606
10607//----------------------------------------------------------------------------
10608
10609/*! Generate a template version of the configuration file.
10610 * If the \a shortList parameter is true a configuration file without
10611 * comments will be generated.
10612 */
10613static void generateConfigFile(const DString &configFile,bool shortList,
10614 bool updateOnly=false)
10615{
10616 std::ofstream f;
10617 bool fileOpened=openOutputFile(configFile,f);
10618 bool writeToStdout=configFile=="-";
10619 if (fileOpened)
10620 {
10621 TextStream t(&f);
10622 Config::writeTemplate(t,shortList,updateOnly);
10623 if (!writeToStdout)
10624 {
10625 if (!updateOnly)
10626 {
10627 msg("\n\nConfiguration file '{}' created.\n\n",configFile);
10628 msg("Now edit the configuration file and enter\n\n");
10629 if (configFile!="Doxyfile" && configFile!="doxyfile")
10630 msg(" doxygen {}\n\n",configFile);
10631 else
10632 msg(" doxygen\n\n");
10633 msg("to generate the documentation for your project\n\n");
10634 }
10635 else
10636 {
10637 msg("\n\nConfiguration file '{}' updated.\n\n",configFile);
10638 }
10639 }
10640 }
10641 else
10642 {
10643 term("Cannot open file {} for writing\n",configFile);
10644 }
10645}
10646
10648{
10649 std::ofstream f;
10650 bool fileOpened=openOutputFile("-",f);
10651 if (fileOpened)
10652 {
10653 TextStream t(&f);
10654 Config::compareDoxyfile(t,diffList);
10655 }
10656 else
10657 {
10658 term("Cannot open stdout for writing\n");
10659 }
10660}
10661
10662//----------------------------------------------------------------------------
10663// read and parse a tag file
10664
10665static void readTagFile(const std::shared_ptr<Entry> &root,const DString &tagLine)
10666{
10667 DString fileName;
10668 DString destName;
10669 if (size_t eqPos = tagLine.find('='); eqPos!=DString::npos) // tag command contains a destination
10670 {
10671 fileName = tagLine.left(eqPos).stripWhiteSpace();
10672 destName = tagLine.mid(eqPos+1).stripWhiteSpace();
10673 if (fileName.empty() || destName.empty()) return;
10674 //printf("insert tagDestination %s->%s\n",qPrint(fi.fileName()),qPrint(destName));
10675 }
10676 else
10677 {
10678 fileName = tagLine;
10679 }
10680
10681 FileInfo fi(fileName.str());
10682 if (!fi.exists() || !fi.isFile())
10683 {
10684 err("Tag file '{}' does not exist or is not a file. Skipping it...\n",fileName);
10685 return;
10686 }
10687
10688 if (Doxygen::tagFileSet.find(fi.absFilePath()) != Doxygen::tagFileSet.end()) return;
10689
10690 Doxygen::tagFileSet.emplace(fi.absFilePath());
10691
10692 if (!destName.empty())
10693 {
10694 Doxygen::tagDestinationMap.emplace(fi.absFilePath(), destName.str());
10695 msg("Reading tag file '{}', location '{}'...\n",fileName,destName);
10696 }
10697 else
10698 {
10699 msg("Reading tag file '{}'...\n",fileName);
10700 }
10701
10702 parseTagFile(root,fi.absFilePath().c_str());
10703}
10704
10705//----------------------------------------------------------------------------
10707{
10708 StringVector latexExtraStyleSheet = Config_getList(LATEX_EXTRA_STYLESHEET);
10709 for (const auto &sheet : latexExtraStyleSheet)
10710 {
10711 std::string fileName = sheet;
10712 if (!fileName.empty())
10713 {
10714 FileInfo fi(fileName);
10715 if (!fi.exists())
10716 {
10717 err("Style sheet '{}' specified by LATEX_EXTRA_STYLESHEET does not exist!\n",fileName);
10718 }
10719 else if (fi.isDir())
10720 {
10721 err("Style sheet '{}' specified by LATEX_EXTRA_STYLESHEET is a directory, it has to be a file!\n", fileName);
10722 }
10723 else
10724 {
10725 DString destFileName = Config_getString(LATEX_OUTPUT)+"/"+fi.fileName();
10727 {
10728 destFileName += LATEX_STYLE_EXTENSION;
10729 }
10730 copyFile(fileName, destFileName);
10731 }
10732 }
10733 }
10734}
10735
10736//----------------------------------------------------------------------------
10737static void copyStyleSheet()
10738{
10739 DString htmlStyleSheet = Config_getString(HTML_STYLESHEET);
10740 if (!htmlStyleSheet.empty())
10741 {
10742 if (!htmlStyleSheet.startsWith("http:") && !htmlStyleSheet.startsWith("https:"))
10743 {
10744 FileInfo fi(htmlStyleSheet.str());
10745 if (!fi.exists())
10746 {
10747 err("Style sheet '{}' specified by HTML_STYLESHEET does not exist!\n",htmlStyleSheet);
10748 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
10749 }
10750 else if (fi.isDir())
10751 {
10752 err("Style sheet '{}' specified by HTML_STYLESHEET is a directory, it has to be a file!\n",htmlStyleSheet);
10753 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
10754 }
10755 else
10756 {
10757 DString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
10758 copyFile(htmlStyleSheet,destFileName);
10759 }
10760 }
10761 }
10762 StringVector htmlExtraStyleSheet = Config_getList(HTML_EXTRA_STYLESHEET);
10763 for (const auto &sheet : htmlExtraStyleSheet)
10764 {
10765 DString fileName(sheet);
10766 if (!fileName.empty() && !fileName.startsWith("http:") && !fileName.startsWith("https:"))
10767 {
10768 FileInfo fi(fileName.str());
10769 if (!fi.exists())
10770 {
10771 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET does not exist!\n",fileName);
10772 }
10773 else if (fi.fileName()=="doxygen.css" || fi.fileName()=="tabs.css" || fi.fileName()=="navtree.css")
10774 {
10775 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET is already a built-in stylesheet. Please use a different name\n",fi.fileName());
10776 }
10777 else if (fi.isDir())
10778 {
10779 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET is a directory, it has to be a file!\n",fileName);
10780 }
10781 else
10782 {
10783 DString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
10784 copyFile(fileName, destFileName);
10785 }
10786 }
10787 }
10788}
10789
10790static void copyLogo(const DString &outputOption, bool toIndex)
10791{
10792 DString projectLogo = projectLogoFile();
10793 if (!projectLogo.empty())
10794 {
10795 FileInfo fi(projectLogo.str());
10796 if (!fi.exists())
10797 {
10798 err("Project logo '{}' specified by PROJECT_LOGO does not exist!\n",projectLogo);
10799 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
10800 }
10801 else if (fi.isDir())
10802 {
10803 err("Project logo '{}' specified by PROJECT_LOGO is a directory, it has to be a file!\n",projectLogo);
10804 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
10805 }
10806 else
10807 {
10808 DString destFileName = outputOption+"/"+fi.fileName();
10809 copyFile(projectLogo,destFileName);
10810 if (toIndex) Doxygen::indexList->addImageFile(fi.fileName());
10811 }
10812 }
10813}
10814
10815static void copyIcon(const DString &outputOption, bool toIndex)
10816{
10817 DString projectIcon = Config_getString(PROJECT_ICON);
10818 if (!projectIcon.empty())
10819 {
10820 FileInfo fi(projectIcon.str());
10821 if (!fi.exists())
10822 {
10823 err("Project icon '{}' specified by PROJECT_ICON does not exist!\n",projectIcon);
10824 projectIcon = Config_updateString(PROJECT_ICON,""); // revert to the default
10825 }
10826 else if (fi.isDir())
10827 {
10828 err("Project icon '{}' specified by PROJECT_ICON is a directory, it has to be a file!\n",projectIcon);
10829 projectIcon = Config_updateString(PROJECT_ICON,""); // revert to the default
10830 }
10831 else
10832 {
10833 DString destFileName = outputOption+"/"+fi.fileName();
10834 copyFile(projectIcon,destFileName);
10835 if (toIndex) Doxygen::indexList->addImageFile(fi.fileName());
10836 }
10837 }
10838}
10839
10840static inline void copyExtraFiles(StringVector files,const DString &filesOption,const DString &outputOption, bool toIndex)
10841{
10842 for (const auto &fileName : files)
10843 {
10844 if (!fileName.empty())
10845 {
10846 FileInfo fi(fileName);
10847 if (!fi.exists())
10848 {
10849 err("Extra file '{}' specified in {} does not exist!\n", fileName,filesOption);
10850 }
10851 else if (fi.isDir())
10852 {
10853 err("Extra file '{}' specified in {} is a directory, it has to be a file!\n", fileName,filesOption);
10854 }
10855 else
10856 {
10857 DString destFileName = outputOption+"/"+fi.fileName();
10858 copyFile(fileName, destFileName);
10859 if (toIndex) Doxygen::indexList->addImageFile(fi.fileName());
10860 }
10861 }
10862 }
10863}
10864
10865//----------------------------------------------------------------------------
10866
10868{
10869 for (const auto &fn : *Doxygen::inputNameLinkedMap)
10870 {
10871 struct FileEntry
10872 {
10873 FileEntry(const DString &p,FileDef *fd) : path(p), fileDef(fd) {}
10874 DString path;
10875 FileDef *fileDef;
10876 };
10877
10878 // collect the entry for which to compute the longest common prefix (LCP) of the path
10879 std::vector<FileEntry> fileEntries;
10880 for (const auto &fd : *fn)
10881 {
10882 if (!fd->isReference()) // skip external references
10883 {
10884 fileEntries.emplace_back(fd->getPath(),fd.get());
10885 }
10886 }
10887
10888 size_t size = fileEntries.size();
10889
10890 if (size==1) // name if unique, so diskname is simply the name
10891 {
10892 FileDef *fd = fileEntries[0].fileDef;
10893 fd->setDiskName(fn->fileName());
10894 }
10895 else if (size>1) // multiple occurrences of the same file name
10896 {
10897 // sort the array
10898 std::stable_sort(fileEntries.begin(),
10899 fileEntries.end(),
10900 [](const FileEntry &fe1,const FileEntry &fe2)
10901 { return dstricmp_sort(fe1.path,fe2.path)<0; }
10902 );
10903
10904 // since the entries are sorted, the common prefix of the whole array is same
10905 // as the common prefix between the first and last entry
10906 const FileEntry &first = fileEntries[0];
10907 const FileEntry &last = fileEntries[size-1];
10908 int first_path_size = static_cast<int>(first.path.size())-1; // -1 to skip trailing slash
10909 int last_path_size = static_cast<int>(last.path.size())-1; // -1 to skip trailing slash
10910 int j=0;
10911 int i=0;
10912 for (i=0;i<first_path_size && i<last_path_size;i++)
10913 {
10914 if (first.path[i]=='/') j=i;
10915 if (first.path[i]!=last.path[i]) break;
10916 }
10917 if (i==first_path_size && i<last_path_size && last.path[i]=='/')
10918 {
10919 // case first='some/path' and last='some/path/more' => match is 'some/path'
10920 j=first_path_size;
10921 }
10922 else if (i==last_path_size && i<first_path_size && first.path[i]=='/')
10923 {
10924 // case first='some/path/more' and last='some/path' => match is 'some/path'
10925 j=last_path_size;
10926 }
10927
10928 // add non-common part of the path to the name
10929 for (auto &fileEntry : fileEntries)
10930 {
10931 DString prefix = fileEntry.path.right(fileEntry.path.length()-j-1);
10932 fileEntry.fileDef->setName(prefix+fn->fileName());
10933 //printf("!!!!!!!! non unique disk name=%s:%s\n",qPrint(prefix),fn->fileName());
10934 fileEntry.fileDef->setDiskName(prefix+fn->fileName());
10935 }
10936 }
10937 }
10938}
10939
10940
10941
10942//----------------------------------------------------------------------------
10943
10944static std::unique_ptr<OutlineParserInterface> getParserForFile(const DString &fn)
10945{
10946 DString fileName=fn;
10947 DString extension;
10948 size_t sep = fileName.rfind('/');
10949 size_t ei = fileName.rfind('.');
10950 if (ei!=DString::npos && (sep==DString::npos || ei>sep)) // matches dir/file.ext but not dir.1/file
10951 {
10952 extension=fileName.mid(ei);
10953 }
10954 else
10955 {
10956 extension = ".no_extension";
10957 }
10958
10959 return Doxygen::parserManager->getOutlineParser(extension);
10960}
10961
10962static std::shared_ptr<Entry> parseFile(OutlineParserInterface &parser,
10963 FileDef *fd,const DString &fn,
10964 ClangTUParser *clangParser,bool newTU)
10965{
10966 DString fileName=fn;
10967 AUTO_TRACE("fileName={}",fileName);
10968 DString extension;
10969 if (size_t ei = fileName.rfind('.'); ei!=DString::npos)
10970 {
10971 extension=fileName.mid(ei);
10972 }
10973 else
10974 {
10975 extension = ".no_extension";
10976 }
10977
10978 FileInfo fi(fileName.str());
10979 std::string preBuf;
10980
10981 if (Config_getBool(ENABLE_PREPROCESSING) &&
10982 parser.needsPreprocessing(extension))
10983 {
10984 Preprocessor preprocessor;
10985 StringVector includePath = Config_getList(INCLUDE_PATH);
10986 for (const auto &s : includePath)
10987 {
10988 std::string absPath = FileInfo(s).absFilePath();
10989 preprocessor.addSearchDir(absPath);
10990 }
10991 std::string inBuf;
10992 msg("Preprocessing {}...\n",fn);
10993 readInputFile(fileName,inBuf);
10994 addTerminalCharIfMissing(inBuf,'\n');
10995 preprocessor.processFile(fileName,inBuf,preBuf);
10996 }
10997 else // no preprocessing
10998 {
10999 msg("Reading {}...\n",fn);
11000 readInputFile(fileName,preBuf);
11001 addTerminalCharIfMissing(preBuf,'\n');
11002 }
11003
11004 std::string convBuf;
11005 convBuf.reserve(preBuf.size()+1024);
11006
11007 // convert multi-line C++ comments to C style comments
11008 convertCppComments(preBuf,convBuf,fileName.str());
11009
11010 std::shared_ptr<Entry> fileRoot = std::make_shared<Entry>();
11011 // use language parse to parse the file
11012 if (clangParser)
11013 {
11014 if (newTU) clangParser->parse();
11015 clangParser->switchToFile(fd);
11016 }
11017 parser.parseInput(fileName,convBuf.data(),fileRoot,clangParser);
11018 fileRoot->setFileDef(fd);
11019 return fileRoot;
11020}
11021
11022//! parse the list of input files
11023static void parseFilesMultiThreading(const std::shared_ptr<Entry> &root)
11024{
11025 AUTO_TRACE();
11026#if USE_LIBCLANG
11028 {
11029 StringUnorderedSet processedFiles;
11030
11031 // create a dictionary with files to process
11032 StringUnorderedSet filesToProcess;
11033 for (const auto &s : g_inputFiles)
11034 {
11035 filesToProcess.insert(s);
11036 }
11037
11038 std::mutex processedFilesLock;
11039 // process source files (and their include dependencies)
11040 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
11041 msg("Processing input using {} threads.\n",numThreads);
11042 ThreadPool threadPool(numThreads);
11043 using FutureType = std::vector< std::shared_ptr<Entry> >;
11044 std::vector< std::future< FutureType > > results;
11045 for (const auto &s : g_inputFiles)
11046 {
11047 bool ambig = false;
11048 DString qs = s;
11050 ASSERT(fd!=nullptr);
11051 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt::Cpp) // this is a source file
11052 {
11053 // lambda representing the work to executed by a thread
11054 auto processFile = [qs,&filesToProcess,&processedFilesLock,&processedFiles]() {
11055 bool ambig_l = false;
11056 std::vector< std::shared_ptr<Entry> > roots;
11057 FileDef *fd_l = Doxygen::inputNameLinkedMap->findFileDef(qs,ambig_l);
11058 auto clangParser = ClangParser::instance()->createTUParser(fd_l);
11059 auto parser = getParserForFile(qs);
11060 auto fileRoot { parseFile(*parser.get(),fd_l,qs,clangParser.get(),true) };
11061 roots.push_back(fileRoot);
11062
11063 // Now process any include files in the same translation unit
11064 // first. When libclang is used this is much more efficient.
11065 for (auto incFile : clangParser->filesInSameTU())
11066 {
11067 DString qincFile = incFile;
11068 if (filesToProcess.find(incFile)!=filesToProcess.end())
11069 {
11070 bool needsToBeProcessed = false;
11071 {
11072 std::lock_guard<std::mutex> lock(processedFilesLock);
11073 needsToBeProcessed = processedFiles.find(incFile)==processedFiles.end();
11074 if (needsToBeProcessed) processedFiles.insert(incFile);
11075 }
11076 if (qincFile!=qs && needsToBeProcessed)
11077 {
11078 FileDef *ifd=Doxygen::inputNameLinkedMap->findFileDef(qincFile,ambig_l);
11079 if (ifd && !ifd->isReference())
11080 {
11081 //printf(" Processing %s in same translation unit as %s\n",incFile,qPrint(s));
11082 fileRoot = parseFile(*parser.get(),ifd,qincFile,clangParser.get(),false);
11083 roots.push_back(fileRoot);
11084 }
11085 }
11086 }
11087 }
11088 return roots;
11089 };
11090 // dispatch the work and collect the future results
11091 results.emplace_back(threadPool.queue(processFile));
11092 }
11093 }
11094 // synchronize with the Entry result lists produced and add them to the root
11095 for (auto &f : results)
11096 {
11097 auto l = f.get();
11098 for (auto &e : l)
11099 {
11100 root->moveToSubEntryAndKeep(e);
11101 }
11102 }
11103 // process remaining files
11104 results.clear();
11105 for (const auto &s : g_inputFiles)
11106 {
11107 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
11108 {
11109 // lambda representing the work to executed by a thread
11110 auto processFile = [s]() {
11111 bool ambig = false;
11112 DString qs = s;
11113 std::vector< std::shared_ptr<Entry> > roots;
11115 auto parser { getParserForFile(qs) };
11116 bool useClang = getLanguageFromFileName(qs)==SrcLangExt::Cpp;
11117 if (useClang)
11118 {
11119 auto clangParser = ClangParser::instance()->createTUParser(fd);
11120 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
11121 roots.push_back(fileRoot);
11122 }
11123 else
11124 {
11125 auto fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
11126 roots.push_back(fileRoot);
11127 }
11128 return roots;
11129 };
11130 results.emplace_back(threadPool.queue(processFile));
11131 }
11132 }
11133 // synchronize with the Entry result lists produced and add them to the root
11134 for (auto &f : results)
11135 {
11136 auto l = f.get();
11137 for (auto &e : l)
11138 {
11139 root->moveToSubEntryAndKeep(e);
11140 }
11141 }
11142 }
11143 else // normal processing
11144#endif
11145 {
11146 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
11147 msg("Processing input using {} threads.\n",numThreads);
11148 ThreadPool threadPool(numThreads);
11149 using FutureType = std::shared_ptr<Entry>;
11150 std::vector< std::future< FutureType > > results;
11151 for (const auto &s : g_inputFiles)
11152 {
11153 // lambda representing the work to executed by a thread
11154 auto processFile = [s]() {
11155 bool ambig = false;
11156 DString qs = s;
11158 auto parser = getParserForFile(qs);
11159 auto fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
11160 return fileRoot;
11161 };
11162 // dispatch the work and collect the future results
11163 results.emplace_back(threadPool.queue(processFile));
11164 }
11165 // synchronize with the Entry results produced and add them to the root
11166 for (auto &f : results)
11167 {
11168 root->moveToSubEntryAndKeep(f.get());
11169 }
11170 }
11171}
11172
11173//! parse the list of input files
11174static void parseFilesSingleThreading(const std::shared_ptr<Entry> &root)
11175{
11176 AUTO_TRACE();
11177#if USE_LIBCLANG
11179 {
11180 StringUnorderedSet processedFiles;
11181
11182 // create a dictionary with files to process
11183 StringUnorderedSet filesToProcess;
11184 for (const auto &s : g_inputFiles)
11185 {
11186 filesToProcess.insert(s);
11187 }
11188
11189 // process source files (and their include dependencies)
11190 for (const auto &s : g_inputFiles)
11191 {
11192 bool ambig = false;
11193 DString qs =s;
11195 ASSERT(fd!=nullptr);
11196 if (fd->isSource() && !fd->isReference() && getLanguageFromFileName(qs)==SrcLangExt::Cpp) // this is a source file
11197 {
11198 auto clangParser = ClangParser::instance()->createTUParser(fd);
11199 auto parser { getParserForFile(qs) };
11200 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
11201 root->moveToSubEntryAndKeep(fileRoot);
11202 processedFiles.insert(s);
11203
11204 // Now process any include files in the same translation unit
11205 // first. When libclang is used this is much more efficient.
11206 for (auto incFile : clangParser->filesInSameTU())
11207 {
11208 //printf(" file %s\n",qPrint(incFile));
11209 if (filesToProcess.find(incFile)!=filesToProcess.end() && // file need to be processed
11210 processedFiles.find(incFile)==processedFiles.end()) // and is not processed already
11211 {
11212 FileDef *ifd=Doxygen::inputNameLinkedMap->findFileDef(incFile,ambig);
11213 if (ifd && !ifd->isReference())
11214 {
11215 //printf(" Processing %s in same translation unit as %s\n",qPrint(incFile),qPrint(qs));
11216 fileRoot = parseFile(*parser.get(),ifd,incFile,clangParser.get(),false);
11217 root->moveToSubEntryAndKeep(fileRoot);
11218 processedFiles.insert(incFile);
11219 }
11220 }
11221 }
11222 }
11223 }
11224 // process remaining files
11225 for (const auto &s : g_inputFiles)
11226 {
11227 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
11228 {
11229 bool ambig = false;
11230 DString qs = s;
11232 if (getLanguageFromFileName(qs)==SrcLangExt::Cpp) // not yet processed
11233 {
11234 auto clangParser = ClangParser::instance()->createTUParser(fd);
11235 auto parser { getParserForFile(qs) };
11236 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
11237 root->moveToSubEntryAndKeep(fileRoot);
11238 }
11239 else
11240 {
11241 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(qs) };
11242 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
11243 root->moveToSubEntryAndKeep(fileRoot);
11244 }
11245 processedFiles.insert(s);
11246 }
11247 }
11248 }
11249 else // normal processing
11250#endif
11251 {
11252 for (const auto &s : g_inputFiles)
11253 {
11254 bool ambig = false;
11255 DString qs = s;
11257 ASSERT(fd!=nullptr);
11258 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(qs) };
11259 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
11260 root->moveToSubEntryAndKeep(std::move(fileRoot));
11261 }
11262 }
11263}
11264
11265// resolves a path that may include symlinks, if a recursive symlink is
11266// found an empty string is returned.
11267static std::string resolveSymlink(const std::string &path)
11268{
11269 size_t sepPos=0;
11270 size_t oldPos=0;
11271 StringUnorderedSet nonSymlinks;
11272 StringUnorderedSet known;
11273 DString result(path);
11274 DString oldPrefix = "/";
11275 do
11276 {
11277#if defined(_WIN32)
11278 // UNC path, skip server and share name
11279 if (sepPos==0 && (result.startsWith("//") || result.startsWith("\\\\")))
11280 sepPos = result.find('/',2);
11281 if (sepPos!=DString::npos)
11282 sepPos = result.find('/',sepPos+1);
11283#else
11284 sepPos = result.find('/',sepPos+1);
11285#endif
11286 DString prefix = sepPos==DString::npos ? result : result.left(sepPos);
11287 if (nonSymlinks.find(prefix.str())==nonSymlinks.end())
11288 {
11289 FileInfo fi(prefix.str());
11290 if (fi.isSymLink())
11291 {
11292 DString target = fi.readLink();
11293 bool isRelative = FileInfo(target.str()).isRelative();
11294 if (isRelative)
11295 {
11296 target = Dir::cleanDirPath(oldPrefix.str()+"/"+target.str());
11297 }
11298 if (sepPos!=DString::npos)
11299 {
11300 if (fi.isDir() && !target.empty() && target.at(target.length()-1)!='/')
11301 {
11302 target+='/';
11303 }
11304 target+=result.mid(sepPos);
11305 }
11306 result = Dir::cleanDirPath(target.str());
11307 if (known.find(result.str())!=known.end()) return std::string(); // recursive symlink!
11308 known.insert(result.str());
11309 if (isRelative)
11310 {
11311 sepPos = oldPos;
11312 }
11313 else // link to absolute path
11314 {
11315 sepPos = 0;
11316 oldPrefix = "/";
11317 }
11318 }
11319 else
11320 {
11321 nonSymlinks.insert(prefix.str());
11322 oldPrefix = prefix;
11323 }
11324 oldPos = sepPos;
11325 }
11326 }
11327 while (sepPos!=DString::npos);
11328 return Dir::cleanDirPath(result.str());
11329}
11330
11332
11333//----------------------------------------------------------------------------
11334// Read all files matching at least one pattern in 'patList' in the
11335// directory represented by 'fi'.
11336// The directory is read iff the recursiveFlag is set.
11337// The contents of all files is append to the input string
11338
11339static void readDir(FileInfo *fi,
11340 FileNameLinkedMap *fnMap,
11341 StringUnorderedSet *exclSet,
11342 const StringVector *patList,
11343 const StringVector *exclPatList,
11344 StringVector *resultList,
11345 StringUnorderedSet *resultSet,
11346 bool errorIfNotExist,
11347 bool recursive,
11348 StringUnorderedSet *killSet,
11349 StringUnorderedSet *paths
11350 )
11351{
11352 std::string dirName = fi->absFilePath();
11353 if (paths && !dirName.empty())
11354 {
11355 paths->insert(dirName);
11356 }
11357 //printf("%s isSymLink()=%d\n",qPrint(dirName),fi->isSymLink());
11358 if (fi->isSymLink())
11359 {
11360 dirName = resolveSymlink(dirName);
11361 if (dirName.empty())
11362 {
11363 //printf("RECURSIVE SYMLINK: %s\n",qPrint(dirName));
11364 return; // recursive symlink
11365 }
11366 }
11367
11368 if (g_pathsVisited.find(dirName)!=g_pathsVisited.end())
11369 {
11370 //printf("PATH ALREADY VISITED: %s\n",qPrint(dirName));
11371 return; // already visited path
11372 }
11373 g_pathsVisited.insert(dirName);
11374
11375 Dir dir(dirName);
11376 msg("Searching for files in directory {}\n", fi->absFilePath());
11377 //printf("killSet=%p count=%d\n",killSet,killSet ? (int)killSet->count() : -1);
11378
11379 StringVector dirResultList;
11380
11381 bool caseSenseNames = useCaseSenseNames();
11382
11383 // Process the entries in a fixed order, so that the order in which FileDefs
11384 // are created (which e.g. determines the SHORT_NAMES numbering) and in which
11385 // subdirectories are visited does not depend on the file system.
11386 StringVector entries;
11387 for (const auto &dirEntry : dir.iterator())
11388 {
11389 entries.push_back(dirEntry.path());
11390 }
11391 std::sort(entries.begin(),entries.end());
11392
11393 for (const auto &entryPath : entries)
11394 {
11395 FileInfo cfi(entryPath);
11396 auto checkPatterns = [&]() -> bool
11397 {
11398 return (patList==nullptr || cfi.match(*patList,caseSenseNames)) &&
11399 (exclPatList==nullptr || !cfi.match(*exclPatList,caseSenseNames)) &&
11400 (killSet==nullptr || killSet->find(cfi.absFilePath())==killSet->end());
11401 };
11402
11403 if (exclSet==nullptr || exclSet->find(cfi.absFilePath())==exclSet->end())
11404 { // file should not be excluded
11405 //printf("killSet->find(%s)\n",qPrint(cfi->absFilePath()));
11406 if (Config_getBool(EXCLUDE_SYMLINKS) && cfi.isSymLink())
11407 {
11408 }
11409 else if (!cfi.exists() || !cfi.isReadable())
11410 {
11411 if (errorIfNotExist && checkPatterns())
11412 {
11413 warn_uncond("source '{}' is not a readable file or directory... skipping.\n",cfi.absFilePath());
11414 }
11415 }
11416 else if (cfi.isFile() && checkPatterns())
11417 {
11418 std::string name=cfi.fileName();
11419 std::string path=cfi.dirPath()+"/";
11420 std::string fullName=path+name;
11421 if (fnMap)
11422 {
11423 auto fd = createFileDef(path,name);
11424 FileName *fn=nullptr;
11425 if (!name.empty())
11426 {
11427 fn = fnMap->add(name);
11428 fn->push_back(std::move(fd));
11429 }
11430 }
11431 dirResultList.push_back(fullName);
11432 if (resultSet) resultSet->insert(fullName);
11433 if (killSet) killSet->insert(fullName);
11434 }
11435 else if (recursive &&
11436 cfi.isDir() &&
11437 (exclPatList==nullptr || !cfi.match(*exclPatList,caseSenseNames)) &&
11438 cfi.fileName().at(0)!='.') // skip "." ".." and ".dir"
11439 {
11440 FileInfo acfi(cfi.absFilePath());
11441 readDir(&acfi,fnMap,exclSet,
11442 patList,exclPatList,&dirResultList,resultSet,errorIfNotExist,
11443 recursive,killSet,paths);
11444 }
11445 }
11446 }
11447 if (resultList && !dirResultList.empty())
11448 {
11449 // sort the resulting list to make the order platform independent.
11450 std::stable_sort(dirResultList.begin(),
11451 dirResultList.end(),
11452 [](const auto &f1,const auto &f2) { return dstricmp_sort(f1.c_str(),f2.c_str())<0; });
11453
11454 // append the sorted results to resultList
11455 resultList->insert(resultList->end(), dirResultList.begin(), dirResultList.end());
11456 }
11457}
11458
11459
11460//----------------------------------------------------------------------------
11461// read a file or all files in a directory and append their contents to the
11462// input string. The names of the files are appended to the 'fiList' list.
11463
11465 FileNameLinkedMap *fnMap,
11466 StringUnorderedSet *exclSet,
11467 const StringVector *patList,
11468 const StringVector *exclPatList,
11469 StringVector *resultList,
11470 StringUnorderedSet *resultSet,
11471 bool recursive,
11472 bool errorIfNotExist,
11473 StringUnorderedSet *killSet,
11474 StringUnorderedSet *paths
11475 )
11476{
11477 //printf("killSet count=%d\n",killSet ? (int)killSet->size() : -1);
11478 // strip trailing slashes
11479 if (s.empty()) return;
11480
11481 g_pathsVisited.clear();
11482
11483 FileInfo fi(s.str());
11484 //printf("readFileOrDirectory(%s)\n",s);
11485 {
11486 if (exclSet==nullptr || exclSet->find(fi.absFilePath())==exclSet->end())
11487 {
11488 if (Config_getBool(EXCLUDE_SYMLINKS) && fi.isSymLink())
11489 {
11490 }
11491 else if (!fi.exists() || !fi.isReadable())
11492 {
11493 if (errorIfNotExist)
11494 {
11495 warn_uncond("source '{}' is not a readable file or directory... skipping.\n",s);
11496 }
11497 }
11498 else if (fi.isFile())
11499 {
11500 std::string dirPath = fi.dirPath(true);
11501 std::string filePath = fi.absFilePath();
11502 if (paths && !dirPath.empty())
11503 {
11504 paths->insert(dirPath);
11505 }
11506 //printf("killSet.find(%s)=%d\n",qPrint(fi.absFilePath()),killSet.find(fi.absFilePath())!=killSet.end());
11507 if (killSet==nullptr || killSet->find(filePath)==killSet->end())
11508 {
11509 std::string name=fi.fileName();
11510 if (fnMap)
11511 {
11512 auto fd = createFileDef(dirPath+"/",name);
11513 if (!name.empty())
11514 {
11515 FileName *fn = fnMap->add(name);
11516 fn->push_back(std::move(fd));
11517 }
11518 }
11519 if (resultList || resultSet)
11520 {
11521 if (resultList) resultList->push_back(filePath);
11522 if (resultSet) resultSet->insert(filePath);
11523 }
11524
11525 if (killSet) killSet->insert(fi.absFilePath());
11526 }
11527 }
11528 else if (fi.isDir()) // readable dir
11529 {
11530 readDir(&fi,fnMap,exclSet,patList,
11531 exclPatList,resultList,resultSet,errorIfNotExist,
11532 recursive,killSet,paths);
11533 }
11534 }
11535 }
11536}
11537
11538//----------------------------------------------------------------------------
11539
11541{
11542 DString anchor;
11544 {
11545 MemberDef *md = toMemberDef(d);
11546 anchor=":"+md->anchor();
11547 }
11548 DString scope;
11549 DString fn = d->getOutputFileBase();
11552 {
11553 scope = fn;
11554 }
11555 t << "REPLACE INTO symbols (symbol_id,scope_id,name,file,line) VALUES('"
11556 << fn+anchor << "','"
11557 << scope << "','"
11558 << d->name() << "','"
11559 << d->getDefFileName() << "','"
11560 << d->getDefLine()
11561 << "');\n";
11562}
11563
11564static void dumpSymbolMap()
11565{
11566 std::ofstream f = Portable::openOutputStream("symbols.sql");
11567 if (f.is_open())
11568 {
11569 TextStream t(&f);
11570 for (const auto &[name,symList] : *Doxygen::symbolMap)
11571 {
11572 for (const auto &def : symList)
11573 {
11574 dumpSymbol(t,def);
11575 }
11576 }
11577 }
11578}
11579
11580// print developer options of Doxygen
11581static void devUsage()
11582{
11584 msg("Developer parameters:\n");
11585 msg(" -m dump symbol map\n");
11586 msg(" -b making messages output unbuffered\n");
11587 msg(" -c <file> process input file as a comment block and produce HTML output\n");
11588#if ENABLE_TRACING
11589 msg(" -t [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout)\n");
11590 msg(" -t_time [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout),\n"
11591 " and include time and thread information\n");
11592#endif
11593 msg(" -d <level> enable a debug level, such as (multiple invocations of -d are possible):\n");
11595}
11596
11597
11598//----------------------------------------------------------------------------
11599// print the version of Doxygen
11600
11601static void version(const bool extended)
11602{
11604 DString versionString = getFullVersion();
11605 msg("{}\n",versionString);
11606 if (extended)
11607 {
11608 DString extVers;
11609 if (!extVers.empty()) extVers+= ", ";
11610 extVers += "sqlite3 ";
11611 extVers += sqlite3_libversion();
11612#if USE_LIBCLANG
11613 if (!extVers.empty()) extVers+= ", ";
11614 extVers += "clang support ";
11615 extVers += CLANG_VERSION_STRING;
11616#endif
11617 if (!extVers.empty())
11618 {
11619 if (size_t lastComma = extVers.rfind(','); lastComma != DString::npos)
11620 {
11621 extVers = extVers.replace(lastComma,1," and");
11622 }
11623 msg(" with {}.\n",extVers);
11624 }
11625 }
11626}
11627
11628//----------------------------------------------------------------------------
11629// print the usage of Doxygen
11630
11631static void usage(const DString &name,const DString &versionString)
11632{
11634 msg("Doxygen version {0}\nCopyright Dimitri van Heesch 1997-2025\n\n"
11635 "You can use Doxygen in a number of ways:\n\n"
11636 "1) Use Doxygen to generate a template configuration file*:\n"
11637 " {1} [-s] -g [configName]\n\n"
11638 "2) Use Doxygen to update an old configuration file*:\n"
11639 " {1} [-s] -u [configName]\n\n"
11640 "3) Use Doxygen to generate documentation using an existing "
11641 "configuration file*:\n"
11642 " {1} [configName]\n\n"
11643 "4) Use Doxygen to generate a template file controlling the layout of the\n"
11644 " generated documentation:\n"
11645 " {1} -l [layoutFileName]\n\n"
11646 " In case layoutFileName is omitted DoxygenLayout.xml will be used as filename.\n"
11647 " If - is used for layoutFileName Doxygen will write to standard output.\n\n"
11648 "5) Use Doxygen to generate a template style sheet file for RTF, HTML or Latex.\n"
11649 " RTF: {1} -w rtf styleSheetFile\n"
11650 " HTML: {1} -w html headerFile footerFile styleSheetFile [configFile]\n"
11651 " LaTeX: {1} -w latex headerFile footerFile styleSheetFile [configFile]\n\n"
11652 "6) Use Doxygen to generate a rtf extensions file\n"
11653 " {1} -e rtf extensionsFile\n\n"
11654 " If - is used for extensionsFile Doxygen will write to standard output.\n\n"
11655 "7) Use Doxygen to compare the used configuration file with the template configuration file\n"
11656 " {1} -x [configFile]\n\n"
11657 " Use Doxygen to compare the used configuration file with the template configuration file\n"
11658 " without replacing the environment variables or CMake type replacement variables\n"
11659 " {1} -x_noenv [configFile]\n\n"
11660 "8) Use Doxygen to show a list of built-in emojis.\n"
11661 " {1} -f emoji outputFileName\n\n"
11662 " If - is used for outputFileName Doxygen will write to standard output.\n\n"
11663 "*) If -s is specified the comments of the configuration items in the config file will be omitted.\n"
11664 " If configName is omitted 'Doxyfile' will be used as a default.\n"
11665 " If - is used for configFile Doxygen will write / read the configuration to /from standard output / input.\n\n"
11666 "If -q is used for a Doxygen documentation run, Doxygen will see this as if QUIET=YES has been set.\n\n"
11667 "-v print version string, -V print extended version information\n"
11668 "-h,-? prints usage help information\n"
11669 "{1} -d prints additional usage flags for debugging purposes\n",versionString,name);
11670}
11671
11672//----------------------------------------------------------------------------
11673// read the argument of option 'c' from the comment argument list and
11674// update the option index 'optInd'.
11675
11676static const char *getArg(int argc,char **argv,int &optInd)
11677{
11678 char *s=nullptr;
11679 if (dstrlen(&argv[optInd][2])>0)
11680 s=&argv[optInd][2];
11681 else if (optInd+1<argc && argv[optInd+1][0]!='-')
11682 s=argv[++optInd];
11683 return s;
11684}
11685
11686//----------------------------------------------------------------------------
11687
11688/** @brief /dev/null outline parser */
11690{
11691 public:
11692 void parseInput(const DString &/* file */, const char * /* buf */,const std::shared_ptr<Entry> &, ClangTUParser*) override {}
11693 bool needsPreprocessing(const DString &) const override { return false; }
11694 void parsePrototype(const DString &) override {}
11695};
11696
11697
11698template<class T> std::function< std::unique_ptr<T>() > make_parser_factory()
11699{
11700 return []() { return std::make_unique<T>(); };
11701}
11702
11704{
11705 initResources();
11706 DString lang = Portable::getenv("LC_ALL");
11707 if (!lang.empty()) Portable::setenv("LANG",lang);
11708 std::setlocale(LC_ALL,"");
11709 std::setlocale(LC_CTYPE,"C"); // to get isspace(0xA0)==0, needed for UTF-8
11710 std::setlocale(LC_NUMERIC,"C");
11711
11713
11737
11738 // register any additional parsers here...
11739
11741
11742#if USE_LIBCLANG
11744#endif
11753 Doxygen::pageLinkedMap = new PageLinkedMap; // all doc pages
11754 Doxygen::exampleLinkedMap = new PageLinkedMap; // all examples
11755 //Doxygen::tagDestinationDict.setAutoDelete(true);
11757
11758 // initialization of these globals depends on
11759 // configuration switches so we need to postpone these
11760 Doxygen::globalScope = nullptr;
11770
11771}
11772
11805
11806void readConfiguration(int argc, char **argv)
11807{
11808 DString versionString = getFullVersion();
11809
11810 // helper that calls \a func to write to file \a fileName via a TextStream
11811 auto writeFile = [](const char *fileName,std::function<void(TextStream&)> func) -> bool
11812 {
11813 std::ofstream f;
11814 if (openOutputFile(fileName,f))
11815 {
11816 TextStream t(&f);
11817 func(t);
11818 return true;
11819 }
11820 return false;
11821 };
11822
11823
11824 /**************************************************************************
11825 * Handle arguments *
11826 **************************************************************************/
11827
11828 int optInd=1;
11829 DString configName;
11830 DString traceName;
11831 bool genConfig=false;
11832 bool shortList=false;
11833 bool traceTiming=false;
11835 bool updateConfig=false;
11836 bool quiet = false;
11837 while (optInd<argc && argv[optInd][0]=='-' &&
11838 (isalpha(argv[optInd][1]) || argv[optInd][1]=='?' ||
11839 argv[optInd][1]=='-')
11840 )
11841 {
11842 switch(argv[optInd][1])
11843 {
11844 case 'g':
11845 {
11846 genConfig=true;
11847 }
11848 break;
11849 case 'l':
11850 {
11851 DString layoutName;
11852 if (optInd+1>=argc)
11853 {
11854 layoutName="DoxygenLayout.xml";
11855 }
11856 else
11857 {
11858 layoutName=argv[optInd+1];
11859 }
11860 writeDefaultLayoutFile(layoutName);
11862 exit(0);
11863 }
11864 break;
11865 case 'c':
11866 if (optInd+1>=argc) // no file name given
11867 {
11868 msg("option \"-c\" is missing the file name to read\n");
11869 devUsage();
11871 exit(1);
11872 }
11873 else
11874 {
11875 g_commentFileName=argv[optInd+1];
11876 optInd++;
11877 }
11878 g_singleComment=true;
11879 quiet=true;
11880 break;
11881 case 'd':
11882 {
11883 DString debugLabel=getArg(argc,argv,optInd);
11884 if (debugLabel.empty())
11885 {
11886 devUsage();
11888 exit(0);
11889 }
11890 int retVal = Debug::setFlagStr(debugLabel);
11891 if (!retVal)
11892 {
11893 msg("option \"-d\" has unknown debug specifier: \"{}\".\n",debugLabel);
11894 devUsage();
11896 exit(1);
11897 }
11898 }
11899 break;
11900 case 't':
11901 {
11902#if ENABLE_TRACING
11903 if (!strcmp(argv[optInd]+1,"t_time"))
11904 {
11905 traceTiming = true;
11906 }
11907 else if (!strcmp(argv[optInd]+1,"t"))
11908 {
11909 traceTiming = false;
11910 }
11911 else
11912 {
11913 err("option should be \"-t\" or \"-t_time\", found: \"{}\".\n",argv[optInd]);
11915 exit(1);
11916 }
11917 if (optInd+1>=argc || argv[optInd+1][0] == '-') // no file name given
11918 {
11919 traceName="stdout";
11920 }
11921 else
11922 {
11923 traceName=argv[optInd+1];
11924 optInd++;
11925 }
11926#else
11927 err("support for option \"-t\" has not been compiled in (use a debug build or a release build with tracing enabled).\n");
11929 exit(1);
11930#endif
11931 }
11932 break;
11933 case 'x':
11934 if (!strcmp(argv[optInd]+1,"x_noenv")) diffList=Config::CompareMode::CompressedNoEnv;
11935 else if (!strcmp(argv[optInd]+1,"x")) diffList=Config::CompareMode::Compressed;
11936 else
11937 {
11938 err("option should be \"-x\" or \"-x_noenv\", found: \"{}\".\n",argv[optInd]);
11940 exit(1);
11941 }
11942 break;
11943 case 's':
11944 shortList=true;
11945 break;
11946 case 'u':
11947 updateConfig=true;
11948 break;
11949 case 'e':
11950 {
11951 DString formatName=getArg(argc,argv,optInd);
11952 if (formatName.empty())
11953 {
11954 err("option \"-e\" is missing format specifier rtf.\n");
11956 exit(1);
11957 }
11958 if (dstricmp(formatName.data(),"rtf")==0)
11959 {
11960 if (optInd+1>=argc)
11961 {
11962 err("option \"-e rtf\" is missing an extensions file name\n");
11964 exit(1);
11965 }
11966 writeFile(argv[optInd+1],RTFGenerator::writeExtensionsFile);
11968 exit(0);
11969 }
11970 err("option \"-e\" has invalid format specifier.\n");
11972 exit(1);
11973 }
11974 break;
11975 case 'f':
11976 {
11977 DString listName=getArg(argc,argv,optInd);
11978 if (listName.empty())
11979 {
11980 err("option \"-f\" is missing list specifier.\n");
11982 exit(1);
11983 }
11984 if (dstricmp(listName.data(),"emoji")==0)
11985 {
11986 if (optInd+1>=argc)
11987 {
11988 err("option \"-f emoji\" is missing an output file name\n");
11990 exit(1);
11991 }
11992 writeFile(argv[optInd+1],[](TextStream &t) { EmojiEntityMapper::instance().writeEmojiFile(t); });
11994 exit(0);
11995 }
11996 err("option \"-f\" has invalid list specifier.\n");
11998 exit(1);
11999 }
12000 break;
12001 case 'w':
12002 {
12003 DString formatName=getArg(argc,argv,optInd);
12004 if (formatName.empty())
12005 {
12006 err("option \"-w\" is missing format specifier rtf, html or latex\n");
12008 exit(1);
12009 }
12010 if (dstricmp(formatName.data(),"rtf")==0)
12011 {
12012 if (optInd+1>=argc)
12013 {
12014 err("option \"-w rtf\" is missing a style sheet file name\n");
12016 exit(1);
12017 }
12018 if (!writeFile(argv[optInd+1],RTFGenerator::writeStyleSheetFile))
12019 {
12020 err("error opening RTF style sheet file {}!\n",argv[optInd+1]);
12022 exit(1);
12023 }
12025 exit(0);
12026 }
12027 else if (dstricmp(formatName.data(),"html")==0)
12028 {
12029 Config::init();
12030 if (optInd+4<argc || FileInfo("Doxyfile").exists() || FileInfo("doxyfile").exists())
12031 // explicit config file mentioned or default found on disk
12032 {
12033 DString df = optInd+4<argc ? argv[optInd+4] : (FileInfo("Doxyfile").exists() ? DString("Doxyfile") : DString("doxyfile"));
12034 if (!Config::parse(df)) // parse the config file
12035 {
12036 err("error opening or reading configuration file {}!\n",argv[optInd+4]);
12038 exit(1);
12039 }
12040 }
12041 if (optInd+3>=argc)
12042 {
12043 err("option \"-w html\" does not have enough arguments\n");
12045 exit(1);
12046 }
12047 Config::postProcess(true);
12050 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
12051 writeFile(argv[optInd+1],[&](TextStream &t) { HtmlGenerator::writeHeaderFile(t,argv[optInd+3]); });
12052 writeFile(argv[optInd+2],HtmlGenerator::writeFooterFile);
12053 writeFile(argv[optInd+3],HtmlGenerator::writeStyleSheetFile);
12055 exit(0);
12056 }
12057 else if (dstricmp(formatName.data(),"latex")==0)
12058 {
12059 Config::init();
12060 if (optInd+4<argc || FileInfo("Doxyfile").exists() || FileInfo("doxyfile").exists())
12061 {
12062 DString df = optInd+4<argc ? argv[optInd+4] : (FileInfo("Doxyfile").exists() ? DString("Doxyfile") : DString("doxyfile"));
12063 if (!Config::parse(df))
12064 {
12065 err("error opening or reading configuration file {}!\n",argv[optInd+4]);
12067 exit(1);
12068 }
12069 }
12070 if (optInd+3>=argc)
12071 {
12072 err("option \"-w latex\" does not have enough arguments\n");
12074 exit(1);
12075 }
12076 Config::postProcess(true);
12079 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
12080 writeFile(argv[optInd+1],LatexGenerator::writeHeaderFile);
12081 writeFile(argv[optInd+2],LatexGenerator::writeFooterFile);
12082 writeFile(argv[optInd+3],LatexGenerator::writeStyleSheetFile);
12084 exit(0);
12085 }
12086 else
12087 {
12088 err("Illegal format specifier \"{}\": should be one of rtf, html or latex\n",formatName);
12090 exit(1);
12091 }
12092 }
12093 break;
12094 case 'm':
12095 g_dumpSymbolMap = true;
12096 break;
12097 case 'v':
12098 version(false);
12100 exit(0);
12101 break;
12102 case 'V':
12103 version(true);
12105 exit(0);
12106 break;
12107 case '-':
12108 if (dstrcmp(&argv[optInd][2],"help")==0)
12109 {
12110 usage(argv[0],versionString);
12111 exit(0);
12112 }
12113 else if (dstrcmp(&argv[optInd][2],"version")==0)
12114 {
12115 version(false);
12117 exit(0);
12118 }
12119 else if ((dstrcmp(&argv[optInd][2],"Version")==0) ||
12120 (dstrcmp(&argv[optInd][2],"VERSION")==0))
12121 {
12122 version(true);
12124 exit(0);
12125 }
12126 else
12127 {
12128 err("Unknown option \"-{}\"\n",&argv[optInd][1]);
12129 usage(argv[0],versionString);
12130 exit(1);
12131 }
12132 break;
12133 case 'b':
12134 setvbuf(stdout,nullptr,_IONBF,0);
12135 break;
12136 case 'q':
12137 quiet = true;
12138 break;
12139 case 'h':
12140 case '?':
12141 usage(argv[0],versionString);
12142 exit(0);
12143 break;
12144 default:
12145 err("Unknown option \"-{:c}\"\n",argv[optInd][1]);
12146 usage(argv[0],versionString);
12147 exit(1);
12148 }
12149 optInd++;
12150 }
12151
12152 /**************************************************************************
12153 * Parse or generate the config file *
12154 **************************************************************************/
12155
12156 initTracing(traceName.data(),traceTiming);
12157 TRACE("Doxygen version used: {}",getFullVersion());
12158 Config::init();
12159
12160 FileInfo configFileInfo1("Doxyfile"),configFileInfo2("doxyfile");
12161 if (optInd>=argc)
12162 {
12163 if (configFileInfo1.exists())
12164 {
12165 configName="Doxyfile";
12166 }
12167 else if (configFileInfo2.exists())
12168 {
12169 configName="doxyfile";
12170 }
12171 else if (genConfig)
12172 {
12173 configName="Doxyfile";
12174 }
12175 else
12176 {
12177 err("Doxyfile not found and no input file specified!\n");
12178 usage(argv[0],versionString);
12179 exit(1);
12180 }
12181 }
12182 else
12183 {
12184 FileInfo fi(argv[optInd]);
12185 if (fi.exists() || dstrcmp(argv[optInd],"-")==0 || genConfig)
12186 {
12187 configName=argv[optInd];
12188 }
12189 else
12190 {
12191 err("configuration file {} not found!\n",argv[optInd]);
12192 usage(argv[0],versionString);
12193 exit(1);
12194 }
12195 }
12196
12197 if (genConfig)
12198 {
12199 generateConfigFile(configName,shortList);
12201 exit(0);
12202 }
12203
12204 if (!Config::parse(configName,updateConfig,diffList))
12205 {
12206 err("could not open or read configuration file {}!\n",configName);
12208 exit(1);
12209 }
12210
12211 if (diffList!=Config::CompareMode::Full)
12212 {
12214 compareDoxyfile(diffList);
12216 exit(0);
12217 }
12218
12219 if (updateConfig)
12220 {
12222 generateConfigFile(configName,shortList,true);
12224 exit(0);
12225 }
12226
12227 /* Perlmod wants to know the path to the config file.*/
12228 FileInfo configFileInfo(configName.str());
12229 setPerlModDoxyfile(configFileInfo.absFilePath());
12230
12231 /* handle -q option */
12232 if (quiet) Config_updateBool(QUIET,true);
12233}
12234
12235/** check and resolve config options */
12237{
12238 AUTO_TRACE();
12239
12240 Config::postProcess(false);
12244}
12245
12246/** adjust globals that depend on configuration settings. */
12248{
12249 AUTO_TRACE();
12250 Doxygen::globalNamespaceDef = createNamespaceDef("<globalScope>",1,1,"<globalScope>");
12261
12262 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
12263
12264 /* Set the global html file extension. */
12265 Doxygen::htmlFileExtension = Config_getString(HTML_FILE_EXTENSION);
12266
12267
12269 Config_getBool(CALLER_GRAPH) ||
12270 Config_getBool(REFERENCES_RELATION) ||
12271 Config_getBool(REFERENCED_BY_RELATION);
12272
12273 /**************************************************************************
12274 * Add custom extension mappings
12275 **************************************************************************/
12276
12277 StringVector extMaps = Config_getList(EXTENSION_MAPPING);
12278 for (const auto &mapping : extMaps)
12279 {
12280 DString mapStr = mapping;
12281 if (size_t i=mapStr.find('='); i==DString::npos)
12282 {
12283 continue;
12284 }
12285 else
12286 {
12287 DString ext = mapStr.left(i).stripWhiteSpace().lower();
12288 DString language = mapStr.mid(i+1).stripWhiteSpace().lower();
12289 if (ext.empty() || language.empty())
12290 {
12291 continue;
12292 }
12293
12294 if (!updateLanguageMapping(ext,language))
12295 {
12296 err("Failed to map file extension '{}' to unsupported language '{}'.\n"
12297 "Check the EXTENSION_MAPPING setting in the config file.\n",
12298 ext,language);
12299 }
12300 else
12301 {
12302 msg("Adding custom extension mapping: '{}' will be treated as language '{}'\n",
12303 ext,language);
12304 }
12305 }
12306 }
12307 // create input file exncodings
12308
12309 // check INPUT_ENCODING
12310 void *cd = portable_iconv_open("UTF-8",Config_getString(INPUT_ENCODING).data());
12311 if (cd==reinterpret_cast<void *>(-1))
12312 {
12313 term("unsupported character conversion: '{}'->'UTF-8': {}\n"
12314 "Check the 'INPUT_ENCODING' setting in the config file!\n",
12315 Config_getString(INPUT_ENCODING),strerror(errno));
12316 }
12317 else
12318 {
12320 }
12321
12322 // check and split INPUT_FILE_ENCODING
12323 StringVector fileEncod = Config_getList(INPUT_FILE_ENCODING);
12324 for (const auto &mapping : fileEncod)
12325 {
12326 DString mapStr = mapping;
12327 if (size_t i=mapStr.find('='); i==DString::npos)
12328 {
12329 continue;
12330 }
12331 else
12332 {
12333 DString pattern = mapStr.left(i).stripWhiteSpace().lower();
12334 DString encoding = mapStr.mid(i+1).stripWhiteSpace().lower();
12335 if (pattern.empty() || encoding.empty())
12336 {
12337 continue;
12338 }
12339 cd = portable_iconv_open("UTF-8",encoding.data());
12340 if (cd==reinterpret_cast<void *>(-1))
12341 {
12342 term("unsupported character conversion: '{}'->'UTF-8': {}\n"
12343 "Check the 'INPUT_FILE_ENCODING' setting in the config file!\n",
12344 encoding,strerror(errno));
12345 }
12346 else
12347 {
12349 }
12350
12351 Doxygen::inputFileEncodingList.emplace_back(pattern, encoding);
12352 }
12353 }
12354
12355 // add predefined macro name to a dictionary
12356 StringVector expandAsDefinedList = Config_getList(EXPAND_AS_DEFINED);
12357 for (const auto &s : expandAsDefinedList)
12358 {
12360 }
12361
12362 // read aliases and store them in a dictionary
12363 readAliases();
12364
12365 // store number of spaces in a tab into Doxygen::spaces
12366 int tabSize = Config_getInt(TAB_SIZE);
12367 Doxygen::spaces.resize(tabSize);
12368 for (int sp=0; sp<tabSize; sp++) Doxygen::spaces.at(sp)=' ';
12369 Doxygen::spaces.at(tabSize)='\0';
12370}
12371
12372#ifdef HAS_SIGNALS
12373static void stopDoxygen(int)
12374{
12375 signal(SIGINT,SIG_DFL); // Re-register signal handler for default action
12376 Dir thisDir;
12377 msg("Cleaning up...\n");
12378 if (!Doxygen::filterDBFileName.empty())
12379 {
12380 thisDir.remove(Doxygen::filterDBFileName.str());
12381 }
12382 killpg(0,SIGINT);
12384 exitTracing();
12385 exit(1);
12386}
12387#endif
12388
12389static void writeTagFile()
12390{
12391 DString generateTagFile = Config_getString(GENERATE_TAGFILE);
12392 if (generateTagFile.empty()) return;
12393
12394 std::ofstream f = Portable::openOutputStream(generateTagFile);
12395 if (!f.is_open())
12396 {
12397 err("cannot open tag file {} for writing\n", generateTagFile);
12398 return;
12399 }
12400 TextStream tagFile(&f);
12401 tagFile << "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>\n";
12402 tagFile << "<tagfile doxygen_version=\"" << getDoxygenVersion() << "\"";
12403 std::string gitVersion = getGitVersion();
12404 if (!gitVersion.empty())
12405 {
12406 tagFile << " doxygen_gitid=\"" << gitVersion << "\"";
12407 }
12408 tagFile << ">\n";
12409
12410 // for each file
12411 for (const auto &fn : *Doxygen::inputNameLinkedMap)
12412 {
12413 for (const auto &fd : *fn)
12414 {
12415 if (fd->isLinkableInProject()) fd->writeTagFile(tagFile);
12416 }
12417 }
12418 // for each class
12419 for (const auto &cd : *Doxygen::classLinkedMap)
12420 {
12421 ClassDefMutable *cdm = toClassDefMutable(cd.get());
12422 if (cdm && cdm->isLinkableInProject())
12423 {
12424 cdm->writeTagFile(tagFile);
12425 }
12426 }
12427 // for each concept
12428 for (const auto &cd : *Doxygen::conceptLinkedMap)
12429 {
12430 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
12431 if (cdm && cdm->isLinkableInProject())
12432 {
12433 cdm->writeTagFile(tagFile);
12434 }
12435 }
12436 // for each namespace
12437 for (const auto &nd : *Doxygen::namespaceLinkedMap)
12438 {
12440 if (ndm && nd->isLinkableInProject())
12441 {
12442 ndm->writeTagFile(tagFile);
12443 }
12444 }
12445 // for each group
12446 for (const auto &gd : *Doxygen::groupLinkedMap)
12447 {
12448 if (gd->isLinkableInProject()) gd->writeTagFile(tagFile);
12449 }
12450 // for each module
12451 for (const auto &mod : ModuleManager::instance().modules())
12452 {
12453 if (mod->isLinkableInProject()) mod->writeTagFile(tagFile);
12454 }
12455 // for each page
12456 for (const auto &pd : *Doxygen::pageLinkedMap)
12457 {
12458 if (pd->isLinkableInProject()) pd->writeTagFile(tagFile);
12459 }
12460 // for requirements
12462 // for each directory
12463 for (const auto &dd : *Doxygen::dirLinkedMap)
12464 {
12465 if (dd->isLinkableInProject()) dd->writeTagFile(tagFile);
12466 }
12467 if (Doxygen::mainPage) Doxygen::mainPage->writeTagFile(tagFile);
12468
12469 tagFile << "</tagfile>\n";
12470}
12471
12472static void exitDoxygen() noexcept
12473{
12474 if (!g_successfulRun) // premature exit
12475 {
12476 Dir thisDir;
12477 msg("Exiting...\n");
12478 if (!Doxygen::filterDBFileName.empty())
12479 {
12480 thisDir.remove(Doxygen::filterDBFileName.str());
12481 }
12482 }
12483}
12484
12485static DString createOutputDirectory(const DString &baseDirName,
12486 const DString &formatDirName,
12487 const char *defaultDirName)
12488{
12489 DString result = formatDirName;
12490 if (result.empty())
12491 {
12492 result = baseDirName + defaultDirName;
12493 }
12494 else if (formatDirName[0]!='/' && (formatDirName.length()==1 || formatDirName[1]!=':'))
12495 {
12496 result.prepend(baseDirName+"/");
12497 }
12498 Dir formatDir(result.str());
12499 if (!formatDir.exists() && !formatDir.mkdir(result.str()))
12500 {
12501 term("Could not create output directory {}\n", result);
12502 }
12503 return result;
12504}
12505
12507{
12508 StringUnorderedSet killSet;
12509
12510 StringVector exclPatterns = Config_getList(EXCLUDE_PATTERNS);
12511 bool alwaysRecursive = Config_getBool(RECURSIVE);
12512 StringUnorderedSet excludeNameSet;
12513
12514 // gather names of all files in the include path
12515 g_s.begin("Searching for include files...\n");
12516 killSet.clear();
12517 StringVector includePathList = Config_getList(INCLUDE_PATH);
12518 for (const auto &s : includePathList)
12519 {
12520 size_t plSize = Config_getList(INCLUDE_FILE_PATTERNS).size();
12521 StringVector pl = plSize==0 ? Config_getList(FILE_PATTERNS) :
12522 Config_getList(INCLUDE_FILE_PATTERNS);
12523 readFileOrDirectory(s, // s
12525 nullptr, // exclSet
12526 &pl, // patList
12527 &exclPatterns, // exclPatList
12528 nullptr, // resultList
12529 nullptr, // resultSet
12530 false, // INCLUDE_PATH isn't recursive
12531 true, // errorIfNotExist
12532 &killSet); // killSet
12533 }
12534 g_s.end();
12535
12536 g_s.begin("Searching for example files...\n");
12537 killSet.clear();
12538 StringVector examplePathList = Config_getList(EXAMPLE_PATH);
12539 for (const auto &s : examplePathList)
12540 {
12541 StringVector patterns = Config_getList(EXAMPLE_PATTERNS);
12542 readFileOrDirectory(s, // s
12544 nullptr, // exclSet
12545 &patterns, // patList
12546 nullptr, // exclPatList
12547 nullptr, // resultList
12548 nullptr, // resultSet
12549 (alwaysRecursive || Config_getBool(EXAMPLE_RECURSIVE)), // recursive
12550 true, // errorIfNotExist
12551 &killSet); // killSet
12552 }
12553 g_s.end();
12554
12555 g_s.begin("Searching for images...\n");
12556 killSet.clear();
12557 StringVector imagePathList=Config_getList(IMAGE_PATH);
12558 for (const auto &s : imagePathList)
12559 {
12560 readFileOrDirectory(s, // s
12562 nullptr, // exclSet
12563 nullptr, // patList
12564 nullptr, // exclPatList
12565 nullptr, // resultList
12566 nullptr, // resultSet
12567 alwaysRecursive, // recursive
12568 true, // errorIfNotExist
12569 &killSet); // killSet
12570 }
12571 g_s.end();
12572
12573 g_s.begin("Searching for dot files...\n");
12574 killSet.clear();
12575 StringVector dotFileList=Config_getList(DOTFILE_DIRS);
12576 for (const auto &s : dotFileList)
12577 {
12578 readFileOrDirectory(s, // s
12580 nullptr, // exclSet
12581 nullptr, // patList
12582 nullptr, // exclPatList
12583 nullptr, // resultList
12584 nullptr, // resultSet
12585 alwaysRecursive, // recursive
12586 true, // errorIfNotExist
12587 &killSet); // killSet
12588 }
12589 g_s.end();
12590
12591 g_s.begin("Searching for msc files...\n");
12592 killSet.clear();
12593 StringVector mscFileList=Config_getList(MSCFILE_DIRS);
12594 for (const auto &s : mscFileList)
12595 {
12596 readFileOrDirectory(s, // s
12598 nullptr, // exclSet
12599 nullptr, // patList
12600 nullptr, // exclPatList
12601 nullptr, // resultList
12602 nullptr, // resultSet
12603 alwaysRecursive, // recursive
12604 true, // errorIfNotExist
12605 &killSet); // killSet
12606 }
12607 g_s.end();
12608
12609 g_s.begin("Searching for dia files...\n");
12610 killSet.clear();
12611 StringVector diaFileList=Config_getList(DIAFILE_DIRS);
12612 for (const auto &s : diaFileList)
12613 {
12614 readFileOrDirectory(s, // s
12616 nullptr, // exclSet
12617 nullptr, // patList
12618 nullptr, // exclPatList
12619 nullptr, // resultList
12620 nullptr, // resultSet
12621 alwaysRecursive, // recursive
12622 true, // errorIfNotExist
12623 &killSet); // killSet
12624 }
12625 g_s.end();
12626
12627 g_s.begin("Searching for plantuml files...\n");
12628 killSet.clear();
12629 StringVector plantUmlFileList=Config_getList(PLANTUMLFILE_DIRS);
12630 for (const auto &s : plantUmlFileList)
12631 {
12632 readFileOrDirectory(s, // s
12634 nullptr, // exclSet
12635 nullptr, // patList
12636 nullptr, // exclPatList
12637 nullptr, // resultList
12638 nullptr, // resultSet
12639 alwaysRecursive, // recursive
12640 true, // errorIfNotExist
12641 &killSet); // killSet
12642 }
12643 g_s.end();
12644
12645 g_s.begin("Searching for mermaid files...\n");
12646 killSet.clear();
12647 StringVector mermaidFileList=Config_getList(MERMAIDFILE_DIRS);
12648 for (const auto &s : mermaidFileList)
12649 {
12650 readFileOrDirectory(s, // s
12652 nullptr, // exclSet
12653 nullptr, // patList
12654 nullptr, // exclPatList
12655 nullptr, // resultList
12656 nullptr, // resultSet
12657 alwaysRecursive, // recursive
12658 true, // errorIfNotExist
12659 &killSet); // killSet
12660 }
12661 g_s.end();
12662
12663 g_s.begin("Searching for files to exclude\n");
12664 StringVector excludeList = Config_getList(EXCLUDE);
12665 for (const auto &s : excludeList)
12666 {
12667 StringVector filePatterns = Config_getList(FILE_PATTERNS);
12668 readFileOrDirectory(s, // s
12669 nullptr, // fnDict
12670 nullptr, // exclSet
12671 &filePatterns, // patList
12672 nullptr, // exclPatList
12673 nullptr, // resultList
12674 &excludeNameSet, // resultSet
12675 alwaysRecursive, // recursive
12676 false); // errorIfNotExist
12677 }
12678 g_s.end();
12679
12680 /**************************************************************************
12681 * Determine Input Files *
12682 **************************************************************************/
12683
12684 g_s.begin("Searching INPUT for files to process...\n");
12685 killSet.clear();
12686 Doxygen::inputPaths.clear();
12687 StringVector inputList=Config_getList(INPUT);
12688 for (const auto &s : inputList)
12689 {
12690 DString path = s;
12691 size_t l = path.length();
12692 if (l>0)
12693 {
12694 // strip trailing slashes
12695 if (path.at(l-1)=='\\' || path.at(l-1)=='/') path=path.left(l-1);
12696
12697 StringVector filePatterns = Config_getList(FILE_PATTERNS);
12699 path, // s
12701 &excludeNameSet, // exclSet
12702 &filePatterns, // patList
12703 &exclPatterns, // exclPatList
12704 &g_inputFiles, // resultList
12705 nullptr, // resultSet
12706 alwaysRecursive, // recursive
12707 true, // errorIfNotExist
12708 &killSet, // killSet
12709 &Doxygen::inputPaths); // paths
12710 }
12711 }
12712
12713 // Sort the FileDef objects by full path to get a predictable ordering over multiple runs
12714 for (auto &fileName : *Doxygen::inputNameLinkedMap)
12715 {
12716 if (fileName->size()>1)
12717 {
12718 std::stable_sort(fileName->begin(),fileName->end(),[](const auto &f1,const auto &f2)
12719 {
12720 return dstricmp_sort(f1->absFilePath(),f2->absFilePath())<0;
12721 });
12722 }
12723 }
12724 std::stable_sort(Doxygen::inputNameLinkedMap->begin(),
12726 [](const auto &f1,const auto &f2)
12727 {
12728 return dstricmp_sort(f1->front()->absFilePath(),f2->front()->absFilePath())<0;
12729 });
12730 if (Doxygen::inputNameLinkedMap->empty())
12731 {
12732 warn_uncond("No files to be processed, please check your settings, in particular INPUT, FILE_PATTERNS, and RECURSIVE\n");
12733 }
12734 g_s.end();
12735}
12736
12737
12739{
12740 if (Config_getBool(MARKDOWN_SUPPORT))
12741 {
12742 DString mdfileAsMainPage = Config_getString(USE_MDFILE_AS_MAINPAGE);
12743 if (mdfileAsMainPage.empty()) return;
12744 FileInfo fi(mdfileAsMainPage.data());
12745 if (!fi.exists())
12746 {
12747 warn_uncond("Specified markdown mainpage '{}' does not exist\n",mdfileAsMainPage);
12748 return;
12749 }
12750 bool ambig = false;
12751 if (Doxygen::inputNameLinkedMap->findFileDef(fi.absFilePath(),ambig)==nullptr)
12752 {
12753 warn_uncond("Specified markdown mainpage '{}' has not been defined as input file\n",mdfileAsMainPage);
12754 return;
12755 }
12756 }
12757}
12758
12760{
12761 AUTO_TRACE();
12762 std::atexit(exitDoxygen);
12763
12764 Portable::correctPath(Config_getList(EXTERNAL_TOOL_PATH));
12765
12766#if USE_LIBCLANG
12767 Doxygen::clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING);
12768#endif
12769
12770 // we would like to show the versionString earlier, but we first have to handle the configuration file
12771 // to know the value of the QUIET setting.
12772 DString versionString = getFullVersion();
12773 msg("Doxygen version used: {}\n",versionString);
12774
12776
12777 /**************************************************************************
12778 * Make sure the output directory exists
12779 **************************************************************************/
12780 DString outputDirectory = Config_getString(OUTPUT_DIRECTORY);
12781 if (!g_singleComment)
12782 {
12783 if (outputDirectory.empty())
12784 {
12785 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,Dir::currentDirPath());
12786 }
12787 else
12788 {
12789 Dir dir(outputDirectory.str());
12790 if (!dir.exists())
12791 {
12793 if (!dir.mkdir(outputDirectory.str()))
12794 {
12795 term("tag OUTPUT_DIRECTORY: Output directory '{}' does not "
12796 "exist and cannot be created\n",outputDirectory);
12797 }
12798 else
12799 {
12800 msg("Notice: Output directory '{}' does not exist. "
12801 "I have created it for you.\n", outputDirectory);
12802 }
12803 dir.setPath(outputDirectory.str());
12804 }
12805 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,dir.absPath());
12806 }
12807 }
12808 AUTO_TRACE_ADD("outputDirectory={}",outputDirectory);
12809
12810 /**************************************************************************
12811 * Initialize global lists and dictionaries
12812 **************************************************************************/
12813
12814#ifdef HAS_SIGNALS
12815 signal(SIGINT, stopDoxygen);
12816#endif
12817
12818 uint32_t pid = Portable::pid();
12819 Doxygen::filterDBFileName.sprintf("doxygen_filterdb_%d.tmp",pid);
12820 Doxygen::filterDBFileName.prepend(outputDirectory+"/");
12821
12822 /**************************************************************************
12823 * Check/create output directories *
12824 **************************************************************************/
12825
12826 bool generateHtml = Config_getBool(GENERATE_HTML);
12827 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
12828 bool generateXml = Config_getBool(GENERATE_XML);
12829 bool generateLatex = Config_getBool(GENERATE_LATEX);
12830 bool generateRtf = Config_getBool(GENERATE_RTF);
12831 bool generateMan = Config_getBool(GENERATE_MAN);
12832 bool generateSql = Config_getBool(GENERATE_SQLITE3);
12833 DString htmlOutput;
12834 DString docbookOutput;
12835 DString xmlOutput;
12836 DString latexOutput;
12837 DString rtfOutput;
12838 DString manOutput;
12839 DString sqlOutput;
12840
12841 if (!g_singleComment)
12842 {
12843 if (generateHtml)
12844 {
12845 htmlOutput = createOutputDirectory(outputDirectory,Config_getString(HTML_OUTPUT),"/html");
12846 Config_updateString(HTML_OUTPUT,htmlOutput);
12847
12848 DString sitemapUrl = Config_getString(SITEMAP_URL);
12849 bool generateSitemap = !sitemapUrl.empty();
12850 if (generateSitemap && !sitemapUrl.endsWith("/"))
12851 {
12852 Config_updateString(SITEMAP_URL,sitemapUrl+"/");
12853 }
12854
12855 // add HTML indexers that are enabled
12856 bool generateHtmlHelp = Config_getBool(GENERATE_HTMLHELP);
12857 bool generateEclipseHelp = Config_getBool(GENERATE_ECLIPSEHELP);
12858 bool generateQhp = Config_getBool(GENERATE_QHP);
12859 bool generateTreeView = Config_getBool(GENERATE_TREEVIEW);
12860 bool generateDocSet = Config_getBool(GENERATE_DOCSET);
12861 if (generateEclipseHelp) Doxygen::indexList->addIndex<EclipseHelp>();
12862 if (generateHtmlHelp) Doxygen::indexList->addIndex<HtmlHelp>();
12863 if (generateQhp) Doxygen::indexList->addIndex<Qhp>();
12864 if (generateSitemap) Doxygen::indexList->addIndex<Sitemap>();
12865 if (generateTreeView) Doxygen::indexList->addIndex<FTVHelp>(true);
12866 if (generateDocSet) Doxygen::indexList->addIndex<DocSets>();
12869 }
12870
12871 if (generateDocbook)
12872 {
12873 docbookOutput = createOutputDirectory(outputDirectory,Config_getString(DOCBOOK_OUTPUT),"/docbook");
12874 Config_updateString(DOCBOOK_OUTPUT,docbookOutput);
12875 }
12876
12877 if (generateXml)
12878 {
12879 xmlOutput = createOutputDirectory(outputDirectory,Config_getString(XML_OUTPUT),"/xml");
12880 Config_updateString(XML_OUTPUT,xmlOutput);
12881 }
12882
12883 if (generateLatex)
12884 {
12885 latexOutput = createOutputDirectory(outputDirectory,Config_getString(LATEX_OUTPUT), "/latex");
12886 Config_updateString(LATEX_OUTPUT,latexOutput);
12887 }
12888
12889 if (generateRtf)
12890 {
12891 rtfOutput = createOutputDirectory(outputDirectory,Config_getString(RTF_OUTPUT),"/rtf");
12892 Config_updateString(RTF_OUTPUT,rtfOutput);
12893 }
12894
12895 if (generateMan)
12896 {
12897 manOutput = createOutputDirectory(outputDirectory,Config_getString(MAN_OUTPUT),"/man");
12898 Config_updateString(MAN_OUTPUT,manOutput);
12899 }
12900
12901 if (generateSql)
12902 {
12903 sqlOutput = createOutputDirectory(outputDirectory,Config_getString(SQLITE3_OUTPUT),"/sqlite3");
12904 Config_updateString(SQLITE3_OUTPUT,sqlOutput);
12905 }
12906 }
12907
12908 if (Config_getBool(HAVE_DOT))
12909 {
12910 DString curFontPath = Config_getString(DOT_FONTPATH);
12911 if (curFontPath.empty())
12912 {
12913 Portable::getenv("DOTFONTPATH");
12914 DString newFontPath = ".";
12915 if (!curFontPath.empty())
12916 {
12917 newFontPath+=Portable::pathListSeparator();
12918 newFontPath+=curFontPath;
12919 }
12920 Portable::setenv("DOTFONTPATH",qPrint(newFontPath));
12921 }
12922 else
12923 {
12924 Portable::setenv("DOTFONTPATH",qPrint(curFontPath));
12925 }
12926 }
12927
12928 /**************************************************************************
12929 * Handle layout file *
12930 **************************************************************************/
12931
12933 DString layoutFileName = Config_getString(LAYOUT_FILE);
12934 bool defaultLayoutUsed = false;
12935 if (layoutFileName.empty())
12936 {
12937 layoutFileName = Config_updateString(LAYOUT_FILE,"DoxygenLayout.xml");
12938 defaultLayoutUsed = true;
12939 }
12940 AUTO_TRACE_ADD("defaultLayoutUsed={}, layoutFileName={}",defaultLayoutUsed,layoutFileName);
12941
12942 FileInfo fi(layoutFileName.str());
12943 if (fi.exists())
12944 {
12945 msg("Parsing layout file {}...\n",layoutFileName);
12946 LayoutDocManager::instance().parse(layoutFileName);
12947 }
12948 else if (!defaultLayoutUsed)
12949 {
12950 warn_uncond("failed to open layout file '{}' for reading! Using default settings.\n",layoutFileName);
12951 }
12952 printLayout();
12953
12954 /**************************************************************************
12955 * Read and preprocess input *
12956 **************************************************************************/
12957
12958 // prevent search in the output directories
12959 StringVector exclPatterns = Config_getList(EXCLUDE_PATTERNS);
12960 if (generateHtml) exclPatterns.push_back(htmlOutput.str());
12961 if (generateDocbook) exclPatterns.push_back(docbookOutput.str());
12962 if (generateXml) exclPatterns.push_back(xmlOutput.str());
12963 if (generateLatex) exclPatterns.push_back(latexOutput.str());
12964 if (generateRtf) exclPatterns.push_back(rtfOutput.str());
12965 if (generateMan) exclPatterns.push_back(manOutput.str());
12966 Config_updateList(EXCLUDE_PATTERNS,exclPatterns);
12967
12968 if (!g_singleComment)
12969 {
12971
12973 }
12974
12975 // Notice: the order of the function calls below is very important!
12976
12977 if (generateHtml && !Config_getBool(USE_MATHJAX))
12978 {
12980 }
12981 if (generateRtf)
12982 {
12984 }
12985 if (generateDocbook)
12986 {
12988 }
12989
12991
12992 /**************************************************************************
12993 * Handle Tag Files *
12994 **************************************************************************/
12995
12996 std::shared_ptr<Entry> root = std::make_shared<Entry>();
12997
12998 if (!g_singleComment)
12999 {
13000 msg("Reading and parsing tag files\n");
13001 StringVector tagFileList = Config_getList(TAGFILES);
13002 for (const auto &s : tagFileList)
13003 {
13004 readTagFile(root,s.c_str());
13005 }
13006 }
13007
13008 /**************************************************************************
13009 * Parse source files *
13010 **************************************************************************/
13011
13012 addSTLSupport(root);
13013
13014 g_s.begin("Parsing files\n");
13015 if (g_singleComment)
13016 {
13017 //printf("Parsing comment %s\n",qPrint(g_commentFileName));
13018 if (g_commentFileName=="-")
13019 {
13020 std::string text = fileToString(g_commentFileName).str();
13021 addTerminalCharIfMissing(text,'\n');
13022 generateHtmlForComment("stdin.md",text);
13023 }
13024 else if (FileInfo(g_commentFileName.str()).isFile())
13025 {
13026 std::string text;
13028 addTerminalCharIfMissing(text,'\n');
13030 }
13031 else
13032 {
13033 }
13035 exit(0);
13036 }
13037 else
13038 {
13039 if (Config_getInt(NUM_PROC_THREADS)==1)
13040 {
13042 }
13043 else
13044 {
13046 }
13047 }
13048 g_s.end();
13049
13050 /**************************************************************************
13051 * Gather information *
13052 **************************************************************************/
13053
13054 g_s.begin("Building macro definition list...\n");
13056 g_s.end();
13057
13058 g_s.begin("Building group list...\n");
13059 buildGroupList(root.get());
13060 organizeSubGroups(root.get());
13061 g_s.end();
13062
13063 g_s.begin("Building directory list...\n");
13065 findDirDocumentation(root.get());
13066 g_s.end();
13067
13068 g_s.begin("Building namespace list...\n");
13069 buildNamespaceList(root.get());
13070 findUsingDirectives(root.get());
13071 g_s.end();
13072
13073 g_s.begin("Building file list...\n");
13074 buildFileList(root.get());
13075 g_s.end();
13076
13077 g_s.begin("Building class list...\n");
13078 buildClassList(root.get());
13079 g_s.end();
13080
13081 g_s.begin("Building concept list...\n");
13082 buildConceptList(root.get());
13083 g_s.end();
13084
13085 // build list of using declarations here (global list)
13086 buildListOfUsingDecls(root.get());
13087 g_s.end();
13088
13089 g_s.begin("Computing nesting relations for classes...\n");
13091 g_s.end();
13092 // 1.8.2-20121111: no longer add nested classes to the group as well
13093 //distributeClassGroupRelations();
13094
13095 // calling buildClassList may result in cached relations that
13096 // become invalid after resolveClassNestingRelations(), that's why
13097 // we need to clear the cache here
13099 // we don't need the list of using declaration anymore
13100 g_usingDeclarations.clear();
13101
13102 g_s.begin("Associating documentation with classes...\n");
13103 buildClassDocList(root.get());
13104 g_s.end();
13105
13106 g_s.begin("Associating documentation with concepts...\n");
13107 buildConceptDocList(root.get());
13109 g_s.end();
13110
13111 g_s.begin("Associating documentation with modules...\n");
13112 findModuleDocumentation(root.get());
13113 g_s.end();
13114
13115 g_s.begin("Building example list...\n");
13116 buildExampleList(root.get());
13117 g_s.end();
13118
13119 g_s.begin("Searching for enumerations...\n");
13120 findEnums(root.get());
13121 g_s.end();
13122
13123 // Since buildVarList calls isVarWithConstructor
13124 // and this calls getResolvedClass we need to process
13125 // typedefs first so the relations between classes via typedefs
13126 // are properly resolved. See bug 536385 for an example.
13127 g_s.begin("Searching for documented typedefs...\n");
13128 buildTypedefList(root.get());
13129 g_s.end();
13130
13131 if (Config_getBool(OPTIMIZE_OUTPUT_SLICE))
13132 {
13133 g_s.begin("Searching for documented sequences...\n");
13134 buildSequenceList(root.get());
13135 g_s.end();
13136
13137 g_s.begin("Searching for documented dictionaries...\n");
13138 buildDictionaryList(root.get());
13139 g_s.end();
13140 }
13141
13142 g_s.begin("Searching for members imported via using declarations...\n");
13143 // this should be after buildTypedefList in order to properly import
13144 // used typedefs
13145 findUsingDeclarations(root.get(),true); // do for python packages first
13146 findUsingDeclarations(root.get(),false); // then the rest
13147 g_s.end();
13148
13149 g_s.begin("Searching for included using directives...\n");
13151 g_s.end();
13152
13153 g_s.begin("Searching for documented variables...\n");
13154 buildVarList(root.get());
13155 g_s.end();
13156
13157 g_s.begin("Building interface member list...\n");
13158 buildInterfaceAndServiceList(root.get()); // UNO IDL
13159
13160 g_s.begin("Building member list...\n"); // using class info only !
13161 buildFunctionList(root.get());
13162 g_s.end();
13163
13164 g_s.begin("Searching for friends...\n");
13165 findFriends();
13166 g_s.end();
13167
13168 g_s.begin("Searching for documented defines...\n");
13169 findDefineDocumentation(root.get());
13170 g_s.end();
13171
13172 g_s.begin("Computing class inheritance relations...\n");
13173 findClassEntries(root.get());
13175 g_s.end();
13176
13177 g_s.begin("Computing class usage relations...\n");
13179 g_s.end();
13180
13181 g_s.begin("Flushing cached template relations that have become invalid...\n");
13183 g_s.end();
13184
13185 g_s.begin("Warn for undocumented namespaces...\n");
13187 g_s.end();
13188
13189 g_s.begin("Computing class relations...\n");
13192 if (Config_getBool(OPTIMIZE_OUTPUT_VHDL))
13193 {
13195 }
13197 g_classEntries.clear();
13198 g_s.end();
13199
13200 g_s.begin("Add enum values to enums...\n");
13201 addEnumValuesToEnums(root.get());
13202 findEnumDocumentation(root.get());
13203 g_s.end();
13204
13205 g_s.begin("Searching for member function documentation...\n");
13206 findObjCMethodDefinitions(root.get());
13207 findMemberDocumentation(root.get()); // may introduce new members !
13208 findUsingDeclImports(root.get()); // may introduce new members !
13209 g_usingClassMap.clear();
13213 g_s.end();
13214
13215 // moved to after finding and copying documentation,
13216 // as this introduces new members see bug 722654
13217 g_s.begin("Creating members for template instances...\n");
13219 g_s.end();
13220
13221 g_s.begin("Searching for tag less structs...\n");
13223 g_s.end();
13224
13225 g_s.begin("Building page list...\n");
13226 buildPageList(root.get());
13227 g_s.end();
13228
13229 g_s.begin("Building requirements list...\n");
13230 buildRequirementsList(root.get());
13231 g_s.end();
13232
13233 g_s.begin("Search for main page...\n");
13234 findMainPage(root.get());
13235 findMainPageTagFiles(root.get());
13236 g_s.end();
13237
13238 g_s.begin("Computing page relations...\n");
13239 computePageRelations(root.get());
13241 g_s.end();
13242
13243 g_s.begin("Determining the scope of groups...\n");
13244 findGroupScope(root.get());
13245 g_s.end();
13246
13247 g_s.begin("Computing module relations...\n");
13248 auto &mm = ModuleManager::instance();
13249 mm.resolvePartitions();
13250 mm.resolveImports();
13251 mm.collectExportedSymbols();
13252 g_s.end();
13253
13254 auto memberNameComp = [](const MemberNameLinkedMap::Ptr &n1,const MemberNameLinkedMap::Ptr &n2)
13255 {
13256 return dstricmp_sort(n1->memberName().data()+getPrefixIndex(n1->memberName()),
13257 n2->memberName().data()+getPrefixIndex(n2->memberName())
13258 )<0;
13259 };
13260
13261 auto classComp = [](const ClassLinkedMap::Ptr &c1,const ClassLinkedMap::Ptr &c2)
13262 {
13263 if (Config_getBool(SORT_BY_SCOPE_NAME))
13264 {
13265 return dstricmp_sort(c1->name(), c2->name())<0;
13266 }
13267 else
13268 {
13269 int i = dstricmp_sort(c1->className(), c2->className());
13270 return i==0 ? dstricmp_sort(c1->name(), c2->name())<0 : i<0;
13271 }
13272 };
13273
13274 auto namespaceComp = [](const NamespaceLinkedMap::Ptr &n1,const NamespaceLinkedMap::Ptr &n2)
13275 {
13276 return dstricmp_sort(n1->name(),n2->name())<0;
13277 };
13278
13279 auto conceptComp = [](const ConceptLinkedMap::Ptr &c1,const ConceptLinkedMap::Ptr &c2)
13280 {
13281 return dstricmp_sort(c1->name(),c2->name())<0;
13282 };
13283
13284 g_s.begin("Sorting lists...\n");
13285 std::stable_sort(Doxygen::memberNameLinkedMap->begin(),
13287 memberNameComp);
13288 std::stable_sort(Doxygen::functionNameLinkedMap->begin(),
13290 memberNameComp);
13291 std::stable_sort(Doxygen::hiddenClassLinkedMap->begin(),
13293 classComp);
13294 std::stable_sort(Doxygen::classLinkedMap->begin(),
13296 classComp);
13297 std::stable_sort(Doxygen::conceptLinkedMap->begin(),
13299 conceptComp);
13300 std::stable_sort(Doxygen::namespaceLinkedMap->begin(),
13302 namespaceComp);
13303 g_s.end();
13304
13305 g_s.begin("Determining which enums are documented\n");
13307 g_s.end();
13308
13309 g_s.begin("Computing member relations...\n");
13312 g_s.end();
13313
13314 g_s.begin("Building full member lists recursively...\n");
13316 g_s.end();
13317
13318 g_s.begin("Adding members to member groups.\n");
13320 g_s.end();
13321
13322 if (Config_getBool(DISTRIBUTE_GROUP_DOC))
13323 {
13324 g_s.begin("Distributing member group documentation.\n");
13326 g_s.end();
13327 }
13328
13329 g_s.begin("Computing member references...\n");
13331 g_s.end();
13332
13333 if (Config_getBool(INHERIT_DOCS))
13334 {
13335 g_s.begin("Inheriting documentation...\n");
13337 g_s.end();
13338 }
13339
13340
13341 // compute the shortest possible names of all files
13342 // without losing the uniqueness of the file names.
13343 g_s.begin("Generating disk names...\n");
13345 g_s.end();
13346
13347 g_s.begin("Adding source references...\n");
13349 g_s.end();
13350
13351 g_s.begin("Adding xrefitems...\n");
13354 g_s.end();
13355
13356 g_s.begin("Adding requirements...\n");
13359 g_s.end();
13360
13361 g_s.begin("Sorting member lists...\n");
13363 g_s.end();
13364
13365 g_s.begin("Setting anonymous enum type...\n");
13367 g_s.end();
13368
13369 g_s.begin("Computing dependencies between directories...\n");
13371 g_s.end();
13372
13373 g_s.begin("Generating citations page...\n");
13375 g_s.end();
13376
13377 g_s.begin("Counting data structures...\n");
13379 g_s.end();
13380
13381 g_s.begin("Resolving user defined references...\n");
13383 g_s.end();
13384
13385 g_s.begin("Finding anchors and sections in the documentation...\n");
13387 g_s.end();
13388
13389 g_s.begin("Transferring function references...\n");
13391 g_s.end();
13392
13393 g_s.begin("Combining using relations...\n");
13395 g_s.end();
13396
13398 g_s.begin("Adding members to index pages...\n");
13400 addToIndices();
13401 g_s.end();
13402
13403 g_s.begin("Correcting members for VHDL...\n");
13405 g_s.end();
13406
13407 g_s.begin("Computing tooltip texts...\n");
13409 g_s.end();
13410
13411 if (Config_getBool(SORT_GROUP_NAMES))
13412 {
13413 std::stable_sort(Doxygen::groupLinkedMap->begin(),
13415 [](const auto &g1,const auto &g2)
13416 { return g1->groupTitle() < g2->groupTitle(); });
13417
13418 for (const auto &gd : *Doxygen::groupLinkedMap)
13419 {
13420 gd->sortSubGroups();
13421 }
13422 }
13423
13424 printNavTree(root.get(),0);
13426}
13427
13429{
13430 AUTO_TRACE();
13431 /**************************************************************************
13432 * Initialize output generators *
13433 **************************************************************************/
13434
13435 /// add extra languages for which we can only produce syntax highlighted code
13437
13438 //// dump all symbols
13439 if (g_dumpSymbolMap)
13440 {
13441 dumpSymbolMap();
13442 exit(0);
13443 }
13444
13445 bool generateHtml = Config_getBool(GENERATE_HTML);
13446 bool generateLatex = Config_getBool(GENERATE_LATEX);
13447 bool generateMan = Config_getBool(GENERATE_MAN);
13448 bool generateRtf = Config_getBool(GENERATE_RTF);
13449 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
13450
13451
13453 if (generateHtml)
13454 {
13458 }
13459 if (generateLatex)
13460 {
13463 }
13464 if (generateDocbook)
13465 {
13468 }
13469 if (generateMan)
13470 {
13473 }
13474 if (generateRtf)
13475 {
13478 }
13479 if (Config_getBool(USE_HTAGS))
13480 {
13481 Htags::useHtags = true;
13482 DString htmldir = Config_getString(HTML_OUTPUT);
13483 if (!Htags::execute(htmldir))
13484 err("USE_HTAGS is YES but htags(1) failed. \n");
13485 else if (!Htags::loadFilemap(htmldir))
13486 err("htags(1) ended normally but failed to load the filemap. \n");
13487 }
13488
13489 /**************************************************************************
13490 * Generate documentation *
13491 **************************************************************************/
13492
13493 g_s.begin("Generating style sheet...\n");
13494 //printf("writing style info\n");
13495 g_outputList->writeStyleInfo(0); // write first part
13496 g_s.end();
13497
13498 bool searchEngine = Config_getBool(SEARCHENGINE);
13499 bool serverBasedSearch = Config_getBool(SERVER_BASED_SEARCH);
13500
13501 g_s.begin("Generating search indices...\n");
13502 if (searchEngine && !serverBasedSearch && generateHtml)
13503 {
13505 }
13506
13507 // generate search indices (need to do this before writing other HTML
13508 // pages as these contain a drop down menu with options depending on
13509 // what categories we find in this function.
13510 if (generateHtml && searchEngine)
13511 {
13512 DString searchDirName = Config_getString(HTML_OUTPUT)+"/search";
13513 Dir searchDir(searchDirName.str());
13514 if (!searchDir.exists() && !searchDir.mkdir(searchDirName.str()))
13515 {
13516 term("Could not create search results directory '{}' $PWD='{}'\n",
13517 searchDirName,Dir::currentDirPath());
13518 }
13519 HtmlGenerator::writeSearchData(searchDirName);
13520 if (!serverBasedSearch) // client side search index
13521 {
13523 }
13524 }
13525 g_s.end();
13526
13527 // copy static stuff
13528 if (generateHtml)
13529 {
13531 copyLogo(Config_getString(HTML_OUTPUT),true);
13532 copyIcon(Config_getString(HTML_OUTPUT),true);
13533 copyExtraFiles(Config_getList(HTML_EXTRA_FILES),"HTML_EXTRA_FILES",Config_getString(HTML_OUTPUT),true);
13534 }
13535 if (generateLatex)
13536 {
13538 copyLogo(Config_getString(LATEX_OUTPUT),false);
13539 copyIcon(Config_getString(LATEX_OUTPUT),false);
13540 copyExtraFiles(Config_getList(LATEX_EXTRA_FILES),"LATEX_EXTRA_FILES",Config_getString(LATEX_OUTPUT),false);
13541 }
13542 if (generateDocbook)
13543 {
13544 copyLogo(Config_getString(DOCBOOK_OUTPUT),false);
13545 copyIcon(Config_getString(DOCBOOK_OUTPUT),false);
13546 }
13547 if (generateRtf)
13548 {
13549 copyLogo(Config_getString(RTF_OUTPUT),false);
13550 copyIcon(Config_getString(RTF_OUTPUT),false);
13551 copyExtraFiles(Config_getList(RTF_EXTRA_FILES),"RTF_EXTRA_FILES",Config_getString(RTF_OUTPUT),false);
13552 }
13553
13555 if (fm.hasFormulas() && generateHtml
13556 && !Config_getBool(USE_MATHJAX))
13557 {
13558 g_s.begin("Generating images for formulas in HTML...\n");
13559 fm.generateImages(Config_getString(HTML_OUTPUT), true, Config_getEnum(HTML_FORMULA_FORMAT)==HTML_FORMULA_FORMAT_t::svg ?
13561 g_s.end();
13562 }
13563 if (fm.hasFormulas() && generateRtf)
13564 {
13565 g_s.begin("Generating images for formulas in RTF...\n");
13567 g_s.end();
13568 }
13569
13570 if (fm.hasFormulas() && generateDocbook)
13571 {
13572 g_s.begin("Generating images for formulas in Docbook...\n");
13574 g_s.end();
13575 }
13576
13577 g_s.begin("Generating example documentation...\n");
13579 g_s.end();
13580
13581 g_s.begin("Generating file sources...\n");
13583 g_s.end();
13584
13585 g_s.begin("Counting members...\n");
13586 // needs to be done after generating the sources
13587 // but before generating the compound documentation, see bug #12233
13588 countMembers();
13589 g_s.end();
13590
13591 g_s.begin("Generating file documentation...\n");
13593 g_s.end();
13594
13595 g_s.begin("Generating page documentation...\n");
13597 g_s.end();
13598
13599 g_s.begin("Generating group documentation...\n");
13601 g_s.end();
13602
13603 g_s.begin("Generating class documentation...\n");
13605 g_s.end();
13606
13607 g_s.begin("Generating concept documentation...\n");
13609 g_s.end();
13610
13611 g_s.begin("Generating module documentation...\n");
13613 g_s.end();
13614
13615 g_s.begin("Generating namespace documentation...\n");
13617 g_s.end();
13618
13619 if (Config_getBool(GENERATE_LEGEND))
13620 {
13621 g_s.begin("Generating graph info page...\n");
13623 g_s.end();
13624 }
13625
13626 g_s.begin("Generating directory documentation...\n");
13628 g_s.end();
13629
13630 if (g_outputList->size()>0)
13631 {
13633 }
13634
13635 g_s.begin("finalizing index lists...\n");
13637 g_s.end();
13638
13639 g_s.begin("writing tag file...\n");
13640 writeTagFile();
13641 g_s.end();
13642
13643 if (Config_getBool(GENERATE_XML))
13644 {
13645 g_s.begin("Generating XML output...\n");
13647 generateXML();
13649 g_s.end();
13650 }
13651 if (Config_getBool(GENERATE_SQLITE3))
13652 {
13653 g_s.begin("Generating SQLITE3 output...\n");
13655 g_s.end();
13656 }
13657
13658 if (Config_getBool(GENERATE_AUTOGEN_DEF))
13659 {
13660 g_s.begin("Generating AutoGen DEF output...\n");
13661 generateDEF();
13662 g_s.end();
13663 }
13664 if (Config_getBool(GENERATE_PERLMOD))
13665 {
13666 g_s.begin("Generating Perl module output...\n");
13668 g_s.end();
13669 }
13670 if (generateHtml && searchEngine && serverBasedSearch)
13671 {
13672 g_s.begin("Generating search index\n");
13673 if (Doxygen::searchIndex.kind()==SearchIndexIntf::Internal) // write own search index
13674 {
13676 Doxygen::searchIndex.write(Config_getString(HTML_OUTPUT)+"/search/search.idx");
13677 }
13678 else // write data for external search index
13679 {
13681 DString searchDataFile = Config_getString(SEARCHDATA_FILE);
13682 if (searchDataFile.empty())
13683 {
13684 searchDataFile="searchdata.xml";
13685 }
13686 if (!Portable::isAbsolutePath(searchDataFile.data()))
13687 {
13688 searchDataFile.prepend(Config_getString(OUTPUT_DIRECTORY)+"/");
13689 }
13690 Doxygen::searchIndex.write(searchDataFile);
13691 }
13692 g_s.end();
13693 }
13694
13695 if (generateRtf)
13696 {
13697 g_s.begin("Combining RTF output...\n");
13698 if (!RTFGenerator::preProcessFileInplace(Config_getString(RTF_OUTPUT),"refman.rtf"))
13699 {
13700 err("An error occurred during post-processing the RTF files!\n");
13701 }
13702 g_s.end();
13703 }
13704
13705 if (PlantumlManager::instance().needToRun())
13706 {
13707 g_s.begin("Running plantuml with JAVA...\n");
13709 g_s.end();
13710 }
13711
13712 if (MermaidManager::instance().needToRun())
13713 {
13714 g_s.begin("Running mermaid (mmdc)...\n");
13716 g_s.end();
13717 }
13718
13719 if (Config_getBool(HAVE_DOT) && DotManager::instance()->needToRun())
13720 {
13721 g_s.begin("Running dot...\n");
13723 g_s.end();
13724 }
13725
13726 if (generateHtml &&
13727 Config_getBool(GENERATE_HTMLHELP) &&
13728 !Config_getString(HHC_LOCATION).empty())
13729 {
13730 g_s.begin("Running html help compiler...\n");
13732 g_s.end();
13733 }
13734
13735 if ( generateHtml &&
13736 Config_getBool(GENERATE_QHP) &&
13737 !Config_getString(QHG_LOCATION).empty())
13738 {
13739 g_s.begin("Running qhelpgenerator...\n");
13741 g_s.end();
13742 }
13743
13746
13748
13750 {
13751
13752 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
13753 if (numThreads<1) numThreads=1;
13754 msg("Total elapsed time: {:.6f} seconds\n(of which an average of {:.6f} seconds per thread waiting for external tools to finish)\n",
13755 (static_cast<double>(Debug::elapsedTime())),
13756 Portable::getSysElapsedTime()/static_cast<double>(numThreads)
13757 );
13758 g_s.print();
13759
13761 msg("finished...\n");
13763 }
13764 else
13765 {
13766 msg("finished...\n");
13767 }
13768
13769
13770 /**************************************************************************
13771 * Start cleaning up *
13772 **************************************************************************/
13773
13775
13777 Dir thisDir;
13778 thisDir.remove(Doxygen::filterDBFileName.str());
13780 exitTracing();
13782 delete Doxygen::clangUsrMap;
13783 g_successfulRun=true;
13784
13785 //dumpDocNodeSizes();
13786}
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 setTaglessParent(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 setHasNamedVariable(bool b)=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
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:1461
static void init()
Definition htmlgen.cpp:1281
static void writeSearchPage()
Definition htmlgen.cpp:3284
static void writeHeaderFile(TextStream &t, const DString &cssname)
Definition htmlgen.cpp:1629
static void writeFooterFile(TextStream &t)
Definition htmlgen.cpp:1635
static void writeTabData()
Additional initialization after indices have been created.
Definition htmlgen.cpp:1445
static void writeExternalSearchPage()
Definition htmlgen.cpp:3383
static void writeStyleSheetFile(TextStream &t)
Definition htmlgen.cpp:1623
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:96
static void writeFooterFile(TextStream &t)
Definition latexgen.cpp:696
static void writeStyleSheetFile(TextStream &t)
Definition latexgen.cpp:702
static void writeHeaderFile(TextStream &t)
Definition latexgen.cpp:690
static void init()
Definition latexgen.cpp:632
void parse(const DString &fileName, const char *data=nullptr)
Parses a user provided layout.
Definition layout.cpp:1482
static LayoutDocManager & instance()
Returns a reference to this singleton.
Definition layout.cpp:1449
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 setFileDef(FileDef *fd)=0
virtual void setPrototype(bool p, const DString &df, int line, size_t column)=0
virtual void setRelatedAlso(ClassDef *cd)=0
virtual void setAnonymousMemberForDeclaration(const MemberDef *md)=0
virtual void overrideReferencesRelation(bool e)=0
virtual void setAnonymousMemberForDetails(const MemberDef *md)=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 setNamespace(NamespaceDef *nd)=0
virtual void moveArgumentList(std::unique_ptr< ArgumentList > al)=0
virtual void makeRelated()=0
virtual void insertReimplementedBy(MemberDef *md)=0
virtual void setAnchor()=0
A list of MemberDef objects as shown in documentation sections.
Definition memberlist.h:126
Wrapper class for the MemberListType type.
Definition types.h:346
Ptr & front()
Definition membername.h:49
size_t size() const
Definition membername.h:46
iterator begin()
Definition membername.h:35
iterator end()
Definition membername.h:36
void push_back(Ptr &&p)
Definition membername.h:52
Ordered dictionary of MemberName objects.
Definition membername.h:61
MemberName::Ptr take(const DString &key, const MemberDef *value)
Definition membername.h:63
const MemberDef * findRev(const DString &name) const
Definition memberlist.h:107
const MemberDef * find(const DString &name) const
Definition memberlist.h:94
void run()
Run mmdc tool for all collected diagrams.
Definition mermaid.cpp:255
static MermaidManager & instance()
Definition mermaid.cpp:38
void sortMemberLists()
static ModuleManager & instance()
void addDocs(const Entry *root)
void addConceptToModule(const Entry *root, ConceptDef *cd)
void addClassToModule(const Entry *root, ClassDef *cd)
void addMemberToModule(const Entry *root, MemberDef *md)
void writeDocumentation(OutputList &ol)
void addMembersToMemberGroup()
void findSectionsInDocumentation()
void distributeMemberGroupDocumentation()
An abstract interface of a namespace symbol.
virtual const LinkedRefMap< NamespaceDef > & getUsedNamespaces() const =0
virtual bool isInline() const =0
virtual void insertUsedFile(FileDef *fd)=0
virtual void findSectionsInDocumentation()=0
virtual void countMembers()=0
virtual void addUsingDirective(NamespaceDef *nd)=0
virtual void setFileName(const DString &fn)=0
virtual void insertMember(MemberDef *md)=0
virtual void setMetaData(const DString &m)=0
virtual void addUsingDeclaration(const Definition *d)=0
virtual void distributeMemberGroupDocumentation()=0
virtual void writeTagFile(TextStream &)=0
virtual void writeDocumentation(OutputList &ol)=0
virtual void setInline(bool isInline)=0
virtual void computeAnchors()=0
virtual void combineUsingRelations(NamespaceDefSet &visitedNamespace)=0
virtual void sortMemberLists()=0
virtual void addMembersToMemberGroup()=0
/dev/null outline parser
void parseInput(const DString &, const char *, const std::shared_ptr< Entry > &, ClangTUParser *) override
Parses a single input file with the goal to build an Entry tree.
void parsePrototype(const DString &) override
Callback function called by the comment block scanner.
bool needsPreprocessing(const DString &) const override
Returns true if the language identified by extension needs the C preprocessor to be run before feed t...
Abstract interface for outline parsers.
Definition parserintf.h:41
virtual bool needsPreprocessing(const DString &extension) const =0
Returns true if the language identified by extension needs the C preprocessor to be run before feed t...
virtual void parseInput(const DString &fileName, const char *fileBuf, const std::shared_ptr< Entry > &root, ClangTUParser *clangParser)=0
Parses a single input file with the goal to build an Entry tree.
Class representing a list of output generators that are written to in parallel.
Definition outputlist.h:311
void disable(OutputType o)
void add()
Definition outputlist.h:348
void enable(OutputType o)
size_t size() const
Definition outputlist.h:357
void docify(const DString &s)
Definition outputlist.h:433
void writeStyleInfo(int part)
Definition outputlist.h:391
void generateDoc(const DString &fileName, int startLine, const Definition *ctx, const MemberDef *md, const DString &docStr, const DocOptions &options)
void cleanup()
Definition outputlist.h:748
void startContents()
Definition outputlist.h:614
A model of a page symbol.
Definition pagedef.h:27
virtual void setLocalToc(const LocalToc &tl)=0
virtual void setFileName(const DString &name)=0
virtual void setShowLineNo(bool)=0
virtual void setPageScope(Definition *)=0
virtual const GroupDef * getGroupDef() const =0
Manages programming language parsers.
Definition parserintf.h:182
std::unique_ptr< CodeParserInterface > getCodeParser(const DString &extension)
Gets the interface to the parser associated with a given extension.
Definition parserintf.h:253
void registerParser(const DString &name, const OutlineParserFactory &outlineParserFactory, const CodeParserFactory &codeParserFactory)
Registers an additional parser.
Definition parserintf.h:215
std::unique_ptr< OutlineParserInterface > getOutlineParser(const DString &extension)
Gets the interface to the parser associated with a given extension.
Definition parserintf.h:244
static PlantumlManager & instance()
Definition plantuml.cpp:236
void run()
Run plant UML tool for all images.
Definition plantuml.cpp:432
void processFile(const DString &fileName, const std::string &input, std::string &output)
Definition pre.l:4302
void addSearchDir(const DString &dir)
Definition pre.l:4284
Definition qhp.h:29
static DString getQchFileName()
Definition qhp.cpp:430
static const DString qhpFileName
Definition qhp.h:49
Generator for RTF output.
Definition rtfgen.h:80
static void init()
Definition rtfgen.cpp:464
static bool preProcessFileInplace(const DString &path, const DString &name)
This is an API to a VERY brittle RTF preprocessor that combines nested RTF files.
Definition rtfgen.cpp:2468
static void writeStyleSheetFile(TextStream &t)
Definition rtfgen.cpp:397
static void writeExtensionsFile(TextStream &t)
Definition rtfgen.cpp:412
static RefListManager & instance()
Definition reflist.h:122
static RequirementManager & instance()
void writeTagFile(TextStream &tagFile)
void addRequirement(Entry *e)
Abstract proxy interface for non-javascript based search indices.
void addWord(const DString &word, bool hiPriority)
void setCurrentDoc(const Definition *ctx, const DString &anchor, bool isSourceFile)
void write(const DString &file)
class that provide information about a section.
Definition section.h:58
DString fileName() const
Definition section.h:74
int lineNr() const
Definition section.h:73
DString ref() const
Definition section.h:72
SectionInfo * replace(const DString &label, const DString &fileName, int lineNr, const DString &title, SectionType type, int level, const DString &ref=DString())
Definition section.h:157
SectionInfo * add(const SectionInfo &si)
Definition section.h:139
static SectionManager & instance()
returns a reference to the singleton
Definition section.h:179
static constexpr int Page
Definition section.h:31
std::vector< stat > stats
Definition doxygen.cpp:273
void print()
Definition doxygen.cpp:250
void begin(const char *name)
Definition doxygen.cpp:237
void end()
Definition doxygen.cpp:243
std::chrono::steady_clock::time_point startTime
Definition doxygen.cpp:274
static void showCacheUsage()
Show usage of the type lookup cache.
static void clearTypeLookupCache(ClearScope scope)
ClassDefMutable * resolveClassMutable(const Definition *scope, const DString &name, bool mayBeUnlinkable=false, bool mayBeHidden=false)
Wrapper around resolveClass that returns a mutable interface to the class object or a nullptr if the ...
const ClassDef * resolveClass(const Definition *scope, const DString &name, bool maybeUnlinkable=false, bool mayBeHidden=false)
Find the class definition matching name within the scope set.
const Definition * resolveSymbol(const Definition *scope, const DString &name, const DString &args=DString(), bool checkCV=false, bool insideCode=false, bool onlyLinkable=false)
Find the symbool definition matching name within the scope set.
DString getTemplateSpec() const
In case a call to resolveClass() points to a template specialization, the template part is return via...
const MemberDef * getTypedef() const
In case a call to resolveClass() resolves to a type member (e.g. an enum) this method will return it.
This struct is used to capture the tag file information for an Entry.
Definition entry.h:101
DString tagName
Definition entry.h:103
DString fileName
Definition entry.h:104
Text streaming class that buffers data.
Definition textstream.h:36
std::string str() const
Return the contents of the buffer as a std::string object.
Definition textstream.h:232
Class managing a pool of worker threads.
Definition threadpool.h:48
auto queue(F &&f, Args &&... args) -> std::future< decltype(f(args...))>
Queue the callable function f for the threads to execute.
Definition threadpool.h:77
virtual DString trOverloadText()=0
virtual DString trPage(bool first_capital, bool singular)=0
virtual DString trMainPage()=0
Wrapper class for a number of boolean properties.
Definition types.h:694
std::string to_string() const
Definition types.h:738
static void correctMemberProperties(MemberDefMutable *md)
static void computeVhdlComponentRelations()
ClassDefMutable * toClassDefMutable(Definition *d)
ClassDef * getClass(const DString &n)
std::unique_ptr< ClassDef > createClassDefAlias(const Definition *newScope, const ClassDef *cd)
Definition classdef.cpp:816
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:583
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:468
Class representing a regular expression.
Definition regex.h:39
Class to iterate through matches.
Definition regex.h:245
Object representing the matching results.
Definition regex.h:160
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:329
std::unique_ptr< ArgumentList > stringToArgumentList(SrcLangExt lang, const DString &argsString, DString *extraTypeChars=nullptr)
Definition defargs.l:821
void generateDEF()
Definition defgen.cpp:463
std::unordered_map< std::string, DefineList > DefinesPerFileList
Definition define.h:50
Definition * toDefinition(DefinitionMutable *dm)
DefinitionMutable * toDefinitionMutable(Definition *d)
DirIterator begin(DirIterator it) noexcept
Definition dir.cpp:176
DirIterator end(const DirIterator &) noexcept
Definition dir.cpp:181
void buildDirectories()
Definition dirdef.cpp:1097
void computeDirDependencies()
Definition dirdef.cpp:1171
void generateDirDocs(OutputList &ol)
Definition dirdef.cpp:1188
#define AUTO_TRACE_ADD(...)
Definition docnode.cpp:54
#define AUTO_TRACE(...)
Definition docnode.cpp:53
#define AUTO_TRACE_EXIT(...)
Definition docnode.cpp:55
constexpr DocNodeVariant * parent(DocNodeVariant *n)
returns the parent node of a given node n or nullptr if the node has no parent.
Definition docnode.h:1335
static void findInheritedTemplateInstances()
Definition doxygen.cpp:5550
void printNavTree(Entry *root, int indent)
static void addClassToContext(const Entry *root)
Definition doxygen.cpp:950
static void makeTemplateInstanceRelation(const Entry *root, ClassDefMutable *cd)
Definition doxygen.cpp:5565
static StringUnorderedSet g_pathsVisited(1009)
static void buildGroupList(const Entry *root)
Definition doxygen.cpp:540
static void insertMemberAlias(Definition *outerScope, const MemberDef *md)
Definition doxygen.cpp:6947
static void findUsingDeclarations(const Entry *root, bool filterPythonPackages)
Definition doxygen.cpp:2323
static void flushCachedTemplateRelations()
Definition doxygen.cpp:9733
static void copyLatexStyleSheet()
static void generateDocsForClassList(const std::vector< ClassDefMutable * > &classList)
Definition doxygen.cpp:9393
static int findFunctionPtr(const std::string &type, SrcLangExt lang, int *pLength=nullptr)
Definition doxygen.cpp:3167
static bool isSpecialization(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists)
Definition doxygen.cpp:6236
static void computeTemplateClassRelations()
Definition doxygen.cpp:5644
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:8408
static void runQHelpGenerator()
static void addConceptToContext(const Entry *root)
Definition doxygen.cpp:1175
static void addRelatedPage(Entry *root)
Definition doxygen.cpp:436
void initDoxygen()
static StringVector g_inputFiles
Definition doxygen.cpp:191
void printSectionsTree()
class Statistics g_s
static void generateXRefPages()
Definition doxygen.cpp:5823
static Definition * buildScopeFromQualifiedName(const DString &name_, SrcLangExt lang, const TagInfo *tagInfo)
Definition doxygen.cpp:724
static void findUsingDeclImports(const Entry *root)
Definition doxygen.cpp:2476
static void copyStyleSheet()
static void addIncludeFile(Def *def, FileDef *ifd, const Entry *root)
Definition doxygen.cpp:311
FindBaseClassRelation_Mode
Definition doxygen.cpp:290
@ Undocumented
Definition doxygen.cpp:293
@ TemplateInstances
Definition doxygen.cpp:291
@ DocumentedOnly
Definition doxygen.cpp:292
void distributeClassGroupRelations()
Definition doxygen.cpp:1511
static void generateGroupDocs()
static void findDirDocumentation(const Entry *root)
Definition doxygen.cpp:9933
void checkConfiguration()
check and resolve config options
static bool findClassRelation(const Entry *root, Definition *context, ClassDefMutable *cd, const BaseInfo *bi, const TemplateNameMap &templateNames, FindBaseClassRelation_Mode mode, bool isArtificial)
Definition doxygen.cpp:5151
static void resolveTemplateInstanceInType(const Entry *root, const Definition *scope, const MemberDef *md)
Definition doxygen.cpp:5081
static void organizeSubGroupsFiltered(const Entry *root, bool additional)
Definition doxygen.cpp:580
static void warnUndocumentedNamespaces()
Definition doxygen.cpp:5599
static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments, const std::string &name)
Definition doxygen.cpp:4760
static void buildConceptList(const Entry *root)
Definition doxygen.cpp:1336
static void addMemberSpecialization(const Entry *root, MemberName *mn, ClassDefMutable *cd, const DString &funcType, const DString &funcName, const DString &funcArgs, const DString &funcDecl, const DString &exceptions, TypeSpecifier spec)
Definition doxygen.cpp:6814
static void resolveClassNestingRelations()
Definition doxygen.cpp:1391
static void generateNamespaceConceptDocs(const ConceptLinkedRefMap &conceptList)
static MemberDef * addVariableToFile(const Entry *root, MemberType mtype, const DString &scope, const DString &type, const DString &name, const DString &args)
Definition doxygen.cpp:2914
static void findClassEntries(const Entry *root)
Definition doxygen.cpp:5515
static void vhdlCorrectMemberProperties()
Definition doxygen.cpp:8652
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:6449
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:6350
static void copyLogo(const DString &outputOption, bool toIndex)
static void computeMemberReferences()
Definition doxygen.cpp:5713
static void transferRelatedFunctionDocumentation()
Definition doxygen.cpp:4674
static void addMembersToMemberGroup()
Definition doxygen.cpp:9598
static void findMainPageTagFiles(Entry *root)
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:6394
static void distributeConceptGroups()
Definition doxygen.cpp:1358
static NamespaceDef * findUsedNamespace(const LinkedRefMap< NamespaceDef > &unl, const DString &name)
Definition doxygen.cpp:2153
static void transferFunctionDocumentation()
Definition doxygen.cpp:4593
static void setAnonymousEnumType()
Definition doxygen.cpp:9338
static void sortMemberLists()
Definition doxygen.cpp:9243
static void findMember(const Entry *root, const DString &relates, const DString &type, const DString &args, DString funcDecl, bool overloaded, bool isFunc)
Definition doxygen.cpp:6990
static void createTemplateInstanceMembers()
Definition doxygen.cpp:8820
void transferStaticInstanceInitializers()
Definition doxygen.cpp:4723
static void findObjCMethodDefinitions(const Entry *root)
Definition doxygen.cpp:7796
static void addMemberDocs(const Entry *root, MemberDefMutable *md, const DString &funcDecl, const ArgumentList *al, bool over_load, TypeSpecifier spec)
Definition doxygen.cpp:5837
static void dumpSymbolMap()
static void buildTypedefList(const Entry *root)
Definition doxygen.cpp:3659
static void findGroupScope(const Entry *root)
Definition doxygen.cpp:555
static void generateFileDocs()
Definition doxygen.cpp:9056
static int findEndOfTemplate(const DString &s, size_t startPos)
Definition doxygen.cpp:3370
static void findDefineDocumentation(Entry *root)
Definition doxygen.cpp:9846
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:4823
void parseInput()
static void findMemberDocumentation(const Entry *root)
Definition doxygen.cpp:7766
static void distributeMemberGroupDocumentation()
Definition doxygen.cpp:9636
static void generateNamespaceClassDocs(const ClassLinkedRefMap &classList)
static void addEnumValuesToEnums(const Entry *root)
Definition doxygen.cpp:8000
static void generatePageDocs()
static void resolveUserReferences()
static void buildRequirementsList(Entry *root)
Definition doxygen.cpp:9993
static void compareDoxyfile(Config::CompareMode diffList)
static void addPageToContext(PageDef *pd, Entry *root)
Definition doxygen.cpp:417
static std::shared_ptr< Entry > parseFile(OutlineParserInterface &parser, FileDef *fd, const DString &fn, ClangTUParser *clangParser, bool newTU)
static void buildVarList(const Entry *root)
Definition doxygen.cpp:3796
static void copyIcon(const DString &outputOption, bool toIndex)
static void buildSequenceList(const Entry *root)
Definition doxygen.cpp:3759
static void generateFileSources()
Definition doxygen.cpp:8890
static void copyExtraFiles(StringVector files, const DString &filesOption, const DString &outputOption, bool toIndex)
static void generateClassDocs()
Definition doxygen.cpp:9491
static int findTemplateSpecializationPosition(const DString &name)
Definition doxygen.cpp:5121
static void buildNamespaceList(const Entry *root)
Definition doxygen.cpp:1982
static void findIncludedUsingDirectives()
Definition doxygen.cpp:2726
static void addDefineDoc(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:9819
static void countMembers()
Definition doxygen.cpp:9352
void clearAll()
Definition doxygen.cpp:205
static void devUsage()
static ClassDef * findClassWithinClassContext(Definition *context, ClassDef *cd, const DString &name)
Definition doxygen.cpp:4788
static void organizeSubGroups(const Entry *root)
Definition doxygen.cpp:599
static void applyMemberOverrideOptions(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:2415
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:6878
static void findFriends()
Definition doxygen.cpp:4496
static void findEnums(const Entry *root)
Definition doxygen.cpp:7824
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:9468
static ClassDefMutable * createTagLessInstance(const Definition *root, const ClassDef *templ, const DString &fieldName, bool hasNamedVariable)
Definition doxygen.cpp:1572
static void addEnumDocs(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:8248
static void addListReferences()
Definition doxygen.cpp:5814
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:6028
static bool isClassSection(const Entry *root)
Definition doxygen.cpp:5493
static void processTagLessClasses(const Definition *root, const Container *def, 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:1897
static void buildGroupListFiltered(const Entry *root, bool additional, bool includeExternal)
Definition doxygen.cpp:467
static void runHtmlHelpCompiler()
static void addMembersToIndex()
Definition doxygen.cpp:8442
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:5524
static void findMainPage(Entry *root)
static ClassDef::CompoundType convertToCompoundType(EntryType section, TypeSpecifier specifier)
Definition doxygen.cpp:908
DString stripTemplateSpecifiers(const DString &s)
Definition doxygen.cpp:697
static void findUsingDirectives(const Entry *root)
Definition doxygen.cpp:2166
static void addInterfaceOrServiceToServiceOrSingleton(const Entry *root, ClassDefMutable *cd, DString const &rname)
Definition doxygen.cpp:3828
static bool g_successfulRun
Definition doxygen.cpp:194
static bool tryAddEnumDocsToGroupMember(const Entry *root, const DString &name)
Definition doxygen.cpp:8290
static void addSourceReferences()
Definition doxygen.cpp:9116
static void associateVariableWithAnonymousEnumType(const MemberDef *md, const Container *cd, const MemberDef *enumTypeMember, MemberListType mlFilter)
Definition doxygen.cpp:1545
static void createUsingMemberImportForClass(const Entry *root, ClassDefMutable *cd, const MemberDef *md, const DString &fileName, const DString &memName)
Definition doxygen.cpp:2427
static DString substituteTemplatesInString(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists, const std::string &src)
Definition doxygen.cpp:6266
std::function< std::unique_ptr< T >() > make_parser_factory()
static void buildExampleList(Entry *root)
static void inheritDocumentation()
Definition doxygen.cpp:9538
static void flushUnresolvedRelations()
Definition doxygen.cpp:9775
static bool isSymbolHidden(const Definition *d)
Definition doxygen.cpp:9285
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:5030
static void findDocumentedEnumValues()
Definition doxygen.cpp:8434
static void findTagLessClasses()
Definition doxygen.cpp:1930
static void generateDiskNames()
static void addToIndices()
Definition doxygen.cpp:8484
static void computeClassRelations()
Definition doxygen.cpp:5619
static void buildFunctionList(const Entry *root)
Definition doxygen.cpp:4191
static void checkPageRelations()
static void addGlobalFunction(const Entry *root, const DString &rname, const DString &sc)
Definition doxygen.cpp:4081
static void findModuleDocumentation(const Entry *root)
Definition doxygen.cpp:1326
static MemberDef * addVariableToClass(const Entry *root, ClassDefMutable *cd, MemberType mtype, const DString &type, const DString &name, const DString &args, Protection prot, Relationship related)
Definition doxygen.cpp:2743
static void readTagFile(const std::shared_ptr< Entry > &root, const DString &tagLine)
static void findEnumDocumentation(const Entry *root)
Definition doxygen.cpp:8323
static void computePageRelations(Entry *root)
static void processTagLessClassesForList(const Definition *root, const Container *def, const TagContainer *tagParent, MemberListType varFilter, MemberListType typeFilter, const DString &prefix, int count)
Definition doxygen.cpp:1790
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:3944
void initResources()
static bool isVarWithConstructor(const Entry *root)
Definition doxygen.cpp:3227
static StringSet g_usingDeclarations
Definition doxygen.cpp:193
static void buildDictionaryList(const Entry *root)
Definition doxygen.cpp:3777
static bool haveEqualFileNames(const Entry *root, const MemberDef *md)
Definition doxygen.cpp:9808
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 computeMemberRelationsForBaseClass(const ClassDef *cd, const BaseClassDef *bcd, const ArgumentList *actualArgs)
Definition doxygen.cpp:8684
static void buildClassDocList(const Entry *root)
Definition doxygen.cpp:1161
static void buildPageList(Entry *root)
static void writeTagFile()
static void addRequirementReferences()
Definition doxygen.cpp:5806
static void computeVerifiedDotPath()
static bool g_singleComment
Definition doxygen.cpp:197
static void findSectionsInDocumentation()
Definition doxygen.cpp:9674
static void mergeCategories()
Definition doxygen.cpp:8839
static const StringUnorderedSet g_compoundKeywords
Definition doxygen.cpp:202
static bool scopeIsTemplate(const Definition *d)
Definition doxygen.cpp:6252
static void buildFileList(const Entry *root)
Definition doxygen.cpp:611
static void buildClassList(const Entry *root)
Definition doxygen.cpp:1151
static void usage(const DString &name, const DString &versionString)
static void findUsedTemplateInstances()
Definition doxygen.cpp:5583
static void computeTooltipTexts()
Definition doxygen.cpp:9292
static void addVariable(const Entry *root, int isFuncPtr=-1)
Definition doxygen.cpp:3438
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:4979
static void parseFilesSingleThreading(const std::shared_ptr< Entry > &root)
parse the list of input files
static void buildCompleteMemberLists()
Definition doxygen.cpp:8860
static const ClassDef * findClassDefinition(FileDef *fd, NamespaceDef *nd, const DString &scopeName)
Definition doxygen.cpp:5989
static void filterMemberDocumentation(const Entry *root, const DString &relates)
Definition doxygen.cpp:7617
static void generateConceptDocs()
Definition doxygen.cpp:9517
static bool isRecursiveBaseClass(const DString &scope, const DString &name)
Definition doxygen.cpp:5110
static std::unique_ptr< OutlineParserInterface > getParserForFile(const DString &fn)
static void combineUsingRelations()
Definition doxygen.cpp:9573
static const char * getArg(int argc, char **argv, int &optInd)
std::unique_ptr< ArgumentList > getTemplateArgumentsFromName(const DString &name, const ArgumentLists &tArgLists)
Definition doxygen.cpp:878
static void checkMarkdownMainfile()
static std::unordered_map< std::string, std::vector< ClassDefMutable * > > g_usingClassMap
Definition doxygen.cpp:2474
static void buildConceptDocList(const Entry *root)
Definition doxygen.cpp:1346
static bool isEntryInGroupOfMember(const Entry *root, const MemberDef *md, bool allowNoGroup=false)
Definition doxygen.cpp:6004
static void applyToAllDefinitions(Func func)
Definition doxygen.cpp:5749
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:4626
static void buildDefineList()
Definition doxygen.cpp:9195
static void buildInterfaceAndServiceList(const Entry *root)
Definition doxygen.cpp:3892
static Definition * findScopeFromQualifiedName(NamespaceDefMutable *startScope, const DString &n, FileDef *fileScope, const TagInfo *tagInfo)
Definition doxygen.cpp:793
static void computeMemberRelations()
Definition doxygen.cpp:8803
static void buildListOfUsingDecls(const Entry *root)
Definition doxygen.cpp:2310
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:2033
std::unique_ptr< FileDef > createFileDef(const DString &p, const DString &n, const DString &ref, const DString &dn)
Definition filedef.cpp:269
std::unordered_set< const FileDef * > FileDefSet
Definition filedef.h:41
void addNamespaceToGroups(const Entry *root, NamespaceDef *nd)
void addGroupToGroups(const Entry *root, GroupDef *subGroup)
void addClassToGroups(const Entry *root, ClassDef *cd)
void addDirToGroups(const Entry *root, DirDef *dd)
std::unique_ptr< GroupDef > createGroupDef(const DString &fileName, int line, const DString &name, const DString &title, const DString &refFileName)
Definition groupdef.cpp:184
void addConceptToGroups(const Entry *root, ConceptDef *cd)
void addMemberToGroups(const Entry *root, MemberDef *md)
void writeGraphInfo(OutputList &ol)
Definition index.cpp:4097
void endTitle(OutputList &ol, const DString &fileName, const DString &name)
Definition index.cpp:398
void writeIndexHierarchy(OutputList &ol)
Definition index.cpp:5812
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:24
void writeDefaultLayoutFile(const DString &fileName)
Definition layout.cpp:1746
void printLayout()
Definition layout.cpp:1834
std::unique_ptr< MemberDef > createMemberDef(const DString &defFileName, int defLine, size_t defColumn, const DString &type, const DString &name, const DString &args, const DString &excp, Protection prot, Specifier virt, bool stat, Relationship related, MemberType t, const ArgumentList &tal, const ArgumentList &al, const DString &metaData)
Factory method to create a new instance of a MemberDef.
std::unique_ptr< MemberDef > createMemberDefAlias(const Definition *newScope, const MemberDef *aliasMd)
MemberDefMutable * toMemberDefMutable(Definition *d)
void mergeMemberOverrideOptions(MemberDefMutable *md1, MemberDefMutable *md2)
void combineDeclarationAndDefinition(MemberDefMutable *mdec, MemberDefMutable *mdef)
MemberDef * toMemberDef(Definition *d)
std::unordered_map< int, std::unique_ptr< MemberGroupInfo > > MemberGroupInfoMap
#define DOX_NOGROUP
Definition membergroup.h:26
void initWarningFormat()
Definition message.cpp:233
void warn_flush()
Definition message.cpp:226
DString warn_line(const DString &file, int line)
Definition message.cpp:211
void finishWarnExit()
Definition message.cpp:291
#define warn_undoc(file, line, fmt,...)
Definition message.h:102
#define warn_uncond(fmt,...)
Definition message.h:122
#define warn(file, line, fmt,...)
Definition message.h:97
#define msg(fmt,...)
Definition message.h:94
#define err(fmt,...)
Definition message.h:127
#define ASSERT(x)
Definition message.h:142
#define term(fmt,...)
Definition message.h:137
void postProcess(bool clearHeaderAndFooter, CompareMode compareMode=CompareMode::Full)
CompareMode
Definition config.h:54
void checkAndCorrect(bool quiet, const bool check)
void compareDoxyfile(TextStream &t, CompareMode compareMode)
void writeTemplate(TextStream &t, bool shortList, bool updateOnly=false)
void deinit()
void init()
void updateObsolete()
bool parse(const DString &fileName, bool update=false, CompareMode compareMode=CompareMode::Full)
void correctPath(const StringVector &list)
Correct a possible wrong PATH variable.
Definition portable.cpp:532
FILE * popen(const DString &name, const DString &type)
Definition portable.cpp:495
double getSysElapsedTime()
Definition portable.cpp:113
void setenv(const DString &variable, const DString &value)
Definition portable.cpp:302
uint32_t pid()
Definition portable.cpp:264
int system(const DString &command, const DString &args, bool commandHasConsole=true)
Definition portable.cpp:121
int pclose(FILE *stream)
Definition portable.cpp:504
DString getenv(const DString &variable)
Definition portable.cpp:337
bool isAbsolutePath(const DString &fileName)
Definition portable.cpp:513
DString pathListSeparator()
Definition portable.cpp:399
std::ofstream openOutputStream(const DString &name, bool append=false)
Definition portable.cpp:681
const char * commandExtension()
Definition portable.cpp:477
void setShortDir()
Definition portable.cpp:569
DString trunc(const DString &s, size_t numChars=15)
Definition trace.h:56
std::unique_ptr< NamespaceDef > createNamespaceDef(const DString &defFileName, int defLine, size_t defColumn, const DString &name, const DString &ref, const DString &refFile, const DString &type, bool isPublished)
Factory method to create new NamespaceDef instance.
std::unique_ptr< NamespaceDef > createNamespaceDefAlias(const Definition *newScope, const NamespaceDef *nd)
Factory method to create an alias of an existing namespace.
void replaceNamespaceAliases(DString &name)
NamespaceDef * getResolvedNamespace(const DString &name)
NamespaceDef * toNamespaceDef(Definition *d)
NamespaceDefMutable * toNamespaceDefMutable(Definition *d)
NamespaceDefMutable * getResolvedNamespaceMutable(const DString &key)
std::unordered_set< const NamespaceDef * > NamespaceDefSet
bool search(std::string_view str, Match &match, const Ex &re, size_t pos)
Search in a given string str starting at position pos for a match against regular expression re.
Definition regex.cpp:850
std::unique_ptr< PageDef > createPageDef(const DString &f, int l, const DString &n, const DString &d, const DString &t)
Definition pagedef.cpp:89
void generatePerlMod()
void setPerlModDoxyfile(const DString &qs)
Portable versions of functions that are platform dependent.
int portable_iconv_close(void *cd)
void * portable_iconv_open(const char *tocode, const char *fromcode)
void addRefItem(const RefItemVector &sli, const DString &key, const DString &prefix, const DString &name, const DString &title, const DString &args, const Definition *scope)
Definition reflist.h:136
void initSearchIndexer()
void finalizeSearchIndexer()
void createJavaScriptSearchIndex()
void writeJavaScriptSearchIndex()
Javascript based search engine.
void generateHtmlForComment(const std::string &fn, const std::string &text)
Helper for implemented the -c option of doxygen, which produces HTML output for a given doxygen forma...
void generateSqlite3()
void addSTLSupport(std::shared_ptr< Entry > &root)
Add stub entries for the most used classes in the standard template library.
Some helper functions for std::string.
void addTerminalCharIfMissing(std::string &s, char c)
Definition stringutil.h:87
Class that contains information about an inheritance relation.
Definition classdef.h:51
ClassDef * classDef
Class definition that this relation inherits from.
Definition classdef.h:56
This class stores information about an inheritance relation.
Definition entry.h:88
Protection prot
inheritance type
Definition entry.h:93
Specifier virt
virtualness
Definition entry.h:94
DString name
the name of the base class
Definition entry.h:92
Data associated with description found in the body.
Definition definition.h:64
Helper class to pass options when calling OutputList::generateDoc().
Definition docoptions.h:24
Grouping info.
Definition types.h:227
DString groupname
name of the group
Definition types.h:257
static constexpr const char * getGroupPriName(GroupPri_t priority) noexcept
Definition types.h:240
@ GROUPING_INGROUP
membership in group was defined by @ingroup
Definition types.h:236
GroupPri_t pri
priority of this definition
Definition types.h:258
static bool execute(const DString &htmldir)
Definition htags.cpp:41
static bool loadFilemap(const DString &htmldir)
Definition htags.cpp:110
static bool useHtags
Definition htags.h:23
stat(const char *n, double el)
Definition doxygen.cpp:271
const char * name
Definition doxygen.cpp:268
void parseTagFile(const std::shared_ptr< Entry > &root, const char *fullName)
void exitTracing()
Definition trace.cpp:55
void initTracing(const DString &logFile, bool timing)
Definition trace.cpp:25
#define TRACE(...)
Definition trace.h:77
MemberType
Definition types.h:569
bool isTypeAClassFriend(const DString &type)
Definition types.h:920
Protection
Definition types.h:32
SrcLangExt
Definition types.h:207
Relationship
Definition types.h:167
Specifier
Definition types.h:80
bool useCaseSenseNames()
Returns true if the names of the symbols can be case sensitive.
Definition util.cpp:2685
DString substituteTemplateArgumentsInString(const DString &nm, const ArgumentList &formalArgs, const ArgumentList *actualArgs)
Definition util.cpp:3530
bool protectionLevelVisible(Protection prot)
Definition util.cpp:4677
DString stripFromIncludePath(const DString &path)
Definition util.cpp:227
DString mergeScopes(const DString &leftScope, const DString &rightScope)
Definition util.cpp:3759
DString filterTitle(const DString &title)
Definition util.cpp:4460
bool matchTemplateArguments(const ArgumentList &srcAl, const ArgumentList &dstAl)
Definition util.cpp:1838
void addCodeOnlyMappings()
Definition util.cpp:4168
bool rightScopeMatch(const DString &scope, const DString &name)
Definition util.cpp:734
bool checkIfTypedef(const Definition *scope, const FileDef *fileScope, const DString &n)
Definition util.cpp:4277
DString replaceAnonymousScopes(const DString &s, const DString &replacement)
Definition util.cpp:155
void cleanupInlineGraphs()
Definition util.cpp:5428
int computeQualifiedIndex(const DString &name)
Return the index of the last :: in the string name that is still before the first <.
Definition util.cpp:5305
DString removeRedundantWhiteSpace(const DString &s)
Definition util.cpp:427
bool checkExtension(const DString &fName, const DString &ext)
Definition util.cpp:3932
DString convertNameToFile(const DString &name, bool allowDots, bool allowUnderscore)
Definition util.cpp:2869
bool leftScopeMatch(const DString &scope, const DString &name)
Definition util.cpp:745
DString tempArgListToString(const ArgumentList &al, SrcLangExt lang, bool includeDefault)
Definition util.cpp:908
DString getFileNameExtension(const DString &fn)
Definition util.cpp:4216
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:4380
DString normalizeNonTemplateArgumentsInString(const DString &name, const Definition *context, const ArgumentList &formalArgs)
Definition util.cpp:3471
SrcLangExt getLanguageFromFileName(const DString &fileName, SrcLangExt defLang)
Definition util.cpp:4174
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:1594
void initDefaultExtensionMapping()
Definition util.cpp:4101
void addHtmlExtensionIfMissing(DString &fName)
Definition util.cpp:3937
DString fileToString(const DString &name, bool filter, bool isSourceCode)
Definition util.cpp:1058
void extractNamespaceName(const DString &scopeName, DString &className, DString &namespaceName, bool allowEmptyClass)
Definition util.cpp:3032
DString stripTemplateSpecifiersFromScope(const DString &fullName, bool parentOnly, DString *pLastScopeStripped, DString scopeName, bool allowArtificial)
Definition util.cpp:3692
DString argListToString(const ArgumentList &al, bool useCanonicalType, bool showDefVals)
Definition util.cpp:864
DString projectLogoFile()
Definition util.cpp:2586
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:4625
int getPrefixIndex(const DString &name)
Definition util.cpp:2657
DString getLanguageSpecificSeparator(SrcLangExt lang, bool classScope)
Definition util.cpp:4635
bool updateLanguageMapping(const DString &extension, const DString &language)
Definition util.cpp:4069
DString mangleCSharpGenericName(const DString &name)
Definition util.cpp:5347
void mergeArguments(ArgumentList &srcAl, ArgumentList &dstAl, bool forceNameOverwrite)
Definition util.cpp:1694
int getScopeFragment(const DString &s, int p, int *l)
Definition util.cpp:3804
int extractClassNameFromType(const DString &type, int &pos, DString &name, DString &templSpec, SrcLangExt lang)
Definition util.cpp:3386
DString removeAnonymousScopes(const DString &str)
Definition util.cpp:98
bool openOutputFile(const DString &outFile, std::ofstream &f)
Definition util.cpp:4992
DString stripAnonymousNamespaceScope(const DString &s)
Definition util.cpp:167
A bunch of utility functions.
void generateXML()
Definition xmlgen.cpp:2357