Doxygen
Loading...
Searching...
No Matches
doxygen.cpp
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * Copyright (C) 1997-2015 by Dimitri van Heesch.
4 *
5 * Permission to use, copy, modify, and distribute this software and its
6 * documentation under the terms of the GNU General Public License is hereby
7 * granted. No representations are made about the suitability of this software
8 * for any purpose. It is provided "as is" without express or implied warranty.
9 * See the GNU General Public License for more details.
10 *
11 * Documents produced by Doxygen are derivative works derived from the
12 * input used in their production; they are not affected by this license.
13 *
14 */
15
16// own header
17#include "doxygen.h"
18
19// standard includes
20#include <algorithm>
21#include <cerrno>
22#include <chrono>
23#include <cinttypes>
24#include <clocale>
25#include <cstdio>
26#include <cstdlib>
27#include <locale>
28#include <memory>
29#include <sys/stat.h>
30#include <unordered_map>
31
32// other includes
33#include "aliases.h"
34#include "arguments.h"
35#include "cite.h"
36#include "clangparser.h"
37#include "classlist.h"
38#include "cmdmapper.h"
39#include "code.h"
40#include "commentcnv.h"
41#include "conceptdef.h"
42#include "config.h"
43#include "debug.h"
44#include "declinfo.h"
45#include "defargs.h"
46#include "defgen.h"
47#include "dir.h"
48#include "dirdef.h"
49#include "docbookgen.h"
50#include "docparser.h"
51#include "docsets.h"
52#include "dot.h"
53#include "eclipsehelp.h"
54#include "emoji.h"
55#include "entry.h"
56#include "fileinfo.h"
57#include "filename.h"
58#include "fileparser.h"
59#include "formula.h"
60#include "fortrancode.h"
61#include "fortranscanner.h"
62#include "ftvhelp.h"
63#include "groupdef.h"
64#include "htags.h"
65#include "htmlgen.h"
66#include "htmlhelp.h"
67#include "index.h"
68#include "indexlist.h"
69#include "language.h"
70#include "latexgen.h"
71#include "layout.h"
72#include "lexcode.h"
73#include "lexscanner.h"
74#include "mangen.h"
75#include "markdown.h"
76#include "membergroup.h"
77#include "memberlist.h"
78#include "membername.h"
79#include "mermaid.h"
80#include "message.h"
81#include "moduledef.h"
82#include "msc.h"
83#include "namespacedef.h"
84#include "outputlist.h"
85#include "pagedef.h"
86#include "parserintf.h"
87#include "perlmodgen.h"
88#include "plantuml.h"
89#include "portable.h"
90#include "pre.h"
91#include "pycode.h"
92#include "pyscanner.h"
93#include "qhp.h"
94#include "reflist.h"
95#include "regex.h"
96#include "requirement.h"
97#include "rtfgen.h"
98#include "scanner.h"
99#include "searchindex_js.h"
100#include "settings.h"
101#include "singlecomment.h"
102#include "sitemap.h"
103#include "sqlcode.h"
104#include "sqlite3gen.h"
105#include "stlsupport.h"
106#include "stringutil.h"
107#include "symbolresolver.h"
108#include "tagreader.h"
109#include "threadpool.h"
110#include "trace.h"
111#include "util.h"
112#include "version.h"
113#include "vhdlcode.h"
114#include "vhdldocgen.h"
115#include "vhdljjparser.h"
116#include "xmlcode.h"
117#include "xmlgen.h"
118
119#include <sqlite3.h>
120
121#if USE_LIBCLANG
122#if defined(__GNUC__)
123#pragma GCC diagnostic push
124#pragma GCC diagnostic ignored "-Wshadow"
125#endif
126#include <clang/Basic/Version.h>
127#if defined(__GNUC__)
128#pragma GCC diagnostic pop
129#endif
130#endif
131
132// provided by the generated file resources.cpp
133extern void initResources();
134
135#if !defined(_WIN32) || defined(__CYGWIN__)
136#include <signal.h>
137#define HAS_SIGNALS
138#endif
139
140// globally accessible variables
152FileNameLinkedMap *Doxygen::includeNameLinkedMap = nullptr; // include names
158FileNameLinkedMap *Doxygen::plantUmlFileNameLinkedMap = nullptr;// plantuml files
159FileNameLinkedMap *Doxygen::mermaidFileNameLinkedMap = nullptr; // mermaid files
161StringMap Doxygen::tagDestinationMap; // all tag locations
162StringUnorderedSet Doxygen::tagFileSet; // all tag file names
163StringUnorderedSet Doxygen::expandAsDefinedSet; // all macros that should be expanded
164MemberGroupInfoMap Doxygen::memberGroupInfoMap; // dictionary of the member groups heading
165std::unique_ptr<PageDef> Doxygen::mainPage;
166std::unique_ptr<NamespaceDef> Doxygen::globalNamespaceDef;
168bool Doxygen::parseSourcesNeeded = false;
186std::mutex Doxygen::addExampleMutex;
188
189// locally accessible globals
190static std::multimap< std::string, const Entry* > g_classEntries;
192static OutputList *g_outputList = nullptr; // list of output generating objects
193static StringSet g_usingDeclarations; // used classes
194static bool g_successfulRun = false;
195static bool g_dumpSymbolMap = false;
197static bool g_singleComment=false;
198
199
200
201// keywords recognized as compounds
203{ "template class", "template struct", "class", "struct", "union", "interface", "exception" };
204
232
234{
235 public:
237 void begin(const char *name)
238 {
239 msg("{}", name);
240 stats.emplace_back(name,0);
241 startTime = std::chrono::steady_clock::now();
242 }
243 void end()
244 {
245 std::chrono::steady_clock::time_point endTime = std::chrono::steady_clock::now();
246 stats.back().elapsed = static_cast<double>(std::chrono::duration_cast<
247 std::chrono::microseconds>(endTime - startTime).count())/1000000.0;
248 warn_flush();
249 }
250 void print()
251 {
252 bool restore=false;
254 {
256 restore=true;
257 }
258 msg("----------------------\n");
259 for (const auto &s : stats)
260 {
261 msg("Spent {:.6f} seconds in {}",s.elapsed,s.name);
262 }
263 if (restore) Debug::setFlag(Debug::Time);
264 }
265 private:
266 struct stat
267 {
268 const char *name;
269 double elapsed;
270 //stat() : name(nullptr),elapsed(0) {}
271 stat(const char *n, double el) : name(n),elapsed(el) {}
272 };
273 std::vector<stat> stats;
274 std::chrono::steady_clock::time_point startTime;
276
277
278static void addMemberDocs(const Entry *root,MemberDefMutable *md, const DString &funcDecl,
279 const ArgumentList *al,bool over_load,TypeSpecifier spec);
280static void findMember(const Entry *root,
281 const DString &relates,
282 const DString &type,
283 const DString &args,
284 DString funcDecl,
285 bool overloaded,
286 bool isFunc
287 );
288
295
296
297static bool findClassRelation(
298 const Entry *root,
299 Definition *context,
300 ClassDefMutable *cd,
301 const BaseInfo *bi,
302 const TemplateNameMap &templateNames,
303 /*bool insertUndocumented*/
305 bool isArtificial
306 );
307
308//----------------------------------------------------------------------------
309
311 FileDef *fileScope,const TagInfo *tagInfo);
312static void resolveTemplateInstanceInType(const Entry *root,const Definition *scope,const MemberDef *md);
313
314static void addPageToContext(PageDef *pd,Entry *root)
315{
316 if (root->parent()) // add the page to it's scope
317 {
318 DString scope = root->parent()->name;
319 if (root->parent()->section.isPackageDoc())
320 {
321 scope=substitute(scope,".","::");
322 }
323 scope = stripAnonymousNamespaceScope(scope);
324 scope+="::"+pd->name();
326 if (d)
327 {
328 pd->setPageScope(d);
329 }
330 }
331}
332
333static void addRelatedPage(Entry *root)
334{
335 GroupDef *gd=nullptr;
336 for (const Grouping &g : root->groups)
337 {
338 if (!g.groupname.empty() && (gd=Doxygen::groupLinkedMap->find(g.groupname))) break;
339 }
340 //printf("---> addRelatedPage() %s gd=%p\n",qPrint(root->name),gd);
341 DString doc=root->doc+root->inbodyDocs;
342
343 PageDef *pd = addRelatedPage(root->name, // name
344 root->args, // ptitle
345 doc, // doc
346 root->docFile, // fileName
347 root->docLine, // docLine
348 root->startLine, // startLine
349 root->sli, // sli
350 gd, // gd
351 root->tagInfo(), // tagInfo
352 false, // xref
353 root->lang // lang
354 );
355 if (pd)
356 {
357 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
359 pd->setLocalToc(root->localToc);
360 addPageToContext(pd,root);
361 }
362}
363
364static void buildGroupListFiltered(const Entry *root,bool additional, bool includeExternal)
365{
366 if (root->section.isGroupDoc() && !root->name.empty() &&
367 ((!includeExternal && root->tagInfo()==nullptr) ||
368 ( includeExternal && root->tagInfo()!=nullptr))
369 )
370 {
371 AUTO_TRACE("additional={} includeExternal={}",additional,includeExternal);
372 if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
373 (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
374 {
376 AUTO_TRACE_ADD("Processing group '{}':'{}' gd={}", root->type,root->name,(void*)gd);
377
378 if (gd)
379 {
380 if ( !gd->hasGroupTitle() )
381 {
382 gd->setGroupTitle( root->type );
383 }
384 else if ( root->type.length() > 0 && root->name != root->type && gd->groupTitle() != root->type )
385 {
386 warn( root->fileName,root->startLine,
387 "group {}: ignoring title \"{}\" that does not match old title \"{}\"",
388 root->name, root->type, gd->groupTitle() );
389 }
390 gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
391 gd->setDocumentation( root->doc, root->docFile, root->docLine );
392 gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
394 gd->setRefItems(root->sli);
396 gd->setLanguage(root->lang);
398 {
399 root->commandOverrides.apply_groupGraph([&](bool b) { gd->overrideGroupGraph(b); });
400 }
401 }
402 else
403 {
404 if (root->tagInfo())
405 {
406 gd = Doxygen::groupLinkedMap->add(root->name,
407 std::unique_ptr<GroupDef>(
408 createGroupDef(root->fileName,root->startLine,root->name,root->type,root->tagInfo()->fileName)));
409 gd->setReference(root->tagInfo()->tagName);
410 }
411 else
412 {
413 gd = Doxygen::groupLinkedMap->add(root->name,
414 std::unique_ptr<GroupDef>(
415 createGroupDef(root->fileName,root->startLine,root->name,root->type)));
416 }
417 gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
418 // allow empty docs for group
419 gd->setDocumentation(!root->doc.empty() ? root->doc : DString(" "),root->docFile,root->docLine,false);
420 gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
422 gd->setRefItems(root->sli);
424 gd->setLanguage(root->lang);
426 {
427 root->commandOverrides.apply_groupGraph([&](bool b) { gd->overrideGroupGraph(b); });
428 }
429 }
430 }
431 }
432 for (const auto &e : root->children()) buildGroupListFiltered(e.get(),additional,includeExternal);
433}
434
435static void buildGroupList(const Entry *root)
436{
437 // --- first process only local groups
438 // first process the @defgroups blocks
439 buildGroupListFiltered(root,false,false);
440 // then process the @addtogroup, @weakgroup blocks
441 buildGroupListFiltered(root,true,false);
442
443 // --- then also process external groups
444 // first process the @defgroups blocks
445 buildGroupListFiltered(root,false,true);
446 // then process the @addtogroup, @weakgroup blocks
447 buildGroupListFiltered(root,true,true);
448}
449
450static void findGroupScope(const Entry *root)
451{
452 if (root->section.isGroupDoc() && !root->name.empty() &&
453 root->parent() && !root->parent()->name.empty())
454 {
456 if (gd)
457 {
458 DString scope = root->parent()->name;
459 if (root->parent()->section.isPackageDoc())
460 {
461 scope=substitute(scope,".","::");
462 }
463 scope = stripAnonymousNamespaceScope(scope);
464 scope+="::"+gd->name();
466 if (d)
467 {
468 gd->setGroupScope(d);
469 }
470 }
471 }
472 for (const auto &e : root->children()) findGroupScope(e.get());
473}
474
475static void organizeSubGroupsFiltered(const Entry *root,bool additional)
476{
477 if (root->section.isGroupDoc() && !root->name.empty())
478 {
479 AUTO_TRACE("additional={}",additional);
480 if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
481 (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
482 {
484 if (gd)
485 {
486 AUTO_TRACE_ADD("adding {} to group {}",root->name,gd->name());
487 addGroupToGroups(root,gd);
488 }
489 }
490 }
491 for (const auto &e : root->children()) organizeSubGroupsFiltered(e.get(),additional);
492}
493
494static void organizeSubGroups(const Entry *root)
495{
496 //printf("Defining groups\n");
497 // first process the @defgroups blocks
498 organizeSubGroupsFiltered(root,false);
499 //printf("Additional groups\n");
500 // then process the @addtogroup, @weakgroup blocks
501 organizeSubGroupsFiltered(root,true);
502}
503
504//----------------------------------------------------------------------
505
506template<class DefMutable>
507static void addIncludeFile(DefMutable *def,FileDef *ifd,const Entry *root)
508{
509 if (
510 (!root->doc.stripWhiteSpace().empty() ||
511 !root->brief.stripWhiteSpace().empty() ||
512 Config_getBool(EXTRACT_ALL)
513 ) && root->protection!=Protection::Private
514 )
515 {
516 //printf(">>>>>> includeFile=%s\n",qPrint(root->includeFile));
517
518 bool local=Config_getBool(FORCE_LOCAL_INCLUDES);
519 DString includeFile = root->includeFile;
520 if (!includeFile.empty() && includeFile.at(0)=='"')
521 {
522 local = true;
523 includeFile=includeFile.mid(1,includeFile.length()-2);
524 }
525 else if (!includeFile.empty() && includeFile.at(0)=='<')
526 {
527 local = false;
528 includeFile=includeFile.mid(1,includeFile.length()-2);
529 }
530
531 bool ambig = false;
532 FileDef *fd=nullptr;
533 // see if we need to include a verbatim copy of the header file
534 //printf("root->includeFile=%s\n",qPrint(root->includeFile));
535 if (!includeFile.empty() &&
536 (fd=Doxygen::inputNameLinkedMap->findFileDef(includeFile,ambig))==nullptr
537 )
538 { // explicit request
539 DString text;
540 text.sprintf("the name '%s' supplied as "
541 "the argument of the \\class, \\struct, \\union, or \\headerfile command ",
542 qPrint(includeFile)
543 );
544 if (ambig) // name is ambiguous
545 {
546 text+="matches the following input files:\n";
548 text+="\n";
549 text+="Please use a more specific name by "
550 "including a (larger) part of the path!";
551 }
552 else // name is not an input file
553 {
554 text+="is not an input file";
555 }
556 warn(root->fileName,root->startLine, "{}", text);
557 }
558 else if (includeFile.empty() && ifd &&
559 // see if the file extension makes sense
560 EntryType::guessSection(ifd->name()).isHeader())
561 { // implicit assumption
562 fd=ifd;
563 }
564
565 // if a file is found, we mark it as a source file.
566 if (fd)
567 {
568 DString iName = !root->includeName.empty() ?
569 root->includeName : includeFile;
570 if (!iName.empty()) // user specified include file
571 {
572 if (iName.at(0)=='<') local=false; // explicit override
573 else if (iName.at(0)=='"') local=true;
574 if (iName.at(0)=='"' || iName.at(0)=='<')
575 {
576 iName=iName.mid(1,iName.length()-2); // strip quotes or brackets
577 }
578 if (iName.empty())
579 {
580 iName=fd->name();
581 }
582 }
583 else if (!Config_getList(STRIP_FROM_INC_PATH).empty())
584 {
586 }
587 else // use name of the file containing the class definition
588 {
589 iName=fd->name();
590 }
591 if (fd->generateSourceFile()) // generate code for header
592 {
593 def->setIncludeFile(fd,iName,local,!root->includeName.empty());
594 }
595 else // put #include in the class documentation without link
596 {
597 def->setIncludeFile(nullptr,iName,local,true);
598 }
599 }
600 }
601}
602
603
604//----------------------------------------------------------------------
605
606static void buildFileList(const Entry *root)
607{
608 if ((root->section.isFileDoc() || (root->section.isFile() && Config_getBool(EXTRACT_ALL))) &&
609 !root->name.empty() && !root->tagInfo() // skip any file coming from tag files
610 )
611 {
612 bool ambig = false;
614 if (!fd || ambig)
615 {
616 bool save_ambig = ambig;
617 // use the directory of the file to see if the described file is in the same
618 // directory as the describing file.
619 DString fn = root->fileName;
620 size_t newIndex=fn.rfind('/');
621 if (newIndex==DString::npos)
622 {
623 fn = root->name;
624 }
625 else
626 {
627 fn = fn.left(newIndex)+"/"+root->name;
628 }
630 if (!fd) ambig = save_ambig;
631 }
632 //printf("**************** root->name=%s fd=%p\n",qPrint(root->name),(void*)fd);
633 if (fd && !ambig)
634 {
635 //printf("Adding documentation!\n");
636 // using false in setDocumentation is small hack to make sure a file
637 // is documented even if a \file command is used without further
638 // documentation
639 fd->setDocumentation(root->doc,root->docFile,root->docLine,false);
640 fd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
642 fd->setRefItems(root->sli);
644 addIncludeFile(fd,fd,root);
645 root->commandOverrides.apply_includeGraph ([&](bool b) { fd->overrideIncludeGraph(b); });
646 root->commandOverrides.apply_includedByGraph([&](bool b) { fd->overrideIncludedByGraph(b); });
647 for (const Grouping &g : root->groups)
648 {
649 GroupDef *gd=nullptr;
651 {
652 if (!gd->containsFile(fd))
653 {
654 gd->addFile(fd);
655 fd->makePartOfGroup(gd);
656 //printf("File %s: in group %s\n",qPrint(fd->name()),qPrint(gd->name()));
657 }
658 }
659 else if (!gd && g.pri == Grouping::GROUPING_INGROUP)
660 {
661 warn(root->fileName, root->startLine,
662 "Found non-existing group '{}' for the command '{}', ignoring command",
664 );
665 }
666 }
667 }
668 else
669 {
670 DString text(4096, DString::ExplicitSize);
671 text.sprintf("the name '%s' supplied as "
672 "the argument in the \\file statement ",
673 qPrint(root->name));
674 if (ambig) // name is ambiguous
675 {
676 text+="matches the following input files:\n";
678 text+="\n";
679 text+="Please use a more specific name by "
680 "including a (larger) part of the path!";
681 }
682 else // name is not an input file
683 {
684 text+="is not an input file";
685 }
686 warn(root->fileName,root->startLine,"{}", text);
687 }
688 }
689 for (const auto &e : root->children()) buildFileList(e.get());
690}
691
693{
694 size_t l = s.length();
695 int count=0;
696 int round=0;
697 DString result;
698 for (size_t i=0;i<l;i++)
699 {
700 char c=s.at(i);
701 if (c=='(') round++;
702 else if (c==')' && round>0) round--;
703 else if (c=='<' && round==0) count++;
704 if (count==0)
705 {
706 result+=c;
707 }
708 if (c=='>' && round==0 && count>0) count--;
709 }
710 //printf("stripTemplateSpecifiers(%s)=%s\n",qPrint(s),qPrint(result));
711 return result;
712}
713
714/*! returns the Definition object belonging to the first \a level levels of
715 * full qualified name \a name. Creates an artificial scope if the scope is
716 * not found and set the parent/child scope relation if the scope is found.
717 */
718[[maybe_unused]]
719static Definition *buildScopeFromQualifiedName(const DString &name_,SrcLangExt lang,const TagInfo *tagInfo)
720{
721 DString name = stripTemplateSpecifiers(name_);
722 name.stripPrefix("::");
723 int level = name.contains("::");
724 //printf("buildScopeFromQualifiedName(%s) level=%d\n",qPrint(name),level);
725 int i=0, p=0, l=0;
727 DString fullScope;
728 while (i<level)
729 {
730 int idx=getScopeFragment(name,p,&l);
731 if (idx==-1) return prevScope;
732 DString nsName = name.mid(idx,l);
733 if (nsName.empty()) return prevScope;
734 if (!fullScope.empty()) fullScope+="::";
735 fullScope+=nsName;
737 DefinitionMutable *innerScope = toDefinitionMutable(nd);
738 ClassDef *cd=nullptr;
739 if (nd==nullptr) cd = getClass(fullScope);
740 if (nd==nullptr && cd) // scope is a class
741 {
742 innerScope = toDefinitionMutable(cd);
743 }
744 else if (nd==nullptr && cd==nullptr && fullScope.find('<')==DString::npos) // scope is not known and could be a namespace!
745 {
746 // introduce bogus namespace
747 //printf("++ adding dummy namespace %s to %s tagInfo=%p\n",qPrint(nsName),qPrint(prevScope->name()),(void*)tagInfo);
748 NamespaceDefMutable *newNd=
750 Doxygen::namespaceLinkedMap->add(fullScope,
752 "[generated]",1,1,fullScope,
753 tagInfo?tagInfo->tagName:DString(),
754 tagInfo?tagInfo->fileName:DString())));
755 if (newNd)
756 {
757 newNd->setLanguage(lang);
758 newNd->setArtificial(true);
759 // add namespace to the list
760 innerScope = newNd;
761 }
762 }
763 else // scope is a namespace
764 {
765 }
766 if (innerScope)
767 {
768 // make the parent/child scope relation
769 DefinitionMutable *prevScopeMutable = toDefinitionMutable(prevScope);
770 if (prevScopeMutable)
771 {
772 prevScopeMutable->addInnerCompound(toDefinition(innerScope));
773 }
774 innerScope->setOuterScope(prevScope);
775 }
776 else // current scope is a class, so return only the namespace part...
777 {
778 return prevScope;
779 }
780 // proceed to the next scope fragment
781 p=idx+l+2;
782 prevScope=toDefinition(innerScope);
783 i++;
784 }
785 return prevScope;
786}
787
789 FileDef *fileScope,const TagInfo *tagInfo)
790{
791 //printf("<findScopeFromQualifiedName(%s,%s)\n",startScope ? qPrint(startScope->name()) : 0, qPrint(n));
792 Definition *resultScope=toDefinition(startScope);
793 if (resultScope==nullptr) resultScope=Doxygen::globalScope;
795 int l1 = 0;
796 int i1 = getScopeFragment(scope,0,&l1);
797 if (i1==-1)
798 {
799 //printf(">no fragments!\n");
800 return resultScope;
801 }
802 int p=i1+l1,l2=0,i2=0;
803 while ((i2=getScopeFragment(scope,p,&l2))!=-1)
804 {
805 DString nestedNameSpecifier = scope.mid(i1,l1);
806 Definition *orgScope = resultScope;
807 //printf(" nestedNameSpecifier=%s\n",qPrint(nestedNameSpecifier));
808 resultScope = const_cast<Definition*>(resultScope->findInnerCompound(nestedNameSpecifier));
809 //printf(" resultScope=%p\n",resultScope);
810 if (resultScope==nullptr)
811 {
812 if (orgScope==Doxygen::globalScope && fileScope && !fileScope->getUsedNamespaces().empty())
813 // also search for used namespaces
814 {
815 for (const auto &nd : fileScope->getUsedNamespaces())
816 {
818 if (mnd)
819 {
820 resultScope = findScopeFromQualifiedName(toNamespaceDefMutable(nd),n,fileScope,tagInfo);
821 if (resultScope!=nullptr) break;
822 }
823 }
824 if (resultScope)
825 {
826 // for a nested class A::I in used namespace N, we get
827 // N::A::I while looking for A, so we should compare
828 // resultScope->name() against scope.left(i2+l2)
829 //printf(" -> result=%s scope=%s\n",qPrint(resultScope->name()),qPrint(scope));
830 if (rightScopeMatch(resultScope->name(),scope.left(i2+l2)))
831 {
832 break;
833 }
834 goto nextFragment;
835 }
836 }
837
838 // also search for used classes. Complication: we haven't been able
839 // to put them in the right scope yet, because we are still resolving
840 // the scope relations!
841 // Therefore loop through all used classes and see if there is a right
842 // scope match between the used class and nestedNameSpecifier.
843 for (const auto &usedName : g_usingDeclarations)
844 {
845 //printf("Checking using class %s\n",qPrint(usedName));
846 if (rightScopeMatch(usedName,nestedNameSpecifier))
847 {
848 // ui.currentKey() is the fully qualified name of nestedNameSpecifier
849 // so use this instead.
850 DString fqn = usedName + scope.mid(p);
851 resultScope = buildScopeFromQualifiedName(fqn,startScope->getLanguage(),nullptr);
852 //printf("Creating scope from fqn=%s result %p\n",qPrint(fqn),resultScope);
853 if (resultScope)
854 {
855 //printf("> Match! resultScope=%s\n",qPrint(resultScope->name()));
856 return resultScope;
857 }
858 }
859 }
860
861 //printf("> name %s not found in scope %s\n",qPrint(nestedNameSpecifier),qPrint(orgScope->name()));
862 return nullptr;
863 }
864 nextFragment:
865 i1=i2;
866 l1=l2;
867 p=i2+l2;
868 }
869 //printf(">findScopeFromQualifiedName scope %s\n",qPrint(resultScope->name()));
870 return resultScope;
871}
872
873std::unique_ptr<ArgumentList> getTemplateArgumentsFromName(
874 const DString &name,
875 const ArgumentLists &tArgLists)
876{
877 // for each scope fragment, check if it is a template and advance through
878 // the list if so.
879 size_t i=0, p=0;
880 auto alIt = tArgLists.begin();
881 while ((i=name.find("::",p))!=DString::npos && alIt!=tArgLists.end())
882 {
884 if (nd==nullptr)
885 {
886 ClassDef *cd = getClass(name.left(i));
887 if (cd)
888 {
889 if (!cd->templateArguments().empty())
890 {
891 ++alIt;
892 }
893 }
894 }
895 p=i+2;
896 }
897 return alIt!=tArgLists.end() ?
898 std::make_unique<ArgumentList>(*alIt) :
899 std::unique_ptr<ArgumentList>();
900}
901
902static
904{
906
907 if (specifier.isStruct())
909 else if (specifier.isUnion())
910 sec=ClassDef::Union;
911 else if (specifier.isCategory())
913 else if (specifier.isInterface())
915 else if (specifier.isProtocol())
917 else if (specifier.isException())
919 else if (specifier.isService())
921 else if (specifier.isSingleton())
923
924 if (section.isUnionDoc())
925 sec=ClassDef::Union;
926 else if (section.isStructDoc())
928 else if (section.isInterfaceDoc())
930 else if (section.isProtocolDoc())
932 else if (section.isCategoryDoc())
934 else if (section.isExceptionDoc())
936 else if (section.isServiceDoc())
938 else if (section.isSingletonDoc())
940
941 return sec;
942}
943
944
945static void addClassToContext(const Entry *root)
946{
947 AUTO_TRACE("name={}",root->name);
948 FileDef *fd = root->fileDef();
949
950 DString scName;
951 if (root->parent()->section.isScope())
952 {
953 scName=root->parent()->name;
954 }
955 // name without parent's scope
956 DString fullName = root->name;
957
958 // strip off any template parameters (but not those for specializations)
959 if (size_t idx=fullName.find('>'); idx!=DString::npos && root->lang==SrcLangExt::CSharp) // mangle A<S,T>::N as A-2-g::N
960 {
961 fullName = mangleCSharpGenericName(fullName.left(idx+1))+fullName.mid(idx+1);
962 }
963 fullName=stripTemplateSpecifiersFromScope(fullName);
964
965 // name with scope (if not present already)
966 DString qualifiedName = fullName;
967 if (!scName.empty() && !leftScopeMatch(scName,fullName))
968 {
969 qualifiedName.prepend(scName+"::");
970 }
971
972 // see if we already found the class before
973 ClassDefMutable *cd = getClassMutable(qualifiedName);
974
975 AUTO_TRACE_ADD("Found class with name '{}', qualifiedName '{}'", cd ? cd->name() : root->name, qualifiedName);
976
977 if (cd)
978 {
979 fullName=cd->name();
980 AUTO_TRACE_ADD("Existing class '{}'",cd->name());
981 //if (cd->templateArguments()==0)
982 //{
983 // //printf("existing ClassDef tempArgList=%p specScope=%s\n",root->tArgList,qPrint(root->scopeSpec));
984 // cd->setTemplateArguments(tArgList);
985 //}
986
987 cd->setDocumentation(root->doc,root->docFile,root->docLine);
988 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
989 root->commandOverrides.apply_collaborationGraph([&](bool b ) { cd->overrideCollaborationGraph(b); });
990 root->commandOverrides.apply_inheritanceGraph ([&](CLASS_GRAPH_t gt) { cd->overrideInheritanceGraph(gt); });
991
992 if (!root->spec.isForwardDecl() && cd->isForwardDeclared())
993 {
994 cd->setDefFile(root->fileName,root->startLine,root->startColumn);
995 if (root->bodyLine!=-1)
996 {
997 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
998 cd->setBodyDef(fd);
999 }
1000 }
1001
1002 if (cd->templateArguments().empty() || (cd->isForwardDeclared() && !root->spec.isForwardDecl()))
1003 {
1004 // this happens if a template class declared with @class is found
1005 // before the actual definition or if a forward declaration has different template
1006 // parameter names.
1007 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(cd->name(),root->tArgLists);
1008 if (tArgList)
1009 {
1010 cd->setTemplateArguments(*tArgList);
1011 }
1012 }
1013 if (cd->requiresClause().empty() && !root->req.empty())
1014 {
1015 cd->setRequiresClause(root->req);
1016 }
1017
1019
1020 cd->setMetaData(root->metaData);
1021 }
1022 else // new class
1023 {
1025
1026 DString className;
1027 DString namespaceName;
1028 extractNamespaceName(fullName,className,namespaceName);
1029
1030 AUTO_TRACE_ADD("New class: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1031 fullName, namespaceName, className, Trace::trunc(root->brief), Trace::trunc(root->doc));
1032
1033 DString tagName;
1034 DString refFileName;
1035 const TagInfo *tagInfo = root->tagInfo();
1036 if (tagInfo)
1037 {
1038 tagName = tagInfo->tagName;
1039 refFileName = tagInfo->fileName;
1040 if (fullName.find("::")!=DString::npos)
1041 // symbols imported via tag files may come without the parent scope,
1042 // so we artificially create it here
1043 {
1044 buildScopeFromQualifiedName(fullName,root->lang,tagInfo);
1045 }
1046 }
1047 std::unique_ptr<ArgumentList> tArgList;
1048 size_t i=0;
1049 if ((root->lang==SrcLangExt::CSharp || root->lang==SrcLangExt::Java) &&
1050 (i=fullName.find('<'))!=DString::npos)
1051 {
1052 // a Java/C# generic class looks like a C++ specialization, so we need to split the
1053 // name and template arguments here
1054 tArgList = stringToArgumentList(root->lang,fullName.mid(i));
1055 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
1056 // A -> A
1057 // A<T> -> A-1-g
1058 // A<T,S> -> A-2-g
1059 {
1060 fullName=mangleCSharpGenericName(fullName);
1061 }
1062 else
1063 {
1064 fullName=fullName.left(i);
1065 }
1066 }
1067 else
1068 {
1069 tArgList = getTemplateArgumentsFromName(fullName,root->tArgLists);
1070 }
1071 // add class to the list
1072 cd = toClassDefMutable(
1073 Doxygen::classLinkedMap->add(fullName,
1074 createClassDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1075 fullName,sec,tagName,refFileName,true,root->spec.isEnum()) ));
1076 if (cd)
1077 {
1078 AUTO_TRACE_ADD("New class '{}' type={} #tArgLists={} tagInfo={} hidden={} artificial={}",
1079 fullName,cd->compoundTypeString(),root->tArgLists.size(),
1080 fmt::ptr(tagInfo),root->hidden,root->artificial);
1081 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1082 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1083 cd->setLanguage(root->lang);
1084 cd->setId(root->id);
1085 cd->setHidden(root->hidden);
1086 cd->setArtificial(root->artificial);
1087 cd->setClassSpecifier(root->spec);
1088 if (root->lang==SrcLangExt::CSharp && !root->args.empty())
1089 {
1091 }
1092 cd->addQualifiers(root->qualifiers);
1093 cd->setTypeConstraints(root->typeConstr);
1094 root->commandOverrides.apply_collaborationGraph([&](bool b ) { cd->overrideCollaborationGraph(b); });
1095 root->commandOverrides.apply_inheritanceGraph ([&](CLASS_GRAPH_t gt) { cd->overrideInheritanceGraph(gt); });
1096
1097 if (tArgList)
1098 {
1099 cd->setTemplateArguments(*tArgList);
1100 }
1101 cd->setRequiresClause(root->req);
1102 cd->setProtection(root->protection);
1103 cd->setIsStatic(root->isStatic);
1104
1105 // file definition containing the class cd
1106 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1107 cd->setBodyDef(fd);
1108
1109 cd->setMetaData(root->metaData);
1110
1111 cd->insertUsedFile(fd);
1112 }
1113 else
1114 {
1115 AUTO_TRACE_ADD("Class {} not added, already exists as alias", fullName);
1116 }
1117 }
1118
1119 if (cd)
1120 {
1122 if (!root->subGrouping) cd->setSubGrouping(false);
1123 if (!root->spec.isForwardDecl())
1124 {
1125 if (cd->hasDocumentation())
1126 {
1127 addIncludeFile(cd,fd,root);
1128 }
1129 if (fd && root->section.isCompound())
1130 {
1131 AUTO_TRACE_ADD("Inserting class {} in file {} (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1132 cd->setFileDef(fd);
1133 fd->insertClass(cd);
1134 }
1135 }
1136 addClassToGroups(root,cd);
1138 cd->setRefItems(root->sli);
1139 cd->setRequirementReferences(root->rqli);
1140 }
1141}
1142
1143//----------------------------------------------------------------------
1144// build a list of all classes mentioned in the documentation
1145// and all classes that have a documentation block before their definition.
1146static void buildClassList(const Entry *root)
1147{
1148 if ((root->section.isCompound() || root->section.isObjcImpl()) && !root->name.empty())
1149 {
1150 AUTO_TRACE();
1151 addClassToContext(root);
1152 }
1153 for (const auto &e : root->children()) buildClassList(e.get());
1154}
1155
1156static void buildClassDocList(const Entry *root)
1157{
1158 if ((root->section.isCompoundDoc()) && !root->name.empty())
1159 {
1160 AUTO_TRACE();
1161 addClassToContext(root);
1162 }
1163 for (const auto &e : root->children()) buildClassDocList(e.get());
1164}
1165
1166//----------------------------------------------------------------------
1167// build a list of all classes mentioned in the documentation
1168// and all classes that have a documentation block before their definition.
1169
1170static void addConceptToContext(const Entry *root)
1171{
1172 AUTO_TRACE();
1173 FileDef *fd = root->fileDef();
1174
1175 DString scName;
1176 if (root->parent()->section.isScope())
1177 {
1178 scName=root->parent()->name;
1179 }
1180
1181 // name with scope (if not present already)
1182 DString qualifiedName = root->name;
1183 if (!scName.empty() && !leftScopeMatch(qualifiedName,scName))
1184 {
1185 qualifiedName.prepend(scName+"::");
1186 }
1187
1188 // see if we already found the concept before
1189 ConceptDefMutable *cd = getConceptMutable(qualifiedName);
1190
1191 AUTO_TRACE_ADD("Found concept with name '{}' (qualifiedName='{}')", cd ? cd->name() : root->name, qualifiedName);
1192
1193 if (cd)
1194 {
1195 qualifiedName=cd->name();
1196 AUTO_TRACE_ADD("Existing concept '{}'",cd->name());
1197
1198 cd->setDocumentation(root->doc,root->docFile,root->docLine);
1199 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1200
1201 addIncludeFile(cd,fd,root);
1202 }
1203 else // new concept
1204 {
1205 DString className;
1206 DString namespaceName;
1207 extractNamespaceName(qualifiedName,className,namespaceName);
1208
1209 AUTO_TRACE_ADD("New concept: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1210 qualifiedName,namespaceName,className,root->brief,root->doc);
1211
1212 DString tagName;
1213 DString refFileName;
1214 const TagInfo *tagInfo = root->tagInfo();
1215 if (tagInfo)
1216 {
1217 tagName = tagInfo->tagName;
1218 refFileName = tagInfo->fileName;
1219 if (qualifiedName.find("::")!=DString::npos)
1220 // symbols imported via tag files may come without the parent scope,
1221 // so we artificially create it here
1222 {
1223 buildScopeFromQualifiedName(qualifiedName,root->lang,tagInfo);
1224 }
1225 }
1226 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(qualifiedName,root->tArgLists);
1227 // add concept to the list
1229 Doxygen::conceptLinkedMap->add(qualifiedName,
1230 createConceptDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1231 qualifiedName,tagName,refFileName)));
1232 if (cd)
1233 {
1234 AUTO_TRACE_ADD("New concept '{}' #tArgLists={} tagInfo={}",
1235 qualifiedName,root->tArgLists.size(),fmt::ptr(tagInfo));
1236 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1237 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1238 cd->setLanguage(root->lang);
1239 cd->setId(root->id);
1240 cd->setHidden(root->hidden);
1241 cd->setGroupId(root->mGrpId);
1242 if (tArgList)
1243 {
1244 cd->setTemplateArguments(*tArgList);
1245 }
1246 cd->setInitializer(root->initializer.str());
1247 // file definition containing the class cd
1248 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1249 cd->setBodyDef(fd);
1251 cd->setRefItems(root->sli);
1252 cd->setRequirementReferences(root->rqli);
1253 addIncludeFile(cd,fd,root);
1254
1255 // also add namespace to the correct structural context
1256 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,qualifiedName,nullptr,tagInfo);
1258 {
1260 if (dm)
1261 {
1262 dm->addInnerCompound(cd);
1263 }
1264 cd->setOuterScope(d);
1265 }
1266 for (const auto &ce : root->children())
1267 {
1268 //printf("Concept %s has child %s\n",qPrint(root->name),qPrint(ce->section.to_string()));
1269 if (ce->section.isConceptDocPart())
1270 {
1271 cd->addSectionsToDefinition(ce->anchors);
1272 cd->setRefItems(ce->sli);
1273 cd->setRequirementReferences(ce->rqli);
1274 if (!ce->brief.empty())
1275 {
1276 cd->addDocPart(ce->brief,ce->startLine,ce->startColumn);
1277 //printf(" brief=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->brief),ce->startLine,ce->startColumn);
1278 }
1279 if (!ce->doc.empty())
1280 {
1281 cd->addDocPart(ce->doc,ce->startLine,ce->startColumn);
1282 //printf(" doc=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->doc),ce->startLine,ce->startColumn);
1283 }
1284 }
1285 else if (ce->section.isConceptCodePart())
1286 {
1287 cd->addCodePart(ce->initializer.str(),ce->startLine,ce->startColumn);
1288 //printf(" code=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->initializer.str()),ce->startLine,ce->startColumn);
1289 }
1290 }
1291 }
1292 else
1293 {
1294 AUTO_TRACE_ADD("Concept '{}' not added, already exists (as alias)", qualifiedName);
1295 }
1296 }
1297
1298 if (cd)
1299 {
1301 for (const auto &ce : root->children())
1302 {
1303 if (ce->section.isConceptDocPart())
1304 {
1305 cd->addSectionsToDefinition(ce->anchors);
1306 }
1307 }
1308 if (fd)
1309 {
1310 AUTO_TRACE_ADD("Inserting concept '{}' in file '{}' (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1311 cd->setFileDef(fd);
1312 fd->insertConcept(cd);
1313 }
1314 addConceptToGroups(root,cd);
1316 cd->setRefItems(root->sli);
1317 cd->setRequirementReferences(root->rqli);
1318 }
1319}
1320
1321static void findModuleDocumentation(const Entry *root)
1322{
1323 if (root->section.isModuleDoc())
1324 {
1325 AUTO_TRACE();
1327 }
1328 for (const auto &e : root->children()) findModuleDocumentation(e.get());
1329}
1330
1331static void buildConceptList(const Entry *root)
1332{
1333 if (root->section.isConcept())
1334 {
1335 AUTO_TRACE();
1336 addConceptToContext(root);
1337 }
1338 for (const auto &e : root->children()) buildConceptList(e.get());
1339}
1340
1341static void buildConceptDocList(const Entry *root)
1342{
1343 if (root->section.isConceptDoc())
1344 {
1345 AUTO_TRACE();
1346 addConceptToContext(root);
1347 }
1348 for (const auto &e : root->children()) buildConceptDocList(e.get());
1349}
1350
1351// This routine is to allow @ingroup X @{ concept A; concept B; @} to work
1352// (same also works for variable and functions because of logic in MemberGroup::insertMember)
1354{
1355 AUTO_TRACE();
1356 for (const auto &cd : *Doxygen::conceptLinkedMap)
1357 {
1358 if (cd->groupId()!=DOX_NOGROUP)
1359 {
1360 for (const auto &ocd : *Doxygen::conceptLinkedMap)
1361 {
1362 if (cd!=ocd && cd->groupId()==ocd->groupId() &&
1363 !cd->partOfGroups().empty() && ocd->partOfGroups().empty())
1364 {
1365 ConceptDefMutable *ocdm = toConceptDefMutable(ocd.get());
1366 if (ocdm)
1367 {
1368 for (const auto &gd : cd->partOfGroups())
1369 {
1370 if (gd)
1371 {
1372 AUTO_TRACE_ADD("making concept '{}' part of group '{}'",ocdm->name(),gd->name());
1373 ocdm->makePartOfGroup(gd);
1374 gd->addConcept(ocd.get());
1375 }
1376 }
1377 }
1378 }
1379 }
1380 }
1381 }
1382}
1383
1384//----------------------------------------------------------------------
1385
1387{
1388 ClassDefSet visitedClasses;
1389
1390 bool done=false;
1391 //int iteration=0;
1392 while (!done)
1393 {
1394 done=true;
1395 //++iteration;
1396 struct ClassAlias
1397 {
1398 ClassAlias(const DString &name,std::unique_ptr<ClassDef> cd,DefinitionMutable *ctx) :
1399 aliasFullName(name),aliasCd(std::move(cd)), aliasContext(ctx) {}
1400 DString aliasFullName;
1401 std::unique_ptr<ClassDef> aliasCd;
1402 DefinitionMutable *aliasContext;
1403 };
1404 std::vector<ClassAlias> aliases;
1405 for (const auto &icd : *Doxygen::classLinkedMap)
1406 {
1407 ClassDefMutable *cd = toClassDefMutable(icd.get());
1408 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1409 {
1410 DString name = stripAnonymousNamespaceScope(icd->name());
1411 //printf("processing=%s, iteration=%d\n",qPrint(cd->name()),iteration);
1412 // also add class to the correct structural context
1414 name,icd->getFileDef(),nullptr);
1415 if (d)
1416 {
1417 //printf("****** adding %s to scope %s in iteration %d\n",qPrint(cd->name()),qPrint(d->name()),iteration);
1419 if (dm)
1420 {
1421 dm->addInnerCompound(cd);
1422 }
1423 cd->setOuterScope(d);
1424
1425 // for inline namespace add an alias of the class to the outer scope
1427 {
1429 //printf("nd->isInline()=%d\n",nd->isInline());
1430 if (nd && nd->isInline())
1431 {
1432 d = d->getOuterScope();
1433 if (d)
1434 {
1435 dm = toDefinitionMutable(d);
1436 if (dm)
1437 {
1438 auto aliasCd = createClassDefAlias(d,cd);
1439 DString aliasFullName = d->qualifiedName()+"::"+aliasCd->localName();
1440 aliases.emplace_back(aliasFullName,std::move(aliasCd),dm);
1441 //printf("adding %s to %s as %s\n",qPrint(aliasCd->name()),qPrint(d->name()),qPrint(aliasFullName));
1442 }
1443 }
1444 }
1445 else
1446 {
1447 break;
1448 }
1449 }
1450
1451 visitedClasses.insert(icd.get());
1452 done=false;
1453 }
1454 //else
1455 //{
1456 // printf("****** ignoring %s: scope not (yet) found in iteration %d\n",qPrint(cd->name()),iteration);
1457 //}
1458 }
1459 }
1460 // add aliases
1461 for (auto &alias : aliases)
1462 {
1463 ClassDef *aliasCd = Doxygen::classLinkedMap->add(alias.aliasFullName,std::move(alias.aliasCd));
1464 if (aliasCd)
1465 {
1466 alias.aliasContext->addInnerCompound(aliasCd);
1467 }
1468 }
1469 }
1470
1471 //give warnings for unresolved compounds
1472 for (const auto &icd : *Doxygen::classLinkedMap)
1473 {
1474 ClassDefMutable *cd = toClassDefMutable(icd.get());
1475 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1476 {
1478 /// create the scope artificially
1479 // anyway, so we can at least relate scopes properly.
1480 Definition *d = buildScopeFromQualifiedName(name,cd->getLanguage(),nullptr);
1481 if (d && d!=cd && !cd->getDefFileName().empty())
1482 // avoid recursion in case of redundant scopes, i.e: namespace N { class N::C {}; }
1483 // for this case doxygen assumes the existence of a namespace N::N in which C is to be found!
1484 // also avoid warning for stuff imported via a tagfile.
1485 {
1487 if (dm)
1488 {
1489 dm->addInnerCompound(cd);
1490 }
1491 cd->setOuterScope(d);
1492 // A specialization can only be written for a template that has been declared before, so a missing
1493 // scope means that declaration is outside the input - e.g. `template<> struct std::hash<MyClass>`.
1494 if (cd->localName().find('<')==DString::npos)
1495 {
1496 warn(cd->getDefFileName(),cd->getDefLine(),
1497 "Incomplete input: scope for class {} not found!{}",name,
1498 name.startsWith("std::") ? " Try enabling BUILTIN_STL_SUPPORT." : ""
1499 );
1500 }
1501 }
1502 }
1503 }
1504}
1505
1507{
1508 //bool inlineGroupedClasses = Config_getBool(INLINE_GROUPED_CLASSES);
1509 //if (!inlineGroupedClasses) return;
1510 //printf("** distributeClassGroupRelations()\n");
1511
1512 ClassDefSet visitedClasses;
1513 for (const auto &cd : *Doxygen::classLinkedMap)
1514 {
1515 //printf("Checking %s\n",qPrint(cd->name()));
1516 // distribute the group to nested classes as well
1517 if (visitedClasses.find(cd.get())==visitedClasses.end() && !cd->partOfGroups().empty())
1518 {
1519 //printf(" Candidate for merging\n");
1520 GroupDef *gd = cd->partOfGroups().front();
1521 for (auto &ncd : cd->getClasses())
1522 {
1524 if (ncdm && ncdm->partOfGroups().empty())
1525 {
1526 //printf(" Adding %s to group '%s'\n",qPrint(ncd->name()),
1527 // gd->groupTitle());
1528 ncdm->makePartOfGroup(gd);
1529 gd->addClass(ncdm);
1530 }
1531 }
1532 visitedClasses.insert(cd.get()); // only visit every class once
1533 }
1534 }
1535}
1536
1537//----------------------------------------------------------------------
1538
1539template<typename Container>
1541 const Container *cd,
1542 const MemberDef *enumTypeMember,
1543 MemberListType mlFilter)
1544{
1545 if (md && md->isEnumerate() && md->name().startsWith("@")) // anonymous enum type
1546 {
1547 MemberList *eiml = cd->getMemberList(mlFilter);
1548 if (eiml)
1549 {
1550 for (const auto &eimd : *eiml)
1551 {
1552 DString vtype = eimd->typeString();
1553 if (vtype.find(md->name())!=DString::npos)
1554 {
1556 if (mimd)
1557 {
1558 mimd->setAnonymousEnumType(enumTypeMember);
1559 break;
1560 }
1561 }
1562 }
1563 }
1564 }
1565}
1566
1567static ClassDefMutable *createTagLessInstance(const Definition *root,const ClassDef *templ,const DString &fieldName)
1568{
1569 DString n = templ->name();
1570 // replace e.g. X::@1343:@4343::Y -> X::[struct]::Y
1571 if (size_t sn = n.find('@'); sn!=DString::npos)
1572 {
1573 const char *p = n.data()+sn;
1574 char c;
1575 while ((c=*p))
1576 {
1577 if (!isdigit(c) && c!='@' && c!=':') break;
1578 p++;
1579 }
1580 n = n.left(sn)+"["+templ->compoundTypeString().str()+"]"+p;
1581 }
1582 // add field name to the class name to make it unique again, e.g. X::[struct]::Y.m
1583 DString fullName = n+"."+fieldName;
1584
1585 //printf("** adding class %s based on %s in %s\n",qPrint(fullName),qPrint(templ->name()),qPrint(root->name()));
1587 Doxygen::classLinkedMap->add(fullName,
1589 templ->getDefLine(),
1590 templ->getDefColumn(),
1591 fullName,
1592 templ->compoundType())));
1593 if (cd)
1594 {
1595 //printf("cd->name()=%s displayName=%s\n",qPrint(cd->name()),qPrint(cd->displayName()));
1596 cd->setDocumentation(templ->documentation(),templ->docFile(),templ->docLine()); // copy docs to definition
1597 cd->setBriefDescription(templ->briefDescription(),templ->briefFile(),templ->briefLine());
1598 cd->setLanguage(templ->getLanguage());
1599 cd->setBodySegment(templ->getDefLine(),templ->getStartBodyLine(),templ->getEndBodyLine());
1600 cd->setBodyDef(templ->getBodyDef());
1601
1602 if (root!=Doxygen::globalScope)
1603 {
1604 DefinitionMutable *outerScope = toDefinitionMutable(const_cast<Definition*>(root));
1605 if (root && root->definitionType()==Definition::TypeFile)
1606 {
1607 FileDef *fd = toFileDef(const_cast<Definition*>(root));
1608 fd->insertClass(cd);
1609 cd->setFileDef(fd);
1611 }
1612 else if (outerScope)
1613 {
1614 outerScope->addInnerCompound(cd);
1615 cd->setOuterScope(const_cast<Definition*>(root));
1616 }
1617 }
1618
1619 for (auto &gd : root->partOfGroups())
1620 {
1621 cd->makePartOfGroup(gd);
1622 gd->addClass(cd);
1623 }
1624
1625 auto addMember = [&](const MemberDef *md) -> MemberDefMutable*
1626 {
1627 auto newMd = createMemberDef(md->getDefFileName(),md->getDefLine(),md->getDefColumn(),
1628 md->typeString(),md->name(),md->argsString(),md->excpString(),
1629 md->protection(),md->virtualness(),md->isStatic(),Relationship::Member,
1630 md->memberType(),
1631 ArgumentList(),ArgumentList(),"");
1632 MemberDefMutable *imd = toMemberDefMutable(newMd.get());
1633 imd->setMemberClass(cd);
1634 imd->setDefinition(md->definition());
1635 imd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
1636 imd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
1637 imd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
1638 imd->setMemberSpecifiers(md->getMemberSpecifiers());
1639 imd->setId(md->id());
1640 imd->addQualifiers(md->getQualifiers());
1641 imd->setVhdlSpecifiers(md->getVhdlSpecifiers());
1642 imd->setMemberGroupId(md->getMemberGroupId());
1643 imd->setInitializer(md->initializer());
1644 imd->setRequiresClause(md->requiresClause());
1645 imd->setMaxInitLines(md->initializerLines());
1646 imd->setBitfields(md->bitfieldString());
1647 imd->setLanguage(md->getLanguage());
1649 cd->insertMember(imd);
1650 associateVariableWithAnonymousEnumType(md,cd,imd,MemberListType::PubAttribs());
1651 MemberName *mn = Doxygen::memberNameLinkedMap->add(md->name());
1652 mn->push_back(std::move(newMd));
1653 return imd;
1654
1655 };
1656
1657 MemberList *ml = templ->getMemberList(MemberListType::PubAttribs());
1658 if (ml)
1659 {
1660 for (const auto &md : *ml)
1661 {
1662 //printf(" Member attribute %s def=%s\n",qPrint(md->name()),qPrint(md->definition()));
1663 addMember(md);
1664 }
1665 }
1666 ml = templ->getMemberList(MemberListType::PubTypes());
1667 if (ml)
1668 {
1669 for (const auto &md : *ml)
1670 {
1671 //printf(" Member type %s def=%s\n",qPrint(md->name()),qPrint(md->definition()));
1672 MemberDefMutable *mdm = addMember(md);
1673 if (md->isEnumerate() && md->name().startsWith("@")) // anonymous enum type
1674 {
1675 for (const auto &emd : md->enumFieldList())
1676 {
1677 //printf(" enum field %s\n",qPrint(emd->name()));
1678 MemberDefMutable *emdm = addMember(emd);
1679 mdm->insertEnumField(emdm);
1680 emdm->setEnumScope(md);
1681 }
1682 }
1683 }
1684 }
1685 }
1686 return cd;
1687}
1688
1689/** Look through the members of class \a cd and its public members.
1690 * If there is a member m of a tag less struct/union,
1691 * then we create a duplicate of the struct/union with the name of the
1692 * member to identify it.
1693 * So if cd has name S, then the tag less struct/union will get name S.m
1694 * Since tag less structs can be nested we need to call this function
1695 * recursively. Later on we need to patch the member types so we keep
1696 * track of the hierarchy of classes we create.
1697 */
1698template<typename Container, typename TagContainer>
1699static void processTagLessClasses(const Definition *root,
1700 const Container *cd,
1701 const TagContainer *tagParent,
1702 MemberListType varFilter,
1703 MemberListType typeFilter,
1704 const DString &prefix,int count)
1705{
1706 AUTO_TRACE("count={} name={}\n",count,cd->name());
1707 if (tagParent /*&& !cd->getClasses().empty()*/)
1708 {
1709 MemberList *ml = cd->getMemberList(varFilter);
1710 if (ml)
1711 {
1712 int pos=0;
1713 for (const auto &md : *ml)
1714 {
1715 DString type = md->typeString();
1716 //printf(" member %s: type='%s' outerScope='%s'\n",qPrint(md->name()),qPrint(type),qPrint(md->getOuterScope()?md->getOuterScope()->name():"<null>"));
1717 if ((cd->definitionType()!=Definition::TypeFile || md->getOuterScope()==Doxygen::globalScope) && // part namespace members only if cd is a namespace
1718 (type.find("::@")!=DString::npos || type.find(" @")!=DString::npos)) // member of tag less struct/union
1719 {
1720 std::vector<const ClassDef *> candidates;
1721 for (const auto &icd : cd->getClasses())
1722 {
1723 candidates.push_back(icd);
1724 }
1725 for (const auto &icd : candidates)
1726 {
1727 //printf(" comparing '%s'<->'%s'\n",qPrint(type),qPrint(icd->name()));
1728 if (type.find(icd->name())!=DString::npos) // matching tag less struct/union
1729 {
1730 DString name = md->name();
1731 if (md->isAnonymous()) name = "__unnamed" + DString().setNum(pos++)+"__";
1732 if (!prefix.empty()) name.prepend(prefix+".");
1733 //printf(" found %s in scope %s\n",qPrint(name),qPrint(cd->name()));
1734 ClassDefMutable *ncd = createTagLessInstance(root,icd,name);
1735 if (ncd)
1736 {
1737 processTagLessClasses(ncd,icd,ncd,MemberListType::PubAttribs(),MemberListType::PubTypes(),name,count+1);
1738 //printf(" addTagged %s to %s\n",qPrint(ncd->name()),qPrint(tagParent->name()));
1739 ncd->setTagLessReference(icd);
1740
1741 // associate the variable of the anonymous type with the type member
1742 MemberList *pml = tagParent->getMemberList(varFilter);
1743 if (pml)
1744 {
1745 for (const auto &pmd : *pml)
1746 {
1748 if (pmdm && pmd->name()==md->name())
1749 {
1750 pmdm->setClassDefOfAnonymousType(ncd);
1751 }
1752 }
1753 }
1754 }
1755 }
1756 else
1757 {
1758 //printf(" no match for %s in %s\n",qPrint(icd->name()),qPrint(type));
1759 }
1760 }
1761 }
1762 }
1763 }
1764 // associate the variable of the anonymous enum type with the type member
1765 ml = cd->getMemberList(typeFilter);
1766 if (ml)
1767 {
1768 for (const auto &md : *ml)
1769 {
1770 MemberListType mlFilter = cd->definitionType()==Definition::TypeClass ? MemberListType::PubAttribs() : MemberListType::DecVarMembers();
1771 associateVariableWithAnonymousEnumType(md,cd,md,mlFilter);
1772 }
1773 }
1774 }
1775}
1776
1777template<typename Container>
1778static void findTagLessClasses(std::set<const Definition *> &candidates,const Container *cd)
1779{
1780 for (const auto &icd : cd->getClasses())
1781 {
1782 if (icd->name().find('@')==DString::npos) // process all non-anonymous inner classes
1783 {
1784 findTagLessClasses(candidates,icd);
1785 }
1786 }
1787
1788 candidates.insert(cd);
1789}
1790
1792{
1793 std::set<const Definition *> candidates;
1794 for (auto &cd : *Doxygen::classLinkedMap)
1795 {
1796 Definition *scope = cd->getOuterScope();
1797 //printf(" scope=%s for class %s\n",qPrint(scope?scope->name():"<null>"),qPrint(cd->name()));
1798 if (scope && scope->definitionType()==Definition::TypeNamespace) // class that is not nested
1799 {
1800 const NamespaceDef *nd = toNamespaceDef(scope);
1801 if (nd && nd==Doxygen::globalScope) // class at global namespace
1802 {
1803 const FileDef *fd = cd->getFileDef();
1804 if (fd)
1805 {
1806 findTagLessClasses(candidates,fd);
1807 }
1808 }
1809 else if (nd) // class in a namespace
1810 {
1811 findTagLessClasses(candidates,nd);
1812 }
1813 }
1814 }
1815
1816 // since processTagLessClasses is potentially adding classes to Doxygen::classLinkedMap
1817 // we need to call it outside of the loop above, otherwise the iterator gets invalidated!
1818 for (const auto &d : candidates)
1819 {
1820 //printf("------ processing tag-less classes for %s\n",qPrint(d->name()));
1821 if (d->definitionType()==Definition::TypeNamespace)
1822 {
1823 const NamespaceDef *nd = toNamespaceDef(d);
1824 processTagLessClasses(nd,nd,nd,MemberListType::DecVarMembers(),MemberListType::DecEnumMembers(),"",0);
1825 }
1826 else if (d->definitionType()==Definition::TypeFile)
1827 {
1828 const FileDef *fd = toFileDef(d);
1829 processTagLessClasses(fd,fd,fd,MemberListType::DecVarMembers(),MemberListType::DecEnumMembers(),"",0);
1830 }
1831 else if (d->definitionType()==Definition::TypeClass)
1832 {
1833 const ClassDef *cd = toClassDef(d);
1834 processTagLessClasses(cd,cd,cd,MemberListType::PubAttribs(),MemberListType::PubTypes(),"",0);
1835 }
1836 }
1837}
1838
1839
1840//----------------------------------------------------------------------
1841// build a list of all namespaces mentioned in the documentation
1842// and all namespaces that have a documentation block before their definition.
1843static void buildNamespaceList(const Entry *root)
1844{
1845 if (
1846 (root->section.isNamespace() ||
1847 root->section.isNamespaceDoc() ||
1848 root->section.isPackageDoc()
1849 ) &&
1850 !root->name.empty()
1851 )
1852 {
1853 AUTO_TRACE("name={}",root->name);
1854
1855 DString fName = root->name;
1856 if (root->section.isPackageDoc())
1857 {
1858 fName=substitute(fName,".","::");
1859 }
1860
1861 DString fullName = stripAnonymousNamespaceScope(fName);
1862 if (!fullName.empty())
1863 {
1864 AUTO_TRACE_ADD("Found namespace {} in {} at line {}",root->name,root->fileName,root->startLine);
1866 if (ndi) // existing namespace
1867 {
1869 if (nd) // non-inline namespace
1870 {
1871 AUTO_TRACE_ADD("Existing namespace");
1872 nd->setDocumentation(root->doc,root->docFile,root->docLine);
1873 nd->setName(fullName); // change name to match docs
1875 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1876 if (nd->getLanguage()==SrcLangExt::Unknown)
1877 {
1878 nd->setLanguage(root->lang);
1879 }
1880 if (root->tagInfo()==nullptr && nd->isReference() && !(root->doc.empty() && root->brief.empty()))
1881 // if we previously found namespace nd in a tag file and now we find a
1882 // documented namespace with the same name in the project, then remove
1883 // the tag file reference
1884 {
1885 nd->setReference("");
1886 nd->setFileName(fullName);
1887 }
1888 nd->setMetaData(root->metaData);
1889
1890 // file definition containing the namespace nd
1891 FileDef *fd=root->fileDef();
1892 if (nd->isArtificial())
1893 {
1894 nd->setArtificial(false); // found namespace explicitly, so cannot be artificial
1895 nd->setDefFile(root->fileName,root->startLine,root->startColumn);
1896 }
1897 // insert the namespace in the file definition
1898 if (fd) fd->insertNamespace(nd);
1899 addNamespaceToGroups(root,nd);
1900 nd->setRefItems(root->sli);
1901 nd->setRequirementReferences(root->rqli);
1902 addIncludeFile(nd,fd,root);
1903 }
1904 }
1905 else // fresh namespace
1906 {
1907 DString tagName;
1908 DString tagFileName;
1909 const TagInfo *tagInfo = root->tagInfo();
1910 if (tagInfo)
1911 {
1912 tagName = tagInfo->tagName;
1913 tagFileName = tagInfo->fileName;
1914 }
1915 AUTO_TRACE_ADD("new namespace {} lang={} tagName={}",fullName,root->lang,tagName);
1916 // add namespace to the list
1918 Doxygen::namespaceLinkedMap->add(fullName,
1919 createNamespaceDef(tagInfo?tagName:root->fileName,root->startLine,
1920 root->startColumn,fullName,tagName,tagFileName,
1921 root->type,root->spec.isPublished())));
1922 if (nd)
1923 {
1924 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1925 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1927 nd->setHidden(root->hidden);
1928 nd->setArtificial(root->artificial);
1929 nd->setLanguage(root->lang);
1930 nd->setId(root->id);
1931 nd->setMetaData(root->metaData);
1932 nd->setInline(root->spec.isInline());
1933 nd->setExported(root->exported);
1934
1935 addNamespaceToGroups(root,nd);
1936 nd->setRefItems(root->sli);
1937 nd->setRequirementReferences(root->rqli);
1938
1939 // file definition containing the namespace nd
1940 FileDef *fd=root->fileDef();
1941 // insert the namespace in the file definition
1942 if (fd) fd->insertNamespace(nd);
1943
1944 // the empty string test is needed for extract all case
1945 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1946 nd->insertUsedFile(fd);
1947 nd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1948 nd->setBodyDef(fd);
1949 addIncludeFile(nd,fd,root);
1950
1951 // also add namespace to the correct structural context
1952 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,fullName,nullptr,tagInfo);
1953 AUTO_TRACE_ADD("adding namespace {} to context {}",nd->name(),d ? d->name() : DString("<none>"));
1954 if (d==nullptr) // we didn't find anything, create the scope artificially
1955 // anyway, so we can at least relate scopes properly.
1956 {
1957 d = buildScopeFromQualifiedName(fullName,nd->getLanguage(),tagInfo);
1959 if (dm)
1960 {
1961 dm->addInnerCompound(nd);
1962 }
1963 nd->setOuterScope(d);
1964 // TODO: Due to the order in which the tag file is written
1965 // a nested class can be found before its parent!
1966 }
1967 else
1968 {
1970 if (dm)
1971 {
1972 dm->addInnerCompound(nd);
1973 }
1974 nd->setOuterScope(d);
1975 // in case of d is an inline namespace, alias insert nd in the part scope of d.
1977 {
1978 NamespaceDef *pnd = toNamespaceDef(d);
1979 if (pnd && pnd->isInline())
1980 {
1981 d = d->getOuterScope();
1982 if (d)
1983 {
1984 dm = toDefinitionMutable(d);
1985 if (dm)
1986 {
1987 auto aliasNd = createNamespaceDefAlias(d,nd);
1988 dm->addInnerCompound(aliasNd.get());
1989 DString aliasName = aliasNd->name();
1990 AUTO_TRACE_ADD("adding alias {} to {}",aliasName,d->name());
1991 Doxygen::namespaceLinkedMap->add(aliasName,std::move(aliasNd));
1992 }
1993 }
1994 else
1995 {
1996 break;
1997 }
1998 }
1999 else
2000 {
2001 break;
2002 }
2003 }
2004 }
2005 }
2006 }
2007 }
2008 }
2009 for (const auto &e : root->children()) buildNamespaceList(e.get());
2010}
2011
2012//----------------------------------------------------------------------
2013
2015 const DString &name)
2016{
2017 NamespaceDef *usingNd =nullptr;
2018 for (auto &und : unl)
2019 {
2020 DString uScope=und->name()+"::";
2021 usingNd = getResolvedNamespace(uScope+name);
2022 if (usingNd!=nullptr) break;
2023 }
2024 return usingNd;
2025}
2026
2027static void findUsingDirectives(const Entry *root)
2028{
2029 if (root->section.isUsingDir())
2030 {
2031 AUTO_TRACE("Found using directive {} at line {} of {}",root->name,root->startLine,root->fileName);
2032 DString name=substitute(root->name,".","::");
2033 if (name.endsWith("::"))
2034 {
2035 name=name.left(name.length()-2);
2036 }
2037 if (!name.empty())
2038 {
2039 NamespaceDef *usingNd = nullptr;
2040 NamespaceDefMutable *nd = nullptr;
2041 FileDef *fd = root->fileDef();
2042 DString nsName;
2043
2044 // see if the using statement was found inside a namespace or inside
2045 // the global file scope.
2046 if (root->parent() && root->parent()->section.isNamespace() &&
2047 (fd==nullptr || fd->getLanguage()!=SrcLangExt::Java) // not a .java file
2048 )
2049 {
2050 nsName=stripAnonymousNamespaceScope(root->parent()->name);
2051 if (!nsName.empty())
2052 {
2053 nd = getResolvedNamespaceMutable(nsName);
2054 }
2055 }
2056
2057 // find the scope in which the 'using' namespace is defined by prepending
2058 // the possible scopes in which the using statement was found, starting
2059 // with the most inner scope and going to the most outer scope (i.e.
2060 // file scope).
2061 int scopeOffset = static_cast<int>(nsName.length());
2062 do
2063 {
2064 DString scope=scopeOffset>0 ?
2065 nsName.left(scopeOffset)+"::" : DString();
2066 usingNd = getResolvedNamespace(scope+name);
2067 //printf("Trying with scope='%s' usingNd=%p\n",(scope+qPrint(name)),usingNd);
2068 if (scopeOffset==0)
2069 {
2070 scopeOffset=-1;
2071 }
2072 else
2073 {
2074 size_t o = nsName.rfind("::",scopeOffset-1);
2075 scopeOffset = o!=DString::npos ? static_cast<int>(o) : 0;
2076 }
2077 } while (scopeOffset>=0 && usingNd==nullptr);
2078
2079 if (usingNd==nullptr && nd) // not found, try used namespaces in this scope
2080 // or in one of the parent namespace scopes
2081 {
2082 const NamespaceDefMutable *pnd = nd;
2083 while (pnd && usingNd==nullptr)
2084 {
2085 // also try with one of the used namespaces found earlier
2087
2088 // goto the parent
2089 Definition *s = pnd->getOuterScope();
2091 {
2093 }
2094 else
2095 {
2096 pnd = nullptr;
2097 }
2098 }
2099 }
2100 if (usingNd==nullptr && fd) // still nothing, also try used namespace in the
2101 // global scope
2102 {
2103 usingNd = findUsedNamespace(fd->getUsedNamespaces(),name);
2104 }
2105
2106 //printf("%s -> %s\n",qPrint(name),usingNd?qPrint(usingNd->name()):"<none>");
2107
2108 // add the namespace the correct scope
2109 if (usingNd)
2110 {
2111 //printf("using fd=%p nd=%p\n",fd,nd);
2112 if (nd)
2113 {
2114 //printf("Inside namespace %s\n",qPrint(nd->name()));
2115 nd->addUsingDirective(usingNd);
2116 }
2117 else if (fd)
2118 {
2119 //printf("Inside file %s\n",qPrint(fd->name()));
2120 fd->addUsingDirective(usingNd);
2121 }
2122 }
2123 else // unknown namespace, but add it anyway.
2124 {
2125 AUTO_TRACE_ADD("new unknown namespace {} lang={} hidden={}",name,root->lang,root->hidden);
2126 // add namespace to the list
2129 createNamespaceDef(root->fileName,root->startLine,root->startColumn,name)));
2130 if (nd)
2131 {
2132 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
2133 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2135 nd->setHidden(root->hidden);
2136 nd->setArtificial(true);
2137 nd->setLanguage(root->lang);
2138 nd->setId(root->id);
2139 nd->setMetaData(root->metaData);
2140 nd->setInline(root->spec.isInline());
2141 nd->setExported(root->exported);
2142
2143 for (const Grouping &g : root->groups)
2144 {
2145 GroupDef *gd=nullptr;
2147 gd->addNamespace(nd);
2148 }
2149
2150 // insert the namespace in the file definition
2151 if (fd)
2152 {
2153 fd->insertNamespace(nd);
2154 fd->addUsingDirective(nd);
2155 }
2156
2157 // the empty string test is needed for extract all case
2158 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2159 nd->insertUsedFile(fd);
2160 nd->setRefItems(root->sli);
2161 nd->setRequirementReferences(root->rqli);
2162 }
2163 }
2164 }
2165 }
2166 for (const auto &e : root->children()) findUsingDirectives(e.get());
2167}
2168
2169//----------------------------------------------------------------------
2170
2171static void buildListOfUsingDecls(const Entry *root)
2172{
2173 if (root->section.isUsingDecl() &&
2174 !root->parent()->section.isCompound() // not a class/struct member
2175 )
2176 {
2177 DString name = substitute(root->name,".","::");
2178 g_usingDeclarations.insert(name.str());
2179 }
2180 for (const auto &e : root->children()) buildListOfUsingDecls(e.get());
2181}
2182
2183
2184static void findUsingDeclarations(const Entry *root,bool filterPythonPackages)
2185{
2186 if (root->section.isUsingDecl() &&
2187 !root->parent()->section.isCompound() && // not a class/struct member
2188 (!filterPythonPackages || (root->lang==SrcLangExt::Python && root->fileName.endsWith("__init__.py")))
2189 )
2190 {
2191 AUTO_TRACE("Found using declaration '{}' at line {} of {} inside section {}",
2192 root->name,root->startLine,root->fileName,root->parent()->section);
2193 if (!root->name.empty())
2194 {
2195 const Definition *usingDef = nullptr;
2196 NamespaceDefMutable *nd = nullptr;
2197 FileDef *fd = root->fileDef();
2198 DString scName;
2199
2200 // see if the using statement was found inside a namespace or inside
2201 // the global file scope.
2202 if (root->parent()->section.isNamespace())
2203 {
2204 scName=root->parent()->name;
2205 if (!scName.empty())
2206 {
2207 nd = getResolvedNamespaceMutable(scName);
2208 }
2209 }
2210
2211 // Assume the using statement was used to import a class.
2212 // Find the scope in which the 'using' namespace is defined by prepending
2213 // the possible scopes in which the using statement was found, starting
2214 // with the most inner scope and going to the most outer scope (i.e.
2215 // file scope).
2216
2217 DString name = substitute(root->name,".","::"); //Java/C# scope->internal
2218
2219 SymbolResolver resolver;
2220 const Definition *scope = nd;
2221 if (nd==nullptr) scope = fd;
2222 usingDef = resolver.resolveSymbol(scope,name);
2223
2224 //printf("usingDef(scope=%s,name=%s)=%s\n",qPrint(nd?nd->qualifiedName():""),qPrint(name),usingDef?qPrint(usingDef->qualifiedName()):"nullptr");
2225
2226 if (!usingDef)
2227 {
2228 usingDef = getClass(name); // try direct lookup, this is needed to get
2229 // builtin STL classes to properly resolve, e.g.
2230 // vector -> std::vector
2231 }
2232 if (!usingDef)
2233 {
2234 usingDef = Doxygen::hiddenClassLinkedMap->find(name); // check if it is already hidden
2235 }
2236#if 0
2237 if (!usingDef)
2238 {
2239 AUTO_TRACE_ADD("New using class '{}' (sec={})! #tArgLists={}",
2240 name,root->section,root->tArgLists.size());
2243 createClassDef( "<using>",1,1, name, ClassDef::Class)));
2244 if (usingCd)
2245 {
2246 usingCd->setArtificial(true);
2247 usingCd->setLanguage(root->lang);
2248 usingDef = usingCd;
2249 }
2250 }
2251#endif
2252 else
2253 {
2254 AUTO_TRACE_ADD("Found used type '{}' in scope='{}'",
2255 usingDef->name(), nd ? nd->name(): fd ? fd->name() : DString("<unknown>"));
2256 }
2257
2258 if (usingDef)
2259 {
2260 if (nd)
2261 {
2262 nd->addUsingDeclaration(usingDef);
2263 }
2264 else if (fd)
2265 {
2266 fd->addUsingDeclaration(usingDef);
2267 }
2268 }
2269 }
2270 }
2271 for (const auto &e : root->children()) findUsingDeclarations(e.get(),filterPythonPackages);
2272}
2273
2274//----------------------------------------------------------------------
2275
2277{
2278 root->commandOverrides.apply_callGraph ([&](bool b) { md->overrideCallGraph(b); });
2279 root->commandOverrides.apply_callerGraph ([&](bool b) { md->overrideCallerGraph(b); });
2280 root->commandOverrides.apply_referencedByRelation([&](bool b) { md->overrideReferencedByRelation(b); });
2281 root->commandOverrides.apply_referencesRelation ([&](bool b) { md->overrideReferencesRelation(b); });
2282 root->commandOverrides.apply_inlineSource ([&](bool b) { md->overrideInlineSource(b); });
2283 root->commandOverrides.apply_enumValues ([&](bool b) { md->overrideEnumValues(b); });
2284}
2285
2286//----------------------------------------------------------------------
2287
2289 const DString &fileName,const DString &memName)
2290{
2291 AUTO_TRACE("creating new member {} for class {}",memName,cd->name());
2292 const ArgumentList &templAl = md->templateArguments();
2293 const ArgumentList &al = md->argumentList();
2294 auto newMd = createMemberDef(
2295 fileName,root->startLine,root->startColumn,
2296 md->typeString(),memName,md->argsString(),
2297 md->excpString(),root->protection,root->virt,
2298 md->isStatic(),Relationship::Member,md->memberType(),
2299 templAl,al,root->metaData
2300 );
2301 auto newMmd = toMemberDefMutable(newMd.get());
2302 newMmd->setMemberClass(cd);
2303 cd->insertMember(newMd.get());
2304 if (!root->doc.empty() || !root->brief.empty())
2305 {
2306 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2307 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2308 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2309 }
2310 else
2311 {
2312 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2313 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2314 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2315 }
2316 newMmd->setDefinition(md->definition());
2317 applyMemberOverrideOptions(root,newMmd);
2318 newMmd->addQualifiers(root->qualifiers);
2319 newMmd->setBitfields(md->bitfieldString());
2320 newMmd->addSectionsToDefinition(root->anchors);
2321 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2322 newMmd->setBodyDef(md->getBodyDef());
2323 newMmd->setInitializer(md->initializer());
2324 newMmd->setRequiresClause(md->requiresClause());
2325 newMmd->setMaxInitLines(md->initializerLines());
2326 newMmd->setMemberGroupId(root->mGrpId);
2327 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2328 newMmd->setVhdlSpecifiers(md->getVhdlSpecifiers());
2329 newMmd->setLanguage(root->lang);
2330 newMmd->setId(root->id);
2332 mn->push_back(std::move(newMd));
2333}
2334
2335static std::unordered_map<std::string,std::vector<ClassDefMutable*>> g_usingClassMap;
2336
2337static void findUsingDeclImports(const Entry *root)
2338{
2339 if (root->section.isUsingDecl() &&
2340 root->parent()->section.isCompound() // in a class/struct member
2341 )
2342 {
2343 AUTO_TRACE("Found using declaration '{}' inside section {}", root->name, root->parent()->section);
2344 DString fullName=removeRedundantWhiteSpace(root->parent()->name);
2345 fullName=stripAnonymousNamespaceScope(fullName);
2346 fullName=stripTemplateSpecifiersFromScope(fullName);
2347 ClassDefMutable *cd = getClassMutable(fullName);
2348 if (cd)
2349 {
2350 AUTO_TRACE_ADD("found class '{}'",cd->name());
2351 size_t i=root->name.rfind("::");
2352 if (i!=DString::npos)
2353 {
2354 DString scope=root->name.left(i);
2355 DString memName=root->name.mid(i+2);
2356 SymbolResolver resolver;
2357 const ClassDef *bcd = resolver.resolveClass(cd,scope); // todo: file in fileScope parameter
2358 AUTO_TRACE_ADD("name={} scope={} bcd={}",scope,cd?cd->name():"<none>",bcd?bcd->name():"<none>");
2359 if (bcd && bcd!=cd)
2360 {
2361 AUTO_TRACE_ADD("found class '{}' memName='{}'",bcd->name(),memName);
2363 const MemberNameInfo *mni = mnlm.find(memName);
2364 if (mni)
2365 {
2366 for (auto &mi : *mni)
2367 {
2368 const MemberDef *md = mi->memberDef();
2369 if (md && md->protection()!=Protection::Private)
2370 {
2371 AUTO_TRACE_ADD("found member '{}'",mni->memberName());
2372 DString fileName = root->fileName;
2373 if (fileName.empty() && root->tagInfo())
2374 {
2375 fileName = root->tagInfo()->tagName;
2376 }
2377 if (!cd->containsOverload(md))
2378 {
2379 createUsingMemberImportForClass(root,cd,md,fileName,memName);
2380 // also insert the member into copies of the class
2381 auto it = g_usingClassMap.find(cd->qualifiedName().str());
2382 if (it != g_usingClassMap.end())
2383 {
2384 for (const auto &copyCd : it->second)
2385 {
2386 createUsingMemberImportForClass(root,copyCd,md,fileName,memName);
2387 }
2388 }
2389 }
2390 }
2391 }
2392 }
2393 }
2394 }
2395 }
2396 }
2397 else if (root->section.isUsingDecl() &&
2398 (root->parent()->section.isNamespace() || root->parent()->section.isEmpty()) && // namespace or global member
2399 root->lang==SrcLangExt::Cpp // do we also want this for e.g. Fortran? (see test case 095)
2400 )
2401 {
2402 AUTO_TRACE("Found using declaration '{}' inside section {}", root->name, root->parent()->section);
2403 Definition *scope = nullptr;
2404 NamespaceDefMutable *nd = nullptr;
2405 FileDef *fd = root->parent()->fileDef();
2406 if (!root->parent()->name.empty())
2407 {
2408 DString fullName=removeRedundantWhiteSpace(root->parent()->name);
2409 fullName=stripAnonymousNamespaceScope(fullName);
2411 scope = nd;
2412 }
2413 else
2414 {
2415 scope = fd;
2416 }
2417 if (scope)
2418 {
2419 AUTO_TRACE_ADD("found scope '{}'",scope->name());
2420 SymbolResolver resolver;
2421 const Definition *def = resolver.resolveSymbol(root->name.startsWith("::") ? nullptr : scope,root->name);
2422 if (def && def->definitionType()==Definition::TypeMember)
2423 {
2424 size_t i=root->name.rfind("::");
2425 DString memName;
2426 if (i!=DString::npos)
2427 {
2428 memName = root->name.right(root->name.length()-i-2);
2429 }
2430 else
2431 {
2432 memName = root->name;
2433 }
2434 const MemberDef *md = toMemberDef(def);
2435 AUTO_TRACE_ADD("found member '{}' for name '{}'",md->qualifiedName(),root->name);
2436 DString fileName = root->fileName;
2437 if (fileName.empty() && root->tagInfo())
2438 {
2439 fileName = root->tagInfo()->tagName;
2440 }
2441 const ArgumentList &templAl = md->templateArguments();
2442 const ArgumentList &al = md->argumentList();
2443
2444 auto newMd = createMemberDef(
2445 fileName,root->startLine,root->startColumn,
2446 md->typeString(),memName,md->argsString(),
2447 md->excpString(),root->protection,root->virt,
2448 md->isStatic(),Relationship::Member,md->memberType(),
2449 templAl,al,root->metaData
2450 );
2451 auto newMmd = toMemberDefMutable(newMd.get());
2452 if (nd)
2453 {
2454 newMmd->setNamespace(nd);
2455 nd->insertMember(newMd.get());
2456 }
2457 if (fd)
2458 {
2459 newMmd->setFileDef(fd);
2460 fd->insertMember(newMd.get());
2461 }
2462 if (!root->doc.empty() || !root->brief.empty())
2463 {
2464 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2465 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2466 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2467 }
2468 else
2469 {
2470 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2471 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2472 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2473 }
2474 newMmd->setDefinition(md->definition());
2475 applyMemberOverrideOptions(root,newMmd);
2476 newMmd->addQualifiers(root->qualifiers);
2477 newMmd->setBitfields(md->bitfieldString());
2478 newMmd->addSectionsToDefinition(root->anchors);
2479 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2480 newMmd->setBodyDef(md->getBodyDef());
2481 newMmd->setInitializer(md->initializer());
2482 newMmd->setRequiresClause(md->requiresClause());
2483 newMmd->setMaxInitLines(md->initializerLines());
2484 newMmd->setMemberGroupId(root->mGrpId);
2485 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2486 newMmd->setVhdlSpecifiers(md->getVhdlSpecifiers());
2487 newMmd->setLanguage(root->lang);
2488 newMmd->setId(root->id);
2490 mn->push_back(std::move(newMd));
2491#if 0 // insert an alias instead of a copy
2492 const MemberDef *md = toMemberDef(def);
2493 AUTO_TRACE_ADD("found member '{}' for name '{}'",md->qualifiedName(),root->name);
2494 auto aliasMd = createMemberDefAlias(nd,md);
2495 DString aliasFullName = nd->qualifiedName()+"::"+aliasMd->localName();
2496 if (nd && aliasMd.get())
2497 {
2498 nd->insertMember(aliasMd.get());
2499 }
2500 if (fd && aliasMd.get())
2501 {
2502 fd->insertMember(aliasMd.get());
2503 }
2504 MemberName *mn = Doxygen::memberNameLinkedMap->add(aliasFullName);
2505 mn->push_back(std::move(aliasMd));
2506#endif
2507 }
2508 else if (def && def->definitionType()==Definition::TypeClass)
2509 {
2510 const ClassDef *cd = toClassDef(def);
2511 DString copyFullName;
2512 if (nd==nullptr)
2513 {
2514 copyFullName = cd->localName();
2515 }
2516 else
2517 {
2518 copyFullName = nd->qualifiedName()+"::"+cd->localName();
2519 }
2520 if (Doxygen::classLinkedMap->find(copyFullName)==nullptr)
2521 {
2523 Doxygen::classLinkedMap->add(copyFullName,
2524 cd->deepCopy(copyFullName)));
2525 AUTO_TRACE_ADD("found class '{}' for name '{}' copy '{}' obj={}",cd->qualifiedName(),root->name,copyFullName,(void*)ncdm);
2526 g_usingClassMap[cd->qualifiedName().str()].push_back(ncdm);
2527 if (ncdm)
2528 {
2529 if (nd) ncdm->moveTo(nd);
2530 if ((!root->doc.empty() || !root->brief.empty())) // use docs at using statement
2531 {
2532 ncdm->setDocumentation(root->doc,root->docFile,root->docLine);
2533 ncdm->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2534 }
2535 else // use docs from used class
2536 {
2537 ncdm->setDocumentation(cd->documentation(),cd->docFile(),cd->docLine());
2539 }
2540 if (nd)
2541 {
2542 nd->addInnerCompound(ncdm);
2543 nd->addUsingDeclaration(ncdm);
2544 }
2545 if (fd)
2546 {
2547 if (ncdm) ncdm->setFileDef(fd);
2548 fd->insertClass(ncdm);
2549 fd->addUsingDeclaration(ncdm);
2550 }
2551 }
2552 }
2553#if 0 // insert an alias instead of a copy
2554 auto aliasCd = createClassDefAlias(nd,cd);
2555 DString aliasFullName;
2556 if (nd==nullptr)
2557 {
2558 aliasFullName = aliasCd->localName();
2559 }
2560 else
2561 {
2562 aliasFullName = nd->qualifiedName()+"::"+aliasCd->localName();
2563 }
2564 AUTO_TRACE_ADD("found class '{}' for name '{}' aliasFullName='{}'",cd->qualifiedName(),root->name,aliasFullName);
2565 auto acd = Doxygen::classLinkedMap->add(aliasFullName,std::move(aliasCd));
2566 if (nd && acd)
2567 {
2568 nd->addInnerCompound(acd);
2569 }
2570 if (fd && acd)
2571 {
2572 fd->insertClass(acd);
2573 }
2574#endif
2575 }
2576 else if (scope)
2577 {
2578 AUTO_TRACE_ADD("no symbol with name '{}' in scope {}",root->name,scope->name());
2579 }
2580 }
2581 }
2582 for (const auto &e : root->children()) findUsingDeclImports(e.get());
2583}
2584
2585//----------------------------------------------------------------------
2586
2588{
2589 FileDefSet visitedFiles;
2590 // then recursively add using directives found in #include files
2591 // to files that have not been visited.
2592 for (const auto &fn : *Doxygen::inputNameLinkedMap)
2593 {
2594 for (const auto &fd : *fn)
2595 {
2596 //printf("----- adding using directives for file %s\n",qPrint(fd->name()));
2597 fd->addIncludedUsingDirectives(visitedFiles);
2598 }
2599 }
2600}
2601
2602//----------------------------------------------------------------------
2603
2605 const Entry *root,
2606 ClassDefMutable *cd,
2607 MemberType mtype,
2608 const DString &type,
2609 const DString &name,
2610 const DString &args,
2611 Protection prot,
2612 Relationship related)
2613{
2615 DString scopeSeparator="::";
2616 SrcLangExt lang = cd->getLanguage();
2617 if (lang==SrcLangExt::Java || lang==SrcLangExt::CSharp)
2618 {
2619 qualScope = substitute(qualScope,"::",".");
2620 scopeSeparator=".";
2621 }
2622 AUTO_TRACE("class variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} init='{}'",
2623 root->fileName, type, qualScope, name, args, root->protection, mtype, lang, root->initializer.str());
2624
2625 DString def;
2626 if (!type.empty())
2627 {
2628 if (related!=Relationship::Member || mtype==MemberType::Friend || Config_getBool(HIDE_SCOPE_NAMES))
2629 {
2630 if (root->spec.isAlias()) // turn 'typedef B A' into 'using A'
2631 {
2632 if (lang==SrcLangExt::Python)
2633 {
2634 def="type "+name+args;
2635 }
2636 else
2637 {
2638 def="using "+name;
2639 }
2640 }
2641 else
2642 {
2643 def=type+" "+name+args;
2644 }
2645 }
2646 else
2647 {
2648 if (root->spec.isAlias()) // turn 'typedef B C::A' into 'using C::A'
2649 {
2650 if (lang==SrcLangExt::Python)
2651 {
2652 def="type "+qualScope+scopeSeparator+name+args;
2653 }
2654 else
2655 {
2656 def="using "+qualScope+scopeSeparator+name;
2657 }
2658 }
2659 else
2660 {
2661 def=type+" "+qualScope+scopeSeparator+name+args;
2662 }
2663 }
2664 }
2665 else
2666 {
2667 if (Config_getBool(HIDE_SCOPE_NAMES))
2668 {
2669 def=name+args;
2670 }
2671 else
2672 {
2673 def=qualScope+scopeSeparator+name+args;
2674 }
2675 }
2676 def.stripPrefix("static ");
2677
2678 // see if the member is already found in the same scope
2679 // (this may be the case for a static member that is initialized
2680 // outside the class)
2682 if (mn)
2683 {
2684 for (const auto &imd : *mn)
2685 {
2686 //printf("md->getClassDef()=%p cd=%p type=[%s] md->typeString()=[%s]\n",
2687 // md->getClassDef(),cd,qPrint(type),md->typeString());
2688 MemberDefMutable *md = toMemberDefMutable(imd.get());
2689 if (md &&
2690 md->getClassDef()==cd &&
2691 ((lang==SrcLangExt::Python && type.empty() && !md->typeString().empty()) ||
2693 // member already in the scope
2694 {
2695
2696 if (root->lang==SrcLangExt::ObjC &&
2697 root->mtype==MethodTypes::Property &&
2698 md->memberType()==MemberType::Variable)
2699 { // Objective-C 2.0 property
2700 // turn variable into a property
2701 md->setProtection(root->protection);
2702 cd->reclassifyMember(md,MemberType::Property);
2703 }
2704 addMemberDocs(root,md,def,nullptr,false,root->spec);
2705 AUTO_TRACE_ADD("Member already found!");
2706 return md;
2707 }
2708 }
2709 }
2710
2711 DString fileName = root->fileName;
2712 if (fileName.empty() && root->tagInfo())
2713 {
2714 fileName = root->tagInfo()->tagName;
2715 }
2716
2717 // new member variable, typedef or enum value
2718 auto md = createMemberDef(
2719 fileName,root->startLine,root->startColumn,
2720 type,name,args,root->exception,
2721 prot,Specifier::Normal,root->isStatic,related,
2722 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2723 ArgumentList(), root->metaData);
2724 auto mmd = toMemberDefMutable(md.get());
2725 mmd->setTagInfo(root->tagInfo());
2726 mmd->setMemberClass(cd); // also sets outer scope (i.e. getOuterScope())
2727 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2728 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2729 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2730 mmd->setDefinition(def);
2731 mmd->setBitfields(root->bitfields);
2732 mmd->addSectionsToDefinition(root->anchors);
2733 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2734 mmd->setInitializer(root->initializer.str());
2735 mmd->setMaxInitLines(root->initLines);
2736 mmd->setMemberGroupId(root->mGrpId);
2737 mmd->setMemberSpecifiers(root->spec);
2738 mmd->setVhdlSpecifiers(root->vhdlSpec);
2739 mmd->setReadAccessor(root->read);
2740 mmd->setWriteAccessor(root->write);
2742 mmd->setHidden(root->hidden);
2743 mmd->setArtificial(root->artificial);
2744 mmd->setLanguage(root->lang);
2745 mmd->setId(root->id);
2746 addMemberToGroups(root,md.get());
2748 mmd->setBodyDef(root->fileDef());
2749 mmd->addQualifiers(root->qualifiers);
2750
2751 AUTO_TRACE_ADD("Adding new member '{}' to class '{}'",name,cd->name());
2752 cd->insertMember(md.get());
2753 mmd->setRefItems(root->sli);
2754 mmd->setRequirementReferences(root->rqli);
2755
2756 cd->insertUsedFile(root->fileDef());
2757 root->markAsProcessed();
2758
2759 if (mtype==MemberType::Typedef)
2760 {
2761 resolveTemplateInstanceInType(root,cd,md.get());
2762 }
2763
2764 // add the member to the global list
2765 MemberDef *result = md.get();
2767 mn->push_back(std::move(md));
2768
2769 return result;
2770}
2771
2772//----------------------------------------------------------------------
2773
2775 const Entry *root,
2776 MemberType mtype,
2777 const DString &scope,
2778 const DString &type,
2779 const DString &name,
2780 const DString &args)
2781{
2782 AUTO_TRACE("global variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} init='{}'",
2783 root->fileName, type, scope, name, args, root->protection, mtype, root->lang, root->initializer.str());
2784
2785 FileDef *fd = root->fileDef();
2786
2787 // see if we have a typedef that should hide a struct or union
2788 if (mtype==MemberType::Typedef && Config_getBool(TYPEDEF_HIDES_STRUCT))
2789 {
2790 DString ttype = type;
2791 ttype.stripPrefix("typedef ");
2792 if (ttype.stripPrefix("struct ") || ttype.stripPrefix("union "))
2793 {
2794 static const reg::Ex re(R"(\a\w*)");
2795 reg::Match match;
2796 const std::string &typ = ttype.str();
2797 if (reg::search(typ,match,re))
2798 {
2799 DString typeValue = match.str();
2800 ClassDefMutable *cd = getClassMutable(typeValue);
2801 if (cd)
2802 {
2803 // this typedef should hide compound name cd, so we
2804 // change the name that is displayed from cd.
2805 cd->setClassName(name);
2806 cd->setDocumentation(root->doc,root->docFile,root->docLine);
2807 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2808 return nullptr;
2809 }
2810 }
2811 }
2812 }
2813
2814 // see if the function is inside a namespace
2815 NamespaceDefMutable *nd = nullptr;
2816 if (!scope.empty())
2817 {
2818 if (scope.find('@')!=DString::npos) return nullptr; // anonymous scope!
2819 nd = getResolvedNamespaceMutable(scope);
2820 }
2821 DString def;
2822
2823 // determine the definition of the global variable
2824 if (nd && !nd->isAnonymous() &&
2825 !Config_getBool(HIDE_SCOPE_NAMES)
2826 )
2827 // variable is inside a namespace, so put the scope before the name
2828 {
2829 SrcLangExt lang = nd->getLanguage();
2831
2832 if (!type.empty())
2833 {
2834 if (root->spec.isAlias()) // turn 'typedef B NS::A' into 'using NS::A'
2835 {
2836 if (lang==SrcLangExt::Python)
2837 {
2838 def="type "+nd->name()+sep+name+args;
2839 }
2840 else
2841 {
2842 def="using "+nd->name()+sep+name;
2843 }
2844 }
2845 else // normal member
2846 {
2847 def=type+" "+nd->name()+sep+name+args;
2848 }
2849 }
2850 else
2851 {
2852 def=nd->name()+sep+name+args;
2853 }
2854 }
2855 else
2856 {
2857 if (!type.empty() && !root->name.empty())
2858 {
2859 if (name.at(0)=='@') // dummy variable representing anonymous union
2860 {
2861 def=type;
2862 }
2863 else
2864 {
2865 if (root->spec.isAlias()) // turn 'typedef B A' into 'using A'
2866 {
2867 if (root->lang==SrcLangExt::Python)
2868 {
2869 def="type "+root->name+args;
2870 }
2871 else
2872 {
2873 def="using "+root->name;
2874 }
2875 }
2876 else // normal member
2877 {
2878 def=type+" "+name+args;
2879 }
2880 }
2881 }
2882 else
2883 {
2884 def=name+args;
2885 }
2886 }
2887 def.stripPrefix("static ");
2888
2890 if (mn)
2891 {
2892 //DString nscope=removeAnonymousScopes(scope);
2893 //NamespaceDef *nd=nullptr;
2894 //if (!nscope.empty())
2895 if (!scope.empty())
2896 {
2897 nd = getResolvedNamespaceMutable(scope);
2898 }
2899 for (const auto &imd : *mn)
2900 {
2901 MemberDefMutable *md = toMemberDefMutable(imd.get());
2902 if (md &&
2903 ((nd==nullptr && md->getNamespaceDef()==nullptr && md->getFileDef() &&
2904 root->fileName==md->getFileDef()->absFilePath()
2905 ) // both variable names in the same file
2906 || (nd!=nullptr && md->getNamespaceDef()==nd) // both in same namespace
2907 )
2908 && !md->isDefine() // function style #define's can be "overloaded" by typedefs or variables
2909 && !md->isEnumerate() // in C# an enum value and enum can have the same name
2910 )
2911 // variable already in the scope
2912 {
2913 bool isPHPArray = md->getLanguage()==SrcLangExt::PHP &&
2914 md->argsString()!=args &&
2915 args.find('[')!=DString::npos;
2916 bool staticsInDifferentFiles =
2917 root->isStatic && md->isStatic() &&
2918 root->fileName!=md->getDefFileName();
2919
2920 if (md->getFileDef() &&
2921 !isPHPArray && // not a php array
2922 !staticsInDifferentFiles
2923 )
2924 // not a php array variable
2925 {
2926 AUTO_TRACE_ADD("variable already found: scope='{}'",md->getOuterScope()->name());
2927 addMemberDocs(root,md,def,nullptr,false,root->spec);
2928 md->setRefItems(root->sli);
2929 md->setRequirementReferences(root->rqli);
2930 // if md is a variable forward declaration and root is the definition that
2931 // turn md into the definition
2932 if (!root->explicitExternal && md->isExternal())
2933 {
2934 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
2935 md->setExplicitExternal(false,root->fileName,root->startLine,root->startColumn);
2936 }
2937 // if md is the definition and root point at a declaration, then add the
2938 // declaration info
2939 else if (root->explicitExternal && !md->isExternal())
2940 {
2941 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
2942 }
2943 return md;
2944 }
2945 }
2946 }
2947 }
2948
2949 DString fileName = root->fileName;
2950 if (fileName.empty() && root->tagInfo())
2951 {
2952 fileName = root->tagInfo()->tagName;
2953 }
2954
2955 AUTO_TRACE_ADD("new variable, namespace='{}'",nd?nd->name():DString("<global>"));
2956 // new global variable, enum value or typedef
2957 auto md = createMemberDef(
2958 fileName,root->startLine,root->startColumn,
2959 type,name,args,DString(),
2960 root->protection, Specifier::Normal,root->isStatic,Relationship::Member,
2961 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2962 root->argList, root->metaData);
2963 auto mmd = toMemberDefMutable(md.get());
2964 mmd->setTagInfo(root->tagInfo());
2965 mmd->setMemberSpecifiers(root->spec);
2966 mmd->setVhdlSpecifiers(root->vhdlSpec);
2967 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2968 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2969 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2970 mmd->addSectionsToDefinition(root->anchors);
2971 mmd->setInitializer(root->initializer.str());
2972 mmd->setMaxInitLines(root->initLines);
2973 mmd->setMemberGroupId(root->mGrpId);
2974 mmd->setDefinition(def);
2975 mmd->setLanguage(root->lang);
2976 mmd->setId(root->id);
2978 mmd->setExplicitExternal(root->explicitExternal,fileName,root->startLine,root->startColumn);
2979 mmd->addQualifiers(root->qualifiers);
2980 //md->setOuterScope(fd);
2981 if (!root->explicitExternal)
2982 {
2983 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2984 mmd->setBodyDef(fd);
2985 }
2986 addMemberToGroups(root,md.get());
2988
2989 mmd->setRefItems(root->sli);
2990 mmd->setRequirementReferences(root->rqli);
2991 if (nd && !nd->isAnonymous())
2992 {
2993 mmd->setNamespace(nd);
2994 nd->insertMember(md.get());
2995 }
2996
2997 // add member to the file (we do this even if we have already inserted
2998 // it into the namespace.
2999 if (fd)
3000 {
3001 mmd->setFileDef(fd);
3002 fd->insertMember(md.get());
3003 }
3004
3005 root->markAsProcessed();
3006
3007 if (mtype==MemberType::Typedef)
3008 {
3009 resolveTemplateInstanceInType(root,nd,md.get());
3010 }
3011
3012 // add member definition to the list of globals
3013 MemberDef *result = md.get();
3015 mn->push_back(std::move(md));
3016
3017
3018
3019 return result;
3020}
3021
3022/*! See if the return type string \a type is that of a function pointer
3023 * \returns -1 if this is not a function pointer variable or
3024 * the index at which the closing brace of (...*name) was found.
3025 */
3026static int findFunctionPtr(const std::string &type,SrcLangExt lang, int *pLength=nullptr)
3027{
3028 AUTO_TRACE("type='{}' lang={}",type,lang);
3029 if (lang == SrcLangExt::Fortran || lang == SrcLangExt::VHDL)
3030 {
3031 return -1; // Fortran and VHDL do not have function pointers
3032 }
3033
3034 static const reg::Ex re(R"(\‍([^)]*[*&^][^)]*\))");
3035 reg::Match match;
3036 size_t i=std::string::npos;
3037 size_t l=0;
3038 if (reg::search(type,match,re)) // contains (...*...) or (...&...) or (...^...)
3039 {
3040 i = match.position();
3041 l = match.length();
3042 }
3043 if (i!=std::string::npos)
3044 {
3045 size_t di = type.find("decltype(");
3046 if (di!=std::string::npos && di<i)
3047 {
3048 i = std::string::npos;
3049 }
3050 }
3051 size_t bb=type.find('<');
3052 size_t be=type.rfind('>');
3053 bool templFp = false;
3054 if (be!=std::string::npos) {
3055 size_t cc_ast = type.find("::*");
3056 size_t cc_amp = type.find("::&");
3057 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>::*)'
3058 }
3059
3060 if (!type.empty() && // return type is non-empty
3061 i!=std::string::npos && // contains (...*...)
3062 type.find("operator")==std::string::npos && // not an operator
3063 (type.find(")(")==std::string::npos || type.find("typedef ")!=std::string::npos) &&
3064 // not a function pointer return type
3065 (!((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
3066 )
3067 {
3068 if (pLength) *pLength=static_cast<int>(l);
3069 //printf("findFunctionPtr=%d\n",(int)i);
3070 AUTO_TRACE_EXIT("result={}",i);
3071 return static_cast<int>(i);
3072 }
3073 else
3074 {
3075 //printf("findFunctionPtr=%d\n",-1);
3076 AUTO_TRACE_EXIT("result=-1");
3077 return -1;
3078 }
3079}
3080
3081//--------------------------------------------------------------------------------------
3082
3083/*! Returns true iff \a type is a class within scope \a context.
3084 * Used to detect variable declarations that look like function prototypes.
3085 */
3086static bool isVarWithConstructor(const Entry *root)
3087{
3088 bool result = false;
3089 bool typeIsClass = false;
3090 bool typePtrType = false;
3091 DString type;
3092 Definition *ctx = nullptr;
3093 FileDef *fd = root->fileDef();
3094 SymbolResolver resolver(fd);
3095
3096 AUTO_TRACE("isVarWithConstructor({})",root->name);
3097 if (root->parent()->section.isCompound())
3098 { // inside a class
3099 result=false;
3100 AUTO_TRACE_EXIT("inside class: result={}",result);
3101 return result;
3102 }
3103 else if ((fd != nullptr) && (fd->name().endsWith(".c") || fd->name().endsWith(".h")))
3104 { // inside a .c file
3105 result=false;
3106 AUTO_TRACE_EXIT("inside C file: result={}",result);
3107 return result;
3108 }
3109 if (root->type.empty())
3110 {
3111 result=false;
3112 AUTO_TRACE_EXIT("no type: result={}",result);
3113 return result;
3114 }
3115 if (!root->parent()->name.empty())
3116 {
3118 }
3119 type = root->type;
3120 // remove qualifiers
3121 type.findAndRemoveWord("const");
3122 type.findAndRemoveWord("static");
3123 type.findAndRemoveWord("volatile");
3124 typePtrType = type.find('*')!=DString::npos || type.find('&')!=DString::npos;
3125 if (!typePtrType)
3126 {
3127 typeIsClass = resolver.resolveClass(ctx,type)!=nullptr;
3128 if (size_t ti=type.find('<'); !typeIsClass && ti!=DString::npos)
3129 {
3130 typeIsClass=resolver.resolveClass(ctx,type.left(ti))!=nullptr;
3131 }
3132 }
3133 if (typeIsClass) // now we still have to check if the arguments are
3134 // types or values. Since we do not have complete type info
3135 // we need to rely on heuristics :-(
3136 {
3137 if (root->argList.empty())
3138 {
3139 result=false; // empty arg list -> function prototype.
3140 AUTO_TRACE_EXIT("empty arg list: result={}",result);
3141 return result;
3142 }
3143 for (const Argument &a : root->argList)
3144 {
3145 static const reg::Ex initChars(R"([\d"'&*!^]+)");
3146 reg::Match match;
3147 if (!a.name.empty() || !a.defval.empty())
3148 {
3149 std::string name = a.name.str();
3150 if (reg::search(name,match,initChars) && match.position()==0)
3151 {
3152 result=true;
3153 }
3154 else
3155 {
3156 result=false; // arg has (type,name) pair -> function prototype
3157 }
3158 AUTO_TRACE_EXIT("function prototype: result={}",result);
3159 return result;
3160 }
3161 if (!a.type.empty() &&
3162 (a.type.at(a.type.length()-1)=='*' ||
3163 a.type.at(a.type.length()-1)=='&'))
3164 // type ends with * or & => pointer or reference
3165 {
3166 result=false;
3167 AUTO_TRACE_EXIT("pointer or reference: result={}",result);
3168 return result;
3169 }
3170 if (a.type.empty() || resolver.resolveClass(ctx,a.type)!=nullptr)
3171 {
3172 result=false; // arg type is a known type
3173 AUTO_TRACE_EXIT("known type: result={}",result);
3174 return result;
3175 }
3176 if (checkIfTypedef(ctx,fd,a.type))
3177 {
3178 result=false; // argument is a typedef
3179 AUTO_TRACE_EXIT("typedef: result={}",result);
3180 return result;
3181 }
3182 std::string atype = a.type.str();
3183 if (reg::search(atype,match,initChars) && match.position()==0)
3184 {
3185 result=true; // argument type starts with typical initializer char
3186 AUTO_TRACE_EXIT("argument with init char: result={}",result);
3187 return result;
3188 }
3189 std::string resType=resolveTypeDef(ctx,a.type).str();
3190 if (resType.empty()) resType=atype;
3191 static const reg::Ex idChars(R"(\a\w*)");
3192 if (reg::search(resType,match,idChars) && match.position()==0) // resType starts with identifier
3193 {
3194 resType=match.str();
3195 if (resType=="int" || resType=="long" ||
3196 resType=="float" || resType=="double" ||
3197 resType=="char" || resType=="void" ||
3198 resType=="signed" || resType=="unsigned" ||
3199 resType=="const" || resType=="volatile" )
3200 {
3201 result=false; // type keyword -> function prototype
3202 AUTO_TRACE_EXIT("type keyword: result={}",result);
3203 return result;
3204 }
3205 }
3206 }
3207 result=true;
3208 }
3209
3210 AUTO_TRACE_EXIT("end: result={}",result);
3211 return result;
3212}
3213
3214//--------------------------------------------------------------------------------------
3215
3216/*! Searches for the end of a template in prototype \a s starting from
3217 * character position \a startPos. If the end was found the position
3218 * of the closing > is returned, otherwise -1 is returned.
3219 *
3220 * Handles exotic cases such as
3221 * \code
3222 * Class<(id<0)>
3223 * Class<bits<<2>
3224 * Class<"<">
3225 * Class<'<'>
3226 * Class<(")<")>
3227 * \endcode
3228 */
3229static int findEndOfTemplate(const DString &s,size_t startPos)
3230{
3231 // locate end of template
3232 size_t e=startPos;
3233 int brCount=1;
3234 int roundCount=0;
3235 size_t len = s.length();
3236 bool insideString=false;
3237 bool insideChar=false;
3238 char pc = 0;
3239 while (e<len && brCount!=0)
3240 {
3241 char c=s.at(e);
3242 switch(c)
3243 {
3244 case '<':
3245 if (!insideString && !insideChar)
3246 {
3247 if (e<len-1 && s.at(e+1)=='<')
3248 e++;
3249 else if (roundCount==0)
3250 brCount++;
3251 }
3252 break;
3253 case '>':
3254 if (!insideString && !insideChar)
3255 {
3256 if (e<len-1 && s.at(e+1)=='>')
3257 e++;
3258 else if (roundCount==0)
3259 brCount--;
3260 }
3261 break;
3262 case '(':
3263 if (!insideString && !insideChar)
3264 roundCount++;
3265 break;
3266 case ')':
3267 if (!insideString && !insideChar)
3268 roundCount--;
3269 break;
3270 case '"':
3271 if (!insideChar)
3272 {
3273 if (insideString && pc!='\\')
3274 insideString=false;
3275 else
3276 insideString=true;
3277 }
3278 break;
3279 case '\'':
3280 if (!insideString)
3281 {
3282 if (insideChar && pc!='\\')
3283 insideChar=false;
3284 else
3285 insideChar=true;
3286 }
3287 break;
3288 }
3289 pc = c;
3290 e++;
3291 }
3292 return brCount==0 ? static_cast<int>(e) : -1;
3293}
3294
3295//--------------------------------------------------------------------------------------
3296
3297static void addVariable(const Entry *root,int isFuncPtr=-1)
3298{
3299 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
3300
3301 AUTO_TRACE("VARIABLE_SEC: type='{}' name='{}' args='{}' bodyLine={} endBodyLine={} mGrpId={} relates='{}'",
3302 root->type, root->name, root->args, root->bodyLine, root->endBodyLine, root->mGrpId, root->relates);
3303 //printf("root->parent->name=%s\n",qPrint(root->parent->name));
3304
3305 DString type = root->type;
3306 DString name = root->name;
3307 DString args = root->args;
3308 if (type.empty() && name.find("operator")==DString::npos &&
3309 (name.find('*')!=DString::npos || name.find('&')!=DString::npos))
3310 {
3311 // recover from parse error caused by redundant braces
3312 // like in "int *(var[10]);", which is parsed as
3313 // type="" name="int *" args="(var[10])"
3314
3315 type=name;
3316 std::string sargs = args.str();
3317 static const reg::Ex reName(R"(\a\w*)");
3318 reg::Match match;
3319 if (reg::search(sargs,match,reName))
3320 {
3321 name = match.str(); // e.g. 'var' in '(var[10])'
3322 sargs = match.suffix().str(); // e.g. '[10]) in '(var[10])'
3323 size_t j = sargs.find(')');
3324 if (j!=std::string::npos) args=sargs.substr(0,j); // extract, e.g '[10]' from '[10])'
3325 }
3326 }
3327 else
3328 {
3329 int i=isFuncPtr;
3330 if (i==-1 && (root->spec.isAlias())==0) i=findFunctionPtr(type.str(),root->lang); // for typedefs isFuncPtr is not yet set
3331 AUTO_TRACE_ADD("functionPtr={}",i!=-1?"yes":"no");
3332 if (i>=0) // function pointer
3333 {
3334 size_t ii = i;
3335 size_t ai = type.find('[',ii);
3336 if (ai!=DString::npos && ai>ii) // function pointer array
3337 {
3338 args.prepend(type.mid(ai));
3339 type=type.left(ai);
3340 }
3341 else if (type.find(')',ii)!=DString::npos) // function ptr, not variable like "int (*bla)[10]"
3342 {
3343 type=type.left(type.length()-1);
3344 args.prepend(") ");
3345 }
3346 }
3347 }
3348 AUTO_TRACE_ADD("after correction: type='{}' name='{}' args='{}'",type,name,args);
3349
3350 DString scope;
3351 name=removeRedundantWhiteSpace(name);
3352
3353 // find the scope of this variable
3354 int index = computeQualifiedIndex(name);
3355 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3356 // grouped members are stored with full scope
3357 {
3358 buildScopeFromQualifiedName(name.left(index+2),root->lang,root->tagInfo());
3359 scope=name.left(index);
3360 name=name.mid(index+2);
3361 }
3362 else
3363 {
3364 Entry *p = root->parent();
3365 while (p->section.isScope())
3366 {
3367 DString scopeName = p->name;
3368 if (!scopeName.empty())
3369 {
3370 scope.prepend(scopeName);
3371 break;
3372 }
3373 p=p->parent();
3374 }
3375 }
3376
3377 DString type_s = type;
3378 type=type.stripWhiteSpace();
3379 ClassDefMutable *cd=nullptr;
3380 bool isRelated=false;
3381 bool isMemberOf=false;
3382
3383 DString classScope=stripAnonymousNamespaceScope(scope);
3384 if (root->lang==SrcLangExt::CSharp)
3385 {
3386 classScope=mangleCSharpGenericName(classScope);
3387 }
3388 else
3389 {
3390 classScope=stripTemplateSpecifiersFromScope(classScope,false);
3391 }
3392 DString annScopePrefix=scope.left(scope.length()-classScope.length());
3393
3394
3395 // Look for last :: not part of template specifier
3396 int p=-1;
3397 for (size_t i=0;i<name.length()-1;i++)
3398 {
3399 if (name[i]==':' && name[i+1]==':')
3400 {
3401 p=static_cast<int>(i);
3402 }
3403 else if (name[i]=='<') // skip over template parts,
3404 // i.e. A::B<C::D> => p=1 and
3405 // A<B::C>::D => p=8
3406 {
3407 int e = findEndOfTemplate(name,i+1);
3408 if (e!=-1) i=static_cast<int>(e);
3409 }
3410 }
3411
3412 if (p!=-1) // found it
3413 {
3414 if (isTypeAClassFriend(type))
3415 {
3416 cd=getClassMutable(scope);
3417 if (cd)
3418 {
3419 addVariableToClass(root, // entry
3420 cd, // class to add member to
3421 MemberType::Friend, // type of member
3422 type, // type value as string
3423 name, // name of the member
3424 args, // arguments as string
3425 Protection::Public, // protection
3426 Relationship::Member // related to a class
3427 );
3428 }
3429 }
3430 if (root->bodyLine!=-1 && root->endBodyLine!=-1) // store the body location for later use
3431 {
3432 Doxygen::staticInitMap.emplace(name.str(),BodyInfo{root->startLine,root->bodyLine,root->endBodyLine});
3433 }
3434
3435
3436 AUTO_TRACE_ADD("static variable {} body=[{}..{}]",name,root->bodyLine,root->endBodyLine);
3437 return; /* skip this member, because it is a
3438 * static variable definition (always?), which will be
3439 * found in a class scope as well, but then we know the
3440 * correct protection level, so only then it will be
3441 * inserted in the correct list!
3442 */
3443 }
3444
3445 MemberType mtype = MemberType::Variable;
3446 if (type=="@")
3447 mtype=MemberType::EnumValue;
3448 else if (type_s.startsWith("typedef "))
3449 mtype=MemberType::Typedef;
3450 else if (type_s.startsWith("friend ") || type_s=="friend")
3451 mtype=MemberType::Friend;
3452 else if (root->mtype==MethodTypes::Property)
3453 mtype=MemberType::Property;
3454 else if (root->mtype==MethodTypes::Event)
3455 mtype=MemberType::Event;
3456 else if (type.find("sequence<") != DString::npos)
3457 mtype=sliceOpt ? MemberType::Sequence : MemberType::Typedef;
3458 else if (type.find("dictionary<") != DString::npos)
3459 mtype=sliceOpt ? MemberType::Dictionary : MemberType::Typedef;
3460
3461 if (!root->relates.empty()) // related variable
3462 {
3463 isRelated=true;
3464 isMemberOf=(root->relatesType==RelatesType::MemberOf);
3465 if (getClass(root->relates)==nullptr && !scope.empty())
3466 scope=mergeScopes(scope,root->relates);
3467 else
3468 scope=root->relates;
3469 }
3470
3471 cd=getClassMutable(scope);
3472 if (cd==nullptr && classScope!=scope) cd=getClassMutable(classScope);
3473 if (cd)
3474 {
3475 // if cd is an anonymous (=tag less) scope we insert the member
3476 // into a non-anonymous parent scope as well. This is needed to
3477 // be able to refer to it using \var or \fn
3478
3479 Relationship relationship = isMemberOf ? Relationship::Foreign :
3480 isRelated ? Relationship::Related :
3481 Relationship::Member ;
3482
3483 addVariableToClass(root, // entry
3484 cd, // class to add member to
3485 mtype, // member type
3486 type, // type value as string
3487 name, // name of the member
3488 args, // arguments as string
3489 root->protection,
3490 relationship
3491 );
3492 }
3493 else if (!name.empty()) // global variable
3494 {
3495 addVariableToFile(root,mtype,scope,type,name,args);
3496 }
3497
3498}
3499
3500//----------------------------------------------------------------------
3501// Searches the Entry tree for typedef documentation sections.
3502// If found they are stored in their class or in the global list.
3503static void buildTypedefList(const Entry *root)
3504{
3505 //printf("buildVarList(%s)\n",qPrint(rootNav->name()));
3506 if (!root->name.empty() &&
3507 root->section.isVariable() &&
3508 root->type.find("typedef ")!=DString::npos // its a typedef
3509 )
3510 {
3511 AUTO_TRACE();
3512 DString rname = removeRedundantWhiteSpace(root->name);
3513 DString scope;
3514 int index = computeQualifiedIndex(rname);
3515 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3516 // grouped members are stored with full scope
3517 {
3518 buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
3519 scope=rname.left(index);
3520 rname=rname.mid(index+2);
3521 }
3522 else
3523 {
3524 scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
3525 }
3529 bool found=false;
3530 if (mn) // symbol with the same name already found
3531 {
3532 for (auto &imd : *mn)
3533 {
3534 if (!imd->isTypedef())
3535 continue;
3536
3537 DString rtype = root->type;
3538 rtype.stripPrefix("typedef ");
3539
3540 // merge the typedefs only if they're not both grouped, and both are
3541 // either part of the same class, part of the same namespace, or both
3542 // are global (i.e., neither in a class or a namespace)
3543 bool notBothGrouped = root->groups.empty() || imd->getGroupDef()==nullptr; // see example #100
3544 bool bothSameScope = (!cd && !nd) || (cd && imd->getClassDef() == cd) || (nd && imd->getNamespaceDef() == nd);
3545 //printf("imd->isTypedef()=%d imd->typeString()=%s root->type=%s\n",imd->isTypedef(),
3546 // qPrint(imd->typeString()),qPrint(root->type));
3547 if (notBothGrouped && bothSameScope && imd->typeString()==rtype)
3548 {
3549 MemberDefMutable *md = toMemberDefMutable(imd.get());
3550 if (md)
3551 {
3552 md->setDocumentation(root->doc,root->docFile,root->docLine);
3554 md->setDocsForDefinition(!root->proto);
3555 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3557 md->setRefItems(root->sli);
3558 md->setRequirementReferences(root->rqli);
3559 md->addQualifiers(root->qualifiers);
3560
3561 // merge ingroup specifiers
3562 if (md->getGroupDef()==nullptr && !root->groups.empty())
3563 {
3564 addMemberToGroups(root,md);
3565 }
3566 else if (md->getGroupDef()!=nullptr && root->groups.empty())
3567 {
3568 //printf("existing member is grouped, new member not\n");
3569 }
3570 else if (md->getGroupDef()!=nullptr && !root->groups.empty())
3571 {
3572 //printf("both members are grouped\n");
3573 }
3574 found=true;
3575 break;
3576 }
3577 }
3578 }
3579 }
3580 if (found)
3581 {
3582 AUTO_TRACE_ADD("typedef '{}' already found",rname);
3583 // mark the entry as processed, as we copied everything from it elsewhere
3584 // also, otherwise, due to containing `typedef` it may later get treated
3585 // as a function typedef in filterMemberDocumentation, which is incorrect
3586 root->markAsProcessed();
3587 }
3588 else
3589 {
3590 AUTO_TRACE_ADD("new typedef '{}'",rname);
3591 addVariable(root);
3592 }
3593
3594 }
3595 for (const auto &e : root->children())
3596 if (!e->section.isEnum())
3597 buildTypedefList(e.get());
3598}
3599
3600//----------------------------------------------------------------------
3601// Searches the Entry tree for sequence documentation sections.
3602// If found they are stored in the global list.
3603static void buildSequenceList(const Entry *root)
3604{
3605 if (!root->name.empty() &&
3606 root->section.isVariable() &&
3607 root->type.find("sequence<")!=DString::npos // it's a sequence
3608 )
3609 {
3610 AUTO_TRACE();
3611 addVariable(root);
3612 }
3613 for (const auto &e : root->children())
3614 if (!e->section.isEnum())
3615 buildSequenceList(e.get());
3616}
3617
3618//----------------------------------------------------------------------
3619// Searches the Entry tree for dictionary documentation sections.
3620// If found they are stored in the global list.
3621static void buildDictionaryList(const Entry *root)
3622{
3623 if (!root->name.empty() &&
3624 root->section.isVariable() &&
3625 root->type.find("dictionary<")!=DString::npos // it's a dictionary
3626 )
3627 {
3628 AUTO_TRACE();
3629 addVariable(root);
3630 }
3631 for (const auto &e : root->children())
3632 if (!e->section.isEnum())
3633 buildDictionaryList(e.get());
3634}
3635
3636//----------------------------------------------------------------------
3637// Searches the Entry tree for Variable documentation sections.
3638// If found they are stored in their class or in the global list.
3639
3640static void buildVarList(const Entry *root)
3641{
3642 //printf("buildVarList(%s) section=%08x\n",qPrint(rootNav->name()),rootNav->section());
3643 int isFuncPtr=-1;
3644 if (!root->name.empty() &&
3645 (root->type.empty() || g_compoundKeywords.find(root->type.str())==g_compoundKeywords.end()) &&
3646 (
3647 (root->section.isVariable() && // it's a variable
3648 root->type.find("typedef ")==DString::npos // and not a typedef
3649 ) ||
3650 (root->section.isFunction() && // or maybe a function pointer variable
3651 (isFuncPtr=findFunctionPtr(root->type.str(),root->lang))!=-1
3652 ) ||
3653 (root->section.isFunction() && // class variable initialized by constructor
3655 )
3656 )
3657 ) // documented variable
3658 {
3659 AUTO_TRACE();
3660 addVariable(root,isFuncPtr);
3661 }
3662 for (const auto &e : root->children())
3663 if (!e->section.isEnum())
3664 buildVarList(e.get());
3665}
3666
3667//----------------------------------------------------------------------
3668// Searches the Entry tree for Interface sections (UNO IDL only).
3669// If found they are stored in their service or in the global list.
3670//
3671
3673 const Entry *root,
3674 ClassDefMutable *cd,
3675 DString const& rname)
3676{
3677 FileDef *fd = root->fileDef();
3678 enum MemberType type = root->section.isExportedInterface() ? MemberType::Interface : MemberType::Service;
3679 DString fileName = root->fileName;
3680 if (fileName.empty() && root->tagInfo())
3681 {
3682 fileName = root->tagInfo()->tagName;
3683 }
3684 auto md = createMemberDef(
3685 fileName, root->startLine, root->startColumn, root->type, rname,
3686 "", "", root->protection, root->virt, root->isStatic, Relationship::Member,
3687 type, ArgumentList(), root->argList, root->metaData);
3688 auto mmd = toMemberDefMutable(md.get());
3689 mmd->setTagInfo(root->tagInfo());
3690 mmd->setMemberClass(cd);
3691 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3692 mmd->setDocsForDefinition(false);
3693 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3694 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3695 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3696 mmd->setMemberSpecifiers(root->spec);
3697 mmd->setVhdlSpecifiers(root->vhdlSpec);
3698 mmd->setMemberGroupId(root->mGrpId);
3699 mmd->setTypeConstraints(root->typeConstr);
3700 mmd->setLanguage(root->lang);
3701 mmd->setBodyDef(fd);
3702 mmd->setFileDef(fd);
3703 mmd->addSectionsToDefinition(root->anchors);
3704 DString const def = root->type + " " + rname;
3705 mmd->setDefinition(def);
3707 mmd->addQualifiers(root->qualifiers);
3708
3709 AUTO_TRACE("Interface member: fileName='{}' type='{}' name='{}' mtype='{}' prot={} virt={} state={} proto={} def='{}'",
3710 fileName,root->type,rname,type,root->protection,root->virt,root->isStatic,root->proto,def);
3711
3712 // add member to the class cd
3713 cd->insertMember(md.get());
3714 // also add the member as a "base" (to get nicer diagrams)
3715 // "optional" interface/service get Protected which turns into dashed line
3716 BaseInfo base(rname,
3717 root->spec.isOptional() ? Protection::Protected : Protection::Public, Specifier::Normal);
3718 TemplateNameMap templateNames;
3719 findClassRelation(root,cd,cd,&base,templateNames,DocumentedOnly,true) ||
3720 findClassRelation(root,cd,cd,&base,templateNames,Undocumented,true);
3721 // add file to list of used files
3722 cd->insertUsedFile(fd);
3723
3724 addMemberToGroups(root,md.get());
3726 root->markAsProcessed();
3727 mmd->setRefItems(root->sli);
3728 mmd->setRequirementReferences(root->rqli);
3729
3730 // add member to the global list of all members
3732 mn->push_back(std::move(md));
3733}
3734
3735static void buildInterfaceAndServiceList(const Entry *root)
3736{
3737 if (root->section.isExportedInterface() || root->section.isIncludedService())
3738 {
3739 AUTO_TRACE("Exported interface/included service: type='{}' scope='{}' name='{}' args='{}'"
3740 " relates='{}' relatesType='{}' file='{}' line={} bodyLine={} #tArgLists={}"
3741 " mGrpId={} spec={} proto={} docFile='{}'",
3742 root->type, root->parent()->name, root->name, root->args,
3743 root->relates, root->relatesType, root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(),
3744 root->mGrpId, root->spec, root->proto, root->docFile);
3745
3746 DString rname = removeRedundantWhiteSpace(root->name);
3747
3748 if (!rname.empty())
3749 {
3750 DString scope = root->parent()->name;
3751 ClassDefMutable *cd = getClassMutable(scope);
3752 ASSERT(cd);
3753 if (cd && ((ClassDef::Interface == cd->compoundType()) ||
3754 (ClassDef::Service == cd->compoundType()) ||
3756 {
3758 }
3759 else
3760 {
3761 ASSERT(false); // was checked by scanner.l
3762 }
3763 }
3764 else if (rname.empty())
3765 {
3766 warn(root->fileName,root->startLine,
3767 "Illegal member name found.");
3768 }
3769 }
3770 // can only have these in IDL anyway
3771 switch (root->lang)
3772 {
3773 case SrcLangExt::Unknown: // fall through (root node always is Unknown)
3774 case SrcLangExt::IDL:
3775 for (const auto &e : root->children()) buildInterfaceAndServiceList(e.get());
3776 break;
3777 default:
3778 return; // nothing to do here
3779 }
3780}
3781
3782
3783//----------------------------------------------------------------------
3784// Searches the Entry tree for Function sections.
3785// If found they are stored in their class or in the global list.
3786
3787static void addMethodToClass(const Entry *root,ClassDefMutable *cd,
3788 const DString &rtype,const DString &rname,const DString &rargs,
3789 bool isFriend,
3790 Protection protection,bool stat,Specifier virt,TypeSpecifier spec,
3791 const DString &relates
3792 )
3793{
3794 FileDef *fd=root->fileDef();
3795
3796 DString type = rtype;
3797 DString args = rargs;
3798
3800 name.stripPrefix("::");
3801
3802 MemberType mtype = MemberType::Function;
3803 if (isFriend) mtype=MemberType::Friend;
3804 else if (root->mtype==MethodTypes::Signal) mtype=MemberType::Signal;
3805 else if (root->mtype==MethodTypes::Slot) mtype=MemberType::Slot;
3806 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType::DCOP;
3807
3808 // strip redundant template specifier for constructors
3809 size_t i = DString::npos;
3810 size_t j = DString::npos;
3811 if ((fd==nullptr || fd->getLanguage()==SrcLangExt::Cpp) &&
3812 !name.startsWith("operator ") && // not operator
3813 (i=name.find('<'))!=DString::npos && // containing <
3814 (j=name.find('>'))!=DString::npos && // or >
3815 (j!=i+2 || name.at(i+1)!='=') // but not the C++20 spaceship operator <=>
3816 )
3817 {
3818 name=name.left(i);
3819 }
3820
3821 DString fileName = root->fileName;
3822 if (fileName.empty() && root->tagInfo())
3823 {
3824 fileName = root->tagInfo()->tagName;
3825 }
3826
3827 //printf("root->name='%s; args='%s' root->argList='%s'\n",
3828 // qPrint(root->name),qPrint(args),qPrint(argListToString(root->argList))
3829 // );
3830
3831 // adding class member
3832 Relationship relationship = relates.empty() ? Relationship::Member :
3833 root->relatesType==RelatesType::MemberOf ? Relationship::Foreign :
3834 Relationship::Related ;
3835 auto md = createMemberDef(
3836 fileName,root->startLine,root->startColumn,
3837 type,name,args,root->exception,
3838 protection,virt,
3839 stat && root->relatesType!=RelatesType::MemberOf,
3840 relationship,
3841 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3842 root->argList, root->metaData);
3843 auto mmd = toMemberDefMutable(md.get());
3844 mmd->setTagInfo(root->tagInfo());
3845 mmd->setMemberClass(cd);
3846 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3847 mmd->setDocsForDefinition(!root->proto);
3848 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3849 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3850 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3851 mmd->setMemberSpecifiers(spec);
3852 mmd->setVhdlSpecifiers(root->vhdlSpec);
3853 mmd->setMemberGroupId(root->mGrpId);
3854 mmd->setTypeConstraints(root->typeConstr);
3855 mmd->setLanguage(root->lang);
3856 mmd->setRequiresClause(root->req);
3857 mmd->setId(root->id);
3858 mmd->setBodyDef(fd);
3859 mmd->setFileDef(fd);
3860 mmd->addSectionsToDefinition(root->anchors);
3861 DString def;
3863 SrcLangExt lang = cd->getLanguage();
3864 DString scopeSeparator=getLanguageSpecificSeparator(lang);
3865 if (scopeSeparator!="::")
3866 {
3867 qualScope = substitute(qualScope,"::",scopeSeparator);
3868 }
3869 if (lang==SrcLangExt::PHP)
3870 {
3871 // for PHP we use Class::method and Namespace\method
3872 scopeSeparator="::";
3873 }
3874 if (!relates.empty() || isFriend || Config_getBool(HIDE_SCOPE_NAMES))
3875 {
3876 if (!type.empty())
3877 {
3878 def=type+" "+name; //+optArgs;
3879 }
3880 else
3881 {
3882 def=name; //+optArgs;
3883 }
3884 }
3885 else
3886 {
3887 if (!type.empty())
3888 {
3889 def=type+" "+qualScope+scopeSeparator+name; //+optArgs;
3890 }
3891 else
3892 {
3893 def=qualScope+scopeSeparator+name; //+optArgs;
3894 }
3895 }
3896 def.stripPrefix("friend ");
3897 mmd->setDefinition(def);
3899 mmd->addQualifiers(root->qualifiers);
3900
3901 AUTO_TRACE("function member: type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
3902 type, qualScope, rname, args, root->proto, def);
3903
3904 // add member to the class cd
3905 cd->insertMember(md.get());
3906 // add file to list of used files
3907 cd->insertUsedFile(fd);
3908
3909 addMemberToGroups(root,md.get());
3911 root->markAsProcessed();
3912 mmd->setRefItems(root->sli);
3913 mmd->setRequirementReferences(root->rqli);
3914
3915 // add member to the global list of all members
3916 //printf("Adding member=%s class=%s\n",qPrint(md->name()),qPrint(cd->name()));
3918 mn->push_back(std::move(md));
3919}
3920
3921//------------------------------------------------------------------------------------------
3922
3923static void addGlobalFunction(const Entry *root,const DString &rname,const DString &sc)
3924{
3925 DString scope = sc;
3926
3927 // new global function
3929 auto md = createMemberDef(
3930 root->fileName,root->startLine,root->startColumn,
3931 root->type,name,root->args,root->exception,
3932 root->protection,root->virt,root->isStatic,Relationship::Member,
3933 MemberType::Function,
3934 !root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3935 root->argList,root->metaData);
3936 auto mmd = toMemberDefMutable(md.get());
3937 mmd->setTagInfo(root->tagInfo());
3938 mmd->setLanguage(root->lang);
3939 mmd->setId(root->id);
3940 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3941 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3942 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3943 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
3944 mmd->setDocsForDefinition(!root->proto);
3945 mmd->setTypeConstraints(root->typeConstr);
3946 //md->setBody(root->body);
3947 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3948 FileDef *fd=root->fileDef();
3949 mmd->setBodyDef(fd);
3950 mmd->addSectionsToDefinition(root->anchors);
3951 mmd->setMemberSpecifiers(root->spec);
3952 mmd->setVhdlSpecifiers(root->vhdlSpec);
3953 mmd->setMemberGroupId(root->mGrpId);
3954 mmd->setRequiresClause(root->req);
3955 mmd->setExplicitExternal(root->explicitExternal,root->fileName,root->startLine,root->startColumn);
3956
3957 NamespaceDefMutable *nd = nullptr;
3958 // see if the function is inside a namespace that was not part of
3959 // the name already (in that case nd should be non-zero already)
3960 if (root->parent()->section.isNamespace())
3961 {
3962 //DString nscope=removeAnonymousScopes(root->parent()->name);
3963 DString nscope=root->parent()->name;
3964 if (!nscope.empty())
3965 {
3966 nd = getResolvedNamespaceMutable(nscope);
3967 }
3968 }
3969 else if (root->parent()->section.isGroupDoc() && !scope.empty())
3970 {
3972 }
3973
3974 if (!scope.empty())
3975 {
3977 if (sep!="::")
3978 {
3979 scope = substitute(scope,"::",sep);
3980 }
3981 scope+=sep;
3982 }
3983
3984 if (Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python) scope = "";
3985 DString def;
3986 //DString optArgs = root->argList.empty() ? DString() : root->args;
3987 if (!root->type.empty())
3988 {
3989 def=root->type+" "+scope+name; //+optArgs;
3990 }
3991 else
3992 {
3993 def=scope+name; //+optArgs;
3994 }
3995 AUTO_TRACE("new non-member function type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
3996 root->type,scope,rname,root->args,root->proto,def);
3997 mmd->setDefinition(def);
3999 mmd->addQualifiers(root->qualifiers);
4000
4001 mmd->setRefItems(root->sli);
4002 mmd->setRequirementReferences(root->rqli);
4003 if (nd && !nd->name().empty() && nd->name().at(0)!='@')
4004 {
4005 // add member to namespace
4006 mmd->setNamespace(nd);
4007 nd->insertMember(md.get());
4008 }
4009 if (fd)
4010 {
4011 // add member to the file (we do this even if we have already
4012 // inserted it into the namespace)
4013 mmd->setFileDef(fd);
4014 fd->insertMember(md.get());
4015 }
4016
4017 addMemberToGroups(root,md.get());
4019 if (root->relatesType == RelatesType::Simple) // if this is a relatesalso command,
4020 // allow find Member to pick it up
4021 {
4022 root->markAsProcessed(); // Otherwise we have finished with this entry.
4023 }
4024
4025 // add member to the list of file members
4027 mn->push_back(std::move(md));
4028}
4029
4030//------------------------------------------------------------------------------------------
4031
4032static void buildFunctionList(const Entry *root)
4033{
4034 if (root->section.isFunction())
4035 {
4036 AUTO_TRACE("member function: type='{}' scope='{}' name='{}' args='{}' relates='{}' relatesType='{}'"
4037 " file='{}' line={} bodyLine={} #tArgLists={} mGrpId={}"
4038 " spec={} proto={} docFile='{}'",
4039 root->type, root->parent()->name, root->name, root->args, root->relates, root->relatesType,
4040 root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(), root->mGrpId,
4041 root->spec, root->proto, root->docFile);
4042
4043 bool isFriend=root->type=="friend" || root->type.find("friend ")!=DString::npos;
4044 DString rname = removeRedundantWhiteSpace(root->name);
4045 //printf("rname=%s\n",qPrint(rname));
4046
4047 DString scope;
4048 int index = computeQualifiedIndex(rname);
4049 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
4050 // grouped members are stored with full scope
4051 {
4052 buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
4053 scope=rname.left(index);
4054 rname=rname.mid(index+2);
4055 }
4056 else
4057 {
4058 scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
4059 }
4060 if (!rname.empty() && scope.find('@')==DString::npos)
4061 {
4062 // check if this function's parent is a class
4063 if (root->lang==SrcLangExt::CSharp)
4064 {
4065 scope=mangleCSharpGenericName(scope);
4066 }
4067 else
4068 {
4069 scope=stripTemplateSpecifiersFromScope(scope,false);
4070 }
4071
4072 FileDef *rfd=root->fileDef();
4073
4074 size_t memIndex=rname.rfind("::");
4075
4077 if (cd && scope+"::"==rname.left(scope.length()+2)) // found A::f inside A
4078 {
4079 // strip scope from name
4080 rname=rname.mid(root->parent()->name.length()+2);
4081 }
4082
4083 bool isMember=false;
4084 if (memIndex!=DString::npos)
4085 {
4086 size_t ts=rname.find('<');
4087 size_t te=rname.find('>');
4088 if (memIndex>0 && (ts==DString::npos || te==DString::npos))
4089 {
4090 // note: the following code was replaced by inMember=true to deal with a
4091 // function rname='X::foo' of class X inside a namespace also called X...
4092 // bug id 548175
4093 //nd = Doxygen::namespaceLinkedMap->find(rname.left(memIndex));
4094 //isMember = nd==nullptr;
4095 //if (nd)
4096 //{
4097 // // strip namespace scope from name
4098 // scope=rname.left(memIndex);
4099 // rname=rname.mid(memIndex+2);
4100 //}
4101 isMember = true;
4102 }
4103 else
4104 {
4105 isMember=memIndex<ts || memIndex>te;
4106 }
4107 }
4108
4109 if (!root->parent()->name.empty() && root->parent()->section.isCompound() && cd)
4110 {
4111 AUTO_TRACE_ADD("member '{}' of class '{}'", rname,cd->name());
4112 addMethodToClass(root,cd,root->type,rname,root->args,isFriend,
4113 root->protection,root->isStatic,root->virt,root->spec,root->relates);
4114 }
4115 else if (root->parent()->section.isObjcImpl() && cd)
4116 {
4117 const MemberDef *md = cd->getMemberByName(rname);
4118 if (md)
4119 {
4120 MemberDefMutable *mdm = toMemberDefMutable(const_cast<MemberDef*>(md));
4121 if (mdm)
4122 {
4123 mdm->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
4124 mdm->setBodyDef(root->fileDef());
4125 }
4126 }
4127 }
4128 else if (!root->parent()->section.isCompound() && !root->parent()->section.isObjcImpl() &&
4129 !isMember &&
4130 (root->relates.empty() || root->relatesType==RelatesType::Duplicate) &&
4131 !root->type.startsWith("extern ") && !root->type.startsWith("typedef ")
4132 )
4133 // no member => unrelated function
4134 {
4135 /* check the uniqueness of the function name in the file.
4136 * A file could contain a function prototype and a function definition
4137 * or even multiple function prototypes.
4138 */
4139 bool found=false;
4140 MemberDef *md_found=nullptr;
4142 if (mn)
4143 {
4144 AUTO_TRACE_ADD("function '{}' already found",rname);
4145 for (const auto &imd : *mn)
4146 {
4147 MemberDefMutable *md = toMemberDefMutable(imd.get());
4148 if (md)
4149 {
4150 const NamespaceDef *mnd = md->getNamespaceDef();
4151 NamespaceDef *rnd = nullptr;
4152 //printf("root namespace=%s\n",qPrint(rootNav->parent()->name()));
4153 DString fullScope = scope;
4154 DString parentScope = root->parent()->name;
4155 if (!parentScope.empty() && !leftScopeMatch(parentScope,scope))
4156 {
4157 if (!scope.empty()) fullScope.prepend("::");
4158 fullScope.prepend(parentScope);
4159 }
4160 //printf("fullScope=%s\n",qPrint(fullScope));
4161 rnd = getResolvedNamespace(fullScope);
4162 const FileDef *mfd = md->getFileDef();
4163 DString nsName,rnsName;
4164 if (mnd) nsName = mnd->name();
4165 if (rnd) rnsName = rnd->name();
4166 //printf("matching arguments for %s%s %s%s\n",
4167 // qPrint(md->name()),md->argsString(),qPrint(rname),qPrint(argListToString(root->argList)));
4168 const ArgumentList &mdAl = md->argumentList();
4169 const ArgumentList &mdTempl = md->templateArguments();
4170
4171 // in case of template functions, we need to check if the
4172 // functions have the same number of template parameters
4173 bool sameTemplateArgs = true;
4174 bool matchingReturnTypes = true;
4175 bool sameRequiresClause = true;
4176 if (!mdTempl.empty() && !root->tArgLists.empty())
4177 {
4178 sameTemplateArgs = matchTemplateArguments(mdTempl,root->tArgLists.back());
4179 if (md->typeString()!=removeRedundantWhiteSpace(root->type))
4180 {
4181 matchingReturnTypes = false;
4182 }
4183 if (md->requiresClause()!=root->req)
4184 {
4185 sameRequiresClause = false;
4186 }
4187 }
4188 else if (!mdTempl.empty() || !root->tArgLists.empty())
4189 { // if one has template parameters and the other doesn't then that also counts as a
4190 // difference
4191 sameTemplateArgs = false;
4192 }
4193
4194 bool staticsInDifferentFiles =
4195 root->isStatic && md->isStatic() && root->fileName!=md->getDefFileName();
4196
4197 if (sameTemplateArgs &&
4198 matchingReturnTypes &&
4199 sameRequiresClause &&
4200 !staticsInDifferentFiles &&
4201 matchArguments2(md->getOuterScope(),mfd,md->typeString(),&mdAl,
4202 rnd ? rnd : Doxygen::globalScope,rfd,root->type,&root->argList,
4203 false,root->lang)
4204 )
4205 {
4206 GroupDef *gd=nullptr;
4207 if (!root->groups.empty() && !root->groups.front().groupname.empty())
4208 {
4209 gd = Doxygen::groupLinkedMap->find(root->groups.front().groupname);
4210 }
4211 //printf("match!\n");
4212 //printf("mnd=%p rnd=%p nsName=%s rnsName=%s\n",mnd,rnd,qPrint(nsName),qPrint(rnsName));
4213 // see if we need to create a new member
4214 found=(mnd && rnd && nsName==rnsName) || // members are in the same namespace
4215 ((mnd==nullptr && rnd==nullptr && mfd!=nullptr && // no external reference and
4216 mfd->absFilePath()==root->fileName // prototype in the same file
4217 )
4218 );
4219 // otherwise, allow a duplicate global member with the same argument list
4220 if (!found && gd && gd==md->getGroupDef() && nsName==rnsName)
4221 {
4222 // member is already in the group, so we don't want to add it again.
4223 found=true;
4224 }
4225
4226 AUTO_TRACE_ADD("combining function with prototype found={} in namespace '{}'",found,nsName);
4227
4228 if (found)
4229 {
4230 // merge argument lists
4231 ArgumentList mergedArgList = root->argList;
4232 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.empty());
4233 // merge documentation
4234 if (md->documentation().empty() && !root->doc.empty())
4235 {
4236 if (root->proto)
4237 {
4239 }
4240 else
4241 {
4243 }
4244 }
4245
4246 md->setDocumentation(root->doc,root->docFile,root->docLine);
4248 md->setDocsForDefinition(!root->proto);
4249 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
4250 {
4251 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
4252 md->setBodyDef(rfd);
4253 }
4254
4255 if (md->briefDescription().empty() && !root->brief.empty())
4256 {
4257 md->setArgsString(root->args);
4258 }
4259 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
4260
4262
4264 md->addQualifiers(root->qualifiers);
4265
4266 // merge ingroup specifiers
4267 if (md->getGroupDef()==nullptr && !root->groups.empty())
4268 {
4269 addMemberToGroups(root,md);
4270 }
4271 else if (md->getGroupDef()!=nullptr && root->groups.empty())
4272 {
4273 //printf("existing member is grouped, new member not\n");
4274 }
4275 else if (md->getGroupDef()!=nullptr && !root->groups.empty())
4276 {
4277 //printf("both members are grouped\n");
4278 }
4280
4281 // if md is a declaration and root is the corresponding
4282 // definition, then turn md into a definition.
4283 if (md->isPrototype() && !root->proto)
4284 {
4285 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
4286 md->setPrototype(false,root->fileName,root->startLine,root->startColumn);
4287 }
4288 // if md is already the definition, then add the declaration info
4289 else if (!md->isPrototype() && root->proto)
4290 {
4291 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
4292 }
4293 }
4294 }
4295 }
4296 if (found)
4297 {
4298 md_found = md;
4299 break;
4300 }
4301 }
4302 }
4303 if (!found) /* global function is unique with respect to the file */
4304 {
4305 addGlobalFunction(root,rname,scope);
4306 }
4307 else
4308 {
4309 FileDef *fd=root->fileDef();
4310 if (fd)
4311 {
4312 // add member to the file (we do this even if we have already
4313 // inserted it into the namespace)
4314 fd->insertMember(md_found);
4315 }
4316 }
4317
4318 AUTO_TRACE_ADD("unrelated function type='{}' name='{}' args='{}'",root->type,rname,root->args);
4319 }
4320 else
4321 {
4322 AUTO_TRACE_ADD("function '{}' is not processed",rname);
4323 }
4324 }
4325 else if (rname.empty())
4326 {
4327 warn(root->fileName,root->startLine,
4328 "Illegal member name found."
4329 );
4330 }
4331 }
4332 for (const auto &e : root->children()) buildFunctionList(e.get());
4333}
4334
4335//----------------------------------------------------------------------
4336
4337static void findFriends()
4338{
4339 AUTO_TRACE();
4340 for (const auto &fn : *Doxygen::functionNameLinkedMap) // for each global function name
4341 {
4342 MemberName *mn = Doxygen::memberNameLinkedMap->find(fn->memberName());
4343 if (mn)
4344 { // there are members with the same name
4345 // for each function with that name
4346 for (const auto &ifmd : *fn)
4347 {
4348 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
4349 // for each member with that name
4350 for (const auto &immd : *mn)
4351 {
4352 MemberDefMutable *mmd = toMemberDefMutable(immd.get());
4353 //printf("Checking for matching arguments
4354 // mmd->isRelated()=%d mmd->isFriend()=%d mmd->isFunction()=%d\n",
4355 // mmd->isRelated(),mmd->isFriend(),mmd->isFunction());
4356 if (fmd && mmd &&
4357 (mmd->isFriend() || (mmd->isRelated() && mmd->isFunction())) &&
4358 matchArguments2(mmd->getOuterScope(), mmd->getFileDef(), mmd->typeString(), &mmd->argumentList(),
4359 fmd->getOuterScope(), fmd->getFileDef(), fmd->typeString(), &fmd->argumentList(),
4360 true,mmd->getLanguage()
4361 )
4362
4363 ) // if the member is related and the arguments match then the
4364 // function is actually a friend.
4365 {
4366 AUTO_TRACE_ADD("Merging related global and member '{}' isFriend={} isRelated={} isFunction={}",
4367 mmd->name(),mmd->isFriend(),mmd->isRelated(),mmd->isFunction());
4368 const ArgumentList &mmdAl = mmd->argumentList();
4369 const ArgumentList &fmdAl = fmd->argumentList();
4370 mergeArguments(const_cast<ArgumentList&>(fmdAl),const_cast<ArgumentList&>(mmdAl));
4371
4372 // reset argument lists to add missing default parameters
4373 DString mmdAlStr = argListToString(mmdAl);
4374 DString fmdAlStr = argListToString(fmdAl);
4375 mmd->setArgsString(mmdAlStr);
4376 fmd->setArgsString(fmdAlStr);
4377 mmd->moveDeclArgumentList(std::make_unique<ArgumentList>(mmdAl));
4378 fmd->moveDeclArgumentList(std::make_unique<ArgumentList>(fmdAl));
4379 AUTO_TRACE_ADD("friend args='{}' member args='{}'",argListToString(fmd->argumentList()),argListToString(mmd->argumentList()));
4380
4381 if (!fmd->documentation().empty())
4382 {
4383 mmd->setDocumentation(fmd->documentation(),fmd->docFile(),fmd->docLine());
4384 }
4385 else if (!mmd->documentation().empty())
4386 {
4387 fmd->setDocumentation(mmd->documentation(),mmd->docFile(),mmd->docLine());
4388 }
4389 if (mmd->briefDescription().empty() && !fmd->briefDescription().empty())
4390 {
4391 mmd->setBriefDescription(fmd->briefDescription(),fmd->briefFile(),fmd->briefLine());
4392 }
4393 else if (!mmd->briefDescription().empty() && !fmd->briefDescription().empty())
4394 {
4395 fmd->setBriefDescription(mmd->briefDescription(),mmd->briefFile(),mmd->briefLine());
4396 }
4397 if (!fmd->inbodyDocumentation().empty())
4398 {
4400 }
4401 else if (!mmd->inbodyDocumentation().empty())
4402 {
4404 }
4405 //printf("body mmd %d fmd %d\n",mmd->getStartBodyLine(),fmd->getStartBodyLine());
4406 if (mmd->getStartBodyLine()==-1 && fmd->getStartBodyLine()!=-1)
4407 {
4408 mmd->setBodySegment(fmd->getDefLine(),fmd->getStartBodyLine(),fmd->getEndBodyLine());
4409 mmd->setBodyDef(fmd->getBodyDef());
4410 //mmd->setBodyMember(fmd);
4411 }
4412 else if (mmd->getStartBodyLine()!=-1 && fmd->getStartBodyLine()==-1)
4413 {
4414 fmd->setBodySegment(mmd->getDefLine(),mmd->getStartBodyLine(),mmd->getEndBodyLine());
4415 fmd->setBodyDef(mmd->getBodyDef());
4416 //fmd->setBodyMember(mmd);
4417 }
4419
4421
4422 mmd->addQualifiers(fmd->getQualifiers());
4423 fmd->addQualifiers(mmd->getQualifiers());
4424
4425 }
4426 }
4427 }
4428 }
4429 }
4430}
4431
4432//----------------------------------------------------------------------
4433
4435{
4436 AUTO_TRACE();
4437
4438 // find matching function declaration and definitions.
4439 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4440 {
4441 //printf("memberName=%s count=%zu\n",qPrint(mn->memberName()),mn->size());
4442 /* find a matching function declaration and definition for this function */
4443 for (const auto &imdec : *mn)
4444 {
4445 MemberDefMutable *mdec = toMemberDefMutable(imdec.get());
4446 if (mdec &&
4447 (mdec->isPrototype() ||
4448 (mdec->isVariable() && mdec->isExternal())
4449 ))
4450 {
4451 for (const auto &imdef : *mn)
4452 {
4453 MemberDefMutable *mdef = toMemberDefMutable(imdef.get());
4454 if (mdef && mdec!=mdef &&
4455 mdec->getNamespaceDef()==mdef->getNamespaceDef())
4456 {
4458 }
4459 }
4460 }
4461 }
4462 }
4463}
4464
4465//----------------------------------------------------------------------
4466
4468{
4469 AUTO_TRACE();
4470 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4471 {
4472 MemberDefMutable *mdef=nullptr,*mdec=nullptr;
4473 /* find a matching function declaration and definition for this function */
4474 for (const auto &imd : *mn)
4475 {
4476 MemberDefMutable *md = toMemberDefMutable(imd.get());
4477 if (md)
4478 {
4479 if (md->isPrototype())
4480 mdec=md;
4481 else if (md->isVariable() && md->isExternal())
4482 mdec=md;
4483
4484 if (md->isFunction() && !md->isStatic() && !md->isPrototype())
4485 mdef=md;
4486 else if (md->isVariable() && !md->isExternal() && !md->isStatic())
4487 mdef=md;
4488 }
4489
4490 if (mdef && mdec) break;
4491 }
4492 if (mdef && mdec)
4493 {
4494 const ArgumentList &mdefAl = mdef->argumentList();
4495 const ArgumentList &mdecAl = mdec->argumentList();
4496 if (
4497 matchArguments2(mdef->getOuterScope(),mdef->getFileDef(),mdef->typeString(),const_cast<ArgumentList*>(&mdefAl),
4498 mdec->getOuterScope(),mdec->getFileDef(),mdec->typeString(),const_cast<ArgumentList*>(&mdecAl),
4499 true,mdef->getLanguage()
4500 )
4501 ) /* match found */
4502 {
4503 AUTO_TRACE_ADD("merging references for mdec={} mdef={}",mdec->name(),mdef->name());
4504 mdef->mergeReferences(mdec);
4505 mdec->mergeReferences(mdef);
4506 mdef->mergeReferencedBy(mdec);
4507 mdec->mergeReferencedBy(mdef);
4508 }
4509 }
4510 }
4511}
4512
4513//----------------------------------------------------------------------
4514
4516{
4517 AUTO_TRACE();
4518 // find match between function declaration and definition for
4519 // related functions
4520 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4521 {
4522 /* find a matching function declaration and definition for this function */
4523 // for each global function
4524 for (const auto &imd : *mn)
4525 {
4526 MemberDefMutable *md = toMemberDefMutable(imd.get());
4527 if (md)
4528 {
4529 //printf(" Function '%s'\n",qPrint(md->name()));
4531 if (rmn) // check if there is a member with the same name
4532 {
4533 //printf(" Member name found\n");
4534 // for each member with the same name
4535 for (const auto &irmd : *rmn)
4536 {
4537 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
4538 //printf(" Member found: related='%d'\n",rmd->isRelated());
4539 if (rmd &&
4540 (rmd->isRelated() || rmd->isForeign()) && // related function
4541 matchArguments2( md->getOuterScope(), md->getFileDef(), md->typeString(), &md->argumentList(),
4542 rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmd->argumentList(),
4543 true,md->getLanguage()
4544 )
4545 )
4546 {
4547 AUTO_TRACE_ADD("Found related member '{}'",md->name());
4548 if (rmd->relatedAlso())
4549 md->setRelatedAlso(rmd->relatedAlso());
4550 else if (rmd->isForeign())
4551 md->makeForeign();
4552 else
4553 md->makeRelated();
4554 }
4555 }
4556 }
4557 }
4558 }
4559 }
4560}
4561
4562//----------------------------------------------------------------------
4563
4565{
4566 AUTO_TRACE();
4567 for (const auto &[qualifiedName,bodyInfo] : Doxygen::staticInitMap)
4568 {
4569 size_t i=qualifiedName.rfind("::");
4570 if (i!=std::string::npos)
4571 {
4572 DString scope = qualifiedName.substr(0,i);
4573 DString name = qualifiedName.substr(i+2);
4575 if (mn)
4576 {
4577 for (const auto &imd : *mn)
4578 {
4579 MemberDefMutable *md = toMemberDefMutable(imd.get());
4580 if (md && md->qualifiedName().str()==qualifiedName && md->isVariable())
4581 {
4582 AUTO_TRACE_ADD("found static member {} body [{}..{}]\n",
4583 md->qualifiedName(),bodyInfo.startLine,bodyInfo.endLine);
4584 md->setBodySegment(bodyInfo.defLine,
4585 bodyInfo.startLine,
4586 bodyInfo.endLine);
4587 }
4588 }
4589 }
4590 }
4591 }
4592}
4593
4594//----------------------------------------------------------------------
4595
4596/*! make a dictionary of all template arguments of class cd
4597 * that are part of the base class name.
4598 * Example: A template class A with template arguments <R,S,T>
4599 * that inherits from B<T,T,S> will have T and S in the dictionary.
4600 */
4601static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments,const std::string &name)
4602{
4603 std::map<std::string,int> templateNames;
4604 int count=0;
4605 for (const Argument &arg : templateArguments)
4606 {
4607 static const reg::Ex re(R"(\a[\w:]*)");
4608 reg::Iterator it(name,re);
4610 for (; it!=end ; ++it)
4611 {
4612 const auto &match = *it;
4613 std::string n = match.str();
4614 if (n==arg.name.str())
4615 {
4616 if (templateNames.find(n)==templateNames.end())
4617 {
4618 templateNames.emplace(n,count);
4619 }
4620 }
4621 }
4622 }
4623 return templateNames;
4624}
4625
4626/*! Searches a class from within \a context and \a cd and returns its
4627 * definition if found (otherwise nullptr is returned).
4628 */
4630{
4631 ClassDef *result=nullptr;
4632 if (cd==nullptr)
4633 {
4634 return result;
4635 }
4636 FileDef *fd=cd->getFileDef();
4637 SymbolResolver resolver(fd);
4638 if (context && cd!=context)
4639 {
4640 result = const_cast<ClassDef*>(resolver.resolveClass(context,name,true,true));
4641 }
4642 //printf("1. result=%p\n",result);
4643 if (result==nullptr)
4644 {
4645 result = const_cast<ClassDef*>(resolver.resolveClass(cd,name,true,true));
4646 }
4647 //printf("2. result=%p\n",result);
4648 if (result==nullptr) // try direct class, needed for namespaced classes imported via tag files (see bug624095)
4649 {
4650 result = getClass(name);
4651 }
4652 //printf("3. result=%p\n",result);
4653 //printf("** Trying to find %s within context %s class %s result=%s lookup=%p\n",
4654 // qPrint(name),
4655 // context ? qPrint(context->name()) : "<none>",
4656 // cd ? qPrint(cd->name()) : "<none>",
4657 // result ? qPrint(result->name()) : "<none>",
4658 // Doxygen::classLinkedMap->find(name)
4659 // );
4660 return result;
4661}
4662
4663
4664static void findUsedClassesForClass(const Entry *root,
4665 Definition *context,
4666 ClassDefMutable *masterCd,
4667 ClassDefMutable *instanceCd,
4668 bool isArtificial,
4669 const ArgumentList *actualArgs = nullptr,
4670 const TemplateNameMap &templateNames = TemplateNameMap()
4671 )
4672{
4673 AUTO_TRACE();
4674 const ArgumentList &formalArgs = masterCd->templateArguments();
4675 for (auto &mni : masterCd->memberNameInfoLinkedMap())
4676 {
4677 for (auto &mi : *mni)
4678 {
4679 const MemberDef *md=mi->memberDef();
4680 if (md->isVariable() || md->isObjCProperty()) // for each member variable in this class
4681 {
4682 AUTO_TRACE_ADD("Found variable '{}' in class '{}'",md->name(),masterCd->name());
4683 DString type = normalizeNonTemplateArgumentsInString(md->typeString(),masterCd,formalArgs);
4684 DString typedefValue = md->getLanguage()==SrcLangExt::Java ? type : resolveTypeDef(masterCd,type);
4685 if (!typedefValue.empty())
4686 {
4687 type = typedefValue;
4688 }
4689 int pos=0;
4690 DString usedClassName;
4691 DString templSpec;
4692 bool found=false;
4693 // the type can contain template variables, replace them if present
4694 type = substituteTemplateArgumentsInString(type,formalArgs,actualArgs);
4695
4696 //printf(" template substitution gives=%s\n",qPrint(type));
4697 while (!found && extractClassNameFromType(type,pos,usedClassName,templSpec,root->lang)!=-1)
4698 {
4699 // find the type (if any) that matches usedClassName
4700 SymbolResolver resolver(masterCd->getFileDef());
4701 const ClassDefMutable *typeCd = resolver.resolveClassMutable(masterCd,usedClassName,false,true);
4702 //printf("====> usedClassName=%s -> typeCd=%s\n",
4703 // qPrint(usedClassName),typeCd?qPrint(typeCd->name()):"<none>");
4704 if (typeCd)
4705 {
4706 usedClassName = typeCd->name();
4707 }
4708
4709 // replace any namespace aliases
4710 replaceNamespaceAliases(usedClassName);
4711 // add any template arguments to the class
4712 DString usedName = removeRedundantWhiteSpace(usedClassName+templSpec);
4713 //printf(" usedName=%s usedClassName=%s templSpec=%s\n",qPrint(usedName),qPrint(usedClassName),qPrint(templSpec));
4714
4715 TemplateNameMap formTemplateNames;
4716 if (templateNames.empty())
4717 {
4718 formTemplateNames = getTemplateArgumentsInName(formalArgs,usedName.str());
4719 }
4720 BaseInfo bi(usedName,Protection::Public,Specifier::Normal);
4721 findClassRelation(root,context,instanceCd,&bi,formTemplateNames,TemplateInstances,isArtificial);
4722
4723 for (const Argument &arg : masterCd->templateArguments())
4724 {
4725 if (arg.name==usedName) // type is a template argument
4726 {
4727 ClassDef *usedCd = Doxygen::hiddenClassLinkedMap->find(usedName);
4728 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4729 if (usedCd==nullptr)
4730 {
4731 usedCdm = toClassDefMutable(
4732 Doxygen::hiddenClassLinkedMap->add(usedName,
4734 masterCd->getDefFileName(),masterCd->getDefLine(),
4735 masterCd->getDefColumn(),
4736 usedName,
4737 ClassDef::Class)));
4738 if (usedCdm)
4739 {
4740 //printf("making %s a template argument!!!\n",qPrint(usedCd->name()));
4741 usedCdm->makeTemplateArgument();
4742 usedCdm->setUsedOnly(true);
4743 usedCdm->setLanguage(masterCd->getLanguage());
4744 usedCd = usedCdm;
4745 }
4746 }
4747 if (usedCd)
4748 {
4749 found=true;
4750 AUTO_TRACE_ADD("case 1: adding used class '{}'", usedCd->name());
4751 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4752 if (usedCdm)
4753 {
4754 if (isArtificial) usedCdm->setArtificial(true);
4755 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4756 }
4757 }
4758 }
4759 }
4760
4761 if (!found)
4762 {
4763 ClassDef *usedCd=findClassWithinClassContext(context,masterCd,usedName);
4764 //printf("Looking for used class %s: result=%s master=%s\n",
4765 // qPrint(usedName),usedCd?qPrint(usedCd->name()):"<none>",masterCd?qPrint(masterCd->name()):"<none>");
4766
4767 if (usedCd)
4768 {
4769 found=true;
4770 AUTO_TRACE_ADD("case 2: adding used class '{}'", usedCd->name());
4771 instanceCd->addUsedClass(usedCd,md->name(),md->protection()); // class exists
4772 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4773 if (usedCdm)
4774 {
4775 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4776 }
4777 }
4778 }
4779 }
4780 if (!found && !type.empty()) // used class is not documented in any scope
4781 {
4783 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4784 if (usedCd==nullptr && !Config_getBool(HIDE_UNDOC_RELATIONS))
4785 {
4786 if (type.endsWith("(*") || type.endsWith("(^")) // type is a function pointer
4787 {
4788 type+=md->argsString();
4789 }
4790 AUTO_TRACE_ADD("New undocumented used class '{}'", type);
4791 usedCdm = toClassDefMutable(
4794 masterCd->getDefFileName(),masterCd->getDefLine(),
4795 masterCd->getDefColumn(),
4796 type,ClassDef::Class)));
4797 if (usedCdm)
4798 {
4799 usedCdm->setUsedOnly(true);
4800 usedCdm->setLanguage(masterCd->getLanguage());
4801 usedCd = usedCdm;
4802 }
4803 }
4804 if (usedCd)
4805 {
4806 AUTO_TRACE_ADD("case 3: adding used class '{}'", usedCd->name());
4807 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4808 if (usedCdm)
4809 {
4810 if (isArtificial) usedCdm->setArtificial(true);
4811 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4812 }
4813 }
4814 }
4815 }
4816 }
4817 }
4818}
4819
4821 const Entry *root,
4822 Definition *context,
4823 ClassDefMutable *masterCd,
4824 ClassDefMutable *instanceCd,
4826 bool isArtificial,
4827 const ArgumentList *actualArgs = nullptr,
4828 const TemplateNameMap &templateNames=TemplateNameMap()
4829 )
4830{
4831 AUTO_TRACE("name={}",root->name);
4832 // The base class could ofcouse also be a non-nested class
4833 const ArgumentList &formalArgs = masterCd->templateArguments();
4834 for (const BaseInfo &bi : root->extends)
4835 {
4836 //printf("masterCd=%s bi.name='%s' #actualArgs=%d\n",
4837 // qPrint(masterCd->localName()),qPrint(bi.name),actualArgs ? (int)actualArgs->size() : -1);
4838 TemplateNameMap formTemplateNames;
4839 if (templateNames.empty())
4840 {
4841 formTemplateNames = getTemplateArgumentsInName(formalArgs,bi.name.str());
4842 }
4843 BaseInfo tbi = bi;
4844 tbi.name = substituteTemplateArgumentsInString(bi.name,formalArgs,actualArgs);
4845 //printf("masterCd=%p instanceCd=%p bi->name=%s tbi.name=%s\n",(void*)masterCd,(void*)instanceCd,qPrint(bi.name),qPrint(tbi.name));
4846
4847 if (mode==DocumentedOnly)
4848 {
4849 // find a documented base class in the correct scope
4850 if (!findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,DocumentedOnly,isArtificial))
4851 {
4852 // 1.8.2: decided to show inheritance relations even if not documented,
4853 // we do make them artificial, so they do not appear in the index
4854 //if (!Config_getBool(HIDE_UNDOC_RELATIONS))
4855 bool b = Config_getBool(HIDE_UNDOC_RELATIONS) ? true : isArtificial;
4856 //{
4857 // no documented base class -> try to find an undocumented one
4858 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,Undocumented,b);
4859 //}
4860 }
4861 }
4862 else if (mode==TemplateInstances)
4863 {
4864 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,TemplateInstances,isArtificial);
4865 }
4866 }
4867}
4868
4869//----------------------------------------------------------------------
4870
4871static void findTemplateInstanceRelation(const Entry *root,
4872 Definition *context,
4873 ClassDefMutable *templateClass,const DString &templSpec,
4874 const TemplateNameMap &templateNames,
4875 bool isArtificial)
4876{
4877 AUTO_TRACE("Derived from template '{}' with parameters '{}' isArtificial={}",
4878 templateClass->name(),templSpec,isArtificial);
4879
4880 DString tempArgsStr = tempArgListToString(templateClass->templateArguments(),root->lang,false);
4881 bool existingClass = templSpec==tempArgsStr;
4882 if (existingClass) return; // avoid recursion
4883
4884 bool freshInstance=false;
4885 ClassDefMutable *instanceClass = toClassDefMutable(
4886 templateClass->insertTemplateInstance(
4887 root->fileName,root->startLine,root->startColumn,templSpec,freshInstance));
4888 if (instanceClass)
4889 {
4890 if (freshInstance)
4891 {
4892 instanceClass->setArtificial(true);
4893 instanceClass->setLanguage(root->lang);
4894
4895 AUTO_TRACE_ADD("found fresh instance '{}'",instanceClass->name());
4896 instanceClass->setTemplateBaseClassNames(templateNames);
4897
4898 // search for new template instances caused by base classes of
4899 // instanceClass
4900 auto it_pair = g_classEntries.equal_range(templateClass->name().str());
4901 for (auto it=it_pair.first ; it!=it_pair.second ; ++it)
4902 {
4903 const Entry *templateRoot = it->second;
4904 AUTO_TRACE_ADD("template root found '{}' templSpec='{}'",templateRoot->name,templSpec);
4905 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(root->lang,templSpec);
4906 findBaseClassesForClass(templateRoot,context,templateClass,instanceClass,
4907 TemplateInstances,isArtificial,templArgs.get(),templateNames);
4908
4909 findUsedClassesForClass(templateRoot,context,templateClass,instanceClass,
4910 isArtificial,templArgs.get(),templateNames);
4911 }
4912 }
4913 else
4914 {
4915 AUTO_TRACE_ADD("instance already exists");
4916 }
4917 }
4918}
4919
4920//----------------------------------------------------------------------
4921
4922static void resolveTemplateInstanceInType(const Entry *root,const Definition *scope,const MemberDef *md)
4923{
4924 // For a statement like 'using X = T<A>', add a template instance 'T<A>' as a symbol, so it can
4925 // be used to match arguments (see issue #11111)
4926 AUTO_TRACE();
4927 DString ttype = md->typeString();
4928 ttype.stripPrefix("typedef ");
4929 if (size_t ti=ttype.find('<'); ti!=DString::npos)
4930 {
4931 DString templateClassName = ttype.left(ti);
4932 SymbolResolver resolver(root->fileDef());
4933 ClassDefMutable *baseClass = resolver.resolveClassMutable(scope ? scope : Doxygen::globalScope,
4934 templateClassName, true, true);
4935 AUTO_TRACE_ADD("templateClassName={} baseClass={}",templateClassName,baseClass?baseClass->name():"<none>");
4936 if (baseClass)
4937 {
4938 const ArgumentList &tl = baseClass->templateArguments();
4939 TemplateNameMap templateNames = getTemplateArgumentsInName(tl,templateClassName.str());
4941 baseClass,
4942 ttype.mid(ti),
4943 templateNames,
4944 baseClass->isArtificial());
4945 }
4946 }
4947}
4948
4949//----------------------------------------------------------------------
4950
4951static bool isRecursiveBaseClass(const DString &scope,const DString &name)
4952{
4953 DString n=name;
4954 if (size_t index=n.find('<'); index!=DString::npos)
4955 {
4956 n=n.left(index);
4957 }
4958 bool result = rightScopeMatch(scope,n);
4959 return result;
4960}
4961
4963{
4964 if (name.empty()) return 0;
4965 int l = static_cast<int>(name.length());
4966 if (name[l-1]=='>') // search backward to find the matching <, allowing nested <...> and strings.
4967 {
4968 int count=1;
4969 int i=l-2;
4970 char insideQuote=0;
4971 while (count>0 && i>=0)
4972 {
4973 char c = name[i--];
4974 switch (c)
4975 {
4976 case '>': if (!insideQuote) count++; break;
4977 case '<': if (!insideQuote) count--; break;
4978 case '\'': if (!insideQuote) insideQuote=c;
4979 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
4980 break;
4981 case '"': if (!insideQuote) insideQuote=c;
4982 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
4983 break;
4984 default: break;
4985 }
4986 }
4987 if (i>=0) l=i+1;
4988 }
4989 return l;
4990}
4991
4993 const Entry *root,
4994 Definition *context,
4995 ClassDefMutable *cd,
4996 const BaseInfo *bi,
4997 const TemplateNameMap &templateNames,
4999 bool isArtificial
5000 )
5001{
5002 AUTO_TRACE("name={} base={} isArtificial={} mode={}",cd->name(),bi->name,isArtificial,(int)mode);
5003
5004 DString biName=bi->name;
5005 bool explicitGlobalScope=false;
5006 if (biName.startsWith("::")) // explicit global scope
5007 {
5008 biName=biName.mid(2);
5009 explicitGlobalScope=true;
5010 }
5011
5012 Entry *parentNode=root->parent();
5013 bool lastParent=false;
5014 do // for each parent scope, starting with the largest scope
5015 // (in case of nested classes)
5016 {
5017 DString scopeName= parentNode ? parentNode->name : DString();
5018 int scopeOffset=explicitGlobalScope ? 0 : static_cast<int>(scopeName.length());
5019 do // try all parent scope prefixes, starting with the largest scope
5020 {
5021 //printf("scopePrefix='%s' biName='%s'\n",
5022 // qPrint(scopeName.left(scopeOffset)),qPrint(biName));
5023
5024 DString baseClassName=biName;
5025 if (scopeOffset>0)
5026 {
5027 baseClassName.prepend(scopeName.left(scopeOffset)+"::");
5028 }
5029 if (root->lang==SrcLangExt::CSharp)
5030 {
5031 baseClassName = mangleCSharpGenericName(baseClassName);
5032 }
5033 AUTO_TRACE_ADD("cd='{}' baseClassName='{}'",cd->name(),baseClassName);
5034 SymbolResolver resolver(cd->getFileDef());
5035 ClassDefMutable *baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
5036 baseClassName,
5037 mode==Undocumented,
5038 true
5039 );
5040 const MemberDef *baseClassTypeDef = resolver.getTypedef();
5041 DString templSpec = resolver.getTemplateSpec();
5042 //printf("baseClassName=%s baseClass=%p cd=%p explicitGlobalScope=%d\n",
5043 // qPrint(baseClassName),baseClass,cd,explicitGlobalScope);
5044 //printf(" scope='%s' baseClassName='%s' baseClass=%s templSpec=%s\n",
5045 // cd ? qPrint(cd->name()):"<none>",
5046 // qPrint(baseClassName),
5047 // baseClass?qPrint(baseClass->name()):"<none>",
5048 // qPrint(templSpec)
5049 // );
5050 //if (baseClassName.left(root->name.length())!=root->name ||
5051 // baseClassName.at(root->name.length())!='<'
5052 // ) // Check for base class with the same name.
5053 // // If found then look in the outer scope for a match
5054 // // and prevent recursion.
5055 if (!isRecursiveBaseClass(root->name,baseClassName)
5056 || explicitGlobalScope
5057 // sadly isRecursiveBaseClass always true for UNO IDL ifc/svc members
5058 // (i.e. this is needed for addInterfaceOrServiceToServiceOrSingleton)
5059 || (root->lang==SrcLangExt::IDL &&
5060 (root->section.isExportedInterface() ||
5061 root->section.isIncludedService()))
5062 )
5063 {
5064 AUTO_TRACE_ADD("class relation '{}' inherited/used by '{}' found prot={} virt={} templSpec='{}'",
5065 baseClassName, root->name, bi->prot, bi->virt, templSpec);
5066
5067 int i=findTemplateSpecializationPosition(baseClassName);
5068 size_t si=baseClassName.rfind("::",i);
5069 if (si==DString::npos) si=0;
5070 if (baseClass==nullptr && static_cast<size_t>(i)!=baseClassName.length())
5071 // base class has template specifiers
5072 {
5073 // TODO: here we should try to find the correct template specialization
5074 // but for now, we only look for the unspecialized base class.
5075 int e=findEndOfTemplate(baseClassName,i+1);
5076 //printf("baseClass==0 i=%d e=%d\n",i,e);
5077 if (e!=-1) // end of template was found at e
5078 {
5079 templSpec = removeRedundantWhiteSpace(baseClassName.mid(i,e-i));
5080 baseClassName = baseClassName.left(i)+baseClassName.mid(e);
5081 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
5082 baseClassName,
5083 mode==Undocumented,
5084 true
5085 );
5086 baseClassTypeDef = resolver.getTypedef();
5087 //printf("baseClass=%p -> baseClass=%s templSpec=%s\n",
5088 // baseClass,qPrint(baseClassName),qPrint(templSpec));
5089 }
5090 }
5091 else if (baseClass && !templSpec.empty()) // we have a known class, but also
5092 // know it is a template, so see if
5093 // we can also link to the explicit
5094 // instance (for instance if a class
5095 // derived from a template argument)
5096 {
5097 //printf("baseClass=%s templSpec=%s\n",qPrint(baseClass->name()),qPrint(templSpec));
5098 ClassDefMutable *templClass=getClassMutable(baseClass->name()+templSpec);
5099 if (templClass)
5100 {
5101 // use the template instance instead of the template base.
5102 baseClass = templClass;
5103 templSpec.clear();
5104 }
5105 }
5106
5107 //printf("cd=%p baseClass=%p\n",cd,baseClass);
5108 bool found=baseClass!=nullptr && (baseClass!=cd || mode==TemplateInstances);
5109 AUTO_TRACE_ADD("1. found={}",found);
5110 if (!found && si!=DString::npos)
5111 {
5112 // replace any namespace aliases
5113 replaceNamespaceAliases(baseClassName);
5114 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
5115 baseClassName,
5116 mode==Undocumented,
5117 true
5118 );
5119 baseClassTypeDef = resolver.getTypedef();
5120 found=baseClass!=nullptr && baseClass!=cd;
5121 if (found) templSpec = resolver.getTemplateSpec();
5122 }
5123 AUTO_TRACE_ADD("2. found={}",found);
5124
5125 if (!found)
5126 {
5127 baseClass=toClassDefMutable(findClassWithinClassContext(context,cd,baseClassName));
5128 //printf("findClassWithinClassContext(%s,%s)=%p\n",
5129 // qPrint(cd->name()),qPrint(baseClassName),baseClass);
5130 found = baseClass!=nullptr && baseClass!=cd;
5131
5132 }
5133 AUTO_TRACE_ADD("3. found={}",found);
5134 if (!found)
5135 {
5136 // for PHP the "use A\B as C" construct map class C to A::B, so we lookup
5137 // the class name also in the alias mapping.
5138 auto it = Doxygen::namespaceAliasMap.find(baseClassName.str());
5139 if (it!=Doxygen::namespaceAliasMap.end()) // see if it is indeed a class.
5140 {
5141 baseClass=getClassMutable(it->second.alias);
5142 found = baseClass!=nullptr && baseClass!=cd;
5143 }
5144 }
5145 bool isATemplateArgument = templateNames.find(biName.str())!=templateNames.end();
5146
5147 AUTO_TRACE_ADD("4. found={}",found);
5148 if (found)
5149 {
5150 AUTO_TRACE_ADD("Documented base class '{}' templSpec='{}'",biName,templSpec);
5151 // add base class to this class
5152
5153 // if templSpec is not empty then we should "instantiate"
5154 // the template baseClass. A new ClassDef should be created
5155 // to represent the instance. To be able to add the (instantiated)
5156 // members and documentation of a template class
5157 // (inserted in that template class at a later stage),
5158 // the template should know about its instances.
5159 // the instantiation process, should be done in a recursive way,
5160 // since instantiating a template may introduce new inheritance
5161 // relations.
5162 if (!templSpec.empty() && mode==TemplateInstances)
5163 {
5164 // if baseClass is actually a typedef then we should not
5165 // instantiate it, since typedefs are in a different namespace
5166 // see bug531637 for an example where this would otherwise hang
5167 // Doxygen
5168 if (baseClassTypeDef==nullptr)
5169 {
5170 //printf(" => findTemplateInstanceRelation: %s\n",qPrint(baseClass->name()));
5171 findTemplateInstanceRelation(root,context,baseClass,templSpec,templateNames,baseClass->isArtificial());
5172 }
5173 }
5174 else if (mode==DocumentedOnly || mode==Undocumented)
5175 {
5176 //printf(" => insert base class\n");
5177 DString usedName;
5178 if (baseClassTypeDef)
5179 {
5180 usedName=biName;
5181 //printf("***** usedName=%s templSpec=%s\n",qPrint(usedName),qPrint(templSpec));
5182 }
5183 Protection prot = bi->prot;
5184 if (Config_getBool(SIP_SUPPORT)) prot=Protection::Public;
5185 if (cd!=baseClass && !cd->isSubClass(baseClass) && baseClass->isBaseClass(cd,true,templSpec)==0) // check for recursion, see bug690787
5186 {
5187 AUTO_TRACE_ADD("insertBaseClass name={} prot={} virt={} templSpec={}",usedName,prot,bi->virt,templSpec);
5188 cd->insertBaseClass(baseClass,usedName,prot,bi->virt,templSpec);
5189 // add this class as super class to the base class
5190 baseClass->insertSubClass(cd,prot,bi->virt,templSpec);
5191 }
5192 else
5193 {
5194 warn(root->fileName,root->startLine,
5195 "Detected potential recursive class relation "
5196 "between class {} and base class {}!",
5197 cd->name(),baseClass->name()
5198 );
5199 }
5200 }
5201 return true;
5202 }
5203 else if (mode==Undocumented && (scopeOffset==0 || isATemplateArgument))
5204 {
5205 AUTO_TRACE_ADD("New undocumented base class '{}' baseClassName='{}' templSpec='{}' isArtificial={}",
5206 biName,baseClassName,templSpec,isArtificial);
5207 baseClass=nullptr;
5208 if (isATemplateArgument)
5209 {
5210 baseClass = toClassDefMutable(Doxygen::hiddenClassLinkedMap->find(baseClassName));
5211 if (baseClass==nullptr) // not found (or alias)
5212 {
5213 baseClass= toClassDefMutable(
5214 Doxygen::hiddenClassLinkedMap->add(baseClassName,
5215 createClassDef(root->fileName,root->startLine,root->startColumn,
5216 baseClassName,
5217 ClassDef::Class)));
5218 if (baseClass) // really added (not alias)
5219 {
5220 if (isArtificial) baseClass->setArtificial(true);
5221 baseClass->setLanguage(root->lang);
5222 }
5223 }
5224 }
5225 else
5226 {
5227 baseClass = toClassDefMutable(Doxygen::classLinkedMap->find(baseClassName));
5228 //printf("*** classDDict->find(%s)=%p biName=%s templSpec=%s\n",
5229 // qPrint(baseClassName),baseClass,qPrint(biName),qPrint(templSpec));
5230 if (baseClass==nullptr) // not found (or alias)
5231 {
5232 baseClass = toClassDefMutable(
5233 Doxygen::classLinkedMap->add(baseClassName,
5234 createClassDef(root->fileName,root->startLine,root->startColumn,
5235 baseClassName,
5236 ClassDef::Class)));
5237 if (baseClass) // really added (not alias)
5238 {
5239 if (isArtificial) baseClass->setArtificial(true);
5240 baseClass->setLanguage(root->lang);
5241 si = baseClassName.rfind("::");
5242 if (si!=DString::npos) // class is nested
5243 {
5244 Definition *sd = findScopeFromQualifiedName(Doxygen::globalScope,baseClassName.left(si),nullptr,root->tagInfo());
5245 if (sd==nullptr || sd==Doxygen::globalScope) // outer scope not found
5246 {
5247 baseClass->setArtificial(true); // see bug678139
5248 }
5249 }
5250 }
5251 }
5252 }
5253 if (baseClass)
5254 {
5255 if (biName.endsWith("-p"))
5256 {
5257 biName="<"+biName.left(biName.length()-2)+">";
5258 }
5259 if (!cd->isSubClass(baseClass) && cd!=baseClass && cd->isBaseClass(baseClass,true,templSpec)==0) // check for recursion
5260 {
5261 AUTO_TRACE_ADD("insertBaseClass name={} prot={} virt={} templSpec={}",biName,bi->prot,bi->virt,templSpec);
5262 // add base class to this class
5263 cd->insertBaseClass(baseClass,biName,bi->prot,bi->virt,templSpec);
5264 // add this class as super class to the base class
5265 baseClass->insertSubClass(cd,bi->prot,bi->virt,templSpec);
5266 }
5267 // the undocumented base was found in this file
5268 baseClass->insertUsedFile(root->fileDef());
5269
5270 Definition *scope = buildScopeFromQualifiedName(baseClass->name(),root->lang,nullptr);
5271 if (scope!=baseClass)
5272 {
5273 baseClass->setOuterScope(scope);
5274 }
5275
5276 if (baseClassName.endsWith("-p"))
5277 {
5279 }
5280 return true;
5281 }
5282 else
5283 {
5284 AUTO_TRACE_ADD("Base class '{}' not created (alias?)",biName);
5285 }
5286 }
5287 else
5288 {
5289 AUTO_TRACE_ADD("Base class '{}' not found",biName);
5290 }
5291 }
5292 else
5293 {
5294 if (mode!=TemplateInstances)
5295 {
5296 warn(root->fileName,root->startLine,
5297 "Detected potential recursive class relation "
5298 "between class {} and base class {}!",
5299 root->name,baseClassName
5300 );
5301 }
5302 // for mode==TemplateInstance this case is quite common and
5303 // indicates a relation between a template class and a template
5304 // instance with the same name.
5305 }
5306 if (scopeOffset==0)
5307 {
5308 scopeOffset=-1;
5309 }
5310 else
5311 {
5312 size_t o = scopeName.rfind("::",scopeOffset-1);
5313 scopeOffset = o!=DString::npos ? static_cast<int>(o) : 0;
5314 }
5315 //printf("new scopeOffset='%d'",scopeOffset);
5316 } while (scopeOffset>=0);
5317
5318 if (parentNode==nullptr)
5319 {
5320 lastParent=true;
5321 }
5322 else
5323 {
5324 parentNode=parentNode->parent();
5325 }
5326 } while (lastParent);
5327
5328 return false;
5329}
5330
5331//----------------------------------------------------------------------
5332// Computes the base and super classes for each class in the tree
5333
5334static bool isClassSection(const Entry *root)
5335{
5336 if ( !root->name.empty() )
5337 {
5338 if (root->section.isCompound())
5339 // is it a compound (class, struct, union, interface ...)
5340 {
5341 return true;
5342 }
5343 else if (root->section.isCompoundDoc())
5344 // is it a documentation block with inheritance info.
5345 {
5346 bool hasExtends = !root->extends.empty();
5347 if (hasExtends) return true;
5348 }
5349 }
5350 return false;
5351}
5352
5353
5354/*! Builds a dictionary of all entry nodes in the tree starting with \a root
5355 */
5356static void findClassEntries(const Entry *root)
5357{
5358 if (isClassSection(root))
5359 {
5360 g_classEntries.emplace(root->name.str(),root);
5361 }
5362 for (const auto &e : root->children()) findClassEntries(e.get());
5363}
5364
5365static DString extractClassName(const Entry *root)
5366{
5367 // strip any anonymous scopes first
5370 if (size_t i=bName.find('<'); (root->lang==SrcLangExt::CSharp || root->lang==SrcLangExt::Java) && i!=DString::npos)
5371 {
5372 // a Java/C# generic class looks like a C++ specialization, so we need to strip the
5373 // template part before looking for matches
5374 if (root->lang==SrcLangExt::CSharp)
5375 {
5376 bName = mangleCSharpGenericName(root->name);
5377 }
5378 else
5379 {
5380 bName = bName.left(i);
5381 }
5382 }
5383 return bName;
5384}
5385
5386/*! Using the dictionary build by findClassEntries(), this
5387 * function will look for additional template specialization that
5388 * exists as inheritance relations only. These instances will be
5389 * added to the template they are derived from.
5390 */
5392{
5393 AUTO_TRACE();
5394 ClassDefSet visitedClasses;
5395 for (const auto &[name,root] : g_classEntries)
5396 {
5397 DString bName = extractClassName(root);
5398 ClassDefMutable *cdm = getClassMutable(bName);
5399 if (cdm)
5400 {
5401 findBaseClassesForClass(root,cdm,cdm,cdm,TemplateInstances,false);
5402 }
5403 }
5404}
5405
5407{
5408 AUTO_TRACE("root->name={} cd={}",root->name,cd->name());
5409 size_t i = root->name.find('<');
5410 size_t j = root->name.rfind('>');
5411 size_t k = j!=DString::npos ? root->name.find("::",j+1) : DString::npos; // A<T::B> => ok, A<T>::B => nok
5412 if (i!=DString::npos && j!=DString::npos && k==DString::npos && root->lang!=SrcLangExt::CSharp && root->lang!=SrcLangExt::Java)
5413 {
5414 ClassDefMutable *master = getClassMutable(root->name.left(i));
5415 if (master && master!=cd && !cd->templateMaster())
5416 {
5417 AUTO_TRACE_ADD("class={} master={}",cd->name(),cd->templateMaster()?cd->templateMaster()->name():"<none>",master->name());
5418 cd->setTemplateMaster(master);
5419 master->insertExplicitTemplateInstance(cd,root->name.mid(i));
5420 }
5421 }
5422}
5423
5425{
5426 AUTO_TRACE();
5427 for (const auto &[name,root] : g_classEntries)
5428 {
5429 DString bName = extractClassName(root);
5430 ClassDefMutable *cdm = getClassMutable(bName);
5431 if (cdm)
5432 {
5433 findUsedClassesForClass(root,cdm,cdm,cdm,true);
5435 cdm->addTypeConstraints();
5436 }
5437 }
5438}
5439
5441{
5442 AUTO_TRACE();
5443 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5444 {
5445 if (!nd->hasDocumentation())
5446 {
5447 if ((EntryType::guessSection(nd->getDefFileName()).isHeader() ||
5448 nd->getLanguage() == SrcLangExt::Fortran) && // Fortran doesn't have header files.
5449 !Config_getBool(HIDE_UNDOC_NAMESPACES) // undocumented namespaces are visible
5450 )
5451 {
5452 warn_undoc(nd->getDefFileName(),nd->getDefLine(), "{} {} is not documented.",
5453 nd->getLanguage() == SrcLangExt::Fortran ? "Module" : "Namespace",
5454 nd->name());
5455 }
5456 }
5457 }
5458}
5459
5461{
5462 AUTO_TRACE();
5463 for (const auto &[name,root] : g_classEntries)
5464 {
5465 DString bName = extractClassName(root);
5466 ClassDefMutable *cd = getClassMutable(bName);
5467 if (cd)
5468 {
5469 findBaseClassesForClass(root,cd,cd,cd,DocumentedOnly,false);
5470 }
5471 size_t numMembers = cd ? cd->memberNameInfoLinkedMap().size() : 0;
5472 if ((cd==nullptr || (!cd->hasDocumentation() && !cd->isReference())) && numMembers>0 && !bName.endsWith("::"))
5473 {
5474 if (!root->name.empty() && root->name.find('@')==DString::npos && // normal name
5475 (EntryType::guessSection(root->fileName).isHeader() ||
5476 Config_getBool(EXTRACT_LOCAL_CLASSES)) && // not defined in source file
5477 protectionLevelVisible(root->protection) && // hidden by protection
5478 !Config_getBool(HIDE_UNDOC_CLASSES) // undocumented class are visible
5479 )
5480 warn_undoc(root->fileName,root->startLine, "Compound {} is not documented.", root->name);
5481 }
5482 }
5483}
5484
5486{
5487 AUTO_TRACE();
5488 for (const auto &[name,root] : g_classEntries)
5489 {
5493 // strip any anonymous scopes first
5494 if (cd && !cd->getTemplateInstances().empty())
5495 {
5496 AUTO_TRACE_ADD("Template class '{}'",cd->name());
5497 for (const auto &ti : cd->getTemplateInstances()) // for each template instance
5498 {
5499 ClassDefMutable *tcd=toClassDefMutable(ti.classDef);
5500 if (tcd)
5501 {
5502 AUTO_TRACE_ADD("Template instance '{}'",tcd->name());
5503 DString templSpec = ti.templSpec;
5504 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(tcd->getLanguage(),templSpec);
5505 for (const BaseInfo &bi : root->extends)
5506 {
5507 // check if the base class is a template argument
5508 BaseInfo tbi = bi;
5509 const ArgumentList &tl = cd->templateArguments();
5510 if (!tl.empty())
5511 {
5512 TemplateNameMap baseClassNames = tcd->getTemplateBaseClassNames();
5513 TemplateNameMap templateNames = getTemplateArgumentsInName(tl,bi.name.str());
5514 // for each template name that we inherit from we need to
5515 // substitute the formal with the actual arguments
5516 TemplateNameMap actualTemplateNames;
5517 for (const auto &tn_kv : templateNames)
5518 {
5519 size_t templIndex = tn_kv.second;
5520 Argument actArg;
5521 bool hasActArg=false;
5522 if (templIndex<templArgs->size())
5523 {
5524 actArg=templArgs->at(templIndex);
5525 hasActArg=true;
5526 }
5527 if (hasActArg &&
5528 baseClassNames.find(actArg.type.str())!=baseClassNames.end() &&
5529 actualTemplateNames.find(actArg.type.str())==actualTemplateNames.end()
5530 )
5531 {
5532 actualTemplateNames.emplace(actArg.type.str(),static_cast<int>(templIndex));
5533 }
5534 }
5535
5536 tbi.name = substituteTemplateArgumentsInString(bi.name,tl,templArgs.get());
5537 // find a documented base class in the correct scope
5538 if (!findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,DocumentedOnly,false))
5539 {
5540 // no documented base class -> try to find an undocumented one
5541 findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,Undocumented,true);
5542 }
5543 }
5544 }
5545 }
5546 }
5547 }
5548 }
5549}
5550
5551//-----------------------------------------------------------------------
5552// compute the references (anchors in HTML) for each function in the file
5553
5555{
5556 AUTO_TRACE();
5557 for (const auto &cd : *Doxygen::classLinkedMap)
5558 {
5559 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5560 if (cdm)
5561 {
5562 cdm->computeAnchors();
5563 }
5564 }
5565 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5566 {
5567 for (const auto &fd : *fn)
5568 {
5569 fd->computeAnchors();
5570 }
5571 }
5572 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5573 {
5575 if (ndm)
5576 {
5577 ndm->computeAnchors();
5578 }
5579 }
5580 for (const auto &gd : *Doxygen::groupLinkedMap)
5581 {
5582 gd->computeAnchors();
5583 }
5584}
5585
5586//----------------------------------------------------------------------
5587
5588
5589template<typename Func>
5590static void applyToAllDefinitions(Func func)
5591{
5592 for (const auto &cd : *Doxygen::classLinkedMap)
5593 {
5594 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5595 if (cdm)
5596 {
5597 func(cdm);
5598 }
5599 }
5600
5601 for (const auto &cd : *Doxygen::conceptLinkedMap)
5602 {
5603 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
5604 if (cdm)
5605 {
5606 func(cdm);
5607 }
5608 }
5609
5610 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5611 {
5612 for (const auto &fd : *fn)
5613 {
5614 func(fd.get());
5615 }
5616 }
5617
5618 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5619 {
5621 if (ndm)
5622 {
5623 func(ndm);
5624 }
5625 }
5626
5627 for (const auto &gd : *Doxygen::groupLinkedMap)
5628 {
5629 func(gd.get());
5630 }
5631
5632 for (const auto &pd : *Doxygen::pageLinkedMap)
5633 {
5634 func(pd.get());
5635 }
5636
5637 for (const auto &dd : *Doxygen::dirLinkedMap)
5638 {
5639 func(dd.get());
5640 }
5641
5642 func(&ModuleManager::instance());
5643}
5644
5645//----------------------------------------------------------------------
5646
5648{
5649 AUTO_TRACE();
5650 applyToAllDefinitions([](auto* obj) { obj->addRequirementReferences(); });
5651}
5652
5653//----------------------------------------------------------------------
5654
5656{
5657 AUTO_TRACE();
5658 applyToAllDefinitions([](auto* obj) { obj->addListReferences(); });
5659}
5660
5661
5662//----------------------------------------------------------------------
5663
5665{
5666 AUTO_TRACE();
5668 {
5669 rl->generatePage();
5670 }
5671}
5672
5673//----------------------------------------------------------------------
5674// Copy the documentation in entry 'root' to member definition 'md' and
5675// set the function declaration of the member to 'funcDecl'. If the boolean
5676// over_load is set the standard overload text is added.
5677
5678static void addMemberDocs(const Entry *root,
5679 MemberDefMutable *md, const DString &funcDecl,
5680 const ArgumentList *al,
5681 bool over_load,
5682 TypeSpecifier spec
5683 )
5684{
5685 if (md==nullptr) return;
5686 AUTO_TRACE("scope='{}' name='{}' args='{}' funcDecl='{}' mSpec={}",
5687 root->parent()->name,md->name(),md->argsString(),funcDecl,spec);
5688 if (!root->section.isDoc()) // @fn or @var does not need to specify the complete definition, so don't overwrite it
5689 {
5690 DString fDecl=funcDecl;
5691 // strip extern specifier
5692 fDecl.stripPrefix("extern ");
5693 md->setDefinition(fDecl);
5694 }
5696 md->addQualifiers(root->qualifiers);
5698 const NamespaceDef *nd=md->getNamespaceDef();
5699 DString fullName;
5700 if (cd)
5701 fullName = cd->name();
5702 else if (nd)
5703 fullName = nd->name();
5704
5705 if (!fullName.empty()) fullName+="::";
5706 fullName+=md->name();
5707 FileDef *rfd=root->fileDef();
5708
5709 // TODO determine scope based on root not md
5710 Definition *rscope = md->getOuterScope();
5711
5712 const ArgumentList &mdAl = md->argumentList();
5713 if (al)
5714 {
5715 ArgumentList mergedAl = *al;
5716 //printf("merging arguments (1) docs=%d\n",root->doc.empty());
5717 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedAl,!root->doc.empty());
5718 }
5719 else
5720 {
5721 if (
5722 matchArguments2( md->getOuterScope(), md->getFileDef(),md->typeString(),const_cast<ArgumentList*>(&mdAl),
5723 rscope,rfd,root->type,&root->argList,
5724 true, root->lang
5725 )
5726 )
5727 {
5728 //printf("merging arguments (2)\n");
5729 ArgumentList mergedArgList = root->argList;
5730 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.empty());
5731 }
5732 }
5733 if (over_load) // the \overload keyword was used
5734 {
5736 if (!root->doc.empty())
5737 {
5738 doc+="<p>";
5739 doc+=root->doc;
5740 }
5741 md->setDocumentation(doc,root->docFile,root->docLine);
5743 md->setDocsForDefinition(!root->proto);
5744 }
5745 else
5746 {
5747 //printf("overwrite!\n");
5748 md->setDocumentation(root->doc,root->docFile,root->docLine);
5749 md->setDocsForDefinition(!root->proto);
5750
5751 //printf("overwrite!\n");
5752 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
5753
5754 if (
5755 (md->inbodyDocumentation().empty() ||
5756 !root->parent()->name.empty()
5757 ) && !root->inbodyDocs.empty()
5758 )
5759 {
5761 }
5762 }
5763
5764 //printf("initializer: '%s'(isEmpty=%d) '%s'(isEmpty=%d)\n",
5765 // qPrint(md->initializer()),md->initializer().empty(),
5766 // qPrint(root->initializer),root->initializer.empty()
5767 // );
5768 std::string rootInit = root->initializer.str();
5769 if (md->initializer().empty() && !rootInit.empty())
5770 {
5771 //printf("setInitializer\n");
5772 md->setInitializer(rootInit);
5773 }
5774 if (md->requiresClause().empty() && !root->req.empty())
5775 {
5776 md->setRequiresClause(root->req);
5777 }
5778
5779 md->setMaxInitLines(root->initLines);
5780
5781 if (rfd)
5782 {
5783 if ((md->getStartBodyLine()==-1 && root->bodyLine!=-1)
5784 )
5785 {
5786 //printf("Setting new body segment [%d,%d]\n",root->bodyLine,root->endBodyLine);
5787 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
5788 md->setBodyDef(rfd);
5789 }
5790
5791 md->setRefItems(root->sli);
5792 md->setRequirementReferences(root->rqli);
5793 }
5794
5796 md->addQualifiers(root->qualifiers);
5797
5798 md->mergeMemberSpecifiers(spec);
5800 addMemberToGroups(root,md);
5802 if (cd) cd->insertUsedFile(rfd);
5803 //printf("root->mGrpId=%d\n",root->mGrpId);
5804 if (root->mGrpId!=-1)
5805 {
5806 if (md->getMemberGroupId()!=-1)
5807 {
5808 if (md->getMemberGroupId()!=root->mGrpId)
5809 {
5810 warn(root->fileName,root->startLine,
5811 "member {} belongs to two different groups. The second one found here will be ignored.",
5812 md->name()
5813 );
5814 }
5815 }
5816 else // set group id
5817 {
5818 //printf("setMemberGroupId=%d md=%s\n",root->mGrpId,qPrint(md->name()));
5819 md->setMemberGroupId(root->mGrpId);
5820 }
5821 }
5822 md->addQualifiers(root->qualifiers);
5823}
5824
5825//----------------------------------------------------------------------
5826// find a class definition given the scope name and (optionally) a
5827// template list specifier
5828
5830 const DString &scopeName)
5831{
5832 SymbolResolver resolver(fd);
5833 const ClassDef *tcd = resolver.resolveClass(nd,scopeName,true,true);
5834 //printf("findClassDefinition(fd=%s,ns=%s,scopeName=%s)='%s'\n",
5835 // qPrint(fd?fd->name():""),qPrint(nd?nd->name():""),
5836 // qPrint(scopeName),qPrint(tcd?tcd->name():""));
5837 return tcd;
5838}
5839
5840//----------------------------------------------------------------------------
5841// Returns true, if the entry belongs to the group of the member definition,
5842// otherwise false.
5843
5844static bool isEntryInGroupOfMember(const Entry *root,const MemberDef *md,bool allowNoGroup=false)
5845{
5846 const GroupDef *gd = md->getGroupDef();
5847 if (!gd)
5848 {
5849 return allowNoGroup;
5850 }
5851
5852 for (const auto &g : root->groups)
5853 {
5854 if (g.groupname == gd->name())
5855 {
5856 return true; // matching group
5857 }
5858 }
5859
5860 return false;
5861}
5862
5863//----------------------------------------------------------------------
5864// Adds the documentation contained in 'root' to a global function
5865// with name 'name' and argument list 'args' (for overloading) and
5866// function declaration 'decl' to the corresponding member definition.
5867
5868static bool findGlobalMember(const Entry *root,
5869 const DString &namespaceName,
5870 const DString &type,
5871 const DString &name,
5872 const DString &tempArg,
5873 const DString &,
5874 const DString &decl,
5875 TypeSpecifier /* spec */)
5876{
5877 AUTO_TRACE("namespace='{}' type='{}' name='{}' tempArg='{}' decl='{}'",namespaceName,type,name,tempArg,decl);
5878 DString n=name;
5879 if (n.empty()) return false;
5880 if (n.find("::")!=DString::npos) return false; // skip undefined class members
5881 MemberName *mn=Doxygen::functionNameLinkedMap->find(n+tempArg); // look in function dictionary
5882 if (mn==nullptr)
5883 {
5884 mn=Doxygen::functionNameLinkedMap->find(n); // try without template arguments
5885 }
5886 if (mn) // function name defined
5887 {
5888 AUTO_TRACE_ADD("Found symbol name");
5889 //int count=0;
5890 bool found=false;
5891 for (const auto &md : *mn)
5892 {
5893 // If the entry has groups, then restrict the search to members which are
5894 // in one of the groups of the entry. If md is not associated with a group yet,
5895 // allow this documentation entry to add the group info.
5896 if (!root->groups.empty() && !isEntryInGroupOfMember(root, md.get(), true))
5897 {
5898 continue;
5899 }
5900
5901 const NamespaceDef *nd=nullptr;
5902 if (md->isAlias() && md->getOuterScope() &&
5903 md->getOuterScope()->definitionType()==Definition::TypeNamespace)
5904 {
5905 nd = toNamespaceDef(md->getOuterScope());
5906 }
5907 else
5908 {
5909 nd = md->getNamespaceDef();
5910 }
5911
5912 // special case for strong enums
5913 size_t enumNamePos=0;
5914 if (nd && md->isEnumValue() && (enumNamePos=namespaceName.rfind("::"))!=DString::npos)
5915 { // md part of a strong enum in a namespace?
5916 DString enumName = namespaceName.mid(enumNamePos+2);
5917 if (namespaceName.left(enumNamePos)==nd->name())
5918 {
5920 if (enumMn)
5921 {
5922 for (const auto &emd : *enumMn)
5923 {
5924 found = emd->isStrong() && md->getEnumScope()==emd.get();
5925 if (found)
5926 {
5927 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,nullptr,false,root->spec);
5928 break;
5929 }
5930 }
5931 }
5932 }
5933 if (found)
5934 {
5935 break;
5936 }
5937 }
5938 else if (nd==nullptr && md->isEnumValue()) // md part of global strong enum?
5939 {
5940 MemberName *enumMn=Doxygen::functionNameLinkedMap->find(namespaceName);
5941 if (enumMn)
5942 {
5943 for (const auto &emd : *enumMn)
5944 {
5945 found = emd->isStrong() && md->getEnumScope()==emd.get();
5946 if (found)
5947 {
5948 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,nullptr,false,root->spec);
5949 break;
5950 }
5951 }
5952 }
5953 }
5954
5955 const FileDef *fd=root->fileDef();
5956 //printf("File %s\n",fd ? qPrint(fd->name()) : "<none>");
5958 if (fd)
5959 {
5960 nl = fd->getUsedNamespaces();
5961 }
5962 //printf("NamespaceList %p\n",nl);
5963
5964 // search in the list of namespaces that are imported via a
5965 // using declaration
5966 bool viaUsingDirective = nd && nl.find(nd->qualifiedName())!=nullptr;
5967
5968 if ((namespaceName.empty() && nd==nullptr) || // not in a namespace
5969 (nd && nd->name()==namespaceName) || // or in the same namespace
5970 viaUsingDirective // member in 'using' namespace
5971 )
5972 {
5973 AUTO_TRACE_ADD("Try to add member '{}' to scope '{}'",md->name(),namespaceName);
5974
5975 NamespaceDef *rnd = nullptr;
5976 if (!namespaceName.empty()) rnd = Doxygen::namespaceLinkedMap->find(namespaceName);
5977
5978 const ArgumentList &mdAl = md.get()->argumentList();
5979 bool matching=
5980 (mdAl.empty() && root->argList.empty()) ||
5981 md->isVariable() || md->isTypedef() || /* in case of function pointers */
5982 matchArguments2(md->getOuterScope(),md->getFileDef(),md->typeString(),&mdAl,
5983 rnd ? rnd : Doxygen::globalScope,fd,root->type,&root->argList,
5984 false,root->lang);
5985
5986 // for template members we need to check if the number of
5987 // template arguments is the same, otherwise we are dealing with
5988 // different functions.
5989 if (matching && !root->tArgLists.empty())
5990 {
5991 const ArgumentList &mdTempl = md->templateArguments();
5992 if (root->tArgLists.back().size()!=mdTempl.size())
5993 {
5994 matching=false;
5995 }
5996 }
5997
5998 //printf("%s<->%s\n",
5999 // qPrint(argListToString(md->argumentList())),
6000 // qPrint(argListToString(root->argList)));
6001
6002 // For static members we also check if the comment block was found in
6003 // the same file. This is needed because static members with the same
6004 // name can be in different files. Thus it would be wrong to just
6005 // put the comment block at the first syntactically matching member. If
6006 // the comment block belongs to a group of the static member, then add
6007 // the documentation even if it is in a different file.
6008 if (matching && md->isStatic() &&
6009 md->getDefFileName()!=root->fileName &&
6010 mn->size()>1 &&
6011 !isEntryInGroupOfMember(root,md.get()))
6012 {
6013 matching = false;
6014 }
6015
6016 // for template member we also need to check the return type and requires
6017 if (!md->templateArguments().empty() && !root->tArgLists.empty())
6018 {
6019 //printf("Comparing return types '%s'<->'%s'\n",
6020 // md->typeString(),type);
6021 //printf("%s: Comparing '%s'<=>'%s'\n",qPrint(md->name()),qPrint(md->requiresClause()),qPrint(root->req));
6022 if (md->templateArguments().size()!=root->tArgLists.back().size() ||
6023 md->typeString()!=type ||
6024 md->requiresClause()!=root->req)
6025 {
6026 //printf(" ---> no matching\n");
6027 matching = false;
6028 }
6029 }
6030
6031 if (matching) // add docs to the member
6032 {
6033 AUTO_TRACE_ADD("Match found");
6034 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,&root->argList,false,root->spec);
6035 found=true;
6036 break;
6037 }
6038 }
6039 }
6040 if (!found && root->relatesType!=RelatesType::Duplicate && root->section.isFunction()) // no match
6041 {
6042 DString fullFuncDecl=decl;
6043 if (!root->argList.empty()) fullFuncDecl+=argListToString(root->argList,true);
6044 DString warnMsg = "no matching file member found for \n"+fullFuncDecl;
6045 if (mn->size()>0)
6046 {
6047 warnMsg+="\nPossible candidates:";
6048 for (const auto &md : *mn)
6049 {
6050 warnMsg+="\n '";
6051 warnMsg+=replaceAnonymousScopes(md->declaration());
6052 warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
6053 }
6054 }
6055 warn(root->fileName,root->startLine, "{}", qPrint(warnMsg));
6056 }
6057 }
6058 else // got docs for an undefined member!
6059 {
6060 if (root->type!="friend class" &&
6061 root->type!="friend struct" &&
6062 root->type!="friend union" &&
6063 root->type!="friend" &&
6064 (!Config_getBool(TYPEDEF_HIDES_STRUCT) ||
6065 root->type.find("typedef ")==DString::npos)
6066 )
6067 {
6068 warn(root->fileName,root->startLine,
6069 "documented symbol '{}' was not declared or defined.",qPrint(decl)
6070 );
6071 }
6072 }
6073 return true;
6074}
6075
6077 const ArgumentLists &srcTempArgLists,
6078 const ArgumentLists &dstTempArgLists
6079 )
6080{
6081 auto srcIt = srcTempArgLists.begin();
6082 auto dstIt = dstTempArgLists.begin();
6083 while (srcIt!=srcTempArgLists.end() && dstIt!=dstTempArgLists.end())
6084 {
6085 if ((*srcIt).size()!=(*dstIt).size()) return true;
6086 ++srcIt;
6087 ++dstIt;
6088 }
6089 return false;
6090}
6091
6092static bool scopeIsTemplate(const Definition *d)
6093{
6094 bool result=false;
6095 //printf("> scopeIsTemplate(%s)\n",qPrint(d?d->name():"null"));
6097 {
6098 auto cd = toClassDef(d);
6099 result = cd->templateArguments().hasParameters() || cd->templateMaster()!=nullptr ||
6101 }
6102 //printf("< scopeIsTemplate=%d\n",result);
6103 return result;
6104}
6105
6107 const ArgumentLists &srcTempArgLists,
6108 const ArgumentLists &dstTempArgLists,
6109 const std::string &src
6110 )
6111{
6112 std::string dst;
6113 static const reg::Ex re(R"(\a\w*)");
6114 reg::Iterator it(src,re);
6116 //printf("type=%s\n",qPrint(sa->type));
6117 size_t p=0;
6118 for (; it!=end ; ++it) // for each word in srcType
6119 {
6120 const auto &match = *it;
6121 size_t i = match.position();
6122 size_t l = match.length();
6123 bool found=false;
6124 dst+=src.substr(p,i-p);
6125 std::string name=match.str();
6126
6127 auto srcIt = srcTempArgLists.begin();
6128 auto dstIt = dstTempArgLists.begin();
6129 while (srcIt!=srcTempArgLists.end() && !found)
6130 {
6131 const ArgumentList *tdAli = nullptr;
6132 std::vector<Argument>::const_iterator tdaIt;
6133 if (dstIt!=dstTempArgLists.end())
6134 {
6135 tdAli = &(*dstIt);
6136 tdaIt = tdAli->begin();
6137 ++dstIt;
6138 }
6139
6140 const ArgumentList &tsaLi = *srcIt;
6141 for (auto tsaIt = tsaLi.begin(); tsaIt!=tsaLi.end() && !found; ++tsaIt)
6142 {
6143 Argument tsa = *tsaIt;
6144 const Argument *tda = nullptr;
6145 if (tdAli && tdaIt!=tdAli->end())
6146 {
6147 tda = &(*tdaIt);
6148 ++tdaIt;
6149 }
6150 //if (tda) printf("tsa=%s|%s tda=%s|%s\n",
6151 // qPrint(tsa.type),qPrint(tsa.name),
6152 // qPrint(tda->type),qPrint(tda->name));
6153 if (name==tsa.name.str())
6154 {
6155 if (tda && tda->name.empty())
6156 {
6157 DString tdaName = tda->name;
6158 DString tdaType = tda->type;
6159 int vc=0;
6160 if (tdaType.startsWith("class ")) vc=6;
6161 else if (tdaType.startsWith("typename ")) vc=9;
6162 if (vc>0) // convert type=="class T" to type=="class" name=="T"
6163 {
6164 tdaName = tdaType.mid(vc);
6165 }
6166 if (!tdaName.empty())
6167 {
6168 name=tdaName.str(); // substitute
6169 found=true;
6170 }
6171 }
6172 }
6173 }
6174
6175 //printf(" srcList='%s' dstList='%s faList='%s'\n",
6176 // qPrint(argListToString(srclali.current())),
6177 // qPrint(argListToString(dstlali.current())),
6178 // funcTempArgList ? qPrint(argListToString(funcTempArgList)) : "<none>");
6179 ++srcIt;
6180 }
6181 dst+=name;
6182 p=i+l;
6183 }
6184 dst+=src.substr(p);
6185 //printf(" substituteTemplatesInString(%s)=%s\n",
6186 // qPrint(src),qPrint(dst));
6187 return dst;
6188}
6189
6191 const ArgumentLists &srcTempArgLists,
6192 const ArgumentLists &dstTempArgLists,
6193 const ArgumentList &src,
6194 ArgumentList &dst
6195 )
6196{
6197 auto dstIt = dst.begin();
6198 for (const Argument &sa : src)
6199 {
6200 DString dstType = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.type.str());
6201 DString dstArray = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.array.str());
6202 if (dstIt == dst.end())
6203 {
6204 Argument da = sa;
6205 da.type = dstType;
6206 da.array = dstArray;
6207 dst.push_back(da);
6208 dstIt = dst.end();
6209 }
6210 else
6211 {
6212 Argument da = *dstIt;
6213 da.type = dstType;
6214 da.array = dstArray;
6215 ++dstIt;
6216 }
6217 }
6222 srcTempArgLists,dstTempArgLists,
6223 src.trailingReturnType().str()));
6224 dst.setIsDeleted(src.isDeleted());
6225 dst.setRefQualifier(src.refQualifier());
6226 dst.setNoParameters(src.noParameters());
6227 //printf("substituteTemplatesInArgList: replacing %s with %s\n",
6228 // qPrint(argListToString(src)),qPrint(argListToString(dst))
6229 // );
6230}
6231
6232//-------------------------------------------------------------------------------------------
6233
6234static void addLocalObjCMethod(const Entry *root,
6235 const DString &scopeName,
6236 const DString &funcType,const DString &funcName,const DString &funcArgs,
6237 const DString &exceptions,const DString &funcDecl,
6238 TypeSpecifier spec)
6239{
6240 AUTO_TRACE();
6241 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
6242 ClassDefMutable *cd=nullptr;
6243 if (Config_getBool(EXTRACT_LOCAL_METHODS) && (cd=getClassMutable(scopeName)))
6244 {
6245 AUTO_TRACE_ADD("Local objective C method '{}' scopeName='{}'",root->name,scopeName);
6246 auto md = createMemberDef(
6247 root->fileName,root->startLine,root->startColumn,
6248 funcType,funcName,funcArgs,exceptions,
6249 root->protection,root->virt,root->isStatic,Relationship::Member,
6250 MemberType::Function,ArgumentList(),root->argList,root->metaData);
6251 auto mmd = toMemberDefMutable(md.get());
6252 mmd->setTagInfo(root->tagInfo());
6253 mmd->setLanguage(root->lang);
6254 mmd->setId(root->id);
6255 mmd->makeImplementationDetail();
6256 mmd->setMemberClass(cd);
6257 mmd->setDefinition(funcDecl);
6259 mmd->addQualifiers(root->qualifiers);
6260 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6261 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6262 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6263 mmd->setDocsForDefinition(!root->proto);
6264 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6265 mmd->addSectionsToDefinition(root->anchors);
6266 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6267 FileDef *fd=root->fileDef();
6268 mmd->setBodyDef(fd);
6269 mmd->setMemberSpecifiers(spec);
6270 mmd->setVhdlSpecifiers(root->vhdlSpec);
6271 mmd->setMemberGroupId(root->mGrpId);
6272 cd->insertMember(md.get());
6273 cd->insertUsedFile(fd);
6274 mmd->setRefItems(root->sli);
6275 mmd->setRequirementReferences(root->rqli);
6276
6278 mn->push_back(std::move(md));
6279 }
6280 else
6281 {
6282 // local objective C method found for class without interface
6283 }
6284}
6285
6286//-------------------------------------------------------------------------------------------
6287
6288static void addMemberFunction(const Entry *root,
6289 MemberName *mn,
6290 const DString &scopeName,
6291 const DString &namespaceName,
6292 const DString &className,
6293 const DString &funcTyp,
6294 const DString &funcName,
6295 const DString &funcArgs,
6296 const DString &funcTempList,
6297 const DString &exceptions,
6298 const DString &type,
6299 const DString &args,
6300 bool isFriend,
6301 TypeSpecifier spec,
6302 const DString &relates,
6303 const DString &funcDecl,
6304 bool overloaded,
6305 bool isFunc)
6306{
6307 AUTO_TRACE();
6308 DString funcType = funcTyp;
6309 int count=0;
6310 int noMatchCount=0;
6311 bool memFound=false;
6312 for (const auto &imd : *mn)
6313 {
6314 MemberDefMutable *md = toMemberDefMutable(imd.get());
6315 if (md==nullptr) continue;
6317 if (cd==nullptr) continue;
6318 //AUTO_TRACE_ADD("member definition found, scope needed='{}' scope='{}' args='{}' fileName='{}'",
6319 // scopeName, cd->name(), md->argsString(), root->fileName);
6320 FileDef *fd=root->fileDef();
6321 NamespaceDef *nd=nullptr;
6322 if (!namespaceName.empty()) nd=getResolvedNamespace(namespaceName);
6323
6324 //printf("scopeName %s->%s\n",qPrint(scopeName),
6325 // qPrint(stripTemplateSpecifiersFromScope(scopeName,false)));
6326
6327 // if the member we are searching for is an enum value that is part of
6328 // a "strong" enum, we need to look into the fields of the enum for a match
6329 size_t enumNamePos=0;
6330 if (md->isEnumValue() && (enumNamePos=className.rfind("::"))!=DString::npos)
6331 {
6332 DString enumName = className.mid(enumNamePos+2);
6333 DString fullScope = className.left(enumNamePos);
6334 if (!namespaceName.empty()) fullScope.prepend(namespaceName+"::");
6335 if (fullScope==cd->name())
6336 {
6337 MemberName *enumMn=Doxygen::memberNameLinkedMap->find(enumName);
6338 //printf("enumMn(%s)=%p\n",qPrint(className),(void*)enumMn);
6339 if (enumMn)
6340 {
6341 for (const auto &emd : *enumMn)
6342 {
6343 memFound = emd->isStrong() && md->getEnumScope()==emd.get();
6344 if (memFound)
6345 {
6346 addMemberDocs(root,md,funcDecl,nullptr,overloaded,spec);
6347 count++;
6348 }
6349 if (memFound) break;
6350 }
6351 }
6352 }
6353 }
6354 if (memFound) break;
6355
6356 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
6357 if (tcd==nullptr && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
6358 {
6359 // don't be fooled by anonymous scopes
6360 tcd=cd;
6361 }
6362 //printf("Looking for %s inside nd=%s result=%s cd=%s\n",
6363 // qPrint(scopeName),nd?qPrint(nd->name()):"<none>",tcd?qPrint(tcd->name()):"",qPrint(cd->name()));
6364
6365 if (cd && tcd==cd) // member's classes match
6366 {
6367 AUTO_TRACE_ADD("class definition '{}' found",cd->name());
6368
6369 // get the template parameter lists found at the member declaration
6370 ArgumentLists declTemplArgs = cd->getTemplateParameterLists();
6371 const ArgumentList &templAl = md->templateArguments();
6372 if (!templAl.empty())
6373 {
6374 declTemplArgs.push_back(templAl);
6375 }
6376
6377 // get the template parameter lists found at the member definition
6378 const ArgumentLists &defTemplArgs = root->tArgLists;
6379 //printf("defTemplArgs=%p\n",defTemplArgs);
6380
6381 // do we replace the decl argument lists with the def argument lists?
6382 bool substDone=false;
6383 ArgumentList argList;
6384
6385 /* substitute the occurrences of class template names in the
6386 * argument list before matching
6387 */
6388 const ArgumentList &mdAl = md->argumentList();
6389 if (declTemplArgs.size()>0 && declTemplArgs.size()==defTemplArgs.size())
6390 {
6391 /* the function definition has template arguments
6392 * and the class definition also has template arguments, so
6393 * we must substitute the template names of the class by that
6394 * of the function definition before matching.
6395 */
6396 substituteTemplatesInArgList(declTemplArgs,defTemplArgs,mdAl,argList);
6397
6398 substDone=true;
6399 }
6400 else /* no template arguments, compare argument lists directly */
6401 {
6402 argList = mdAl;
6403 }
6404
6405 bool matching=
6406 md->isVariable() || md->isTypedef() || // needed for function pointers
6408 md->getClassDef(),md->getFileDef(),md->typeString(),&argList,
6409 cd,fd,root->type,&root->argList,
6410 true,root->lang);
6411
6412 AUTO_TRACE_ADD("matching '{}'<=>'{}' className='{}' namespaceName='{}' result={}",
6413 argListToString(argList,true),argListToString(root->argList,true),className,namespaceName,matching);
6414
6415 if (md->getLanguage()==SrcLangExt::ObjC && md->isVariable() && root->section.isFunction())
6416 {
6417 matching = false; // don't match methods and attributes with the same name
6418 }
6419
6420 // for template member we also need to check the return type
6421 if (!md->templateArguments().empty() && !root->tArgLists.empty())
6422 {
6423 DString memType = md->typeString();
6424 memType.stripPrefix("static "); // see bug700696
6425 funcType=substitute(stripTemplateSpecifiersFromScope(funcType,true),
6426 className+"::",""); // see bug700693 & bug732594
6427 memType=substitute(stripTemplateSpecifiersFromScope(memType,true),
6428 className+"::",""); // see bug758900
6429 if (memType=="auto" && !argList.trailingReturnType().empty())
6430 {
6431 memType = argList.trailingReturnType();
6432 memType.stripPrefix(" -> ");
6433 }
6434 if (funcType=="auto" && !root->argList.trailingReturnType().empty())
6435 {
6436 funcType = root->argList.trailingReturnType();
6437 funcType.stripPrefix(" -> ");
6439 substDone=true;
6440 }
6441 AUTO_TRACE_ADD("Comparing return types '{}'<->'{}' #args {}<->{}",
6442 memType,funcType,md->templateArguments().size(),root->tArgLists.back().size());
6443 if (md->templateArguments().size()!=root->tArgLists.back().size() || memType!=funcType)
6444 {
6445 //printf(" ---> no matching\n");
6446 matching = false;
6447 }
6448 }
6449 else if (defTemplArgs.size()>declTemplArgs.size())
6450 {
6451 AUTO_TRACE_ADD("Different number of template arguments {} vs {}",defTemplArgs.size(),declTemplArgs.size());
6452 // avoid matching a non-template function in a template class against a
6453 // template function with the same name and parameters, see issue #10184
6454 substDone = false;
6455 matching = false;
6456 }
6457 bool rootIsUserDoc = root->section.isMemberDoc();
6458 bool classIsTemplate = scopeIsTemplate(md->getClassDef());
6459 bool mdIsTemplate = md->templateArguments().hasParameters();
6460 bool classOrMdIsTemplate = mdIsTemplate || classIsTemplate;
6461 bool rootIsTemplate = !root->tArgLists.empty();
6462 //printf("classIsTemplate=%d mdIsTemplate=%d rootIsTemplate=%d\n",classIsTemplate,mdIsTemplate,rootIsTemplate);
6463 if (!rootIsUserDoc && // don't check out-of-line @fn references, see bug722457
6464 (mdIsTemplate || rootIsTemplate) && // either md or root is a template
6465 ((classOrMdIsTemplate && !rootIsTemplate) || (!classOrMdIsTemplate && rootIsTemplate))
6466 )
6467 {
6468 // Method with template return type does not match method without return type
6469 // even if the parameters are the same. See also bug709052
6470 AUTO_TRACE_ADD("Comparing return types: template v.s. non-template");
6471 matching = false;
6472 }
6473
6474 AUTO_TRACE_ADD("Match results of matchArguments2='{}' substDone='{}'",matching,substDone);
6475
6476 if (substDone) // found a new argument list
6477 {
6478 if (matching) // replace member's argument list
6479 {
6481 md->moveArgumentList(std::make_unique<ArgumentList>(argList));
6482 }
6483 else // no match
6484 {
6485 if (!funcTempList.empty() &&
6486 isSpecialization(declTemplArgs,defTemplArgs))
6487 {
6488 // check if we are dealing with a partial template
6489 // specialization. In this case we add it to the class
6490 // even though the member arguments do not match.
6491
6492 addMethodToClass(root,cd,type,md->name(),args,isFriend,
6493 md->protection(),md->isStatic(),md->virtualness(),spec,relates);
6494 return;
6495 }
6496 }
6497 }
6498 if (matching)
6499 {
6500 addMemberDocs(root,md,funcDecl,nullptr,overloaded,spec);
6501 count++;
6502 memFound=true;
6503 }
6504 }
6505 else if (cd && cd!=tcd) // we did find a class with the same name as cd
6506 // but in a different namespace
6507 {
6508 noMatchCount++;
6509 }
6510
6511 if (memFound) break;
6512 }
6513 if (count==0 && root->parent() && root->parent()->section.isObjcImpl())
6514 {
6515 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
6516 return;
6517 }
6518 if (count==0 && !(isFriend && funcType=="class"))
6519 {
6520 int candidates=0;
6521 const ClassDef *ecd = nullptr, *ucd = nullptr;
6522 MemberDef *emd = nullptr, *umd = nullptr;
6523 //printf("Assume template class\n");
6524 for (const auto &md : *mn)
6525 {
6526 MemberDef *cmd=md.get();
6528 ClassDefMutable *ccd=cdmdm ? cdmdm->getClassDefMutable() : nullptr;
6529 //printf("ccd->name()==%s className=%s\n",qPrint(ccd->name()),qPrint(className));
6530 if (ccd!=nullptr && rightScopeMatch(ccd->name(),className))
6531 {
6532 const ArgumentList &templAl = md->templateArguments();
6533 if (!root->tArgLists.empty() && !templAl.empty() &&
6534 root->tArgLists.back().size()<=templAl.size())
6535 {
6536 AUTO_TRACE_ADD("add template specialization");
6537 addMethodToClass(root,ccd,type,md->name(),args,isFriend,
6538 root->protection,root->isStatic,root->virt,spec,relates);
6539 return;
6540 }
6541 if (argListToString(md->argumentList(),false,false) ==
6542 argListToString(root->argList,false,false))
6543 { // exact argument list match -> remember
6544 ucd = ecd = ccd;
6545 umd = emd = cmd;
6546 AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': exact match",
6547 className,ccd->name(),md->argsString());
6548 }
6549 else // arguments do not match, but member name and scope do -> remember
6550 {
6551 ucd = ccd;
6552 umd = cmd;
6553 AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': no match",
6554 className,ccd->name(),md->argsString());
6555 }
6556 candidates++;
6557 }
6558 }
6559 bool strictProtoMatching = Config_getBool(STRICT_PROTO_MATCHING);
6560 if (!strictProtoMatching)
6561 {
6562 if (candidates==1 && ucd && umd)
6563 {
6564 // we didn't find an actual match on argument lists, but there is only 1 member with this
6565 // name in the same scope, so that has to be the one.
6566 addMemberDocs(root,toMemberDefMutable(umd),funcDecl,nullptr,overloaded,spec);
6567 return;
6568 }
6569 else if (candidates>1 && ecd && emd)
6570 {
6571 // we didn't find a unique match using type resolution,
6572 // but one of the matches has the exact same signature so
6573 // we take that one.
6574 addMemberDocs(root,toMemberDefMutable(emd),funcDecl,nullptr,overloaded,spec);
6575 return;
6576 }
6577 }
6578
6579 DString warnMsg = "no ";
6580 if (noMatchCount>1) warnMsg+="uniquely ";
6581 warnMsg+="matching class member found for \n";
6582
6583 for (const ArgumentList &al : root->tArgLists)
6584 {
6585 warnMsg+=" template ";
6586 warnMsg+=tempArgListToString(al,root->lang);
6587 warnMsg+='\n';
6588 }
6589
6590 DString fullFuncDecl=funcDecl;
6591 if (isFunc) fullFuncDecl+=argListToString(root->argList,true);
6592
6593 warnMsg+=" ";
6594 warnMsg+=fullFuncDecl;
6595
6596 if (candidates>0 || noMatchCount>=1)
6597 {
6598 warnMsg+="\nPossible candidates:";
6599
6600 NamespaceDef *nd=nullptr;
6601 if (!namespaceName.empty()) nd=getResolvedNamespace(namespaceName);
6602 FileDef *fd=root->fileDef();
6603
6604 for (const auto &md : *mn)
6605 {
6606 const ClassDef *cd=md->getClassDef();
6607 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
6608 if (tcd==nullptr && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
6609 {
6610 // don't be fooled by anonymous scopes
6611 tcd=cd;
6612 }
6613 if (cd!=nullptr && (rightScopeMatch(cd->name(),className) || (cd!=tcd)))
6614 {
6615 warnMsg+='\n';
6616 const ArgumentList &templAl = md->templateArguments();
6617 warnMsg+=" '";
6618 if (templAl.hasParameters())
6619 {
6620 warnMsg+="template ";
6621 warnMsg+=tempArgListToString(templAl,root->lang);
6622 warnMsg+='\n';
6623 warnMsg+=" ";
6624 }
6625 if (!md->typeString().empty())
6626 {
6627 warnMsg+=md->typeString();
6628 warnMsg+=' ';
6629 }
6631 if (!qScope.empty())
6632 warnMsg+=qScope+"::"+md->name();
6633 warnMsg+=md->argsString();
6634 warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
6635 }
6636 }
6637 }
6638 warn(root->fileName,root->startLine,"{}",warnMsg);
6639 }
6640}
6641
6642//-------------------------------------------------------------------------------------------
6643
6644static void addMemberSpecialization(const Entry *root,
6645 MemberName *mn,
6646 ClassDefMutable *cd,
6647 const DString &funcType,
6648 const DString &funcName,
6649 const DString &funcArgs,
6650 const DString &funcDecl,
6651 const DString &exceptions,
6652 TypeSpecifier spec
6653 )
6654{
6655 AUTO_TRACE("funcType={} funcName={} funcArgs={} funcDecl={} spec={}",funcType,funcName,funcArgs,funcDecl,spec);
6656 MemberDef *declMd=nullptr;
6657 for (const auto &md : *mn)
6658 {
6659 if (md->getClassDef()==cd)
6660 {
6661 // TODO: we should probably also check for matching arguments
6662 declMd = md.get();
6663 break;
6664 }
6665 }
6666 MemberType mtype=MemberType::Function;
6667 ArgumentList tArgList;
6668 // getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6669 auto md = createMemberDef(
6670 root->fileName,root->startLine,root->startColumn,
6671 funcType,funcName,funcArgs,exceptions,
6672 declMd ? declMd->protection() : root->protection,
6673 root->virt,root->isStatic,Relationship::Member,
6674 mtype,tArgList,root->argList,root->metaData);
6675 auto mmd = toMemberDefMutable(md.get());
6676 //printf("new specialized member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6677 mmd->setTagInfo(root->tagInfo());
6678 mmd->setLanguage(root->lang);
6679 mmd->setId(root->id);
6680 mmd->setMemberClass(cd);
6681 mmd->setTemplateSpecialization(true);
6682 mmd->setTypeConstraints(root->typeConstr);
6683 mmd->setDefinition(funcDecl);
6685 mmd->addQualifiers(root->qualifiers);
6686 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6687 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6688 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6689 mmd->setDocsForDefinition(!root->proto);
6690 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6691 mmd->addSectionsToDefinition(root->anchors);
6692 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6693 FileDef *fd=root->fileDef();
6694 mmd->setBodyDef(fd);
6695 mmd->setMemberSpecifiers(spec);
6696 mmd->setVhdlSpecifiers(root->vhdlSpec);
6697 mmd->setMemberGroupId(root->mGrpId);
6698 cd->insertMember(md.get());
6699 mmd->setRefItems(root->sli);
6700 mmd->setRequirementReferences(root->rqli);
6701
6702 mn->push_back(std::move(md));
6703}
6704
6705//-------------------------------------------------------------------------------------------
6706
6707static void addOverloaded(const Entry *root,MemberName *mn,
6708 const DString &funcType,const DString &funcName,const DString &funcArgs,
6709 const DString &funcDecl,const DString &exceptions,TypeSpecifier spec)
6710{
6711 // for unique overloaded member we allow the class to be
6712 // omitted, this is to be Qt compatible. Using this should
6713 // however be avoided, because it is error prone
6714 bool sameClass=false;
6715 if (mn->size()>0)
6716 {
6717 // check if all members with the same name are also in the same class
6718 sameClass = std::equal(mn->begin()+1,mn->end(),mn->begin(),
6719 [](const auto &md1,const auto &md2)
6720 { return md1->getClassDef()->name()==md2->getClassDef()->name(); });
6721 }
6722 if (sameClass)
6723 {
6724 MemberDefMutable *mdm = toMemberDefMutable(mn->front().get());
6725 ClassDefMutable *cd = mdm ? mdm->getClassDefMutable() : nullptr;
6726 if (cd==nullptr) return;
6727
6728 MemberType mtype = MemberType::Function;
6729 if (root->mtype==MethodTypes::Signal) mtype=MemberType::Signal;
6730 else if (root->mtype==MethodTypes::Slot) mtype=MemberType::Slot;
6731 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType::DCOP;
6732
6733 // new overloaded member function
6734 std::unique_ptr<ArgumentList> tArgList =
6735 getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6736 //printf("new related member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6737 auto md = createMemberDef(
6738 root->fileName,root->startLine,root->startColumn,
6739 funcType,funcName,funcArgs,exceptions,
6740 root->protection,root->virt,root->isStatic,Relationship::Related,
6741 mtype,tArgList ? *tArgList : ArgumentList(),root->argList,root->metaData);
6742 auto mmd = toMemberDefMutable(md.get());
6743 mmd->setTagInfo(root->tagInfo());
6744 mmd->setLanguage(root->lang);
6745 mmd->setId(root->id);
6746 mmd->setTypeConstraints(root->typeConstr);
6747 mmd->setMemberClass(cd);
6748 mmd->setDefinition(funcDecl);
6750 mmd->addQualifiers(root->qualifiers);
6752 doc+="<p>";
6753 doc+=root->doc;
6754 mmd->setDocumentation(doc,root->docFile,root->docLine);
6755 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6756 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6757 mmd->setDocsForDefinition(!root->proto);
6758 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6759 mmd->addSectionsToDefinition(root->anchors);
6760 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6761 FileDef *fd=root->fileDef();
6762 mmd->setBodyDef(fd);
6763 mmd->setMemberSpecifiers(spec);
6764 mmd->setVhdlSpecifiers(root->vhdlSpec);
6765 mmd->setMemberGroupId(root->mGrpId);
6766 cd->insertMember(md.get());
6767 cd->insertUsedFile(fd);
6768 mmd->setRefItems(root->sli);
6769 mmd->setRequirementReferences(root->rqli);
6770
6771 mn->push_back(std::move(md));
6772 }
6773}
6774
6775static void insertMemberAlias(Definition *outerScope,const MemberDef *md)
6776{
6777 if (outerScope && outerScope!=Doxygen::globalScope)
6778 {
6779 auto aliasMd = createMemberDefAlias(outerScope,md);
6780 if (outerScope->definitionType()==Definition::TypeClass)
6781 {
6782 ClassDefMutable *cdm = toClassDefMutable(outerScope);
6783 if (cdm)
6784 {
6785 cdm->insertMember(aliasMd.get());
6786 }
6787 }
6788 else if (outerScope->definitionType()==Definition::TypeNamespace)
6789 {
6790 NamespaceDefMutable *ndm = toNamespaceDefMutable(outerScope);
6791 if (ndm)
6792 {
6793 ndm->insertMember(aliasMd.get());
6794 }
6795 }
6796 else if (outerScope->definitionType()==Definition::TypeFile)
6797 {
6798 toFileDef(outerScope)->insertMember(aliasMd.get());
6799 }
6800 if (aliasMd)
6801 {
6802 Doxygen::functionNameLinkedMap->add(md->name())->push_back(std::move(aliasMd));
6803 }
6804 }
6805}
6806
6807//-------------------------------------------------------------------------------------------
6808
6809/*! This function tries to find a member (in a documented class/file/namespace)
6810 * that corresponds to the function/variable declaration given in \a funcDecl.
6811 *
6812 * The boolean \a overloaded is used to specify whether or not a standard
6813 * overload documentation line should be generated.
6814 *
6815 * The boolean \a isFunc is a hint that indicates that this is a function
6816 * instead of a variable or typedef.
6817 */
6818static void findMember(const Entry *root,
6819 const DString &relates,
6820 const DString &type,
6821 const DString &args,
6822 DString funcDecl,
6823 bool overloaded,
6824 bool isFunc
6825 )
6826{
6827 AUTO_TRACE("root='{}' funcDecl='{}' related='{}' overload={} isFunc={} mGrpId={} #tArgList={} spec={} lang={}",
6828 root->name, funcDecl, relates, overloaded, isFunc, root->mGrpId, root->tArgLists.size(),
6829 root->spec, root->lang);
6830
6831 DString scopeName;
6832 DString className;
6833 DString namespaceName;
6834 DString funcType;
6835 DString funcName;
6836 DString funcArgs;
6837 DString funcTempList;
6838 DString exceptions;
6839 DString funcSpec;
6840 bool isRelated=false;
6841 bool isMemberOf=false;
6842 bool isFriend=false;
6843 bool done=false;
6844 TypeSpecifier spec = root->spec;
6845 while (!done)
6846 {
6847 done=true;
6848 if (funcDecl.stripPrefix("friend ")) // treat friends as related members
6849 {
6850 isFriend=true;
6851 done=false;
6852 }
6853 if (funcDecl.stripPrefix("inline "))
6854 {
6855 spec.setInline(true);
6856 done=false;
6857 }
6858 if (funcDecl.stripPrefix("explicit "))
6859 {
6860 spec.setExplicit(true);
6861 done=false;
6862 }
6863 if (funcDecl.stripPrefix("mutable "))
6864 {
6865 spec.setMutable(true);
6866 done=false;
6867 }
6868 if (funcDecl.stripPrefix("thread_local "))
6869 {
6870 spec.setThreadLocal(true);
6871 done=false;
6872 }
6873 if (funcDecl.stripPrefix("virtual "))
6874 {
6875 done=false;
6876 }
6877 }
6878
6879 // delete any ; from the function declaration
6880 size_t sep=0;
6881 while ((sep=funcDecl.find(';'))!=DString::npos)
6882 {
6883 funcDecl=(funcDecl.left(sep)+funcDecl.mid(sep+1)).stripWhiteSpace();
6884 }
6885
6886 // make sure the first character is a space to simplify searching.
6887 if (!funcDecl.empty() && funcDecl[0]!=' ') funcDecl.prepend(" ");
6888
6889 // remove some superfluous spaces
6890 funcDecl= substitute(
6891 substitute(
6892 substitute(funcDecl,"~ ","~"),
6893 ":: ","::"
6894 ),
6895 " ::","::"
6896 ).stripWhiteSpace();
6897
6898 //printf("funcDecl='%s'\n",qPrint(funcDecl));
6899 if (isFriend && funcDecl.startsWith("class "))
6900 {
6901 //printf("friend class\n");
6902 funcDecl=funcDecl.mid(6);
6903 funcName = funcDecl;
6904 }
6905 else if (isFriend && funcDecl.startsWith("struct "))
6906 {
6907 funcDecl=funcDecl.mid(7);
6908 funcName = funcDecl;
6909 }
6910 else
6911 {
6912 // extract information from the declarations
6913 parseFuncDecl(funcDecl,root->lang,scopeName,funcType,funcName,
6914 funcArgs,funcTempList,exceptions
6915 );
6916 }
6917
6918 // the class name can also be a namespace name, we decide this later.
6919 // if a related class name is specified and the class name could
6920 // not be derived from the function declaration, then use the
6921 // related field.
6922 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}' funcType='{}' funcName='{}' funcArgs='{}'",
6923 scopeName,className,namespaceName,funcType,funcName,funcArgs);
6924 if (!relates.empty())
6925 { // related member, prefix user specified scope
6926 isRelated=true;
6927 isMemberOf=(root->relatesType == RelatesType::MemberOf);
6928 if (getClass(relates)==nullptr && !scopeName.empty())
6929 {
6930 scopeName= mergeScopes(scopeName,relates);
6931 }
6932 else
6933 {
6934 scopeName = relates;
6935 }
6936 }
6937
6938 if (relates.empty() && root->parent() &&
6939 (root->parent()->section.isScope() || root->parent()->section.isObjcImpl()) &&
6940 !root->parent()->name.empty()) // see if we can combine scopeName
6941 // with the scope in which it was found
6942 {
6943 DString joinedName = root->parent()->name+"::"+scopeName;
6944 if (!scopeName.empty() &&
6945 (getClass(joinedName) || Doxygen::namespaceLinkedMap->find(joinedName)))
6946 {
6947 scopeName = joinedName;
6948 }
6949 else
6950 {
6951 scopeName = mergeScopes(root->parent()->name,scopeName);
6952 }
6953 }
6954 else // see if we can prefix a namespace or class that is used from the file
6955 {
6956 FileDef *fd=root->fileDef();
6957 if (fd)
6958 {
6959 for (const auto &fnd : fd->getUsedNamespaces())
6960 {
6961 DString joinedName = fnd->name()+"::"+scopeName;
6962 if (Doxygen::namespaceLinkedMap->find(joinedName))
6963 {
6964 scopeName=joinedName;
6965 break;
6966 }
6967 }
6968 }
6969 }
6971 removeRedundantWhiteSpace(scopeName),false,&funcSpec,DString(),false);
6972
6973 // funcSpec contains the last template specifiers of the given scope.
6974 // If this method does not have any template arguments or they are
6975 // empty while funcSpec is not empty we assume this is a
6976 // specialization of a method. If not, we clear the funcSpec and treat
6977 // this as a normal method of a template class.
6978 if (!(root->tArgLists.size()>0 &&
6979 root->tArgLists.front().size()==0
6980 )
6981 )
6982 {
6983 funcSpec.clear();
6984 }
6985
6986 //namespaceName=removeAnonymousScopes(namespaceName);
6987 if (!Config_getBool(EXTRACT_ANON_NSPACES) && scopeName.find('@')!=DString::npos) return; // skip stuff in anonymous namespace...
6988
6989 // split scope into a namespace and a class part
6990 extractNamespaceName(scopeName,className,namespaceName,true);
6991 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}'",scopeName,className,namespaceName);
6992
6993 //printf("namespaceName='%s' className='%s'\n",qPrint(namespaceName),qPrint(className));
6994 // merge class and namespace scopes again
6995 scopeName.clear();
6996 if (!namespaceName.empty())
6997 {
6998 if (className.empty())
6999 {
7000 scopeName=namespaceName;
7001 }
7002 else if (!relates.empty() || // relates command with explicit scope
7003 !getClass(className)) // class name only exists in a namespace
7004 {
7005 scopeName=namespaceName+"::"+className;
7006 }
7007 else
7008 {
7009 scopeName=className;
7010 }
7011 }
7012 else if (!className.empty())
7013 {
7014 scopeName=className;
7015 }
7016 //printf("new scope='%s'\n",qPrint(scopeName));
7017
7018 DString tempScopeName=scopeName;
7019 ClassDefMutable *cd=getClassMutable(scopeName);
7020 if (cd)
7021 {
7022 if (funcSpec.empty())
7023 {
7024 uint32_t argListIndex=0;
7025 tempScopeName=cd->qualifiedNameWithTemplateParameters(&root->tArgLists,&argListIndex);
7026 }
7027 else
7028 {
7029 tempScopeName=scopeName+funcSpec;
7030 }
7031 }
7032 //printf("scopeName=%s cd=%p root->tArgLists=%p result=%s\n",
7033 // qPrint(scopeName),cd,root->tArgLists,qPrint(tempScopeName));
7034
7035 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
7036 // rebuild the function declaration (needed to get the scope right).
7037 if (!scopeName.empty() && !isRelated && !isFriend && !Config_getBool(HIDE_SCOPE_NAMES) && root->lang!=SrcLangExt::Python)
7038 {
7039 if (!funcType.empty())
7040 {
7041 if (isFunc) // a function -> we use argList for the arguments
7042 {
7043 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcTempList;
7044 }
7045 else
7046 {
7047 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcArgs;
7048 }
7049 }
7050 else
7051 {
7052 if (isFunc) // a function => we use argList for the arguments
7053 {
7054 funcDecl=tempScopeName+"::"+funcName+funcTempList;
7055 }
7056 else // variable => add 'argument' list
7057 {
7058 funcDecl=tempScopeName+"::"+funcName+funcArgs;
7059 }
7060 }
7061 }
7062 else // build declaration without scope
7063 {
7064 if (!funcType.empty()) // but with a type
7065 {
7066 if (isFunc) // function => omit argument list
7067 {
7068 funcDecl=funcType+" "+funcName+funcTempList;
7069 }
7070 else // variable => add 'argument' list
7071 {
7072 funcDecl=funcType+" "+funcName+funcArgs;
7073 }
7074 }
7075 else // no type
7076 {
7077 if (isFunc)
7078 {
7079 funcDecl=funcName+funcTempList;
7080 }
7081 else
7082 {
7083 funcDecl=funcName+funcArgs;
7084 }
7085 }
7086 }
7087
7088 if (funcType=="template class" && !funcTempList.empty())
7089 return; // ignore explicit template instantiations
7090
7091 AUTO_TRACE_ADD("Parse results: namespaceName='{}' className=`{}` funcType='{}' funcSpec='{}' "
7092 " funcName='{}' funcArgs='{}' funcTempList='{}' funcDecl='{}' relates='{}'"
7093 " exceptions='{}' isRelated={} isMemberOf={} isFriend={} isFunc={}",
7094 namespaceName, className, funcType, funcSpec,
7095 funcName, funcArgs, funcTempList, funcDecl, relates,
7096 exceptions, isRelated, isMemberOf, isFriend, isFunc);
7097
7098 if (!funcName.empty()) // function name is valid
7099 {
7100 // check if 'className' is actually a scoped enum, in which case we need to
7101 // process it as a global, see issue #6471
7102 bool strongEnum = false;
7103 MemberName *mn=nullptr;
7104 if (!className.empty() && (mn=Doxygen::functionNameLinkedMap->find(className)))
7105 {
7106 for (const auto &imd : *mn)
7107 {
7108 MemberDefMutable *md = toMemberDefMutable(imd.get());
7109 Definition *mdScope = nullptr;
7110 if (md && md->isEnumerate() && md->isStrong() && (mdScope=md->getOuterScope()) &&
7111 // need filter for the correct scope, see issue #9668
7112 ((namespaceName.empty() && mdScope==Doxygen::globalScope) || (mdScope->name()==namespaceName)))
7113 {
7114 AUTO_TRACE_ADD("'{}' is a strong enum! (namespace={} md->getOuterScope()->name()={})",md->name(),namespaceName,md->getOuterScope()->name());
7115 strongEnum = true;
7116 // pass the scope name name as a 'namespace' to the findGlobalMember function
7117 if (!namespaceName.empty())
7118 {
7119 namespaceName+="::"+className;
7120 }
7121 else
7122 {
7123 namespaceName=className;
7124 }
7125 }
7126 }
7127 }
7128
7129 if (funcName.startsWith("operator ")) // strip class scope from cast operator
7130 {
7131 funcName = substitute(funcName,className+"::","");
7132 }
7133 mn = nullptr;
7134 if (!funcTempList.empty()) // try with member specialization
7135 {
7136 mn=Doxygen::memberNameLinkedMap->find(funcName+funcTempList);
7137 }
7138 if (mn==nullptr) // try without specialization
7139 {
7140 mn=Doxygen::memberNameLinkedMap->find(funcName);
7141 }
7142 if (!isRelated && !strongEnum && mn) // function name already found
7143 {
7144 AUTO_TRACE_ADD("member name exists ({} members with this name)",mn->size());
7145 if (!className.empty()) // class name is valid
7146 {
7147 if (funcSpec.empty()) // not a member specialization
7148 {
7149 addMemberFunction(root,mn,scopeName,namespaceName,className,funcType,funcName,
7150 funcArgs,funcTempList,exceptions,
7151 type,args,isFriend,spec,relates,funcDecl,overloaded,isFunc);
7152 }
7153 else if (cd) // member specialization
7154 {
7155 addMemberSpecialization(root,mn,cd,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
7156 }
7157 else
7158 {
7159 //printf("*** Specialized member %s of unknown scope %s%s found!\n",
7160 // qPrint(scopeName),qPrint(funcName),qPrint(funcArgs));
7161 }
7162 }
7163 else if (overloaded) // check if the function belongs to only one class
7164 {
7165 addOverloaded(root,mn,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
7166 }
7167 else // unrelated function with the same name as a member
7168 {
7169 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
7170 {
7171 DString fullFuncDecl=funcDecl;
7172 if (isFunc) fullFuncDecl+=argListToString(root->argList,true);
7173 warn(root->fileName,root->startLine,
7174 "Cannot determine class for function\n{}",
7175 fullFuncDecl
7176 );
7177 }
7178 }
7179 }
7180 else if (isRelated && !relates.empty())
7181 {
7182 AUTO_TRACE_ADD("related function scopeName='{}' className='{}'",scopeName,className);
7183 if (className.empty()) className=relates;
7184 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
7185 if ((cd=getClassMutable(scopeName)))
7186 {
7187 bool newMember=true; // assume we have a new member
7188 MemberDefMutable *mdDefine=nullptr;
7189 {
7190 mn = Doxygen::functionNameLinkedMap->find(funcName);
7191 if (mn)
7192 {
7193 for (const auto &imd : *mn)
7194 {
7195 MemberDefMutable *md = toMemberDefMutable(imd.get());
7196 if (md && md->isDefine())
7197 {
7198 mdDefine = md;
7199 break;
7200 }
7201 }
7202 }
7203 }
7204
7205 if (mdDefine) // macro definition is already created by the preprocessor and inserted as a file member
7206 {
7207 //printf("moving #define %s into class %s\n",qPrint(mdDefine->name()),qPrint(cd->name()));
7208
7209 // take mdDefine from the Doxygen::functionNameLinkedMap (without deleting the data)
7210 auto mdDefineTaken = Doxygen::functionNameLinkedMap->take(funcName,mdDefine);
7211 // insert it as a class member
7212 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==nullptr)
7213 {
7214 mn=Doxygen::memberNameLinkedMap->add(funcName);
7215 }
7216
7217 if (mdDefine->getFileDef())
7218 {
7219 mdDefine->getFileDef()->removeMember(mdDefine);
7220 }
7221 mdDefine->makeRelated();
7222 mdDefine->setMemberClass(cd);
7223 mdDefine->moveTo(cd);
7224 cd->insertMember(mdDefine);
7225 // also insert the member as an alias in the parent's scope, so it can be referenced also without cd's scope
7226 insertMemberAlias(cd->getOuterScope(),mdDefine);
7227 mn->push_back(std::move(mdDefineTaken));
7228 }
7229 else // normal member, needs to be created and added to the class
7230 {
7231 FileDef *fd=root->fileDef();
7232
7233 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==nullptr)
7234 {
7235 mn=Doxygen::memberNameLinkedMap->add(funcName);
7236 }
7237 else
7238 {
7239 // see if we got another member with matching arguments
7240 MemberDefMutable *rmd_found = nullptr;
7241 for (const auto &irmd : *mn)
7242 {
7243 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
7244 if (rmd)
7245 {
7246 const ArgumentList &rmdAl = rmd->argumentList();
7247
7248 newMember=
7249 className!=rmd->getOuterScope()->name() ||
7250 !matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmdAl,
7251 cd,fd,root->type,&root->argList,
7252 true,root->lang);
7253 if (!newMember)
7254 {
7255 rmd_found = rmd;
7256 }
7257 }
7258 }
7259 if (rmd_found) // member already exists as rmd -> add docs
7260 {
7261 AUTO_TRACE_ADD("addMemberDocs for related member {}",root->name);
7262 addMemberDocs(root,rmd_found,funcDecl,nullptr,overloaded,spec);
7263 newMember=false;
7264 }
7265 }
7266
7267 if (newMember) // need to create a new member
7268 {
7269 MemberType mtype = MemberType::Function;
7270 switch (root->mtype)
7271 {
7272 case MethodTypes::Method: mtype = MemberType::Function; break;
7273 case MethodTypes::Signal: mtype = MemberType::Signal; break;
7274 case MethodTypes::Slot: mtype = MemberType::Slot; break;
7275 case MethodTypes::DCOP: mtype = MemberType::DCOP; break;
7276 case MethodTypes::Property: mtype = MemberType::Property; break;
7277 case MethodTypes::Event: mtype = MemberType::Event; break;
7278 }
7279
7280 //printf("New related name '%s' '%d'\n",qPrint(funcName),
7281 // root->argList ? (int)root->argList->count() : -1);
7282
7283 // first note that we pass:
7284 // (root->tArgLists ? root->tArgLists->last() : nullptr)
7285 // for the template arguments for the new "member."
7286 // this accurately reflects the template arguments of
7287 // the related function, which don't have to do with
7288 // those of the related class.
7289 auto md = createMemberDef(
7290 root->fileName,root->startLine,root->startColumn,
7291 funcType,funcName,funcArgs,exceptions,
7292 root->protection,root->virt,
7293 root->isStatic,
7294 isMemberOf ? Relationship::Foreign : Relationship::Related,
7295 mtype,
7296 (!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList()),
7297 funcArgs.empty() ? ArgumentList() : root->argList,
7298 root->metaData);
7299 auto mmd = toMemberDefMutable(md.get());
7300
7301 // also insert the member as an alias in the parent's scope, so it can be referenced also without cd's scope
7302 insertMemberAlias(cd->getOuterScope(),md.get());
7303
7304 // we still have the problem that
7305 // MemberDef::writeDocumentation() in memberdef.cpp
7306 // writes the template argument list for the class,
7307 // as if this member is a member of the class.
7308 // fortunately, MemberDef::writeDocumentation() has
7309 // a special mechanism that allows us to totally
7310 // override the set of template argument lists that
7311 // are printed. We use that and set it to the
7312 // template argument lists of the related function.
7313 //
7314 mmd->setDefinitionTemplateParameterLists(root->tArgLists);
7315
7316 mmd->setTagInfo(root->tagInfo());
7317
7318 //printf("Related member name='%s' decl='%s' bodyLine='%d'\n",
7319 // qPrint(funcName),qPrint(funcDecl),root->bodyLine);
7320
7321 // try to find the matching line number of the body from the
7322 // global function list
7323 bool found=false;
7324 if (root->bodyLine==-1)
7325 {
7327 if (rmn)
7328 {
7329 const MemberDefMutable *rmd_found=nullptr;
7330 for (const auto &irmd : *rmn)
7331 {
7332 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
7333 if (rmd)
7334 {
7335 const ArgumentList &rmdAl = rmd->argumentList();
7336 // check for matching argument lists
7337 if (
7338 matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmdAl,
7339 cd,fd,root->type,&root->argList,
7340 true,root->lang)
7341 )
7342 {
7343 found=true;
7344 rmd_found = rmd;
7345 break;
7346 }
7347 }
7348 }
7349 if (rmd_found) // member found -> copy line number info
7350 {
7351 mmd->setBodySegment(rmd_found->getDefLine(),rmd_found->getStartBodyLine(),rmd_found->getEndBodyLine());
7352 mmd->setBodyDef(rmd_found->getBodyDef());
7353 //md->setBodyMember(rmd);
7354 }
7355 }
7356 }
7357 if (!found) // line number could not be found or is available in this
7358 // entry
7359 {
7360 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7361 mmd->setBodyDef(fd);
7362 }
7363
7364 //if (root->mGrpId!=-1)
7365 //{
7366 // md->setMemberGroup(memberGroupDict[root->mGrpId]);
7367 //}
7368 mmd->setMemberClass(cd);
7369 mmd->setMemberSpecifiers(spec);
7370 mmd->setVhdlSpecifiers(root->vhdlSpec);
7371 mmd->setDefinition(funcDecl);
7373 mmd->addQualifiers(root->qualifiers);
7374 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7375 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7376 mmd->setDocsForDefinition(!root->proto);
7377 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
7378 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7379 mmd->addSectionsToDefinition(root->anchors);
7380 mmd->setMemberGroupId(root->mGrpId);
7381 mmd->setLanguage(root->lang);
7382 mmd->setId(root->id);
7383 //md->setMemberDefTemplateArguments(root->mtArgList);
7384 cd->insertMember(md.get());
7385 cd->insertUsedFile(fd);
7386 mmd->setRefItems(root->sli);
7387 mmd->setRequirementReferences(root->rqli);
7388 if (root->relatesType==RelatesType::Duplicate) mmd->setRelatedAlso(cd);
7389 addMemberToGroups(root,md.get());
7391 //printf("Adding member=%s\n",qPrint(md->name()));
7392 mn->push_back(std::move(md));
7393 }
7394 if (root->relatesType==RelatesType::Duplicate)
7395 {
7396 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
7397 {
7398 DString fullFuncDecl=funcDecl;
7399 if (isFunc) fullFuncDecl+=argListToString(root->argList,true);
7400 warn(root->fileName,root->startLine,
7401 "Cannot determine file/namespace for relatedalso function\n{}",
7402 fullFuncDecl
7403 );
7404 }
7405 }
7406 }
7407 }
7408 else
7409 {
7410 warn_undoc(root->fileName,root->startLine, "class '{}' for related function '{}' is not documented.", className,funcName);
7411 }
7412 }
7413 else if (root->parent() && root->parent()->section.isObjcImpl())
7414 {
7415 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
7416 }
7417 else // unrelated not overloaded member found
7418 {
7419 bool globMem = findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec);
7420 if (className.empty() && !globMem)
7421 {
7422 warn(root->fileName,root->startLine, "class for member '{}' cannot be found.", funcName);
7423 }
7424 else if (!className.empty() && !globMem)
7425 {
7426 warn(root->fileName,root->startLine,
7427 "member '{}' of class '{}' cannot be found",
7428 funcName,className);
7429 }
7430 }
7431 }
7432 else
7433 {
7434 // this should not be called
7435 warn(root->fileName,root->startLine,"member with no name found.");
7436 }
7437 return;
7438}
7439
7440//----------------------------------------------------------------------
7441// find the members corresponding to the different documentation blocks
7442// that are extracted from the sources.
7443
7444static void filterMemberDocumentation(const Entry *root,const DString &relates)
7445{
7446 AUTO_TRACE("root->type='{}' root->inside='{}' root->name='{}' root->args='{}' section={} root->spec={} root->mGrpId={}",
7447 root->type,root->inside,root->name,root->args,root->section,root->spec,root->mGrpId);
7448 //printf("root->parent()->name=%s\n",qPrint(root->parent()->name));
7449 bool isFunc=true;
7450
7451 DString type = root->type;
7452 DString args = root->args;
7453 int i=-1, l=0;
7454 if ( // detect func variable/typedef to func ptr
7455 (i=findFunctionPtr(type.str(),root->lang,&l))!=-1
7456 )
7457 {
7458 //printf("Fixing function pointer!\n");
7459 // fix type and argument
7460 args.prepend(type.mid(i+l));
7461 type=type.left(i+l);
7462 //printf("Results type=%s,name=%s,args=%s\n",qPrint(type),qPrint(root->name),qPrint(args));
7463 isFunc=false;
7464 }
7465 else if ((type.startsWith("typedef ") && args.find('(')!=DString::npos))
7466 // detect function types marked as functions
7467 {
7468 isFunc=false;
7469 }
7470
7471 //printf("Member %s isFunc=%d\n",qPrint(root->name),isFunc);
7472 if (root->section.isMemberDoc())
7473 {
7474 //printf("Documentation for inline member '%s' found args='%s'\n",
7475 // qPrint(root->name),qPrint(args));
7476 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
7477 if (type.empty())
7478 {
7479 findMember(root,
7480 relates,
7481 type,
7482 args,
7483 root->name + args + root->exception,
7484 false,
7485 isFunc);
7486 }
7487 else
7488 {
7489 findMember(root,
7490 relates,
7491 type,
7492 args,
7493 type + " " + root->name + args + root->exception,
7494 false,
7495 isFunc);
7496 }
7497 }
7498 else if (root->section.isOverloadDoc())
7499 {
7500 //printf("Overloaded member %s found\n",qPrint(root->name));
7501 findMember(root,
7502 relates,
7503 type,
7504 args,
7505 root->name,
7506 true,
7507 isFunc);
7508 }
7509 else if
7510 ((root->section.isFunction() // function
7511 ||
7512 (root->section.isVariable() && // variable
7513 !type.empty() && // with a type
7514 g_compoundKeywords.find(type.str())==g_compoundKeywords.end() // that is not a keyword
7515 // (to skip forward declaration of class etc.)
7516 )
7517 )
7518 )
7519 {
7520 //printf("Documentation for member '%s' found args='%s' excp='%s'\n",
7521 // qPrint(root->name),qPrint(args),qPrint(root->exception));
7522 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
7523 //printf("Inside=%s\n Relates=%s\n",qPrint(root->inside),qPrint(relates));
7524 if (isTypeAClassFriend(type))
7525 {
7526 findMember(root,
7527 relates,
7528 type,
7529 args,
7530 type+" "+root->name,
7531 false,false);
7532
7533 }
7534 else if (!type.empty())
7535 {
7536 findMember(root,
7537 relates,
7538 type,
7539 args,
7540 type+" "+ root->inside + root->name + args + root->exception,
7541 false,isFunc);
7542 }
7543 else
7544 {
7545 findMember(root,
7546 relates,
7547 type,
7548 args,
7549 root->inside + root->name + args + root->exception,
7550 false,isFunc);
7551 }
7552 }
7553 else if (root->section.isDefine() && !relates.empty())
7554 {
7555 findMember(root,
7556 relates,
7557 type,
7558 args,
7559 root->name + args,
7560 false,
7561 !args.empty());
7562 }
7563 else if (root->section.isVariableDoc())
7564 {
7565 //printf("Documentation for variable %s found\n",qPrint(root->name));
7566 //if (!relates.empty()) printf(" Relates %s\n",qPrint(relates));
7567 findMember(root,
7568 relates,
7569 type,
7570 args,
7571 root->name,
7572 false,
7573 false);
7574 }
7575 else if (root->section.isExportedInterface() ||
7576 root->section.isIncludedService())
7577 {
7578 findMember(root,
7579 relates,
7580 type,
7581 args,
7582 type + " " + root->name,
7583 false,
7584 false);
7585 }
7586 else
7587 {
7588 // skip section
7589 //printf("skip section\n");
7590 }
7591}
7592
7593static void findMemberDocumentation(const Entry *root)
7594{
7595 if (root->section.isMemberDoc() ||
7596 root->section.isOverloadDoc() ||
7597 root->section.isFunction() ||
7598 root->section.isVariable() ||
7599 root->section.isVariableDoc() ||
7600 root->section.isDefine() ||
7601 root->section.isIncludedService() ||
7602 root->section.isExportedInterface()
7603 )
7604 {
7605 AUTO_TRACE();
7606 if (root->relatesType==RelatesType::Duplicate && !root->relates.empty())
7607 {
7609 }
7611 }
7612 for (const auto &e : root->children())
7613 {
7614 if (!e->section.isEnum())
7615 {
7616 findMemberDocumentation(e.get());
7617 }
7618 }
7619}
7620
7621//----------------------------------------------------------------------
7622
7623static void findObjCMethodDefinitions(const Entry *root)
7624{
7625 AUTO_TRACE();
7626 for (const auto &objCImpl : root->children())
7627 {
7628 if (objCImpl->section.isObjcImpl())
7629 {
7630 for (const auto &objCMethod : objCImpl->children())
7631 {
7632 if (objCMethod->section.isFunction())
7633 {
7634 //printf(" Found ObjC method definition %s\n",qPrint(objCMethod->name));
7635 findMember(objCMethod.get(),
7636 objCMethod->relates,
7637 objCMethod->type,
7638 objCMethod->args,
7639 objCMethod->type+" "+objCImpl->name+"::"+objCMethod->name+" "+objCMethod->args,
7640 false,true);
7641 objCMethod->section=EntryType::makeEmpty();
7642 }
7643 }
7644 }
7645 }
7646}
7647
7648//----------------------------------------------------------------------
7649// find and add the enumeration to their classes, namespaces or files
7650
7651static void findEnums(const Entry *root)
7652{
7653 if (root->section.isEnum())
7654 {
7655 AUTO_TRACE("name={}",root->name);
7656 ClassDefMutable *cd = nullptr;
7657 FileDef *fd = nullptr;
7658 NamespaceDefMutable *nd = nullptr;
7659 MemberNameLinkedMap *mnsd = nullptr;
7660 bool isGlobal = false;
7661 bool isRelated = false;
7662 bool isMemberOf = false;
7663 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7664
7665 DString name;
7666 DString scope;
7667
7668 if (size_t i = root->name.rfind("::"); i!=DString::npos) // scope is specified
7669 {
7670 scope=root->name.left(i); // extract scope
7671 if (root->lang==SrcLangExt::CSharp)
7672 {
7673 scope = mangleCSharpGenericName(scope);
7674 }
7675 name=root->name.right(root->name.length()-i-2); // extract name
7676 if ((cd=getClassMutable(scope))==nullptr)
7677 {
7679 }
7680 }
7681 else // no scope, check the scope in which the docs where found
7682 {
7683 if (root->parent()->section.isScope() && !root->parent()->name.empty()) // found enum docs inside a compound
7684 {
7685 scope=root->parent()->name;
7686 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7687 }
7688 name=root->name;
7689 }
7690
7691 if (!root->relates.empty())
7692 { // related member, prefix user specified scope
7693 isRelated=true;
7694 isMemberOf=(root->relatesType==RelatesType::MemberOf);
7695 if (getClass(root->relates)==nullptr && !scope.empty())
7696 scope=mergeScopes(scope,root->relates);
7697 else
7698 scope=root->relates;
7699 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7700 }
7701
7702 if (cd && !name.empty()) // found a enum inside a compound
7703 {
7704 //printf("Enum '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7705 fd=nullptr;
7707 isGlobal=false;
7708 }
7709 else if (nd) // found enum inside namespace
7710 {
7712 isGlobal=true;
7713 }
7714 else // found a global enum
7715 {
7716 fd=root->fileDef();
7718 isGlobal=true;
7719 }
7720
7721 if (!name.empty())
7722 {
7723 // new enum type
7724 AUTO_TRACE_ADD("new enum {} at line {} of {}",name,root->bodyLine,root->fileName);
7725 auto md = createMemberDef(
7726 root->fileName,root->startLine,root->startColumn,
7727 DString(),name,DString(),DString(),
7728 root->protection,Specifier::Normal,false,
7729 isMemberOf ? Relationship::Foreign : isRelated ? Relationship::Related : Relationship::Member,
7730 MemberType::Enumeration,
7732 auto mmd = toMemberDefMutable(md.get());
7733 mmd->setTagInfo(root->tagInfo());
7734 mmd->setLanguage(root->lang);
7735 mmd->setId(root->id);
7736 if (!isGlobal) mmd->setMemberClass(cd); else mmd->setFileDef(fd);
7737 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7738 mmd->setBodyDef(root->fileDef());
7739 mmd->setMemberSpecifiers(root->spec);
7740 mmd->setVhdlSpecifiers(root->vhdlSpec);
7741 mmd->setEnumBaseType(root->args);
7742 //printf("Enum %s definition at line %d of %s: protection=%d scope=%s\n",
7743 // qPrint(root->name),root->bodyLine,qPrint(root->fileName),root->protection,cd?qPrint(cd->name()):"<none>");
7744 mmd->addSectionsToDefinition(root->anchors);
7745 mmd->setMemberGroupId(root->mGrpId);
7747 mmd->addQualifiers(root->qualifiers);
7748 //printf("%s::setRefItems(%zu)\n",qPrint(md->name()),root->sli.size());
7749 mmd->setRefItems(root->sli);
7750 mmd->setRequirementReferences(root->rqli);
7751 //printf("found enum %s nd=%p\n",qPrint(md->name()),nd);
7752 bool defSet=false;
7753
7754 DString baseType = root->args;
7755 if (!baseType.empty())
7756 {
7757 baseType.prepend(" : ");
7758 }
7759
7760 if (nd)
7761 {
7762 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python)
7763 {
7764 mmd->setDefinition(name+baseType);
7765 }
7766 else
7767 {
7768 mmd->setDefinition(nd->name()+"::"+name+baseType);
7769 }
7770 //printf("definition=%s\n",md->definition());
7771 defSet=true;
7772 mmd->setNamespace(nd);
7773 nd->insertMember(md.get());
7774 }
7775
7776 // even if we have already added the enum to a namespace, we still
7777 // also want to add it to other appropriate places such as file
7778 // or class.
7779 if (isGlobal && (nd==nullptr || !nd->isAnonymous()))
7780 {
7781 if (!defSet) mmd->setDefinition(name+baseType);
7782 if (fd==nullptr && root->parent())
7783 {
7784 fd=root->parent()->fileDef();
7785 }
7786 if (fd)
7787 {
7788 mmd->setFileDef(fd);
7789 fd->insertMember(md.get());
7790 }
7791 }
7792 else if (cd)
7793 {
7794 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python)
7795 {
7796 mmd->setDefinition(name+baseType);
7797 }
7798 else
7799 {
7800 mmd->setDefinition(cd->name()+"::"+name+baseType);
7801 }
7802 cd->insertMember(md.get());
7803 cd->insertUsedFile(fd);
7804 }
7805 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7806 mmd->setDocsForDefinition(!root->proto);
7807 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7808 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7809
7810 //printf("Adding member=%s\n",qPrint(md->name()));
7811 addMemberToGroups(root,md.get());
7813
7814 MemberName *mn = mnsd->add(name);
7815 mn->push_back(std::move(md));
7816 }
7817 }
7818 else
7819 {
7820 for (const auto &e : root->children()) findEnums(e.get());
7821 }
7822}
7823
7824//----------------------------------------------------------------------
7825
7826static void addEnumValuesToEnums(const Entry *root)
7827{
7828 if (root->section.isEnum())
7829 // non anonymous enumeration
7830 {
7831 AUTO_TRACE("name={}",root->name);
7832 ClassDefMutable *cd = nullptr;
7833 FileDef *fd = nullptr;
7834 NamespaceDefMutable *nd = nullptr;
7835 MemberNameLinkedMap *mnsd = nullptr;
7836 bool isGlobal = false;
7837 bool isRelated = false;
7838 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7839
7840 DString name;
7841 DString scope;
7842
7843 if (size_t i = root->name.rfind("::"); i!=DString::npos) // scope is specified
7844 {
7845 scope=root->name.left(i); // extract scope
7846 if (root->lang==SrcLangExt::CSharp)
7847 {
7848 scope = mangleCSharpGenericName(scope);
7849 }
7850 name=root->name.right(root->name.length()-i-2); // extract name
7851 if ((cd=getClassMutable(scope))==nullptr)
7852 {
7854 }
7855 }
7856 else // no scope, check the scope in which the docs where found
7857 {
7858 if (root->parent()->section.isScope() && !root->parent()->name.empty()) // found enum docs inside a compound
7859 {
7860 scope=root->parent()->name;
7861 if (root->lang==SrcLangExt::CSharp)
7862 {
7863 scope = mangleCSharpGenericName(scope);
7864 }
7865 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7866 }
7867 name=root->name;
7868 }
7869
7870 if (!root->relates.empty())
7871 { // related member, prefix user specified scope
7872 isRelated=true;
7873 if (getClassMutable(root->relates)==nullptr && !scope.empty())
7874 scope=mergeScopes(scope,root->relates);
7875 else
7876 scope=root->relates;
7877 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7878 }
7879
7880 if (cd && !name.empty()) // found a enum inside a compound
7881 {
7882 //printf("Enum in class '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7883 fd=nullptr;
7885 isGlobal=false;
7886 }
7887 else if (nd && !nd->isAnonymous()) // found enum inside namespace
7888 {
7889 //printf("Enum in namespace '%s'::'%s'\n",qPrint(nd->name()),qPrint(name));
7891 isGlobal=true;
7892 }
7893 else // found a global enum
7894 {
7895 fd=root->fileDef();
7896 //printf("Enum in file '%s': '%s'\n",qPrint(fd->name()),qPrint(name));
7898 isGlobal=true;
7899 }
7900
7901 if (!name.empty())
7902 {
7903 //printf("** name=%s\n",qPrint(name));
7904 MemberName *mn = mnsd->find(name); // for all members with this name
7905 if (mn)
7906 {
7907 struct EnumValueInfo
7908 {
7909 EnumValueInfo(const DString &n,std::unique_ptr<MemberDef> &&md) :
7910 name(n), member(std::move(md)) {}
7911 DString name;
7912 std::unique_ptr<MemberDef> member;
7913 };
7914 std::vector< EnumValueInfo > extraMembers;
7915 // for each enum in this list
7916 for (const auto &imd : *mn)
7917 {
7918 MemberDefMutable *md = toMemberDefMutable(imd.get());
7919 // use raw pointer in this loop, since we modify mn and can then invalidate mdp.
7920 if (md && md->isEnumerate() && !root->children().empty())
7921 {
7922 AUTO_TRACE_ADD("enum {} with {} children",md->name(),root->children().size());
7923 for (const auto &e : root->children())
7924 {
7925 SrcLangExt sle = root->lang;
7926 bool isJavaLike = sle==SrcLangExt::CSharp || sle==SrcLangExt::Java || sle==SrcLangExt::XML;
7927 if ( isJavaLike || root->spec.isStrong())
7928 {
7929 if (sle == SrcLangExt::Cpp && e->section.isDefine()) continue;
7930 // Unlike classic C/C++ enums, for C++11, C# & Java enum
7931 // values are only visible inside the enum scope, so we must create
7932 // them here and only add them to the enum
7933 //printf("md->qualifiedName()=%s e->name=%s tagInfo=%p name=%s\n",
7934 // qPrint(md->qualifiedName()),qPrint(e->name),(void*)e->tagInfo(),qPrint(e->name));
7935 DString qualifiedName = root->name;
7936 if (size_t i = qualifiedName.rfind("::"); i!=DString::npos && sle==SrcLangExt::CSharp)
7937 {
7938 qualifiedName = mangleCSharpGenericName(qualifiedName.left(i))+qualifiedName.mid(i);
7939 }
7940 if (isJavaLike)
7941 {
7942 qualifiedName=substitute(qualifiedName,"::",".");
7943 }
7944 if (md->qualifiedName()==qualifiedName) // enum value scope matches that of the enum
7945 {
7946 DString fileName = e->fileName;
7947 if (fileName.empty() && e->tagInfo())
7948 {
7949 fileName = e->tagInfo()->tagName;
7950 }
7951 AUTO_TRACE_ADD("strong enum value {}",e->name);
7952 auto fmd = createMemberDef(
7953 fileName,e->startLine,e->startColumn,
7954 e->type,e->name,e->args,DString(),
7955 e->protection, Specifier::Normal,e->isStatic,Relationship::Member,
7956 MemberType::EnumValue,ArgumentList(),ArgumentList(),e->metaData);
7957 auto fmmd = toMemberDefMutable(fmd.get());
7958 NamespaceDef *mnd = md->getNamespaceDef();
7959 if (md->getClassDef())
7960 fmmd->setMemberClass(md->getClassDef());
7961 else if (mnd && (mnd->isLinkable() || mnd->isAnonymous()))
7962 fmmd->setNamespace(mnd);
7963 else if (md->getFileDef())
7964 fmmd->setFileDef(md->getFileDef());
7965 fmmd->setOuterScope(md->getOuterScope());
7966 fmmd->setTagInfo(e->tagInfo());
7967 fmmd->setLanguage(e->lang);
7968 fmmd->setBodySegment(e->startLine,e->bodyLine,e->endBodyLine);
7969 fmmd->setBodyDef(e->fileDef());
7970 fmmd->setId(e->id);
7971 fmmd->setDocumentation(e->doc,e->docFile,e->docLine);
7972 fmmd->setBriefDescription(e->brief,e->briefFile,e->briefLine);
7973 fmmd->addSectionsToDefinition(e->anchors);
7974 fmmd->setInitializer(e->initializer.str());
7975 fmmd->setMaxInitLines(e->initLines);
7976 fmmd->setMemberGroupId(e->mGrpId);
7977 fmmd->setExplicitExternal(e->explicitExternal,fileName,e->startLine,e->startColumn);
7978 fmmd->setRefItems(e->sli);
7979 fmmd->setRequirementReferences(e->rqli);
7980 fmmd->setAnchor();
7981 md->insertEnumField(fmd.get());
7982 fmmd->setEnumScope(md,true);
7983 extraMembers.emplace_back(e->name,std::move(fmd));
7984 }
7985 }
7986 else
7987 {
7988 AUTO_TRACE_ADD("enum value {}",e->name);
7989 //printf("e->name=%s isRelated=%d\n",qPrint(e->name),isRelated);
7990 MemberName *fmn=nullptr;
7991 MemberNameLinkedMap *emnsd = isRelated ? Doxygen::functionNameLinkedMap : mnsd;
7992 if (!e->name.empty() && (fmn=emnsd->find(e->name)))
7993 // get list of members with the same name as the field
7994 {
7995 for (const auto &ifmd : *fmn)
7996 {
7997 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
7998 if (fmd && fmd->isEnumValue() && fmd->getOuterScope()==md->getOuterScope()) // in same scope
7999 {
8000 //printf("found enum value with same name %s in scope %s\n",
8001 // qPrint(fmd->name()),qPrint(fmd->getOuterScope()->name()));
8002 if (nd && !nd->isAnonymous())
8003 {
8004 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
8005 {
8006 const NamespaceDef *fnd=fmd->getNamespaceDef();
8007 if (fnd==nd) // enum value is inside a namespace
8008 {
8009 md->insertEnumField(fmd);
8010 fmd->setEnumScope(md);
8011 }
8012 }
8013 }
8014 else if (isGlobal)
8015 {
8016 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
8017 {
8018 const FileDef *ffd=fmd->getFileDef();
8019 if (ffd==fd && ffd==md->getFileDef()) // enum value has file scope
8020 {
8021 md->insertEnumField(fmd);
8022 fmd->setEnumScope(md);
8023 }
8024 }
8025 }
8026 else if (isRelated && cd) // reparent enum value to
8027 // match the enum's scope
8028 {
8029 md->insertEnumField(fmd); // add field def to list
8030 fmd->setEnumScope(md); // cross ref with enum name
8031 fmd->setEnumClassScope(cd); // cross ref with enum name
8032 fmd->setOuterScope(cd);
8033 fmd->makeRelated();
8034 cd->insertMember(fmd);
8035 }
8036 else
8037 {
8038 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
8039 {
8040 const ClassDef *fcd=fmd->getClassDef();
8041 if (fcd==cd) // enum value is inside a class
8042 {
8043 //printf("Inserting enum field %s in enum scope %s\n",
8044 // qPrint(fmd->name()),qPrint(md->name()));
8045 md->insertEnumField(fmd); // add field def to list
8046 fmd->setEnumScope(md); // cross ref with enum name
8047 }
8048 }
8049 }
8050 }
8051 }
8052 }
8053 }
8054 }
8055 }
8056 }
8057 // move the newly added members into mn
8058 for (auto &e : extraMembers)
8059 {
8060 MemberName *emn=mnsd->add(e.name);
8061 emn->push_back(std::move(e.member));
8062 }
8063 }
8064 }
8065 }
8066 else
8067 {
8068 for (const auto &e : root->children()) addEnumValuesToEnums(e.get());
8069 }
8070}
8071
8072//----------------------------------------------------------------------
8073
8074static void addEnumDocs(const Entry *root,MemberDefMutable *md)
8075{
8076 AUTO_TRACE();
8077 // documentation outside a compound overrides the documentation inside it
8078 {
8079 md->setDocumentation(root->doc,root->docFile,root->docLine);
8080 md->setDocsForDefinition(!root->proto);
8081 }
8082
8083 // brief descriptions inside a compound override the documentation
8084 // outside it
8085 {
8086 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
8087 }
8088
8089 if (md->inbodyDocumentation().empty() || !root->parent()->name.empty())
8090 {
8092 }
8093
8094 if (root->mGrpId!=-1 && md->getMemberGroupId()==-1)
8095 {
8096 md->setMemberGroupId(root->mGrpId);
8097 }
8098
8100 md->setRefItems(root->sli);
8101 md->setRequirementReferences(root->rqli);
8102
8103 const GroupDef *gd=md->getGroupDef();
8104 if (gd==nullptr && !root->groups.empty()) // member not grouped but out-of-line documentation is
8105 {
8106 addMemberToGroups(root,md);
8107 }
8109}
8110
8111//----------------------------------------------------------------------
8112// Search for the name in the associated groups. If a matching member
8113// definition exists, then add the documentation to it and return true,
8114// otherwise false.
8115
8116static bool tryAddEnumDocsToGroupMember(const Entry *root,const DString &name)
8117{
8118 for (const auto &g : root->groups)
8119 {
8120 const GroupDef *gd = Doxygen::groupLinkedMap->find(g.groupname);
8121 if (gd)
8122 {
8123 MemberList *ml = gd->getMemberList(MemberListType::DecEnumMembers());
8124 if (ml)
8125 {
8126 MemberDefMutable *md = toMemberDefMutable(ml->find(name));
8127 if (md)
8128 {
8129 addEnumDocs(root,md);
8130 return true;
8131 }
8132 }
8133 }
8134 else if (!gd && g.pri == Grouping::GROUPING_INGROUP)
8135 {
8136 warn(root->fileName, root->startLine,
8137 "Found non-existing group '{}' for the command '{}', ignoring command",
8138 g.groupname, Grouping::getGroupPriName( g.pri )
8139 );
8140 }
8141 }
8142
8143 return false;
8144}
8145
8146//----------------------------------------------------------------------
8147// find the documentation blocks for the enumerations
8148
8149static void findEnumDocumentation(const Entry *root)
8150{
8151 if (root->section.isEnumDoc() &&
8152 !root->name.empty() &&
8153 root->name.at(0)!='@' // skip anonymous enums
8154 )
8155 {
8156 DString name;
8157 DString scope;
8158 if (size_t i = root->name.rfind("::"); i!=DString::npos) // scope is specified as part of the name
8159 {
8160 name=root->name.mid(i+2); // extract name
8161 scope=root->name.left(i); // extract scope
8162 //printf("Scope='%s' Name='%s'\n",qPrint(scope),qPrint(name));
8163 }
8164 else // just the name
8165 {
8166 name=root->name;
8167 }
8168 if (root->parent()->section.isScope() && !root->parent()->name.empty()) // found enum docs inside a compound
8169 {
8170 if (!scope.empty()) scope.prepend("::");
8171 scope.prepend(root->parent()->name);
8172 }
8173 const ClassDef *cd = getClass(scope);
8175 const FileDef *fd = root->fileDef();
8176 AUTO_TRACE("Found docs for enum with name '{}' and scope '{}' in context '{}' cd='{}', nd='{}' fd='{}'",
8177 name,scope,root->parent()->name,
8178 cd ? cd->name() : DString("<none>"),
8179 nd ? nd->name() : DString("<none>"),
8180 fd ? fd->name() : DString("<none>"));
8181
8182 if (!name.empty())
8183 {
8184 bool found = tryAddEnumDocsToGroupMember(root, name);
8185 if (!found)
8186 {
8188 if (mn)
8189 {
8190 for (const auto &imd : *mn)
8191 {
8192 MemberDefMutable *md = toMemberDefMutable(imd.get());
8193 if (md && md->isEnumerate())
8194 {
8195 const ClassDef *mcd = md->getClassDef();
8196 const NamespaceDef *mnd = md->getNamespaceDef();
8197 const FileDef *mfd = md->getFileDef();
8198 if (cd && mcd==cd)
8199 {
8200 AUTO_TRACE_ADD("Match found for class scope");
8201 addEnumDocs(root,md);
8202 found = true;
8203 break;
8204 }
8205 else if (cd==nullptr && mcd==nullptr && nd!=nullptr && mnd==nd)
8206 {
8207 AUTO_TRACE_ADD("Match found for namespace scope");
8208 addEnumDocs(root,md);
8209 found = true;
8210 break;
8211 }
8212 else if (cd==nullptr && nd==nullptr && mcd==nullptr && mnd==nullptr && fd==mfd)
8213 {
8214 AUTO_TRACE_ADD("Match found for global scope");
8215 addEnumDocs(root,md);
8216 found = true;
8217 break;
8218 }
8219 }
8220 }
8221 }
8222 }
8223 if (!found)
8224 {
8225 warn(root->fileName,root->startLine, "Documentation for undefined enum '{}' found.", name);
8226 }
8227 }
8228 }
8229 for (const auto &e : root->children()) findEnumDocumentation(e.get());
8230}
8231
8232// search for each enum (member or function) in mnl if it has documented
8233// enum values.
8234static void findDEV(const MemberNameLinkedMap &mnsd)
8235{
8236 // for each member name
8237 for (const auto &mn : mnsd)
8238 {
8239 // for each member definition
8240 for (const auto &imd : *mn)
8241 {
8242 MemberDefMutable *md = toMemberDefMutable(imd.get());
8243 if (md && md->isEnumerate()) // member is an enum
8244 {
8245 int documentedEnumValues=0;
8246 // for each enum value
8247 for (const auto &fmd : md->enumFieldList())
8248 {
8249 if (fmd->isLinkableInProject()) documentedEnumValues++;
8250 }
8251 // at least one enum value is documented
8252 if (documentedEnumValues>0) md->setDocumentedEnumValues(true);
8253 }
8254 }
8255 }
8256}
8257
8258// search for each enum (member or function) if it has documented enum
8259// values.
8265
8266//----------------------------------------------------------------------
8267
8269{
8270 auto &index = Index::instance();
8271 // for each class member name
8272 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8273 {
8274 // for each member definition
8275 for (const auto &md : *mn)
8276 {
8277 index.addClassMemberNameToIndex(md.get());
8278 if (md->getModuleDef())
8279 {
8280 index.addModuleMemberNameToIndex(md.get());
8281 }
8282 }
8283 }
8284 // for each file/namespace function name
8285 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8286 {
8287 // for each member definition
8288 for (const auto &md : *mn)
8289 {
8290 if (md->getNamespaceDef())
8291 {
8292 index.addNamespaceMemberNameToIndex(md.get());
8293 }
8294 else
8295 {
8296 index.addFileMemberNameToIndex(md.get());
8297 }
8298 if (md->getModuleDef())
8299 {
8300 index.addModuleMemberNameToIndex(md.get());
8301 }
8302 }
8303 }
8304
8305 index.sortMemberIndexLists();
8306}
8307
8308//----------------------------------------------------------------------
8309
8310static void addToIndices()
8311{
8312 for (const auto &cd : *Doxygen::classLinkedMap)
8313 {
8314 if (cd->isLinkableInProject())
8315 {
8316 Doxygen::indexList->addIndexItem(cd.get(),nullptr);
8317 if (Doxygen::searchIndex.enabled())
8318 {
8319 Doxygen::searchIndex.setCurrentDoc(cd.get(),cd->anchor(),false);
8320 Doxygen::searchIndex.addWord(cd->localName(),true);
8321 }
8322 }
8323 }
8324
8325 for (const auto &cd : *Doxygen::conceptLinkedMap)
8326 {
8327 if (cd->isLinkableInProject())
8328 {
8329 Doxygen::indexList->addIndexItem(cd.get(),nullptr);
8330 if (Doxygen::searchIndex.enabled())
8331 {
8332 Doxygen::searchIndex.setCurrentDoc(cd.get(),cd->anchor(),false);
8333 Doxygen::searchIndex.addWord(cd->localName(),true);
8334 }
8335 }
8336 }
8337
8338 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8339 {
8340 if (nd->isLinkableInProject())
8341 {
8342 Doxygen::indexList->addIndexItem(nd.get(),nullptr);
8343 if (Doxygen::searchIndex.enabled())
8344 {
8345 Doxygen::searchIndex.setCurrentDoc(nd.get(),nd->anchor(),false);
8346 Doxygen::searchIndex.addWord(nd->localName(),true);
8347 }
8348 }
8349 }
8350
8351 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8352 {
8353 for (const auto &fd : *fn)
8354 {
8355 if (Doxygen::searchIndex.enabled() && fd->isLinkableInProject())
8356 {
8357 Doxygen::searchIndex.setCurrentDoc(fd.get(),fd->anchor(),false);
8358 Doxygen::searchIndex.addWord(fd->localName(),true);
8359 }
8360 }
8361 }
8362
8363 auto addWordsForTitle = [](const Definition *d,const DString &anchor,const DString &title)
8364 {
8366 if (Doxygen::searchIndex.enabled())
8367 {
8368 Doxygen::searchIndex.setCurrentDoc(d,anchor,false);
8369 std::string s = title.str();
8370 static const reg::Ex re(R"(\a[\w-]*)");
8371 reg::Iterator it(s,re);
8373 for (; it!=end ; ++it)
8374 {
8375 const auto &match = *it;
8376 std::string matchStr = match.str();
8377 Doxygen::searchIndex.addWord(matchStr,true);
8378 }
8379 }
8380 };
8381
8382 for (const auto &gd : *Doxygen::groupLinkedMap)
8383 {
8384 if (gd->isLinkableInProject())
8385 {
8386 addWordsForTitle(gd.get(),gd->anchor(),gd->groupTitle());
8387 }
8388 }
8389
8390 for (const auto &pd : *Doxygen::pageLinkedMap)
8391 {
8392 if (pd->isLinkableInProject())
8393 {
8394 addWordsForTitle(pd.get(),pd->anchor(),pd->title());
8395 }
8396 }
8397
8399 {
8400 addWordsForTitle(Doxygen::mainPage.get(),Doxygen::mainPage->anchor(),Doxygen::mainPage->title());
8401 }
8402
8403 auto addMemberToSearchIndex = [](const MemberDef *md)
8404 {
8405 if (Doxygen::searchIndex.enabled())
8406 {
8407 Doxygen::searchIndex.setCurrentDoc(md,md->anchor(),false);
8408 DString ln=md->localName();
8409 DString qn=md->qualifiedName();
8411 if (ln!=qn)
8412 {
8414 if (md->getClassDef())
8415 {
8416 Doxygen::searchIndex.addWord(md->getClassDef()->displayName(),true);
8417 }
8418 if (md->getNamespaceDef())
8419 {
8420 Doxygen::searchIndex.addWord(md->getNamespaceDef()->displayName(),true);
8421 }
8422 }
8423 }
8424 };
8425
8426 auto getScope = [](const MemberDef *md)
8427 {
8428 const Definition *scope = nullptr;
8429 if (md->getGroupDef()) scope = md->getGroupDef();
8430 else if (md->getClassDef()) scope = md->getClassDef();
8431 else if (md->getNamespaceDef()) scope = md->getNamespaceDef();
8432 else if (md->getFileDef()) scope = md->getFileDef();
8433 return scope;
8434 };
8435
8436 auto addMemberToIndices = [addMemberToSearchIndex,getScope](const MemberDef *md)
8437 {
8438 if (md->isLinkableInProject())
8439 {
8440 if (!(md->isEnumerate() && md->isAnonymous()))
8441 {
8442 Doxygen::indexList->addIndexItem(getScope(md),md);
8443 addMemberToSearchIndex(md);
8444 }
8445 if (md->isEnumerate())
8446 {
8447 for (const auto &fmd : md->enumFieldList())
8448 {
8449 Doxygen::indexList->addIndexItem(getScope(fmd),fmd);
8450 addMemberToSearchIndex(fmd);
8451 }
8452 }
8453 }
8454 };
8455
8456 // for each class member name
8457 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8458 {
8459 // for each member definition
8460 for (const auto &md : *mn)
8461 {
8462 addMemberToIndices(md.get());
8463 }
8464 }
8465 // for each file/namespace function name
8466 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8467 {
8468 // for each member definition
8469 for (const auto &md : *mn)
8470 {
8471 addMemberToIndices(md.get());
8472 }
8473 }
8474}
8475
8476//----------------------------------------------------------------------
8477
8479{
8480 // for each member name
8481 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8482 {
8483 // for each member definition
8484 for (const auto &imd : *mn)
8485 {
8486 MemberDefMutable *md = toMemberDefMutable(imd.get());
8487 if (md)
8488 {
8490 }
8491 }
8492 }
8493 // for each member name
8494 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8495 {
8496 // for each member definition
8497 for (const auto &imd : *mn)
8498 {
8499 MemberDefMutable *md = toMemberDefMutable(imd.get());
8500 if (md)
8501 {
8503 }
8504 }
8505 }
8506}
8507
8508// recursive helper function looking for reimplements/implemented
8509// by relations between class cd and direct or indirect base class bcd
8511{
8512 for (const auto &mn : cd->memberNameInfoLinkedMap()) // for each member in class cd with a unique name
8513 {
8514 for (const auto &imd : *mn) // for each member with a given name
8515 {
8516 MemberDefMutable *md = toMemberDefMutable(imd->memberDef());
8517 if (md && (md->isFunction() || md->isCSharpProperty())) // filter on reimplementable members
8518 {
8519 ClassDef *mbcd = bcd->classDef;
8520 if (mbcd && mbcd->isLinkable()) // filter on linkable classes
8521 {
8522 const auto &bmn = mbcd->memberNameInfoLinkedMap();
8523 const auto &bmni = bmn.find(mn->memberName());
8524 if (bmni) // there are base class members with the same name
8525 {
8526 for (const auto &ibmd : *bmni) // for base class member with that name
8527 {
8528 MemberDefMutable *bmd = toMemberDefMutable(ibmd->memberDef());
8529 if (bmd) // not part of an inline namespace
8530 {
8531 auto lang = bmd->getLanguage();
8532 auto compType = mbcd->compoundType();
8533 if (bmd->virtualness()!=Specifier::Normal ||
8534 lang==SrcLangExt::Python ||
8535 lang==SrcLangExt::Java ||
8536 lang==SrcLangExt::PHP ||
8537 compType==ClassDef::Interface ||
8538 compType==ClassDef::Protocol)
8539 {
8540 const ArgumentList &bmdAl = bmd->argumentList();
8541 const ArgumentList &mdAl = md->argumentList();
8542 //printf(" Base argList='%s'\n Super argList='%s'\n",
8543 // qPrint(argListToString(bmdAl)),
8544 // qPrint(argListToString(mdAl))
8545 // );
8546 if (
8547 lang==SrcLangExt::Python ||
8548 matchArguments2(bmd->getOuterScope(),bmd->getFileDef(),bmd->typeString(),&bmdAl,
8549 md->getOuterScope(), md->getFileDef(), md->typeString(),&mdAl,
8550 true,lang
8551 )
8552 )
8553 {
8554 if (lang==SrcLangExt::Python && md->name().startsWith("__")) continue; // private members do not reimplement
8555 //printf("match!\n");
8556 const MemberDef *rmd = md->reimplements();
8557 if (rmd==nullptr) // not already assigned
8558 {
8559 //printf("%s: setting (new) reimplements member %s\n",qPrint(md->qualifiedName()),qPrint(bmd->qualifiedName()));
8560 md->setReimplements(bmd);
8561 }
8562 //printf("%s: add reimplementedBy member %s\n",qPrint(bmd->qualifiedName()),qPrint(md->qualifiedName()));
8563 bmd->insertReimplementedBy(md);
8564 }
8565 else
8566 {
8567 //printf("no match!\n");
8568 }
8569 }
8570 }
8571 }
8572 }
8573 }
8574 }
8575 }
8576 }
8577
8578 // do also for indirect base classes
8579 for (const auto &bbcd : bcd->classDef->baseClasses())
8580 {
8582 }
8583}
8584
8585//----------------------------------------------------------------------
8586// computes the relation between all members. For each member 'm'
8587// the members that override the implementation of 'm' are searched and
8588// the member that 'm' overrides is searched.
8589
8591{
8592 for (const auto &cd : *Doxygen::classLinkedMap)
8593 {
8594 if (cd->isLinkable())
8595 {
8596 for (const auto &bcd : cd->baseClasses())
8597 {
8599 }
8600 }
8601 }
8602}
8603
8604//----------------------------------------------------------------------------
8605
8607{
8608 // for each class
8609 for (const auto &cd : *Doxygen::classLinkedMap)
8610 {
8611 // that is a template
8612 for (const auto &ti : cd->getTemplateInstances())
8613 {
8614 ClassDefMutable *tcdm = toClassDefMutable(ti.classDef);
8615 if (tcdm)
8616 {
8617 tcdm->addMembersToTemplateInstance(cd.get(),cd->templateArguments(),ti.templSpec);
8618 }
8619 }
8620 }
8621}
8622
8623//----------------------------------------------------------------------------
8624
8625static void mergeCategories()
8626{
8627 AUTO_TRACE();
8628 // merge members of categories into the class they extend
8629 for (const auto &cd : *Doxygen::classLinkedMap)
8630 {
8631 if (size_t i=cd->name().find('('); i!=DString::npos) // it is an Objective-C category
8632 {
8633 DString baseName=cd->name().left(i);
8634 ClassDefMutable *baseClass=toClassDefMutable(Doxygen::classLinkedMap->find(baseName));
8635 if (baseClass)
8636 {
8637 AUTO_TRACE_ADD("merging members of category {} into {}",cd->name(),baseClass->name());
8638 baseClass->mergeCategory(cd.get());
8639 }
8640 }
8641 }
8642}
8643
8644// builds the list of all members for each class
8645
8647{
8648 // merge the member list of base classes into the inherited classes.
8649 for (const auto &cd : *Doxygen::classLinkedMap)
8650 {
8651 if (// !cd->isReference() && // not an external class
8652 cd->subClasses().empty() && // is a root of the hierarchy
8653 !cd->baseClasses().empty()) // and has at least one base class
8654 {
8655 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8656 if (cdm)
8657 {
8658 //printf("*** merging members for %s\n",qPrint(cd->name()));
8659 cdm->mergeMembers();
8660 }
8661 }
8662 }
8663 // now sort the member list of all members for all classes.
8664 for (const auto &cd : *Doxygen::classLinkedMap)
8665 {
8666 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8667 if (cdm)
8668 {
8669 cdm->sortAllMembersList();
8670 }
8671 }
8672}
8673
8674//----------------------------------------------------------------------------
8675
8677{
8678 auto processSourceFile = [](FileDef *fd,OutputList &ol,ClangTUParser *parser)
8679 {
8680 bool showSources = fd->generateSourceFile() && !Htags::useHtags; // sources need to be shown in the output
8681 bool parseSources = !fd->isReference() && Doxygen::parseSourcesNeeded; // we needed to parse the sources even if we do not show them
8682 if (showSources)
8683 {
8684 msg("Generating code for file {}...\n",fd->docName());
8685 fd->writeSourceHeader(ol);
8686 fd->writeSourceBody(ol,parser);
8687 fd->writeSourceFooter(ol);
8688 }
8689 else if (parseSources)
8690 {
8691 msg("Parsing code for file {}...\n",fd->docName());
8692 fd->parseSource(parser);
8693 }
8694 };
8695 if (!Doxygen::inputNameLinkedMap->empty())
8696 {
8697#if USE_LIBCLANG
8699 {
8700 StringUnorderedSet processedFiles;
8701
8702 // create a dictionary with files to process
8703 StringUnorderedSet filesToProcess;
8704
8705 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8706 {
8707 for (const auto &fd : *fn)
8708 {
8709 filesToProcess.insert(fd->absFilePath().str());
8710 }
8711 }
8712 // process source files (and their include dependencies)
8713 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8714 {
8715 for (const auto &fd : *fn)
8716 {
8717 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt::Cpp &&
8718 (fd->generateSourceFile() ||
8720 )
8721 )
8722 {
8723 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8724 clangParser->parse();
8725 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8726
8727 for (auto incFile : clangParser->filesInSameTU())
8728 {
8729 if (filesToProcess.find(incFile)!=filesToProcess.end() && // part of input
8730 fd->absFilePath()!=incFile && // not same file
8731 processedFiles.find(incFile)==processedFiles.end()) // not yet marked as processed
8732 {
8733 StringVector moreFiles;
8734 bool ambig = false;
8735 FileDef *ifd=Doxygen::inputNameLinkedMap->findFileDef(incFile,ambig);
8736 if (ifd && !ifd->isReference())
8737 {
8738 processSourceFile(ifd,*g_outputList,clangParser.get());
8739 processedFiles.insert(incFile);
8740 }
8741 }
8742 }
8743 processedFiles.insert(fd->absFilePath().str());
8744 }
8745 }
8746 }
8747 // process remaining files
8748 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8749 {
8750 for (const auto &fd : *fn)
8751 {
8752 if (processedFiles.find(fd->absFilePath().str())==processedFiles.end()) // not yet processed
8753 {
8754 if (fd->getLanguage()==SrcLangExt::Cpp) // C/C++ file, use clang parser
8755 {
8756 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8757 clangParser->parse();
8758 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8759 }
8760 else // non C/C++ file, use built-in parser
8761 {
8762 processSourceFile(fd.get(),*g_outputList,nullptr);
8763 }
8764 }
8765 }
8766 }
8767 }
8768 else
8769#endif
8770 {
8771 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8772 if (numThreads>1)
8773 {
8774 msg("Generating code files using {} threads.\n",numThreads);
8775 struct SourceContext
8776 {
8777 SourceContext(FileDef *fd_,bool gen_,const OutputList &ol_)
8778 : fd(fd_), generateSourceFile(gen_), ol(ol_) {}
8779 FileDef *fd;
8780 bool generateSourceFile;
8781 OutputList ol;
8782 };
8783 ThreadPool threadPool(numThreads);
8784 std::vector< std::future< std::shared_ptr<SourceContext> > > results;
8785 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8786 {
8787 for (const auto &fd : *fn)
8788 {
8789 bool generateSourceFile = fd->generateSourceFile() && !Htags::useHtags;
8790 auto ctx = std::make_shared<SourceContext>(fd.get(),generateSourceFile,*g_outputList);
8791 auto processFile = [ctx]()
8792 {
8793 if (ctx->generateSourceFile)
8794 {
8795 msg("Generating code for file {}...\n",ctx->fd->docName());
8796 }
8797 else
8798 {
8799 msg("Parsing code for file {}...\n",ctx->fd->docName());
8800 }
8801 StringVector filesInSameTu;
8802 ctx->fd->getAllIncludeFilesRecursively(filesInSameTu);
8803 if (ctx->generateSourceFile) // sources need to be shown in the output
8804 {
8805 ctx->fd->writeSourceHeader(ctx->ol);
8806 ctx->fd->writeSourceBody(ctx->ol,nullptr);
8807 ctx->fd->writeSourceFooter(ctx->ol);
8808 }
8809 else if (!ctx->fd->isReference() && Doxygen::parseSourcesNeeded)
8810 // we needed to parse the sources even if we do not show them
8811 {
8812 ctx->fd->parseSource(nullptr);
8813 }
8814 return ctx;
8815 };
8816 results.emplace_back(threadPool.queue(processFile));
8817 }
8818 }
8819 for (auto &f : results)
8820 {
8821 auto ctx = f.get();
8822 }
8823 }
8824 else // single threaded version
8825 {
8826 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8827 {
8828 for (const auto &fd : *fn)
8829 {
8830 StringVector filesInSameTu;
8831 fd->getAllIncludeFilesRecursively(filesInSameTu);
8832 processSourceFile(fd.get(),*g_outputList,nullptr);
8833 }
8834 }
8835 }
8836 }
8837 }
8838}
8839
8840//----------------------------------------------------------------------------
8841
8842static void generateFileDocs()
8843{
8844 if (Index::instance().numDocumentedFiles()==0) return;
8845
8846 if (!Doxygen::inputNameLinkedMap->empty())
8847 {
8848 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8849 if (numThreads>1) // multi threaded processing
8850 {
8851 struct DocContext
8852 {
8853 DocContext(FileDef *fd_,const OutputList &ol_)
8854 : fd(fd_), ol(ol_) {}
8855 FileDef *fd;
8856 OutputList ol;
8857 };
8858 ThreadPool threadPool(numThreads);
8859 std::vector< std::future< std::shared_ptr<DocContext> > > results;
8860 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8861 {
8862 for (const auto &fd : *fn)
8863 {
8864 bool doc = fd->isLinkableInProject();
8865 if (doc)
8866 {
8867 auto ctx = std::make_shared<DocContext>(fd.get(),*g_outputList);
8868 auto processFile = [ctx]() {
8869 msg("Generating docs for file {}...\n",ctx->fd->docName());
8870 ctx->fd->writeDocumentation(ctx->ol);
8871 return ctx;
8872 };
8873 results.emplace_back(threadPool.queue(processFile));
8874 }
8875 }
8876 }
8877 for (auto &f : results)
8878 {
8879 auto ctx = f.get();
8880 }
8881 }
8882 else // single threaded processing
8883 {
8884 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8885 {
8886 for (const auto &fd : *fn)
8887 {
8888 bool doc = fd->isLinkableInProject();
8889 if (doc)
8890 {
8891 msg("Generating docs for file {}...\n",fd->docName());
8892 fd->writeDocumentation(*g_outputList);
8893 }
8894 }
8895 }
8896 }
8897 }
8898}
8899
8900//----------------------------------------------------------------------------
8901
8903{
8904 // add source references for class definitions
8905 for (const auto &cd : *Doxygen::classLinkedMap)
8906 {
8907 const FileDef *fd=cd->getBodyDef();
8908 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8909 {
8910 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),nullptr);
8911 }
8912 }
8913 // add source references for concept definitions
8914 for (const auto &cd : *Doxygen::conceptLinkedMap)
8915 {
8916 const FileDef *fd=cd->getBodyDef();
8917 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8918 {
8919 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),nullptr);
8920 }
8921 }
8922 // add source references for namespace definitions
8923 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8924 {
8925 const FileDef *fd=nd->getBodyDef();
8926 if (fd && nd->isLinkableInProject() && nd->getStartDefLine()!=-1)
8927 {
8928 const_cast<FileDef*>(fd)->addSourceRef(nd->getStartDefLine(),nd.get(),nullptr);
8929 }
8930 }
8931
8932 // add source references for member names
8933 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8934 {
8935 for (const auto &md : *mn)
8936 {
8937 //printf("class member %s: def=%s body=%d link?=%d\n",
8938 // qPrint(md->name()),
8939 // md->getBodyDef()?qPrint(md->getBodyDef()->name()):"<none>",
8940 // md->getStartBodyLine(),md->isLinkableInProject());
8941 const FileDef *fd=md->getBodyDef();
8942 if (fd &&
8943 md->getStartDefLine()!=-1 &&
8944 md->isLinkableInProject() &&
8946 )
8947 {
8948 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8949 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8950 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8951 }
8952 }
8953 }
8954 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8955 {
8956 for (const auto &md : *mn)
8957 {
8958 const FileDef *fd=md->getBodyDef();
8959 //printf("member %s body=[%d,%d] fd=%p link=%d parseSources=%d\n",
8960 // qPrint(md->name()),
8961 // md->getStartBodyLine(),md->getEndBodyLine(),fd,
8962 // md->isLinkableInProject(),
8963 // Doxygen::parseSourcesNeeded);
8964 if (fd &&
8965 md->getStartDefLine()!=-1 &&
8966 md->isLinkableInProject() &&
8968 )
8969 {
8970 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8971 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8972 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8973 }
8974 }
8975 }
8976}
8977
8978//----------------------------------------------------------------------------
8979
8980// add the macro definitions found during preprocessing as file members
8981static void buildDefineList()
8982{
8983 AUTO_TRACE();
8984 for (const auto &s : g_inputFiles)
8985 {
8986 auto it = Doxygen::macroDefinitions.find(s);
8988 {
8989 for (const auto &def : it->second)
8990 {
8991 auto md = createMemberDef(
8992 def.fileName,def.lineNr,def.columnNr,
8993 "#define",def.name,def.args,DString(),
8994 Protection::Public,Specifier::Normal,false,Relationship::Member,MemberType::Define,
8995 ArgumentList(),ArgumentList(),"");
8996 auto mmd = toMemberDefMutable(md.get());
8997
8998 if (!def.args.empty())
8999 {
9000 mmd->moveArgumentList(stringToArgumentList(SrcLangExt::Cpp, def.args));
9001 }
9002 mmd->setInitializer(def.definition);
9003 mmd->setFileDef(def.fileDef);
9004 mmd->setDefinition("#define "+def.name);
9005
9007 if (def.fileDef)
9008 {
9009 const MemberList *defMl = def.fileDef->getMemberList(MemberListType::DocDefineMembers());
9010 if (defMl)
9011 {
9012 const MemberDef *defMd = defMl->findRev(def.name);
9013 if (defMd) // definition already stored
9014 {
9015 mmd->setRedefineCount(defMd->redefineCount()+1);
9016 }
9017 }
9018 def.fileDef->insertMember(md.get());
9019 }
9020 AUTO_TRACE_ADD("adding macro {} with definition {}",def.name,def.definition);
9021 mn->push_back(std::move(md));
9022 }
9023 }
9024 }
9025}
9026
9027//----------------------------------------------------------------------------
9028
9029static void sortMemberLists()
9030{
9031 // sort class member lists
9032 for (const auto &cd : *Doxygen::classLinkedMap)
9033 {
9034 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9035 if (cdm)
9036 {
9037 cdm->sortMemberLists();
9038 }
9039 }
9040
9041 // sort namespace member lists
9042 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9043 {
9045 if (ndm)
9046 {
9047 ndm->sortMemberLists();
9048 }
9049 }
9050
9051 // sort file member lists
9052 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9053 {
9054 for (const auto &fd : *fn)
9055 {
9056 fd->sortMemberLists();
9057 }
9058 }
9059
9060 // sort group member lists
9061 for (const auto &gd : *Doxygen::groupLinkedMap)
9062 {
9063 gd->sortMemberLists();
9064 }
9065
9067}
9068
9069//----------------------------------------------------------------------------
9070
9071static bool isSymbolHidden(const Definition *d)
9072{
9073 bool hidden = d->isHidden();
9074 const Definition *parent = d->getOuterScope();
9075 return parent ? hidden || isSymbolHidden(parent) : hidden;
9076}
9077
9079{
9080 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9081 if (numThreads>1)
9082 {
9083 ThreadPool threadPool(numThreads);
9084 std::vector < std::future< void > > results;
9085 // queue the work
9086 for (const auto &[name,symList] : *Doxygen::symbolMap)
9087 {
9088 for (const auto &def : symList)
9089 {
9091 if (dm && !isSymbolHidden(def) && !def->isArtificial() && def->isLinkableInProject())
9092 {
9093 auto processTooltip = [dm]() {
9094 dm->computeTooltip();
9095 };
9096 results.emplace_back(threadPool.queue(processTooltip));
9097 }
9098 }
9099 }
9100 // wait for the results
9101 for (auto &f : results)
9102 {
9103 f.get();
9104 }
9105 }
9106 else
9107 {
9108 for (const auto &[name,symList] : *Doxygen::symbolMap)
9109 {
9110 for (const auto &def : symList)
9111 {
9113 if (dm && !isSymbolHidden(def) && !def->isArtificial() && def->isLinkableInProject())
9114 {
9115 dm->computeTooltip();
9116 }
9117 }
9118 }
9119 }
9120}
9121
9122//----------------------------------------------------------------------------
9123
9125{
9126 for (const auto &cd : *Doxygen::classLinkedMap)
9127 {
9128 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9129 if (cdm)
9130 {
9131 cdm->setAnonymousEnumType();
9132 }
9133 }
9134}
9135
9136//----------------------------------------------------------------------------
9137
9138static void countMembers()
9139{
9140 for (const auto &cd : *Doxygen::classLinkedMap)
9141 {
9142 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9143 if (cdm)
9144 {
9145 cdm->countMembers();
9146 }
9147 }
9148
9149 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9150 {
9152 if (ndm)
9153 {
9154 ndm->countMembers();
9155 }
9156 }
9157
9158 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9159 {
9160 for (const auto &fd : *fn)
9161 {
9162 fd->countMembers();
9163 }
9164 }
9165
9166 for (const auto &gd : *Doxygen::groupLinkedMap)
9167 {
9168 gd->countMembers();
9169 }
9170
9171 auto &mm = ModuleManager::instance();
9172 mm.countMembers();
9173}
9174
9175
9176//----------------------------------------------------------------------------
9177// generate the documentation for all classes
9178
9179static void generateDocsForClassList(const std::vector<ClassDefMutable*> &classList)
9180{
9181 AUTO_TRACE();
9182 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9183 if (numThreads>1) // multi threaded processing
9184 {
9185 struct DocContext
9186 {
9187 DocContext(ClassDefMutable *cd_,const OutputList &ol_)
9188 : cd(cd_), ol(ol_) {}
9189 ClassDefMutable *cd;
9190 OutputList ol;
9191 };
9192 ThreadPool threadPool(numThreads);
9193 std::vector< std::future< std::shared_ptr<DocContext> > > results;
9194 for (const auto &cd : classList)
9195 {
9196 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
9197 if (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9198 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
9199 )
9200 {
9201 auto ctx = std::make_shared<DocContext>(cd,*g_outputList);
9202 auto processFile = [ctx]()
9203 {
9204 msg("Generating docs for compound {}...\n",ctx->cd->displayName());
9205
9206 // skip external references, anonymous compounds and
9207 // template instances
9208 if (!ctx->cd->isHidden() && !ctx->cd->isEmbeddedInOuterScope() &&
9209 ctx->cd->isLinkableInProject() && !ctx->cd->isImplicitTemplateInstance())
9210 {
9211 ctx->cd->writeDocumentation(ctx->ol);
9212 ctx->cd->writeMemberList(ctx->ol);
9213 }
9214
9215 // even for undocumented classes, the inner classes can be documented.
9216 ctx->cd->writeDocumentationForInnerClasses(ctx->ol);
9217 return ctx;
9218 };
9219 results.emplace_back(threadPool.queue(processFile));
9220 }
9221 }
9222 for (auto &f : results)
9223 {
9224 auto ctx = f.get();
9225 }
9226 }
9227 else // single threaded processing
9228 {
9229 for (const auto &cd : classList)
9230 {
9231 //printf("cd=%s getOuterScope=%p global=%p hidden=%d embeddedInOuterScope=%d\n",
9232 // qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope,cd->isHidden(),cd->isEmbeddedInOuterScope());
9233 if (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9234 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
9235 )
9236 {
9237 // skip external references, anonymous compounds and
9238 // template instances
9239 if ( !cd->isHidden() && !cd->isEmbeddedInOuterScope() &&
9240 cd->isLinkableInProject() && !cd->isImplicitTemplateInstance())
9241 {
9242 msg("Generating docs for compound {}...\n",cd->displayName());
9243
9244 cd->writeDocumentation(*g_outputList);
9245 cd->writeMemberList(*g_outputList);
9246 }
9247 // even for undocumented classes, the inner classes can be documented.
9248 cd->writeDocumentationForInnerClasses(*g_outputList);
9249 }
9250 }
9251 }
9252}
9253
9254static void addClassAndNestedClasses(std::vector<ClassDefMutable*> &list,ClassDefMutable *cd)
9255{
9256 list.push_back(cd);
9257 for (const auto &innerCdi : cd->getClasses())
9258 {
9259 ClassDefMutable *innerCd = toClassDefMutable(innerCdi);
9260 if (innerCd)
9261 {
9262 AUTO_TRACE("innerCd={} isLinkable={} isImplicitTemplateInstance={} protectLevelVisible={} embeddedInOuterScope={}",
9263 innerCd->name(),innerCd->isLinkableInProject(),innerCd->isImplicitTemplateInstance(),protectionLevelVisible(innerCd->protection()),
9264 innerCd->isEmbeddedInOuterScope());
9265 }
9266 if (innerCd && innerCd->isLinkableInProject() && !innerCd->isImplicitTemplateInstance() &&
9267 protectionLevelVisible(innerCd->protection()) &&
9268 !innerCd->isEmbeddedInOuterScope()
9269 )
9270 {
9271 list.push_back(innerCd);
9272 addClassAndNestedClasses(list,innerCd);
9273 }
9274 }
9275}
9276
9278{
9279 std::vector<ClassDefMutable*> classList;
9280 for (const auto &cdi : *Doxygen::classLinkedMap)
9281 {
9282 ClassDefMutable *cd = toClassDefMutable(cdi.get());
9283 if (cd && (cd->getOuterScope()==nullptr ||
9285 {
9286 addClassAndNestedClasses(classList,cd);
9287 }
9288 }
9289 for (const auto &cdi : *Doxygen::hiddenClassLinkedMap)
9290 {
9291 ClassDefMutable *cd = toClassDefMutable(cdi.get());
9292 if (cd && (cd->getOuterScope()==nullptr ||
9294 {
9295 addClassAndNestedClasses(classList,cd);
9296 }
9297 }
9298 generateDocsForClassList(classList);
9299}
9300
9301//----------------------------------------------------------------------------
9302
9304{
9305 for (const auto &cdi : *Doxygen::conceptLinkedMap)
9306 {
9308
9309 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
9310 if (cd &&
9311 (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9312 cd->getOuterScope()==Doxygen::globalScope // only look at global concepts
9313 ) && !cd->isHidden() && cd->isLinkableInProject()
9314 )
9315 {
9316 msg("Generating docs for concept {}...\n",cd->displayName());
9318 }
9319 }
9320}
9321
9322//----------------------------------------------------------------------------
9323
9325{
9326 for (const auto &mn : *Doxygen::memberNameLinkedMap)
9327 {
9328 for (const auto &imd : *mn)
9329 {
9330 MemberDefMutable *md = toMemberDefMutable(imd.get());
9331 //static int count=0;
9332 //printf("%04d Member '%s'\n",count++,qPrint(md->qualifiedName()));
9333 if (md && md->documentation().empty() && md->briefDescription().empty())
9334 { // no documentation yet
9335 const MemberDef *bmd = md->reimplements();
9336 while (bmd && bmd->documentation().empty() &&
9337 bmd->briefDescription().empty()
9338 )
9339 { // search up the inheritance tree for a documentation member
9340 //printf("bmd=%s class=%s\n",qPrint(bmd->name()),qPrint(bmd->getClassDef()->name()));
9341 bmd = bmd->reimplements();
9342 }
9343 if (bmd) // copy the documentation from the reimplemented member
9344 {
9345 md->setInheritsDocsFrom(bmd);
9346 md->setDocumentation(bmd->documentation(),bmd->docFile(),bmd->docLine());
9348 md->setBriefDescription(bmd->briefDescription(),bmd->briefFile(),bmd->briefLine());
9349 md->copyArgumentNames(bmd);
9351 }
9352 }
9353 }
9354 }
9355}
9356
9357//----------------------------------------------------------------------------
9358
9360{
9361 // for each file
9362 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9363 {
9364 for (const auto &fd : *fn)
9365 {
9366 fd->combineUsingRelations();
9367 }
9368 }
9369
9370 // for each namespace
9371 NamespaceDefSet visitedNamespaces;
9372 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9373 {
9375 if (ndm)
9376 {
9377 ndm->combineUsingRelations(visitedNamespaces);
9378 }
9379 }
9380}
9381
9382//----------------------------------------------------------------------------
9383
9385{
9386 // for each class
9387 for (const auto &cd : *Doxygen::classLinkedMap)
9388 {
9389 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9390 if (cdm)
9391 {
9393 }
9394 }
9395 // for each file
9396 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9397 {
9398 for (const auto &fd : *fn)
9399 {
9400 fd->addMembersToMemberGroup();
9401 }
9402 }
9403 // for each namespace
9404 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9405 {
9407 if (ndm)
9408 {
9410 }
9411 }
9412 // for each group
9413 for (const auto &gd : *Doxygen::groupLinkedMap)
9414 {
9415 gd->addMembersToMemberGroup();
9416 }
9418}
9419
9420//----------------------------------------------------------------------------
9421
9423{
9424 // for each class
9425 for (const auto &cd : *Doxygen::classLinkedMap)
9426 {
9427 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9428 if (cdm)
9429 {
9431 }
9432 }
9433 // for each file
9434 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9435 {
9436 for (const auto &fd : *fn)
9437 {
9438 fd->distributeMemberGroupDocumentation();
9439 }
9440 }
9441 // for each namespace
9442 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9443 {
9445 if (ndm)
9446 {
9448 }
9449 }
9450 // for each group
9451 for (const auto &gd : *Doxygen::groupLinkedMap)
9452 {
9453 gd->distributeMemberGroupDocumentation();
9454 }
9456}
9457
9458//----------------------------------------------------------------------------
9459
9461{
9462 // for each class
9463 for (const auto &cd : *Doxygen::classLinkedMap)
9464 {
9465 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9466 if (cdm)
9467 {
9469 }
9470 }
9471 // for each concept
9472 for (const auto &cd : *Doxygen::conceptLinkedMap)
9473 {
9474 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
9475 if (cdm)
9476 {
9478 }
9479 }
9480 // for each file
9481 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9482 {
9483 for (const auto &fd : *fn)
9484 {
9485 fd->findSectionsInDocumentation();
9486 }
9487 }
9488 // for each namespace
9489 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9490 {
9492 if (ndm)
9493 {
9495 }
9496 }
9497 // for each group
9498 for (const auto &gd : *Doxygen::groupLinkedMap)
9499 {
9500 gd->findSectionsInDocumentation();
9501 }
9502 // for each page
9503 for (const auto &pd : *Doxygen::pageLinkedMap)
9504 {
9505 pd->findSectionsInDocumentation();
9506 }
9507 // for each directory
9508 for (const auto &dd : *Doxygen::dirLinkedMap)
9509 {
9510 dd->findSectionsInDocumentation();
9511 }
9513 if (Doxygen::mainPage) Doxygen::mainPage->findSectionsInDocumentation();
9514}
9515
9516//----------------------------------------------------------------------
9517
9518
9520{
9521 // remove all references to classes from the cache
9522 // as there can be new template instances in the inheritance path
9523 // to this class. Optimization: only remove those classes that
9524 // have inheritance instances as direct or indirect sub classes.
9526
9527 // remove all cached typedef resolutions whose target is a
9528 // template class as this may now be a template instance
9529 // for each global function name
9530 for (const auto &fn : *Doxygen::functionNameLinkedMap)
9531 {
9532 // for each function with that name
9533 for (const auto &ifmd : *fn)
9534 {
9535 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
9536 if (fmd && fmd->isTypedefValCached())
9537 {
9538 const ClassDef *cd = fmd->getCachedTypedefVal();
9539 if (cd->isTemplate()) fmd->invalidateTypedefValCache();
9540 }
9541 }
9542 }
9543 // for each class method name
9544 for (const auto &nm : *Doxygen::memberNameLinkedMap)
9545 {
9546 // for each function with that name
9547 for (const auto &imd : *nm)
9548 {
9549 MemberDefMutable *md = toMemberDefMutable(imd.get());
9550 if (md && md->isTypedefValCached())
9551 {
9552 const ClassDef *cd = md->getCachedTypedefVal();
9553 if (cd->isTemplate()) md->invalidateTypedefValCache();
9554 }
9555 }
9556 }
9557}
9558
9559//----------------------------------------------------------------------------
9560
9562{
9563 // Remove all unresolved references to classes from the cache.
9564 // This is needed before resolving the inheritance relations, since
9565 // it would otherwise not find the inheritance relation
9566 // for C in the example below, as B::I was already found to be unresolvable
9567 // (which is correct if you ignore the inheritance relation between A and B).
9568 //
9569 // class A { class I {} };
9570 // class B : public A {};
9571 // class C : public B::I {};
9573
9574 // for each class method name
9575 for (const auto &nm : *Doxygen::memberNameLinkedMap)
9576 {
9577 // for each function with that name
9578 for (const auto &imd : *nm)
9579 {
9580 MemberDefMutable *md = toMemberDefMutable(imd.get());
9581 if (md)
9582 {
9584 }
9585 }
9586 }
9587
9588}
9589
9590//----------------------------------------------------------------------------
9591// Returns true if the entry and member definition have equal file names,
9592// otherwise false.
9593
9594static bool haveEqualFileNames(const Entry *root, const MemberDef *md)
9595{
9596 if (const FileDef *fd = md->getFileDef())
9597 {
9598 return fd->absFilePath() == root->fileName;
9599 }
9600 return false;
9601}
9602
9603//----------------------------------------------------------------------------
9604
9605static void addDefineDoc(const Entry *root, MemberDefMutable *md)
9606{
9607 md->setDocumentation(root->doc,root->docFile,root->docLine);
9608 md->setDocsForDefinition(!root->proto);
9609 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9610 if (md->inbodyDocumentation().empty())
9611 {
9613 }
9614 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
9615 {
9616 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
9617 md->setBodyDef(root->fileDef());
9618 }
9620 md->setMaxInitLines(root->initLines);
9622 md->setRefItems(root->sli);
9623 md->setRequirementReferences(root->rqli);
9624 md->addQualifiers(root->qualifiers);
9625 if (root->mGrpId!=-1) md->setMemberGroupId(root->mGrpId);
9626 addMemberToGroups(root,md);
9628}
9629
9630//----------------------------------------------------------------------------
9631
9633{
9634 if ((root->section.isDefineDoc() || root->section.isDefine()) && !root->name.empty())
9635 {
9636 //printf("found define '%s' '%s' brief='%s' doc='%s'\n",
9637 // qPrint(root->name),qPrint(root->args),qPrint(root->brief),qPrint(root->doc));
9638
9639 if (root->tagInfo() && !root->name.empty()) // define read from a tag file
9640 {
9641 auto md = createMemberDef(root->tagInfo()->tagName,1,1,
9642 "#define",root->name,root->args,DString(),
9643 Protection::Public,Specifier::Normal,false,Relationship::Member,MemberType::Define,
9644 ArgumentList(),ArgumentList(),"");
9645 auto mmd = toMemberDefMutable(md.get());
9646 mmd->setTagInfo(root->tagInfo());
9647 mmd->setLanguage(root->lang);
9648 mmd->addQualifiers(root->qualifiers);
9649 //printf("Searching for '%s' fd=%p\n",qPrint(filePathName),fd);
9650 mmd->setFileDef(root->parent()->fileDef());
9651 //printf("Adding member=%s\n",qPrint(md->name()));
9653 mn->push_back(std::move(md));
9654 }
9656 if (mn)
9657 {
9658 int count=0;
9659 for (const auto &md : *mn)
9660 {
9661 if (md->memberType()==MemberType::Define) count++;
9662 }
9663 if (count==1)
9664 {
9665 for (const auto &imd : *mn)
9666 {
9667 MemberDefMutable *md = toMemberDefMutable(imd.get());
9668 if (md && md->memberType()==MemberType::Define)
9669 {
9670 addDefineDoc(root,md);
9671 }
9672 }
9673 }
9674 else if (count>1 &&
9675 (!root->doc.empty() ||
9676 !root->brief.empty() ||
9677 root->bodyLine!=-1
9678 )
9679 )
9680 // multiple defines don't know where to add docs
9681 // but maybe they are in different files together with their documentation
9682 {
9683 for (const auto &imd : *mn)
9684 {
9685 MemberDefMutable *md = toMemberDefMutable(imd.get());
9686 if (md && md->memberType()==MemberType::Define)
9687 {
9688 if (haveEqualFileNames(root, md) || isEntryInGroupOfMember(root, md))
9689 // doc and define in the same file or group assume they belong together.
9690 {
9691 addDefineDoc(root,md);
9692 }
9693 }
9694 }
9695 //warn("define {} found in the following files:\n",root->name);
9696 //warn("Cannot determine where to add the documentation found "
9697 // "at line {} of file {}. \n",
9698 // root->startLine,root->fileName);
9699 }
9700 }
9701 else if (!root->doc.empty() || !root->brief.empty()) // define not found
9702 {
9703 bool preEnabled = Config_getBool(ENABLE_PREPROCESSING);
9704 if (preEnabled)
9705 {
9706 warn(root->fileName,root->startLine,"documentation for unknown define {} found.",root->name);
9707 }
9708 else
9709 {
9710 warn(root->fileName,root->startLine, "found documented #define {} but ignoring it because ENABLE_PREPROCESSING is NO.", root->name);
9711 }
9712 }
9713 }
9714 for (const auto &e : root->children()) findDefineDocumentation(e.get());
9715}
9716
9717//----------------------------------------------------------------------------
9718
9719static void findDirDocumentation(const Entry *root)
9720{
9721 if (root->section.isDirDoc())
9722 {
9723 DString normalizedName = root->name;
9724 normalizedName = substitute(normalizedName,"\\","/");
9725 //printf("root->docFile=%s normalizedName=%s\n",
9726 // qPrint(root->docFile),qPrint(normalizedName));
9727 if (root->docFile==normalizedName) // current dir?
9728 {
9729 if (size_t lastSlashPos=normalizedName.rfind('/'); lastSlashPos!=DString::npos) // strip file name
9730 {
9731 normalizedName=normalizedName.left(lastSlashPos);
9732 }
9733 }
9734 if (normalizedName.at(normalizedName.length()-1)!='/')
9735 {
9736 normalizedName+='/';
9737 }
9738 DirDef *matchingDir=nullptr;
9739 for (const auto &dir : *Doxygen::dirLinkedMap)
9740 {
9741 //printf("Dir: %s<->%s\n",qPrint(dir->name()),qPrint(normalizedName));
9742 if (dir->name().right(normalizedName.length())==normalizedName)
9743 {
9744 if (matchingDir)
9745 {
9746 warn(root->fileName,root->startLine,
9747 "\\dir command matches multiple directories.\n"
9748 " Applying the command for directory {}\n"
9749 " Ignoring the command for directory {}",
9750 matchingDir->name(),dir->name()
9751 );
9752 }
9753 else
9754 {
9755 matchingDir=dir.get();
9756 }
9757 }
9758 }
9759 if (matchingDir)
9760 {
9761 //printf("Match for with dir %s #anchor=%zu\n",qPrint(matchingDir->name()),root->anchors.size());
9762 matchingDir->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9763 matchingDir->setDocumentation(root->doc,root->docFile,root->docLine);
9764 matchingDir->setRefItems(root->sli);
9765 matchingDir->setRequirementReferences(root->rqli);
9766 matchingDir->addSectionsToDefinition(root->anchors);
9767 root->commandOverrides.apply_directoryGraph([&](bool b) { matchingDir->overrideDirectoryGraph(b); });
9768 addDirToGroups(root,matchingDir);
9769 }
9770 else
9771 {
9772 warn(root->fileName,root->startLine,"No matching directory found for command \\dir {}",normalizedName);
9773 }
9774 }
9775 for (const auto &e : root->children()) findDirDocumentation(e.get());
9776}
9777
9778//----------------------------------------------------------------------------
9780{
9781 if (root->section.isRequirementDoc())
9782 {
9784 }
9785 for (const auto &e : root->children()) buildRequirementsList(e.get());
9786}
9787
9788//----------------------------------------------------------------------------
9789// create a (sorted) list of separate documentation pages
9790
9791static void buildPageList(Entry *root)
9792{
9793 if (root->section.isPageDoc())
9794 {
9795 if (!root->name.empty())
9796 {
9797 addRelatedPage(root);
9798 }
9799 }
9800 else if (root->section.isMainpageDoc())
9801 {
9802 DString title=root->args.stripWhiteSpace();
9803 if (title.empty()) title=theTranslator->trMainPage();
9804 //DString name = Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9805 DString name = "index";
9806 addRefItem(root->sli,
9807 name,
9808 theTranslator->trPage(true,true),
9809 name,
9810 title,
9811 DString(),nullptr
9812 );
9813 }
9814 for (const auto &e : root->children()) buildPageList(e.get());
9815}
9816
9817// search for the main page defined in this project
9818static void findMainPage(Entry *root)
9819{
9820 if (root->section.isMainpageDoc())
9821 {
9822 if (Doxygen::mainPage==nullptr && root->tagInfo()==nullptr)
9823 {
9824 //printf("mainpage: docLine=%d startLine=%d\n",root->docLine,root->startLine);
9825 //printf("Found main page! \n======\n%s\n=======\n",qPrint(root->doc));
9826 DString title=root->args.stripWhiteSpace();
9827 if (title.empty()) title = Config_getString(PROJECT_NAME);
9828 //DString indexName=Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9829 DString indexName="index";
9831 indexName, root->brief+root->doc+root->inbodyDocs,title);
9832 //setFileNameForSections(root->anchors,"index",Doxygen::mainPage);
9833 Doxygen::mainPage->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9834 Doxygen::mainPage->setBodySegment(root->startLine,root->startLine,-1);
9835 Doxygen::mainPage->setFileName(indexName);
9836 Doxygen::mainPage->setLocalToc(root->localToc);
9838
9840 if (si)
9841 {
9842 if (!si->ref().empty()) // we are from a tag file
9843 {
9844 // a page name is a label as well! but should no be double either
9846 Doxygen::mainPage->name(),
9847 indexName,
9848 root->startLine,
9849 Doxygen::mainPage->title(),
9851 0); // level 0
9852 }
9853 else if (si->lineNr() != -1)
9854 {
9855 warn(root->fileName,root->startLine,"multiple use of section label '{}' for main page, (first occurrence: {}, line {})",
9856 Doxygen::mainPage->name(),si->fileName(),si->lineNr());
9857 }
9858 else
9859 {
9860 warn(root->fileName,root->startLine,"multiple use of section label '{}' for main page, (first occurrence: {})",
9861 Doxygen::mainPage->name(),si->fileName());
9862 }
9863 }
9864 else
9865 {
9866 // a page name is a label as well! but should no be double either
9868 Doxygen::mainPage->name(),
9869 indexName,
9870 root->startLine,
9871 Doxygen::mainPage->title(),
9873 0); // level 0
9874 }
9875 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9876 }
9877 else if (root->tagInfo()==nullptr)
9878 {
9879 warn(root->fileName,root->startLine,
9880 "found more than one \\mainpage comment block! (first occurrence: {}, line {}), Skipping current block!",
9881 Doxygen::mainPage->docFile(),Doxygen::mainPage->getStartBodyLine());
9882 }
9883 }
9884 for (const auto &e : root->children()) findMainPage(e.get());
9885}
9886
9887// search for the main page imported via tag files and add only the section labels
9888static void findMainPageTagFiles(Entry *root)
9889{
9890 if (root->section.isMainpageDoc())
9891 {
9892 if (Doxygen::mainPage && root->tagInfo())
9893 {
9894 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9895 }
9896 }
9897 for (const auto &e : root->children()) findMainPageTagFiles(e.get());
9898}
9899
9900static void computePageRelations(Entry *root)
9901{
9902 if ((root->section.isPageDoc() || root->section.isMainpageDoc()) && !root->name.empty())
9903 {
9904 PageDef *pd = root->section.isPageDoc() ?
9906 Doxygen::mainPage.get();
9907 if (pd)
9908 {
9909 for (const BaseInfo &bi : root->extends)
9910 {
9912 if (pd==subPd)
9913 {
9914 term("page defined {} with label {} is a direct "
9915 "subpage of itself! Please remove this cyclic dependency.\n",
9916 warn_line(pd->docFile(),pd->docLine()),pd->name());
9917 }
9918 else if (subPd)
9919 {
9920 pd->addInnerCompound(subPd);
9921 //printf("*** Added subpage relation: %s->%s\n",
9922 // qPrint(pd->name()),qPrint(subPd->name()));
9923 }
9924 }
9925 }
9926 }
9927 for (const auto &e : root->children()) computePageRelations(e.get());
9928}
9929
9931{
9932 for (const auto &pd : *Doxygen::pageLinkedMap)
9933 {
9934 Definition *ppd = pd->getOuterScope();
9935 while (ppd)
9936 {
9937 if (ppd==pd.get())
9938 {
9939 term("page defined {} with label {} is a subpage "
9940 "of itself! Please remove this cyclic dependency.\n",
9941 warn_line(pd->docFile(),pd->docLine()),pd->name());
9942 }
9943 ppd=ppd->getOuterScope();
9944 }
9945 }
9946}
9947
9948//----------------------------------------------------------------------------
9949
9951{
9952 for (const auto &si : SectionManager::instance())
9953 {
9954 //printf("si->label='%s' si->definition=%s si->fileName='%s'\n",
9955 // qPrint(si->label),si->definition?qPrint(si->definition->name()):"<none>",
9956 // qPrint(si->fileName));
9957 PageDef *pd=nullptr;
9958
9959 // hack: the items of a todo/test/bug/deprecated list are all fragments from
9960 // different files, so the resulting section's all have the wrong file
9961 // name (not from the todo/test/bug/deprecated list, but from the file in
9962 // which they are defined). We correct this here by looking at the
9963 // generated section labels!
9965 {
9966 DString label="_"+rl->listName(); // "_todo", "_test", ...
9967 if (si->label().left(label.length())==label)
9968 {
9969 si->setFileName(rl->listName());
9970 si->setGenerated(true);
9971 break;
9972 }
9973 }
9974
9975 //printf("start: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
9976 if (!si->generated())
9977 {
9978 // if this section is in a page and the page is in a group, then we
9979 // have to adjust the link file name to point to the group.
9980 if (!si->fileName().empty() &&
9981 (pd=Doxygen::pageLinkedMap->find(si->fileName())) &&
9982 pd->getGroupDef())
9983 {
9984 si->setFileName(pd->getGroupDef()->getOutputFileBase());
9985 }
9986
9987 if (si->definition())
9988 {
9989 // TODO: there should be one function in Definition that returns
9990 // the file to link to, so we can avoid the following tests.
9991 const GroupDef *gd=nullptr;
9992 if (si->definition()->definitionType()==Definition::TypeMember)
9993 {
9994 gd = (toMemberDef(si->definition()))->getGroupDef();
9995 }
9996
9997 if (gd)
9998 {
9999 si->setFileName(gd->getOutputFileBase());
10000 }
10001 else
10002 {
10003 //si->fileName=si->definition->getOutputFileBase();
10004 //printf("Setting si->fileName to %s\n",qPrint(si->fileName));
10005 }
10006 }
10007 }
10008 //printf("end: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
10009 }
10010}
10011
10012
10013
10014//----------------------------------------------------------------------------
10015// generate all separate documentation pages
10016
10017
10018static void generatePageDocs()
10019{
10020 //printf("documentedPages=%d real=%d\n",documentedPages,Doxygen::pageLinkedMap->count());
10021 if (Index::instance().numDocumentedPages()==0) return;
10022 for (const auto &pd : *Doxygen::pageLinkedMap)
10023 {
10024 if (!pd->getGroupDef() && !pd->isReference())
10025 {
10026 msg("Generating docs for page {}...\n",pd->name());
10027 pd->writeDocumentation(*g_outputList);
10028 }
10029 }
10030}
10031
10032//----------------------------------------------------------------------------
10033// create a (sorted) list & dictionary of example pages
10034
10035static void buildExampleList(Entry *root)
10036{
10037 if ((root->section.isExample() || root->section.isExampleLineno()) && !root->name.empty())
10038 {
10039 if (Doxygen::exampleLinkedMap->find(root->name))
10040 {
10041 warn(root->fileName,root->startLine,"Example {} was already documented. Ignoring documentation found here.",root->name);
10042 }
10043 else
10044 {
10046 createPageDef(root->fileName,root->startLine,
10047 root->name,root->brief+root->doc+root->inbodyDocs,root->args));
10048 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
10049 pd->setFileName(convertNameToFile(pd->name()+"-example",false,true));
10051 pd->setLanguage(root->lang);
10052 pd->setShowLineNo(root->section.isExampleLineno());
10053
10054 //we don't add example to groups
10055 //addExampleToGroups(root,pd);
10056 }
10057 }
10058 for (const auto &e : root->children()) buildExampleList(e.get());
10059}
10060
10061//----------------------------------------------------------------------------
10062// prints the Entry tree (for debugging)
10063
10064void printNavTree(Entry *root,int indent)
10065{
10067 {
10068 DString indentStr;
10069 indentStr.fill(' ',indent);
10070 Debug::print(Debug::Entries,0,"{}{} at {}:{} (sec={}, spec={})\n",
10071 indentStr.empty()?"":indentStr,
10072 root->name.empty()?"<empty>":root->name,
10073 root->fileName,root->startLine,
10074 root->section.to_string(),
10075 root->spec.to_string());
10076 for (const auto &e : root->children())
10077 {
10078 printNavTree(e.get(),indent+2);
10079 }
10080 }
10081}
10082
10083
10084//----------------------------------------------------------------------------
10085// prints the Sections tree (for debugging)
10086
10088{
10090 {
10091 for (const auto &si : SectionManager::instance())
10092 {
10093 Debug::print(Debug::Sections,0,"Section = {}, file = {}, title = {}, type = {}, ref = {}\n",
10094 si->label(),si->fileName(),si->title(),si->type().level(),si->ref());
10095 }
10096 }
10097}
10098
10099
10100//----------------------------------------------------------------------------
10101// generate the example documentation
10102
10104{
10106 for (const auto &pd : *Doxygen::exampleLinkedMap)
10107 {
10108 msg("Generating docs for example {}...\n",pd->name());
10109 SrcLangExt lang = getLanguageFromFileName(pd->name(), SrcLangExt::Unknown);
10110 if (lang != SrcLangExt::Unknown)
10111 {
10112 DString ext = getFileNameExtension(pd->name());
10113 auto intf = Doxygen::parserManager->getCodeParser(ext);
10114 intf->resetCodeParserState();
10115 }
10116 DString n=pd->getOutputFileBase();
10117 startFile(*g_outputList,n,false,n,pd->name());
10119 g_outputList->docify(pd->name());
10122 DString lineNoOptStr;
10123 if (pd->showLineNo())
10124 {
10125 lineNoOptStr="{lineno}";
10126 }
10127 g_outputList->generateDoc(pd->docFile(), // file
10128 pd->docLine(), // startLine
10129 pd.get(), // context
10130 nullptr, // memberDef
10131 (pd->briefDescription().empty()?"":pd->briefDescription()+"\n\n")+
10132 pd->documentation()+"\n\n\\include"+lineNoOptStr+" "+pd->name(), // docs
10133 DocOptions()
10134 .setIndexWords(true)
10135 .setExample(pd->name()));
10136 endFile(*g_outputList); // contains g_outputList->endContents()
10137 }
10139}
10140
10141//----------------------------------------------------------------------------
10142// generate module pages
10143
10145{
10146 for (const auto &gd : *Doxygen::groupLinkedMap)
10147 {
10148 if (!gd->isReference())
10149 {
10150 gd->writeDocumentation(*g_outputList);
10151 }
10152 }
10153}
10154
10155//----------------------------------------------------------------------------
10156// generate module pages
10157
10159{
10160 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10161 if (numThreads>1) // multi threaded processing
10162 {
10163 struct DocContext
10164 {
10165 DocContext(ClassDefMutable *cdm_,const OutputList &ol_)
10166 : cdm(cdm_), ol(ol_) {}
10167 ClassDefMutable *cdm;
10168 OutputList ol;
10169 };
10170 ThreadPool threadPool(numThreads);
10171 std::vector< std::future< std::shared_ptr<DocContext> > > results;
10172 // for each class in the namespace...
10173 for (const auto &cd : classList)
10174 {
10176 if (cdm)
10177 {
10178 auto ctx = std::make_shared<DocContext>(cdm,*g_outputList);
10179 auto processFile = [ctx]()
10180 {
10181 if ( ( ctx->cdm->isLinkableInProject() &&
10182 !ctx->cdm->isImplicitTemplateInstance()
10183 ) // skip external references, anonymous compounds and
10184 // template instances and nested classes
10185 && !ctx->cdm->isHidden() && !ctx->cdm->isEmbeddedInOuterScope()
10186 )
10187 {
10188 msg("Generating docs for compound {}...\n",ctx->cdm->displayName());
10189 ctx->cdm->writeDocumentation(ctx->ol);
10190 ctx->cdm->writeMemberList(ctx->ol);
10191 }
10192 ctx->cdm->writeDocumentationForInnerClasses(ctx->ol);
10193 return ctx;
10194 };
10195 results.emplace_back(threadPool.queue(processFile));
10196 }
10197 }
10198 // wait for the results
10199 for (auto &f : results)
10200 {
10201 auto ctx = f.get();
10202 }
10203 }
10204 else // single threaded processing
10205 {
10206 // for each class in the namespace...
10207 for (const auto &cd : classList)
10208 {
10210 if (cdm)
10211 {
10212 if ( ( cd->isLinkableInProject() &&
10213 !cd->isImplicitTemplateInstance()
10214 ) // skip external references, anonymous compounds and
10215 // template instances and nested classes
10216 && !cd->isHidden() && !cd->isEmbeddedInOuterScope()
10217 )
10218 {
10219 msg("Generating docs for compound {}...\n",cd->displayName());
10220
10223 }
10225 }
10226 }
10227 }
10228}
10229
10231{
10232 // for each concept in the namespace...
10233 for (const auto &cd : conceptList)
10234 {
10236 if ( cdm && cd->isLinkableInProject() && !cd->isHidden())
10237 {
10238 msg("Generating docs for concept {}...\n",cd->name());
10240 }
10241 }
10242}
10243
10245{
10246 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
10247
10248 //writeNamespaceIndex(*g_outputList);
10249
10250 // for each namespace...
10251 for (const auto &nd : *Doxygen::namespaceLinkedMap)
10252 {
10253 if (nd->isLinkableInProject())
10254 {
10256 if (ndm)
10257 {
10258 msg("Generating docs for namespace {}\n",nd->displayName());
10260 }
10261 }
10262
10263 generateNamespaceClassDocs(nd->getClasses());
10264 if (sliceOpt)
10265 {
10266 generateNamespaceClassDocs(nd->getInterfaces());
10267 generateNamespaceClassDocs(nd->getStructs());
10268 generateNamespaceClassDocs(nd->getExceptions());
10269 }
10270 generateNamespaceConceptDocs(nd->getConcepts());
10271 }
10272}
10273
10275{
10276 std::string oldDir = Dir::currentDirPath();
10277 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
10280 {
10281 err("failed to run html help compiler on {}\n", HtmlHelp::hhpFileName);
10282 }
10283 Dir::setCurrent(oldDir);
10284}
10285
10287{
10288 DString args = Qhp::qhpFileName + " -o \"" + Qhp::getQchFileName() + "\"";
10289 std::string oldDir = Dir::currentDirPath();
10290 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
10291
10292 DString qhgLocation=Config_getString(QHG_LOCATION);
10293 if (Debug::isFlagSet(Debug::Qhp)) // produce info for debugging
10294 {
10295 // run qhelpgenerator -v and extract the Qt version used
10296 DString cmd=qhgLocation+ " -v 2>&1";
10297 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
10298 FILE *f=Portable::popen(cmd,"r");
10299 if (!f)
10300 {
10301 err("could not execute {}\n",qhgLocation);
10302 }
10303 else
10304 {
10305 const size_t bufSize = 1024;
10306 char inBuf[bufSize+1];
10307 size_t numRead=fread(inBuf,1,bufSize,f);
10308 inBuf[numRead] = '\0';
10309 Debug::print(Debug::Qhp,0,"{}",inBuf);
10311
10312 int qtVersion=0;
10313 static const reg::Ex versionReg(R"(Qt (\d+)\.(\d+)\.(\d+))");
10314 reg::Match match;
10315 std::string s = inBuf;
10316 if (reg::search(s,match,versionReg))
10317 {
10318 qtVersion = 10000*DString(match[1].str()).toInt() +
10319 100*DString(match[2].str()).toInt() +
10320 DString(match[3].str()).toInt();
10321 }
10322 if (qtVersion>0 && (qtVersion<60000 || qtVersion >= 60205))
10323 {
10324 // dump the output of qhelpgenerator -c file.qhp
10325 // Qt<6 or Qt>=6.2.5 or higher, see https://bugreports.qt.io/browse/QTBUG-101070
10326 cmd=qhgLocation+ " -c " + Qhp::qhpFileName + " 2>&1";
10327 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
10328 f=Portable::popen(cmd,"r");
10329 if (!f)
10330 {
10331 err("could not execute {}\n",qhgLocation);
10332 }
10333 else
10334 {
10335 std::string output;
10336 while ((numRead=fread(inBuf,1,bufSize,f))>0)
10337 {
10338 inBuf[numRead] = '\0';
10339 output += inBuf;
10340 }
10342 Debug::print(Debug::Qhp,0,"{}",output);
10343 }
10344 }
10345 }
10346 }
10347
10348 if (Portable::system(qhgLocation, args, false))
10349 {
10350 err("failed to run qhelpgenerator on {}\n",Qhp::qhpFileName);
10351 }
10352 Dir::setCurrent(oldDir);
10353}
10354
10355//----------------------------------------------------------------------------
10356
10358{
10359 // check dot path
10360 DString dotPath = Config_getString(DOT_PATH);
10361 if (!dotPath.empty())
10362 {
10363 FileInfo fi(dotPath.str());
10364 if (!(fi.exists() && fi.isFile()) )// not an existing user specified path + exec
10365 {
10366 dotPath = dotPath+"/dot"+Portable::commandExtension();
10367 FileInfo dp(dotPath.str());
10368 if (!dp.exists() || !dp.isFile())
10369 {
10370 warn_uncond("the dot tool could not be found as '{}'\n",dotPath);
10371 dotPath = "dot";
10372 dotPath += Portable::commandExtension();
10373 }
10374 }
10375#if defined(_WIN32) // convert slashes
10376 size_t l=dotPath.length();
10377 for (size_t i=0;i<l;i++) if (dotPath.at(i)=='/') dotPath.at(i)='\\';
10378#endif
10379 }
10380 else
10381 {
10382 dotPath = "dot";
10383 dotPath += Portable::commandExtension();
10384 }
10385 Doxygen::verifiedDotPath = dotPath;
10387}
10388
10389//----------------------------------------------------------------------------
10390
10391/*! Generate a template version of the configuration file.
10392 * If the \a shortList parameter is true a configuration file without
10393 * comments will be generated.
10394 */
10395static void generateConfigFile(const DString &configFile,bool shortList,
10396 bool updateOnly=false)
10397{
10398 std::ofstream f;
10399 bool fileOpened=openOutputFile(configFile,f);
10400 bool writeToStdout=configFile=="-";
10401 if (fileOpened)
10402 {
10403 TextStream t(&f);
10404 Config::writeTemplate(t,shortList,updateOnly);
10405 if (!writeToStdout)
10406 {
10407 if (!updateOnly)
10408 {
10409 msg("\n\nConfiguration file '{}' created.\n\n",configFile);
10410 msg("Now edit the configuration file and enter\n\n");
10411 if (configFile!="Doxyfile" && configFile!="doxyfile")
10412 msg(" doxygen {}\n\n",configFile);
10413 else
10414 msg(" doxygen\n\n");
10415 msg("to generate the documentation for your project\n\n");
10416 }
10417 else
10418 {
10419 msg("\n\nConfiguration file '{}' updated.\n\n",configFile);
10420 }
10421 }
10422 }
10423 else
10424 {
10425 term("Cannot open file {} for writing\n",configFile);
10426 }
10427}
10428
10430{
10431 std::ofstream f;
10432 bool fileOpened=openOutputFile("-",f);
10433 if (fileOpened)
10434 {
10435 TextStream t(&f);
10436 Config::compareDoxyfile(t,diffList);
10437 }
10438 else
10439 {
10440 term("Cannot open stdout for writing\n");
10441 }
10442}
10443
10444//----------------------------------------------------------------------------
10445// read and parse a tag file
10446
10447static void readTagFile(const std::shared_ptr<Entry> &root,const DString &tagLine)
10448{
10449 DString fileName;
10450 DString destName;
10451 if (size_t eqPos = tagLine.find('='); eqPos!=DString::npos) // tag command contains a destination
10452 {
10453 fileName = tagLine.left(eqPos).stripWhiteSpace();
10454 destName = tagLine.mid(eqPos+1).stripWhiteSpace();
10455 if (fileName.empty() || destName.empty()) return;
10456 //printf("insert tagDestination %s->%s\n",qPrint(fi.fileName()),qPrint(destName));
10457 }
10458 else
10459 {
10460 fileName = tagLine;
10461 }
10462
10463 FileInfo fi(fileName.str());
10464 if (!fi.exists() || !fi.isFile())
10465 {
10466 err("Tag file '{}' does not exist or is not a file. Skipping it...\n",fileName);
10467 return;
10468 }
10469
10470 if (Doxygen::tagFileSet.find(fi.absFilePath()) != Doxygen::tagFileSet.end()) return;
10471
10472 Doxygen::tagFileSet.emplace(fi.absFilePath());
10473
10474 if (!destName.empty())
10475 {
10476 Doxygen::tagDestinationMap.emplace(fi.absFilePath(), destName.str());
10477 msg("Reading tag file '{}', location '{}'...\n",fileName,destName);
10478 }
10479 else
10480 {
10481 msg("Reading tag file '{}'...\n",fileName);
10482 }
10483
10484 parseTagFile(root,fi.absFilePath().c_str());
10485}
10486
10487//----------------------------------------------------------------------------
10489{
10490 StringVector latexExtraStyleSheet = Config_getList(LATEX_EXTRA_STYLESHEET);
10491 for (const auto &sheet : latexExtraStyleSheet)
10492 {
10493 std::string fileName = sheet;
10494 if (!fileName.empty())
10495 {
10496 FileInfo fi(fileName);
10497 if (!fi.exists())
10498 {
10499 err("Style sheet '{}' specified by LATEX_EXTRA_STYLESHEET does not exist!\n",fileName);
10500 }
10501 else if (fi.isDir())
10502 {
10503 err("Style sheet '{}' specified by LATEX_EXTRA_STYLESHEET is a directory, it has to be a file!\n", fileName);
10504 }
10505 else
10506 {
10507 DString destFileName = Config_getString(LATEX_OUTPUT)+"/"+fi.fileName();
10509 {
10510 destFileName += LATEX_STYLE_EXTENSION;
10511 }
10512 copyFile(fileName, destFileName);
10513 }
10514 }
10515 }
10516}
10517
10518//----------------------------------------------------------------------------
10519static void copyStyleSheet()
10520{
10521 DString htmlStyleSheet = Config_getString(HTML_STYLESHEET);
10522 if (!htmlStyleSheet.empty())
10523 {
10524 if (!htmlStyleSheet.startsWith("http:") && !htmlStyleSheet.startsWith("https:"))
10525 {
10526 FileInfo fi(htmlStyleSheet.str());
10527 if (!fi.exists())
10528 {
10529 err("Style sheet '{}' specified by HTML_STYLESHEET does not exist!\n",htmlStyleSheet);
10530 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
10531 }
10532 else if (fi.isDir())
10533 {
10534 err("Style sheet '{}' specified by HTML_STYLESHEET is a directory, it has to be a file!\n",htmlStyleSheet);
10535 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
10536 }
10537 else
10538 {
10539 DString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
10540 copyFile(htmlStyleSheet,destFileName);
10541 }
10542 }
10543 }
10544 StringVector htmlExtraStyleSheet = Config_getList(HTML_EXTRA_STYLESHEET);
10545 for (const auto &sheet : htmlExtraStyleSheet)
10546 {
10547 DString fileName(sheet);
10548 if (!fileName.empty() && !fileName.startsWith("http:") && !fileName.startsWith("https:"))
10549 {
10550 FileInfo fi(fileName.str());
10551 if (!fi.exists())
10552 {
10553 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET does not exist!\n",fileName);
10554 }
10555 else if (fi.fileName()=="doxygen.css" || fi.fileName()=="tabs.css" || fi.fileName()=="navtree.css")
10556 {
10557 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET is already a built-in stylesheet. Please use a different name\n",fi.fileName());
10558 }
10559 else if (fi.isDir())
10560 {
10561 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET is a directory, it has to be a file!\n",fileName);
10562 }
10563 else
10564 {
10565 DString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
10566 copyFile(fileName, destFileName);
10567 }
10568 }
10569 }
10570}
10571
10572static void copyLogo(const DString &outputOption, bool toIndex)
10573{
10574 DString projectLogo = projectLogoFile();
10575 if (!projectLogo.empty())
10576 {
10577 FileInfo fi(projectLogo.str());
10578 if (!fi.exists())
10579 {
10580 err("Project logo '{}' specified by PROJECT_LOGO does not exist!\n",projectLogo);
10581 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
10582 }
10583 else if (fi.isDir())
10584 {
10585 err("Project logo '{}' specified by PROJECT_LOGO is a directory, it has to be a file!\n",projectLogo);
10586 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
10587 }
10588 else
10589 {
10590 DString destFileName = outputOption+"/"+fi.fileName();
10591 copyFile(projectLogo,destFileName);
10592 if (toIndex) Doxygen::indexList->addImageFile(fi.fileName());
10593 }
10594 }
10595}
10596
10597static void copyIcon(const DString &outputOption, bool toIndex)
10598{
10599 DString projectIcon = Config_getString(PROJECT_ICON);
10600 if (!projectIcon.empty())
10601 {
10602 FileInfo fi(projectIcon.str());
10603 if (!fi.exists())
10604 {
10605 err("Project icon '{}' specified by PROJECT_ICON does not exist!\n",projectIcon);
10606 projectIcon = Config_updateString(PROJECT_ICON,""); // revert to the default
10607 }
10608 else if (fi.isDir())
10609 {
10610 err("Project icon '{}' specified by PROJECT_ICON is a directory, it has to be a file!\n",projectIcon);
10611 projectIcon = Config_updateString(PROJECT_ICON,""); // revert to the default
10612 }
10613 else
10614 {
10615 DString destFileName = outputOption+"/"+fi.fileName();
10616 copyFile(projectIcon,destFileName);
10617 if (toIndex) Doxygen::indexList->addImageFile(fi.fileName());
10618 }
10619 }
10620}
10621
10622static inline void copyExtraFiles(StringVector files,const DString &filesOption,const DString &outputOption, bool toIndex)
10623{
10624 for (const auto &fileName : files)
10625 {
10626 if (!fileName.empty())
10627 {
10628 FileInfo fi(fileName);
10629 if (!fi.exists())
10630 {
10631 err("Extra file '{}' specified in {} does not exist!\n", fileName,filesOption);
10632 }
10633 else if (fi.isDir())
10634 {
10635 err("Extra file '{}' specified in {} is a directory, it has to be a file!\n", fileName,filesOption);
10636 }
10637 else
10638 {
10639 DString destFileName = outputOption+"/"+fi.fileName();
10640 copyFile(fileName, destFileName);
10641 if (toIndex) Doxygen::indexList->addImageFile(fi.fileName());
10642 }
10643 }
10644 }
10645}
10646
10647//----------------------------------------------------------------------------
10648
10650{
10651 for (const auto &fn : *Doxygen::inputNameLinkedMap)
10652 {
10653 struct FileEntry
10654 {
10655 FileEntry(const DString &p,FileDef *fd) : path(p), fileDef(fd) {}
10656 DString path;
10657 FileDef *fileDef;
10658 };
10659
10660 // collect the entry for which to compute the longest common prefix (LCP) of the path
10661 std::vector<FileEntry> fileEntries;
10662 for (const auto &fd : *fn)
10663 {
10664 if (!fd->isReference()) // skip external references
10665 {
10666 fileEntries.emplace_back(fd->getPath(),fd.get());
10667 }
10668 }
10669
10670 size_t size = fileEntries.size();
10671
10672 if (size==1) // name if unique, so diskname is simply the name
10673 {
10674 FileDef *fd = fileEntries[0].fileDef;
10675 fd->setDiskName(fn->fileName());
10676 }
10677 else if (size>1) // multiple occurrences of the same file name
10678 {
10679 // sort the array
10680 std::stable_sort(fileEntries.begin(),
10681 fileEntries.end(),
10682 [](const FileEntry &fe1,const FileEntry &fe2)
10683 { return dstricmp_sort(fe1.path,fe2.path)<0; }
10684 );
10685
10686 // since the entries are sorted, the common prefix of the whole array is same
10687 // as the common prefix between the first and last entry
10688 const FileEntry &first = fileEntries[0];
10689 const FileEntry &last = fileEntries[size-1];
10690 int first_path_size = static_cast<int>(first.path.size())-1; // -1 to skip trailing slash
10691 int last_path_size = static_cast<int>(last.path.size())-1; // -1 to skip trailing slash
10692 int j=0;
10693 int i=0;
10694 for (i=0;i<first_path_size && i<last_path_size;i++)
10695 {
10696 if (first.path[i]=='/') j=i;
10697 if (first.path[i]!=last.path[i]) break;
10698 }
10699 if (i==first_path_size && i<last_path_size && last.path[i]=='/')
10700 {
10701 // case first='some/path' and last='some/path/more' => match is 'some/path'
10702 j=first_path_size;
10703 }
10704 else if (i==last_path_size && i<first_path_size && first.path[i]=='/')
10705 {
10706 // case first='some/path/more' and last='some/path' => match is 'some/path'
10707 j=last_path_size;
10708 }
10709
10710 // add non-common part of the path to the name
10711 for (auto &fileEntry : fileEntries)
10712 {
10713 DString prefix = fileEntry.path.right(fileEntry.path.length()-j-1);
10714 fileEntry.fileDef->setName(prefix+fn->fileName());
10715 //printf("!!!!!!!! non unique disk name=%s:%s\n",qPrint(prefix),fn->fileName());
10716 fileEntry.fileDef->setDiskName(prefix+fn->fileName());
10717 }
10718 }
10719 }
10720}
10721
10722
10723
10724//----------------------------------------------------------------------------
10725
10726static std::unique_ptr<OutlineParserInterface> getParserForFile(const DString &fn)
10727{
10728 DString fileName=fn;
10729 DString extension;
10730 size_t sep = fileName.rfind('/');
10731 size_t ei = fileName.rfind('.');
10732 if (ei!=DString::npos && (sep==DString::npos || ei>sep)) // matches dir/file.ext but not dir.1/file
10733 {
10734 extension=fileName.mid(ei);
10735 }
10736 else
10737 {
10738 extension = ".no_extension";
10739 }
10740
10741 return Doxygen::parserManager->getOutlineParser(extension);
10742}
10743
10744static std::shared_ptr<Entry> parseFile(OutlineParserInterface &parser,
10745 FileDef *fd,const DString &fn,
10746 ClangTUParser *clangParser,bool newTU)
10747{
10748 DString fileName=fn;
10749 AUTO_TRACE("fileName={}",fileName);
10750 DString extension;
10751 if (size_t ei = fileName.rfind('.'); ei!=DString::npos)
10752 {
10753 extension=fileName.mid(ei);
10754 }
10755 else
10756 {
10757 extension = ".no_extension";
10758 }
10759
10760 FileInfo fi(fileName.str());
10761 std::string preBuf;
10762
10763 if (Config_getBool(ENABLE_PREPROCESSING) &&
10764 parser.needsPreprocessing(extension))
10765 {
10766 Preprocessor preprocessor;
10767 StringVector includePath = Config_getList(INCLUDE_PATH);
10768 for (const auto &s : includePath)
10769 {
10770 std::string absPath = FileInfo(s).absFilePath();
10771 preprocessor.addSearchDir(absPath);
10772 }
10773 std::string inBuf;
10774 msg("Preprocessing {}...\n",fn);
10775 readInputFile(fileName,inBuf);
10776 addTerminalCharIfMissing(inBuf,'\n');
10777 preprocessor.processFile(fileName,inBuf,preBuf);
10778 }
10779 else // no preprocessing
10780 {
10781 msg("Reading {}...\n",fn);
10782 readInputFile(fileName,preBuf);
10783 addTerminalCharIfMissing(preBuf,'\n');
10784 }
10785
10786 std::string convBuf;
10787 convBuf.reserve(preBuf.size()+1024);
10788
10789 // convert multi-line C++ comments to C style comments
10790 convertCppComments(preBuf,convBuf,fileName.str());
10791
10792 std::shared_ptr<Entry> fileRoot = std::make_shared<Entry>();
10793 // use language parse to parse the file
10794 if (clangParser)
10795 {
10796 if (newTU) clangParser->parse();
10797 clangParser->switchToFile(fd);
10798 }
10799 parser.parseInput(fileName,convBuf.data(),fileRoot,clangParser);
10800 fileRoot->setFileDef(fd);
10801 return fileRoot;
10802}
10803
10804//! parse the list of input files
10805static void parseFilesMultiThreading(const std::shared_ptr<Entry> &root)
10806{
10807 AUTO_TRACE();
10808#if USE_LIBCLANG
10810 {
10811 StringUnorderedSet processedFiles;
10812
10813 // create a dictionary with files to process
10814 StringUnorderedSet filesToProcess;
10815 for (const auto &s : g_inputFiles)
10816 {
10817 filesToProcess.insert(s);
10818 }
10819
10820 std::mutex processedFilesLock;
10821 // process source files (and their include dependencies)
10822 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10823 msg("Processing input using {} threads.\n",numThreads);
10824 ThreadPool threadPool(numThreads);
10825 using FutureType = std::vector< std::shared_ptr<Entry> >;
10826 std::vector< std::future< FutureType > > results;
10827 for (const auto &s : g_inputFiles)
10828 {
10829 bool ambig = false;
10830 DString qs = s;
10832 ASSERT(fd!=nullptr);
10833 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt::Cpp) // this is a source file
10834 {
10835 // lambda representing the work to executed by a thread
10836 auto processFile = [qs,&filesToProcess,&processedFilesLock,&processedFiles]() {
10837 bool ambig_l = false;
10838 std::vector< std::shared_ptr<Entry> > roots;
10839 FileDef *fd_l = Doxygen::inputNameLinkedMap->findFileDef(qs,ambig_l);
10840 auto clangParser = ClangParser::instance()->createTUParser(fd_l);
10841 auto parser = getParserForFile(qs);
10842 auto fileRoot { parseFile(*parser.get(),fd_l,qs,clangParser.get(),true) };
10843 roots.push_back(fileRoot);
10844
10845 // Now process any include files in the same translation unit
10846 // first. When libclang is used this is much more efficient.
10847 for (auto incFile : clangParser->filesInSameTU())
10848 {
10849 DString qincFile = incFile;
10850 if (filesToProcess.find(incFile)!=filesToProcess.end())
10851 {
10852 bool needsToBeProcessed = false;
10853 {
10854 std::lock_guard<std::mutex> lock(processedFilesLock);
10855 needsToBeProcessed = processedFiles.find(incFile)==processedFiles.end();
10856 if (needsToBeProcessed) processedFiles.insert(incFile);
10857 }
10858 if (qincFile!=qs && needsToBeProcessed)
10859 {
10860 FileDef *ifd=Doxygen::inputNameLinkedMap->findFileDef(qincFile,ambig_l);
10861 if (ifd && !ifd->isReference())
10862 {
10863 //printf(" Processing %s in same translation unit as %s\n",incFile,qPrint(s));
10864 fileRoot = parseFile(*parser.get(),ifd,qincFile,clangParser.get(),false);
10865 roots.push_back(fileRoot);
10866 }
10867 }
10868 }
10869 }
10870 return roots;
10871 };
10872 // dispatch the work and collect the future results
10873 results.emplace_back(threadPool.queue(processFile));
10874 }
10875 }
10876 // synchronize with the Entry result lists produced and add them to the root
10877 for (auto &f : results)
10878 {
10879 auto l = f.get();
10880 for (auto &e : l)
10881 {
10882 root->moveToSubEntryAndKeep(e);
10883 }
10884 }
10885 // process remaining files
10886 results.clear();
10887 for (const auto &s : g_inputFiles)
10888 {
10889 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
10890 {
10891 // lambda representing the work to executed by a thread
10892 auto processFile = [s]() {
10893 bool ambig = false;
10894 DString qs = s;
10895 std::vector< std::shared_ptr<Entry> > roots;
10897 auto parser { getParserForFile(qs) };
10898 bool useClang = getLanguageFromFileName(qs)==SrcLangExt::Cpp;
10899 if (useClang)
10900 {
10901 auto clangParser = ClangParser::instance()->createTUParser(fd);
10902 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
10903 roots.push_back(fileRoot);
10904 }
10905 else
10906 {
10907 auto fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10908 roots.push_back(fileRoot);
10909 }
10910 return roots;
10911 };
10912 results.emplace_back(threadPool.queue(processFile));
10913 }
10914 }
10915 // synchronize with the Entry result lists produced and add them to the root
10916 for (auto &f : results)
10917 {
10918 auto l = f.get();
10919 for (auto &e : l)
10920 {
10921 root->moveToSubEntryAndKeep(e);
10922 }
10923 }
10924 }
10925 else // normal processing
10926#endif
10927 {
10928 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10929 msg("Processing input using {} threads.\n",numThreads);
10930 ThreadPool threadPool(numThreads);
10931 using FutureType = std::shared_ptr<Entry>;
10932 std::vector< std::future< FutureType > > results;
10933 for (const auto &s : g_inputFiles)
10934 {
10935 // lambda representing the work to executed by a thread
10936 auto processFile = [s]() {
10937 bool ambig = false;
10938 DString qs = s;
10940 auto parser = getParserForFile(qs);
10941 auto fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10942 return fileRoot;
10943 };
10944 // dispatch the work and collect the future results
10945 results.emplace_back(threadPool.queue(processFile));
10946 }
10947 // synchronize with the Entry results produced and add them to the root
10948 for (auto &f : results)
10949 {
10950 root->moveToSubEntryAndKeep(f.get());
10951 }
10952 }
10953}
10954
10955//! parse the list of input files
10956static void parseFilesSingleThreading(const std::shared_ptr<Entry> &root)
10957{
10958 AUTO_TRACE();
10959#if USE_LIBCLANG
10961 {
10962 StringUnorderedSet processedFiles;
10963
10964 // create a dictionary with files to process
10965 StringUnorderedSet filesToProcess;
10966 for (const auto &s : g_inputFiles)
10967 {
10968 filesToProcess.insert(s);
10969 }
10970
10971 // process source files (and their include dependencies)
10972 for (const auto &s : g_inputFiles)
10973 {
10974 bool ambig = false;
10975 DString qs =s;
10977 ASSERT(fd!=nullptr);
10978 if (fd->isSource() && !fd->isReference() && getLanguageFromFileName(qs)==SrcLangExt::Cpp) // this is a source file
10979 {
10980 auto clangParser = ClangParser::instance()->createTUParser(fd);
10981 auto parser { getParserForFile(qs) };
10982 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
10983 root->moveToSubEntryAndKeep(fileRoot);
10984 processedFiles.insert(s);
10985
10986 // Now process any include files in the same translation unit
10987 // first. When libclang is used this is much more efficient.
10988 for (auto incFile : clangParser->filesInSameTU())
10989 {
10990 //printf(" file %s\n",qPrint(incFile));
10991 if (filesToProcess.find(incFile)!=filesToProcess.end() && // file need to be processed
10992 processedFiles.find(incFile)==processedFiles.end()) // and is not processed already
10993 {
10994 FileDef *ifd=Doxygen::inputNameLinkedMap->findFileDef(incFile,ambig);
10995 if (ifd && !ifd->isReference())
10996 {
10997 //printf(" Processing %s in same translation unit as %s\n",qPrint(incFile),qPrint(qs));
10998 fileRoot = parseFile(*parser.get(),ifd,incFile,clangParser.get(),false);
10999 root->moveToSubEntryAndKeep(fileRoot);
11000 processedFiles.insert(incFile);
11001 }
11002 }
11003 }
11004 }
11005 }
11006 // process remaining files
11007 for (const auto &s : g_inputFiles)
11008 {
11009 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
11010 {
11011 bool ambig = false;
11012 DString qs = s;
11014 if (getLanguageFromFileName(qs)==SrcLangExt::Cpp) // not yet processed
11015 {
11016 auto clangParser = ClangParser::instance()->createTUParser(fd);
11017 auto parser { getParserForFile(qs) };
11018 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
11019 root->moveToSubEntryAndKeep(fileRoot);
11020 }
11021 else
11022 {
11023 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(qs) };
11024 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
11025 root->moveToSubEntryAndKeep(fileRoot);
11026 }
11027 processedFiles.insert(s);
11028 }
11029 }
11030 }
11031 else // normal processing
11032#endif
11033 {
11034 for (const auto &s : g_inputFiles)
11035 {
11036 bool ambig = false;
11037 DString qs = s;
11039 ASSERT(fd!=nullptr);
11040 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(qs) };
11041 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
11042 root->moveToSubEntryAndKeep(std::move(fileRoot));
11043 }
11044 }
11045}
11046
11047// resolves a path that may include symlinks, if a recursive symlink is
11048// found an empty string is returned.
11049static std::string resolveSymlink(const std::string &path)
11050{
11051 size_t sepPos=0;
11052 size_t oldPos=0;
11053 StringUnorderedSet nonSymlinks;
11054 StringUnorderedSet known;
11055 DString result(path);
11056 DString oldPrefix = "/";
11057 do
11058 {
11059#if defined(_WIN32)
11060 // UNC path, skip server and share name
11061 if (sepPos==0 && (result.startsWith("//") || result.startsWith("\\\\")))
11062 sepPos = result.find('/',2);
11063 if (sepPos!=DString::npos)
11064 sepPos = result.find('/',sepPos+1);
11065#else
11066 sepPos = result.find('/',sepPos+1);
11067#endif
11068 DString prefix = sepPos==DString::npos ? result : result.left(sepPos);
11069 if (nonSymlinks.find(prefix.str())==nonSymlinks.end())
11070 {
11071 FileInfo fi(prefix.str());
11072 if (fi.isSymLink())
11073 {
11074 DString target = fi.readLink();
11075 bool isRelative = FileInfo(target.str()).isRelative();
11076 if (isRelative)
11077 {
11078 target = Dir::cleanDirPath(oldPrefix.str()+"/"+target.str());
11079 }
11080 if (sepPos!=DString::npos)
11081 {
11082 if (fi.isDir() && !target.empty() && target.at(target.length()-1)!='/')
11083 {
11084 target+='/';
11085 }
11086 target+=result.mid(sepPos);
11087 }
11088 result = Dir::cleanDirPath(target.str());
11089 if (known.find(result.str())!=known.end()) return std::string(); // recursive symlink!
11090 known.insert(result.str());
11091 if (isRelative)
11092 {
11093 sepPos = oldPos;
11094 }
11095 else // link to absolute path
11096 {
11097 sepPos = 0;
11098 oldPrefix = "/";
11099 }
11100 }
11101 else
11102 {
11103 nonSymlinks.insert(prefix.str());
11104 oldPrefix = prefix;
11105 }
11106 oldPos = sepPos;
11107 }
11108 }
11109 while (sepPos!=DString::npos);
11110 return Dir::cleanDirPath(result.str());
11111}
11112
11114
11115//----------------------------------------------------------------------------
11116// Read all files matching at least one pattern in 'patList' in the
11117// directory represented by 'fi'.
11118// The directory is read iff the recursiveFlag is set.
11119// The contents of all files is append to the input string
11120
11121static void readDir(FileInfo *fi,
11122 FileNameLinkedMap *fnMap,
11123 StringUnorderedSet *exclSet,
11124 const StringVector *patList,
11125 const StringVector *exclPatList,
11126 StringVector *resultList,
11127 StringUnorderedSet *resultSet,
11128 bool errorIfNotExist,
11129 bool recursive,
11130 StringUnorderedSet *killSet,
11131 StringUnorderedSet *paths
11132 )
11133{
11134 std::string dirName = fi->absFilePath();
11135 if (paths && !dirName.empty())
11136 {
11137 paths->insert(dirName);
11138 }
11139 //printf("%s isSymLink()=%d\n",qPrint(dirName),fi->isSymLink());
11140 if (fi->isSymLink())
11141 {
11142 dirName = resolveSymlink(dirName);
11143 if (dirName.empty())
11144 {
11145 //printf("RECURSIVE SYMLINK: %s\n",qPrint(dirName));
11146 return; // recursive symlink
11147 }
11148 }
11149
11150 if (g_pathsVisited.find(dirName)!=g_pathsVisited.end())
11151 {
11152 //printf("PATH ALREADY VISITED: %s\n",qPrint(dirName));
11153 return; // already visited path
11154 }
11155 g_pathsVisited.insert(dirName);
11156
11157 Dir dir(dirName);
11158 msg("Searching for files in directory {}\n", fi->absFilePath());
11159 //printf("killSet=%p count=%d\n",killSet,killSet ? (int)killSet->count() : -1);
11160
11161 StringVector dirResultList;
11162
11163 bool caseSenseNames = useCaseSenseNames();
11164
11165 for (const auto &dirEntry : dir.iterator())
11166 {
11167 FileInfo cfi(dirEntry.path());
11168 auto checkPatterns = [&]() -> bool
11169 {
11170 return (patList==nullptr || cfi.match(*patList,caseSenseNames)) &&
11171 (exclPatList==nullptr || !cfi.match(*exclPatList,caseSenseNames)) &&
11172 (killSet==nullptr || killSet->find(cfi.absFilePath())==killSet->end());
11173 };
11174
11175 if (exclSet==nullptr || exclSet->find(cfi.absFilePath())==exclSet->end())
11176 { // file should not be excluded
11177 //printf("killSet->find(%s)\n",qPrint(cfi->absFilePath()));
11178 if (Config_getBool(EXCLUDE_SYMLINKS) && cfi.isSymLink())
11179 {
11180 }
11181 else if (!cfi.exists() || !cfi.isReadable())
11182 {
11183 if (errorIfNotExist && checkPatterns())
11184 {
11185 warn_uncond("source '{}' is not a readable file or directory... skipping.\n",cfi.absFilePath());
11186 }
11187 }
11188 else if (cfi.isFile() && checkPatterns())
11189 {
11190 std::string name=cfi.fileName();
11191 std::string path=cfi.dirPath()+"/";
11192 std::string fullName=path+name;
11193 if (fnMap)
11194 {
11195 auto fd = createFileDef(path,name);
11196 FileName *fn=nullptr;
11197 if (!name.empty())
11198 {
11199 fn = fnMap->add(name);
11200 fn->push_back(std::move(fd));
11201 }
11202 }
11203 dirResultList.push_back(fullName);
11204 if (resultSet) resultSet->insert(fullName);
11205 if (killSet) killSet->insert(fullName);
11206 }
11207 else if (recursive &&
11208 cfi.isDir() &&
11209 (exclPatList==nullptr || !cfi.match(*exclPatList,caseSenseNames)) &&
11210 cfi.fileName().at(0)!='.') // skip "." ".." and ".dir"
11211 {
11212 FileInfo acfi(cfi.absFilePath());
11213 readDir(&acfi,fnMap,exclSet,
11214 patList,exclPatList,&dirResultList,resultSet,errorIfNotExist,
11215 recursive,killSet,paths);
11216 }
11217 }
11218 }
11219 if (resultList && !dirResultList.empty())
11220 {
11221 // sort the resulting list to make the order platform independent.
11222 std::stable_sort(dirResultList.begin(),
11223 dirResultList.end(),
11224 [](const auto &f1,const auto &f2) { return dstricmp_sort(f1.c_str(),f2.c_str())<0; });
11225
11226 // append the sorted results to resultList
11227 resultList->insert(resultList->end(), dirResultList.begin(), dirResultList.end());
11228 }
11229}
11230
11231
11232//----------------------------------------------------------------------------
11233// read a file or all files in a directory and append their contents to the
11234// input string. The names of the files are appended to the 'fiList' list.
11235
11237 FileNameLinkedMap *fnMap,
11238 StringUnorderedSet *exclSet,
11239 const StringVector *patList,
11240 const StringVector *exclPatList,
11241 StringVector *resultList,
11242 StringUnorderedSet *resultSet,
11243 bool recursive,
11244 bool errorIfNotExist,
11245 StringUnorderedSet *killSet,
11246 StringUnorderedSet *paths
11247 )
11248{
11249 //printf("killSet count=%d\n",killSet ? (int)killSet->size() : -1);
11250 // strip trailing slashes
11251 if (s.empty()) return;
11252
11253 g_pathsVisited.clear();
11254
11255 FileInfo fi(s.str());
11256 //printf("readFileOrDirectory(%s)\n",s);
11257 {
11258 if (exclSet==nullptr || exclSet->find(fi.absFilePath())==exclSet->end())
11259 {
11260 if (Config_getBool(EXCLUDE_SYMLINKS) && fi.isSymLink())
11261 {
11262 }
11263 else if (!fi.exists() || !fi.isReadable())
11264 {
11265 if (errorIfNotExist)
11266 {
11267 warn_uncond("source '{}' is not a readable file or directory... skipping.\n",s);
11268 }
11269 }
11270 else if (fi.isFile())
11271 {
11272 std::string dirPath = fi.dirPath(true);
11273 std::string filePath = fi.absFilePath();
11274 if (paths && !dirPath.empty())
11275 {
11276 paths->insert(dirPath);
11277 }
11278 //printf("killSet.find(%s)=%d\n",qPrint(fi.absFilePath()),killSet.find(fi.absFilePath())!=killSet.end());
11279 if (killSet==nullptr || killSet->find(filePath)==killSet->end())
11280 {
11281 std::string name=fi.fileName();
11282 if (fnMap)
11283 {
11284 auto fd = createFileDef(dirPath+"/",name);
11285 if (!name.empty())
11286 {
11287 FileName *fn = fnMap->add(name);
11288 fn->push_back(std::move(fd));
11289 }
11290 }
11291 if (resultList || resultSet)
11292 {
11293 if (resultList) resultList->push_back(filePath);
11294 if (resultSet) resultSet->insert(filePath);
11295 }
11296
11297 if (killSet) killSet->insert(fi.absFilePath());
11298 }
11299 }
11300 else if (fi.isDir()) // readable dir
11301 {
11302 readDir(&fi,fnMap,exclSet,patList,
11303 exclPatList,resultList,resultSet,errorIfNotExist,
11304 recursive,killSet,paths);
11305 }
11306 }
11307 }
11308}
11309
11310//----------------------------------------------------------------------------
11311
11313{
11314 DString anchor;
11316 {
11317 MemberDef *md = toMemberDef(d);
11318 anchor=":"+md->anchor();
11319 }
11320 DString scope;
11321 DString fn = d->getOutputFileBase();
11324 {
11325 scope = fn;
11326 }
11327 t << "REPLACE INTO symbols (symbol_id,scope_id,name,file,line) VALUES('"
11328 << fn+anchor << "','"
11329 << scope << "','"
11330 << d->name() << "','"
11331 << d->getDefFileName() << "','"
11332 << d->getDefLine()
11333 << "');\n";
11334}
11335
11336static void dumpSymbolMap()
11337{
11338 std::ofstream f = Portable::openOutputStream("symbols.sql");
11339 if (f.is_open())
11340 {
11341 TextStream t(&f);
11342 for (const auto &[name,symList] : *Doxygen::symbolMap)
11343 {
11344 for (const auto &def : symList)
11345 {
11346 dumpSymbol(t,def);
11347 }
11348 }
11349 }
11350}
11351
11352// print developer options of Doxygen
11353static void devUsage()
11354{
11356 msg("Developer parameters:\n");
11357 msg(" -m dump symbol map\n");
11358 msg(" -b making messages output unbuffered\n");
11359 msg(" -c <file> process input file as a comment block and produce HTML output\n");
11360#if ENABLE_TRACING
11361 msg(" -t [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout)\n");
11362 msg(" -t_time [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout),\n"
11363 " and include time and thread information\n");
11364#endif
11365 msg(" -d <level> enable a debug level, such as (multiple invocations of -d are possible):\n");
11367}
11368
11369
11370//----------------------------------------------------------------------------
11371// print the version of Doxygen
11372
11373static void version(const bool extended)
11374{
11376 DString versionString = getFullVersion();
11377 msg("{}\n",versionString);
11378 if (extended)
11379 {
11380 DString extVers;
11381 if (!extVers.empty()) extVers+= ", ";
11382 extVers += "sqlite3 ";
11383 extVers += sqlite3_libversion();
11384#if USE_LIBCLANG
11385 if (!extVers.empty()) extVers+= ", ";
11386 extVers += "clang support ";
11387 extVers += CLANG_VERSION_STRING;
11388#endif
11389 if (!extVers.empty())
11390 {
11391 if (size_t lastComma = extVers.rfind(','); lastComma != DString::npos)
11392 {
11393 extVers = extVers.replace(lastComma,1," and");
11394 }
11395 msg(" with {}.\n",extVers);
11396 }
11397 }
11398}
11399
11400//----------------------------------------------------------------------------
11401// print the usage of Doxygen
11402
11403static void usage(const DString &name,const DString &versionString)
11404{
11406 msg("Doxygen version {0}\nCopyright Dimitri van Heesch 1997-2025\n\n"
11407 "You can use Doxygen in a number of ways:\n\n"
11408 "1) Use Doxygen to generate a template configuration file*:\n"
11409 " {1} [-s] -g [configName]\n\n"
11410 "2) Use Doxygen to update an old configuration file*:\n"
11411 " {1} [-s] -u [configName]\n\n"
11412 "3) Use Doxygen to generate documentation using an existing "
11413 "configuration file*:\n"
11414 " {1} [configName]\n\n"
11415 "4) Use Doxygen to generate a template file controlling the layout of the\n"
11416 " generated documentation:\n"
11417 " {1} -l [layoutFileName]\n\n"
11418 " In case layoutFileName is omitted DoxygenLayout.xml will be used as filename.\n"
11419 " If - is used for layoutFileName Doxygen will write to standard output.\n\n"
11420 "5) Use Doxygen to generate a template style sheet file for RTF, HTML or Latex.\n"
11421 " RTF: {1} -w rtf styleSheetFile\n"
11422 " HTML: {1} -w html headerFile footerFile styleSheetFile [configFile]\n"
11423 " LaTeX: {1} -w latex headerFile footerFile styleSheetFile [configFile]\n\n"
11424 "6) Use Doxygen to generate a rtf extensions file\n"
11425 " {1} -e rtf extensionsFile\n\n"
11426 " If - is used for extensionsFile Doxygen will write to standard output.\n\n"
11427 "7) Use Doxygen to compare the used configuration file with the template configuration file\n"
11428 " {1} -x [configFile]\n\n"
11429 " Use Doxygen to compare the used configuration file with the template configuration file\n"
11430 " without replacing the environment variables or CMake type replacement variables\n"
11431 " {1} -x_noenv [configFile]\n\n"
11432 "8) Use Doxygen to show a list of built-in emojis.\n"
11433 " {1} -f emoji outputFileName\n\n"
11434 " If - is used for outputFileName Doxygen will write to standard output.\n\n"
11435 "*) If -s is specified the comments of the configuration items in the config file will be omitted.\n"
11436 " If configName is omitted 'Doxyfile' will be used as a default.\n"
11437 " If - is used for configFile Doxygen will write / read the configuration to /from standard output / input.\n\n"
11438 "If -q is used for a Doxygen documentation run, Doxygen will see this as if QUIET=YES has been set.\n\n"
11439 "-v print version string, -V print extended version information\n"
11440 "-h,-? prints usage help information\n"
11441 "{1} -d prints additional usage flags for debugging purposes\n",versionString,name);
11442}
11443
11444//----------------------------------------------------------------------------
11445// read the argument of option 'c' from the comment argument list and
11446// update the option index 'optInd'.
11447
11448static const char *getArg(int argc,char **argv,int &optInd)
11449{
11450 char *s=nullptr;
11451 if (dstrlen(&argv[optInd][2])>0)
11452 s=&argv[optInd][2];
11453 else if (optInd+1<argc && argv[optInd+1][0]!='-')
11454 s=argv[++optInd];
11455 return s;
11456}
11457
11458//----------------------------------------------------------------------------
11459
11460/** @brief /dev/null outline parser */
11462{
11463 public:
11464 void parseInput(const DString &/* file */, const char * /* buf */,const std::shared_ptr<Entry> &, ClangTUParser*) override {}
11465 bool needsPreprocessing(const DString &) const override { return false; }
11466 void parsePrototype(const DString &) override {}
11467};
11468
11469
11470template<class T> std::function< std::unique_ptr<T>() > make_parser_factory()
11471{
11472 return []() { return std::make_unique<T>(); };
11473}
11474
11476{
11477 initResources();
11478 DString lang = Portable::getenv("LC_ALL");
11479 if (!lang.empty()) Portable::setenv("LANG",lang);
11480 std::setlocale(LC_ALL,"");
11481 std::setlocale(LC_CTYPE,"C"); // to get isspace(0xA0)==0, needed for UTF-8
11482 std::setlocale(LC_NUMERIC,"C");
11483
11485
11509
11510 // register any additional parsers here...
11511
11513
11514#if USE_LIBCLANG
11516#endif
11525 Doxygen::pageLinkedMap = new PageLinkedMap; // all doc pages
11526 Doxygen::exampleLinkedMap = new PageLinkedMap; // all examples
11527 //Doxygen::tagDestinationDict.setAutoDelete(true);
11529
11530 // initialization of these globals depends on
11531 // configuration switches so we need to postpone these
11532 Doxygen::globalScope = nullptr;
11542
11543}
11544
11577
11578void readConfiguration(int argc, char **argv)
11579{
11580 DString versionString = getFullVersion();
11581
11582 // helper that calls \a func to write to file \a fileName via a TextStream
11583 auto writeFile = [](const char *fileName,std::function<void(TextStream&)> func) -> bool
11584 {
11585 std::ofstream f;
11586 if (openOutputFile(fileName,f))
11587 {
11588 TextStream t(&f);
11589 func(t);
11590 return true;
11591 }
11592 return false;
11593 };
11594
11595
11596 /**************************************************************************
11597 * Handle arguments *
11598 **************************************************************************/
11599
11600 int optInd=1;
11601 DString configName;
11602 DString traceName;
11603 bool genConfig=false;
11604 bool shortList=false;
11605 bool traceTiming=false;
11607 bool updateConfig=false;
11608 bool quiet = false;
11609 while (optInd<argc && argv[optInd][0]=='-' &&
11610 (isalpha(argv[optInd][1]) || argv[optInd][1]=='?' ||
11611 argv[optInd][1]=='-')
11612 )
11613 {
11614 switch(argv[optInd][1])
11615 {
11616 case 'g':
11617 {
11618 genConfig=true;
11619 }
11620 break;
11621 case 'l':
11622 {
11623 DString layoutName;
11624 if (optInd+1>=argc)
11625 {
11626 layoutName="DoxygenLayout.xml";
11627 }
11628 else
11629 {
11630 layoutName=argv[optInd+1];
11631 }
11632 writeDefaultLayoutFile(layoutName);
11634 exit(0);
11635 }
11636 break;
11637 case 'c':
11638 if (optInd+1>=argc) // no file name given
11639 {
11640 msg("option \"-c\" is missing the file name to read\n");
11641 devUsage();
11643 exit(1);
11644 }
11645 else
11646 {
11647 g_commentFileName=argv[optInd+1];
11648 optInd++;
11649 }
11650 g_singleComment=true;
11651 quiet=true;
11652 break;
11653 case 'd':
11654 {
11655 DString debugLabel=getArg(argc,argv,optInd);
11656 if (debugLabel.empty())
11657 {
11658 devUsage();
11660 exit(0);
11661 }
11662 int retVal = Debug::setFlagStr(debugLabel);
11663 if (!retVal)
11664 {
11665 msg("option \"-d\" has unknown debug specifier: \"{}\".\n",debugLabel);
11666 devUsage();
11668 exit(1);
11669 }
11670 }
11671 break;
11672 case 't':
11673 {
11674#if ENABLE_TRACING
11675 if (!strcmp(argv[optInd]+1,"t_time"))
11676 {
11677 traceTiming = true;
11678 }
11679 else if (!strcmp(argv[optInd]+1,"t"))
11680 {
11681 traceTiming = false;
11682 }
11683 else
11684 {
11685 err("option should be \"-t\" or \"-t_time\", found: \"{}\".\n",argv[optInd]);
11687 exit(1);
11688 }
11689 if (optInd+1>=argc || argv[optInd+1][0] == '-') // no file name given
11690 {
11691 traceName="stdout";
11692 }
11693 else
11694 {
11695 traceName=argv[optInd+1];
11696 optInd++;
11697 }
11698#else
11699 err("support for option \"-t\" has not been compiled in (use a debug build or a release build with tracing enabled).\n");
11701 exit(1);
11702#endif
11703 }
11704 break;
11705 case 'x':
11706 if (!strcmp(argv[optInd]+1,"x_noenv")) diffList=Config::CompareMode::CompressedNoEnv;
11707 else if (!strcmp(argv[optInd]+1,"x")) diffList=Config::CompareMode::Compressed;
11708 else
11709 {
11710 err("option should be \"-x\" or \"-x_noenv\", found: \"{}\".\n",argv[optInd]);
11712 exit(1);
11713 }
11714 break;
11715 case 's':
11716 shortList=true;
11717 break;
11718 case 'u':
11719 updateConfig=true;
11720 break;
11721 case 'e':
11722 {
11723 DString formatName=getArg(argc,argv,optInd);
11724 if (formatName.empty())
11725 {
11726 err("option \"-e\" is missing format specifier rtf.\n");
11728 exit(1);
11729 }
11730 if (dstricmp(formatName.data(),"rtf")==0)
11731 {
11732 if (optInd+1>=argc)
11733 {
11734 err("option \"-e rtf\" is missing an extensions file name\n");
11736 exit(1);
11737 }
11738 writeFile(argv[optInd+1],RTFGenerator::writeExtensionsFile);
11740 exit(0);
11741 }
11742 err("option \"-e\" has invalid format specifier.\n");
11744 exit(1);
11745 }
11746 break;
11747 case 'f':
11748 {
11749 DString listName=getArg(argc,argv,optInd);
11750 if (listName.empty())
11751 {
11752 err("option \"-f\" is missing list specifier.\n");
11754 exit(1);
11755 }
11756 if (dstricmp(listName.data(),"emoji")==0)
11757 {
11758 if (optInd+1>=argc)
11759 {
11760 err("option \"-f emoji\" is missing an output file name\n");
11762 exit(1);
11763 }
11764 writeFile(argv[optInd+1],[](TextStream &t) { EmojiEntityMapper::instance().writeEmojiFile(t); });
11766 exit(0);
11767 }
11768 err("option \"-f\" has invalid list specifier.\n");
11770 exit(1);
11771 }
11772 break;
11773 case 'w':
11774 {
11775 DString formatName=getArg(argc,argv,optInd);
11776 if (formatName.empty())
11777 {
11778 err("option \"-w\" is missing format specifier rtf, html or latex\n");
11780 exit(1);
11781 }
11782 if (dstricmp(formatName.data(),"rtf")==0)
11783 {
11784 if (optInd+1>=argc)
11785 {
11786 err("option \"-w rtf\" is missing a style sheet file name\n");
11788 exit(1);
11789 }
11790 if (!writeFile(argv[optInd+1],RTFGenerator::writeStyleSheetFile))
11791 {
11792 err("error opening RTF style sheet file {}!\n",argv[optInd+1]);
11794 exit(1);
11795 }
11797 exit(0);
11798 }
11799 else if (dstricmp(formatName.data(),"html")==0)
11800 {
11801 Config::init();
11802 if (optInd+4<argc || FileInfo("Doxyfile").exists() || FileInfo("doxyfile").exists())
11803 // explicit config file mentioned or default found on disk
11804 {
11805 DString df = optInd+4<argc ? argv[optInd+4] : (FileInfo("Doxyfile").exists() ? DString("Doxyfile") : DString("doxyfile"));
11806 if (!Config::parse(df)) // parse the config file
11807 {
11808 err("error opening or reading configuration file {}!\n",argv[optInd+4]);
11810 exit(1);
11811 }
11812 }
11813 if (optInd+3>=argc)
11814 {
11815 err("option \"-w html\" does not have enough arguments\n");
11817 exit(1);
11818 }
11819 Config::postProcess(true);
11822 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11823 writeFile(argv[optInd+1],[&](TextStream &t) { HtmlGenerator::writeHeaderFile(t,argv[optInd+3]); });
11824 writeFile(argv[optInd+2],HtmlGenerator::writeFooterFile);
11825 writeFile(argv[optInd+3],HtmlGenerator::writeStyleSheetFile);
11827 exit(0);
11828 }
11829 else if (dstricmp(formatName.data(),"latex")==0)
11830 {
11831 Config::init();
11832 if (optInd+4<argc || FileInfo("Doxyfile").exists() || FileInfo("doxyfile").exists())
11833 {
11834 DString df = optInd+4<argc ? argv[optInd+4] : (FileInfo("Doxyfile").exists() ? DString("Doxyfile") : DString("doxyfile"));
11835 if (!Config::parse(df))
11836 {
11837 err("error opening or reading configuration file {}!\n",argv[optInd+4]);
11839 exit(1);
11840 }
11841 }
11842 if (optInd+3>=argc)
11843 {
11844 err("option \"-w latex\" does not have enough arguments\n");
11846 exit(1);
11847 }
11848 Config::postProcess(true);
11851 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11852 writeFile(argv[optInd+1],LatexGenerator::writeHeaderFile);
11853 writeFile(argv[optInd+2],LatexGenerator::writeFooterFile);
11854 writeFile(argv[optInd+3],LatexGenerator::writeStyleSheetFile);
11856 exit(0);
11857 }
11858 else
11859 {
11860 err("Illegal format specifier \"{}\": should be one of rtf, html or latex\n",formatName);
11862 exit(1);
11863 }
11864 }
11865 break;
11866 case 'm':
11867 g_dumpSymbolMap = true;
11868 break;
11869 case 'v':
11870 version(false);
11872 exit(0);
11873 break;
11874 case 'V':
11875 version(true);
11877 exit(0);
11878 break;
11879 case '-':
11880 if (dstrcmp(&argv[optInd][2],"help")==0)
11881 {
11882 usage(argv[0],versionString);
11883 exit(0);
11884 }
11885 else if (dstrcmp(&argv[optInd][2],"version")==0)
11886 {
11887 version(false);
11889 exit(0);
11890 }
11891 else if ((dstrcmp(&argv[optInd][2],"Version")==0) ||
11892 (dstrcmp(&argv[optInd][2],"VERSION")==0))
11893 {
11894 version(true);
11896 exit(0);
11897 }
11898 else
11899 {
11900 err("Unknown option \"-{}\"\n",&argv[optInd][1]);
11901 usage(argv[0],versionString);
11902 exit(1);
11903 }
11904 break;
11905 case 'b':
11906 setvbuf(stdout,nullptr,_IONBF,0);
11907 break;
11908 case 'q':
11909 quiet = true;
11910 break;
11911 case 'h':
11912 case '?':
11913 usage(argv[0],versionString);
11914 exit(0);
11915 break;
11916 default:
11917 err("Unknown option \"-{:c}\"\n",argv[optInd][1]);
11918 usage(argv[0],versionString);
11919 exit(1);
11920 }
11921 optInd++;
11922 }
11923
11924 /**************************************************************************
11925 * Parse or generate the config file *
11926 **************************************************************************/
11927
11928 initTracing(traceName.data(),traceTiming);
11929 TRACE("Doxygen version used: {}",getFullVersion());
11930 Config::init();
11931
11932 FileInfo configFileInfo1("Doxyfile"),configFileInfo2("doxyfile");
11933 if (optInd>=argc)
11934 {
11935 if (configFileInfo1.exists())
11936 {
11937 configName="Doxyfile";
11938 }
11939 else if (configFileInfo2.exists())
11940 {
11941 configName="doxyfile";
11942 }
11943 else if (genConfig)
11944 {
11945 configName="Doxyfile";
11946 }
11947 else
11948 {
11949 err("Doxyfile not found and no input file specified!\n");
11950 usage(argv[0],versionString);
11951 exit(1);
11952 }
11953 }
11954 else
11955 {
11956 FileInfo fi(argv[optInd]);
11957 if (fi.exists() || dstrcmp(argv[optInd],"-")==0 || genConfig)
11958 {
11959 configName=argv[optInd];
11960 }
11961 else
11962 {
11963 err("configuration file {} not found!\n",argv[optInd]);
11964 usage(argv[0],versionString);
11965 exit(1);
11966 }
11967 }
11968
11969 if (genConfig)
11970 {
11971 generateConfigFile(configName,shortList);
11973 exit(0);
11974 }
11975
11976 if (!Config::parse(configName,updateConfig,diffList))
11977 {
11978 err("could not open or read configuration file {}!\n",configName);
11980 exit(1);
11981 }
11982
11983 if (diffList!=Config::CompareMode::Full)
11984 {
11986 compareDoxyfile(diffList);
11988 exit(0);
11989 }
11990
11991 if (updateConfig)
11992 {
11994 generateConfigFile(configName,shortList,true);
11996 exit(0);
11997 }
11998
11999 /* Perlmod wants to know the path to the config file.*/
12000 FileInfo configFileInfo(configName.str());
12001 setPerlModDoxyfile(configFileInfo.absFilePath());
12002
12003 /* handle -q option */
12004 if (quiet) Config_updateBool(QUIET,true);
12005}
12006
12007/** check and resolve config options */
12009{
12010 AUTO_TRACE();
12011
12012 Config::postProcess(false);
12016}
12017
12018/** adjust globals that depend on configuration settings. */
12020{
12021 AUTO_TRACE();
12022 Doxygen::globalNamespaceDef = createNamespaceDef("<globalScope>",1,1,"<globalScope>");
12033
12034 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
12035
12036 /* Set the global html file extension. */
12037 Doxygen::htmlFileExtension = Config_getString(HTML_FILE_EXTENSION);
12038
12039
12041 Config_getBool(CALLER_GRAPH) ||
12042 Config_getBool(REFERENCES_RELATION) ||
12043 Config_getBool(REFERENCED_BY_RELATION);
12044
12045 /**************************************************************************
12046 * Add custom extension mappings
12047 **************************************************************************/
12048
12049 StringVector extMaps = Config_getList(EXTENSION_MAPPING);
12050 for (const auto &mapping : extMaps)
12051 {
12052 DString mapStr = mapping;
12053 if (size_t i=mapStr.find('='); i==DString::npos)
12054 {
12055 continue;
12056 }
12057 else
12058 {
12059 DString ext = mapStr.left(i).stripWhiteSpace().lower();
12060 DString language = mapStr.mid(i+1).stripWhiteSpace().lower();
12061 if (ext.empty() || language.empty())
12062 {
12063 continue;
12064 }
12065
12066 if (!updateLanguageMapping(ext,language))
12067 {
12068 err("Failed to map file extension '{}' to unsupported language '{}'.\n"
12069 "Check the EXTENSION_MAPPING setting in the config file.\n",
12070 ext,language);
12071 }
12072 else
12073 {
12074 msg("Adding custom extension mapping: '{}' will be treated as language '{}'\n",
12075 ext,language);
12076 }
12077 }
12078 }
12079 // create input file exncodings
12080
12081 // check INPUT_ENCODING
12082 void *cd = portable_iconv_open("UTF-8",Config_getString(INPUT_ENCODING).data());
12083 if (cd==reinterpret_cast<void *>(-1))
12084 {
12085 term("unsupported character conversion: '{}'->'UTF-8': {}\n"
12086 "Check the 'INPUT_ENCODING' setting in the config file!\n",
12087 Config_getString(INPUT_ENCODING),strerror(errno));
12088 }
12089 else
12090 {
12092 }
12093
12094 // check and split INPUT_FILE_ENCODING
12095 StringVector fileEncod = Config_getList(INPUT_FILE_ENCODING);
12096 for (const auto &mapping : fileEncod)
12097 {
12098 DString mapStr = mapping;
12099 if (size_t i=mapStr.find('='); i==DString::npos)
12100 {
12101 continue;
12102 }
12103 else
12104 {
12105 DString pattern = mapStr.left(i).stripWhiteSpace().lower();
12106 DString encoding = mapStr.mid(i+1).stripWhiteSpace().lower();
12107 if (pattern.empty() || encoding.empty())
12108 {
12109 continue;
12110 }
12111 cd = portable_iconv_open("UTF-8",encoding.data());
12112 if (cd==reinterpret_cast<void *>(-1))
12113 {
12114 term("unsupported character conversion: '{}'->'UTF-8': {}\n"
12115 "Check the 'INPUT_FILE_ENCODING' setting in the config file!\n",
12116 encoding,strerror(errno));
12117 }
12118 else
12119 {
12121 }
12122
12123 Doxygen::inputFileEncodingList.emplace_back(pattern, encoding);
12124 }
12125 }
12126
12127 // add predefined macro name to a dictionary
12128 StringVector expandAsDefinedList = Config_getList(EXPAND_AS_DEFINED);
12129 for (const auto &s : expandAsDefinedList)
12130 {
12132 }
12133
12134 // read aliases and store them in a dictionary
12135 readAliases();
12136
12137 // store number of spaces in a tab into Doxygen::spaces
12138 int tabSize = Config_getInt(TAB_SIZE);
12139 Doxygen::spaces.resize(tabSize);
12140 for (int sp=0; sp<tabSize; sp++) Doxygen::spaces.at(sp)=' ';
12141 Doxygen::spaces.at(tabSize)='\0';
12142}
12143
12144#ifdef HAS_SIGNALS
12145static void stopDoxygen(int)
12146{
12147 signal(SIGINT,SIG_DFL); // Re-register signal handler for default action
12148 Dir thisDir;
12149 msg("Cleaning up...\n");
12150 if (!Doxygen::filterDBFileName.empty())
12151 {
12152 thisDir.remove(Doxygen::filterDBFileName.str());
12153 }
12154 killpg(0,SIGINT);
12156 exitTracing();
12157 exit(1);
12158}
12159#endif
12160
12161static void writeTagFile()
12162{
12163 DString generateTagFile = Config_getString(GENERATE_TAGFILE);
12164 if (generateTagFile.empty()) return;
12165
12166 std::ofstream f = Portable::openOutputStream(generateTagFile);
12167 if (!f.is_open())
12168 {
12169 err("cannot open tag file {} for writing\n", generateTagFile);
12170 return;
12171 }
12172 TextStream tagFile(&f);
12173 tagFile << "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>\n";
12174 tagFile << "<tagfile doxygen_version=\"" << getDoxygenVersion() << "\"";
12175 std::string gitVersion = getGitVersion();
12176 if (!gitVersion.empty())
12177 {
12178 tagFile << " doxygen_gitid=\"" << gitVersion << "\"";
12179 }
12180 tagFile << ">\n";
12181
12182 // for each file
12183 for (const auto &fn : *Doxygen::inputNameLinkedMap)
12184 {
12185 for (const auto &fd : *fn)
12186 {
12187 if (fd->isLinkableInProject()) fd->writeTagFile(tagFile);
12188 }
12189 }
12190 // for each class
12191 for (const auto &cd : *Doxygen::classLinkedMap)
12192 {
12193 ClassDefMutable *cdm = toClassDefMutable(cd.get());
12194 if (cdm && cdm->isLinkableInProject())
12195 {
12196 cdm->writeTagFile(tagFile);
12197 }
12198 }
12199 // for each concept
12200 for (const auto &cd : *Doxygen::conceptLinkedMap)
12201 {
12202 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
12203 if (cdm && cdm->isLinkableInProject())
12204 {
12205 cdm->writeTagFile(tagFile);
12206 }
12207 }
12208 // for each namespace
12209 for (const auto &nd : *Doxygen::namespaceLinkedMap)
12210 {
12212 if (ndm && nd->isLinkableInProject())
12213 {
12214 ndm->writeTagFile(tagFile);
12215 }
12216 }
12217 // for each group
12218 for (const auto &gd : *Doxygen::groupLinkedMap)
12219 {
12220 if (gd->isLinkableInProject()) gd->writeTagFile(tagFile);
12221 }
12222 // for each module
12223 for (const auto &mod : ModuleManager::instance().modules())
12224 {
12225 if (mod->isLinkableInProject()) mod->writeTagFile(tagFile);
12226 }
12227 // for each page
12228 for (const auto &pd : *Doxygen::pageLinkedMap)
12229 {
12230 if (pd->isLinkableInProject()) pd->writeTagFile(tagFile);
12231 }
12232 // for requirements
12234 // for each directory
12235 for (const auto &dd : *Doxygen::dirLinkedMap)
12236 {
12237 if (dd->isLinkableInProject()) dd->writeTagFile(tagFile);
12238 }
12239 if (Doxygen::mainPage) Doxygen::mainPage->writeTagFile(tagFile);
12240
12241 tagFile << "</tagfile>\n";
12242}
12243
12244static void exitDoxygen() noexcept
12245{
12246 if (!g_successfulRun) // premature exit
12247 {
12248 Dir thisDir;
12249 msg("Exiting...\n");
12250 if (!Doxygen::filterDBFileName.empty())
12251 {
12252 thisDir.remove(Doxygen::filterDBFileName.str());
12253 }
12254 }
12255}
12256
12257static DString createOutputDirectory(const DString &baseDirName,
12258 const DString &formatDirName,
12259 const char *defaultDirName)
12260{
12261 DString result = formatDirName;
12262 if (result.empty())
12263 {
12264 result = baseDirName + defaultDirName;
12265 }
12266 else if (formatDirName[0]!='/' && (formatDirName.length()==1 || formatDirName[1]!=':'))
12267 {
12268 result.prepend(baseDirName+"/");
12269 }
12270 Dir formatDir(result.str());
12271 if (!formatDir.exists() && !formatDir.mkdir(result.str()))
12272 {
12273 term("Could not create output directory {}\n", result);
12274 }
12275 return result;
12276}
12277
12279{
12280 StringUnorderedSet killSet;
12281
12282 StringVector exclPatterns = Config_getList(EXCLUDE_PATTERNS);
12283 bool alwaysRecursive = Config_getBool(RECURSIVE);
12284 StringUnorderedSet excludeNameSet;
12285
12286 // gather names of all files in the include path
12287 g_s.begin("Searching for include files...\n");
12288 killSet.clear();
12289 StringVector includePathList = Config_getList(INCLUDE_PATH);
12290 for (const auto &s : includePathList)
12291 {
12292 size_t plSize = Config_getList(INCLUDE_FILE_PATTERNS).size();
12293 StringVector pl = plSize==0 ? Config_getList(FILE_PATTERNS) :
12294 Config_getList(INCLUDE_FILE_PATTERNS);
12295 readFileOrDirectory(s, // s
12297 nullptr, // exclSet
12298 &pl, // patList
12299 &exclPatterns, // exclPatList
12300 nullptr, // resultList
12301 nullptr, // resultSet
12302 false, // INCLUDE_PATH isn't recursive
12303 true, // errorIfNotExist
12304 &killSet); // killSet
12305 }
12306 g_s.end();
12307
12308 g_s.begin("Searching for example files...\n");
12309 killSet.clear();
12310 StringVector examplePathList = Config_getList(EXAMPLE_PATH);
12311 for (const auto &s : examplePathList)
12312 {
12313 StringVector patterns = Config_getList(EXAMPLE_PATTERNS);
12314 readFileOrDirectory(s, // s
12316 nullptr, // exclSet
12317 &patterns, // patList
12318 nullptr, // exclPatList
12319 nullptr, // resultList
12320 nullptr, // resultSet
12321 (alwaysRecursive || Config_getBool(EXAMPLE_RECURSIVE)), // recursive
12322 true, // errorIfNotExist
12323 &killSet); // killSet
12324 }
12325 g_s.end();
12326
12327 g_s.begin("Searching for images...\n");
12328 killSet.clear();
12329 StringVector imagePathList=Config_getList(IMAGE_PATH);
12330 for (const auto &s : imagePathList)
12331 {
12332 readFileOrDirectory(s, // s
12334 nullptr, // exclSet
12335 nullptr, // patList
12336 nullptr, // exclPatList
12337 nullptr, // resultList
12338 nullptr, // resultSet
12339 alwaysRecursive, // recursive
12340 true, // errorIfNotExist
12341 &killSet); // killSet
12342 }
12343 g_s.end();
12344
12345 g_s.begin("Searching for dot files...\n");
12346 killSet.clear();
12347 StringVector dotFileList=Config_getList(DOTFILE_DIRS);
12348 for (const auto &s : dotFileList)
12349 {
12350 readFileOrDirectory(s, // s
12352 nullptr, // exclSet
12353 nullptr, // patList
12354 nullptr, // exclPatList
12355 nullptr, // resultList
12356 nullptr, // resultSet
12357 alwaysRecursive, // recursive
12358 true, // errorIfNotExist
12359 &killSet); // killSet
12360 }
12361 g_s.end();
12362
12363 g_s.begin("Searching for msc files...\n");
12364 killSet.clear();
12365 StringVector mscFileList=Config_getList(MSCFILE_DIRS);
12366 for (const auto &s : mscFileList)
12367 {
12368 readFileOrDirectory(s, // s
12370 nullptr, // exclSet
12371 nullptr, // patList
12372 nullptr, // exclPatList
12373 nullptr, // resultList
12374 nullptr, // resultSet
12375 alwaysRecursive, // recursive
12376 true, // errorIfNotExist
12377 &killSet); // killSet
12378 }
12379 g_s.end();
12380
12381 g_s.begin("Searching for dia files...\n");
12382 killSet.clear();
12383 StringVector diaFileList=Config_getList(DIAFILE_DIRS);
12384 for (const auto &s : diaFileList)
12385 {
12386 readFileOrDirectory(s, // s
12388 nullptr, // exclSet
12389 nullptr, // patList
12390 nullptr, // exclPatList
12391 nullptr, // resultList
12392 nullptr, // resultSet
12393 alwaysRecursive, // recursive
12394 true, // errorIfNotExist
12395 &killSet); // killSet
12396 }
12397 g_s.end();
12398
12399 g_s.begin("Searching for plantuml files...\n");
12400 killSet.clear();
12401 StringVector plantUmlFileList=Config_getList(PLANTUMLFILE_DIRS);
12402 for (const auto &s : plantUmlFileList)
12403 {
12404 readFileOrDirectory(s, // s
12406 nullptr, // exclSet
12407 nullptr, // patList
12408 nullptr, // exclPatList
12409 nullptr, // resultList
12410 nullptr, // resultSet
12411 alwaysRecursive, // recursive
12412 true, // errorIfNotExist
12413 &killSet); // killSet
12414 }
12415 g_s.end();
12416
12417 g_s.begin("Searching for mermaid files...\n");
12418 killSet.clear();
12419 StringVector mermaidFileList=Config_getList(MERMAIDFILE_DIRS);
12420 for (const auto &s : mermaidFileList)
12421 {
12422 readFileOrDirectory(s, // s
12424 nullptr, // exclSet
12425 nullptr, // patList
12426 nullptr, // exclPatList
12427 nullptr, // resultList
12428 nullptr, // resultSet
12429 alwaysRecursive, // recursive
12430 true, // errorIfNotExist
12431 &killSet); // killSet
12432 }
12433 g_s.end();
12434
12435 g_s.begin("Searching for files to exclude\n");
12436 StringVector excludeList = Config_getList(EXCLUDE);
12437 for (const auto &s : excludeList)
12438 {
12439 StringVector filePatterns = Config_getList(FILE_PATTERNS);
12440 readFileOrDirectory(s, // s
12441 nullptr, // fnDict
12442 nullptr, // exclSet
12443 &filePatterns, // patList
12444 nullptr, // exclPatList
12445 nullptr, // resultList
12446 &excludeNameSet, // resultSet
12447 alwaysRecursive, // recursive
12448 false); // errorIfNotExist
12449 }
12450 g_s.end();
12451
12452 /**************************************************************************
12453 * Determine Input Files *
12454 **************************************************************************/
12455
12456 g_s.begin("Searching INPUT for files to process...\n");
12457 killSet.clear();
12458 Doxygen::inputPaths.clear();
12459 StringVector inputList=Config_getList(INPUT);
12460 for (const auto &s : inputList)
12461 {
12462 DString path = s;
12463 size_t l = path.length();
12464 if (l>0)
12465 {
12466 // strip trailing slashes
12467 if (path.at(l-1)=='\\' || path.at(l-1)=='/') path=path.left(l-1);
12468
12469 StringVector filePatterns = Config_getList(FILE_PATTERNS);
12471 path, // s
12473 &excludeNameSet, // exclSet
12474 &filePatterns, // patList
12475 &exclPatterns, // exclPatList
12476 &g_inputFiles, // resultList
12477 nullptr, // resultSet
12478 alwaysRecursive, // recursive
12479 true, // errorIfNotExist
12480 &killSet, // killSet
12481 &Doxygen::inputPaths); // paths
12482 }
12483 }
12484
12485 // Sort the FileDef objects by full path to get a predictable ordering over multiple runs
12486 for (auto &fileName : *Doxygen::inputNameLinkedMap)
12487 {
12488 if (fileName->size()>1)
12489 {
12490 std::stable_sort(fileName->begin(),fileName->end(),[](const auto &f1,const auto &f2)
12491 {
12492 return dstricmp_sort(f1->absFilePath(),f2->absFilePath())<0;
12493 });
12494 }
12495 }
12496 std::stable_sort(Doxygen::inputNameLinkedMap->begin(),
12498 [](const auto &f1,const auto &f2)
12499 {
12500 return dstricmp_sort(f1->front()->absFilePath(),f2->front()->absFilePath())<0;
12501 });
12502 if (Doxygen::inputNameLinkedMap->empty())
12503 {
12504 warn_uncond("No files to be processed, please check your settings, in particular INPUT, FILE_PATTERNS, and RECURSIVE\n");
12505 }
12506 g_s.end();
12507}
12508
12509
12511{
12512 if (Config_getBool(MARKDOWN_SUPPORT))
12513 {
12514 DString mdfileAsMainPage = Config_getString(USE_MDFILE_AS_MAINPAGE);
12515 if (mdfileAsMainPage.empty()) return;
12516 FileInfo fi(mdfileAsMainPage.data());
12517 if (!fi.exists())
12518 {
12519 warn_uncond("Specified markdown mainpage '{}' does not exist\n",mdfileAsMainPage);
12520 return;
12521 }
12522 bool ambig = false;
12523 if (Doxygen::inputNameLinkedMap->findFileDef(fi.absFilePath(),ambig)==nullptr)
12524 {
12525 warn_uncond("Specified markdown mainpage '{}' has not been defined as input file\n",mdfileAsMainPage);
12526 return;
12527 }
12528 }
12529}
12530
12532{
12533 AUTO_TRACE();
12534 std::atexit(exitDoxygen);
12535
12536 Portable::correctPath(Config_getList(EXTERNAL_TOOL_PATH));
12537
12538#if USE_LIBCLANG
12539 Doxygen::clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING);
12540#endif
12541
12542 // we would like to show the versionString earlier, but we first have to handle the configuration file
12543 // to know the value of the QUIET setting.
12544 DString versionString = getFullVersion();
12545 msg("Doxygen version used: {}\n",versionString);
12546
12548
12549 /**************************************************************************
12550 * Make sure the output directory exists
12551 **************************************************************************/
12552 DString outputDirectory = Config_getString(OUTPUT_DIRECTORY);
12553 if (!g_singleComment)
12554 {
12555 if (outputDirectory.empty())
12556 {
12557 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,Dir::currentDirPath());
12558 }
12559 else
12560 {
12561 Dir dir(outputDirectory.str());
12562 if (!dir.exists())
12563 {
12565 if (!dir.mkdir(outputDirectory.str()))
12566 {
12567 term("tag OUTPUT_DIRECTORY: Output directory '{}' does not "
12568 "exist and cannot be created\n",outputDirectory);
12569 }
12570 else
12571 {
12572 msg("Notice: Output directory '{}' does not exist. "
12573 "I have created it for you.\n", outputDirectory);
12574 }
12575 dir.setPath(outputDirectory.str());
12576 }
12577 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,dir.absPath());
12578 }
12579 }
12580 AUTO_TRACE_ADD("outputDirectory={}",outputDirectory);
12581
12582 /**************************************************************************
12583 * Initialize global lists and dictionaries
12584 **************************************************************************/
12585
12586#ifdef HAS_SIGNALS
12587 signal(SIGINT, stopDoxygen);
12588#endif
12589
12590 uint32_t pid = Portable::pid();
12591 Doxygen::filterDBFileName.sprintf("doxygen_filterdb_%d.tmp",pid);
12592 Doxygen::filterDBFileName.prepend(outputDirectory+"/");
12593
12594 /**************************************************************************
12595 * Check/create output directories *
12596 **************************************************************************/
12597
12598 bool generateHtml = Config_getBool(GENERATE_HTML);
12599 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
12600 bool generateXml = Config_getBool(GENERATE_XML);
12601 bool generateLatex = Config_getBool(GENERATE_LATEX);
12602 bool generateRtf = Config_getBool(GENERATE_RTF);
12603 bool generateMan = Config_getBool(GENERATE_MAN);
12604 bool generateSql = Config_getBool(GENERATE_SQLITE3);
12605 DString htmlOutput;
12606 DString docbookOutput;
12607 DString xmlOutput;
12608 DString latexOutput;
12609 DString rtfOutput;
12610 DString manOutput;
12611 DString sqlOutput;
12612
12613 if (!g_singleComment)
12614 {
12615 if (generateHtml)
12616 {
12617 htmlOutput = createOutputDirectory(outputDirectory,Config_getString(HTML_OUTPUT),"/html");
12618 Config_updateString(HTML_OUTPUT,htmlOutput);
12619
12620 DString sitemapUrl = Config_getString(SITEMAP_URL);
12621 bool generateSitemap = !sitemapUrl.empty();
12622 if (generateSitemap && !sitemapUrl.endsWith("/"))
12623 {
12624 Config_updateString(SITEMAP_URL,sitemapUrl+"/");
12625 }
12626
12627 // add HTML indexers that are enabled
12628 bool generateHtmlHelp = Config_getBool(GENERATE_HTMLHELP);
12629 bool generateEclipseHelp = Config_getBool(GENERATE_ECLIPSEHELP);
12630 bool generateQhp = Config_getBool(GENERATE_QHP);
12631 bool generateTreeView = Config_getBool(GENERATE_TREEVIEW);
12632 bool generateDocSet = Config_getBool(GENERATE_DOCSET);
12633 if (generateEclipseHelp) Doxygen::indexList->addIndex<EclipseHelp>();
12634 if (generateHtmlHelp) Doxygen::indexList->addIndex<HtmlHelp>();
12635 if (generateQhp) Doxygen::indexList->addIndex<Qhp>();
12636 if (generateSitemap) Doxygen::indexList->addIndex<Sitemap>();
12637 if (generateTreeView) Doxygen::indexList->addIndex<FTVHelp>(true);
12638 if (generateDocSet) Doxygen::indexList->addIndex<DocSets>();
12641 }
12642
12643 if (generateDocbook)
12644 {
12645 docbookOutput = createOutputDirectory(outputDirectory,Config_getString(DOCBOOK_OUTPUT),"/docbook");
12646 Config_updateString(DOCBOOK_OUTPUT,docbookOutput);
12647 }
12648
12649 if (generateXml)
12650 {
12651 xmlOutput = createOutputDirectory(outputDirectory,Config_getString(XML_OUTPUT),"/xml");
12652 Config_updateString(XML_OUTPUT,xmlOutput);
12653 }
12654
12655 if (generateLatex)
12656 {
12657 latexOutput = createOutputDirectory(outputDirectory,Config_getString(LATEX_OUTPUT), "/latex");
12658 Config_updateString(LATEX_OUTPUT,latexOutput);
12659 }
12660
12661 if (generateRtf)
12662 {
12663 rtfOutput = createOutputDirectory(outputDirectory,Config_getString(RTF_OUTPUT),"/rtf");
12664 Config_updateString(RTF_OUTPUT,rtfOutput);
12665 }
12666
12667 if (generateMan)
12668 {
12669 manOutput = createOutputDirectory(outputDirectory,Config_getString(MAN_OUTPUT),"/man");
12670 Config_updateString(MAN_OUTPUT,manOutput);
12671 }
12672
12673 if (generateSql)
12674 {
12675 sqlOutput = createOutputDirectory(outputDirectory,Config_getString(SQLITE3_OUTPUT),"/sqlite3");
12676 Config_updateString(SQLITE3_OUTPUT,sqlOutput);
12677 }
12678 }
12679
12680 if (Config_getBool(HAVE_DOT))
12681 {
12682 DString curFontPath = Config_getString(DOT_FONTPATH);
12683 if (curFontPath.empty())
12684 {
12685 Portable::getenv("DOTFONTPATH");
12686 DString newFontPath = ".";
12687 if (!curFontPath.empty())
12688 {
12689 newFontPath+=Portable::pathListSeparator();
12690 newFontPath+=curFontPath;
12691 }
12692 Portable::setenv("DOTFONTPATH",qPrint(newFontPath));
12693 }
12694 else
12695 {
12696 Portable::setenv("DOTFONTPATH",qPrint(curFontPath));
12697 }
12698 }
12699
12700 /**************************************************************************
12701 * Handle layout file *
12702 **************************************************************************/
12703
12705 DString layoutFileName = Config_getString(LAYOUT_FILE);
12706 bool defaultLayoutUsed = false;
12707 if (layoutFileName.empty())
12708 {
12709 layoutFileName = Config_updateString(LAYOUT_FILE,"DoxygenLayout.xml");
12710 defaultLayoutUsed = true;
12711 }
12712 AUTO_TRACE_ADD("defaultLayoutUsed={}, layoutFileName={}",defaultLayoutUsed,layoutFileName);
12713
12714 FileInfo fi(layoutFileName.str());
12715 if (fi.exists())
12716 {
12717 msg("Parsing layout file {}...\n",layoutFileName);
12718 LayoutDocManager::instance().parse(layoutFileName);
12719 }
12720 else if (!defaultLayoutUsed)
12721 {
12722 warn_uncond("failed to open layout file '{}' for reading! Using default settings.\n",layoutFileName);
12723 }
12724 printLayout();
12725
12726 /**************************************************************************
12727 * Read and preprocess input *
12728 **************************************************************************/
12729
12730 // prevent search in the output directories
12731 StringVector exclPatterns = Config_getList(EXCLUDE_PATTERNS);
12732 if (generateHtml) exclPatterns.push_back(htmlOutput.str());
12733 if (generateDocbook) exclPatterns.push_back(docbookOutput.str());
12734 if (generateXml) exclPatterns.push_back(xmlOutput.str());
12735 if (generateLatex) exclPatterns.push_back(latexOutput.str());
12736 if (generateRtf) exclPatterns.push_back(rtfOutput.str());
12737 if (generateMan) exclPatterns.push_back(manOutput.str());
12738 Config_updateList(EXCLUDE_PATTERNS,exclPatterns);
12739
12740 if (!g_singleComment)
12741 {
12743
12745 }
12746
12747 // Notice: the order of the function calls below is very important!
12748
12749 if (generateHtml && !Config_getBool(USE_MATHJAX))
12750 {
12752 }
12753 if (generateRtf)
12754 {
12756 }
12757 if (generateDocbook)
12758 {
12760 }
12761
12763
12764 /**************************************************************************
12765 * Handle Tag Files *
12766 **************************************************************************/
12767
12768 std::shared_ptr<Entry> root = std::make_shared<Entry>();
12769
12770 if (!g_singleComment)
12771 {
12772 msg("Reading and parsing tag files\n");
12773 StringVector tagFileList = Config_getList(TAGFILES);
12774 for (const auto &s : tagFileList)
12775 {
12776 readTagFile(root,s.c_str());
12777 }
12778 }
12779
12780 /**************************************************************************
12781 * Parse source files *
12782 **************************************************************************/
12783
12784 addSTLSupport(root);
12785
12786 g_s.begin("Parsing files\n");
12787 if (g_singleComment)
12788 {
12789 //printf("Parsing comment %s\n",qPrint(g_commentFileName));
12790 if (g_commentFileName=="-")
12791 {
12792 std::string text = fileToString(g_commentFileName).str();
12793 addTerminalCharIfMissing(text,'\n');
12794 generateHtmlForComment("stdin.md",text);
12795 }
12796 else if (FileInfo(g_commentFileName.str()).isFile())
12797 {
12798 std::string text;
12800 addTerminalCharIfMissing(text,'\n');
12802 }
12803 else
12804 {
12805 }
12807 exit(0);
12808 }
12809 else
12810 {
12811 if (Config_getInt(NUM_PROC_THREADS)==1)
12812 {
12814 }
12815 else
12816 {
12818 }
12819 }
12820 g_s.end();
12821
12822 /**************************************************************************
12823 * Gather information *
12824 **************************************************************************/
12825
12826 g_s.begin("Building macro definition list...\n");
12828 g_s.end();
12829
12830 g_s.begin("Building group list...\n");
12831 buildGroupList(root.get());
12832 organizeSubGroups(root.get());
12833 g_s.end();
12834
12835 g_s.begin("Building directory list...\n");
12837 findDirDocumentation(root.get());
12838 g_s.end();
12839
12840 g_s.begin("Building namespace list...\n");
12841 buildNamespaceList(root.get());
12842 findUsingDirectives(root.get());
12843 g_s.end();
12844
12845 g_s.begin("Building file list...\n");
12846 buildFileList(root.get());
12847 g_s.end();
12848
12849 g_s.begin("Building class list...\n");
12850 buildClassList(root.get());
12851 g_s.end();
12852
12853 g_s.begin("Building concept list...\n");
12854 buildConceptList(root.get());
12855 g_s.end();
12856
12857 // build list of using declarations here (global list)
12858 buildListOfUsingDecls(root.get());
12859 g_s.end();
12860
12861 g_s.begin("Computing nesting relations for classes...\n");
12863 g_s.end();
12864 // 1.8.2-20121111: no longer add nested classes to the group as well
12865 //distributeClassGroupRelations();
12866
12867 // calling buildClassList may result in cached relations that
12868 // become invalid after resolveClassNestingRelations(), that's why
12869 // we need to clear the cache here
12871 // we don't need the list of using declaration anymore
12872 g_usingDeclarations.clear();
12873
12874 g_s.begin("Associating documentation with classes...\n");
12875 buildClassDocList(root.get());
12876 g_s.end();
12877
12878 g_s.begin("Associating documentation with concepts...\n");
12879 buildConceptDocList(root.get());
12881 g_s.end();
12882
12883 g_s.begin("Associating documentation with modules...\n");
12884 findModuleDocumentation(root.get());
12885 g_s.end();
12886
12887 g_s.begin("Building example list...\n");
12888 buildExampleList(root.get());
12889 g_s.end();
12890
12891 g_s.begin("Searching for enumerations...\n");
12892 findEnums(root.get());
12893 g_s.end();
12894
12895 // Since buildVarList calls isVarWithConstructor
12896 // and this calls getResolvedClass we need to process
12897 // typedefs first so the relations between classes via typedefs
12898 // are properly resolved. See bug 536385 for an example.
12899 g_s.begin("Searching for documented typedefs...\n");
12900 buildTypedefList(root.get());
12901 g_s.end();
12902
12903 if (Config_getBool(OPTIMIZE_OUTPUT_SLICE))
12904 {
12905 g_s.begin("Searching for documented sequences...\n");
12906 buildSequenceList(root.get());
12907 g_s.end();
12908
12909 g_s.begin("Searching for documented dictionaries...\n");
12910 buildDictionaryList(root.get());
12911 g_s.end();
12912 }
12913
12914 g_s.begin("Searching for members imported via using declarations...\n");
12915 // this should be after buildTypedefList in order to properly import
12916 // used typedefs
12917 findUsingDeclarations(root.get(),true); // do for python packages first
12918 findUsingDeclarations(root.get(),false); // then the rest
12919 g_s.end();
12920
12921 g_s.begin("Searching for included using directives...\n");
12923 g_s.end();
12924
12925 g_s.begin("Searching for documented variables...\n");
12926 buildVarList(root.get());
12927 g_s.end();
12928
12929 g_s.begin("Building interface member list...\n");
12930 buildInterfaceAndServiceList(root.get()); // UNO IDL
12931
12932 g_s.begin("Building member list...\n"); // using class info only !
12933 buildFunctionList(root.get());
12934 g_s.end();
12935
12936 g_s.begin("Searching for friends...\n");
12937 findFriends();
12938 g_s.end();
12939
12940 g_s.begin("Searching for documented defines...\n");
12941 findDefineDocumentation(root.get());
12942 g_s.end();
12943
12944 g_s.begin("Computing class inheritance relations...\n");
12945 findClassEntries(root.get());
12947 g_s.end();
12948
12949 g_s.begin("Computing class usage relations...\n");
12951 g_s.end();
12952
12953 g_s.begin("Flushing cached template relations that have become invalid...\n");
12955 g_s.end();
12956
12957 g_s.begin("Warn for undocumented namespaces...\n");
12959 g_s.end();
12960
12961 g_s.begin("Computing class relations...\n");
12964 if (Config_getBool(OPTIMIZE_OUTPUT_VHDL))
12965 {
12967 }
12969 g_classEntries.clear();
12970 g_s.end();
12971
12972 g_s.begin("Add enum values to enums...\n");
12973 addEnumValuesToEnums(root.get());
12974 findEnumDocumentation(root.get());
12975 g_s.end();
12976
12977 g_s.begin("Searching for member function documentation...\n");
12978 findObjCMethodDefinitions(root.get());
12979 findMemberDocumentation(root.get()); // may introduce new members !
12980 findUsingDeclImports(root.get()); // may introduce new members !
12981 g_usingClassMap.clear();
12985 g_s.end();
12986
12987 // moved to after finding and copying documentation,
12988 // as this introduces new members see bug 722654
12989 g_s.begin("Creating members for template instances...\n");
12991 g_s.end();
12992
12993 g_s.begin("Searching for tag less structs...\n");
12995 g_s.end();
12996
12997 g_s.begin("Building page list...\n");
12998 buildPageList(root.get());
12999 g_s.end();
13000
13001 g_s.begin("Building requirements list...\n");
13002 buildRequirementsList(root.get());
13003 g_s.end();
13004
13005 g_s.begin("Search for main page...\n");
13006 findMainPage(root.get());
13007 findMainPageTagFiles(root.get());
13008 g_s.end();
13009
13010 g_s.begin("Computing page relations...\n");
13011 computePageRelations(root.get());
13013 g_s.end();
13014
13015 g_s.begin("Determining the scope of groups...\n");
13016 findGroupScope(root.get());
13017 g_s.end();
13018
13019 g_s.begin("Computing module relations...\n");
13020 auto &mm = ModuleManager::instance();
13021 mm.resolvePartitions();
13022 mm.resolveImports();
13023 mm.collectExportedSymbols();
13024 g_s.end();
13025
13026 auto memberNameComp = [](const MemberNameLinkedMap::Ptr &n1,const MemberNameLinkedMap::Ptr &n2)
13027 {
13028 return dstricmp_sort(n1->memberName().data()+getPrefixIndex(n1->memberName()),
13029 n2->memberName().data()+getPrefixIndex(n2->memberName())
13030 )<0;
13031 };
13032
13033 auto classComp = [](const ClassLinkedMap::Ptr &c1,const ClassLinkedMap::Ptr &c2)
13034 {
13035 if (Config_getBool(SORT_BY_SCOPE_NAME))
13036 {
13037 return dstricmp_sort(c1->name(), c2->name())<0;
13038 }
13039 else
13040 {
13041 int i = dstricmp_sort(c1->className(), c2->className());
13042 return i==0 ? dstricmp_sort(c1->name(), c2->name())<0 : i<0;
13043 }
13044 };
13045
13046 auto namespaceComp = [](const NamespaceLinkedMap::Ptr &n1,const NamespaceLinkedMap::Ptr &n2)
13047 {
13048 return dstricmp_sort(n1->name(),n2->name())<0;
13049 };
13050
13051 auto conceptComp = [](const ConceptLinkedMap::Ptr &c1,const ConceptLinkedMap::Ptr &c2)
13052 {
13053 return dstricmp_sort(c1->name(),c2->name())<0;
13054 };
13055
13056 g_s.begin("Sorting lists...\n");
13057 std::stable_sort(Doxygen::memberNameLinkedMap->begin(),
13059 memberNameComp);
13060 std::stable_sort(Doxygen::functionNameLinkedMap->begin(),
13062 memberNameComp);
13063 std::stable_sort(Doxygen::hiddenClassLinkedMap->begin(),
13065 classComp);
13066 std::stable_sort(Doxygen::classLinkedMap->begin(),
13068 classComp);
13069 std::stable_sort(Doxygen::conceptLinkedMap->begin(),
13071 conceptComp);
13072 std::stable_sort(Doxygen::namespaceLinkedMap->begin(),
13074 namespaceComp);
13075 g_s.end();
13076
13077 g_s.begin("Determining which enums are documented\n");
13079 g_s.end();
13080
13081 g_s.begin("Computing member relations...\n");
13084 g_s.end();
13085
13086 g_s.begin("Building full member lists recursively...\n");
13088 g_s.end();
13089
13090 g_s.begin("Adding members to member groups.\n");
13092 g_s.end();
13093
13094 if (Config_getBool(DISTRIBUTE_GROUP_DOC))
13095 {
13096 g_s.begin("Distributing member group documentation.\n");
13098 g_s.end();
13099 }
13100
13101 g_s.begin("Computing member references...\n");
13103 g_s.end();
13104
13105 if (Config_getBool(INHERIT_DOCS))
13106 {
13107 g_s.begin("Inheriting documentation...\n");
13109 g_s.end();
13110 }
13111
13112
13113 // compute the shortest possible names of all files
13114 // without losing the uniqueness of the file names.
13115 g_s.begin("Generating disk names...\n");
13117 g_s.end();
13118
13119 g_s.begin("Adding source references...\n");
13121 g_s.end();
13122
13123 g_s.begin("Adding xrefitems...\n");
13126 g_s.end();
13127
13128 g_s.begin("Adding requirements...\n");
13131 g_s.end();
13132
13133 g_s.begin("Sorting member lists...\n");
13135 g_s.end();
13136
13137 g_s.begin("Setting anonymous enum type...\n");
13139 g_s.end();
13140
13141 g_s.begin("Computing dependencies between directories...\n");
13143 g_s.end();
13144
13145 g_s.begin("Generating citations page...\n");
13147 g_s.end();
13148
13149 g_s.begin("Counting data structures...\n");
13151 g_s.end();
13152
13153 g_s.begin("Resolving user defined references...\n");
13155 g_s.end();
13156
13157 g_s.begin("Finding anchors and sections in the documentation...\n");
13159 g_s.end();
13160
13161 g_s.begin("Transferring function references...\n");
13163 g_s.end();
13164
13165 g_s.begin("Combining using relations...\n");
13167 g_s.end();
13168
13170 g_s.begin("Adding members to index pages...\n");
13172 addToIndices();
13173 g_s.end();
13174
13175 g_s.begin("Correcting members for VHDL...\n");
13177 g_s.end();
13178
13179 g_s.begin("Computing tooltip texts...\n");
13181 g_s.end();
13182
13183 if (Config_getBool(SORT_GROUP_NAMES))
13184 {
13185 std::stable_sort(Doxygen::groupLinkedMap->begin(),
13187 [](const auto &g1,const auto &g2)
13188 { return g1->groupTitle() < g2->groupTitle(); });
13189
13190 for (const auto &gd : *Doxygen::groupLinkedMap)
13191 {
13192 gd->sortSubGroups();
13193 }
13194 }
13195
13196 printNavTree(root.get(),0);
13198}
13199
13201{
13202 AUTO_TRACE();
13203 /**************************************************************************
13204 * Initialize output generators *
13205 **************************************************************************/
13206
13207 /// add extra languages for which we can only produce syntax highlighted code
13209
13210 //// dump all symbols
13211 if (g_dumpSymbolMap)
13212 {
13213 dumpSymbolMap();
13214 exit(0);
13215 }
13216
13217 bool generateHtml = Config_getBool(GENERATE_HTML);
13218 bool generateLatex = Config_getBool(GENERATE_LATEX);
13219 bool generateMan = Config_getBool(GENERATE_MAN);
13220 bool generateRtf = Config_getBool(GENERATE_RTF);
13221 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
13222
13223
13225 if (generateHtml)
13226 {
13230 }
13231 if (generateLatex)
13232 {
13235 }
13236 if (generateDocbook)
13237 {
13240 }
13241 if (generateMan)
13242 {
13245 }
13246 if (generateRtf)
13247 {
13250 }
13251 if (Config_getBool(USE_HTAGS))
13252 {
13253 Htags::useHtags = true;
13254 DString htmldir = Config_getString(HTML_OUTPUT);
13255 if (!Htags::execute(htmldir))
13256 err("USE_HTAGS is YES but htags(1) failed. \n");
13257 else if (!Htags::loadFilemap(htmldir))
13258 err("htags(1) ended normally but failed to load the filemap. \n");
13259 }
13260
13261 /**************************************************************************
13262 * Generate documentation *
13263 **************************************************************************/
13264
13265 g_s.begin("Generating style sheet...\n");
13266 //printf("writing style info\n");
13267 g_outputList->writeStyleInfo(0); // write first part
13268 g_s.end();
13269
13270 bool searchEngine = Config_getBool(SEARCHENGINE);
13271 bool serverBasedSearch = Config_getBool(SERVER_BASED_SEARCH);
13272
13273 g_s.begin("Generating search indices...\n");
13274 if (searchEngine && !serverBasedSearch && generateHtml)
13275 {
13277 }
13278
13279 // generate search indices (need to do this before writing other HTML
13280 // pages as these contain a drop down menu with options depending on
13281 // what categories we find in this function.
13282 if (generateHtml && searchEngine)
13283 {
13284 DString searchDirName = Config_getString(HTML_OUTPUT)+"/search";
13285 Dir searchDir(searchDirName.str());
13286 if (!searchDir.exists() && !searchDir.mkdir(searchDirName.str()))
13287 {
13288 term("Could not create search results directory '{}' $PWD='{}'\n",
13289 searchDirName,Dir::currentDirPath());
13290 }
13291 HtmlGenerator::writeSearchData(searchDirName);
13292 if (!serverBasedSearch) // client side search index
13293 {
13295 }
13296 }
13297 g_s.end();
13298
13299 // copy static stuff
13300 if (generateHtml)
13301 {
13303 copyLogo(Config_getString(HTML_OUTPUT),true);
13304 copyIcon(Config_getString(HTML_OUTPUT),true);
13305 copyExtraFiles(Config_getList(HTML_EXTRA_FILES),"HTML_EXTRA_FILES",Config_getString(HTML_OUTPUT),true);
13306 }
13307 if (generateLatex)
13308 {
13310 copyLogo(Config_getString(LATEX_OUTPUT),false);
13311 copyIcon(Config_getString(LATEX_OUTPUT),false);
13312 copyExtraFiles(Config_getList(LATEX_EXTRA_FILES),"LATEX_EXTRA_FILES",Config_getString(LATEX_OUTPUT),false);
13313 }
13314 if (generateDocbook)
13315 {
13316 copyLogo(Config_getString(DOCBOOK_OUTPUT),false);
13317 copyIcon(Config_getString(DOCBOOK_OUTPUT),false);
13318 }
13319 if (generateRtf)
13320 {
13321 copyLogo(Config_getString(RTF_OUTPUT),false);
13322 copyIcon(Config_getString(RTF_OUTPUT),false);
13323 copyExtraFiles(Config_getList(RTF_EXTRA_FILES),"RTF_EXTRA_FILES",Config_getString(RTF_OUTPUT),false);
13324 }
13325
13327 if (fm.hasFormulas() && generateHtml
13328 && !Config_getBool(USE_MATHJAX))
13329 {
13330 g_s.begin("Generating images for formulas in HTML...\n");
13331 fm.generateImages(Config_getString(HTML_OUTPUT), true, Config_getEnum(HTML_FORMULA_FORMAT)==HTML_FORMULA_FORMAT_t::svg ?
13333 g_s.end();
13334 }
13335 if (fm.hasFormulas() && generateRtf)
13336 {
13337 g_s.begin("Generating images for formulas in RTF...\n");
13339 g_s.end();
13340 }
13341
13342 if (fm.hasFormulas() && generateDocbook)
13343 {
13344 g_s.begin("Generating images for formulas in Docbook...\n");
13346 g_s.end();
13347 }
13348
13349 g_s.begin("Generating example documentation...\n");
13351 g_s.end();
13352
13353 g_s.begin("Generating file sources...\n");
13355 g_s.end();
13356
13357 g_s.begin("Counting members...\n");
13358 // needs to be done after generating the sources
13359 // but before generating the compound documentation, see bug #12233
13360 countMembers();
13361 g_s.end();
13362
13363 g_s.begin("Generating file documentation...\n");
13365 g_s.end();
13366
13367 g_s.begin("Generating page documentation...\n");
13369 g_s.end();
13370
13371 g_s.begin("Generating group documentation...\n");
13373 g_s.end();
13374
13375 g_s.begin("Generating class documentation...\n");
13377 g_s.end();
13378
13379 g_s.begin("Generating concept documentation...\n");
13381 g_s.end();
13382
13383 g_s.begin("Generating module documentation...\n");
13385 g_s.end();
13386
13387 g_s.begin("Generating namespace documentation...\n");
13389 g_s.end();
13390
13391 if (Config_getBool(GENERATE_LEGEND))
13392 {
13393 g_s.begin("Generating graph info page...\n");
13395 g_s.end();
13396 }
13397
13398 g_s.begin("Generating directory documentation...\n");
13400 g_s.end();
13401
13402 if (g_outputList->size()>0)
13403 {
13405 }
13406
13407 g_s.begin("finalizing index lists...\n");
13409 g_s.end();
13410
13411 g_s.begin("writing tag file...\n");
13412 writeTagFile();
13413 g_s.end();
13414
13415 if (Config_getBool(GENERATE_XML))
13416 {
13417 g_s.begin("Generating XML output...\n");
13419 generateXML();
13421 g_s.end();
13422 }
13423 if (Config_getBool(GENERATE_SQLITE3))
13424 {
13425 g_s.begin("Generating SQLITE3 output...\n");
13427 g_s.end();
13428 }
13429
13430 if (Config_getBool(GENERATE_AUTOGEN_DEF))
13431 {
13432 g_s.begin("Generating AutoGen DEF output...\n");
13433 generateDEF();
13434 g_s.end();
13435 }
13436 if (Config_getBool(GENERATE_PERLMOD))
13437 {
13438 g_s.begin("Generating Perl module output...\n");
13440 g_s.end();
13441 }
13442 if (generateHtml && searchEngine && serverBasedSearch)
13443 {
13444 g_s.begin("Generating search index\n");
13445 if (Doxygen::searchIndex.kind()==SearchIndexIntf::Internal) // write own search index
13446 {
13448 Doxygen::searchIndex.write(Config_getString(HTML_OUTPUT)+"/search/search.idx");
13449 }
13450 else // write data for external search index
13451 {
13453 DString searchDataFile = Config_getString(SEARCHDATA_FILE);
13454 if (searchDataFile.empty())
13455 {
13456 searchDataFile="searchdata.xml";
13457 }
13458 if (!Portable::isAbsolutePath(searchDataFile.data()))
13459 {
13460 searchDataFile.prepend(Config_getString(OUTPUT_DIRECTORY)+"/");
13461 }
13462 Doxygen::searchIndex.write(searchDataFile);
13463 }
13464 g_s.end();
13465 }
13466
13467 if (generateRtf)
13468 {
13469 g_s.begin("Combining RTF output...\n");
13470 if (!RTFGenerator::preProcessFileInplace(Config_getString(RTF_OUTPUT),"refman.rtf"))
13471 {
13472 err("An error occurred during post-processing the RTF files!\n");
13473 }
13474 g_s.end();
13475 }
13476
13477 if (PlantumlManager::instance().needToRun())
13478 {
13479 g_s.begin("Running plantuml with JAVA...\n");
13481 g_s.end();
13482 }
13483
13484 if (MermaidManager::instance().needToRun())
13485 {
13486 g_s.begin("Running mermaid (mmdc)...\n");
13488 g_s.end();
13489 }
13490
13491 if (Config_getBool(HAVE_DOT) && DotManager::instance()->needToRun())
13492 {
13493 g_s.begin("Running dot...\n");
13495 g_s.end();
13496 }
13497
13498 if (generateHtml &&
13499 Config_getBool(GENERATE_HTMLHELP) &&
13500 !Config_getString(HHC_LOCATION).empty())
13501 {
13502 g_s.begin("Running html help compiler...\n");
13504 g_s.end();
13505 }
13506
13507 if ( generateHtml &&
13508 Config_getBool(GENERATE_QHP) &&
13509 !Config_getString(QHG_LOCATION).empty())
13510 {
13511 g_s.begin("Running qhelpgenerator...\n");
13513 g_s.end();
13514 }
13515
13518
13520
13522 {
13523
13524 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
13525 if (numThreads<1) numThreads=1;
13526 msg("Total elapsed time: {:.6f} seconds\n(of which an average of {:.6f} seconds per thread waiting for external tools to finish)\n",
13527 (static_cast<double>(Debug::elapsedTime())),
13528 Portable::getSysElapsedTime()/static_cast<double>(numThreads)
13529 );
13530 g_s.print();
13531
13533 msg("finished...\n");
13535 }
13536 else
13537 {
13538 msg("finished...\n");
13539 }
13540
13541
13542 /**************************************************************************
13543 * Start cleaning up *
13544 **************************************************************************/
13545
13547
13549 Dir thisDir;
13550 thisDir.remove(Doxygen::filterDBFileName.str());
13552 exitTracing();
13554 delete Doxygen::clangUsrMap;
13555 g_successfulRun=true;
13556
13557 //dumpDocNodeSizes();
13558}
void readAliases()
Definition aliases.cpp:162
constexpr auto prefix
Definition anchor.cpp:47
This class contains the information about the argument of a function or template.
Definition arguments.h:27
DString defval
Definition arguments.h:47
DString array
Definition arguments.h:46
DString name
Definition arguments.h:45
DString type
Definition arguments.h:43
This class represents an function or template argument list.
Definition arguments.h:66
RefQualifierType refQualifier() const
Definition arguments.h:117
bool noParameters() const
Definition arguments.h:118
bool pureSpecifier() const
Definition arguments.h:114
iterator end()
Definition arguments.h:95
bool hasParameters() const
Definition arguments.h:77
DString trailingReturnType() const
Definition arguments.h:115
bool isDeleted() const
Definition arguments.h:116
size_t size() const
Definition arguments.h:101
void setPureSpecifier(bool b)
Definition arguments.h:122
bool constSpecifier() const
Definition arguments.h:112
void setTrailingReturnType(const DString &s)
Definition arguments.cpp:40
void push_back(const Argument &a)
Definition arguments.h:103
bool empty() const
Definition arguments.h:100
void setConstSpecifier(bool b)
Definition arguments.h:120
void setRefQualifier(RefQualifierType t)
Definition arguments.h:127
void setIsDeleted(bool b)
Definition arguments.h:126
iterator begin()
Definition arguments.h:94
bool volatileSpecifier() const
Definition arguments.h:113
void setNoParameters(bool b)
Definition arguments.h:128
void setVolatileSpecifier(bool b)
Definition arguments.h:121
static CitationManager & instance()
Definition cite.cpp:90
void clear()
clears the database
Definition cite.cpp:115
void generatePage()
Generate the citations page.
Definition cite.cpp:336
std::unique_ptr< ClangTUParser > createTUParser(const FileDef *fd) const
static ClangParser * instance()
Returns the one and only instance of the class.
Clang parser object for a single translation unit, which consists of a source file and the directly o...
Definition clangparser.h:25
void switchToFile(const FileDef *fd)
Switches to another file within the translation unit started with start().
void parse()
Parse the file given at construction time as a translation unit This file should already be preproces...
A abstract class representing of a compound symbol.
Definition classdef.h:100
virtual DString requiresClause() const =0
virtual const MemberDef * getMemberByName(const DString &) const =0
Returns the member with the given name.
virtual const ArgumentList & templateArguments() const =0
Returns the template arguments of this class.
virtual void writeDocumentation(OutputList &ol) const =0
virtual void writeMemberList(OutputList &ol) const =0
virtual bool isTemplate() const =0
Returns true if this class is a template.
virtual const BaseClassList & baseClasses() const =0
Returns the list of base classes from which this class directly inherits.
virtual int isBaseClass(const ClassDef *bcd, bool followInstances, const DString &templSpec=DString()) const =0
Returns true iff bcd is a direct or indirect base class of this class.
virtual const TemplateInstanceList & getTemplateInstances() const =0
Returns a sorted dictionary with all template instances found for this template class.
virtual ArgumentLists getTemplateParameterLists() const =0
Returns the template parameter lists that form the template declaration of this class.
virtual Protection protection() const =0
Return the protection level (Public,Protected,Private) in which this compound was found.
virtual DString compoundTypeString() const =0
Returns the type of compound as a string.
virtual MemberList * getMemberList(MemberListType lt) const =0
Returns the members in the list identified by lt.
virtual std::unique_ptr< ClassDef > deepCopy(const DString &name) const =0
virtual bool isForwardDeclared() const =0
Returns true if this class represents a forward declaration of a template class.
virtual bool isSubClass(ClassDef *bcd, int level=0) const =0
Returns true iff bcd is a direct or indirect sub class of this class.
virtual void moveTo(Definition *)=0
virtual const TemplateNameMap & getTemplateBaseClassNames() const =0
virtual DString qualifiedNameWithTemplateParameters(const ArgumentLists *actualParams=nullptr, uint32_t *actualParamIndex=nullptr) const =0
virtual bool isEmbeddedInOuterScope() const =0
virtual const MemberNameInfoLinkedMap & memberNameInfoLinkedMap() const =0
Returns a dictionary of all members.
virtual bool isImplicitTemplateInstance() const =0
virtual const ClassDef * templateMaster() const =0
Returns the template master of which this class is an instance.
CompoundType
The various compound types.
Definition classdef.h:105
@ Singleton
Definition classdef.h:113
@ Interface
Definition classdef.h:108
@ Exception
Definition classdef.h:111
virtual CompoundType compoundType() const =0
Returns the type of compound this is, i.e. class/struct/union/...
virtual bool containsOverload(const MemberDef *md) const =0
virtual ClassLinkedRefMap getClasses() const =0
returns the classes nested into this class
virtual FileDef * getFileDef() const =0
Returns the file in which this compound's definition can be found.
virtual void writeTagFile(TextStream &) const =0
virtual void writeDocumentationForInnerClasses(OutputList &ol) const =0
virtual void computeAnchors()=0
virtual void addTypeConstraints()=0
virtual void overrideCollaborationGraph(bool e)=0
virtual void addUsedClass(ClassDef *cd, const DString &accessName, Protection prot)=0
virtual void insertExplicitTemplateInstance(ClassDef *instance, const DString &spec)=0
virtual void countMembers()=0
virtual void addMembersToMemberGroup()=0
virtual void setPrimaryConstructorParams(const ArgumentList &list)=0
virtual void setTemplateBaseClassNames(const TemplateNameMap &templateNames)=0
virtual ClassDef * insertTemplateInstance(const DString &fileName, int startLine, size_t startColumn, const DString &templSpec, bool &freshInstance)=0
virtual void setClassName(const DString &name)=0
virtual void setFileDef(FileDef *fd)=0
virtual void reclassifyMember(MemberDefMutable *md, MemberType t)=0
virtual void setTemplateArguments(const ArgumentList &al)=0
virtual void setTemplateMaster(const ClassDef *tm)=0
virtual void makeTemplateArgument(bool b=true)=0
virtual void mergeCategory(ClassDef *category)=0
virtual void addQualifiers(const StringVector &qualifiers)=0
virtual void setClassSpecifier(TypeSpecifier spec)=0
virtual void insertUsedFile(const FileDef *)=0
virtual void setTagLessReference(const ClassDef *cd)=0
virtual void setUsedOnly(bool b)=0
virtual void sortMemberLists()=0
virtual void setProtection(Protection p)=0
virtual void setTypeConstraints(const ArgumentList &al)=0
virtual void overrideInheritanceGraph(CLASS_GRAPH_t e)=0
virtual void setAnonymousEnumType()=0
virtual void setCompoundType(CompoundType t)=0
virtual void distributeMemberGroupDocumentation()=0
virtual void setMetaData(const DString &md)=0
virtual void findSectionsInDocumentation()=0
virtual void insertMember(MemberDef *)=0
virtual void sortAllMembersList()=0
virtual void mergeMembers()=0
virtual void setIsStatic(bool b)=0
virtual void addMembersToTemplateInstance(const ClassDef *cd, const ArgumentList &templateArguments, const DString &templSpec)=0
virtual void insertSubClass(ClassDef *, Protection p, Specifier s, const DString &t=DString())=0
virtual void addUsedByClass(ClassDef *cd, const DString &accessName, Protection prot)=0
virtual void setSubGrouping(bool enabled)=0
virtual void setRequiresClause(const DString &req)=0
virtual void insertBaseClass(ClassDef *, const DString &name, Protection p, Specifier s, const DString &t=DString())=0
virtual void addDocPart(const DString &doc, int lineNr, size_t colNr)=0
virtual void addCodePart(const DString &code, int lineNr, size_t colNr)=0
virtual void setFileDef(FileDef *fd)=0
virtual void writeTagFile(TextStream &)=0
virtual void writeDocumentation(OutputList &ol)=0
virtual void setGroupId(int id)=0
virtual void setInitializer(const DString &init)=0
virtual void findSectionsInDocumentation()=0
virtual void setTemplateArguments(const ArgumentList &al)=0
A String class for use with Doxygen wrapping std::string and adding some additional functionality off...
Definition dstring.h:84
void clear()
Definition dstring.h:214
DString & setNum(short n)
Definition dstring.h:552
void resize(size_t newlen)
Definition dstring.h:209
size_t rfind(char c, size_t pos=npos) const
Definition dstring.h:244
DString fill(char c, size_t len)
Fills a string with a predefined character.
Definition dstring.h:278
DString mid(size_t index, size_t len=npos) const
Definition dstring.h:318
DString lower() const
Definition dstring.h:326
bool empty() const
Returns true iff the string is empty (std::string compatible alias for isEmpty()).
Definition dstring.h:148
DString & replace(size_t index, size_t len, const char *s)
Definition dstring.cpp:154
DString substr(size_t pos=0, size_t count=npos) const
Returns a substring of length count starting at pos.
Definition dstring.h:223
static constexpr size_t npos
value used to indicate 'not found' or 'to the end of the string', matching std::string::npos
Definition dstring.h:178
char & at(size_t i)
Returns a reference to the character at index i.
Definition dstring.h:686
bool findAndRemoveWord(const char *word)
removes occurrences of whole word from this string, while keeping internal spaces and reducing multip...
Definition dstring.cpp:660
DString right(size_t len) const
Definition dstring.h:311
size_t size() const
Returns the length of the string, not counting the 0-terminator.
Definition dstring.h:154
DString & prepend(const char *s)
Definition dstring.h:515
int contains(char c, bool cs=true) const
Definition dstring.cpp:85
size_t find(char c, size_t pos=0) const
Definition dstring.h:239
DString & sprintf(const char *format,...)
Definition dstring.cpp:34
int toInt(bool *ok=nullptr, int base=10) const
Definition dstring.cpp:191
@ ExplicitSize
Definition dstring.h:131
DString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
Definition dstring.h:337
DString left(size_t len) const
Definition dstring.h:306
const std::string & str() const
Definition dstring.h:645
bool stripPrefix(const DString &prefix)
Definition dstring.h:290
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition dstring.h:157
bool startsWith(const char *s) const
Definition dstring.h:600
bool endsWith(const char *s) const
Definition dstring.h:617
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition dstring.h:151
@ ExtCmd
Definition debug.h:37
@ Sections
Definition debug.h:49
@ Time
Definition debug.h:36
@ Qhp
Definition debug.h:45
@ Entries
Definition debug.h:48
static void printFlags()
Definition debug.cpp:137
static void clearFlag(const DebugMask mask)
Definition debug.cpp:122
static bool isFlagSet(const DebugMask mask)
Definition debug.cpp:132
static void print(DebugMask mask, int prio, fmt::format_string< Args... > fmt, Args &&... args)
Definition debug.h:78
static double elapsedTime()
Definition debug.cpp:200
static void startTimer()
Definition debug.cpp:195
static bool setFlagStr(const DString &label)
Definition debug.cpp:103
static void setFlag(const DebugMask mask)
Definition debug.cpp:117
The common base class of all entity definitions found in the sources.
Definition definition.h:77
virtual DString briefDescription(bool abbreviate=false) const =0
virtual DString inbodyFile() const =0
virtual int getEndBodyLine() const =0
virtual DString briefFile() const =0
virtual SrcLangExt getLanguage() const =0
Returns the programming language this definition was written in.
virtual int docLine() const =0
virtual DString getDefFileName() const =0
virtual DString documentation() const =0
virtual bool isLinkable() const =0
virtual DString inbodyDocumentation() const =0
virtual int getDefLine() const =0
virtual DefType definitionType() const =0
virtual const DString & name() const =0
virtual const DString & localName() const =0
virtual int inbodyLine() const =0
virtual const FileDef * getBodyDef() const =0
virtual int briefLine() const =0
virtual bool hasDocumentation() const =0
virtual bool isLinkableInProject() const =0
virtual size_t getDefColumn() const =0
virtual bool isAnonymous() const =0
virtual DString displayName(bool includeScope=true) const =0
virtual DString qualifiedName() const =0
virtual bool isHidden() const =0
virtual DString anchor() const =0
virtual int getStartDefLine() const =0
virtual const GroupList & partOfGroups() const =0
virtual bool isArtificial() const =0
virtual Definition * getOuterScope() const =0
virtual DString docFile() const =0
virtual int getStartBodyLine() const =0
virtual bool isReference() const =0
virtual const Definition * findInnerCompound(const DString &name) const =0
virtual DString getOutputFileBase() const =0
virtual void mergeReferencedBy(const Definition *other)=0
virtual void setName(const DString &name)=0
virtual void setExported(bool b)=0
virtual void setBodySegment(int defLine, int bls, int ble)=0
virtual void setHidden(bool b)=0
virtual void setReference(const DString &r)=0
virtual void mergeReferences(const Definition *other)=0
virtual void setInbodyDocumentation(const DString &d, const DString &docFile, int docLine)=0
virtual void setDocumentation(const DString &d, const DString &docFile, int docLine, bool stripWhiteSpace=true)=0
virtual void addInnerCompound(Definition *d)=0
virtual void setDefFile(const DString &df, int defLine, size_t defColumn)=0
virtual void addSectionsToDefinition(const std::vector< const SectionInfo * > &anchorList)=0
virtual void setLanguage(SrcLangExt lang)=0
virtual void setOuterScope(Definition *d)=0
virtual void setArtificial(bool b)=0
virtual void setBriefDescription(const DString &b, const DString &briefFile, int briefLine)=0
virtual void makePartOfGroup(GroupDef *gd)=0
virtual void setBodyDef(const FileDef *fd)=0
virtual void setRequirementReferences(const RequirementRefs &rqli)=0
virtual void setId(const DString &name)=0
virtual void setRefItems(const RefItemVector &sli)=0
virtual void computeTooltip()=0
A model of a directory symbol.
Definition dirdef.h:108
virtual void overrideDirectoryGraph(bool e)=0
Class representing a directory in the file system.
Definition dir.h:73
static std::string currentDirPath()
Definition dir.cpp:348
std::string absPath() const
Definition dir.cpp:370
bool mkdir(const std::string &path, bool acceptsAbsPath=true) const
Definition dir.cpp:301
void setPath(const std::string &path)
Definition dir.cpp:235
bool remove(const std::string &path, bool acceptsAbsPath=true) const
Definition dir.cpp:320
DirIterator iterator() const
Definition dir.cpp:245
static std::string cleanDirPath(const std::string &path)
Definition dir.cpp:363
static bool setCurrent(const std::string &path)
Definition dir.cpp:356
bool exists() const
Definition dir.cpp:263
A linked map of directories.
Definition dirdef.h:173
A class that generates docset files.
Definition docsets.h:35
static void init()
bool run()
Definition dot.cpp:119
static DotManager * instance()
Definition dot.cpp:78
static NamespaceLinkedMap * namespaceLinkedMap
Definition doxygen.h:108
static ConceptLinkedMap * conceptLinkedMap
Definition doxygen.h:90
static bool suppressDocWarnings
Definition doxygen.h:123
static FileNameLinkedMap * plantUmlFileNameLinkedMap
Definition doxygen.h:102
static bool parseSourcesNeeded
Definition doxygen.h:116
static StringUnorderedSet inputPaths
Definition doxygen.h:96
static std::unique_ptr< PageDef > mainPage
Definition doxygen.h:93
static bool clangAssistedParsing
Definition doxygen.h:129
static StringUnorderedSet expandAsDefinedSet
Definition doxygen.h:112
static FileNameLinkedMap * inputNameLinkedMap
Definition doxygen.h:97
static DString filterDBFileName
Definition doxygen.h:124
static ParserManager * parserManager
Definition doxygen.h:122
static InputFileEncodingList inputFileEncodingList
Definition doxygen.h:131
static DString verifiedDotPath
Definition doxygen.h:130
static ClassLinkedMap * classLinkedMap
Definition doxygen.h:88
static MemberNameLinkedMap * functionNameLinkedMap
Definition doxygen.h:105
static PageLinkedMap * exampleLinkedMap
Definition doxygen.h:91
static FileNameLinkedMap * dotFileNameLinkedMap
Definition doxygen.h:99
static NamespaceDefMutable * globalScope
Definition doxygen.h:114
static FileNameLinkedMap * imageNameLinkedMap
Definition doxygen.h:98
static FileNameLinkedMap * mscFileNameLinkedMap
Definition doxygen.h:100
static FileNameLinkedMap * mermaidFileNameLinkedMap
Definition doxygen.h:103
static MemberGroupInfoMap memberGroupInfoMap
Definition doxygen.h:111
static IndexList * indexList
Definition doxygen.h:125
static StaticInitMap staticInitMap
Definition doxygen.h:134
static StringMap tagDestinationMap
Definition doxygen.h:109
static std::mutex countFlowKeywordsMutex
Definition doxygen.h:132
static ClassLinkedMap * hiddenClassLinkedMap
Definition doxygen.h:89
static FileNameLinkedMap * diaFileNameLinkedMap
Definition doxygen.h:101
static DString spaces
Definition doxygen.h:126
static PageLinkedMap * pageLinkedMap
Definition doxygen.h:92
static bool generatingXmlOutput
Definition doxygen.h:127
static std::unique_ptr< NamespaceDef > globalNamespaceDef
Definition doxygen.h:113
static DString htmlFileExtension
Definition doxygen.h:115
static DefinesPerFileList macroDefinitions
Definition doxygen.h:128
static DirLinkedMap * dirLinkedMap
Definition doxygen.h:120
static NamespaceAliasInfoMap namespaceAliasMap
Definition doxygen.h:106
static MemberNameLinkedMap * memberNameLinkedMap
Definition doxygen.h:104
static SymbolMap< Definition > * symbolMap
Definition doxygen.h:118
static StringUnorderedSet tagFileSet
Definition doxygen.h:110
static FileNameLinkedMap * includeNameLinkedMap
Definition doxygen.h:94
static FileNameLinkedMap * exampleNameLinkedMap
Definition doxygen.h:95
static SearchIndexIntf searchIndex
Definition doxygen.h:117
static DirRelationLinkedMap dirRelations
Definition doxygen.h:121
static std::mutex addExampleMutex
Definition doxygen.h:133
static ClangUsrMap * clangUsrMap
Definition doxygen.h:119
static GroupLinkedMap * groupLinkedMap
Definition doxygen.h:107
Generator for Eclipse help files.
Definition eclipsehelp.h:42
static EmojiEntityMapper & instance()
Returns the one and only instance of the Emoji entity mapper.
Definition emoji.cpp:1981
void writeEmojiFile(TextStream &t)
Writes the list of supported emojis to the given file.
Definition emoji.cpp:2002
Represents an unstructured piece of information, about an entity found in the sources.
Definition entry.h:115
TextStream initializer
initial value (for variables)
Definition entry.h:197
VhdlSpecifier vhdlSpec
VHDL specifiers.
Definition entry.h:182
bool subGrouping
automatically group class members?
Definition entry.h:187
RequirementRefs rqli
references to requirements
Definition entry.h:227
const std::vector< std::shared_ptr< Entry > > & children() const
Definition entry.h:138
bool proto
prototype ?
Definition entry.h:186
GroupDocType groupDocType
Definition entry.h:231
int docLine
line number at which the documentation was found
Definition entry.h:201
DString includeName
include name (3 arg of \class)
Definition entry.h:199
DString bitfields
member's bit fields
Definition entry.h:192
ArgumentList typeConstr
where clause (C#) for type constraints
Definition entry.h:215
void markAsProcessed() const
Definition entry.h:166
int endBodyLine
line number where the definition ends
Definition entry.h:218
DString write
property write accessor
Definition entry.h:212
bool exported
is the symbol exported from a C++20 module
Definition entry.h:188
const TagInfo * tagInfo() const
Definition entry.h:176
DString includeFile
include file (2 arg of \class, must be unique)
Definition entry.h:198
DString fileName
file this entry was extracted from
Definition entry.h:223
ArgumentLists tArgLists
template argument declarations
Definition entry.h:195
DString docFile
file in which the documentation was found
Definition entry.h:202
LocalToc localToc
Definition entry.h:233
MethodTypes mtype
signal, slot, (dcop) method, or property?
Definition entry.h:180
@ GROUPDOC_NORMAL
defgroup
Definition entry.h:120
DString args
member argument string
Definition entry.h:191
SrcLangExt lang
programming language in which this entry was found
Definition entry.h:228
Entry * parent() const
Definition entry.h:133
DString inside
name of the class in which documents are found
Definition entry.h:213
DString doc
documentation block (partly parsed)
Definition entry.h:200
DString req
C++20 requires clause.
Definition entry.h:235
bool explicitExternal
explicitly defined as external?
Definition entry.h:185
DString brief
brief description (doc block)
Definition entry.h:203
std::vector< const SectionInfo * > anchors
list of anchors defined in this entry
Definition entry.h:222
RelatesType relatesType
how relates is handled
Definition entry.h:210
std::vector< Grouping > groups
list of groups this entry belongs to
Definition entry.h:221
CommandOverrides commandOverrides
store info for commands whose default can be overridden
Definition entry.h:189
int startLine
start line of entry in the source
Definition entry.h:224
size_t startColumn
start column of entry in the source
Definition entry.h:225
ArgumentList argList
member arguments as a list
Definition entry.h:194
DString type
member type
Definition entry.h:172
int inbodyLine
line number at which the body doc was found
Definition entry.h:207
EntryType section
entry type (see Sections);
Definition entry.h:171
int bodyLine
line number of the body in the source
Definition entry.h:216
DString exception
throw specification
Definition entry.h:214
DString relates
related class (doc block)
Definition entry.h:209
int mGrpId
member group id
Definition entry.h:219
std::vector< BaseInfo > extends
list of base classes
Definition entry.h:220
DString inbodyFile
file in which the body doc was found
Definition entry.h:208
Specifier virt
virtualness of the entry
Definition entry.h:190
DString metaData
Slice metadata.
Definition entry.h:234
std::vector< std::string > qualifiers
qualifiers specified with the qualifier command
Definition entry.h:236
DString name
member name
Definition entry.h:173
RefItemVector sli
special lists (test/todo/bug/deprecated/..) this entry is in
Definition entry.h:226
Protection protection
class protection
Definition entry.h:179
bool artificial
Artificially introduced item.
Definition entry.h:230
bool hidden
does this represent an entity that is hidden from the output
Definition entry.h:229
DString inbodyDocs
documentation inside the body of a function
Definition entry.h:206
int briefLine
line number at which the brief desc. was found
Definition entry.h:204
FileDef * fileDef() const
Definition entry.h:168
DString id
libclang id
Definition entry.h:232
int initLines
define/variable initializer lines to show
Definition entry.h:183
bool isStatic
static ?
Definition entry.h:184
TypeSpecifier spec
class/member specifiers
Definition entry.h:181
DString briefFile
file in which the brief desc. was found
Definition entry.h:205
DString read
property read accessor
Definition entry.h:211
Wrapper class for the Entry type.
Definition types.h:856
constexpr bool isCompoundDoc() const noexcept
Definition types.h:866
constexpr bool isFile() const noexcept
Definition types.h:865
constexpr bool isDoc() const noexcept
Definition types.h:867
ENTRY_TYPES constexpr bool isCompound() const noexcept
Definition types.h:863
std::string to_string() const
Definition types.h:868
static EntryType guessSection(const DString &name)
Definition types.cpp:22
constexpr bool isScope() const noexcept
Definition types.h:864
A class that generates a dynamic tree view side panel.
Definition ftvhelp.h:41
A model of a file symbol.
Definition filedef.h:97
virtual void addUsingDeclaration(const Definition *d)=0
virtual void removeMember(MemberDef *md)=0
virtual void insertClass(ClassDef *cd)=0
virtual void setDiskName(const DString &name)=0
virtual void insertConcept(ConceptDef *cd)=0
virtual void overrideIncludeGraph(bool e)=0
virtual void writeSourceHeader(OutputList &ol)=0
virtual bool generateSourceFile() const =0
virtual DString absFilePath() const =0
virtual const DString & docName() const =0
virtual const LinkedRefMap< NamespaceDef > & getUsedNamespaces() const =0
virtual bool isSource() const =0
virtual void parseSource(ClangTUParser *clangParser)=0
virtual void getAllIncludeFilesRecursively(StringVector &incFiles) const =0
virtual void writeSourceFooter(OutputList &ol)=0
virtual void writeSourceBody(OutputList &ol, ClangTUParser *clangParser)=0
virtual void addUsingDirective(NamespaceDef *nd)=0
virtual void overrideIncludedByGraph(bool e)=0
virtual void insertMember(MemberDef *md)=0
virtual void insertNamespace(NamespaceDef *nd)=0
Minimal replacement for QFileInfo.
Definition fileinfo.h:26
std::string readLink() const
Definition fileinfo.cpp:88
bool isRelative() const
Definition fileinfo.cpp:62
bool isSymLink() const
Definition fileinfo.cpp:81
bool exists() const
Definition fileinfo.cpp:34
bool match(const PatternList &patList, bool caseSenseNames, PatternElem *elem=nullptr, PatternGet getter=[](const std::string &s){ return s;}) const
Match the file name against a list of patterns.
Definition fileinfo.h:60
std::string fileName() const
Definition fileinfo.cpp:122
bool isReadable() const
Definition fileinfo.cpp:48
bool isDir() const
Definition fileinfo.cpp:74
bool isFile() const
Definition fileinfo.cpp:67
std::string dirPath(bool absPath=true) const
Definition fileinfo.cpp:141
std::string absFilePath() const
Definition fileinfo.cpp:105
Class representing all files with a certain base name.
Definition filename.h:31
Ordered dictionary of FileName objects.
Definition filename.h:70
DString showFileDefMatches(const DString &n) const
Returns a list of file definitions in fnMap that match the file name n.
Definition filename.cpp:131
FileDef * findFileDef(const DString &n, bool &ambig) const
Returns the file definition in fnMap that matches the file name n.
Definition filename.cpp:38
bool hasFormulas() const
Definition formula.cpp:720
void initFromRepository(const DString &dir)
Definition formula.cpp:60
void checkRepositories()
Definition formula.cpp:173
static FormulaManager & instance()
Definition formula.cpp:54
void generateImages(const DString &outputDir, bool toIndex, Format format, HighDPI hd=HighDPI::Off)
Definition formula.cpp:635
A model of a group of symbols.
Definition groupdef.h:48
virtual DString groupTitle() const =0
virtual void overrideGroupGraph(bool e)=0
virtual bool addClass(ClassDef *def)=0
virtual bool containsFile(const FileDef *def) const =0
virtual bool addNamespace(NamespaceDef *def)=0
virtual void setGroupScope(Definition *d)=0
virtual void addFile(FileDef *def)=0
virtual void setGroupTitle(const DString &newtitle)=0
virtual MemberList * getMemberList(MemberListType lt) const =0
virtual bool hasGroupTitle() const =0
Generator for HTML output.
Definition htmlgen.h:93
static void writeSearchData(const DString &dir)
Definition htmlgen.cpp:1455
static void init()
Definition htmlgen.cpp:1275
static void writeSearchPage()
Definition htmlgen.cpp:3272
static void writeHeaderFile(TextStream &t, const DString &cssname)
Definition htmlgen.cpp:1623
static void writeFooterFile(TextStream &t)
Definition htmlgen.cpp:1629
static void writeTabData()
Additional initialization after indices have been created.
Definition htmlgen.cpp:1439
static void writeExternalSearchPage()
Definition htmlgen.cpp:3371
static void writeStyleSheetFile(TextStream &t)
Definition htmlgen.cpp:1617
A class that generated the HTML Help specific files.
Definition htmlhelp.h:37
static const DString hhpFileName
Definition htmlhelp.h:90
static Index & instance()
Definition index.cpp:110
void countDataStructures()
Definition index.cpp:266
A list of index interfaces.
Definition indexlist.h:65
void initialize()
Definition indexlist.h:101
void addIndexItem(const Definition *context, const MemberDef *md, const DString &sectionAnchor=DString(), const DString &title=DString())
Definition indexlist.h:118
void addIndex(As &&... args)
Add an index generator to the list, using a syntax similar to std::make_unique<T>().
Definition indexlist.h:98
void addImageFile(const DString &name)
Definition indexlist.h:124
void finalize()
Definition indexlist.h:104
Generator for LaTeX output.
Definition latexgen.h:93
static void writeFooterFile(TextStream &t)
Definition latexgen.cpp:698
static void writeStyleSheetFile(TextStream &t)
Definition latexgen.cpp:704
static void writeHeaderFile(TextStream &t)
Definition latexgen.cpp:692
static void init()
Definition latexgen.cpp:634
void parse(const DString &fileName, const char *data=nullptr)
Parses a user provided layout.
Definition layout.cpp:1471
static LayoutDocManager & instance()
Returns a reference to this singleton.
Definition layout.cpp:1438
void clear()
Definition linkedmap.h:212
std::unique_ptr< RefList > Ptr
Definition linkedmap.h:38
size_t size() const
Definition linkedmap.h:210
T * add(const char *k, Args &&... args)
Definition linkedmap.h:90
const T * find(const std::string &key) const
Definition linkedmap.h:47
Container class representing a vector of objects with keys.
Definition linkedmap.h:232
const T * find(const std::string &key) const
Definition linkedmap.h:243
bool empty() const
Definition linkedmap.h:374
Generator for Man page output.
Definition mangen.h:69
static void init()
Definition mangen.cpp:275
A model of a class/file/namespace member symbol.
Definition memberdef.h:45
virtual DString requiresClause() const =0
virtual bool isFriend() const =0
virtual DString argsString() const =0
virtual bool isForeign() const =0
virtual bool isRelated() const =0
virtual DString definition() const =0
virtual const ClassDef * getCachedTypedefVal() const =0
virtual const ClassDef * getClassDef() const =0
virtual DString excpString() const =0
virtual const ArgumentList & templateArguments() const =0
virtual const DString & initializer() const =0
virtual GroupDef * getGroupDef()=0
virtual bool isCSharpProperty() const =0
virtual bool isTypedef() const =0
virtual const MemberVector & enumFieldList() const =0
virtual void moveTo(Definition *)=0
virtual const FileDef * getFileDef() const =0
virtual const ArgumentList & argumentList() const =0
virtual bool isStrongEnumValue() const =0
virtual VhdlSpecifier getVhdlSpecifiers() const =0
virtual bool isFunction() const =0
virtual bool isExternal() const =0
virtual int getMemberGroupId() const =0
virtual DString bitfieldString() const =0
virtual bool isStatic() const =0
virtual const MemberDef * reimplements() const =0
virtual StringVector getQualifiers() const =0
virtual bool isTypedefValCached() const =0
virtual bool isDocsForDefinition() const =0
virtual bool isDefine() const =0
virtual const NamespaceDef * getNamespaceDef() const =0
virtual bool isObjCProperty() const =0
virtual Protection protection() const =0
virtual TypeSpecifier getMemberSpecifiers() const =0
virtual bool isEnumerate() const =0
virtual MemberType memberType() const =0
virtual ClassDef * relatedAlso() const =0
virtual bool isVariable() const =0
virtual bool isStrong() const =0
virtual DString typeString() const =0
virtual Specifier virtualness(int count=0) const =0
virtual int redefineCount() const =0
virtual int initializerLines() const =0
virtual const MemberDef * getEnumScope() const =0
virtual bool isEnumValue() const =0
virtual bool isPrototype() const =0
virtual void setRequiresClause(const DString &req)=0
virtual void setClassDefOfAnonymousType(const ClassDef *cd)=0
virtual void setMemberClass(ClassDef *cd)=0
virtual void setProtection(Protection p)=0
virtual void setDefinition(const DString &d)=0
virtual void setMemberGroupId(int id)=0
virtual void setDocumentedEnumValues(bool value)=0
virtual void setMemberSpecifiers(TypeSpecifier s)=0
virtual ClassDefMutable * getClassDefMutable()=0
virtual void setArgsString(const DString &as)=0
virtual void setEnumScope(MemberDef *md, bool livesInsideEnum=false)=0
virtual void setDefinitionTemplateParameterLists(const ArgumentLists &lists)=0
virtual void invalidateTypedefValCache()=0
virtual void setVhdlSpecifiers(VhdlSpecifier s)=0
virtual void setEnumClassScope(ClassDef *cd)=0
virtual void setInitializer(const DString &i)=0
virtual void setMaxInitLines(int lines)=0
virtual void setInheritsDocsFrom(const MemberDef *md)=0
virtual void setPrototype(bool p, const DString &df, int line, size_t column)=0
virtual void setRelatedAlso(ClassDef *cd)=0
virtual void overrideReferencesRelation(bool e)=0
virtual void makeForeign()=0
virtual void overrideReferencedByRelation(bool e)=0
virtual void setDocsForDefinition(bool b)=0
virtual void overrideCallGraph(bool e)=0
virtual void overrideInlineSource(bool e)=0
virtual void setBitfields(const DString &s)=0
virtual void copyArgumentNames(const MemberDef *bmd)=0
virtual void overrideEnumValues(bool e)=0
virtual void mergeMemberSpecifiers(TypeSpecifier s)=0
virtual void addQualifiers(const StringVector &qualifiers)=0
virtual void setExplicitExternal(bool b, const DString &df, int line, size_t column)=0
virtual void insertEnumField(MemberDef *md)=0
virtual void moveDeclArgumentList(std::unique_ptr< ArgumentList > al)=0
virtual void setDeclFile(const DString &df, int line, size_t column)=0
virtual void setAnonymousEnumType(const MemberDef *md)=0
virtual void overrideCallerGraph(bool e)=0
virtual void setReimplements(MemberDef *md)=0
virtual void invalidateCachedArgumentTypes()=0
virtual void moveArgumentList(std::unique_ptr< ArgumentList > al)=0
virtual void makeRelated()=0
virtual void insertReimplementedBy(MemberDef *md)=0
A list of MemberDef objects as shown in documentation sections.
Definition memberlist.h:126
Wrapper class for the MemberListType type.
Definition types.h:346
Ptr & front()
Definition membername.h:49
size_t size() const
Definition membername.h:46
iterator begin()
Definition membername.h:35
iterator end()
Definition membername.h:36
void push_back(Ptr &&p)
Definition membername.h:52
Ordered dictionary of MemberName objects.
Definition membername.h:61
MemberName::Ptr take(const DString &key, const MemberDef *value)
Definition membername.h:63
const MemberDef * findRev(const DString &name) const
Definition memberlist.h:107
const MemberDef * find(const DString &name) const
Definition memberlist.h:94
void run()
Run mmdc tool for all collected diagrams.
Definition mermaid.cpp:255
static MermaidManager & instance()
Definition mermaid.cpp:38
void sortMemberLists()
static ModuleManager & instance()
void addDocs(const Entry *root)
void addConceptToModule(const Entry *root, ConceptDef *cd)
void addClassToModule(const Entry *root, ClassDef *cd)
void addMemberToModule(const Entry *root, MemberDef *md)
void writeDocumentation(OutputList &ol)
void addMembersToMemberGroup()
void findSectionsInDocumentation()
void distributeMemberGroupDocumentation()
An abstract interface of a namespace symbol.
virtual const LinkedRefMap< NamespaceDef > & getUsedNamespaces() const =0
virtual bool isInline() const =0
virtual void insertUsedFile(FileDef *fd)=0
virtual void findSectionsInDocumentation()=0
virtual void countMembers()=0
virtual void addUsingDirective(NamespaceDef *nd)=0
virtual void setFileName(const DString &fn)=0
virtual void insertMember(MemberDef *md)=0
virtual void setMetaData(const DString &m)=0
virtual void addUsingDeclaration(const Definition *d)=0
virtual void distributeMemberGroupDocumentation()=0
virtual void writeTagFile(TextStream &)=0
virtual void writeDocumentation(OutputList &ol)=0
virtual void setInline(bool isInline)=0
virtual void computeAnchors()=0
virtual void combineUsingRelations(NamespaceDefSet &visitedNamespace)=0
virtual void sortMemberLists()=0
virtual void addMembersToMemberGroup()=0
/dev/null outline parser
void parseInput(const DString &, const char *, const std::shared_ptr< Entry > &, ClangTUParser *) override
Parses a single input file with the goal to build an Entry tree.
void parsePrototype(const DString &) override
Callback function called by the comment block scanner.
bool needsPreprocessing(const DString &) const override
Returns true if the language identified by extension needs the C preprocessor to be run before feed t...
Abstract interface for outline parsers.
Definition parserintf.h:41
virtual bool needsPreprocessing(const DString &extension) const =0
Returns true if the language identified by extension needs the C preprocessor to be run before feed t...
virtual void parseInput(const DString &fileName, const char *fileBuf, const std::shared_ptr< Entry > &root, ClangTUParser *clangParser)=0
Parses a single input file with the goal to build an Entry tree.
Class representing a list of output generators that are written to in parallel.
Definition outputlist.h:311
void disable(OutputType o)
void add()
Definition outputlist.h:348
void enable(OutputType o)
size_t size() const
Definition outputlist.h:357
void docify(const DString &s)
Definition outputlist.h:433
void writeStyleInfo(int part)
Definition outputlist.h:391
void generateDoc(const DString &fileName, int startLine, const Definition *ctx, const MemberDef *md, const DString &docStr, const DocOptions &options)
void cleanup()
Definition outputlist.h:748
void startContents()
Definition outputlist.h:614
A model of a page symbol.
Definition pagedef.h:27
virtual void setLocalToc(const LocalToc &tl)=0
virtual void setFileName(const DString &name)=0
virtual void setShowLineNo(bool)=0
virtual void setPageScope(Definition *)=0
virtual const GroupDef * getGroupDef() const =0
Manages programming language parsers.
Definition parserintf.h:182
std::unique_ptr< CodeParserInterface > getCodeParser(const DString &extension)
Gets the interface to the parser associated with a given extension.
Definition parserintf.h:253
void registerParser(const DString &name, const OutlineParserFactory &outlineParserFactory, const CodeParserFactory &codeParserFactory)
Registers an additional parser.
Definition parserintf.h:215
std::unique_ptr< OutlineParserInterface > getOutlineParser(const DString &extension)
Gets the interface to the parser associated with a given extension.
Definition parserintf.h:244
static PlantumlManager & instance()
Definition plantuml.cpp:236
void run()
Run plant UML tool for all images.
Definition plantuml.cpp:432
void processFile(const DString &fileName, const std::string &input, std::string &output)
Definition pre.l:4302
void addSearchDir(const DString &dir)
Definition pre.l:4284
Definition qhp.h:29
static DString getQchFileName()
Definition qhp.cpp:430
static const DString qhpFileName
Definition qhp.h:49
Generator for RTF output.
Definition rtfgen.h:80
static void init()
Definition rtfgen.cpp:464
static bool preProcessFileInplace(const DString &path, const DString &name)
This is an API to a VERY brittle RTF preprocessor that combines nested RTF files.
Definition rtfgen.cpp:2468
static void writeStyleSheetFile(TextStream &t)
Definition rtfgen.cpp:397
static void writeExtensionsFile(TextStream &t)
Definition rtfgen.cpp:412
static RefListManager & instance()
Definition reflist.h:122
static RequirementManager & instance()
void writeTagFile(TextStream &tagFile)
void addRequirement(Entry *e)
Abstract proxy interface for non-javascript based search indices.
void addWord(const DString &word, bool hiPriority)
void setCurrentDoc(const Definition *ctx, const DString &anchor, bool isSourceFile)
void write(const DString &file)
class that provide information about a section.
Definition section.h:58
DString fileName() const
Definition section.h:74
int lineNr() const
Definition section.h:73
DString ref() const
Definition section.h:72
SectionInfo * replace(const DString &label, const DString &fileName, int lineNr, const DString &title, SectionType type, int level, const DString &ref=DString())
Definition section.h:157
SectionInfo * add(const SectionInfo &si)
Definition section.h:139
static SectionManager & instance()
returns a reference to the singleton
Definition section.h:179
static constexpr int Page
Definition section.h:31
std::vector< stat > stats
Definition doxygen.cpp:273
void print()
Definition doxygen.cpp:250
void begin(const char *name)
Definition doxygen.cpp:237
void end()
Definition doxygen.cpp:243
std::chrono::steady_clock::time_point startTime
Definition doxygen.cpp:274
static void showCacheUsage()
Show usage of the type lookup cache.
static void clearTypeLookupCache(ClearScope scope)
ClassDefMutable * resolveClassMutable(const Definition *scope, const DString &name, bool mayBeUnlinkable=false, bool mayBeHidden=false)
Wrapper around resolveClass that returns a mutable interface to the class object or a nullptr if the ...
const ClassDef * resolveClass(const Definition *scope, const DString &name, bool maybeUnlinkable=false, bool mayBeHidden=false)
Find the class definition matching name within the scope set.
const Definition * resolveSymbol(const Definition *scope, const DString &name, const DString &args=DString(), bool checkCV=false, bool insideCode=false, bool onlyLinkable=false)
Find the symbool definition matching name within the scope set.
DString getTemplateSpec() const
In case a call to resolveClass() points to a template specialization, the template part is return via...
const MemberDef * getTypedef() const
In case a call to resolveClass() resolves to a type member (e.g. an enum) this method will return it.
This struct is used to capture the tag file information for an Entry.
Definition entry.h:101
DString tagName
Definition entry.h:103
DString fileName
Definition entry.h:104
Text streaming class that buffers data.
Definition textstream.h:36
std::string str() const
Return the contents of the buffer as a std::string object.
Definition textstream.h:232
Class managing a pool of worker threads.
Definition threadpool.h:48
auto queue(F &&f, Args &&... args) -> std::future< decltype(f(args...))>
Queue the callable function f for the threads to execute.
Definition threadpool.h:77
virtual DString trOverloadText()=0
virtual DString trPage(bool first_capital, bool singular)=0
virtual DString trMainPage()=0
Wrapper class for a number of boolean properties.
Definition types.h:694
std::string to_string() const
Definition types.h:738
static void correctMemberProperties(MemberDefMutable *md)
static void computeVhdlComponentRelations()
ClassDefMutable * toClassDefMutable(Definition *d)
ClassDef * getClass(const DString &n)
std::unique_ptr< ClassDef > createClassDefAlias(const Definition *newScope, const ClassDef *cd)
Definition classdef.cpp:803
std::unique_ptr< ClassDef > createClassDef(const DString &fileName, int startLine, size_t startColumn, const DString &name, ClassDef::CompoundType ct, const DString &ref, const DString &fName, bool isSymbol, bool isJavaEnum)
Factory method to create a new ClassDef object.
Definition classdef.cpp:577
ClassDef * toClassDef(Definition *d)
std::unordered_set< const ClassDef * > ClassDefSet
Definition classdef.h:91
std::map< std::string, int > TemplateNameMap
Definition classdef.h:89
ClassDefMutable * getClassMutable(const DString &key)
Definition classdef.h:461
Class representing a regular expression.
Definition regex.h:39
Class to iterate through matches.
Definition regex.h:239
Object representing the matching results.
Definition regex.h:154
First pass comment processing.
void convertCppComments(const std::string &inBuf, std::string &outBuf, const std::string &fn)
Converts the comments in a file.
ConceptDefMutable * toConceptDefMutable(Definition *d)
std::unique_ptr< ConceptDef > createConceptDef(const DString &fileName, int startLine, size_t startColumn, const DString &name, const DString &tagRef, const DString &tagFile)
ConceptDefMutable * getConceptMutable(const DString &key)
Definition conceptdef.h:106
#define Config_getInt(name)
Definition config.h:34
#define Config_getList(name)
Definition config.h:38
#define Config_updateString(name, value)
Definition config.h:39
#define Config_updateBool(name, value)
Definition config.h:40
#define Config_getBool(name)
Definition config.h:33
#define Config_getString(name)
Definition config.h:32
#define Config_updateList(name,...)
Definition config.h:43
#define Config_getEnum(name)
Definition config.h:35
std::set< std::string > StringSet
Definition containers.h:31
std::unordered_set< std::string > StringUnorderedSet
Definition containers.h:29
std::map< std::string, std::string > StringMap
Definition containers.h:30
std::vector< std::string > StringVector
Definition containers.h:33
void parseFuncDecl(const DString &decl, const SrcLangExt lang, DString &clName, DString &type, DString &name, DString &args, DString &funcTempList, DString &exceptions)
Definition declinfo.l:318
std::unique_ptr< ArgumentList > stringToArgumentList(SrcLangExt lang, const DString &argsString, DString *extraTypeChars=nullptr)
Definition defargs.l:821
void generateDEF()
Definition defgen.cpp:463
std::unordered_map< std::string, DefineList > DefinesPerFileList
Definition define.h:50
Definition * toDefinition(DefinitionMutable *dm)
DefinitionMutable * toDefinitionMutable(Definition *d)
DirIterator begin(DirIterator it) noexcept
Definition dir.cpp:176
DirIterator end(const DirIterator &) noexcept
Definition dir.cpp:181
void buildDirectories()
Definition dirdef.cpp:1098
void computeDirDependencies()
Definition dirdef.cpp:1172
void generateDirDocs(OutputList &ol)
Definition dirdef.cpp:1189
#define AUTO_TRACE_ADD(...)
Definition docnode.cpp:54
#define AUTO_TRACE(...)
Definition docnode.cpp:53
#define AUTO_TRACE_EXIT(...)
Definition docnode.cpp:55
constexpr DocNodeVariant * parent(DocNodeVariant *n)
returns the parent node of a given node n or nullptr if the node has no parent.
Definition docnode.h:1335
static void findInheritedTemplateInstances()
Definition doxygen.cpp:5391
void printNavTree(Entry *root, int indent)
static void addClassToContext(const Entry *root)
Definition doxygen.cpp:945
static void makeTemplateInstanceRelation(const Entry *root, ClassDefMutable *cd)
Definition doxygen.cpp:5406
static StringUnorderedSet g_pathsVisited(1009)
static void buildGroupList(const Entry *root)
Definition doxygen.cpp:435
static void insertMemberAlias(Definition *outerScope, const MemberDef *md)
Definition doxygen.cpp:6775
static void findUsingDeclarations(const Entry *root, bool filterPythonPackages)
Definition doxygen.cpp:2184
static void flushCachedTemplateRelations()
Definition doxygen.cpp:9519
static void copyLatexStyleSheet()
static void generateDocsForClassList(const std::vector< ClassDefMutable * > &classList)
Definition doxygen.cpp:9179
static int findFunctionPtr(const std::string &type, SrcLangExt lang, int *pLength=nullptr)
Definition doxygen.cpp:3026
static bool isSpecialization(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists)
Definition doxygen.cpp:6076
static void computeTemplateClassRelations()
Definition doxygen.cpp:5485
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:8234
static void runQHelpGenerator()
static void addConceptToContext(const Entry *root)
Definition doxygen.cpp:1170
static void addRelatedPage(Entry *root)
Definition doxygen.cpp:333
void initDoxygen()
static StringVector g_inputFiles
Definition doxygen.cpp:191
void printSectionsTree()
class Statistics g_s
static void generateXRefPages()
Definition doxygen.cpp:5664
static Definition * buildScopeFromQualifiedName(const DString &name_, SrcLangExt lang, const TagInfo *tagInfo)
Definition doxygen.cpp:719
static void findUsingDeclImports(const Entry *root)
Definition doxygen.cpp:2337
static void copyStyleSheet()
FindBaseClassRelation_Mode
Definition doxygen.cpp:290
@ Undocumented
Definition doxygen.cpp:293
@ TemplateInstances
Definition doxygen.cpp:291
@ DocumentedOnly
Definition doxygen.cpp:292
void distributeClassGroupRelations()
Definition doxygen.cpp:1506
static void generateGroupDocs()
static void findDirDocumentation(const Entry *root)
Definition doxygen.cpp:9719
void checkConfiguration()
check and resolve config options
static void processTagLessClasses(const Definition *root, const Container *cd, const TagContainer *tagParent, MemberListType varFilter, MemberListType typeFilter, const DString &prefix, int count)
Look through the members of class cd and its public members.
Definition doxygen.cpp:1699
static bool findClassRelation(const Entry *root, Definition *context, ClassDefMutable *cd, const BaseInfo *bi, const TemplateNameMap &templateNames, FindBaseClassRelation_Mode mode, bool isArtificial)
Definition doxygen.cpp:4992
static void resolveTemplateInstanceInType(const Entry *root, const Definition *scope, const MemberDef *md)
Definition doxygen.cpp:4922
static void organizeSubGroupsFiltered(const Entry *root, bool additional)
Definition doxygen.cpp:475
static void warnUndocumentedNamespaces()
Definition doxygen.cpp:5440
static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments, const std::string &name)
Definition doxygen.cpp:4601
static void buildConceptList(const Entry *root)
Definition doxygen.cpp:1331
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:6644
static void resolveClassNestingRelations()
Definition doxygen.cpp:1386
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:2774
static void findClassEntries(const Entry *root)
Definition doxygen.cpp:5356
static void vhdlCorrectMemberProperties()
Definition doxygen.cpp:8478
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:6288
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:6190
static void copyLogo(const DString &outputOption, bool toIndex)
static void computeMemberReferences()
Definition doxygen.cpp:5554
static void transferRelatedFunctionDocumentation()
Definition doxygen.cpp:4515
static void addMembersToMemberGroup()
Definition doxygen.cpp:9384
static void findMainPageTagFiles(Entry *root)
Definition doxygen.cpp:9888
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:6234
static void distributeConceptGroups()
Definition doxygen.cpp:1353
static NamespaceDef * findUsedNamespace(const LinkedRefMap< NamespaceDef > &unl, const DString &name)
Definition doxygen.cpp:2014
static void transferFunctionDocumentation()
Definition doxygen.cpp:4434
static void setAnonymousEnumType()
Definition doxygen.cpp:9124
static void sortMemberLists()
Definition doxygen.cpp:9029
static void findMember(const Entry *root, const DString &relates, const DString &type, const DString &args, DString funcDecl, bool overloaded, bool isFunc)
Definition doxygen.cpp:6818
static void createTemplateInstanceMembers()
Definition doxygen.cpp:8606
void transferStaticInstanceInitializers()
Definition doxygen.cpp:4564
static void findObjCMethodDefinitions(const Entry *root)
Definition doxygen.cpp:7623
static void addMemberDocs(const Entry *root, MemberDefMutable *md, const DString &funcDecl, const ArgumentList *al, bool over_load, TypeSpecifier spec)
Definition doxygen.cpp:5678
static void dumpSymbolMap()
static void buildTypedefList(const Entry *root)
Definition doxygen.cpp:3503
static void findGroupScope(const Entry *root)
Definition doxygen.cpp:450
static void generateFileDocs()
Definition doxygen.cpp:8842
static int findEndOfTemplate(const DString &s, size_t startPos)
Definition doxygen.cpp:3229
static void findDefineDocumentation(Entry *root)
Definition doxygen.cpp:9632
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:4664
void parseInput()
static void findMemberDocumentation(const Entry *root)
Definition doxygen.cpp:7593
static void distributeMemberGroupDocumentation()
Definition doxygen.cpp:9422
static void generateNamespaceClassDocs(const ClassLinkedRefMap &classList)
static void addEnumValuesToEnums(const Entry *root)
Definition doxygen.cpp:7826
static void generatePageDocs()
static void resolveUserReferences()
Definition doxygen.cpp:9950
static void buildRequirementsList(Entry *root)
Definition doxygen.cpp:9779
static void compareDoxyfile(Config::CompareMode diffList)
static void addPageToContext(PageDef *pd, Entry *root)
Definition doxygen.cpp:314
static std::shared_ptr< Entry > parseFile(OutlineParserInterface &parser, FileDef *fd, const DString &fn, ClangTUParser *clangParser, bool newTU)
static void buildVarList(const Entry *root)
Definition doxygen.cpp:3640
static void copyIcon(const DString &outputOption, bool toIndex)
static void buildSequenceList(const Entry *root)
Definition doxygen.cpp:3603
static void generateFileSources()
Definition doxygen.cpp:8676
static void copyExtraFiles(StringVector files, const DString &filesOption, const DString &outputOption, bool toIndex)
static void generateClassDocs()
Definition doxygen.cpp:9277
static int findTemplateSpecializationPosition(const DString &name)
Definition doxygen.cpp:4962
static void buildNamespaceList(const Entry *root)
Definition doxygen.cpp:1843
static void findIncludedUsingDirectives()
Definition doxygen.cpp:2587
static void addDefineDoc(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:9605
static void countMembers()
Definition doxygen.cpp:9138
void clearAll()
Definition doxygen.cpp:205
static void devUsage()
static ClassDef * findClassWithinClassContext(Definition *context, ClassDef *cd, const DString &name)
Definition doxygen.cpp:4629
static void organizeSubGroups(const Entry *root)
Definition doxygen.cpp:494
static void applyMemberOverrideOptions(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:2276
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:6707
static void findFriends()
Definition doxygen.cpp:4337
static void findEnums(const Entry *root)
Definition doxygen.cpp:7651
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:9254
static void addEnumDocs(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:8074
static void addListReferences()
Definition doxygen.cpp:5655
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:5868
static bool isClassSection(const Entry *root)
Definition doxygen.cpp:5334
static void buildGroupListFiltered(const Entry *root, bool additional, bool includeExternal)
Definition doxygen.cpp:364
static void runHtmlHelpCompiler()
static void addMembersToIndex()
Definition doxygen.cpp:8268
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:5365
static void findMainPage(Entry *root)
Definition doxygen.cpp:9818
static ClassDef::CompoundType convertToCompoundType(EntryType section, TypeSpecifier specifier)
Definition doxygen.cpp:903
DString stripTemplateSpecifiers(const DString &s)
Definition doxygen.cpp:692
static void findUsingDirectives(const Entry *root)
Definition doxygen.cpp:2027
static void addInterfaceOrServiceToServiceOrSingleton(const Entry *root, ClassDefMutable *cd, DString const &rname)
Definition doxygen.cpp:3672
static bool g_successfulRun
Definition doxygen.cpp:194
static bool tryAddEnumDocsToGroupMember(const Entry *root, const DString &name)
Definition doxygen.cpp:8116
static void addSourceReferences()
Definition doxygen.cpp:8902
static void associateVariableWithAnonymousEnumType(const MemberDef *md, const Container *cd, const MemberDef *enumTypeMember, MemberListType mlFilter)
Definition doxygen.cpp:1540
static void createUsingMemberImportForClass(const Entry *root, ClassDefMutable *cd, const MemberDef *md, const DString &fileName, const DString &memName)
Definition doxygen.cpp:2288
static DString substituteTemplatesInString(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists, const std::string &src)
Definition doxygen.cpp:6106
std::function< std::unique_ptr< T >() > make_parser_factory()
static void buildExampleList(Entry *root)
static void inheritDocumentation()
Definition doxygen.cpp:9324
static void flushUnresolvedRelations()
Definition doxygen.cpp:9561
static bool isSymbolHidden(const Definition *d)
Definition doxygen.cpp:9071
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:4871
static void findDocumentedEnumValues()
Definition doxygen.cpp:8260
static void findTagLessClasses()
Definition doxygen.cpp:1791
static void generateDiskNames()
static void addToIndices()
Definition doxygen.cpp:8310
static void computeClassRelations()
Definition doxygen.cpp:5460
static void buildFunctionList(const Entry *root)
Definition doxygen.cpp:4032
static void checkPageRelations()
Definition doxygen.cpp:9930
static void addGlobalFunction(const Entry *root, const DString &rname, const DString &sc)
Definition doxygen.cpp:3923
static void findModuleDocumentation(const Entry *root)
Definition doxygen.cpp:1321
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:2604
static void readTagFile(const std::shared_ptr< Entry > &root, const DString &tagLine)
static void findEnumDocumentation(const Entry *root)
Definition doxygen.cpp:8149
static void computePageRelations(Entry *root)
Definition doxygen.cpp:9900
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:3787
void initResources()
static bool isVarWithConstructor(const Entry *root)
Definition doxygen.cpp:3086
static StringSet g_usingDeclarations
Definition doxygen.cpp:193
static void buildDictionaryList(const Entry *root)
Definition doxygen.cpp:3621
static bool haveEqualFileNames(const Entry *root, const MemberDef *md)
Definition doxygen.cpp:9594
void readFileOrDirectory(const DString &s, FileNameLinkedMap *fnMap, StringUnorderedSet *exclSet, const StringVector *patList, const StringVector *exclPatList, StringVector *resultList, StringUnorderedSet *resultSet, bool recursive, bool errorIfNotExist, StringUnorderedSet *killSet, StringUnorderedSet *paths)
static void generateNamespaceDocs()
static void buildClassDocList(const Entry *root)
Definition doxygen.cpp:1156
static void buildPageList(Entry *root)
Definition doxygen.cpp:9791
static void writeTagFile()
static void addRequirementReferences()
Definition doxygen.cpp:5647
static void computeVerifiedDotPath()
static bool g_singleComment
Definition doxygen.cpp:197
static void findSectionsInDocumentation()
Definition doxygen.cpp:9460
static void mergeCategories()
Definition doxygen.cpp:8625
static const StringUnorderedSet g_compoundKeywords
Definition doxygen.cpp:202
static bool scopeIsTemplate(const Definition *d)
Definition doxygen.cpp:6092
static void buildFileList(const Entry *root)
Definition doxygen.cpp:606
static void buildClassList(const Entry *root)
Definition doxygen.cpp:1146
static void usage(const DString &name, const DString &versionString)
static void findUsedTemplateInstances()
Definition doxygen.cpp:5424
static void computeTooltipTexts()
Definition doxygen.cpp:9078
static void addVariable(const Entry *root, int isFuncPtr=-1)
Definition doxygen.cpp:3297
static void addIncludeFile(DefMutable *def, FileDef *ifd, const Entry *root)
Definition doxygen.cpp:507
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:4820
static void parseFilesSingleThreading(const std::shared_ptr< Entry > &root)
parse the list of input files
static void buildCompleteMemberLists()
Definition doxygen.cpp:8646
static const ClassDef * findClassDefinition(FileDef *fd, NamespaceDef *nd, const DString &scopeName)
Definition doxygen.cpp:5829
static void filterMemberDocumentation(const Entry *root, const DString &relates)
Definition doxygen.cpp:7444
static void generateConceptDocs()
Definition doxygen.cpp:9303
static bool isRecursiveBaseClass(const DString &scope, const DString &name)
Definition doxygen.cpp:4951
static std::unique_ptr< OutlineParserInterface > getParserForFile(const DString &fn)
static void combineUsingRelations()
Definition doxygen.cpp:9359
static const char * getArg(int argc, char **argv, int &optInd)
std::unique_ptr< ArgumentList > getTemplateArgumentsFromName(const DString &name, const ArgumentLists &tArgLists)
Definition doxygen.cpp:873
static ClassDefMutable * createTagLessInstance(const Definition *root, const ClassDef *templ, const DString &fieldName)
Definition doxygen.cpp:1567
static void checkMarkdownMainfile()
static std::unordered_map< std::string, std::vector< ClassDefMutable * > > g_usingClassMap
Definition doxygen.cpp:2335
static void buildConceptDocList(const Entry *root)
Definition doxygen.cpp:1341
static bool isEntryInGroupOfMember(const Entry *root, const MemberDef *md, bool allowNoGroup=false)
Definition doxygen.cpp:5844
static void applyToAllDefinitions(Func func)
Definition doxygen.cpp:5590
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:4467
static void buildDefineList()
Definition doxygen.cpp:8981
static void buildInterfaceAndServiceList(const Entry *root)
Definition doxygen.cpp:3735
static Definition * findScopeFromQualifiedName(NamespaceDefMutable *startScope, const DString &n, FileDef *fileScope, const TagInfo *tagInfo)
Definition doxygen.cpp:788
static void computeMemberRelations()
Definition doxygen.cpp:8590
static void buildListOfUsingDecls(const Entry *root)
Definition doxygen.cpp:2171
static void computeMemberRelationsForBaseClass(const ClassDef *cd, const BaseClassDef *bcd)
Definition doxygen.cpp:8510
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:2028
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:180
void addConceptToGroups(const Entry *root, ConceptDef *cd)
void addMemberToGroups(const Entry *root, MemberDef *md)
void writeGraphInfo(OutputList &ol)
Definition index.cpp:4097
void endTitle(OutputList &ol, const DString &fileName, const DString &name)
Definition index.cpp:398
void writeIndexHierarchy(OutputList &ol)
Definition index.cpp:5811
void endFile(OutputList &ol, bool skipNavIndex, bool skipEndContents, const DString &navPath)
Definition index.cpp:431
void startTitle(OutputList &ol, const DString &fileName, const DefinitionMutable *def)
Definition index.cpp:388
void startFile(OutputList &ol, const DString &name, bool isSource, const DString &manName, const DString &title, HighlightedItem hli, bool additionalIndices, const DString &altSidebarName, int hierarchyLevel, const DString &allMembersFile)
Definition index.cpp:405
Translator * theTranslator
Definition language.cpp:76
void setTranslator(OUTPUT_LANGUAGE_t langName)
Definition language.cpp:78
#define LATEX_STYLE_EXTENSION
Definition latexgen.h:21
void writeDefaultLayoutFile(const DString &fileName)
Definition layout.cpp:1735
void printLayout()
Definition layout.cpp:1823
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:2679
DString substituteTemplateArgumentsInString(const DString &nm, const ArgumentList &formalArgs, const ArgumentList *actualArgs)
Definition util.cpp:3524
bool protectionLevelVisible(Protection prot)
Definition util.cpp:4671
DString stripFromIncludePath(const DString &path)
Definition util.cpp:227
DString mergeScopes(const DString &leftScope, const DString &rightScope)
Definition util.cpp:3753
DString filterTitle(const DString &title)
Definition util.cpp:4454
bool matchTemplateArguments(const ArgumentList &srcAl, const ArgumentList &dstAl)
Definition util.cpp:1833
void addCodeOnlyMappings()
Definition util.cpp:4162
bool rightScopeMatch(const DString &scope, const DString &name)
Definition util.cpp:729
bool checkIfTypedef(const Definition *scope, const FileDef *fileScope, const DString &n)
Definition util.cpp:4271
DString replaceAnonymousScopes(const DString &s, const DString &replacement)
Definition util.cpp:155
void cleanupInlineGraphs()
Definition util.cpp:5398
int computeQualifiedIndex(const DString &name)
Return the index of the last :: in the string name that is still before the first <.
Definition util.cpp:5293
DString removeRedundantWhiteSpace(const DString &s)
Definition util.cpp:426
bool checkExtension(const DString &fName, const DString &ext)
Definition util.cpp:3926
DString convertNameToFile(const DString &name, bool allowDots, bool allowUnderscore)
Definition util.cpp:2863
bool leftScopeMatch(const DString &scope, const DString &name)
Definition util.cpp:740
DString tempArgListToString(const ArgumentList &al, SrcLangExt lang, bool includeDefault)
Definition util.cpp:903
DString getFileNameExtension(const DString &fn)
Definition util.cpp:4210
DString resolveTypeDef(const Definition *context, const DString &qualifiedName, const Definition **typedefContext)
Definition util.cpp:232
bool readInputFile(const DString &fileName, std::string &contents, bool filter, bool isSourceCode)
read a file name fileName and optionally filter and transcode it
Definition util.cpp:4374
DString normalizeNonTemplateArgumentsInString(const DString &name, const Definition *context, const ArgumentList &formalArgs)
Definition util.cpp:3465
SrcLangExt getLanguageFromFileName(const DString &fileName, SrcLangExt defLang)
Definition util.cpp:4168
bool matchArguments2(const Definition *srcScope, const FileDef *srcFileScope, const DString &srcReturnType, const ArgumentList *srcAl, const Definition *dstScope, const FileDef *dstFileScope, const DString &dstReturnType, const ArgumentList *dstAl, bool checkCV, SrcLangExt lang)
Definition util.cpp:1589
void initDefaultExtensionMapping()
Definition util.cpp:4095
void addHtmlExtensionIfMissing(DString &fName)
Definition util.cpp:3931
DString fileToString(const DString &name, bool filter, bool isSourceCode)
Definition util.cpp:1053
void extractNamespaceName(const DString &scopeName, DString &className, DString &namespaceName, bool allowEmptyClass)
Definition util.cpp:3026
DString stripTemplateSpecifiersFromScope(const DString &fullName, bool parentOnly, DString *pLastScopeStripped, DString scopeName, bool allowArtificial)
Definition util.cpp:3686
DString argListToString(const ArgumentList &al, bool useCanonicalType, bool showDefVals)
Definition util.cpp:859
DString projectLogoFile()
Definition util.cpp:2580
bool copyFile(const DString &src, const DString &dest)
Copies the contents of file with name src to the newly created file with name dest.
Definition util.cpp:4619
int getPrefixIndex(const DString &name)
Definition util.cpp:2651
DString getLanguageSpecificSeparator(SrcLangExt lang, bool classScope)
Definition util.cpp:4629
bool updateLanguageMapping(const DString &extension, const DString &language)
Definition util.cpp:4063
DString mangleCSharpGenericName(const DString &name)
Definition util.cpp:5334
void mergeArguments(ArgumentList &srcAl, ArgumentList &dstAl, bool forceNameOverwrite)
Definition util.cpp:1689
int getScopeFragment(const DString &s, int p, int *l)
Definition util.cpp:3798
int extractClassNameFromType(const DString &type, int &pos, DString &name, DString &templSpec, SrcLangExt lang)
Definition util.cpp:3380
bool openOutputFile(const DString &outFile, std::ofstream &f)
Definition util.cpp:4986
DString stripAnonymousNamespaceScope(const DString &s)
Definition util.cpp:167
A bunch of utility functions.
void generateXML()
Definition xmlgen.cpp:2316