Doxygen
Loading...
Searching...
No Matches
doxygen.cpp
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * Copyright (C) 1997-2015 by Dimitri van Heesch.
4 *
5 * Permission to use, copy, modify, and distribute this software and its
6 * documentation under the terms of the GNU General Public License is hereby
7 * granted. No representations are made about the suitability of this software
8 * for any purpose. It is provided "as is" without express or implied warranty.
9 * See the GNU General Public License for more details.
10 *
11 * Documents produced by Doxygen are derivative works derived from the
12 * input used in their production; they are not affected by this license.
13 *
14 */
15
16// own header
17#include "doxygen.h"
18
19// standard includes
20#include <algorithm>
21#include <cerrno>
22#include <chrono>
23#include <cinttypes>
24#include <clocale>
25#include <cstdio>
26#include <cstdlib>
27#include <locale>
28#include <memory>
29#include <sys/stat.h>
30#include <unordered_map>
31
32// other includes
33#include "aliases.h"
34#include "arguments.h"
35#include "cite.h"
36#include "clangparser.h"
37#include "classlist.h"
38#include "cmdmapper.h"
39#include "code.h"
40#include "commentcnv.h"
41#include "conceptdef.h"
42#include "config.h"
43#include "debug.h"
44#include "declinfo.h"
45#include "defargs.h"
46#include "defgen.h"
47#include "dir.h"
48#include "dirdef.h"
49#include "docbookgen.h"
50#include "docparser.h"
51#include "docsets.h"
52#include "dot.h"
53#include "eclipsehelp.h"
54#include "emoji.h"
55#include "entry.h"
56#include "fileinfo.h"
57#include "filename.h"
58#include "fileparser.h"
59#include "formula.h"
60#include "fortrancode.h"
61#include "fortranscanner.h"
62#include "ftvhelp.h"
63#include "groupdef.h"
64#include "htags.h"
65#include "htmlgen.h"
66#include "htmlhelp.h"
67#include "index.h"
68#include "indexlist.h"
69#include "language.h"
70#include "latexgen.h"
71#include "layout.h"
72#include "lexcode.h"
73#include "lexscanner.h"
74#include "mangen.h"
75#include "markdown.h"
76#include "membergroup.h"
77#include "memberlist.h"
78#include "membername.h"
79#include "mermaid.h"
80#include "message.h"
81#include "moduledef.h"
82#include "msc.h"
83#include "namespacedef.h"
84#include "outputlist.h"
85#include "pagedef.h"
86#include "parserintf.h"
87#include "perlmodgen.h"
88#include "plantuml.h"
89#include "portable.h"
90#include "pre.h"
91#include "pycode.h"
92#include "pyscanner.h"
93#include "qhp.h"
94#include "reflist.h"
95#include "regex.h"
96#include "requirement.h"
97#include "rtfgen.h"
98#include "scanner.h"
99#include "searchindex_js.h"
100#include "settings.h"
101#include "singlecomment.h"
102#include "sitemap.h"
103#include "sqlcode.h"
104#include "sqlite3gen.h"
105#include "stlsupport.h"
106#include "stringutil.h"
107#include "symbolresolver.h"
108#include "tagreader.h"
109#include "threadpool.h"
110#include "trace.h"
111#include "util.h"
112#include "version.h"
113#include "vhdlcode.h"
114#include "vhdldocgen.h"
115#include "vhdljjparser.h"
116#include "xmlcode.h"
117#include "xmlgen.h"
118
119#include <sqlite3.h>
120
121#if USE_LIBCLANG
122#if defined(__GNUC__)
123#pragma GCC diagnostic push
124#pragma GCC diagnostic ignored "-Wshadow"
125#endif
126#include <clang/Basic/Version.h>
127#if defined(__GNUC__)
128#pragma GCC diagnostic pop
129#endif
130#endif
131
132// provided by the generated file resources.cpp
133extern void initResources();
134
135#if !defined(_WIN32) || defined(__CYGWIN__)
136#include <signal.h>
137#define HAS_SIGNALS
138#endif
139
140// globally accessible variables
152FileNameLinkedMap *Doxygen::includeNameLinkedMap = nullptr; // include names
158FileNameLinkedMap *Doxygen::plantUmlFileNameLinkedMap = nullptr;// plantuml files
159FileNameLinkedMap *Doxygen::mermaidFileNameLinkedMap = nullptr; // mermaid files
161StringMap Doxygen::tagDestinationMap; // all tag locations
162StringUnorderedSet Doxygen::tagFileSet; // all tag file names
163StringUnorderedSet Doxygen::expandAsDefinedSet; // all macros that should be expanded
164MemberGroupInfoMap Doxygen::memberGroupInfoMap; // dictionary of the member groups heading
165std::unique_ptr<PageDef> Doxygen::mainPage;
166std::unique_ptr<NamespaceDef> Doxygen::globalNamespaceDef;
168bool Doxygen::parseSourcesNeeded = false;
186std::mutex Doxygen::addExampleMutex;
188
189// locally accessible globals
190static std::multimap< std::string, const Entry* > g_classEntries;
192static OutputList *g_outputList = nullptr; // list of output generating objects
193static StringSet g_usingDeclarations; // used classes
194static bool g_successfulRun = false;
195static bool g_dumpSymbolMap = false;
197static bool g_singleComment=false;
198
199
200
201// keywords recognized as compounds
203{ "template class", "template struct", "class", "struct", "union", "interface", "exception" };
204
232
234{
235 public:
237 void begin(const char *name)
238 {
239 msg("{}", name);
240 stats.emplace_back(name,0);
241 startTime = std::chrono::steady_clock::now();
242 }
243 void end()
244 {
245 std::chrono::steady_clock::time_point endTime = std::chrono::steady_clock::now();
246 stats.back().elapsed = static_cast<double>(std::chrono::duration_cast<
247 std::chrono::microseconds>(endTime - startTime).count())/1000000.0;
248 warn_flush();
249 }
250 void print()
251 {
252 bool restore=false;
254 {
256 restore=true;
257 }
258 msg("----------------------\n");
259 for (const auto &s : stats)
260 {
261 msg("Spent {:.6f} seconds in {}",s.elapsed,s.name);
262 }
263 if (restore) Debug::setFlag(Debug::Time);
264 }
265 private:
266 struct stat
267 {
268 const char *name;
269 double elapsed;
270 //stat() : name(nullptr),elapsed(0) {}
271 stat(const char *n, double el) : name(n),elapsed(el) {}
272 };
273 std::vector<stat> stats;
274 std::chrono::steady_clock::time_point startTime;
276
277
278static void addMemberDocs(const Entry *root,MemberDefMutable *md, const DString &funcDecl,
279 const ArgumentList *al,bool over_load,TypeSpecifier spec);
280static void findMember(const Entry *root,
281 const DString &relates,
282 const DString &type,
283 const DString &args,
284 DString funcDecl,
285 bool overloaded,
286 bool isFunc
287 );
288
295
296
297static bool findClassRelation(
298 const Entry *root,
299 Definition *context,
300 ClassDefMutable *cd,
301 const BaseInfo *bi,
302 const TemplateNameMap &templateNames,
303 /*bool insertUndocumented*/
305 bool isArtificial
306 );
307
308//----------------------------------------------------------------------
309
310template<class Def>
311static void addIncludeFile(Def *def,FileDef *ifd,const Entry *root)
312{
313 AUTO_TRACE();
314 if (
315 (!root->doc.stripWhiteSpace().empty() ||
316 !root->brief.stripWhiteSpace().empty() ||
317 Config_getBool(EXTRACT_ALL)
318 ) && root->protection!=Protection::Private
319 )
320 {
321 AUTO_TRACE_ADD("includefile={}",root->includeFile);
322 //printf(">>>>>> includeFile=%s\n",qPrint(root->includeFile));
323
324 bool local=Config_getBool(FORCE_LOCAL_INCLUDES);
325 DString includeFile = root->includeFile;
326 if (!includeFile.empty() && includeFile.at(0)=='"')
327 {
328 local = true;
329 includeFile=includeFile.mid(1,includeFile.length()-2);
330 }
331 else if (!includeFile.empty() && includeFile.at(0)=='<')
332 {
333 local = false;
334 includeFile=includeFile.mid(1,includeFile.length()-2);
335 }
336
337 bool ambig = false;
338 FileDef *fd=nullptr;
339 // see if we need to include a verbatim copy of the header file
340 //printf("root->includeFile=%s\n",qPrint(root->includeFile));
341 if (!includeFile.empty() &&
342 (fd=Doxygen::inputNameLinkedMap->findFileDef(includeFile,ambig))==nullptr
343 )
344 { // explicit request
345 DString text;
346 text.sprintf("the name '%s' supplied as "
347 "the argument of the \\class, \\struct, \\union, or \\headerfile command ",
348 qPrint(includeFile)
349 );
350 if (ambig) // name is ambiguous
351 {
352 text+="matches the following input files:\n";
354 text+="\n";
355 text+="Please use a more specific name by "
356 "including a (larger) part of the path!";
357 }
358 else // name is not an input file
359 {
360 text+="is not an input file";
361 }
362 warn(root->fileName,root->startLine, "{}", text);
363 }
364 else if (includeFile.empty() && ifd &&
365 // see if the file extension makes sense
366 EntryType::guessSection(ifd->name()).isHeader())
367 { // implicit assumption
368 fd=ifd;
369 }
370
371 // if a file is found, we mark it as a source file.
372 if (fd)
373 {
374 DString iName = !root->includeName.empty() ?
375 root->includeName : includeFile;
376 if (!iName.empty()) // user specified include file
377 {
378 if (iName.at(0)=='<') local=false; // explicit override
379 else if (iName.at(0)=='"') local=true;
380 if (iName.at(0)=='"' || iName.at(0)=='<')
381 {
382 iName=iName.mid(1,iName.length()-2); // strip quotes or brackets
383 }
384 if (iName.empty())
385 {
386 iName=fd->name();
387 }
388 }
389 else if (!Config_getList(STRIP_FROM_INC_PATH).empty())
390 {
392 }
393 else // use name of the file containing the class definition
394 {
395 iName=fd->name();
396 }
397 AUTO_TRACE_ADD("iName={}",iName);
398 if (fd->generateSourceFile()) // generate code for header
399 {
400 def->setIncludeFile(fd,iName,local,!root->includeName.empty());
401 }
402 else // put #include in the class documentation without link
403 {
404 def->setIncludeFile(nullptr,iName,local,true);
405 }
406 }
407 }
408}
409
410
411//----------------------------------------------------------------------------
412
414 FileDef *fileScope,const TagInfo *tagInfo);
415static void resolveTemplateInstanceInType(const Entry *root,const Definition *scope,const MemberDef *md);
416
417static void addPageToContext(PageDef *pd,Entry *root)
418{
419 if (root->parent()) // add the page to it's scope
420 {
421 DString scope = root->parent()->name;
422 if (root->parent()->section.isPackageDoc())
423 {
424 scope=substitute(scope,".","::");
425 }
426 scope = stripAnonymousNamespaceScope(scope);
427 scope+="::"+pd->name();
429 if (d)
430 {
431 pd->setPageScope(d);
432 }
433 }
434}
435
436static void addRelatedPage(Entry *root)
437{
438 GroupDef *gd=nullptr;
439 for (const Grouping &g : root->groups)
440 {
441 if (!g.groupname.empty() && (gd=Doxygen::groupLinkedMap->find(g.groupname))) break;
442 }
443 //printf("---> addRelatedPage() %s gd=%p\n",qPrint(root->name),gd);
444 DString doc=root->doc+root->inbodyDocs;
445
446 PageDef *pd = addRelatedPage(root->name, // name
447 root->args, // ptitle
448 doc, // doc
449 root->docFile, // fileName
450 root->docLine, // docLine
451 root->startLine, // startLine
452 root->sli, // sli
453 gd, // gd
454 root->tagInfo(), // tagInfo
455 false, // xref
456 root->lang // lang
457 );
458 if (pd)
459 {
460 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
462 pd->setLocalToc(root->localToc);
463 addPageToContext(pd,root);
464 }
465}
466
467static void buildGroupListFiltered(const Entry *root,bool additional, bool includeExternal)
468{
469 if (root->section.isGroupDoc() && !root->name.empty() &&
470 ((!includeExternal && root->tagInfo()==nullptr) ||
471 ( includeExternal && root->tagInfo()!=nullptr))
472 )
473 {
474 AUTO_TRACE("additional={} includeExternal={}",additional,includeExternal);
475 if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
476 (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
477 {
479 AUTO_TRACE_ADD("Processing group '{}':'{}' gd={}", root->type,root->name,(void*)gd);
480
481 if (gd)
482 {
483 if ( !gd->hasGroupTitle() )
484 {
485 gd->setGroupTitle( root->type );
486 }
487 else if ( root->type.length() > 0 && root->name != root->type && gd->groupTitle() != root->type )
488 {
489 warn( root->fileName,root->startLine,
490 "group {}: ignoring title \"{}\" that does not match old title \"{}\"",
491 root->name, root->type, gd->groupTitle() );
492 }
493 gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
494 gd->setDocumentation( root->doc, root->docFile, root->docLine );
495 gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
497 gd->setRefItems(root->sli);
499 gd->setLanguage(root->lang);
501 {
502 root->commandOverrides.apply_groupGraph([&](bool b) { gd->overrideGroupGraph(b); });
503 }
504 addIncludeFile(gd,nullptr,root);
505 }
506 else
507 {
508 if (root->tagInfo())
509 {
510 gd = Doxygen::groupLinkedMap->add(root->name,
511 std::unique_ptr<GroupDef>(
512 createGroupDef(root->fileName,root->startLine,root->name,root->type,root->tagInfo()->fileName)));
513 gd->setReference(root->tagInfo()->tagName);
514 }
515 else
516 {
517 gd = Doxygen::groupLinkedMap->add(root->name,
518 std::unique_ptr<GroupDef>(
519 createGroupDef(root->fileName,root->startLine,root->name,root->type)));
520 }
521 gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
522 // allow empty docs for group
523 gd->setDocumentation(!root->doc.empty() ? root->doc : DString(" "),root->docFile,root->docLine,false);
524 gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
526 gd->setRefItems(root->sli);
528 gd->setLanguage(root->lang);
529 addIncludeFile(gd,nullptr,root);
531 {
532 root->commandOverrides.apply_groupGraph([&](bool b) { gd->overrideGroupGraph(b); });
533 }
534 }
535 }
536 }
537 for (const auto &e : root->children()) buildGroupListFiltered(e.get(),additional,includeExternal);
538}
539
540static void buildGroupList(const Entry *root)
541{
542 // --- first process only local groups
543 // first process the @defgroups blocks
544 buildGroupListFiltered(root,false,false);
545 // then process the @addtogroup, @weakgroup blocks
546 buildGroupListFiltered(root,true,false);
547
548 // --- then also process external groups
549 // first process the @defgroups blocks
550 buildGroupListFiltered(root,false,true);
551 // then process the @addtogroup, @weakgroup blocks
552 buildGroupListFiltered(root,true,true);
553}
554
555static void findGroupScope(const Entry *root)
556{
557 if (root->section.isGroupDoc() && !root->name.empty() &&
558 root->parent() && !root->parent()->name.empty())
559 {
561 if (gd)
562 {
563 DString scope = root->parent()->name;
564 if (root->parent()->section.isPackageDoc())
565 {
566 scope=substitute(scope,".","::");
567 }
568 scope = stripAnonymousNamespaceScope(scope);
569 scope+="::"+gd->name();
571 if (d)
572 {
573 gd->setGroupScope(d);
574 }
575 }
576 }
577 for (const auto &e : root->children()) findGroupScope(e.get());
578}
579
580static void organizeSubGroupsFiltered(const Entry *root,bool additional)
581{
582 if (root->section.isGroupDoc() && !root->name.empty())
583 {
584 AUTO_TRACE("additional={}",additional);
585 if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
586 (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
587 {
589 if (gd)
590 {
591 AUTO_TRACE_ADD("adding {} to group {}",root->name,gd->name());
592 addGroupToGroups(root,gd);
593 }
594 }
595 }
596 for (const auto &e : root->children()) organizeSubGroupsFiltered(e.get(),additional);
597}
598
599static void organizeSubGroups(const Entry *root)
600{
601 //printf("Defining groups\n");
602 // first process the @defgroups blocks
603 organizeSubGroupsFiltered(root,false);
604 //printf("Additional groups\n");
605 // then process the @addtogroup, @weakgroup blocks
606 organizeSubGroupsFiltered(root,true);
607}
608
609//----------------------------------------------------------------------
610
611static void buildFileList(const Entry *root)
612{
613 if ((root->section.isFileDoc() || (root->section.isFile() && Config_getBool(EXTRACT_ALL))) &&
614 !root->name.empty() && !root->tagInfo() // skip any file coming from tag files
615 )
616 {
617 bool ambig = false;
619 if (!fd || ambig)
620 {
621 bool save_ambig = ambig;
622 // use the directory of the file to see if the described file is in the same
623 // directory as the describing file.
624 DString fn = root->fileName;
625 size_t newIndex=fn.rfind('/');
626 if (newIndex==DString::npos)
627 {
628 fn = root->name;
629 }
630 else
631 {
632 fn = fn.left(newIndex)+"/"+root->name;
633 }
635 if (!fd) ambig = save_ambig;
636 }
637 //printf("**************** root->name=%s fd=%p\n",qPrint(root->name),(void*)fd);
638 if (fd && !ambig)
639 {
640 //printf("Adding documentation!\n");
641 // using false in setDocumentation is small hack to make sure a file
642 // is documented even if a \file command is used without further
643 // documentation
644 fd->setDocumentation(root->doc,root->docFile,root->docLine,false);
645 fd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
647 fd->setRefItems(root->sli);
649 addIncludeFile(fd,fd,root);
650 root->commandOverrides.apply_includeGraph ([&](bool b) { fd->overrideIncludeGraph(b); });
651 root->commandOverrides.apply_includedByGraph([&](bool b) { fd->overrideIncludedByGraph(b); });
652 for (const Grouping &g : root->groups)
653 {
654 GroupDef *gd=nullptr;
656 {
657 if (!gd->containsFile(fd))
658 {
659 gd->addFile(fd);
660 fd->makePartOfGroup(gd);
661 //printf("File %s: in group %s\n",qPrint(fd->name()),qPrint(gd->name()));
662 }
663 }
664 else if (!gd && g.pri == Grouping::GROUPING_INGROUP)
665 {
666 warn(root->fileName, root->startLine,
667 "Found non-existing group '{}' for the command '{}', ignoring command",
669 );
670 }
671 }
672 }
673 else
674 {
675 DString text(4096, DString::ExplicitSize);
676 text.sprintf("the name '%s' supplied as "
677 "the argument in the \\file statement ",
678 qPrint(root->name));
679 if (ambig) // name is ambiguous
680 {
681 text+="matches the following input files:\n";
683 text+="\n";
684 text+="Please use a more specific name by "
685 "including a (larger) part of the path!";
686 }
687 else // name is not an input file
688 {
689 text+="is not an input file";
690 }
691 warn(root->fileName,root->startLine,"{}", text);
692 }
693 }
694 for (const auto &e : root->children()) buildFileList(e.get());
695}
696
698{
699 size_t l = s.length();
700 int count=0;
701 int round=0;
702 DString result;
703 for (size_t i=0;i<l;i++)
704 {
705 char c=s.at(i);
706 if (c=='(') round++;
707 else if (c==')' && round>0) round--;
708 else if (c=='<' && round==0) count++;
709 if (count==0)
710 {
711 result+=c;
712 }
713 if (c=='>' && round==0 && count>0) count--;
714 }
715 //printf("stripTemplateSpecifiers(%s)=%s\n",qPrint(s),qPrint(result));
716 return result;
717}
718
719/*! returns the Definition object belonging to the first \a level levels of
720 * full qualified name \a name. Creates an artificial scope if the scope is
721 * not found and set the parent/child scope relation if the scope is found.
722 */
723[[maybe_unused]]
724static Definition *buildScopeFromQualifiedName(const DString &name_,SrcLangExt lang,const TagInfo *tagInfo)
725{
726 DString name = stripTemplateSpecifiers(name_);
727 name.stripPrefix("::");
728 int level = name.contains("::");
729 //printf("buildScopeFromQualifiedName(%s) level=%d\n",qPrint(name),level);
730 int i=0, p=0, l=0;
732 DString fullScope;
733 while (i<level)
734 {
735 int idx=getScopeFragment(name,p,&l);
736 if (idx==-1) return prevScope;
737 DString nsName = name.mid(idx,l);
738 if (nsName.empty()) return prevScope;
739 if (!fullScope.empty()) fullScope+="::";
740 fullScope+=nsName;
742 DefinitionMutable *innerScope = toDefinitionMutable(nd);
743 ClassDef *cd=nullptr;
744 if (nd==nullptr) cd = getClass(fullScope);
745 if (nd==nullptr && cd) // scope is a class
746 {
747 innerScope = toDefinitionMutable(cd);
748 }
749 else if (nd==nullptr && cd==nullptr && fullScope.find('<')==DString::npos) // scope is not known and could be a namespace!
750 {
751 // introduce bogus namespace
752 //printf("++ adding dummy namespace %s to %s tagInfo=%p\n",qPrint(nsName),qPrint(prevScope->name()),(void*)tagInfo);
753 NamespaceDefMutable *newNd=
755 Doxygen::namespaceLinkedMap->add(fullScope,
757 "[generated]",1,1,fullScope,
758 tagInfo?tagInfo->tagName:DString(),
759 tagInfo?tagInfo->fileName:DString())));
760 if (newNd)
761 {
762 newNd->setLanguage(lang);
763 newNd->setArtificial(true);
764 // add namespace to the list
765 innerScope = newNd;
766 }
767 }
768 else // scope is a namespace
769 {
770 }
771 if (innerScope)
772 {
773 // make the parent/child scope relation
774 DefinitionMutable *prevScopeMutable = toDefinitionMutable(prevScope);
775 if (prevScopeMutable)
776 {
777 prevScopeMutable->addInnerCompound(toDefinition(innerScope));
778 }
779 innerScope->setOuterScope(prevScope);
780 }
781 else // current scope is a class, so return only the namespace part...
782 {
783 return prevScope;
784 }
785 // proceed to the next scope fragment
786 p=idx+l+2;
787 prevScope=toDefinition(innerScope);
788 i++;
789 }
790 return prevScope;
791}
792
794 FileDef *fileScope,const TagInfo *tagInfo)
795{
796 //printf("<findScopeFromQualifiedName(%s,%s)\n",startScope ? qPrint(startScope->name()) : 0, qPrint(n));
797 Definition *resultScope=toDefinition(startScope);
798 if (resultScope==nullptr) resultScope=Doxygen::globalScope;
800 int l1 = 0;
801 int i1 = getScopeFragment(scope,0,&l1);
802 if (i1==-1)
803 {
804 //printf(">no fragments!\n");
805 return resultScope;
806 }
807 int p=i1+l1,l2=0,i2=0;
808 while ((i2=getScopeFragment(scope,p,&l2))!=-1)
809 {
810 DString nestedNameSpecifier = scope.mid(i1,l1);
811 Definition *orgScope = resultScope;
812 //printf(" nestedNameSpecifier=%s\n",qPrint(nestedNameSpecifier));
813 resultScope = const_cast<Definition*>(resultScope->findInnerCompound(nestedNameSpecifier));
814 //printf(" resultScope=%p\n",resultScope);
815 if (resultScope==nullptr)
816 {
817 if (orgScope==Doxygen::globalScope && fileScope && !fileScope->getUsedNamespaces().empty())
818 // also search for used namespaces
819 {
820 for (const auto &nd : fileScope->getUsedNamespaces())
821 {
823 if (mnd)
824 {
825 resultScope = findScopeFromQualifiedName(toNamespaceDefMutable(nd),n,fileScope,tagInfo);
826 if (resultScope!=nullptr) break;
827 }
828 }
829 if (resultScope)
830 {
831 // for a nested class A::I in used namespace N, we get
832 // N::A::I while looking for A, so we should compare
833 // resultScope->name() against scope.left(i2+l2)
834 //printf(" -> result=%s scope=%s\n",qPrint(resultScope->name()),qPrint(scope));
835 if (rightScopeMatch(resultScope->name(),scope.left(i2+l2)))
836 {
837 break;
838 }
839 goto nextFragment;
840 }
841 }
842
843 // also search for used classes. Complication: we haven't been able
844 // to put them in the right scope yet, because we are still resolving
845 // the scope relations!
846 // Therefore loop through all used classes and see if there is a right
847 // scope match between the used class and nestedNameSpecifier.
848 for (const auto &usedName : g_usingDeclarations)
849 {
850 //printf("Checking using class %s\n",qPrint(usedName));
851 if (rightScopeMatch(usedName,nestedNameSpecifier))
852 {
853 // ui.currentKey() is the fully qualified name of nestedNameSpecifier
854 // so use this instead.
855 DString fqn = usedName + scope.mid(p);
856 resultScope = buildScopeFromQualifiedName(fqn,startScope->getLanguage(),nullptr);
857 //printf("Creating scope from fqn=%s result %p\n",qPrint(fqn),resultScope);
858 if (resultScope)
859 {
860 //printf("> Match! resultScope=%s\n",qPrint(resultScope->name()));
861 return resultScope;
862 }
863 }
864 }
865
866 //printf("> name %s not found in scope %s\n",qPrint(nestedNameSpecifier),qPrint(orgScope->name()));
867 return nullptr;
868 }
869 nextFragment:
870 i1=i2;
871 l1=l2;
872 p=i2+l2;
873 }
874 //printf(">findScopeFromQualifiedName scope %s\n",qPrint(resultScope->name()));
875 return resultScope;
876}
877
878std::unique_ptr<ArgumentList> getTemplateArgumentsFromName(
879 const DString &name,
880 const ArgumentLists &tArgLists)
881{
882 // for each scope fragment, check if it is a template and advance through
883 // the list if so.
884 size_t i=0, p=0;
885 auto alIt = tArgLists.begin();
886 while ((i=name.find("::",p))!=DString::npos && alIt!=tArgLists.end())
887 {
889 if (nd==nullptr)
890 {
891 ClassDef *cd = getClass(name.left(i));
892 if (cd)
893 {
894 if (!cd->templateArguments().empty())
895 {
896 ++alIt;
897 }
898 }
899 }
900 p=i+2;
901 }
902 return alIt!=tArgLists.end() ?
903 std::make_unique<ArgumentList>(*alIt) :
904 std::unique_ptr<ArgumentList>();
905}
906
907static
909{
911
912 if (specifier.isStruct())
914 else if (specifier.isUnion())
915 sec=ClassDef::Union;
916 else if (specifier.isCategory())
918 else if (specifier.isInterface())
920 else if (specifier.isProtocol())
922 else if (specifier.isException())
924 else if (specifier.isService())
926 else if (specifier.isSingleton())
928
929 if (section.isUnionDoc())
930 sec=ClassDef::Union;
931 else if (section.isStructDoc())
933 else if (section.isInterfaceDoc())
935 else if (section.isProtocolDoc())
937 else if (section.isCategoryDoc())
939 else if (section.isExceptionDoc())
941 else if (section.isServiceDoc())
943 else if (section.isSingletonDoc())
945
946 return sec;
947}
948
949
950static void addClassToContext(const Entry *root)
951{
952 AUTO_TRACE("name={}",root->name);
953 FileDef *fd = root->fileDef();
954
955 DString scName;
956 if (root->parent()->section.isScope())
957 {
958 scName=root->parent()->name;
959 }
960 // name without parent's scope
961 DString fullName = root->name;
962
963 // strip off any template parameters (but not those for specializations)
964 if (size_t idx=fullName.find('>'); idx!=DString::npos && root->lang==SrcLangExt::CSharp) // mangle A<S,T>::N as A-2-g::N
965 {
966 fullName = mangleCSharpGenericName(fullName.left(idx+1))+fullName.mid(idx+1);
967 }
968 fullName=stripTemplateSpecifiersFromScope(fullName);
969
970 // name with scope (if not present already)
971 DString qualifiedName = fullName;
972 if (!scName.empty() && !leftScopeMatch(scName,fullName))
973 {
974 qualifiedName.prepend(scName+"::");
975 }
976
977 // see if we already found the class before
978 ClassDefMutable *cd = getClassMutable(qualifiedName);
979
980 AUTO_TRACE_ADD("Found class with name '{}', qualifiedName '{}'", cd ? cd->name() : root->name, qualifiedName);
981
982 if (cd)
983 {
984 fullName=cd->name();
985 AUTO_TRACE_ADD("Existing class '{}'",cd->name());
986 //if (cd->templateArguments()==0)
987 //{
988 // //printf("existing ClassDef tempArgList=%p specScope=%s\n",root->tArgList,qPrint(root->scopeSpec));
989 // cd->setTemplateArguments(tArgList);
990 //}
991
992 cd->setDocumentation(root->doc,root->docFile,root->docLine);
993 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
994 root->commandOverrides.apply_collaborationGraph([&](bool b ) { cd->overrideCollaborationGraph(b); });
995 root->commandOverrides.apply_inheritanceGraph ([&](CLASS_GRAPH_t gt) { cd->overrideInheritanceGraph(gt); });
996
997 if (!root->spec.isForwardDecl() && cd->isForwardDeclared())
998 {
999 cd->setDefFile(root->fileName,root->startLine,root->startColumn);
1000 if (root->bodyLine!=-1)
1001 {
1002 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1003 cd->setBodyDef(fd);
1004 }
1005 }
1006
1007 if (cd->templateArguments().empty() || (cd->isForwardDeclared() && !root->spec.isForwardDecl()))
1008 {
1009 // this happens if a template class declared with @class is found
1010 // before the actual definition or if a forward declaration has different template
1011 // parameter names.
1012 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(cd->name(),root->tArgLists);
1013 if (tArgList)
1014 {
1015 cd->setTemplateArguments(*tArgList);
1016 }
1017 }
1018 if (cd->requiresClause().empty() && !root->req.empty())
1019 {
1020 cd->setRequiresClause(root->req);
1021 }
1022
1024
1025 cd->setMetaData(root->metaData);
1026 }
1027 else // new class
1028 {
1030
1031 DString className;
1032 DString namespaceName;
1033 extractNamespaceName(fullName,className,namespaceName);
1034
1035 AUTO_TRACE_ADD("New class: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1036 fullName, namespaceName, className, Trace::trunc(root->brief), Trace::trunc(root->doc));
1037
1038 DString tagName;
1039 DString refFileName;
1040 const TagInfo *tagInfo = root->tagInfo();
1041 if (tagInfo)
1042 {
1043 tagName = tagInfo->tagName;
1044 refFileName = tagInfo->fileName;
1045 if (fullName.find("::")!=DString::npos)
1046 // symbols imported via tag files may come without the parent scope,
1047 // so we artificially create it here
1048 {
1049 buildScopeFromQualifiedName(fullName,root->lang,tagInfo);
1050 }
1051 }
1052 std::unique_ptr<ArgumentList> tArgList;
1053 size_t i=0;
1054 if ((root->lang==SrcLangExt::CSharp || root->lang==SrcLangExt::Java) &&
1055 (i=fullName.find('<'))!=DString::npos)
1056 {
1057 // a Java/C# generic class looks like a C++ specialization, so we need to split the
1058 // name and template arguments here
1059 tArgList = stringToArgumentList(root->lang,fullName.mid(i));
1060 if (i!=DString::npos && root->lang==SrcLangExt::CSharp) // in C# A, A<T>, and A<T,S> are different classes, so we need some way to disguish them using this name mangling
1061 // A -> A
1062 // A<T> -> A-1-g
1063 // A<T,S> -> A-2-g
1064 {
1065 fullName=mangleCSharpGenericName(fullName);
1066 }
1067 else
1068 {
1069 fullName=fullName.left(i);
1070 }
1071 }
1072 else
1073 {
1074 tArgList = getTemplateArgumentsFromName(fullName,root->tArgLists);
1075 }
1076 // add class to the list
1077 cd = toClassDefMutable(
1078 Doxygen::classLinkedMap->add(fullName,
1079 createClassDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1080 fullName,sec,tagName,refFileName,true,root->spec.isEnum()) ));
1081 if (cd)
1082 {
1083 AUTO_TRACE_ADD("New class '{}' type={} #tArgLists={} tagInfo={} hidden={} artificial={}",
1084 fullName,cd->compoundTypeString(),root->tArgLists.size(),
1085 fmt::ptr(tagInfo),root->hidden,root->artificial);
1086 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1087 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1088 cd->setLanguage(root->lang);
1089 cd->setId(root->id);
1090 cd->setHidden(root->hidden);
1091 cd->setArtificial(root->artificial);
1092 cd->setClassSpecifier(root->spec);
1093 if (root->lang==SrcLangExt::CSharp && !root->args.empty())
1094 {
1096 }
1097 cd->addQualifiers(root->qualifiers);
1098 cd->setTypeConstraints(root->typeConstr);
1099 root->commandOverrides.apply_collaborationGraph([&](bool b ) { cd->overrideCollaborationGraph(b); });
1100 root->commandOverrides.apply_inheritanceGraph ([&](CLASS_GRAPH_t gt) { cd->overrideInheritanceGraph(gt); });
1101
1102 if (tArgList)
1103 {
1104 cd->setTemplateArguments(*tArgList);
1105 }
1106 cd->setRequiresClause(root->req);
1107 cd->setProtection(root->protection);
1108 cd->setIsStatic(root->isStatic);
1109
1110 // file definition containing the class cd
1111 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1112 cd->setBodyDef(fd);
1113
1114 cd->setMetaData(root->metaData);
1115
1116 cd->insertUsedFile(fd);
1117 }
1118 else
1119 {
1120 AUTO_TRACE_ADD("Class {} not added, already exists as alias", fullName);
1121 }
1122 }
1123
1124 if (cd)
1125 {
1127 if (!root->subGrouping) cd->setSubGrouping(false);
1128 if (!root->spec.isForwardDecl())
1129 {
1130 if (cd->hasDocumentation())
1131 {
1132 addIncludeFile(cd,fd,root);
1133 }
1134 if (fd && root->section.isCompound())
1135 {
1136 AUTO_TRACE_ADD("Inserting class {} in file {} (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1137 cd->setFileDef(fd);
1138 fd->insertClass(cd);
1139 }
1140 }
1141 addClassToGroups(root,cd);
1143 cd->setRefItems(root->sli);
1144 cd->setRequirementReferences(root->rqli);
1145 }
1146}
1147
1148//----------------------------------------------------------------------
1149// build a list of all classes mentioned in the documentation
1150// and all classes that have a documentation block before their definition.
1151static void buildClassList(const Entry *root)
1152{
1153 if ((root->section.isCompound() || root->section.isObjcImpl()) && !root->name.empty())
1154 {
1155 AUTO_TRACE();
1156 addClassToContext(root);
1157 }
1158 for (const auto &e : root->children()) buildClassList(e.get());
1159}
1160
1161static void buildClassDocList(const Entry *root)
1162{
1163 if ((root->section.isCompoundDoc()) && !root->name.empty())
1164 {
1165 AUTO_TRACE();
1166 addClassToContext(root);
1167 }
1168 for (const auto &e : root->children()) buildClassDocList(e.get());
1169}
1170
1171//----------------------------------------------------------------------
1172// build a list of all classes mentioned in the documentation
1173// and all classes that have a documentation block before their definition.
1174
1175static void addConceptToContext(const Entry *root)
1176{
1177 AUTO_TRACE();
1178 FileDef *fd = root->fileDef();
1179
1180 DString scName;
1181 if (root->parent()->section.isScope())
1182 {
1183 scName=root->parent()->name;
1184 }
1185
1186 // name with scope (if not present already)
1187 DString qualifiedName = root->name;
1188 if (!scName.empty() && !leftScopeMatch(qualifiedName,scName))
1189 {
1190 qualifiedName.prepend(scName+"::");
1191 }
1192
1193 // see if we already found the concept before
1194 ConceptDefMutable *cd = getConceptMutable(qualifiedName);
1195
1196 AUTO_TRACE_ADD("Found concept with name '{}' (qualifiedName='{}')", cd ? cd->name() : root->name, qualifiedName);
1197
1198 if (cd)
1199 {
1200 qualifiedName=cd->name();
1201 AUTO_TRACE_ADD("Existing concept '{}'",cd->name());
1202
1203 cd->setDocumentation(root->doc,root->docFile,root->docLine);
1204 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1205
1206 addIncludeFile(cd,fd,root);
1207 }
1208 else // new concept
1209 {
1210 DString className;
1211 DString namespaceName;
1212 extractNamespaceName(qualifiedName,className,namespaceName);
1213
1214 AUTO_TRACE_ADD("New concept: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1215 qualifiedName,namespaceName,className,root->brief,root->doc);
1216
1217 DString tagName;
1218 DString refFileName;
1219 const TagInfo *tagInfo = root->tagInfo();
1220 if (tagInfo)
1221 {
1222 tagName = tagInfo->tagName;
1223 refFileName = tagInfo->fileName;
1224 if (qualifiedName.find("::")!=DString::npos)
1225 // symbols imported via tag files may come without the parent scope,
1226 // so we artificially create it here
1227 {
1228 buildScopeFromQualifiedName(qualifiedName,root->lang,tagInfo);
1229 }
1230 }
1231 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(qualifiedName,root->tArgLists);
1232 // add concept to the list
1234 Doxygen::conceptLinkedMap->add(qualifiedName,
1235 createConceptDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1236 qualifiedName,tagName,refFileName)));
1237 if (cd)
1238 {
1239 AUTO_TRACE_ADD("New concept '{}' #tArgLists={} tagInfo={}",
1240 qualifiedName,root->tArgLists.size(),fmt::ptr(tagInfo));
1241 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1242 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1243 cd->setLanguage(root->lang);
1244 cd->setId(root->id);
1245 cd->setHidden(root->hidden);
1246 cd->setGroupId(root->mGrpId);
1247 if (tArgList)
1248 {
1249 cd->setTemplateArguments(*tArgList);
1250 }
1251 cd->setInitializer(root->initializer.str());
1252 // file definition containing the class cd
1253 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1254 cd->setBodyDef(fd);
1256 cd->setRefItems(root->sli);
1257 cd->setRequirementReferences(root->rqli);
1258 addIncludeFile(cd,fd,root);
1259
1260 // also add namespace to the correct structural context
1261 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,qualifiedName,nullptr,tagInfo);
1263 {
1265 if (dm)
1266 {
1267 dm->addInnerCompound(cd);
1268 }
1269 cd->setOuterScope(d);
1270 }
1271 for (const auto &ce : root->children())
1272 {
1273 //printf("Concept %s has child %s\n",qPrint(root->name),qPrint(ce->section.to_string()));
1274 if (ce->section.isConceptDocPart())
1275 {
1276 cd->addSectionsToDefinition(ce->anchors);
1277 cd->setRefItems(ce->sli);
1278 cd->setRequirementReferences(ce->rqli);
1279 if (!ce->brief.empty())
1280 {
1281 cd->addDocPart(ce->brief,ce->startLine,ce->startColumn);
1282 //printf(" brief=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->brief),ce->startLine,ce->startColumn);
1283 }
1284 if (!ce->doc.empty())
1285 {
1286 cd->addDocPart(ce->doc,ce->startLine,ce->startColumn);
1287 //printf(" doc=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->doc),ce->startLine,ce->startColumn);
1288 }
1289 }
1290 else if (ce->section.isConceptCodePart())
1291 {
1292 cd->addCodePart(ce->initializer.str(),ce->startLine,ce->startColumn);
1293 //printf(" code=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->initializer.str()),ce->startLine,ce->startColumn);
1294 }
1295 }
1296 }
1297 else
1298 {
1299 AUTO_TRACE_ADD("Concept '{}' not added, already exists (as alias)", qualifiedName);
1300 }
1301 }
1302
1303 if (cd)
1304 {
1306 for (const auto &ce : root->children())
1307 {
1308 if (ce->section.isConceptDocPart())
1309 {
1310 cd->addSectionsToDefinition(ce->anchors);
1311 }
1312 }
1313 if (fd)
1314 {
1315 AUTO_TRACE_ADD("Inserting concept '{}' in file '{}' (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1316 cd->setFileDef(fd);
1317 fd->insertConcept(cd);
1318 }
1319 addConceptToGroups(root,cd);
1321 cd->setRefItems(root->sli);
1322 cd->setRequirementReferences(root->rqli);
1323 }
1324}
1325
1326static void findModuleDocumentation(const Entry *root)
1327{
1328 if (root->section.isModuleDoc())
1329 {
1330 AUTO_TRACE();
1332 }
1333 for (const auto &e : root->children()) findModuleDocumentation(e.get());
1334}
1335
1336static void buildConceptList(const Entry *root)
1337{
1338 if (root->section.isConcept())
1339 {
1340 AUTO_TRACE();
1341 addConceptToContext(root);
1342 }
1343 for (const auto &e : root->children()) buildConceptList(e.get());
1344}
1345
1346static void buildConceptDocList(const Entry *root)
1347{
1348 if (root->section.isConceptDoc())
1349 {
1350 AUTO_TRACE();
1351 addConceptToContext(root);
1352 }
1353 for (const auto &e : root->children()) buildConceptDocList(e.get());
1354}
1355
1356// This routine is to allow @ingroup X @{ concept A; concept B; @} to work
1357// (same also works for variable and functions because of logic in MemberGroup::insertMember)
1359{
1360 AUTO_TRACE();
1361 for (const auto &cd : *Doxygen::conceptLinkedMap)
1362 {
1363 if (cd->groupId()!=DOX_NOGROUP)
1364 {
1365 for (const auto &ocd : *Doxygen::conceptLinkedMap)
1366 {
1367 if (cd!=ocd && cd->groupId()==ocd->groupId() &&
1368 !cd->partOfGroups().empty() && ocd->partOfGroups().empty())
1369 {
1370 ConceptDefMutable *ocdm = toConceptDefMutable(ocd.get());
1371 if (ocdm)
1372 {
1373 for (const auto &gd : cd->partOfGroups())
1374 {
1375 if (gd)
1376 {
1377 AUTO_TRACE_ADD("making concept '{}' part of group '{}'",ocdm->name(),gd->name());
1378 ocdm->makePartOfGroup(gd);
1379 gd->addConcept(ocd.get());
1380 }
1381 }
1382 }
1383 }
1384 }
1385 }
1386 }
1387}
1388
1389//----------------------------------------------------------------------
1390
1392{
1393 ClassDefSet visitedClasses;
1394
1395 bool done=false;
1396 //int iteration=0;
1397 while (!done)
1398 {
1399 done=true;
1400 //++iteration;
1401 struct ClassAlias
1402 {
1403 ClassAlias(const DString &name,std::unique_ptr<ClassDef> cd,DefinitionMutable *ctx) :
1404 aliasFullName(name),aliasCd(std::move(cd)), aliasContext(ctx) {}
1405 DString aliasFullName;
1406 std::unique_ptr<ClassDef> aliasCd;
1407 DefinitionMutable *aliasContext;
1408 };
1409 std::vector<ClassAlias> aliases;
1410 for (const auto &icd : *Doxygen::classLinkedMap)
1411 {
1412 ClassDefMutable *cd = toClassDefMutable(icd.get());
1413 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1414 {
1415 DString name = stripAnonymousNamespaceScope(icd->name());
1416 //printf("processing=%s, iteration=%d\n",qPrint(cd->name()),iteration);
1417 // also add class to the correct structural context
1419 name,icd->getFileDef(),nullptr);
1420 if (d)
1421 {
1422 //printf("****** adding %s to scope %s in iteration %d\n",qPrint(cd->name()),qPrint(d->name()),iteration);
1424 if (dm)
1425 {
1426 dm->addInnerCompound(cd);
1427 }
1428 cd->setOuterScope(d);
1429
1430 // for inline namespace add an alias of the class to the outer scope
1432 {
1434 //printf("nd->isInline()=%d\n",nd->isInline());
1435 if (nd && nd->isInline())
1436 {
1437 d = d->getOuterScope();
1438 if (d)
1439 {
1440 dm = toDefinitionMutable(d);
1441 if (dm)
1442 {
1443 auto aliasCd = createClassDefAlias(d,cd);
1444 DString aliasFullName = d->qualifiedName()+"::"+aliasCd->localName();
1445 aliases.emplace_back(aliasFullName,std::move(aliasCd),dm);
1446 //printf("adding %s to %s as %s\n",qPrint(aliasCd->name()),qPrint(d->name()),qPrint(aliasFullName));
1447 }
1448 }
1449 }
1450 else
1451 {
1452 break;
1453 }
1454 }
1455
1456 visitedClasses.insert(icd.get());
1457 done=false;
1458 }
1459 //else
1460 //{
1461 // printf("****** ignoring %s: scope not (yet) found in iteration %d\n",qPrint(cd->name()),iteration);
1462 //}
1463 }
1464 }
1465 // add aliases
1466 for (auto &alias : aliases)
1467 {
1468 ClassDef *aliasCd = Doxygen::classLinkedMap->add(alias.aliasFullName,std::move(alias.aliasCd));
1469 if (aliasCd)
1470 {
1471 alias.aliasContext->addInnerCompound(aliasCd);
1472 }
1473 }
1474 }
1475
1476 //give warnings for unresolved compounds
1477 for (const auto &icd : *Doxygen::classLinkedMap)
1478 {
1479 ClassDefMutable *cd = toClassDefMutable(icd.get());
1480 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1481 {
1483 /// create the scope artificially
1484 // anyway, so we can at least relate scopes properly.
1485 Definition *d = buildScopeFromQualifiedName(name,cd->getLanguage(),nullptr);
1486 if (d && d!=cd && !cd->getDefFileName().empty())
1487 // avoid recursion in case of redundant scopes, i.e: namespace N { class N::C {}; }
1488 // for this case doxygen assumes the existence of a namespace N::N in which C is to be found!
1489 // also avoid warning for stuff imported via a tagfile.
1490 {
1492 if (dm)
1493 {
1494 dm->addInnerCompound(cd);
1495 }
1496 cd->setOuterScope(d);
1497 // A specialization can only be written for a template that has been declared before, so a missing
1498 // scope means that declaration is outside the input - e.g. `template<> struct std::hash<MyClass>`.
1499 if (cd->localName().find('<')==DString::npos)
1500 {
1501 warn(cd->getDefFileName(),cd->getDefLine(),
1502 "Incomplete input: scope for class {} not found!{}",name,
1503 name.startsWith("std::") ? " Try enabling BUILTIN_STL_SUPPORT." : ""
1504 );
1505 }
1506 }
1507 }
1508 }
1509}
1510
1512{
1513 //bool inlineGroupedClasses = Config_getBool(INLINE_GROUPED_CLASSES);
1514 //if (!inlineGroupedClasses) return;
1515 //printf("** distributeClassGroupRelations()\n");
1516
1517 ClassDefSet visitedClasses;
1518 for (const auto &cd : *Doxygen::classLinkedMap)
1519 {
1520 //printf("Checking %s\n",qPrint(cd->name()));
1521 // distribute the group to nested classes as well
1522 if (visitedClasses.find(cd.get())==visitedClasses.end() && !cd->partOfGroups().empty())
1523 {
1524 //printf(" Candidate for merging\n");
1525 GroupDef *gd = cd->partOfGroups().front();
1526 for (auto &ncd : cd->getClasses())
1527 {
1529 if (ncdm && ncdm->partOfGroups().empty())
1530 {
1531 //printf(" Adding %s to group '%s'\n",qPrint(ncd->name()),
1532 // gd->groupTitle());
1533 ncdm->makePartOfGroup(gd);
1534 gd->addClass(ncdm);
1535 }
1536 }
1537 visitedClasses.insert(cd.get()); // only visit every class once
1538 }
1539 }
1540}
1541
1542//----------------------------------------------------------------------
1543
1544template<typename Container>
1546 const Container *cd,
1547 const MemberDef *enumTypeMember,
1548 MemberListType mlFilter)
1549{
1550 if (md && md->isEnumerate() && md->name().startsWith("@")) // anonymous enum type
1551 {
1552 MemberList *eiml = cd->getMemberList(mlFilter);
1553 if (eiml)
1554 {
1555 for (const auto &eimd : *eiml)
1556 {
1557 DString vtype = eimd->typeString();
1558 if (vtype.find(md->name())!=DString::npos)
1559 {
1561 if (mimd)
1562 {
1563 mimd->setAnonymousEnumType(enumTypeMember);
1564 break;
1565 }
1566 }
1567 }
1568 }
1569 }
1570}
1571
1572static ClassDefMutable *createTagLessInstance(const Definition *root,const ClassDef *templ,const DString &fieldName,bool hasNamedVariable)
1573{
1574 DString n = templ->name();
1575 // replace e.g. X::@1343:@4343::Y -> X::[struct]::Y
1576 if (size_t sn = n.find('@'); sn!=DString::npos)
1577 {
1578 const char *p = n.data()+sn;
1579 char c;
1580 while ((c=*p))
1581 {
1582 if (!isdigit(c) && c!='@' && c!=':') break;
1583 p++;
1584 }
1585 n = n.left(sn)+"["+templ->compoundTypeString().str()+"]"+p;
1586 }
1587 // add field name to the class name to make it unique again, e.g. X::[struct]::Y.m
1588 DString fullName = n+"."+fieldName;
1589
1590 //printf("** adding class %s based on %s in %s\n",qPrint(fullName),qPrint(templ->name()),qPrint(root->name()));
1592 Doxygen::classLinkedMap->add(fullName,
1594 templ->getDefLine(),
1595 templ->getDefColumn(),
1596 fullName,
1597 templ->compoundType())));
1598 if (cd)
1599 {
1600 //printf("cd->name()=%s displayName=%s\n",qPrint(cd->name()),qPrint(cd->displayName()));
1601 cd->setDocumentation(templ->documentation(),templ->docFile(),templ->docLine()); // copy docs to definition
1602 cd->setBriefDescription(templ->briefDescription(),templ->briefFile(),templ->briefLine());
1603 cd->setLanguage(templ->getLanguage());
1604 cd->setBodySegment(templ->getDefLine(),templ->getStartBodyLine(),templ->getEndBodyLine());
1605 cd->setBodyDef(templ->getBodyDef());
1606
1607 if (hasNamedVariable)
1608 {
1609 cd->setHasNamedVariable(true);
1610 if (root!=Doxygen::globalScope)
1611 {
1612 DefinitionMutable *outerScope = toDefinitionMutable(const_cast<Definition*>(root));
1613 if (root && root->definitionType()==Definition::TypeFile)
1614 {
1615 FileDef *fd = toFileDef(const_cast<Definition*>(root));
1616 fd->insertClass(cd);
1617 cd->setFileDef(fd);
1619 }
1620 else if (outerScope)
1621 {
1622 outerScope->addInnerCompound(cd);
1623 cd->setOuterScope(const_cast<Definition*>(root));
1624 }
1625 }
1626 }
1627 else
1628 {
1629 cd->setArtificial(true);
1630 }
1631
1632 for (auto &gd : root->partOfGroups())
1633 {
1634 cd->makePartOfGroup(gd);
1635 gd->addClass(cd);
1636 }
1637
1638 auto addMember = [&](const MemberDef *md) -> MemberDefMutable*
1639 {
1640 auto newMd = createMemberDef(md->getDefFileName(),md->getDefLine(),md->getDefColumn(),
1641 md->typeString(),md->name(),md->argsString(),md->excpString(),
1642 md->protection(),md->virtualness(),md->isStatic(),Relationship::Member,
1643 md->memberType(),
1644 ArgumentList(),ArgumentList(),"");
1645 MemberDefMutable *imd = toMemberDefMutable(newMd.get());
1646 imd->setDefinition(md->definition());
1647 imd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
1648 imd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
1649 imd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
1650 imd->setMemberSpecifiers(md->getMemberSpecifiers());
1651 imd->setId(md->id());
1652 imd->addQualifiers(md->getQualifiers());
1653 imd->setVhdlSpecifiers(md->getVhdlSpecifiers());
1654 imd->setMemberGroupId(md->getMemberGroupId());
1655 imd->setInitializer(md->initializer());
1656 imd->setRequiresClause(md->requiresClause());
1657 imd->setMaxInitLines(md->initializerLines());
1658 imd->setBitfields(md->bitfieldString());
1659 imd->setLanguage(md->getLanguage());
1660 imd->setAnchor();
1661 MemberName *mn = Doxygen::memberNameLinkedMap->add(md->name());
1662 mn->push_back(std::move(newMd));
1663 return imd;
1664 };
1665
1666 static auto memberListPairs = std::array<std::pair<MemberListType,MemberListType>,3>{{
1667 { MemberListType::PubAttribs(), MemberListType::PubTypes() },
1668 { MemberListType::ProAttribs(), MemberListType::ProTypes() },
1669 { MemberListType::PriAttribs(), MemberListType::PriTypes() }
1670 }};
1671 for (auto [attribs,types] : memberListPairs)
1672 {
1673 MemberList *ml = templ->getMemberList(attribs);
1674 if (ml)
1675 {
1676 for (const auto &md : *ml)
1677 {
1678 //printf(" Member attribute %s def=%s\n",qPrint(md->name()),qPrint(md->definition()));
1679 MemberDefMutable *imd = addMember(md);
1680 if (!hasNamedVariable && root) // we have an anonymous struct/union without associated member variable,
1681 // so we also need to create a new member to show in the outer scope's detailed section.
1682 // e.g. struct S { union { int a; int b; }; }; needs S::a and S::b as members
1683 {
1684 MemberDefMutable *dimd = addMember(md);
1685 // link the detailed member to the original member
1688 if (root && root->definitionType()==Definition::TypeClass && dimd)
1689 {
1690 ClassDefMutable *cm = toClassDefMutable(const_cast<Definition*>(root));
1691 if (cm)
1692 {
1693 //printf(" Member attribute %s added to class %s\n",qPrint(md->name()),qPrint(cm->name()));
1694 dimd->setMemberClass(cm);
1695 cm->insertMember(dimd);
1696 associateVariableWithAnonymousEnumType(md,cm,dimd,attribs);
1697 }
1698 }
1699 else if (root && root->definitionType()==Definition::TypeFile && dimd)
1700 {
1701 FileDef *fd = toFileDef(const_cast<Definition*>(root));
1702 if (fd)
1703 {
1704 //printf(" Member attribute %s added to file %s\n",qPrint(md->name()),qPrint(fd->name()));
1705 dimd->setOuterScope(fd);
1706 dimd->setFileDef(fd);
1707 fd->insertMember(dimd);
1708 associateVariableWithAnonymousEnumType(md,fd,dimd,attribs);
1709 }
1710 }
1711 else if (root && root->definitionType()==Definition::TypeNamespace && dimd)
1712 {
1713 NamespaceDefMutable *ndm = toNamespaceDefMutable(const_cast<Definition*>(root));
1714 if (ndm)
1715 {
1716 //printf(" Member attribute %s added to namespace %s\n",qPrint(md->name()),qPrint(ndm->name()));
1717 dimd->setOuterScope(ndm);
1718 dimd->setNamespace(ndm);
1719 ndm->insertMember(dimd);
1720 associateVariableWithAnonymousEnumType(md,ndm,dimd,attribs);
1721 }
1722 }
1723 }
1724
1725 imd->setMemberClass(cd);
1726 cd->insertMember(imd);
1727 associateVariableWithAnonymousEnumType(md,cd,imd,attribs);
1728 }
1729 }
1730 ml = templ->getMemberList(types);
1731 if (ml)
1732 {
1733 for (const auto &md : *ml)
1734 {
1735 //printf(" Member type %s def=%s\n",qPrint(md->name()),qPrint(md->definition()));
1736 MemberDefMutable *mdm = addMember(md);
1737 mdm->setMemberClass(cd);
1738 cd->insertMember(mdm);
1740 associateVariableWithAnonymousEnumType(md,cd,mdm,attribs);
1741 if (md->isEnumerate() && md->name().startsWith("@")) // anonymous enum type
1742 {
1743 for (const auto &emd : md->enumFieldList())
1744 {
1745 //printf(" enum field %s\n",qPrint(emd->name()));
1746 MemberDefMutable *emdm = addMember(emd);
1747 mdm->insertEnumField(emdm);
1748 emdm->setEnumScope(md);
1749 emdm->setMemberClass(cd);
1750 cd->insertMember(emdm);
1752 associateVariableWithAnonymousEnumType(md,cd,emdm,attribs);
1753 }
1754 }
1755 }
1756 }
1757 }
1758 }
1759 return cd;
1760}
1761
1762template<typename Container, typename TagContainer>
1763static void processTagLessClasses(const Definition *root,
1764 const Container *def,
1765 const TagContainer *tagParent,
1766 MemberListType varFilter,
1767 MemberListType typeFilter,
1768 const DString &prefix,int count);
1769
1770template<typename Container, typename TagContainer>
1772 const Container *def,
1773 const TagContainer *tagParent,
1774 MemberListType varFilter,
1775 MemberListType typeFilter,
1776 const DString &prefix,int count)
1777{
1778 AUTO_TRACE("count={} name={}\n",count,def->name());
1779 if (tagParent)
1780 {
1781 MemberList *ml = def->getMemberList(varFilter);
1782 if (ml)
1783 {
1784 int pos=0;
1785 // make a copy of the member list, since we may add new members to the list while iterating over it
1786 std::vector<MemberDef *> members;
1787 for (const auto &md : *ml)
1788 {
1789 members.push_back(md);
1790 }
1791 for (const auto &md : members)
1792 {
1793 MemberDefMutable *pmdm = nullptr;
1794 if (tagParent!=def)
1795 {
1796 // associate the variable of the anonymous type with the type member
1797 MemberList *pml = tagParent->getMemberList(varFilter);
1798 if (pml)
1799 {
1800 for (const auto &pmd : *pml)
1801 {
1802 if (pmd->name()==md->name())
1803 {
1804 pmdm = toMemberDefMutable(pmd);
1805 }
1806 }
1807 }
1808 }
1809 else
1810 {
1811 pmdm = toMemberDefMutable(md);
1812 }
1813 bool hasNamedVariable = pmdm==nullptr || !pmdm->isAnonymous();
1814
1815 DString type = md->typeString();
1816 //printf(" member %s: type='%s' outerScope='%s'\n",qPrint(md->name()),qPrint(type),qPrint(md->getOuterScope()?md->getOuterScope()->name():"<null>"));
1817 if ((def->definitionType()!=Definition::TypeFile || md->getOuterScope()==Doxygen::globalScope) && // parse namespace members only if def is a namespace
1818 (type.find("::@")!=DString::npos || type.find(" @")!=DString::npos)) // member of tag less struct/union
1819 {
1820 // make a copy of the class list, since we may add new members to the list while iterating over it
1821 std::vector<const ClassDef *> candidates;
1822 for (const auto &icd : def->getClasses())
1823 {
1824 candidates.push_back(icd);
1825 }
1826 for (const auto &icd : candidates)
1827 {
1828 //printf(" comparing '%s'<->'%s'\n",qPrint(type),qPrint(icd->name()));
1829 if (type.find(icd->name())!=DString::npos) // matching tag less struct/union
1830 {
1831 DString name = md->name();
1832 if (md->isAnonymous()) name = "__unnamed" + DString().setNum(pos++)+"__"; // TODO: remove dead code
1833 if (!prefix.empty()) name.prepend(prefix+".");
1834 //printf(" found %s in scope %s\n",qPrint(name),qPrint(def->name()));
1835 ClassDefMutable *ncd = createTagLessInstance(root,icd,name,hasNamedVariable);
1836 if (ncd)
1837 {
1838 processTagLessClasses(ncd,icd,ncd,MemberListType::PubAttribs(),MemberListType::PubTypes(),name,count+1);
1839 //printf(" addTagged %s to %s\n",qPrint(ncd->name()),qPrint(tagParent->name()));
1840 ncd->setTaglessParent(icd);
1841 if (pmdm)
1842 {
1843 pmdm->setClassDefOfAnonymousType(ncd);
1844 }
1845 }
1846 }
1847 else
1848 {
1849 //printf(" no match for %s in %s\n",qPrint(icd->name()),qPrint(type));
1850 }
1851 }
1852 }
1853 }
1854 }
1855 // associate the variable of the anonymous enum type with the type member
1856 ml = def->getMemberList(typeFilter);
1857 if (ml)
1858 {
1859 for (const auto &md : *ml)
1860 {
1861 MemberListType mlFilter = def->definitionType()==Definition::TypeClass ? varFilter : MemberListType::DecVarMembers();
1862 associateVariableWithAnonymousEnumType(md,def,md,mlFilter);
1863 }
1864 }
1865 }
1866}
1867
1868/** Look through the members of class \a cd and its public members.
1869 * If there is a member m of a tag less struct/union,
1870 * then we create a duplicate of the struct/union with the name of the
1871 * member to identify it.
1872 * So if cd has name S, then the tag less struct/union will get name S.m
1873 * Since tag less structs can be nested we need to call this function
1874 * recursively. Later on we need to patch the member types so we keep
1875 * track of the hierarchy of classes we create.
1876 */
1877template<typename Container, typename TagContainer>
1878static void processTagLessClasses(const Definition *root,
1879 const Container *def,
1880 const TagContainer *tagParent,
1881 MemberListType varFilter,
1882 MemberListType typeFilter,
1883 const DString &prefix,int count)
1884{
1885 if (varFilter==MemberListType::PubAttribs()) // also handle protected and private members
1886 {
1887 processTagLessClassesForList(root,def,tagParent,MemberListType::PubAttribs(),MemberListType::PubTypes(),prefix,count);
1888 processTagLessClassesForList(root,def,tagParent,MemberListType::ProAttribs(),MemberListType::ProTypes(),prefix,count);
1889 processTagLessClassesForList(root,def,tagParent,MemberListType::PriAttribs(),MemberListType::PriTypes(),prefix,count);
1890 }
1891 else
1892 {
1893 processTagLessClassesForList(root,def,tagParent,varFilter,typeFilter,prefix,count);
1894 }
1895}
1896
1897template<typename Container>
1898static void findTagLessClasses(std::set<const Definition *> &candidates,const Container *cd)
1899{
1900 for (const auto &icd : cd->getClasses())
1901 {
1902 if (icd->name().find('@')==DString::npos) // process all non-anonymous inner classes
1903 {
1904 findTagLessClasses(candidates,icd);
1905 }
1906 }
1907
1908 candidates.insert(cd);
1909}
1910
1912{
1913 std::set<const Definition *> candidates;
1914 for (auto &cd : *Doxygen::classLinkedMap)
1915 {
1916 Definition *scope = cd->getOuterScope();
1917 //printf(" scope=%s for class %s\n",qPrint(scope?scope->name():"<null>"),qPrint(cd->name()));
1918 if (scope && scope->definitionType()==Definition::TypeNamespace) // class that is not nested
1919 {
1920 const NamespaceDef *nd = toNamespaceDef(scope);
1921 if (nd && nd==Doxygen::globalScope) // class at global namespace
1922 {
1923 const FileDef *fd = cd->getFileDef();
1924 if (fd)
1925 {
1926 findTagLessClasses(candidates,fd);
1927 }
1928 }
1929 else if (nd) // class in a namespace
1930 {
1931 findTagLessClasses(candidates,nd);
1932 }
1933 }
1934 }
1935
1936 // since processTagLessClasses is potentially adding classes to Doxygen::classLinkedMap
1937 // we need to call it outside of the loop above, otherwise the iterator gets invalidated!
1938 for (const auto &d : candidates)
1939 {
1940 //printf("------ processing tag-less classes for %s\n",qPrint(d->name()));
1941 if (d->definitionType()==Definition::TypeNamespace)
1942 {
1943 const NamespaceDef *nd = toNamespaceDef(d);
1944 processTagLessClasses(nd,nd,nd,MemberListType::DecVarMembers(),MemberListType::DecEnumMembers(),"",0);
1945 }
1946 else if (d->definitionType()==Definition::TypeFile)
1947 {
1948 const FileDef *fd = toFileDef(d);
1949 processTagLessClasses(fd,fd,fd,MemberListType::DecVarMembers(),MemberListType::DecEnumMembers(),"",0);
1950 }
1951 else if (d->definitionType()==Definition::TypeClass)
1952 {
1953 const ClassDef *cd = toClassDef(d);
1954 processTagLessClasses(cd,cd,cd,MemberListType::PubAttribs(),MemberListType::PubTypes(),"",0);
1955 }
1956 }
1957}
1958
1959
1960//----------------------------------------------------------------------
1961// build a list of all namespaces mentioned in the documentation
1962// and all namespaces that have a documentation block before their definition.
1963static void buildNamespaceList(const Entry *root)
1964{
1965 if (
1966 (root->section.isNamespace() ||
1967 root->section.isNamespaceDoc() ||
1968 root->section.isPackageDoc()
1969 ) &&
1970 !root->name.empty()
1971 )
1972 {
1973 AUTO_TRACE("name={}",root->name);
1974
1975 DString fName = root->name;
1976 if (root->section.isPackageDoc())
1977 {
1978 fName=substitute(fName,".","::");
1979 }
1980
1981 DString fullName = stripAnonymousNamespaceScope(fName);
1982 if (!fullName.empty())
1983 {
1984 AUTO_TRACE_ADD("Found namespace {} in {} at line {}",root->name,root->fileName,root->startLine);
1986 if (ndi) // existing namespace
1987 {
1989 if (nd) // non-inline namespace
1990 {
1991 AUTO_TRACE_ADD("Existing namespace");
1992 nd->setDocumentation(root->doc,root->docFile,root->docLine);
1993 nd->setName(fullName); // change name to match docs
1995 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1996 if (nd->getLanguage()==SrcLangExt::Unknown)
1997 {
1998 nd->setLanguage(root->lang);
1999 }
2000 if (root->tagInfo()==nullptr && nd->isReference() && !(root->doc.empty() && root->brief.empty()))
2001 // if we previously found namespace nd in a tag file and now we find a
2002 // documented namespace with the same name in the project, then remove
2003 // the tag file reference
2004 {
2005 nd->setReference("");
2006 nd->setFileName(fullName);
2007 }
2008 nd->setMetaData(root->metaData);
2009
2010 // file definition containing the namespace nd
2011 FileDef *fd=root->fileDef();
2012 if (nd->isArtificial())
2013 {
2014 nd->setArtificial(false); // found namespace explicitly, so cannot be artificial
2015 nd->setDefFile(root->fileName,root->startLine,root->startColumn);
2016 }
2017 // insert the namespace in the file definition
2018 if (fd) fd->insertNamespace(nd);
2019 addNamespaceToGroups(root,nd);
2020 nd->setRefItems(root->sli);
2021 nd->setRequirementReferences(root->rqli);
2022 addIncludeFile(nd,fd,root);
2023 }
2024 }
2025 else // fresh namespace
2026 {
2027 DString tagName;
2028 DString tagFileName;
2029 const TagInfo *tagInfo = root->tagInfo();
2030 if (tagInfo)
2031 {
2032 tagName = tagInfo->tagName;
2033 tagFileName = tagInfo->fileName;
2034 }
2035 AUTO_TRACE_ADD("new namespace {} lang={} tagName={}",fullName,root->lang,tagName);
2036 // add namespace to the list
2038 Doxygen::namespaceLinkedMap->add(fullName,
2039 createNamespaceDef(tagInfo?tagName:root->fileName,root->startLine,
2040 root->startColumn,fullName,tagName,tagFileName,
2041 root->type,root->spec.isPublished())));
2042 if (nd)
2043 {
2044 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
2045 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2047 nd->setHidden(root->hidden);
2048 nd->setArtificial(root->artificial);
2049 nd->setLanguage(root->lang);
2050 nd->setId(root->id);
2051 nd->setMetaData(root->metaData);
2052 nd->setInline(root->spec.isInline());
2053 nd->setExported(root->exported);
2054
2055 addNamespaceToGroups(root,nd);
2056 nd->setRefItems(root->sli);
2057 nd->setRequirementReferences(root->rqli);
2058
2059 // file definition containing the namespace nd
2060 FileDef *fd=root->fileDef();
2061 // insert the namespace in the file definition
2062 if (fd) fd->insertNamespace(nd);
2063
2064 // the empty string test is needed for extract all case
2065 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2066 nd->insertUsedFile(fd);
2067 nd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2068 nd->setBodyDef(fd);
2069 addIncludeFile(nd,fd,root);
2070
2071 // also add namespace to the correct structural context
2072 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,fullName,nullptr,tagInfo);
2073 AUTO_TRACE_ADD("adding namespace {} to context {}",nd->name(),d ? d->name() : DString("<none>"));
2074 if (d==nullptr) // we didn't find anything, create the scope artificially
2075 // anyway, so we can at least relate scopes properly.
2076 {
2077 d = buildScopeFromQualifiedName(fullName,nd->getLanguage(),tagInfo);
2079 if (dm)
2080 {
2081 dm->addInnerCompound(nd);
2082 }
2083 nd->setOuterScope(d);
2084 // TODO: Due to the order in which the tag file is written
2085 // a nested class can be found before its parent!
2086 }
2087 else
2088 {
2090 if (dm)
2091 {
2092 dm->addInnerCompound(nd);
2093 }
2094 nd->setOuterScope(d);
2095 // in case of d is an inline namespace, alias insert nd in the part scope of d.
2097 {
2098 NamespaceDef *pnd = toNamespaceDef(d);
2099 if (pnd && pnd->isInline())
2100 {
2101 d = d->getOuterScope();
2102 if (d)
2103 {
2104 dm = toDefinitionMutable(d);
2105 if (dm)
2106 {
2107 auto aliasNd = createNamespaceDefAlias(d,nd);
2108 dm->addInnerCompound(aliasNd.get());
2109 DString aliasName = aliasNd->name();
2110 AUTO_TRACE_ADD("adding alias {} to {}",aliasName,d->name());
2111 Doxygen::namespaceLinkedMap->add(aliasName,std::move(aliasNd));
2112 }
2113 }
2114 else
2115 {
2116 break;
2117 }
2118 }
2119 else
2120 {
2121 break;
2122 }
2123 }
2124 }
2125 }
2126 }
2127 }
2128 }
2129 for (const auto &e : root->children()) buildNamespaceList(e.get());
2130}
2131
2132//----------------------------------------------------------------------
2133
2135 const DString &name)
2136{
2137 NamespaceDef *usingNd =nullptr;
2138 for (auto &und : unl)
2139 {
2140 DString uScope=und->name()+"::";
2141 usingNd = getResolvedNamespace(uScope+name);
2142 if (usingNd!=nullptr) break;
2143 }
2144 return usingNd;
2145}
2146
2147static void findUsingDirectives(const Entry *root)
2148{
2149 if (root->section.isUsingDir())
2150 {
2151 AUTO_TRACE("Found using directive {} at line {} of {}",root->name,root->startLine,root->fileName);
2152 DString name=substitute(root->name,".","::");
2153 if (name.endsWith("::"))
2154 {
2155 name=name.left(name.length()-2);
2156 }
2157 if (!name.empty())
2158 {
2159 NamespaceDef *usingNd = nullptr;
2160 NamespaceDefMutable *nd = nullptr;
2161 FileDef *fd = root->fileDef();
2162 DString nsName;
2163
2164 // see if the using statement was found inside a namespace or inside
2165 // the global file scope.
2166 if (root->parent() && root->parent()->section.isNamespace() &&
2167 (fd==nullptr || fd->getLanguage()!=SrcLangExt::Java) // not a .java file
2168 )
2169 {
2170 nsName=stripAnonymousNamespaceScope(root->parent()->name);
2171 if (!nsName.empty())
2172 {
2173 nd = getResolvedNamespaceMutable(nsName);
2174 }
2175 }
2176
2177 // find the scope in which the 'using' namespace is defined by prepending
2178 // the possible scopes in which the using statement was found, starting
2179 // with the most inner scope and going to the most outer scope (i.e.
2180 // file scope).
2181 int scopeOffset = static_cast<int>(nsName.length());
2182 do
2183 {
2184 DString scope=scopeOffset>0 ?
2185 nsName.left(scopeOffset)+"::" : DString();
2186 usingNd = getResolvedNamespace(scope+name);
2187 //printf("Trying with scope='%s' usingNd=%p\n",(scope+qPrint(name)),usingNd);
2188 if (scopeOffset==0)
2189 {
2190 scopeOffset=-1;
2191 }
2192 else
2193 {
2194 size_t o = nsName.rfind("::",scopeOffset-1);
2195 scopeOffset = o!=DString::npos ? static_cast<int>(o) : 0;
2196 }
2197 } while (scopeOffset>=0 && usingNd==nullptr);
2198
2199 if (usingNd==nullptr && nd) // not found, try used namespaces in this scope
2200 // or in one of the parent namespace scopes
2201 {
2202 const NamespaceDefMutable *pnd = nd;
2203 while (pnd && usingNd==nullptr)
2204 {
2205 // also try with one of the used namespaces found earlier
2207
2208 // goto the parent
2209 Definition *s = pnd->getOuterScope();
2211 {
2213 }
2214 else
2215 {
2216 pnd = nullptr;
2217 }
2218 }
2219 }
2220 if (usingNd==nullptr && fd) // still nothing, also try used namespace in the
2221 // global scope
2222 {
2223 usingNd = findUsedNamespace(fd->getUsedNamespaces(),name);
2224 }
2225
2226 //printf("%s -> %s\n",qPrint(name),usingNd?qPrint(usingNd->name()):"<none>");
2227
2228 // add the namespace the correct scope
2229 if (usingNd)
2230 {
2231 //printf("using fd=%p nd=%p\n",fd,nd);
2232 if (nd)
2233 {
2234 //printf("Inside namespace %s\n",qPrint(nd->name()));
2235 nd->addUsingDirective(usingNd);
2236 }
2237 else if (fd)
2238 {
2239 //printf("Inside file %s\n",qPrint(fd->name()));
2240 fd->addUsingDirective(usingNd);
2241 }
2242 }
2243 else // unknown namespace, but add it anyway.
2244 {
2245 AUTO_TRACE_ADD("new unknown namespace {} lang={} hidden={}",name,root->lang,root->hidden);
2246 // add namespace to the list
2249 createNamespaceDef(root->fileName,root->startLine,root->startColumn,name)));
2250 if (nd)
2251 {
2252 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
2253 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2255 nd->setHidden(root->hidden);
2256 nd->setArtificial(true);
2257 nd->setLanguage(root->lang);
2258 nd->setId(root->id);
2259 nd->setMetaData(root->metaData);
2260 nd->setInline(root->spec.isInline());
2261 nd->setExported(root->exported);
2262
2263 for (const Grouping &g : root->groups)
2264 {
2265 GroupDef *gd=nullptr;
2267 gd->addNamespace(nd);
2268 }
2269
2270 // insert the namespace in the file definition
2271 if (fd)
2272 {
2273 fd->insertNamespace(nd);
2274 fd->addUsingDirective(nd);
2275 }
2276
2277 // the empty string test is needed for extract all case
2278 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2279 nd->insertUsedFile(fd);
2280 nd->setRefItems(root->sli);
2281 nd->setRequirementReferences(root->rqli);
2282 }
2283 }
2284 }
2285 }
2286 for (const auto &e : root->children()) findUsingDirectives(e.get());
2287}
2288
2289//----------------------------------------------------------------------
2290
2291static void buildListOfUsingDecls(const Entry *root)
2292{
2293 if (root->section.isUsingDecl() &&
2294 !root->parent()->section.isCompound() // not a class/struct member
2295 )
2296 {
2297 DString name = substitute(root->name,".","::");
2298 g_usingDeclarations.insert(name.str());
2299 }
2300 for (const auto &e : root->children()) buildListOfUsingDecls(e.get());
2301}
2302
2303
2304static void findUsingDeclarations(const Entry *root,bool filterPythonPackages)
2305{
2306 if (root->section.isUsingDecl() &&
2307 !root->parent()->section.isCompound() && // not a class/struct member
2308 (!filterPythonPackages || (root->lang==SrcLangExt::Python && root->fileName.endsWith("__init__.py")))
2309 )
2310 {
2311 AUTO_TRACE("Found using declaration '{}' at line {} of {} inside section {}",
2312 root->name,root->startLine,root->fileName,root->parent()->section);
2313 if (!root->name.empty())
2314 {
2315 const Definition *usingDef = nullptr;
2316 NamespaceDefMutable *nd = nullptr;
2317 FileDef *fd = root->fileDef();
2318 DString scName;
2319
2320 // see if the using statement was found inside a namespace or inside
2321 // the global file scope.
2322 if (root->parent()->section.isNamespace())
2323 {
2324 scName=root->parent()->name;
2325 if (!scName.empty())
2326 {
2327 nd = getResolvedNamespaceMutable(scName);
2328 }
2329 }
2330
2331 // Assume the using statement was used to import a class.
2332 // Find the scope in which the 'using' namespace is defined by prepending
2333 // the possible scopes in which the using statement was found, starting
2334 // with the most inner scope and going to the most outer scope (i.e.
2335 // file scope).
2336
2337 DString name = substitute(root->name,".","::"); //Java/C# scope->internal
2338
2339 SymbolResolver resolver;
2340 const Definition *scope = nd;
2341 if (nd==nullptr) scope = fd;
2342 usingDef = resolver.resolveSymbol(scope,name);
2343
2344 //printf("usingDef(scope=%s,name=%s)=%s\n",qPrint(nd?nd->qualifiedName():""),qPrint(name),usingDef?qPrint(usingDef->qualifiedName()):"nullptr");
2345
2346 if (!usingDef)
2347 {
2348 usingDef = getClass(name); // try direct lookup, this is needed to get
2349 // builtin STL classes to properly resolve, e.g.
2350 // vector -> std::vector
2351 }
2352 if (!usingDef)
2353 {
2354 usingDef = Doxygen::hiddenClassLinkedMap->find(name); // check if it is already hidden
2355 }
2356#if 0
2357 if (!usingDef)
2358 {
2359 AUTO_TRACE_ADD("New using class '{}' (sec={})! #tArgLists={}",
2360 name,root->section,root->tArgLists.size());
2363 createClassDef( "<using>",1,1, name, ClassDef::Class)));
2364 if (usingCd)
2365 {
2366 usingCd->setArtificial(true);
2367 usingCd->setLanguage(root->lang);
2368 usingDef = usingCd;
2369 }
2370 }
2371#endif
2372 else
2373 {
2374 AUTO_TRACE_ADD("Found used type '{}' in scope='{}'",
2375 usingDef->name(), nd ? nd->name(): fd ? fd->name() : DString("<unknown>"));
2376 }
2377
2378 if (usingDef)
2379 {
2380 if (nd)
2381 {
2382 nd->addUsingDeclaration(usingDef);
2383 }
2384 else if (fd)
2385 {
2386 fd->addUsingDeclaration(usingDef);
2387 }
2388 }
2389 }
2390 }
2391 for (const auto &e : root->children()) findUsingDeclarations(e.get(),filterPythonPackages);
2392}
2393
2394//----------------------------------------------------------------------
2395
2397{
2398 root->commandOverrides.apply_callGraph ([&](bool b) { md->overrideCallGraph(b); });
2399 root->commandOverrides.apply_callerGraph ([&](bool b) { md->overrideCallerGraph(b); });
2400 root->commandOverrides.apply_referencedByRelation([&](bool b) { md->overrideReferencedByRelation(b); });
2401 root->commandOverrides.apply_referencesRelation ([&](bool b) { md->overrideReferencesRelation(b); });
2402 root->commandOverrides.apply_inlineSource ([&](bool b) { md->overrideInlineSource(b); });
2403 root->commandOverrides.apply_enumValues ([&](bool b) { md->overrideEnumValues(b); });
2404}
2405
2406//----------------------------------------------------------------------
2407
2409 const DString &fileName,const DString &memName)
2410{
2411 AUTO_TRACE("creating new member {} for class {}",memName,cd->name());
2412 const ArgumentList &templAl = md->templateArguments();
2413 const ArgumentList &al = md->argumentList();
2414 auto newMd = createMemberDef(
2415 fileName,root->startLine,root->startColumn,
2416 md->typeString(),memName,md->argsString(),
2417 md->excpString(),root->protection,root->virt,
2418 md->isStatic(),Relationship::Member,md->memberType(),
2419 templAl,al,root->metaData
2420 );
2421 auto newMmd = toMemberDefMutable(newMd.get());
2422 newMmd->setMemberClass(cd);
2423 cd->insertMember(newMd.get());
2424 if (!root->doc.empty() || !root->brief.empty())
2425 {
2426 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2427 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2428 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2429 }
2430 else
2431 {
2432 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2433 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2434 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2435 }
2436 newMmd->setDefinition(md->definition());
2437 applyMemberOverrideOptions(root,newMmd);
2438 newMmd->addQualifiers(root->qualifiers);
2439 newMmd->setBitfields(md->bitfieldString());
2440 newMmd->addSectionsToDefinition(root->anchors);
2441 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2442 newMmd->setBodyDef(md->getBodyDef());
2443 newMmd->setInitializer(md->initializer());
2444 newMmd->setRequiresClause(md->requiresClause());
2445 newMmd->setMaxInitLines(md->initializerLines());
2446 newMmd->setMemberGroupId(root->mGrpId);
2447 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2448 newMmd->setVhdlSpecifiers(md->getVhdlSpecifiers());
2449 newMmd->setLanguage(root->lang);
2450 newMmd->setId(root->id);
2452 mn->push_back(std::move(newMd));
2453}
2454
2455static std::unordered_map<std::string,std::vector<ClassDefMutable*>> g_usingClassMap;
2456
2457static void findUsingDeclImports(const Entry *root)
2458{
2459 if (root->section.isUsingDecl() &&
2460 root->parent()->section.isCompound() // in a class/struct member
2461 )
2462 {
2463 AUTO_TRACE("Found using declaration '{}' inside section {}", root->name, root->parent()->section);
2464 DString fullName=removeRedundantWhiteSpace(root->parent()->name);
2465 fullName=stripAnonymousNamespaceScope(fullName);
2466 fullName=stripTemplateSpecifiersFromScope(fullName);
2467 ClassDefMutable *cd = getClassMutable(fullName);
2468 if (cd)
2469 {
2470 AUTO_TRACE_ADD("found class '{}'",cd->name());
2471 size_t i=root->name.rfind("::");
2472 if (i!=DString::npos)
2473 {
2474 DString scope=root->name.left(i);
2475 DString memName=root->name.mid(i+2);
2476 SymbolResolver resolver;
2477 const ClassDef *bcd = resolver.resolveClass(cd,scope); // todo: file in fileScope parameter
2478 AUTO_TRACE_ADD("name={} scope={} bcd={}",scope,cd?cd->name():"<none>",bcd?bcd->name():"<none>");
2479 if (bcd && bcd!=cd)
2480 {
2481 AUTO_TRACE_ADD("found class '{}' memName='{}'",bcd->name(),memName);
2483 const MemberNameInfo *mni = mnlm.find(memName);
2484 if (mni)
2485 {
2486 for (auto &mi : *mni)
2487 {
2488 const MemberDef *md = mi->memberDef();
2489 if (md && md->protection()!=Protection::Private)
2490 {
2491 AUTO_TRACE_ADD("found member '{}'",mni->memberName());
2492 DString fileName = root->fileName;
2493 if (fileName.empty() && root->tagInfo())
2494 {
2495 fileName = root->tagInfo()->tagName;
2496 }
2497 if (!cd->containsOverload(md))
2498 {
2499 createUsingMemberImportForClass(root,cd,md,fileName,memName);
2500 // also insert the member into copies of the class
2501 auto it = g_usingClassMap.find(cd->qualifiedName().str());
2502 if (it != g_usingClassMap.end())
2503 {
2504 for (const auto &copyCd : it->second)
2505 {
2506 createUsingMemberImportForClass(root,copyCd,md,fileName,memName);
2507 }
2508 }
2509 }
2510 }
2511 }
2512 }
2513 }
2514 }
2515 }
2516 }
2517 else if (root->section.isUsingDecl() &&
2518 (root->parent()->section.isNamespace() || root->parent()->section.isEmpty()) && // namespace or global member
2519 root->lang==SrcLangExt::Cpp // do we also want this for e.g. Fortran? (see test case 095)
2520 )
2521 {
2522 AUTO_TRACE("Found using declaration '{}' inside section {}", root->name, root->parent()->section);
2523 Definition *scope = nullptr;
2524 NamespaceDefMutable *nd = nullptr;
2525 FileDef *fd = root->parent()->fileDef();
2526 if (!root->parent()->name.empty())
2527 {
2528 DString fullName=removeRedundantWhiteSpace(root->parent()->name);
2529 fullName=stripAnonymousNamespaceScope(fullName);
2531 scope = nd;
2532 }
2533 else
2534 {
2535 scope = fd;
2536 }
2537 if (scope)
2538 {
2539 AUTO_TRACE_ADD("found scope '{}'",scope->name());
2540 SymbolResolver resolver;
2541 const Definition *def = resolver.resolveSymbol(root->name.startsWith("::") ? nullptr : scope,root->name);
2542 if (def && def->definitionType()==Definition::TypeMember)
2543 {
2544 size_t i=root->name.rfind("::");
2545 DString memName;
2546 if (i!=DString::npos)
2547 {
2548 memName = root->name.right(root->name.length()-i-2);
2549 }
2550 else
2551 {
2552 memName = root->name;
2553 }
2554 const MemberDef *md = toMemberDef(def);
2555 AUTO_TRACE_ADD("found member '{}' for name '{}'",md->qualifiedName(),root->name);
2556 DString fileName = root->fileName;
2557 if (fileName.empty() && root->tagInfo())
2558 {
2559 fileName = root->tagInfo()->tagName;
2560 }
2561 const ArgumentList &templAl = md->templateArguments();
2562 const ArgumentList &al = md->argumentList();
2563
2564 auto newMd = createMemberDef(
2565 fileName,root->startLine,root->startColumn,
2566 md->typeString(),memName,md->argsString(),
2567 md->excpString(),root->protection,root->virt,
2568 md->isStatic(),Relationship::Member,md->memberType(),
2569 templAl,al,root->metaData
2570 );
2571 auto newMmd = toMemberDefMutable(newMd.get());
2572 if (nd)
2573 {
2574 newMmd->setNamespace(nd);
2575 nd->insertMember(newMd.get());
2576 }
2577 if (fd)
2578 {
2579 newMmd->setFileDef(fd);
2580 fd->insertMember(newMd.get());
2581 }
2582 if (!root->doc.empty() || !root->brief.empty())
2583 {
2584 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2585 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2586 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2587 }
2588 else
2589 {
2590 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2591 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2592 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2593 }
2594 newMmd->setDefinition(md->definition());
2595 applyMemberOverrideOptions(root,newMmd);
2596 newMmd->addQualifiers(root->qualifiers);
2597 newMmd->setBitfields(md->bitfieldString());
2598 newMmd->addSectionsToDefinition(root->anchors);
2599 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2600 newMmd->setBodyDef(md->getBodyDef());
2601 newMmd->setInitializer(md->initializer());
2602 newMmd->setRequiresClause(md->requiresClause());
2603 newMmd->setMaxInitLines(md->initializerLines());
2604 newMmd->setMemberGroupId(root->mGrpId);
2605 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2606 newMmd->setVhdlSpecifiers(md->getVhdlSpecifiers());
2607 newMmd->setLanguage(root->lang);
2608 newMmd->setId(root->id);
2610 mn->push_back(std::move(newMd));
2611#if 0 // insert an alias instead of a copy
2612 const MemberDef *md = toMemberDef(def);
2613 AUTO_TRACE_ADD("found member '{}' for name '{}'",md->qualifiedName(),root->name);
2614 auto aliasMd = createMemberDefAlias(nd,md);
2615 DString aliasFullName = nd->qualifiedName()+"::"+aliasMd->localName();
2616 if (nd && aliasMd.get())
2617 {
2618 nd->insertMember(aliasMd.get());
2619 }
2620 if (fd && aliasMd.get())
2621 {
2622 fd->insertMember(aliasMd.get());
2623 }
2624 MemberName *mn = Doxygen::memberNameLinkedMap->add(aliasFullName);
2625 mn->push_back(std::move(aliasMd));
2626#endif
2627 }
2628 else if (def && def->definitionType()==Definition::TypeClass)
2629 {
2630 const ClassDef *cd = toClassDef(def);
2631 DString copyFullName;
2632 if (nd==nullptr)
2633 {
2634 copyFullName = cd->localName();
2635 }
2636 else
2637 {
2638 copyFullName = nd->qualifiedName()+"::"+cd->localName();
2639 }
2640 if (Doxygen::classLinkedMap->find(copyFullName)==nullptr)
2641 {
2643 Doxygen::classLinkedMap->add(copyFullName,
2644 cd->deepCopy(copyFullName)));
2645 AUTO_TRACE_ADD("found class '{}' for name '{}' copy '{}' obj={}",cd->qualifiedName(),root->name,copyFullName,(void*)ncdm);
2646 g_usingClassMap[cd->qualifiedName().str()].push_back(ncdm);
2647 if (ncdm)
2648 {
2649 if (nd) ncdm->moveTo(nd);
2650 if ((!root->doc.empty() || !root->brief.empty())) // use docs at using statement
2651 {
2652 ncdm->setDocumentation(root->doc,root->docFile,root->docLine);
2653 ncdm->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2654 }
2655 else // use docs from used class
2656 {
2657 ncdm->setDocumentation(cd->documentation(),cd->docFile(),cd->docLine());
2659 }
2660 if (nd)
2661 {
2662 nd->addInnerCompound(ncdm);
2663 nd->addUsingDeclaration(ncdm);
2664 }
2665 if (fd)
2666 {
2667 if (ncdm) ncdm->setFileDef(fd);
2668 fd->insertClass(ncdm);
2669 fd->addUsingDeclaration(ncdm);
2670 }
2671 }
2672 }
2673#if 0 // insert an alias instead of a copy
2674 auto aliasCd = createClassDefAlias(nd,cd);
2675 DString aliasFullName;
2676 if (nd==nullptr)
2677 {
2678 aliasFullName = aliasCd->localName();
2679 }
2680 else
2681 {
2682 aliasFullName = nd->qualifiedName()+"::"+aliasCd->localName();
2683 }
2684 AUTO_TRACE_ADD("found class '{}' for name '{}' aliasFullName='{}'",cd->qualifiedName(),root->name,aliasFullName);
2685 auto acd = Doxygen::classLinkedMap->add(aliasFullName,std::move(aliasCd));
2686 if (nd && acd)
2687 {
2688 nd->addInnerCompound(acd);
2689 }
2690 if (fd && acd)
2691 {
2692 fd->insertClass(acd);
2693 }
2694#endif
2695 }
2696 else if (scope)
2697 {
2698 AUTO_TRACE_ADD("no symbol with name '{}' in scope {}",root->name,scope->name());
2699 }
2700 }
2701 }
2702 for (const auto &e : root->children()) findUsingDeclImports(e.get());
2703}
2704
2705//----------------------------------------------------------------------
2706
2708{
2709 FileDefSet visitedFiles;
2710 // then recursively add using directives found in #include files
2711 // to files that have not been visited.
2712 for (const auto &fn : *Doxygen::inputNameLinkedMap)
2713 {
2714 for (const auto &fd : *fn)
2715 {
2716 //printf("----- adding using directives for file %s\n",qPrint(fd->name()));
2717 fd->addIncludedUsingDirectives(visitedFiles);
2718 }
2719 }
2720}
2721
2722//----------------------------------------------------------------------
2723
2725 const Entry *root,
2726 ClassDefMutable *cd,
2727 MemberType mtype,
2728 const DString &type,
2729 const DString &name,
2730 const DString &args,
2731 Protection prot,
2732 Relationship related)
2733{
2735 DString scopeSeparator="::";
2736 SrcLangExt lang = cd->getLanguage();
2737 if (lang==SrcLangExt::Java || lang==SrcLangExt::CSharp)
2738 {
2739 qualScope = substitute(qualScope,"::",".");
2740 scopeSeparator=".";
2741 }
2742 AUTO_TRACE("class variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} init='{}'",
2743 root->fileName, type, qualScope, name, args, root->protection, mtype, lang, root->initializer.str());
2744
2745 DString def;
2746 if (!type.empty())
2747 {
2748 if (related!=Relationship::Member || mtype==MemberType::Friend || Config_getBool(HIDE_SCOPE_NAMES))
2749 {
2750 if (root->spec.isAlias()) // turn 'typedef B A' into 'using A'
2751 {
2752 if (lang==SrcLangExt::Python)
2753 {
2754 def="type "+name+args;
2755 }
2756 else
2757 {
2758 def="using "+name;
2759 }
2760 }
2761 else
2762 {
2763 def=type+" "+name+args;
2764 }
2765 }
2766 else
2767 {
2768 if (root->spec.isAlias()) // turn 'typedef B C::A' into 'using C::A'
2769 {
2770 if (lang==SrcLangExt::Python)
2771 {
2772 def="type "+qualScope+scopeSeparator+name+args;
2773 }
2774 else
2775 {
2776 def="using "+qualScope+scopeSeparator+name;
2777 }
2778 }
2779 else
2780 {
2781 def=type+" "+qualScope+scopeSeparator+name+args;
2782 }
2783 }
2784 }
2785 else
2786 {
2787 if (Config_getBool(HIDE_SCOPE_NAMES))
2788 {
2789 def=name+args;
2790 }
2791 else
2792 {
2793 def=qualScope+scopeSeparator+name+args;
2794 }
2795 }
2796 def.stripPrefix("static ");
2797
2798 // see if the member is already found in the same scope
2799 // (this may be the case for a static member that is initialized
2800 // outside the class)
2802 if (mn)
2803 {
2804 for (const auto &imd : *mn)
2805 {
2806 //printf("md->getClassDef()=%p cd=%p type=[%s] md->typeString()=[%s]\n",
2807 // md->getClassDef(),cd,qPrint(type),md->typeString());
2808 MemberDefMutable *md = toMemberDefMutable(imd.get());
2809 if (md &&
2810 md->getClassDef()==cd &&
2811 ((lang==SrcLangExt::Python && type.empty() && !md->typeString().empty()) ||
2813 // member already in the scope
2814 {
2815
2816 if (root->lang==SrcLangExt::ObjC &&
2817 root->mtype==MethodTypes::Property &&
2818 md->memberType()==MemberType::Variable)
2819 { // Objective-C 2.0 property
2820 // turn variable into a property
2821 md->setProtection(root->protection);
2822 cd->reclassifyMember(md,MemberType::Property);
2823 }
2824 addMemberDocs(root,md,def,nullptr,false,root->spec);
2825 AUTO_TRACE_ADD("Member already found!");
2826 return md;
2827 }
2828 }
2829 }
2830
2831 DString fileName = root->fileName;
2832 if (fileName.empty() && root->tagInfo())
2833 {
2834 fileName = root->tagInfo()->tagName;
2835 }
2836
2837 // new member variable, typedef or enum value
2838 auto md = createMemberDef(
2839 fileName,root->startLine,root->startColumn,
2840 type,name,args,root->exception,
2841 prot,Specifier::Normal,root->isStatic,related,
2842 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2843 ArgumentList(), root->metaData);
2844 auto mmd = toMemberDefMutable(md.get());
2845 mmd->setTagInfo(root->tagInfo());
2846 mmd->setMemberClass(cd); // also sets outer scope (i.e. getOuterScope())
2847 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2848 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2849 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2850 mmd->setDefinition(def);
2851 mmd->setBitfields(root->bitfields);
2852 mmd->addSectionsToDefinition(root->anchors);
2853 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2854 mmd->setInitializer(root->initializer.str());
2855 mmd->setMaxInitLines(root->initLines);
2856 mmd->setMemberGroupId(root->mGrpId);
2857 mmd->setMemberSpecifiers(root->spec);
2858 mmd->setVhdlSpecifiers(root->vhdlSpec);
2859 mmd->setReadAccessor(root->read);
2860 mmd->setWriteAccessor(root->write);
2862 mmd->setHidden(root->hidden);
2863 mmd->setArtificial(root->artificial);
2864 mmd->setLanguage(root->lang);
2865 mmd->setId(root->id);
2866 addMemberToGroups(root,md.get());
2868 mmd->setBodyDef(root->fileDef());
2869 mmd->addQualifiers(root->qualifiers);
2870 addIncludeFile(mmd,root->fileDef(),root);
2871
2872 AUTO_TRACE_ADD("Adding new member '{}' to class '{}'",name,cd->name());
2873 cd->insertMember(md.get());
2874 mmd->setRefItems(root->sli);
2875 mmd->setRequirementReferences(root->rqli);
2876
2877 cd->insertUsedFile(root->fileDef());
2878 root->markAsProcessed();
2879
2880 if (mtype==MemberType::Typedef)
2881 {
2882 resolveTemplateInstanceInType(root,cd,md.get());
2883 }
2884
2885 // add the member to the global list
2886 MemberDef *result = md.get();
2888 mn->push_back(std::move(md));
2889
2890 return result;
2891}
2892
2893//----------------------------------------------------------------------
2894
2896 const Entry *root,
2897 MemberType mtype,
2898 const DString &scope,
2899 const DString &type,
2900 const DString &name,
2901 const DString &args)
2902{
2903 AUTO_TRACE("global variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} init='{}'",
2904 root->fileName, type, scope, name, args, root->protection, mtype, root->lang, root->initializer.str());
2905
2906 FileDef *fd = root->fileDef();
2907
2908 // see if we have a typedef that should hide a struct or union
2909 if (mtype==MemberType::Typedef && Config_getBool(TYPEDEF_HIDES_STRUCT))
2910 {
2911 DString ttype = type;
2912 ttype.stripPrefix("typedef ");
2913 if (ttype.stripPrefix("struct ") || ttype.stripPrefix("union "))
2914 {
2915 static const reg::Ex re(R"(\a\w*)");
2916 reg::Match match;
2917 const std::string &typ = ttype.str();
2918 if (reg::search(typ,match,re))
2919 {
2920 DString typeValue = match.str();
2921 ClassDefMutable *cd = getClassMutable(typeValue);
2922 if (cd)
2923 {
2924 // this typedef should hide compound name cd, so we
2925 // change the name that is displayed from cd.
2926 cd->setClassName(name);
2927 cd->setDocumentation(root->doc,root->docFile,root->docLine);
2928 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2929 return nullptr;
2930 }
2931 }
2932 }
2933 }
2934
2935 // see if the function is inside a namespace
2936 NamespaceDefMutable *nd = nullptr;
2937 if (!scope.empty())
2938 {
2939 if (scope.find('@')!=DString::npos) return nullptr; // anonymous scope!
2940 nd = getResolvedNamespaceMutable(scope);
2941 }
2942 DString def;
2943
2944 // determine the definition of the global variable
2945 if (nd && !nd->isAnonymous() &&
2946 !Config_getBool(HIDE_SCOPE_NAMES)
2947 )
2948 // variable is inside a namespace, so put the scope before the name
2949 {
2950 SrcLangExt lang = nd->getLanguage();
2952
2953 if (!type.empty())
2954 {
2955 if (root->spec.isAlias()) // turn 'typedef B NS::A' into 'using NS::A'
2956 {
2957 if (lang==SrcLangExt::Python)
2958 {
2959 def="type "+nd->name()+sep+name+args;
2960 }
2961 else
2962 {
2963 def="using "+nd->name()+sep+name;
2964 }
2965 }
2966 else // normal member
2967 {
2968 def=type+" "+nd->name()+sep+name+args;
2969 }
2970 }
2971 else
2972 {
2973 def=nd->name()+sep+name+args;
2974 }
2975 }
2976 else
2977 {
2978 if (!type.empty() && !root->name.empty())
2979 {
2980 if (name.at(0)=='@') // dummy variable representing anonymous union
2981 {
2982 def=type;
2983 }
2984 else
2985 {
2986 if (root->spec.isAlias()) // turn 'typedef B A' into 'using A'
2987 {
2988 if (root->lang==SrcLangExt::Python)
2989 {
2990 def="type "+root->name+args;
2991 }
2992 else
2993 {
2994 def="using "+root->name;
2995 }
2996 }
2997 else // normal member
2998 {
2999 def=type+" "+name+args;
3000 }
3001 }
3002 }
3003 else
3004 {
3005 def=name+args;
3006 }
3007 }
3008 def.stripPrefix("static ");
3009
3011 if (mn)
3012 {
3013 //DString nscope=removeAnonymousScopes(scope);
3014 //NamespaceDef *nd=nullptr;
3015 //if (!nscope.empty())
3016 if (!scope.empty())
3017 {
3018 nd = getResolvedNamespaceMutable(scope);
3019 }
3020 for (const auto &imd : *mn)
3021 {
3022 MemberDefMutable *md = toMemberDefMutable(imd.get());
3023 if (md &&
3024 ((nd==nullptr && md->getNamespaceDef()==nullptr && md->getFileDef() &&
3025 root->fileName==md->getFileDef()->absFilePath()
3026 ) // both variable names in the same file
3027 || (nd!=nullptr && md->getNamespaceDef()==nd) // both in same namespace
3028 )
3029 && !md->isDefine() // function style #define's can be "overloaded" by typedefs or variables
3030 && !md->isEnumerate() // in C# an enum value and enum can have the same name
3031 )
3032 // variable already in the scope
3033 {
3034 bool isPHPArray = md->getLanguage()==SrcLangExt::PHP &&
3035 md->argsString()!=args &&
3036 args.find('[')!=DString::npos;
3037 bool staticsInDifferentFiles =
3038 root->isStatic && md->isStatic() &&
3039 root->fileName!=md->getDefFileName();
3040
3041 if (md->getFileDef() &&
3042 !isPHPArray && // not a php array
3043 !staticsInDifferentFiles
3044 )
3045 // not a php array variable
3046 {
3047 AUTO_TRACE_ADD("variable already found: scope='{}'",md->getOuterScope()->name());
3048 addMemberDocs(root,md,def,nullptr,false,root->spec);
3049 md->setRefItems(root->sli);
3050 md->setRequirementReferences(root->rqli);
3051 // if md is a variable forward declaration and root is the definition that
3052 // turn md into the definition
3053 if (!root->explicitExternal && md->isExternal())
3054 {
3055 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
3056 md->setExplicitExternal(false,root->fileName,root->startLine,root->startColumn);
3057 }
3058 // if md is the definition and root point at a declaration, then add the
3059 // declaration info
3060 else if (root->explicitExternal && !md->isExternal())
3061 {
3062 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
3063 }
3064 return md;
3065 }
3066 }
3067 }
3068 }
3069
3070 DString fileName = root->fileName;
3071 if (fileName.empty() && root->tagInfo())
3072 {
3073 fileName = root->tagInfo()->tagName;
3074 }
3075
3076 AUTO_TRACE_ADD("new variable, namespace='{}'",nd?nd->name():DString("<global>"));
3077 // new global variable, enum value or typedef
3078 auto md = createMemberDef(
3079 fileName,root->startLine,root->startColumn,
3080 type,name,args,DString(),
3081 root->protection, Specifier::Normal,root->isStatic,Relationship::Member,
3082 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3083 root->argList, root->metaData);
3084 auto mmd = toMemberDefMutable(md.get());
3085 mmd->setTagInfo(root->tagInfo());
3086 mmd->setMemberSpecifiers(root->spec);
3087 mmd->setVhdlSpecifiers(root->vhdlSpec);
3088 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3089 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3090 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3091 mmd->addSectionsToDefinition(root->anchors);
3092 mmd->setInitializer(root->initializer.str());
3093 mmd->setMaxInitLines(root->initLines);
3094 mmd->setMemberGroupId(root->mGrpId);
3095 mmd->setDefinition(def);
3096 mmd->setLanguage(root->lang);
3097 mmd->setId(root->id);
3099 mmd->setExplicitExternal(root->explicitExternal,fileName,root->startLine,root->startColumn);
3100 mmd->addQualifiers(root->qualifiers);
3101 //md->setOuterScope(fd);
3102 if (!root->explicitExternal)
3103 {
3104 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3105 mmd->setBodyDef(fd);
3106 }
3107 addMemberToGroups(root,md.get());
3108 addIncludeFile(mmd,fd,root);
3110
3111 mmd->setRefItems(root->sli);
3112 mmd->setRequirementReferences(root->rqli);
3113 if (nd && !nd->isAnonymous())
3114 {
3115 mmd->setNamespace(nd);
3116 nd->insertMember(md.get());
3117 }
3118
3119 // add member to the file (we do this even if we have already inserted
3120 // it into the namespace.
3121 if (fd)
3122 {
3123 mmd->setFileDef(fd);
3124 fd->insertMember(md.get());
3125 }
3126
3127 root->markAsProcessed();
3128
3129 if (mtype==MemberType::Typedef)
3130 {
3131 resolveTemplateInstanceInType(root,nd,md.get());
3132 }
3133
3134 // add member definition to the list of globals
3135 MemberDef *result = md.get();
3137 mn->push_back(std::move(md));
3138
3139
3140
3141 return result;
3142}
3143
3144/*! See if the return type string \a type is that of a function pointer
3145 * \returns -1 if this is not a function pointer variable or
3146 * the index at which the closing brace of (...*name) was found.
3147 */
3148static int findFunctionPtr(const std::string &type,SrcLangExt lang, int *pLength=nullptr)
3149{
3150 AUTO_TRACE("type='{}' lang={}",type,lang);
3151 if (lang == SrcLangExt::Fortran || lang == SrcLangExt::VHDL)
3152 {
3153 return -1; // Fortran and VHDL do not have function pointers
3154 }
3155
3156 static const reg::Ex re(R"(\‍([^)]*[*&^][^)]*\))");
3157 reg::Match match;
3158 size_t i=std::string::npos;
3159 size_t l=0;
3160 if (reg::search(type,match,re)) // contains (...*...) or (...&...) or (...^...)
3161 {
3162 i = match.position();
3163 l = match.length();
3164 }
3165 if (i!=std::string::npos)
3166 {
3167 size_t di = type.find("decltype(");
3168 if (di!=std::string::npos && di<i)
3169 {
3170 i = std::string::npos;
3171 }
3172 }
3173 size_t bb=type.find('<');
3174 size_t be=type.rfind('>');
3175 bool templFp = false;
3176 if (be!=std::string::npos) {
3177 size_t cc_ast = type.find("::*");
3178 size_t cc_amp = type.find("::&");
3179 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>::*)'
3180 }
3181
3182 if (!type.empty() && // return type is non-empty
3183 i!=std::string::npos && // contains (...*...)
3184 type.find("operator")==std::string::npos && // not an operator
3185 (type.find(")(")==std::string::npos || type.find("typedef ")!=std::string::npos) &&
3186 // not a function pointer return type
3187 (!((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
3188 )
3189 {
3190 if (pLength) *pLength=static_cast<int>(l);
3191 //printf("findFunctionPtr=%d\n",(int)i);
3192 AUTO_TRACE_EXIT("result={}",i);
3193 return static_cast<int>(i);
3194 }
3195 else
3196 {
3197 //printf("findFunctionPtr=%d\n",-1);
3198 AUTO_TRACE_EXIT("result=-1");
3199 return -1;
3200 }
3201}
3202
3203//--------------------------------------------------------------------------------------
3204
3205/*! Returns true iff \a type is a class within scope \a context.
3206 * Used to detect variable declarations that look like function prototypes.
3207 */
3208static bool isVarWithConstructor(const Entry *root)
3209{
3210 bool result = false;
3211 bool typeIsClass = false;
3212 bool typePtrType = false;
3213 DString type;
3214 Definition *ctx = nullptr;
3215 FileDef *fd = root->fileDef();
3216 SymbolResolver resolver(fd);
3217
3218 AUTO_TRACE("isVarWithConstructor({})",root->name);
3219 if (root->parent()->section.isCompound())
3220 { // inside a class
3221 result=false;
3222 AUTO_TRACE_EXIT("inside class: result={}",result);
3223 return result;
3224 }
3225 else if ((fd != nullptr) && (fd->name().endsWith(".c") || fd->name().endsWith(".h")))
3226 { // inside a .c file
3227 result=false;
3228 AUTO_TRACE_EXIT("inside C file: result={}",result);
3229 return result;
3230 }
3231 if (root->type.empty())
3232 {
3233 result=false;
3234 AUTO_TRACE_EXIT("no type: result={}",result);
3235 return result;
3236 }
3237 if (!root->parent()->name.empty())
3238 {
3240 }
3241 type = root->type;
3242 // remove qualifiers
3243 type.findAndRemoveWord("const");
3244 type.findAndRemoveWord("static");
3245 type.findAndRemoveWord("volatile");
3246 typePtrType = type.find('*')!=DString::npos || type.find('&')!=DString::npos;
3247 if (!typePtrType)
3248 {
3249 typeIsClass = resolver.resolveClass(ctx,type)!=nullptr;
3250 if (size_t ti=type.find('<'); !typeIsClass && ti!=DString::npos)
3251 {
3252 typeIsClass=resolver.resolveClass(ctx,type.left(ti))!=nullptr;
3253 }
3254 }
3255 if (typeIsClass) // now we still have to check if the arguments are
3256 // types or values. Since we do not have complete type info
3257 // we need to rely on heuristics :-(
3258 {
3259 if (root->argList.empty())
3260 {
3261 result=false; // empty arg list -> function prototype.
3262 AUTO_TRACE_EXIT("empty arg list: result={}",result);
3263 return result;
3264 }
3265 for (const Argument &a : root->argList)
3266 {
3267 static const reg::Ex initChars(R"([\d"'&*!^]+)");
3268 reg::Match match;
3269 if (!a.name.empty() || !a.defval.empty())
3270 {
3271 std::string name = a.name.str();
3272 if (reg::search(name,match,initChars) && match.position()==0)
3273 {
3274 result=true;
3275 }
3276 else
3277 {
3278 result=false; // arg has (type,name) pair -> function prototype
3279 }
3280 AUTO_TRACE_EXIT("function prototype: result={}",result);
3281 return result;
3282 }
3283 if (!a.type.empty() &&
3284 (a.type.at(a.type.length()-1)=='*' ||
3285 a.type.at(a.type.length()-1)=='&'))
3286 // type ends with * or & => pointer or reference
3287 {
3288 result=false;
3289 AUTO_TRACE_EXIT("pointer or reference: result={}",result);
3290 return result;
3291 }
3292 if (a.type.empty() || resolver.resolveClass(ctx,a.type)!=nullptr)
3293 {
3294 result=false; // arg type is a known type
3295 AUTO_TRACE_EXIT("known type: result={}",result);
3296 return result;
3297 }
3298 if (checkIfTypedef(ctx,fd,a.type))
3299 {
3300 result=false; // argument is a typedef
3301 AUTO_TRACE_EXIT("typedef: result={}",result);
3302 return result;
3303 }
3304 std::string atype = a.type.str();
3305 if (reg::search(atype,match,initChars) && match.position()==0)
3306 {
3307 result=true; // argument type starts with typical initializer char
3308 AUTO_TRACE_EXIT("argument with init char: result={}",result);
3309 return result;
3310 }
3311 std::string resType=resolveTypeDef(ctx,a.type).str();
3312 if (resType.empty()) resType=atype;
3313 static const reg::Ex idChars(R"(\a\w*)");
3314 if (reg::search(resType,match,idChars) && match.position()==0) // resType starts with identifier
3315 {
3316 resType=match.str();
3317 if (resType=="int" || resType=="long" ||
3318 resType=="float" || resType=="double" ||
3319 resType=="char" || resType=="void" ||
3320 resType=="signed" || resType=="unsigned" ||
3321 resType=="const" || resType=="volatile" )
3322 {
3323 result=false; // type keyword -> function prototype
3324 AUTO_TRACE_EXIT("type keyword: result={}",result);
3325 return result;
3326 }
3327 }
3328 }
3329 result=true;
3330 }
3331
3332 AUTO_TRACE_EXIT("end: result={}",result);
3333 return result;
3334}
3335
3336//--------------------------------------------------------------------------------------
3337
3338/*! Searches for the end of a template in prototype \a s starting from
3339 * character position \a startPos. If the end was found the position
3340 * of the closing > is returned, otherwise -1 is returned.
3341 *
3342 * Handles exotic cases such as
3343 * \code
3344 * Class<(id<0)>
3345 * Class<bits<<2>
3346 * Class<"<">
3347 * Class<'<'>
3348 * Class<(")<")>
3349 * \endcode
3350 */
3351static int findEndOfTemplate(const DString &s,size_t startPos)
3352{
3353 // locate end of template
3354 size_t e=startPos;
3355 int brCount=1;
3356 int roundCount=0;
3357 size_t len = s.length();
3358 bool insideString=false;
3359 bool insideChar=false;
3360 char pc = 0;
3361 while (e<len && brCount!=0)
3362 {
3363 char c=s.at(e);
3364 switch(c)
3365 {
3366 case '<':
3367 if (!insideString && !insideChar)
3368 {
3369 if (e<len-1 && s.at(e+1)=='<')
3370 e++;
3371 else if (roundCount==0)
3372 brCount++;
3373 }
3374 break;
3375 case '>':
3376 if (!insideString && !insideChar)
3377 {
3378 if (e<len-1 && s.at(e+1)=='>')
3379 e++;
3380 else if (roundCount==0)
3381 brCount--;
3382 }
3383 break;
3384 case '(':
3385 if (!insideString && !insideChar)
3386 roundCount++;
3387 break;
3388 case ')':
3389 if (!insideString && !insideChar)
3390 roundCount--;
3391 break;
3392 case '"':
3393 if (!insideChar)
3394 {
3395 if (insideString && pc!='\\')
3396 insideString=false;
3397 else
3398 insideString=true;
3399 }
3400 break;
3401 case '\'':
3402 if (!insideString)
3403 {
3404 if (insideChar && pc!='\\')
3405 insideChar=false;
3406 else
3407 insideChar=true;
3408 }
3409 break;
3410 }
3411 pc = c;
3412 e++;
3413 }
3414 return brCount==0 ? static_cast<int>(e) : -1;
3415}
3416
3417//--------------------------------------------------------------------------------------
3418
3419static void addVariable(const Entry *root,int isFuncPtr=-1)
3420{
3421 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
3422
3423 AUTO_TRACE("VARIABLE_SEC: type='{}' name='{}' args='{}' bodyLine={} endBodyLine={} mGrpId={} relates='{}'",
3424 root->type, root->name, root->args, root->bodyLine, root->endBodyLine, root->mGrpId, root->relates);
3425 //printf("root->parent->name=%s\n",qPrint(root->parent->name));
3426
3427 DString type = root->type;
3428 DString name = root->name;
3429 DString args = root->args;
3430 if (type.empty() && name.find("operator")==DString::npos &&
3431 (name.find('*')!=DString::npos || name.find('&')!=DString::npos))
3432 {
3433 // recover from parse error caused by redundant braces
3434 // like in "int *(var[10]);", which is parsed as
3435 // type="" name="int *" args="(var[10])"
3436
3437 type=name;
3438 std::string sargs = args.str();
3439 static const reg::Ex reName(R"(\a\w*)");
3440 reg::Match match;
3441 if (reg::search(sargs,match,reName))
3442 {
3443 name = match.str(); // e.g. 'var' in '(var[10])'
3444 sargs = match.suffix().str(); // e.g. '[10]) in '(var[10])'
3445 size_t j = sargs.find(')');
3446 if (j!=std::string::npos) args=sargs.substr(0,j); // extract, e.g '[10]' from '[10])'
3447 }
3448 }
3449 else
3450 {
3451 int i=isFuncPtr;
3452 if (i==-1 && (root->spec.isAlias())==0) i=findFunctionPtr(type.str(),root->lang); // for typedefs isFuncPtr is not yet set
3453 AUTO_TRACE_ADD("functionPtr={}",i!=-1?"yes":"no");
3454 if (i>=0) // function pointer
3455 {
3456 size_t ii = i;
3457 size_t ai = type.find('[',ii);
3458 if (ai!=DString::npos && ai>ii) // function pointer array
3459 {
3460 args.prepend(type.mid(ai));
3461 type=type.left(ai);
3462 }
3463 else if (type.find(')',ii)!=DString::npos) // function ptr, not variable like "int (*bla)[10]"
3464 {
3465 type=type.left(type.length()-1);
3466 args.prepend(") ");
3467 }
3468 }
3469 }
3470 AUTO_TRACE_ADD("after correction: type='{}' name='{}' args='{}'",type,name,args);
3471
3472 DString scope;
3473 name=removeRedundantWhiteSpace(name);
3474
3475 // find the scope of this variable
3476 int index = computeQualifiedIndex(name);
3477 // grouped members are stored with full scope
3478 bool qualifiedGroupMember = index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo();
3479 // A specialization of a variable template that lives in a namespace, i.e.
3480 // `template<class T> constexpr bool std::ranges::enable_view<MyView<T>> = true;`.
3481 // Its scope has to be spelled out, so it must not be mistaken for the definition of a static
3482 // data member, which is declared in its class as well and is therefore picked up from there.
3483 bool variableTemplateSpecialization = index!=-1 && !root->tArgLists.empty() &&
3484 name.mid(index+2).find('<')!=DString::npos &&
3485 getClass(name.left(index))==nullptr;
3486 if (qualifiedGroupMember || variableTemplateSpecialization)
3487 {
3488 Definition *d = buildScopeFromQualifiedName(name.left(index+2),root->lang,root->tagInfo());
3489 if (variableTemplateSpecialization)
3490 // the scope may have been made up for this specialization, but it holds a documented member
3491 // now, which is only reachable through a page of its own
3492 {
3493 for (Definition *s=d; s && s!=Doxygen::globalScope; s=s->getOuterScope())
3494 {
3496 if (ndm && ndm->isArtificial()) ndm->setArtificial(false);
3497 }
3498 }
3499 scope=name.left(index);
3500 name=name.mid(index+2);
3501 }
3502 else
3503 {
3504 Entry *p = root->parent();
3505 while (p->section.isScope())
3506 {
3507 DString scopeName = p->name;
3508 if (!scopeName.empty())
3509 {
3510 scope.prepend(scopeName);
3511 break;
3512 }
3513 p=p->parent();
3514 }
3515 }
3516
3517 DString type_s = type;
3518 type=type.stripWhiteSpace();
3519 ClassDefMutable *cd=nullptr;
3520 bool isRelated=false;
3521 bool isMemberOf=false;
3522
3523 DString classScope=stripAnonymousNamespaceScope(scope);
3524 if (root->lang==SrcLangExt::CSharp)
3525 {
3526 classScope=mangleCSharpGenericName(classScope);
3527 }
3528 else
3529 {
3530 classScope=stripTemplateSpecifiersFromScope(classScope,false);
3531 }
3532 DString annScopePrefix=scope.left(scope.length()-classScope.length());
3533
3534
3535 // Look for last :: not part of template specifier
3536 int p=-1;
3537 for (size_t i=0;i<name.length()-1;i++)
3538 {
3539 if (name[i]==':' && name[i+1]==':')
3540 {
3541 p=static_cast<int>(i);
3542 }
3543 else if (name[i]=='<') // skip over template parts,
3544 // i.e. A::B<C::D> => p=1 and
3545 // A<B::C>::D => p=8
3546 {
3547 int e = findEndOfTemplate(name,i+1);
3548 if (e!=-1) i=static_cast<int>(e);
3549 }
3550 }
3551
3552 if (p!=-1) // found it
3553 {
3554 if (isTypeAClassFriend(type))
3555 {
3556 cd=getClassMutable(scope);
3557 if (cd)
3558 {
3559 addVariableToClass(root, // entry
3560 cd, // class to add member to
3561 MemberType::Friend, // type of member
3562 type, // type value as string
3563 name, // name of the member
3564 args, // arguments as string
3565 Protection::Public, // protection
3566 Relationship::Member // related to a class
3567 );
3568 }
3569 }
3570 if (root->bodyLine!=-1 && root->endBodyLine!=-1) // store the body location for later use
3571 {
3572 Doxygen::staticInitMap.emplace(name.str(),BodyInfo{root->startLine,root->bodyLine,root->endBodyLine});
3573 }
3574
3575
3576 AUTO_TRACE_ADD("static variable {} body=[{}..{}]",name,root->bodyLine,root->endBodyLine);
3577 return; /* skip this member, because it is a
3578 * static variable definition (always?), which will be
3579 * found in a class scope as well, but then we know the
3580 * correct protection level, so only then it will be
3581 * inserted in the correct list!
3582 */
3583 }
3584
3585 MemberType mtype = MemberType::Variable;
3586 if (type=="@")
3587 mtype=MemberType::EnumValue;
3588 else if (type_s.startsWith("typedef "))
3589 mtype=MemberType::Typedef;
3590 else if (type_s.startsWith("friend ") || type_s=="friend")
3591 mtype=MemberType::Friend;
3592 else if (root->mtype==MethodTypes::Property)
3593 mtype=MemberType::Property;
3594 else if (root->mtype==MethodTypes::Event)
3595 mtype=MemberType::Event;
3596 else if (type.find("sequence<") != DString::npos)
3597 mtype=sliceOpt ? MemberType::Sequence : MemberType::Typedef;
3598 else if (type.find("dictionary<") != DString::npos)
3599 mtype=sliceOpt ? MemberType::Dictionary : MemberType::Typedef;
3600
3601 if (!root->relates.empty()) // related variable
3602 {
3603 isRelated=true;
3604 isMemberOf=(root->relatesType==RelatesType::MemberOf);
3605 if (getClass(root->relates)==nullptr && !scope.empty())
3606 scope=mergeScopes(scope,root->relates);
3607 else
3608 scope=root->relates;
3609 }
3610
3611 cd=getClassMutable(scope);
3612 if (cd==nullptr && classScope!=scope) cd=getClassMutable(classScope);
3613 if (cd)
3614 {
3615 Relationship relationship = isMemberOf ? Relationship::Foreign :
3616 isRelated ? Relationship::Related :
3617 Relationship::Member ;
3618
3619 addVariableToClass(root, // entry
3620 cd, // class to add member to
3621 mtype, // member type
3622 type, // type value as string
3623 name, // name of the member
3624 args, // arguments as string
3625 root->protection,
3626 relationship
3627 );
3628
3629 }
3630 else if (!name.empty()) // global variable
3631 {
3632 addVariableToFile(root,mtype,scope,type,name,args);
3633 }
3634
3635}
3636
3637//----------------------------------------------------------------------
3638// Searches the Entry tree for typedef documentation sections.
3639// If found they are stored in their class or in the global list.
3640static void buildTypedefList(const Entry *root)
3641{
3642 //printf("buildVarList(%s)\n",qPrint(rootNav->name()));
3643 if (!root->name.empty() &&
3644 root->section.isVariable() &&
3645 root->type.find("typedef ")!=DString::npos // its a typedef
3646 )
3647 {
3648 AUTO_TRACE();
3649 DString rname = removeRedundantWhiteSpace(root->name);
3650 DString scope;
3651 int index = computeQualifiedIndex(rname);
3652 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3653 // grouped members are stored with full scope
3654 {
3655 buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
3656 scope=rname.left(index);
3657 rname=rname.mid(index+2);
3658 }
3659 else
3660 {
3661 scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
3662 }
3666 bool found=false;
3667 if (mn) // symbol with the same name already found
3668 {
3669 for (auto &imd : *mn)
3670 {
3671 if (!imd->isTypedef())
3672 continue;
3673
3674 DString rtype = root->type;
3675 rtype.stripPrefix("typedef ");
3676
3677 // merge the typedefs only if they're not both grouped, and both are
3678 // either part of the same class, part of the same namespace, or both
3679 // are global (i.e., neither in a class or a namespace)
3680 bool notBothGrouped = root->groups.empty() || imd->getGroupDef()==nullptr; // see example #100
3681 bool bothSameScope = (!cd && !nd) || (cd && imd->getClassDef() == cd) || (nd && imd->getNamespaceDef() == nd);
3682 //printf("imd->isTypedef()=%d imd->typeString()=%s root->type=%s\n",imd->isTypedef(),
3683 // qPrint(imd->typeString()),qPrint(root->type));
3684 if (notBothGrouped && bothSameScope && imd->typeString()==rtype)
3685 {
3686 MemberDefMutable *md = toMemberDefMutable(imd.get());
3687 if (md)
3688 {
3689 md->setDocumentation(root->doc,root->docFile,root->docLine);
3691 md->setDocsForDefinition(!root->proto);
3692 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3694 md->setRefItems(root->sli);
3695 md->setRequirementReferences(root->rqli);
3696 md->addQualifiers(root->qualifiers);
3697
3698 // merge ingroup specifiers
3699 if (md->getGroupDef()==nullptr && !root->groups.empty())
3700 {
3701 addMemberToGroups(root,md);
3702 }
3703 else if (md->getGroupDef()!=nullptr && root->groups.empty())
3704 {
3705 //printf("existing member is grouped, new member not\n");
3706 }
3707 else if (md->getGroupDef()!=nullptr && !root->groups.empty())
3708 {
3709 //printf("both members are grouped\n");
3710 }
3711 found=true;
3712 break;
3713 }
3714 }
3715 }
3716 }
3717 if (found)
3718 {
3719 AUTO_TRACE_ADD("typedef '{}' already found",rname);
3720 // mark the entry as processed, as we copied everything from it elsewhere
3721 // also, otherwise, due to containing `typedef` it may later get treated
3722 // as a function typedef in filterMemberDocumentation, which is incorrect
3723 root->markAsProcessed();
3724 }
3725 else
3726 {
3727 AUTO_TRACE_ADD("new typedef '{}'",rname);
3728 addVariable(root);
3729 }
3730
3731 }
3732 for (const auto &e : root->children())
3733 if (!e->section.isEnum())
3734 buildTypedefList(e.get());
3735}
3736
3737//----------------------------------------------------------------------
3738// Searches the Entry tree for sequence documentation sections.
3739// If found they are stored in the global list.
3740static void buildSequenceList(const Entry *root)
3741{
3742 if (!root->name.empty() &&
3743 root->section.isVariable() &&
3744 root->type.find("sequence<")!=DString::npos // it's a sequence
3745 )
3746 {
3747 AUTO_TRACE();
3748 addVariable(root);
3749 }
3750 for (const auto &e : root->children())
3751 if (!e->section.isEnum())
3752 buildSequenceList(e.get());
3753}
3754
3755//----------------------------------------------------------------------
3756// Searches the Entry tree for dictionary documentation sections.
3757// If found they are stored in the global list.
3758static void buildDictionaryList(const Entry *root)
3759{
3760 if (!root->name.empty() &&
3761 root->section.isVariable() &&
3762 root->type.find("dictionary<")!=DString::npos // it's a dictionary
3763 )
3764 {
3765 AUTO_TRACE();
3766 addVariable(root);
3767 }
3768 for (const auto &e : root->children())
3769 if (!e->section.isEnum())
3770 buildDictionaryList(e.get());
3771}
3772
3773//----------------------------------------------------------------------
3774// Searches the Entry tree for Variable documentation sections.
3775// If found they are stored in their class or in the global list.
3776
3777static void buildVarList(const Entry *root)
3778{
3779 //printf("buildVarList(%s) section=%08x\n",qPrint(rootNav->name()),rootNav->section());
3780 int isFuncPtr=-1;
3781 if (!root->name.empty() &&
3782 (root->type.empty() || g_compoundKeywords.find(root->type.str())==g_compoundKeywords.end()) &&
3783 (
3784 (root->section.isVariable() && // it's a variable
3785 root->type.find("typedef ")==DString::npos // and not a typedef
3786 ) ||
3787 (root->section.isFunction() && // or maybe a function pointer variable
3788 (isFuncPtr=findFunctionPtr(root->type.str(),root->lang))!=-1
3789 ) ||
3790 (root->section.isFunction() && // class variable initialized by constructor
3792 )
3793 )
3794 ) // documented variable
3795 {
3796 AUTO_TRACE();
3797 addVariable(root,isFuncPtr);
3798 }
3799 for (const auto &e : root->children())
3800 if (!e->section.isEnum())
3801 buildVarList(e.get());
3802}
3803
3804//----------------------------------------------------------------------
3805// Searches the Entry tree for Interface sections (UNO IDL only).
3806// If found they are stored in their service or in the global list.
3807//
3808
3810 const Entry *root,
3811 ClassDefMutable *cd,
3812 DString const& rname)
3813{
3814 FileDef *fd = root->fileDef();
3815 enum MemberType type = root->section.isExportedInterface() ? MemberType::Interface : MemberType::Service;
3816 DString fileName = root->fileName;
3817 if (fileName.empty() && root->tagInfo())
3818 {
3819 fileName = root->tagInfo()->tagName;
3820 }
3821 auto md = createMemberDef(
3822 fileName, root->startLine, root->startColumn, root->type, rname,
3823 "", "", root->protection, root->virt, root->isStatic, Relationship::Member,
3824 type, ArgumentList(), root->argList, root->metaData);
3825 auto mmd = toMemberDefMutable(md.get());
3826 mmd->setTagInfo(root->tagInfo());
3827 mmd->setMemberClass(cd);
3828 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3829 mmd->setDocsForDefinition(false);
3830 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3831 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3832 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3833 mmd->setMemberSpecifiers(root->spec);
3834 mmd->setVhdlSpecifiers(root->vhdlSpec);
3835 mmd->setMemberGroupId(root->mGrpId);
3836 mmd->setTypeConstraints(root->typeConstr);
3837 mmd->setLanguage(root->lang);
3838 mmd->setBodyDef(fd);
3839 mmd->setFileDef(fd);
3840 mmd->addSectionsToDefinition(root->anchors);
3841 DString const def = root->type + " " + rname;
3842 mmd->setDefinition(def);
3844 mmd->addQualifiers(root->qualifiers);
3845 addIncludeFile(mmd,fd,root);
3846
3847 AUTO_TRACE("Interface member: fileName='{}' type='{}' name='{}' mtype='{}' prot={} virt={} state={} proto={} def='{}'",
3848 fileName,root->type,rname,type,root->protection,root->virt,root->isStatic,root->proto,def);
3849
3850 // add member to the class cd
3851 cd->insertMember(md.get());
3852 // also add the member as a "base" (to get nicer diagrams)
3853 // "optional" interface/service get Protected which turns into dashed line
3854 BaseInfo base(rname,
3855 root->spec.isOptional() ? Protection::Protected : Protection::Public, Specifier::Normal);
3856 TemplateNameMap templateNames;
3857 findClassRelation(root,cd,cd,&base,templateNames,DocumentedOnly,true) ||
3858 findClassRelation(root,cd,cd,&base,templateNames,Undocumented,true);
3859 // add file to list of used files
3860 cd->insertUsedFile(fd);
3861
3862 addMemberToGroups(root,md.get());
3864 root->markAsProcessed();
3865 mmd->setRefItems(root->sli);
3866 mmd->setRequirementReferences(root->rqli);
3867
3868 // add member to the global list of all members
3870 mn->push_back(std::move(md));
3871}
3872
3873static void buildInterfaceAndServiceList(const Entry *root)
3874{
3875 if (root->section.isExportedInterface() || root->section.isIncludedService())
3876 {
3877 AUTO_TRACE("Exported interface/included service: type='{}' scope='{}' name='{}' args='{}'"
3878 " relates='{}' relatesType='{}' file='{}' line={} bodyLine={} #tArgLists={}"
3879 " mGrpId={} spec={} proto={} docFile='{}'",
3880 root->type, root->parent()->name, root->name, root->args,
3881 root->relates, root->relatesType, root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(),
3882 root->mGrpId, root->spec, root->proto, root->docFile);
3883
3884 DString rname = removeRedundantWhiteSpace(root->name);
3885
3886 if (!rname.empty())
3887 {
3888 DString scope = root->parent()->name;
3889 ClassDefMutable *cd = getClassMutable(scope);
3890 ASSERT(cd);
3891 if (cd && ((ClassDef::Interface == cd->compoundType()) ||
3892 (ClassDef::Service == cd->compoundType()) ||
3894 {
3896 }
3897 else
3898 {
3899 ASSERT(false); // was checked by scanner.l
3900 }
3901 }
3902 else if (rname.empty())
3903 {
3904 warn(root->fileName,root->startLine,
3905 "Illegal member name found.");
3906 }
3907 }
3908 // can only have these in IDL anyway
3909 switch (root->lang)
3910 {
3911 case SrcLangExt::Unknown: // fall through (root node always is Unknown)
3912 case SrcLangExt::IDL:
3913 for (const auto &e : root->children()) buildInterfaceAndServiceList(e.get());
3914 break;
3915 default:
3916 return; // nothing to do here
3917 }
3918}
3919
3920
3921//----------------------------------------------------------------------
3922// Searches the Entry tree for Function sections.
3923// If found they are stored in their class or in the global list.
3924
3925static void addMethodToClass(const Entry *root,ClassDefMutable *cd,
3926 const DString &rtype,const DString &rname,const DString &rargs,
3927 bool isFriend,
3928 Protection protection,bool stat,Specifier virt,TypeSpecifier spec,
3929 const DString &relates
3930 )
3931{
3932 FileDef *fd=root->fileDef();
3933
3934 DString type = rtype;
3935 DString args = rargs;
3936
3938 name.stripPrefix("::");
3939
3940 MemberType mtype = MemberType::Function;
3941 if (isFriend) mtype=MemberType::Friend;
3942 else if (root->mtype==MethodTypes::Signal) mtype=MemberType::Signal;
3943 else if (root->mtype==MethodTypes::Slot) mtype=MemberType::Slot;
3944 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType::DCOP;
3945
3946 // strip redundant template specifier for constructors
3947 size_t i = DString::npos;
3948 size_t j = DString::npos;
3949 if ((fd==nullptr || fd->getLanguage()==SrcLangExt::Cpp) &&
3950 !name.startsWith("operator ") && // not operator
3951 (i=name.find('<'))!=DString::npos && // containing <
3952 (j=name.find('>'))!=DString::npos && // or >
3953 (j!=i+2 || name.at(i+1)!='=') // but not the C++20 spaceship operator <=>
3954 )
3955 {
3956 name=name.left(i);
3957 }
3958
3959 DString fileName = root->fileName;
3960 if (fileName.empty() && root->tagInfo())
3961 {
3962 fileName = root->tagInfo()->tagName;
3963 }
3964
3965 //printf("root->name='%s; args='%s' root->argList='%s'\n",
3966 // qPrint(root->name),qPrint(args),qPrint(argListToString(root->argList))
3967 // );
3968
3969 // adding class member
3970 Relationship relationship = relates.empty() ? Relationship::Member :
3971 root->relatesType==RelatesType::MemberOf ? Relationship::Foreign :
3972 Relationship::Related ;
3973 auto md = createMemberDef(
3974 fileName,root->startLine,root->startColumn,
3975 type,name,args,root->exception,
3976 protection,virt,
3977 stat && root->relatesType!=RelatesType::MemberOf,
3978 relationship,
3979 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3980 root->argList, root->metaData);
3981 auto mmd = toMemberDefMutable(md.get());
3982 mmd->setTagInfo(root->tagInfo());
3983 mmd->setMemberClass(cd);
3984 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3985 mmd->setDocsForDefinition(!root->proto);
3986 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3987 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3988 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3989 mmd->setMemberSpecifiers(spec);
3990 mmd->setVhdlSpecifiers(root->vhdlSpec);
3991 mmd->setMemberGroupId(root->mGrpId);
3992 mmd->setTypeConstraints(root->typeConstr);
3993 mmd->setLanguage(root->lang);
3994 mmd->setRequiresClause(root->req);
3995 mmd->setId(root->id);
3996 mmd->setBodyDef(fd);
3997 mmd->setFileDef(fd);
3998 mmd->addSectionsToDefinition(root->anchors);
3999 addIncludeFile(mmd,fd,root);
4000 DString def;
4002 SrcLangExt lang = cd->getLanguage();
4003 DString scopeSeparator=getLanguageSpecificSeparator(lang);
4004 if (scopeSeparator!="::")
4005 {
4006 qualScope = substitute(qualScope,"::",scopeSeparator);
4007 }
4008 if (lang==SrcLangExt::PHP)
4009 {
4010 // for PHP we use Class::method and Namespace\method
4011 scopeSeparator="::";
4012 }
4013 if (!relates.empty() || isFriend || Config_getBool(HIDE_SCOPE_NAMES))
4014 {
4015 if (!type.empty())
4016 {
4017 def=type+" "+name; //+optArgs;
4018 }
4019 else
4020 {
4021 def=name; //+optArgs;
4022 }
4023 }
4024 else
4025 {
4026 if (!type.empty())
4027 {
4028 def=type+" "+qualScope+scopeSeparator+name; //+optArgs;
4029 }
4030 else
4031 {
4032 def=qualScope+scopeSeparator+name; //+optArgs;
4033 }
4034 }
4035 def.stripPrefix("friend ");
4036 mmd->setDefinition(def);
4038 mmd->addQualifiers(root->qualifiers);
4039
4040 AUTO_TRACE("function member: type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
4041 type, qualScope, rname, args, root->proto, def);
4042
4043 // add member to the class cd
4044 cd->insertMember(md.get());
4045 // add file to list of used files
4046 cd->insertUsedFile(fd);
4047
4048 addMemberToGroups(root,md.get());
4050 root->markAsProcessed();
4051 mmd->setRefItems(root->sli);
4052 mmd->setRequirementReferences(root->rqli);
4053
4054 // add member to the global list of all members
4055 //printf("Adding member=%s class=%s\n",qPrint(md->name()),qPrint(cd->name()));
4057 mn->push_back(std::move(md));
4058}
4059
4060//------------------------------------------------------------------------------------------
4061
4062static void addGlobalFunction(const Entry *root,const DString &rname,const DString &sc)
4063{
4064 DString scope = sc;
4065
4066 // new global function
4068 auto md = createMemberDef(
4069 root->fileName,root->startLine,root->startColumn,
4070 root->type,name,root->args,root->exception,
4071 root->protection,root->virt,root->isStatic,Relationship::Member,
4072 MemberType::Function,
4073 !root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
4074 root->argList,root->metaData);
4075 auto mmd = toMemberDefMutable(md.get());
4076 mmd->setTagInfo(root->tagInfo());
4077 mmd->setLanguage(root->lang);
4078 mmd->setId(root->id);
4079 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
4080 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
4081 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
4082 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
4083 mmd->setDocsForDefinition(!root->proto);
4084 mmd->setTypeConstraints(root->typeConstr);
4085 //md->setBody(root->body);
4086 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
4087 FileDef *fd=root->fileDef();
4088 mmd->setBodyDef(fd);
4089 mmd->addSectionsToDefinition(root->anchors);
4090 mmd->setMemberSpecifiers(root->spec);
4091 mmd->setVhdlSpecifiers(root->vhdlSpec);
4092 mmd->setMemberGroupId(root->mGrpId);
4093 mmd->setRequiresClause(root->req);
4094 mmd->setExplicitExternal(root->explicitExternal,root->fileName,root->startLine,root->startColumn);
4095 addIncludeFile(mmd,fd,root);
4096
4097 NamespaceDefMutable *nd = nullptr;
4098 // see if the function is inside a namespace that was not part of
4099 // the name already (in that case nd should be non-zero already)
4100 if (root->parent()->section.isNamespace())
4101 {
4102 //DString nscope=removeAnonymousScopes(root->parent()->name);
4103 DString nscope=root->parent()->name;
4104 if (!nscope.empty())
4105 {
4106 nd = getResolvedNamespaceMutable(nscope);
4107 }
4108 }
4109 else if (root->parent()->section.isGroupDoc() && !scope.empty())
4110 {
4112 }
4113
4114 if (!scope.empty())
4115 {
4117 if (sep!="::")
4118 {
4119 scope = substitute(scope,"::",sep);
4120 }
4121 scope+=sep;
4122 }
4123
4124 if (Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python) scope = "";
4125 DString def;
4126 //DString optArgs = root->argList.empty() ? DString() : root->args;
4127 if (!root->type.empty())
4128 {
4129 def=root->type+" "+scope+name; //+optArgs;
4130 }
4131 else
4132 {
4133 def=scope+name; //+optArgs;
4134 }
4135 AUTO_TRACE("new non-member function type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
4136 root->type,scope,rname,root->args,root->proto,def);
4137 mmd->setDefinition(def);
4139 mmd->addQualifiers(root->qualifiers);
4140
4141 mmd->setRefItems(root->sli);
4142 mmd->setRequirementReferences(root->rqli);
4143 if (nd && !nd->name().empty() && nd->name().at(0)!='@')
4144 {
4145 // add member to namespace
4146 mmd->setNamespace(nd);
4147 nd->insertMember(md.get());
4148 }
4149 if (fd)
4150 {
4151 // add member to the file (we do this even if we have already
4152 // inserted it into the namespace)
4153 mmd->setFileDef(fd);
4154 fd->insertMember(md.get());
4155 }
4156
4157 addMemberToGroups(root,md.get());
4159 if (root->relatesType == RelatesType::Simple) // if this is a relatesalso command,
4160 // allow find Member to pick it up
4161 {
4162 root->markAsProcessed(); // Otherwise we have finished with this entry.
4163 }
4164
4165 // add member to the list of file members
4167 mn->push_back(std::move(md));
4168}
4169
4170//------------------------------------------------------------------------------------------
4171
4172static void buildFunctionList(const Entry *root)
4173{
4174 if (root->section.isFunction())
4175 {
4176 AUTO_TRACE("member function: type='{}' scope='{}' name='{}' args='{}' relates='{}' relatesType='{}'"
4177 " file='{}' line={} bodyLine={} #tArgLists={} mGrpId={}"
4178 " spec={} proto={} docFile='{}'",
4179 root->type, root->parent()->name, root->name, root->args, root->relates, root->relatesType,
4180 root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(), root->mGrpId,
4181 root->spec, root->proto, root->docFile);
4182
4183 bool isFriend=root->type=="friend" || root->type.find("friend ")!=DString::npos;
4184 DString rname = removeRedundantWhiteSpace(root->name);
4185 //printf("rname=%s\n",qPrint(rname));
4186
4187 DString scope;
4188 int index = computeQualifiedIndex(rname);
4189 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
4190 // grouped members are stored with full scope
4191 {
4192 buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
4193 scope=rname.left(index);
4194 rname=rname.mid(index+2);
4195 }
4196 else
4197 {
4198 scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
4199 }
4200 if (!rname.empty() && scope.find('@')==DString::npos)
4201 {
4202 // check if this function's parent is a class
4203 if (root->lang==SrcLangExt::CSharp)
4204 {
4205 scope=mangleCSharpGenericName(scope);
4206 }
4207 else
4208 {
4209 scope=stripTemplateSpecifiersFromScope(scope,false);
4210 }
4211
4212 FileDef *rfd=root->fileDef();
4213
4214 size_t memIndex=rname.rfind("::");
4215
4217 if (cd && scope+"::"==rname.left(scope.length()+2)) // found A::f inside A
4218 {
4219 // strip scope from name
4220 rname=rname.mid(root->parent()->name.length()+2);
4221 }
4222
4223 bool isMember=false;
4224 if (memIndex!=DString::npos)
4225 {
4226 size_t ts=rname.find('<');
4227 size_t te=rname.find('>');
4228 if (memIndex>0 && (ts==DString::npos || te==DString::npos))
4229 {
4230 // note: the following code was replaced by inMember=true to deal with a
4231 // function rname='X::foo' of class X inside a namespace also called X...
4232 // bug id 548175
4233 //nd = Doxygen::namespaceLinkedMap->find(rname.left(memIndex));
4234 //isMember = nd==nullptr;
4235 //if (nd)
4236 //{
4237 // // strip namespace scope from name
4238 // scope=rname.left(memIndex);
4239 // rname=rname.mid(memIndex+2);
4240 //}
4241 isMember = true;
4242 }
4243 else
4244 {
4245 isMember=memIndex<ts || memIndex>te;
4246 }
4247 }
4248
4249 if (!root->parent()->name.empty() && root->parent()->section.isCompound() && cd)
4250 {
4251 AUTO_TRACE_ADD("member '{}' of class '{}'", rname,cd->name());
4252 addMethodToClass(root,cd,root->type,rname,root->args,isFriend,
4253 root->protection,root->isStatic,root->virt,root->spec,root->relates);
4254 }
4255 else if (root->parent()->section.isObjcImpl() && cd)
4256 {
4257 const MemberDef *md = cd->getMemberByName(rname);
4258 if (md)
4259 {
4260 MemberDefMutable *mdm = toMemberDefMutable(const_cast<MemberDef*>(md));
4261 if (mdm)
4262 {
4263 mdm->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
4264 mdm->setBodyDef(root->fileDef());
4265 }
4266 }
4267 }
4268 else if (!root->parent()->section.isCompound() && !root->parent()->section.isObjcImpl() &&
4269 !isMember &&
4270 (root->relates.empty() || root->relatesType==RelatesType::Duplicate) &&
4271 !root->type.startsWith("extern ") && !root->type.startsWith("typedef ")
4272 )
4273 // no member => unrelated function
4274 {
4275 /* check the uniqueness of the function name in the file.
4276 * A file could contain a function prototype and a function definition
4277 * or even multiple function prototypes.
4278 */
4279 bool found=false;
4280 MemberDef *md_found=nullptr;
4282 if (mn)
4283 {
4284 AUTO_TRACE_ADD("function '{}' already found",rname);
4285 for (const auto &imd : *mn)
4286 {
4287 MemberDefMutable *md = toMemberDefMutable(imd.get());
4288 if (md)
4289 {
4290 const NamespaceDef *mnd = md->getNamespaceDef();
4291 NamespaceDef *rnd = nullptr;
4292 //printf("root namespace=%s\n",qPrint(rootNav->parent()->name()));
4293 DString fullScope = scope;
4294 DString parentScope = root->parent()->name;
4295 if (!parentScope.empty() && !leftScopeMatch(parentScope,scope))
4296 {
4297 if (!scope.empty()) fullScope.prepend("::");
4298 fullScope.prepend(parentScope);
4299 }
4300 //printf("fullScope=%s\n",qPrint(fullScope));
4301 rnd = getResolvedNamespace(fullScope);
4302 const FileDef *mfd = md->getFileDef();
4303 DString nsName,rnsName;
4304 if (mnd) nsName = mnd->name();
4305 if (rnd) rnsName = rnd->name();
4306 //printf("matching arguments for %s%s %s%s\n",
4307 // qPrint(md->name()),md->argsString(),qPrint(rname),qPrint(argListToString(root->argList)));
4308 const ArgumentList &mdAl = md->argumentList();
4309 const ArgumentList &mdTempl = md->templateArguments();
4310
4311 // in case of template functions, we need to check if the
4312 // functions have the same number of template parameters
4313 bool sameTemplateArgs = true;
4314 bool matchingReturnTypes = true;
4315 bool sameRequiresClause = true;
4316 if (!mdTempl.empty() && !root->tArgLists.empty())
4317 {
4318 sameTemplateArgs = matchTemplateArguments(mdTempl,root->tArgLists.back());
4319 if (md->typeString()!=removeRedundantWhiteSpace(root->type))
4320 {
4321 matchingReturnTypes = false;
4322 }
4323 if (md->requiresClause()!=root->req)
4324 {
4325 sameRequiresClause = false;
4326 }
4327 }
4328 else if (!mdTempl.empty() || !root->tArgLists.empty())
4329 { // if one has template parameters and the other doesn't then that also counts as a
4330 // difference
4331 sameTemplateArgs = false;
4332 }
4333
4334 bool staticsInDifferentFiles =
4335 root->isStatic && md->isStatic() && root->fileName!=md->getDefFileName();
4336
4337 if (sameTemplateArgs &&
4338 matchingReturnTypes &&
4339 sameRequiresClause &&
4340 !staticsInDifferentFiles &&
4341 matchArguments2(md->getOuterScope(),mfd,md->typeString(),&mdAl,
4342 rnd ? rnd : Doxygen::globalScope,rfd,root->type,&root->argList,
4343 false,root->lang)
4344 )
4345 {
4346 GroupDef *gd=nullptr;
4347 if (!root->groups.empty() && !root->groups.front().groupname.empty())
4348 {
4349 gd = Doxygen::groupLinkedMap->find(root->groups.front().groupname);
4350 }
4351 //printf("match!\n");
4352 //printf("mnd=%p rnd=%p nsName=%s rnsName=%s\n",mnd,rnd,qPrint(nsName),qPrint(rnsName));
4353 // see if we need to create a new member
4354 found=(mnd && rnd && nsName==rnsName) || // members are in the same namespace
4355 ((mnd==nullptr && rnd==nullptr && mfd!=nullptr && // no external reference and
4356 mfd->absFilePath()==root->fileName // prototype in the same file
4357 )
4358 );
4359 // otherwise, allow a duplicate global member with the same argument list
4360 if (!found && gd && gd==md->getGroupDef() && nsName==rnsName)
4361 {
4362 // member is already in the group, so we don't want to add it again.
4363 found=true;
4364 }
4365
4366 AUTO_TRACE_ADD("combining function with prototype found={} in namespace '{}'",found,nsName);
4367
4368 if (found)
4369 {
4370 // merge argument lists
4371 ArgumentList mergedArgList = root->argList;
4372 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.empty());
4373 // merge documentation
4374 if (md->documentation().empty() && !root->doc.empty())
4375 {
4376 if (root->proto)
4377 {
4379 }
4380 else
4381 {
4383 }
4384 }
4385
4386 md->setDocumentation(root->doc,root->docFile,root->docLine);
4388 md->setDocsForDefinition(!root->proto);
4389 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
4390 {
4391 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
4392 md->setBodyDef(rfd);
4393 }
4394
4395 if (md->briefDescription().empty() && !root->brief.empty())
4396 {
4397 md->setArgsString(root->args);
4398 }
4399 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
4400
4402
4404 md->addQualifiers(root->qualifiers);
4405
4406 // merge ingroup specifiers
4407 if (md->getGroupDef()==nullptr && !root->groups.empty())
4408 {
4409 addMemberToGroups(root,md);
4410 }
4411 else if (md->getGroupDef()!=nullptr && root->groups.empty())
4412 {
4413 //printf("existing member is grouped, new member not\n");
4414 }
4415 else if (md->getGroupDef()!=nullptr && !root->groups.empty())
4416 {
4417 //printf("both members are grouped\n");
4418 }
4420
4421 // if md is a declaration and root is the corresponding
4422 // definition, then turn md into a definition.
4423 if (md->isPrototype() && !root->proto)
4424 {
4425 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
4426 md->setPrototype(false,root->fileName,root->startLine,root->startColumn);
4427 }
4428 // if md is already the definition, then add the declaration info
4429 else if (!md->isPrototype() && root->proto)
4430 {
4431 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
4432 }
4433 }
4434 }
4435 }
4436 if (found)
4437 {
4438 md_found = md;
4439 break;
4440 }
4441 }
4442 }
4443 if (!found) /* global function is unique with respect to the file */
4444 {
4445 addGlobalFunction(root,rname,scope);
4446 }
4447 else
4448 {
4449 FileDef *fd=root->fileDef();
4450 if (fd)
4451 {
4452 // add member to the file (we do this even if we have already
4453 // inserted it into the namespace)
4454 fd->insertMember(md_found);
4455 }
4456 }
4457
4458 AUTO_TRACE_ADD("unrelated function type='{}' name='{}' args='{}'",root->type,rname,root->args);
4459 }
4460 else
4461 {
4462 AUTO_TRACE_ADD("function '{}' is not processed",rname);
4463 }
4464 }
4465 else if (rname.empty())
4466 {
4467 warn(root->fileName,root->startLine,
4468 "Illegal member name found."
4469 );
4470 }
4471 }
4472 for (const auto &e : root->children()) buildFunctionList(e.get());
4473}
4474
4475//----------------------------------------------------------------------
4476
4477static void findFriends()
4478{
4479 AUTO_TRACE();
4480 for (const auto &fn : *Doxygen::functionNameLinkedMap) // for each global function name
4481 {
4482 MemberName *mn = Doxygen::memberNameLinkedMap->find(fn->memberName());
4483 if (mn)
4484 { // there are members with the same name
4485 // for each function with that name
4486 for (const auto &ifmd : *fn)
4487 {
4488 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
4489 // for each member with that name
4490 for (const auto &immd : *mn)
4491 {
4492 MemberDefMutable *mmd = toMemberDefMutable(immd.get());
4493 //printf("Checking for matching arguments
4494 // mmd->isRelated()=%d mmd->isFriend()=%d mmd->isFunction()=%d\n",
4495 // mmd->isRelated(),mmd->isFriend(),mmd->isFunction());
4496 if (fmd && mmd &&
4497 (mmd->isFriend() || (mmd->isRelated() && mmd->isFunction())) &&
4498 matchArguments2(mmd->getOuterScope(), mmd->getFileDef(), mmd->typeString(), &mmd->argumentList(),
4499 fmd->getOuterScope(), fmd->getFileDef(), fmd->typeString(), &fmd->argumentList(),
4500 true,mmd->getLanguage()
4501 )
4502
4503 ) // if the member is related and the arguments match then the
4504 // function is actually a friend.
4505 {
4506 AUTO_TRACE_ADD("Merging related global and member '{}' isFriend={} isRelated={} isFunction={}",
4507 mmd->name(),mmd->isFriend(),mmd->isRelated(),mmd->isFunction());
4508 const ArgumentList &mmdAl = mmd->argumentList();
4509 const ArgumentList &fmdAl = fmd->argumentList();
4510 mergeArguments(const_cast<ArgumentList&>(fmdAl),const_cast<ArgumentList&>(mmdAl));
4511
4512 // reset argument lists to add missing default parameters
4513 DString mmdAlStr = argListToString(mmdAl);
4514 DString fmdAlStr = argListToString(fmdAl);
4515 mmd->setArgsString(mmdAlStr);
4516 fmd->setArgsString(fmdAlStr);
4517 mmd->moveDeclArgumentList(std::make_unique<ArgumentList>(mmdAl));
4518 fmd->moveDeclArgumentList(std::make_unique<ArgumentList>(fmdAl));
4519 AUTO_TRACE_ADD("friend args='{}' member args='{}'",argListToString(fmd->argumentList()),argListToString(mmd->argumentList()));
4520
4521 if (!fmd->documentation().empty())
4522 {
4523 mmd->setDocumentation(fmd->documentation(),fmd->docFile(),fmd->docLine());
4524 }
4525 else if (!mmd->documentation().empty())
4526 {
4527 fmd->setDocumentation(mmd->documentation(),mmd->docFile(),mmd->docLine());
4528 }
4529 if (mmd->briefDescription().empty() && !fmd->briefDescription().empty())
4530 {
4531 mmd->setBriefDescription(fmd->briefDescription(),fmd->briefFile(),fmd->briefLine());
4532 }
4533 else if (!mmd->briefDescription().empty() && !fmd->briefDescription().empty())
4534 {
4535 fmd->setBriefDescription(mmd->briefDescription(),mmd->briefFile(),mmd->briefLine());
4536 }
4537 if (!fmd->inbodyDocumentation().empty())
4538 {
4540 }
4541 else if (!mmd->inbodyDocumentation().empty())
4542 {
4544 }
4545 //printf("body mmd %d fmd %d\n",mmd->getStartBodyLine(),fmd->getStartBodyLine());
4546 if (mmd->getStartBodyLine()==-1 && fmd->getStartBodyLine()!=-1)
4547 {
4548 mmd->setBodySegment(fmd->getDefLine(),fmd->getStartBodyLine(),fmd->getEndBodyLine());
4549 mmd->setBodyDef(fmd->getBodyDef());
4550 //mmd->setBodyMember(fmd);
4551 }
4552 else if (mmd->getStartBodyLine()!=-1 && fmd->getStartBodyLine()==-1)
4553 {
4554 fmd->setBodySegment(mmd->getDefLine(),mmd->getStartBodyLine(),mmd->getEndBodyLine());
4555 fmd->setBodyDef(mmd->getBodyDef());
4556 //fmd->setBodyMember(mmd);
4557 }
4559
4561
4562 mmd->addQualifiers(fmd->getQualifiers());
4563 fmd->addQualifiers(mmd->getQualifiers());
4564
4565 }
4566 }
4567 }
4568 }
4569 }
4570}
4571
4572//----------------------------------------------------------------------
4573
4575{
4576 AUTO_TRACE();
4577
4578 // find matching function declaration and definitions.
4579 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4580 {
4581 //printf("memberName=%s count=%zu\n",qPrint(mn->memberName()),mn->size());
4582 /* find a matching function declaration and definition for this function */
4583 for (const auto &imdec : *mn)
4584 {
4585 MemberDefMutable *mdec = toMemberDefMutable(imdec.get());
4586 if (mdec &&
4587 (mdec->isPrototype() ||
4588 (mdec->isVariable() && mdec->isExternal())
4589 ))
4590 {
4591 for (const auto &imdef : *mn)
4592 {
4593 MemberDefMutable *mdef = toMemberDefMutable(imdef.get());
4594 if (mdef && mdec!=mdef &&
4595 mdec->getNamespaceDef()==mdef->getNamespaceDef())
4596 {
4598 }
4599 }
4600 }
4601 }
4602 }
4603}
4604
4605//----------------------------------------------------------------------
4606
4608{
4609 AUTO_TRACE();
4610 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4611 {
4612 MemberDefMutable *mdef=nullptr,*mdec=nullptr;
4613 /* find a matching function declaration and definition for this function */
4614 for (const auto &imd : *mn)
4615 {
4616 MemberDefMutable *md = toMemberDefMutable(imd.get());
4617 if (md)
4618 {
4619 if (md->isPrototype())
4620 mdec=md;
4621 else if (md->isVariable() && md->isExternal())
4622 mdec=md;
4623
4624 if (md->isFunction() && !md->isStatic() && !md->isPrototype())
4625 mdef=md;
4626 else if (md->isVariable() && !md->isExternal() && !md->isStatic())
4627 mdef=md;
4628 }
4629
4630 if (mdef && mdec) break;
4631 }
4632 if (mdef && mdec)
4633 {
4634 const ArgumentList &mdefAl = mdef->argumentList();
4635 const ArgumentList &mdecAl = mdec->argumentList();
4636 if (
4637 matchArguments2(mdef->getOuterScope(),mdef->getFileDef(),mdef->typeString(),const_cast<ArgumentList*>(&mdefAl),
4638 mdec->getOuterScope(),mdec->getFileDef(),mdec->typeString(),const_cast<ArgumentList*>(&mdecAl),
4639 true,mdef->getLanguage()
4640 )
4641 ) /* match found */
4642 {
4643 AUTO_TRACE_ADD("merging references for mdec={} mdef={}",mdec->name(),mdef->name());
4644 mdef->mergeReferences(mdec);
4645 mdec->mergeReferences(mdef);
4646 mdef->mergeReferencedBy(mdec);
4647 mdec->mergeReferencedBy(mdef);
4648 }
4649 }
4650 }
4651}
4652
4653//----------------------------------------------------------------------
4654
4656{
4657 AUTO_TRACE();
4658 // find match between function declaration and definition for
4659 // related functions
4660 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4661 {
4662 /* find a matching function declaration and definition for this function */
4663 // for each global function
4664 for (const auto &imd : *mn)
4665 {
4666 MemberDefMutable *md = toMemberDefMutable(imd.get());
4667 if (md)
4668 {
4669 //printf(" Function '%s'\n",qPrint(md->name()));
4671 if (rmn) // check if there is a member with the same name
4672 {
4673 //printf(" Member name found\n");
4674 // for each member with the same name
4675 for (const auto &irmd : *rmn)
4676 {
4677 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
4678 //printf(" Member found: related='%d'\n",rmd->isRelated());
4679 if (rmd &&
4680 (rmd->isRelated() || rmd->isForeign()) && // related function
4681 matchArguments2( md->getOuterScope(), md->getFileDef(), md->typeString(), &md->argumentList(),
4682 rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmd->argumentList(),
4683 true,md->getLanguage()
4684 )
4685 )
4686 {
4687 AUTO_TRACE_ADD("Found related member '{}'",md->name());
4688 if (rmd->relatedAlso())
4689 md->setRelatedAlso(rmd->relatedAlso());
4690 else if (rmd->isForeign())
4691 md->makeForeign();
4692 else
4693 md->makeRelated();
4694 }
4695 }
4696 }
4697 }
4698 }
4699 }
4700}
4701
4702//----------------------------------------------------------------------
4703
4705{
4706 AUTO_TRACE();
4707 for (const auto &[qualifiedName,bodyInfo] : Doxygen::staticInitMap)
4708 {
4709 size_t i=qualifiedName.rfind("::");
4710 if (i!=std::string::npos)
4711 {
4712 DString scope = qualifiedName.substr(0,i);
4713 DString name = qualifiedName.substr(i+2);
4715 if (mn)
4716 {
4717 for (const auto &imd : *mn)
4718 {
4719 MemberDefMutable *md = toMemberDefMutable(imd.get());
4720 if (md && md->qualifiedName().str()==qualifiedName && md->isVariable())
4721 {
4722 AUTO_TRACE_ADD("found static member {} body [{}..{}]\n",
4723 md->qualifiedName(),bodyInfo.startLine,bodyInfo.endLine);
4724 md->setBodySegment(bodyInfo.defLine,
4725 bodyInfo.startLine,
4726 bodyInfo.endLine);
4727 }
4728 }
4729 }
4730 }
4731 }
4732}
4733
4734//----------------------------------------------------------------------
4735
4736/*! make a dictionary of all template arguments of class cd
4737 * that are part of the base class name.
4738 * Example: A template class A with template arguments <R,S,T>
4739 * that inherits from B<T,T,S> will have T and S in the dictionary.
4740 */
4741static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments,const std::string &name)
4742{
4743 std::map<std::string,int> templateNames;
4744 int count=0;
4745 for (const Argument &arg : templateArguments)
4746 {
4747 static const reg::Ex re(R"(\a[\w:]*)");
4748 reg::Iterator it(name,re);
4750 for (; it!=end ; ++it)
4751 {
4752 const auto &match = *it;
4753 std::string n = match.str();
4754 if (n==arg.name.str())
4755 {
4756 if (templateNames.find(n)==templateNames.end())
4757 {
4758 templateNames.emplace(n,count);
4759 }
4760 }
4761 }
4762 }
4763 return templateNames;
4764}
4765
4766/*! Searches a class from within \a context and \a cd and returns its
4767 * definition if found (otherwise nullptr is returned).
4768 */
4770{
4771 ClassDef *result=nullptr;
4772 if (cd==nullptr)
4773 {
4774 return result;
4775 }
4776 FileDef *fd=cd->getFileDef();
4777 SymbolResolver resolver(fd);
4778 if (context && cd!=context)
4779 {
4780 result = const_cast<ClassDef*>(resolver.resolveClass(context,name,true,true));
4781 }
4782 //printf("1. result=%p\n",result);
4783 if (result==nullptr)
4784 {
4785 result = const_cast<ClassDef*>(resolver.resolveClass(cd,name,true,true));
4786 }
4787 //printf("2. result=%p\n",result);
4788 if (result==nullptr) // try direct class, needed for namespaced classes imported via tag files (see bug624095)
4789 {
4790 result = getClass(name);
4791 }
4792 //printf("3. result=%p\n",result);
4793 //printf("** Trying to find %s within context %s class %s result=%s lookup=%p\n",
4794 // qPrint(name),
4795 // context ? qPrint(context->name()) : "<none>",
4796 // cd ? qPrint(cd->name()) : "<none>",
4797 // result ? qPrint(result->name()) : "<none>",
4798 // Doxygen::classLinkedMap->find(name)
4799 // );
4800 return result;
4801}
4802
4803
4804static void findUsedClassesForClass(const Entry *root,
4805 Definition *context,
4806 ClassDefMutable *masterCd,
4807 ClassDefMutable *instanceCd,
4808 bool isArtificial,
4809 const ArgumentList *actualArgs = nullptr,
4810 const TemplateNameMap &templateNames = TemplateNameMap()
4811 )
4812{
4813 AUTO_TRACE();
4814 const ArgumentList &formalArgs = masterCd->templateArguments();
4815 for (auto &mni : masterCd->memberNameInfoLinkedMap())
4816 {
4817 for (auto &mi : *mni)
4818 {
4819 const MemberDef *md=mi->memberDef();
4820 if (md->isVariable() || md->isObjCProperty()) // for each member variable in this class
4821 {
4822 AUTO_TRACE_ADD("Found variable '{}' in class '{}'",md->name(),masterCd->name());
4823 DString type = normalizeNonTemplateArgumentsInString(md->typeString(),masterCd,formalArgs);
4824 DString typedefValue = md->getLanguage()==SrcLangExt::Java ? type : resolveTypeDef(masterCd,type);
4825 if (!typedefValue.empty())
4826 {
4827 type = typedefValue;
4828 }
4829 int pos=0;
4830 DString usedClassName;
4831 DString templSpec;
4832 bool found=false;
4833 // the type can contain template variables, replace them if present
4834 type = substituteTemplateArgumentsInString(type,formalArgs,actualArgs);
4835
4836 //printf(" template substitution gives=%s\n",qPrint(type));
4837 while (!found && extractClassNameFromType(type,pos,usedClassName,templSpec,root->lang)!=-1)
4838 {
4839 // find the type (if any) that matches usedClassName
4840 SymbolResolver resolver(masterCd->getFileDef());
4841 const ClassDefMutable *typeCd = resolver.resolveClassMutable(masterCd,usedClassName,false,true);
4842 //printf("====> usedClassName=%s -> typeCd=%s\n",
4843 // qPrint(usedClassName),typeCd?qPrint(typeCd->name()):"<none>");
4844 if (typeCd)
4845 {
4846 usedClassName = typeCd->name();
4847 }
4848
4849 // replace any namespace aliases
4850 replaceNamespaceAliases(usedClassName);
4851 // add any template arguments to the class
4852 DString usedName = removeRedundantWhiteSpace(usedClassName+templSpec);
4853 //printf(" usedName=%s usedClassName=%s templSpec=%s\n",qPrint(usedName),qPrint(usedClassName),qPrint(templSpec));
4854
4855 TemplateNameMap formTemplateNames;
4856 if (templateNames.empty())
4857 {
4858 formTemplateNames = getTemplateArgumentsInName(formalArgs,usedName.str());
4859 }
4860 BaseInfo bi(usedName,Protection::Public,Specifier::Normal);
4861 findClassRelation(root,context,instanceCd,&bi,formTemplateNames,TemplateInstances,isArtificial);
4862
4863 for (const Argument &arg : masterCd->templateArguments())
4864 {
4865 if (arg.name==usedName) // type is a template argument
4866 {
4867 ClassDef *usedCd = Doxygen::hiddenClassLinkedMap->find(usedName);
4868 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4869 if (usedCd==nullptr)
4870 {
4871 usedCdm = toClassDefMutable(
4872 Doxygen::hiddenClassLinkedMap->add(usedName,
4874 masterCd->getDefFileName(),masterCd->getDefLine(),
4875 masterCd->getDefColumn(),
4876 usedName,
4877 ClassDef::Class)));
4878 if (usedCdm)
4879 {
4880 //printf("making %s a template argument!!!\n",qPrint(usedCd->name()));
4881 usedCdm->makeTemplateArgument();
4882 usedCdm->setUsedOnly(true);
4883 usedCdm->setLanguage(masterCd->getLanguage());
4884 usedCd = usedCdm;
4885 }
4886 }
4887 if (usedCd)
4888 {
4889 found=true;
4890 AUTO_TRACE_ADD("case 1: adding used class '{}'", usedCd->name());
4891 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4892 if (usedCdm)
4893 {
4894 if (isArtificial) usedCdm->setArtificial(true);
4895 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4896 }
4897 }
4898 }
4899 }
4900
4901 if (!found)
4902 {
4903 ClassDef *usedCd=findClassWithinClassContext(context,masterCd,usedName);
4904 //printf("Looking for used class %s: result=%s master=%s\n",
4905 // qPrint(usedName),usedCd?qPrint(usedCd->name()):"<none>",masterCd?qPrint(masterCd->name()):"<none>");
4906
4907 if (usedCd)
4908 {
4909 found=true;
4910 AUTO_TRACE_ADD("case 2: adding used class '{}'", usedCd->name());
4911 instanceCd->addUsedClass(usedCd,md->name(),md->protection()); // class exists
4912 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4913 if (usedCdm)
4914 {
4915 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4916 }
4917 }
4918 }
4919 }
4920 if (!found && !type.empty()) // used class is not documented in any scope
4921 {
4923 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4924 if (usedCd==nullptr && !Config_getBool(HIDE_UNDOC_RELATIONS))
4925 {
4926 if (type.endsWith("(*") || type.endsWith("(^")) // type is a function pointer
4927 {
4928 type+=md->argsString();
4929 }
4930 AUTO_TRACE_ADD("New undocumented used class '{}'", type);
4931 usedCdm = toClassDefMutable(
4934 masterCd->getDefFileName(),masterCd->getDefLine(),
4935 masterCd->getDefColumn(),
4936 type,ClassDef::Class)));
4937 if (usedCdm)
4938 {
4939 usedCdm->setUsedOnly(true);
4940 usedCdm->setLanguage(masterCd->getLanguage());
4941 usedCd = usedCdm;
4942 }
4943 }
4944 if (usedCd)
4945 {
4946 AUTO_TRACE_ADD("case 3: adding used class '{}'", usedCd->name());
4947 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4948 if (usedCdm)
4949 {
4950 if (isArtificial) usedCdm->setArtificial(true);
4951 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4952 }
4953 }
4954 }
4955 }
4956 }
4957 }
4958}
4959
4961 const Entry *root,
4962 Definition *context,
4963 ClassDefMutable *masterCd,
4964 ClassDefMutable *instanceCd,
4966 bool isArtificial,
4967 const ArgumentList *actualArgs = nullptr,
4968 const TemplateNameMap &templateNames=TemplateNameMap()
4969 )
4970{
4971 AUTO_TRACE("name={}",root->name);
4972 // The base class could ofcouse also be a non-nested class
4973 const ArgumentList &formalArgs = masterCd->templateArguments();
4974 for (const BaseInfo &bi : root->extends)
4975 {
4976 //printf("masterCd=%s bi.name='%s' #actualArgs=%d\n",
4977 // qPrint(masterCd->localName()),qPrint(bi.name),actualArgs ? (int)actualArgs->size() : -1);
4978 TemplateNameMap formTemplateNames;
4979 if (templateNames.empty())
4980 {
4981 formTemplateNames = getTemplateArgumentsInName(formalArgs,bi.name.str());
4982 }
4983 BaseInfo tbi = bi;
4984 tbi.name = substituteTemplateArgumentsInString(bi.name,formalArgs,actualArgs);
4985 //printf("masterCd=%p instanceCd=%p bi->name=%s tbi.name=%s\n",(void*)masterCd,(void*)instanceCd,qPrint(bi.name),qPrint(tbi.name));
4986
4987 if (mode==DocumentedOnly)
4988 {
4989 // find a documented base class in the correct scope
4990 if (!findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,DocumentedOnly,isArtificial))
4991 {
4992 // 1.8.2: decided to show inheritance relations even if not documented,
4993 // we do make them artificial, so they do not appear in the index
4994 //if (!Config_getBool(HIDE_UNDOC_RELATIONS))
4995 bool b = Config_getBool(HIDE_UNDOC_RELATIONS) ? true : isArtificial;
4996 //{
4997 // no documented base class -> try to find an undocumented one
4998 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,Undocumented,b);
4999 //}
5000 }
5001 }
5002 else if (mode==TemplateInstances)
5003 {
5004 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,TemplateInstances,isArtificial);
5005 }
5006 }
5007}
5008
5009//----------------------------------------------------------------------
5010
5011static void findTemplateInstanceRelation(const Entry *root,
5012 Definition *context,
5013 ClassDefMutable *templateClass,const DString &templSpec,
5014 const TemplateNameMap &templateNames,
5015 bool isArtificial)
5016{
5017 AUTO_TRACE("Derived from template '{}' with parameters '{}' isArtificial={}",
5018 templateClass->name(),templSpec,isArtificial);
5019
5020 DString tempArgsStr = tempArgListToString(templateClass->templateArguments(),root->lang,false);
5021 bool existingClass = templSpec==tempArgsStr;
5022 if (existingClass) return; // avoid recursion
5023
5024 bool freshInstance=false;
5025 ClassDefMutable *instanceClass = toClassDefMutable(
5026 templateClass->insertTemplateInstance(
5027 root->fileName,root->startLine,root->startColumn,templSpec,freshInstance));
5028 if (instanceClass)
5029 {
5030 if (freshInstance)
5031 {
5032 instanceClass->setArtificial(true);
5033 instanceClass->setLanguage(root->lang);
5034
5035 AUTO_TRACE_ADD("found fresh instance '{}'",instanceClass->name());
5036 instanceClass->setTemplateBaseClassNames(templateNames);
5037
5038 // search for new template instances caused by base classes of
5039 // instanceClass
5040 auto it_pair = g_classEntries.equal_range(templateClass->name().str());
5041 for (auto it=it_pair.first ; it!=it_pair.second ; ++it)
5042 {
5043 const Entry *templateRoot = it->second;
5044 AUTO_TRACE_ADD("template root found '{}' templSpec='{}'",templateRoot->name,templSpec);
5045 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(root->lang,templSpec);
5046 findBaseClassesForClass(templateRoot,context,templateClass,instanceClass,
5047 TemplateInstances,isArtificial,templArgs.get(),templateNames);
5048
5049 findUsedClassesForClass(templateRoot,context,templateClass,instanceClass,
5050 isArtificial,templArgs.get(),templateNames);
5051 }
5052 }
5053 else
5054 {
5055 AUTO_TRACE_ADD("instance already exists");
5056 }
5057 }
5058}
5059
5060//----------------------------------------------------------------------
5061
5062static void resolveTemplateInstanceInType(const Entry *root,const Definition *scope,const MemberDef *md)
5063{
5064 // For a statement like 'using X = T<A>', add a template instance 'T<A>' as a symbol, so it can
5065 // be used to match arguments (see issue #11111)
5066 AUTO_TRACE();
5067 DString ttype = md->typeString();
5068 ttype.stripPrefix("typedef ");
5069 if (size_t ti=ttype.find('<'); ti!=DString::npos)
5070 {
5071 DString templateClassName = ttype.left(ti);
5072 SymbolResolver resolver(root->fileDef());
5073 ClassDefMutable *baseClass = resolver.resolveClassMutable(scope ? scope : Doxygen::globalScope,
5074 templateClassName, true, true);
5075 AUTO_TRACE_ADD("templateClassName={} baseClass={}",templateClassName,baseClass?baseClass->name():"<none>");
5076 if (baseClass)
5077 {
5078 const ArgumentList &tl = baseClass->templateArguments();
5079 TemplateNameMap templateNames = getTemplateArgumentsInName(tl,templateClassName.str());
5081 baseClass,
5082 ttype.mid(ti),
5083 templateNames,
5084 baseClass->isArtificial());
5085 }
5086 }
5087}
5088
5089//----------------------------------------------------------------------
5090
5091static bool isRecursiveBaseClass(const DString &scope,const DString &name)
5092{
5093 DString n=name;
5094 if (size_t index=n.find('<'); index!=DString::npos)
5095 {
5096 n=n.left(index);
5097 }
5098 bool result = rightScopeMatch(scope,n);
5099 return result;
5100}
5101
5103{
5104 if (name.empty()) return 0;
5105 int l = static_cast<int>(name.length());
5106 if (name[l-1]=='>') // search backward to find the matching <, allowing nested <...> and strings.
5107 {
5108 int count=1;
5109 int i=l-2;
5110 char insideQuote=0;
5111 while (count>0 && i>=0)
5112 {
5113 char c = name[i--];
5114 switch (c)
5115 {
5116 case '>': if (!insideQuote) count++; break;
5117 case '<': if (!insideQuote) count--; break;
5118 case '\'': if (!insideQuote) insideQuote=c;
5119 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
5120 break;
5121 case '"': if (!insideQuote) insideQuote=c;
5122 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
5123 break;
5124 default: break;
5125 }
5126 }
5127 if (i>=0) l=i+1;
5128 }
5129 return l;
5130}
5131
5133 const Entry *root,
5134 Definition *context,
5135 ClassDefMutable *cd,
5136 const BaseInfo *bi,
5137 const TemplateNameMap &templateNames,
5139 bool isArtificial
5140 )
5141{
5142 AUTO_TRACE("name={} base={} isArtificial={} mode={}",cd->name(),bi->name,isArtificial,(int)mode);
5143
5144 DString biName=bi->name;
5145 bool explicitGlobalScope=false;
5146 if (biName.startsWith("::")) // explicit global scope
5147 {
5148 biName=biName.mid(2);
5149 explicitGlobalScope=true;
5150 }
5151
5152 Entry *parentNode=root->parent();
5153 bool lastParent=false;
5154 do // for each parent scope, starting with the largest scope
5155 // (in case of nested classes)
5156 {
5157 DString scopeName= parentNode ? parentNode->name : DString();
5158 int scopeOffset=explicitGlobalScope ? 0 : static_cast<int>(scopeName.length());
5159 do // try all parent scope prefixes, starting with the largest scope
5160 {
5161 //printf("scopePrefix='%s' biName='%s'\n",
5162 // qPrint(scopeName.left(scopeOffset)),qPrint(biName));
5163
5164 DString baseClassName=biName;
5165 if (scopeOffset>0)
5166 {
5167 baseClassName.prepend(scopeName.left(scopeOffset)+"::");
5168 }
5169 if (root->lang==SrcLangExt::CSharp)
5170 {
5171 baseClassName = mangleCSharpGenericName(baseClassName);
5172 }
5173 AUTO_TRACE_ADD("cd='{}' baseClassName='{}'",cd->name(),baseClassName);
5174 SymbolResolver resolver(cd->getFileDef());
5175 ClassDefMutable *baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
5176 baseClassName,
5177 mode==Undocumented,
5178 true
5179 );
5180 const MemberDef *baseClassTypeDef = resolver.getTypedef();
5181 DString templSpec = resolver.getTemplateSpec();
5182 //printf("baseClassName=%s baseClass=%p cd=%p explicitGlobalScope=%d\n",
5183 // qPrint(baseClassName),baseClass,cd,explicitGlobalScope);
5184 //printf(" scope='%s' baseClassName='%s' baseClass=%s templSpec=%s\n",
5185 // cd ? qPrint(cd->name()):"<none>",
5186 // qPrint(baseClassName),
5187 // baseClass?qPrint(baseClass->name()):"<none>",
5188 // qPrint(templSpec)
5189 // );
5190 //if (baseClassName.left(root->name.length())!=root->name ||
5191 // baseClassName.at(root->name.length())!='<'
5192 // ) // Check for base class with the same name.
5193 // // If found then look in the outer scope for a match
5194 // // and prevent recursion.
5195 if (!isRecursiveBaseClass(root->name,baseClassName)
5196 || explicitGlobalScope
5197 // sadly isRecursiveBaseClass always true for UNO IDL ifc/svc members
5198 // (i.e. this is needed for addInterfaceOrServiceToServiceOrSingleton)
5199 || (root->lang==SrcLangExt::IDL &&
5200 (root->section.isExportedInterface() ||
5201 root->section.isIncludedService()))
5202 )
5203 {
5204 AUTO_TRACE_ADD("class relation '{}' inherited/used by '{}' found prot={} virt={} templSpec='{}'",
5205 baseClassName, root->name, bi->prot, bi->virt, templSpec);
5206
5207 int i=findTemplateSpecializationPosition(baseClassName);
5208 size_t si=baseClassName.rfind("::",i);
5209 if (si==DString::npos) si=0;
5210 if (baseClass==nullptr && static_cast<size_t>(i)!=baseClassName.length())
5211 // base class has template specifiers
5212 {
5213 // TODO: here we should try to find the correct template specialization
5214 // but for now, we only look for the unspecialized base class.
5215 int e=findEndOfTemplate(baseClassName,i+1);
5216 //printf("baseClass==0 i=%d e=%d\n",i,e);
5217 if (e!=-1) // end of template was found at e
5218 {
5219 templSpec = removeRedundantWhiteSpace(baseClassName.mid(i,e-i));
5220 baseClassName = baseClassName.left(i)+baseClassName.mid(e);
5221 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
5222 baseClassName,
5223 mode==Undocumented,
5224 true
5225 );
5226 baseClassTypeDef = resolver.getTypedef();
5227 //printf("baseClass=%p -> baseClass=%s templSpec=%s\n",
5228 // baseClass,qPrint(baseClassName),qPrint(templSpec));
5229 }
5230 }
5231 else if (baseClass && !templSpec.empty()) // we have a known class, but also
5232 // know it is a template, so see if
5233 // we can also link to the explicit
5234 // instance (for instance if a class
5235 // derived from a template argument)
5236 {
5237 //printf("baseClass=%s templSpec=%s\n",qPrint(baseClass->name()),qPrint(templSpec));
5238 ClassDefMutable *templClass=getClassMutable(baseClass->name()+templSpec);
5239 if (templClass)
5240 {
5241 // use the template instance instead of the template base.
5242 baseClass = templClass;
5243 templSpec.clear();
5244 }
5245 }
5246
5247 //printf("cd=%p baseClass=%p\n",cd,baseClass);
5248 bool found=baseClass!=nullptr && (baseClass!=cd || mode==TemplateInstances);
5249 AUTO_TRACE_ADD("1. found={}",found);
5250 if (!found && si!=DString::npos)
5251 {
5252 // replace any namespace aliases
5253 replaceNamespaceAliases(baseClassName);
5254 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
5255 baseClassName,
5256 mode==Undocumented,
5257 true
5258 );
5259 baseClassTypeDef = resolver.getTypedef();
5260 found=baseClass!=nullptr && baseClass!=cd;
5261 if (found) templSpec = resolver.getTemplateSpec();
5262 }
5263 AUTO_TRACE_ADD("2. found={}",found);
5264
5265 if (!found)
5266 {
5267 baseClass=toClassDefMutable(findClassWithinClassContext(context,cd,baseClassName));
5268 //printf("findClassWithinClassContext(%s,%s)=%p\n",
5269 // qPrint(cd->name()),qPrint(baseClassName),baseClass);
5270 found = baseClass!=nullptr && baseClass!=cd;
5271
5272 }
5273 AUTO_TRACE_ADD("3. found={}",found);
5274 if (!found)
5275 {
5276 // for PHP the "use A\B as C" construct map class C to A::B, so we lookup
5277 // the class name also in the alias mapping.
5278 auto it = Doxygen::namespaceAliasMap.find(baseClassName.str());
5279 if (it!=Doxygen::namespaceAliasMap.end()) // see if it is indeed a class.
5280 {
5281 baseClass=getClassMutable(it->second.alias);
5282 found = baseClass!=nullptr && baseClass!=cd;
5283 }
5284 }
5285 bool isATemplateArgument = templateNames.find(biName.str())!=templateNames.end();
5286
5287 AUTO_TRACE_ADD("4. found={}",found);
5288 if (found)
5289 {
5290 AUTO_TRACE_ADD("Documented base class '{}' templSpec='{}'",biName,templSpec);
5291 // add base class to this class
5292
5293 // if templSpec is not empty then we should "instantiate"
5294 // the template baseClass. A new ClassDef should be created
5295 // to represent the instance. To be able to add the (instantiated)
5296 // members and documentation of a template class
5297 // (inserted in that template class at a later stage),
5298 // the template should know about its instances.
5299 // the instantiation process, should be done in a recursive way,
5300 // since instantiating a template may introduce new inheritance
5301 // relations.
5302 if (!templSpec.empty() && mode==TemplateInstances)
5303 {
5304 // if baseClass is actually a typedef then we should not
5305 // instantiate it, since typedefs are in a different namespace
5306 // see bug531637 for an example where this would otherwise hang
5307 // Doxygen
5308 if (baseClassTypeDef==nullptr)
5309 {
5310 //printf(" => findTemplateInstanceRelation: %s\n",qPrint(baseClass->name()));
5311 findTemplateInstanceRelation(root,context,baseClass,templSpec,templateNames,baseClass->isArtificial());
5312 }
5313 }
5314 else if (mode==DocumentedOnly || mode==Undocumented)
5315 {
5316 //printf(" => insert base class\n");
5317 DString usedName;
5318 if (baseClassTypeDef)
5319 {
5320 usedName=biName;
5321 //printf("***** usedName=%s templSpec=%s\n",qPrint(usedName),qPrint(templSpec));
5322 }
5323 Protection prot = bi->prot;
5324 if (Config_getBool(SIP_SUPPORT)) prot=Protection::Public;
5325 if (cd!=baseClass && !cd->isSubClass(baseClass) && baseClass->isBaseClass(cd,true,templSpec)==0) // check for recursion, see bug690787
5326 {
5327 AUTO_TRACE_ADD("insertBaseClass name={} prot={} virt={} templSpec={}",usedName,prot,bi->virt,templSpec);
5328 cd->insertBaseClass(baseClass,usedName,prot,bi->virt,templSpec);
5329 // add this class as super class to the base class
5330 baseClass->insertSubClass(cd,prot,bi->virt,templSpec);
5331 }
5332 else
5333 {
5334 warn(root->fileName,root->startLine,
5335 "Detected potential recursive class relation "
5336 "between class {} and base class {}!",
5337 cd->name(),baseClass->name()
5338 );
5339 }
5340 }
5341 return true;
5342 }
5343 else if (mode==Undocumented && (scopeOffset==0 || isATemplateArgument))
5344 {
5345 AUTO_TRACE_ADD("New undocumented base class '{}' baseClassName='{}' templSpec='{}' isArtificial={}",
5346 biName,baseClassName,templSpec,isArtificial);
5347 baseClass=nullptr;
5348 if (isATemplateArgument)
5349 {
5350 baseClass = toClassDefMutable(Doxygen::hiddenClassLinkedMap->find(baseClassName));
5351 if (baseClass==nullptr) // not found (or alias)
5352 {
5353 baseClass= toClassDefMutable(
5354 Doxygen::hiddenClassLinkedMap->add(baseClassName,
5355 createClassDef(root->fileName,root->startLine,root->startColumn,
5356 baseClassName,
5357 ClassDef::Class)));
5358 if (baseClass) // really added (not alias)
5359 {
5360 if (isArtificial) baseClass->setArtificial(true);
5361 baseClass->setLanguage(root->lang);
5362 }
5363 }
5364 }
5365 else
5366 {
5367 baseClass = toClassDefMutable(Doxygen::classLinkedMap->find(baseClassName));
5368 //printf("*** classDDict->find(%s)=%p biName=%s templSpec=%s\n",
5369 // qPrint(baseClassName),baseClass,qPrint(biName),qPrint(templSpec));
5370 if (baseClass==nullptr) // not found (or alias)
5371 {
5372 baseClass = toClassDefMutable(
5373 Doxygen::classLinkedMap->add(baseClassName,
5374 createClassDef(root->fileName,root->startLine,root->startColumn,
5375 baseClassName,
5376 ClassDef::Class)));
5377 if (baseClass) // really added (not alias)
5378 {
5379 if (isArtificial) baseClass->setArtificial(true);
5380 baseClass->setLanguage(root->lang);
5381 si = baseClassName.rfind("::");
5382 if (si!=DString::npos) // class is nested
5383 {
5384 Definition *sd = findScopeFromQualifiedName(Doxygen::globalScope,baseClassName.left(si),nullptr,root->tagInfo());
5385 if (sd==nullptr || sd==Doxygen::globalScope) // outer scope not found
5386 {
5387 baseClass->setArtificial(true); // see bug678139
5388 }
5389 }
5390 }
5391 }
5392 }
5393 if (baseClass)
5394 {
5395 if (biName.endsWith("-p"))
5396 {
5397 biName="<"+biName.left(biName.length()-2)+">";
5398 }
5399 if (!cd->isSubClass(baseClass) && cd!=baseClass && cd->isBaseClass(baseClass,true,templSpec)==0) // check for recursion
5400 {
5401 AUTO_TRACE_ADD("insertBaseClass name={} prot={} virt={} templSpec={}",biName,bi->prot,bi->virt,templSpec);
5402 // add base class to this class
5403 cd->insertBaseClass(baseClass,biName,bi->prot,bi->virt,templSpec);
5404 // add this class as super class to the base class
5405 baseClass->insertSubClass(cd,bi->prot,bi->virt,templSpec);
5406 }
5407 // the undocumented base was found in this file
5408 baseClass->insertUsedFile(root->fileDef());
5409
5410 Definition *scope = buildScopeFromQualifiedName(baseClass->name(),root->lang,nullptr);
5411 if (scope!=baseClass)
5412 {
5413 baseClass->setOuterScope(scope);
5414 }
5415
5416 if (baseClassName.endsWith("-p"))
5417 {
5419 }
5420 return true;
5421 }
5422 else
5423 {
5424 AUTO_TRACE_ADD("Base class '{}' not created (alias?)",biName);
5425 }
5426 }
5427 else
5428 {
5429 AUTO_TRACE_ADD("Base class '{}' not found",biName);
5430 }
5431 }
5432 else
5433 {
5434 if (mode!=TemplateInstances)
5435 {
5436 warn(root->fileName,root->startLine,
5437 "Detected potential recursive class relation "
5438 "between class {} and base class {}!",
5439 root->name,baseClassName
5440 );
5441 }
5442 // for mode==TemplateInstance this case is quite common and
5443 // indicates a relation between a template class and a template
5444 // instance with the same name.
5445 }
5446 if (scopeOffset==0)
5447 {
5448 scopeOffset=-1;
5449 }
5450 else
5451 {
5452 size_t o = scopeName.rfind("::",scopeOffset-1);
5453 scopeOffset = o!=DString::npos ? static_cast<int>(o) : 0;
5454 }
5455 //printf("new scopeOffset='%d'",scopeOffset);
5456 } while (scopeOffset>=0);
5457
5458 if (parentNode==nullptr)
5459 {
5460 lastParent=true;
5461 }
5462 else
5463 {
5464 parentNode=parentNode->parent();
5465 }
5466 } while (lastParent);
5467
5468 return false;
5469}
5470
5471//----------------------------------------------------------------------
5472// Computes the base and super classes for each class in the tree
5473
5474static bool isClassSection(const Entry *root)
5475{
5476 if ( !root->name.empty() )
5477 {
5478 if (root->section.isCompound())
5479 // is it a compound (class, struct, union, interface ...)
5480 {
5481 return true;
5482 }
5483 else if (root->section.isCompoundDoc())
5484 // is it a documentation block with inheritance info.
5485 {
5486 bool hasExtends = !root->extends.empty();
5487 if (hasExtends) return true;
5488 }
5489 }
5490 return false;
5491}
5492
5493
5494/*! Builds a dictionary of all entry nodes in the tree starting with \a root
5495 */
5496static void findClassEntries(const Entry *root)
5497{
5498 if (isClassSection(root))
5499 {
5500 g_classEntries.emplace(root->name.str(),root);
5501 }
5502 for (const auto &e : root->children()) findClassEntries(e.get());
5503}
5504
5505static DString extractClassName(const Entry *root)
5506{
5507 // strip any anonymous scopes first
5510 if (size_t i=bName.find('<'); (root->lang==SrcLangExt::CSharp || root->lang==SrcLangExt::Java) && i!=DString::npos)
5511 {
5512 // a Java/C# generic class looks like a C++ specialization, so we need to strip the
5513 // template part before looking for matches
5514 if (root->lang==SrcLangExt::CSharp)
5515 {
5516 bName = mangleCSharpGenericName(root->name);
5517 }
5518 else
5519 {
5520 bName = bName.left(i);
5521 }
5522 }
5523 return bName;
5524}
5525
5526/*! Using the dictionary build by findClassEntries(), this
5527 * function will look for additional template specialization that
5528 * exists as inheritance relations only. These instances will be
5529 * added to the template they are derived from.
5530 */
5532{
5533 AUTO_TRACE();
5534 ClassDefSet visitedClasses;
5535 for (const auto &[name,root] : g_classEntries)
5536 {
5537 DString bName = extractClassName(root);
5538 ClassDefMutable *cdm = getClassMutable(bName);
5539 if (cdm)
5540 {
5541 findBaseClassesForClass(root,cdm,cdm,cdm,TemplateInstances,false);
5542 }
5543 }
5544}
5545
5547{
5548 AUTO_TRACE("root->name={} cd={}",root->name,cd->name());
5549 size_t i = root->name.find('<');
5550 size_t j = root->name.rfind('>');
5551 size_t k = j!=DString::npos ? root->name.find("::",j+1) : DString::npos; // A<T::B> => ok, A<T>::B => nok
5552 if (i!=DString::npos && j!=DString::npos && k==DString::npos && root->lang!=SrcLangExt::CSharp && root->lang!=SrcLangExt::Java)
5553 {
5554 ClassDefMutable *master = getClassMutable(root->name.left(i));
5555 if (master && master!=cd && !cd->templateMaster())
5556 {
5557 AUTO_TRACE_ADD("class={} master={}",cd->name(),cd->templateMaster()?cd->templateMaster()->name():"<none>",master->name());
5558 cd->setTemplateMaster(master);
5559 master->insertExplicitTemplateInstance(cd,root->name.mid(i));
5560 }
5561 }
5562}
5563
5565{
5566 AUTO_TRACE();
5567 for (const auto &[name,root] : g_classEntries)
5568 {
5569 DString bName = extractClassName(root);
5570 ClassDefMutable *cdm = getClassMutable(bName);
5571 if (cdm)
5572 {
5573 findUsedClassesForClass(root,cdm,cdm,cdm,true);
5575 cdm->addTypeConstraints();
5576 }
5577 }
5578}
5579
5581{
5582 AUTO_TRACE();
5583 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5584 {
5585 if (!nd->hasDocumentation())
5586 {
5587 if ((EntryType::guessSection(nd->getDefFileName()).isHeader() ||
5588 nd->getLanguage() == SrcLangExt::Fortran) && // Fortran doesn't have header files.
5589 !Config_getBool(HIDE_UNDOC_NAMESPACES) // undocumented namespaces are visible
5590 )
5591 {
5592 warn_undoc(nd->getDefFileName(),nd->getDefLine(), "{} {} is not documented.",
5593 nd->getLanguage() == SrcLangExt::Fortran ? "Module" : "Namespace",
5594 nd->name());
5595 }
5596 }
5597 }
5598}
5599
5601{
5602 AUTO_TRACE();
5603 for (const auto &[name,root] : g_classEntries)
5604 {
5605 DString bName = extractClassName(root);
5606 ClassDefMutable *cd = getClassMutable(bName);
5607 if (cd)
5608 {
5609 findBaseClassesForClass(root,cd,cd,cd,DocumentedOnly,false);
5610 }
5611 size_t numMembers = cd ? cd->memberNameInfoLinkedMap().size() : 0;
5612 if ((cd==nullptr || (!cd->hasDocumentation() && !cd->isReference())) && numMembers>0 && !bName.endsWith("::"))
5613 {
5614 if (!root->name.empty() && root->name.find('@')==DString::npos && // normal name
5615 (EntryType::guessSection(root->fileName).isHeader() ||
5616 Config_getBool(EXTRACT_LOCAL_CLASSES)) && // not defined in source file
5617 protectionLevelVisible(root->protection) && // hidden by protection
5618 !Config_getBool(HIDE_UNDOC_CLASSES) // undocumented class are visible
5619 )
5620 warn_undoc(root->fileName,root->startLine, "Compound {} is not documented.", root->name);
5621 }
5622 }
5623}
5624
5626{
5627 AUTO_TRACE();
5628 for (const auto &[name,root] : g_classEntries)
5629 {
5633 // strip any anonymous scopes first
5634 if (cd && !cd->getTemplateInstances().empty())
5635 {
5636 AUTO_TRACE_ADD("Template class '{}'",cd->name());
5637 for (const auto &ti : cd->getTemplateInstances()) // for each template instance
5638 {
5639 ClassDefMutable *tcd=toClassDefMutable(ti.classDef);
5640 if (tcd)
5641 {
5642 AUTO_TRACE_ADD("Template instance '{}'",tcd->name());
5643 DString templSpec = ti.templSpec;
5644 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(tcd->getLanguage(),templSpec);
5645 for (const BaseInfo &bi : root->extends)
5646 {
5647 // check if the base class is a template argument
5648 BaseInfo tbi = bi;
5649 const ArgumentList &tl = cd->templateArguments();
5650 if (!tl.empty())
5651 {
5652 TemplateNameMap baseClassNames = tcd->getTemplateBaseClassNames();
5653 TemplateNameMap templateNames = getTemplateArgumentsInName(tl,bi.name.str());
5654 // for each template name that we inherit from we need to
5655 // substitute the formal with the actual arguments
5656 TemplateNameMap actualTemplateNames;
5657 for (const auto &tn_kv : templateNames)
5658 {
5659 size_t templIndex = tn_kv.second;
5660 Argument actArg;
5661 bool hasActArg=false;
5662 if (templIndex<templArgs->size())
5663 {
5664 actArg=templArgs->at(templIndex);
5665 hasActArg=true;
5666 }
5667 if (hasActArg &&
5668 baseClassNames.find(actArg.type.str())!=baseClassNames.end() &&
5669 actualTemplateNames.find(actArg.type.str())==actualTemplateNames.end()
5670 )
5671 {
5672 actualTemplateNames.emplace(actArg.type.str(),static_cast<int>(templIndex));
5673 }
5674 }
5675
5676 tbi.name = substituteTemplateArgumentsInString(bi.name,tl,templArgs.get());
5677 // find a documented base class in the correct scope
5678 if (!findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,DocumentedOnly,false))
5679 {
5680 // no documented base class -> try to find an undocumented one
5681 findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,Undocumented,true);
5682 }
5683 }
5684 }
5685 }
5686 }
5687 }
5688 }
5689}
5690
5691//-----------------------------------------------------------------------
5692// compute the references (anchors in HTML) for each function in the file
5693
5695{
5696 AUTO_TRACE();
5697 for (const auto &cd : *Doxygen::classLinkedMap)
5698 {
5699 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5700 if (cdm)
5701 {
5702 cdm->computeAnchors();
5703 }
5704 }
5705 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5706 {
5707 for (const auto &fd : *fn)
5708 {
5709 fd->computeAnchors();
5710 }
5711 }
5712 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5713 {
5715 if (ndm)
5716 {
5717 ndm->computeAnchors();
5718 }
5719 }
5720 for (const auto &gd : *Doxygen::groupLinkedMap)
5721 {
5722 gd->computeAnchors();
5723 }
5724}
5725
5726//----------------------------------------------------------------------
5727
5728
5729template<typename Func>
5730static void applyToAllDefinitions(Func func)
5731{
5732 for (const auto &cd : *Doxygen::classLinkedMap)
5733 {
5734 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5735 if (cdm)
5736 {
5737 func(cdm);
5738 }
5739 }
5740
5741 for (const auto &cd : *Doxygen::conceptLinkedMap)
5742 {
5743 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
5744 if (cdm)
5745 {
5746 func(cdm);
5747 }
5748 }
5749
5750 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5751 {
5752 for (const auto &fd : *fn)
5753 {
5754 func(fd.get());
5755 }
5756 }
5757
5758 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5759 {
5761 if (ndm)
5762 {
5763 func(ndm);
5764 }
5765 }
5766
5767 for (const auto &gd : *Doxygen::groupLinkedMap)
5768 {
5769 func(gd.get());
5770 }
5771
5772 for (const auto &pd : *Doxygen::pageLinkedMap)
5773 {
5774 func(pd.get());
5775 }
5776
5777 for (const auto &dd : *Doxygen::dirLinkedMap)
5778 {
5779 func(dd.get());
5780 }
5781
5782 func(&ModuleManager::instance());
5783}
5784
5785//----------------------------------------------------------------------
5786
5788{
5789 AUTO_TRACE();
5790 applyToAllDefinitions([](auto* obj) { obj->addRequirementReferences(); });
5791}
5792
5793//----------------------------------------------------------------------
5794
5796{
5797 AUTO_TRACE();
5798 applyToAllDefinitions([](auto* obj) { obj->addListReferences(); });
5799}
5800
5801
5802//----------------------------------------------------------------------
5803
5805{
5806 AUTO_TRACE();
5808 {
5809 rl->generatePage();
5810 }
5811}
5812
5813//----------------------------------------------------------------------
5814// Copy the documentation in entry 'root' to member definition 'md' and
5815// set the function declaration of the member to 'funcDecl'. If the boolean
5816// over_load is set the standard overload text is added.
5817
5818static void addMemberDocs(const Entry *root,
5819 MemberDefMutable *md, const DString &funcDecl,
5820 const ArgumentList *al,
5821 bool over_load,
5822 TypeSpecifier spec
5823 )
5824{
5825 if (md==nullptr) return;
5826 AUTO_TRACE("scope='{}' name='{}' args='{}' funcDecl='{}' mSpec={}",
5827 root->parent()->name,md->name(),md->argsString(),funcDecl,spec);
5828 if (!root->section.isDoc()) // @fn or @var does not need to specify the complete definition, so don't overwrite it
5829 {
5830 DString fDecl=funcDecl;
5831 // strip extern specifier
5832 fDecl.stripPrefix("extern ");
5833 md->setDefinition(fDecl);
5834 }
5836 md->addQualifiers(root->qualifiers);
5838 const NamespaceDef *nd=md->getNamespaceDef();
5839 DString fullName;
5840 if (cd)
5841 fullName = cd->name();
5842 else if (nd)
5843 fullName = nd->name();
5844
5845 if (!fullName.empty()) fullName+="::";
5846 fullName+=md->name();
5847 FileDef *rfd=root->fileDef();
5848
5849 // TODO determine scope based on root not md
5850 Definition *rscope = md->getOuterScope();
5851
5852 const ArgumentList &mdAl = md->argumentList();
5853 if (al)
5854 {
5855 ArgumentList mergedAl = *al;
5856 //printf("merging arguments (1) docs=%d\n",root->doc.empty());
5857 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedAl,!root->doc.empty());
5858 }
5859 else
5860 {
5861 if (
5862 matchArguments2( md->getOuterScope(), md->getFileDef(),md->typeString(),const_cast<ArgumentList*>(&mdAl),
5863 rscope,rfd,root->type,&root->argList,
5864 true, root->lang
5865 )
5866 )
5867 {
5868 //printf("merging arguments (2)\n");
5869 ArgumentList mergedArgList = root->argList;
5870 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.empty());
5871 }
5872 }
5873 if (over_load) // the \overload keyword was used
5874 {
5876 if (!root->doc.empty())
5877 {
5878 doc+="<p>";
5879 doc+=root->doc;
5880 }
5881 md->setDocumentation(doc,root->docFile,root->docLine);
5883 md->setDocsForDefinition(!root->proto);
5884 }
5885 else
5886 {
5887 //printf("overwrite!\n");
5888 md->setDocumentation(root->doc,root->docFile,root->docLine);
5889 md->setDocsForDefinition(!root->proto);
5890
5891 //printf("overwrite!\n");
5892 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
5893
5894 if (
5895 (md->inbodyDocumentation().empty() ||
5896 !root->parent()->name.empty()
5897 ) && !root->inbodyDocs.empty()
5898 )
5899 {
5901 }
5902 }
5903
5904 //printf("initializer: '%s'(isEmpty=%d) '%s'(isEmpty=%d)\n",
5905 // qPrint(md->initializer()),md->initializer().empty(),
5906 // qPrint(root->initializer),root->initializer.empty()
5907 // );
5908 std::string rootInit = root->initializer.str();
5909 if (md->initializer().empty() && !rootInit.empty())
5910 {
5911 //printf("setInitializer\n");
5912 md->setInitializer(rootInit);
5913 }
5914 if (md->requiresClause().empty() && !root->req.empty())
5915 {
5916 md->setRequiresClause(root->req);
5917 }
5918
5919 md->setMaxInitLines(root->initLines);
5920
5921 if (rfd)
5922 {
5923 if ((md->getStartBodyLine()==-1 && root->bodyLine!=-1)
5924 )
5925 {
5926 //printf("Setting new body segment [%d,%d]\n",root->bodyLine,root->endBodyLine);
5927 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
5928 md->setBodyDef(rfd);
5929 }
5930
5931 md->setRefItems(root->sli);
5932 md->setRequirementReferences(root->rqli);
5933 }
5934
5936 md->addQualifiers(root->qualifiers);
5937
5938 md->mergeMemberSpecifiers(spec);
5940 addMemberToGroups(root,md);
5941 addIncludeFile(md,rfd,root);
5943 if (cd) cd->insertUsedFile(rfd);
5944 //printf("root->mGrpId=%d\n",root->mGrpId);
5945 if (root->mGrpId!=-1)
5946 {
5947 if (md->getMemberGroupId()!=-1)
5948 {
5949 if (md->getMemberGroupId()!=root->mGrpId)
5950 {
5951 warn(root->fileName,root->startLine,
5952 "member {} belongs to two different groups. The second one found here will be ignored.",
5953 md->name()
5954 );
5955 }
5956 }
5957 else // set group id
5958 {
5959 //printf("setMemberGroupId=%d md=%s\n",root->mGrpId,qPrint(md->name()));
5960 md->setMemberGroupId(root->mGrpId);
5961 }
5962 }
5963 md->addQualifiers(root->qualifiers);
5964}
5965
5966//----------------------------------------------------------------------
5967// find a class definition given the scope name and (optionally) a
5968// template list specifier
5969
5971 const DString &scopeName)
5972{
5973 SymbolResolver resolver(fd);
5974 const ClassDef *tcd = resolver.resolveClass(nd,scopeName,true,true);
5975 //printf("findClassDefinition(fd=%s,ns=%s,scopeName=%s)='%s'\n",
5976 // qPrint(fd?fd->name():""),qPrint(nd?nd->name():""),
5977 // qPrint(scopeName),qPrint(tcd?tcd->name():""));
5978 return tcd;
5979}
5980
5981//----------------------------------------------------------------------------
5982// Returns true, if the entry belongs to the group of the member definition,
5983// otherwise false.
5984
5985static bool isEntryInGroupOfMember(const Entry *root,const MemberDef *md,bool allowNoGroup=false)
5986{
5987 const GroupDef *gd = md->getGroupDef();
5988 if (!gd)
5989 {
5990 return allowNoGroup;
5991 }
5992
5993 for (const auto &g : root->groups)
5994 {
5995 if (g.groupname == gd->name())
5996 {
5997 return true; // matching group
5998 }
5999 }
6000
6001 return false;
6002}
6003
6004//----------------------------------------------------------------------
6005// Adds the documentation contained in 'root' to a global function
6006// with name 'name' and argument list 'args' (for overloading) and
6007// function declaration 'decl' to the corresponding member definition.
6008
6009static bool findGlobalMember(const Entry *root,
6010 const DString &namespaceName,
6011 const DString &type,
6012 const DString &name,
6013 const DString &tempArg,
6014 const DString &,
6015 const DString &decl,
6016 TypeSpecifier /* spec */)
6017{
6018 AUTO_TRACE("namespace='{}' type='{}' name='{}' tempArg='{}' decl='{}'",namespaceName,type,name,tempArg,decl);
6019 DString n=name;
6020 if (n.empty()) return false;
6021 if (n.find("::")!=DString::npos) return false; // skip undefined class members
6022 MemberName *mn=Doxygen::functionNameLinkedMap->find(n+tempArg); // look in function dictionary
6023 if (mn==nullptr)
6024 {
6025 mn=Doxygen::functionNameLinkedMap->find(n); // try without template arguments
6026 }
6027 if (mn) // function name defined
6028 {
6029 AUTO_TRACE_ADD("Found symbol name");
6030 //int count=0;
6031 bool found=false;
6032 for (const auto &md : *mn)
6033 {
6034 // If the entry has groups, then restrict the search to members which are
6035 // in one of the groups of the entry. If md is not associated with a group yet,
6036 // allow this documentation entry to add the group info.
6037 if (!root->groups.empty() && !isEntryInGroupOfMember(root, md.get(), true))
6038 {
6039 continue;
6040 }
6041
6042 const NamespaceDef *nd=nullptr;
6043 if (md->isAlias() && md->getOuterScope() &&
6044 md->getOuterScope()->definitionType()==Definition::TypeNamespace)
6045 {
6046 nd = toNamespaceDef(md->getOuterScope());
6047 }
6048 else
6049 {
6050 nd = md->getNamespaceDef();
6051 }
6052
6053 // special case for strong enums
6054 size_t enumNamePos=0;
6055 if (nd && md->isEnumValue() && (enumNamePos=namespaceName.rfind("::"))!=DString::npos)
6056 { // md part of a strong enum in a namespace?
6057 DString enumName = namespaceName.mid(enumNamePos+2);
6058 if (namespaceName.left(enumNamePos)==nd->name())
6059 {
6061 if (enumMn)
6062 {
6063 for (const auto &emd : *enumMn)
6064 {
6065 found = emd->isStrong() && md->getEnumScope()==emd.get();
6066 if (found)
6067 {
6068 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,nullptr,false,root->spec);
6069 break;
6070 }
6071 }
6072 }
6073 }
6074 if (found)
6075 {
6076 break;
6077 }
6078 }
6079 else if (nd==nullptr && md->isEnumValue()) // md part of global strong enum?
6080 {
6081 MemberName *enumMn=Doxygen::functionNameLinkedMap->find(namespaceName);
6082 if (enumMn)
6083 {
6084 for (const auto &emd : *enumMn)
6085 {
6086 found = emd->isStrong() && md->getEnumScope()==emd.get();
6087 if (found)
6088 {
6089 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,nullptr,false,root->spec);
6090 break;
6091 }
6092 }
6093 }
6094 }
6095
6096 const FileDef *fd=root->fileDef();
6097 //printf("File %s\n",fd ? qPrint(fd->name()) : "<none>");
6099 if (fd)
6100 {
6101 nl = fd->getUsedNamespaces();
6102 }
6103 //printf("NamespaceList %p\n",nl);
6104
6105 // search in the list of namespaces that are imported via a
6106 // using declaration
6107 bool viaUsingDirective = nd && nl.find(nd->qualifiedName())!=nullptr;
6108
6109 if ((namespaceName.empty() && nd==nullptr) || // not in a namespace
6110 (nd && nd->name()==namespaceName) || // or in the same namespace
6111 viaUsingDirective // member in 'using' namespace
6112 )
6113 {
6114 AUTO_TRACE_ADD("Try to add member '{}' to scope '{}'",md->name(),namespaceName);
6115
6116 NamespaceDef *rnd = nullptr;
6117 if (!namespaceName.empty()) rnd = Doxygen::namespaceLinkedMap->find(namespaceName);
6118
6119 const ArgumentList &mdAl = md.get()->argumentList();
6120 bool matching=
6121 (mdAl.empty() && root->argList.empty()) ||
6122 md->isVariable() || md->isTypedef() || /* in case of function pointers */
6123 matchArguments2(md->getOuterScope(),md->getFileDef(),md->typeString(),&mdAl,
6124 rnd ? rnd : Doxygen::globalScope,fd,root->type,&root->argList,
6125 false,root->lang);
6126
6127 // for template members we need to check if the number of
6128 // template arguments is the same, otherwise we are dealing with
6129 // different functions.
6130 if (matching && !root->tArgLists.empty())
6131 {
6132 const ArgumentList &mdTempl = md->templateArguments();
6133 if (root->tArgLists.back().size()!=mdTempl.size())
6134 {
6135 matching=false;
6136 }
6137 }
6138
6139 //printf("%s<->%s\n",
6140 // qPrint(argListToString(md->argumentList())),
6141 // qPrint(argListToString(root->argList)));
6142
6143 // For static members we also check if the comment block was found in
6144 // the same file. This is needed because static members with the same
6145 // name can be in different files. Thus it would be wrong to just
6146 // put the comment block at the first syntactically matching member. If
6147 // the comment block belongs to a group of the static member, then add
6148 // the documentation even if it is in a different file.
6149 if (matching && md->isStatic() &&
6150 md->getDefFileName()!=root->fileName &&
6151 mn->size()>1 &&
6152 !isEntryInGroupOfMember(root,md.get()))
6153 {
6154 matching = false;
6155 }
6156
6157 // for template member we also need to check the return type and requires
6158 if (!md->templateArguments().empty() && !root->tArgLists.empty())
6159 {
6160 //printf("Comparing return types '%s'<->'%s'\n",
6161 // md->typeString(),type);
6162 //printf("%s: Comparing '%s'<=>'%s'\n",qPrint(md->name()),qPrint(md->requiresClause()),qPrint(root->req));
6163 if (md->templateArguments().size()!=root->tArgLists.back().size() ||
6164 md->typeString()!=type ||
6165 md->requiresClause()!=root->req)
6166 {
6167 //printf(" ---> no matching\n");
6168 matching = false;
6169 }
6170 }
6171
6172 if (matching) // add docs to the member
6173 {
6174 AUTO_TRACE_ADD("Match found");
6175 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,&root->argList,false,root->spec);
6176 found=true;
6177 break;
6178 }
6179 }
6180 }
6181 if (!found && root->relatesType!=RelatesType::Duplicate && root->section.isFunction()) // no match
6182 {
6183 DString fullFuncDecl=decl;
6184 if (!root->argList.empty()) fullFuncDecl+=argListToString(root->argList,true);
6185 DString warnMsg = "no matching file member found for \n"+fullFuncDecl;
6186 if (mn->size()>0)
6187 {
6188 warnMsg+="\nPossible candidates:";
6189 for (const auto &md : *mn)
6190 {
6191 warnMsg+="\n '";
6192 warnMsg+=replaceAnonymousScopes(md->declaration());
6193 warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
6194 }
6195 }
6196 warn(root->fileName,root->startLine, "{}", qPrint(warnMsg));
6197 }
6198 }
6199 else // got docs for an undefined member!
6200 {
6201 if (root->type!="friend class" &&
6202 root->type!="friend struct" &&
6203 root->type!="friend union" &&
6204 root->type!="friend" &&
6205 (!Config_getBool(TYPEDEF_HIDES_STRUCT) ||
6206 root->type.find("typedef ")==DString::npos)
6207 )
6208 {
6209 warn(root->fileName,root->startLine,
6210 "documented symbol '{}' was not declared or defined.",qPrint(decl)
6211 );
6212 }
6213 }
6214 return true;
6215}
6216
6218 const ArgumentLists &srcTempArgLists,
6219 const ArgumentLists &dstTempArgLists
6220 )
6221{
6222 auto srcIt = srcTempArgLists.begin();
6223 auto dstIt = dstTempArgLists.begin();
6224 while (srcIt!=srcTempArgLists.end() && dstIt!=dstTempArgLists.end())
6225 {
6226 if ((*srcIt).size()!=(*dstIt).size()) return true;
6227 ++srcIt;
6228 ++dstIt;
6229 }
6230 return false;
6231}
6232
6233static bool scopeIsTemplate(const Definition *d)
6234{
6235 bool result=false;
6236 //printf("> scopeIsTemplate(%s)\n",qPrint(d?d->name():"null"));
6238 {
6239 auto cd = toClassDef(d);
6240 result = cd->templateArguments().hasParameters() || cd->templateMaster()!=nullptr ||
6242 }
6243 //printf("< scopeIsTemplate=%d\n",result);
6244 return result;
6245}
6246
6248 const ArgumentLists &srcTempArgLists,
6249 const ArgumentLists &dstTempArgLists,
6250 const std::string &src
6251 )
6252{
6253 std::string dst;
6254 static const reg::Ex re(R"(\a\w*)");
6255 reg::Iterator it(src,re);
6257 //printf("type=%s\n",qPrint(sa->type));
6258 size_t p=0;
6259 for (; it!=end ; ++it) // for each word in srcType
6260 {
6261 const auto &match = *it;
6262 size_t i = match.position();
6263 size_t l = match.length();
6264 bool found=false;
6265 dst+=src.substr(p,i-p);
6266 std::string name=match.str();
6267
6268 auto srcIt = srcTempArgLists.begin();
6269 auto dstIt = dstTempArgLists.begin();
6270 while (srcIt!=srcTempArgLists.end() && !found)
6271 {
6272 const ArgumentList *tdAli = nullptr;
6273 std::vector<Argument>::const_iterator tdaIt;
6274 if (dstIt!=dstTempArgLists.end())
6275 {
6276 tdAli = &(*dstIt);
6277 tdaIt = tdAli->begin();
6278 ++dstIt;
6279 }
6280
6281 const ArgumentList &tsaLi = *srcIt;
6282 for (auto tsaIt = tsaLi.begin(); tsaIt!=tsaLi.end() && !found; ++tsaIt)
6283 {
6284 Argument tsa = *tsaIt;
6285 const Argument *tda = nullptr;
6286 if (tdAli && tdaIt!=tdAli->end())
6287 {
6288 tda = &(*tdaIt);
6289 ++tdaIt;
6290 }
6291 //if (tda) printf("tsa=%s|%s tda=%s|%s\n",
6292 // qPrint(tsa.type),qPrint(tsa.name),
6293 // qPrint(tda->type),qPrint(tda->name));
6294 if (name==tsa.name.str())
6295 {
6296 if (tda && tda->name.empty())
6297 {
6298 DString tdaName = tda->name;
6299 DString tdaType = tda->type;
6300 int vc=0;
6301 if (tdaType.startsWith("class ")) vc=6;
6302 else if (tdaType.startsWith("typename ")) vc=9;
6303 if (vc>0) // convert type=="class T" to type=="class" name=="T"
6304 {
6305 tdaName = tdaType.mid(vc);
6306 }
6307 if (!tdaName.empty())
6308 {
6309 name=tdaName.str(); // substitute
6310 found=true;
6311 }
6312 }
6313 }
6314 }
6315
6316 //printf(" srcList='%s' dstList='%s faList='%s'\n",
6317 // qPrint(argListToString(srclali.current())),
6318 // qPrint(argListToString(dstlali.current())),
6319 // funcTempArgList ? qPrint(argListToString(funcTempArgList)) : "<none>");
6320 ++srcIt;
6321 }
6322 dst+=name;
6323 p=i+l;
6324 }
6325 dst+=src.substr(p);
6326 //printf(" substituteTemplatesInString(%s)=%s\n",
6327 // qPrint(src),qPrint(dst));
6328 return dst;
6329}
6330
6332 const ArgumentLists &srcTempArgLists,
6333 const ArgumentLists &dstTempArgLists,
6334 const ArgumentList &src,
6335 ArgumentList &dst
6336 )
6337{
6338 auto dstIt = dst.begin();
6339 for (const Argument &sa : src)
6340 {
6341 DString dstType = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.type.str());
6342 DString dstArray = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.array.str());
6343 if (dstIt == dst.end())
6344 {
6345 Argument da = sa;
6346 da.type = dstType;
6347 da.array = dstArray;
6348 dst.push_back(da);
6349 dstIt = dst.end();
6350 }
6351 else
6352 {
6353 Argument da = *dstIt;
6354 da.type = dstType;
6355 da.array = dstArray;
6356 ++dstIt;
6357 }
6358 }
6363 srcTempArgLists,dstTempArgLists,
6364 src.trailingReturnType().str()));
6365 dst.setIsDeleted(src.isDeleted());
6366 dst.setRefQualifier(src.refQualifier());
6367 dst.setNoParameters(src.noParameters());
6368 //printf("substituteTemplatesInArgList: replacing %s with %s\n",
6369 // qPrint(argListToString(src)),qPrint(argListToString(dst))
6370 // );
6371}
6372
6373//-------------------------------------------------------------------------------------------
6374
6375static void addLocalObjCMethod(const Entry *root,
6376 const DString &scopeName,
6377 const DString &funcType,const DString &funcName,const DString &funcArgs,
6378 const DString &exceptions,const DString &funcDecl,
6379 TypeSpecifier spec)
6380{
6381 AUTO_TRACE();
6382 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
6383 ClassDefMutable *cd=nullptr;
6384 if (Config_getBool(EXTRACT_LOCAL_METHODS) && (cd=getClassMutable(scopeName)))
6385 {
6386 AUTO_TRACE_ADD("Local objective C method '{}' scopeName='{}'",root->name,scopeName);
6387 auto md = createMemberDef(
6388 root->fileName,root->startLine,root->startColumn,
6389 funcType,funcName,funcArgs,exceptions,
6390 root->protection,root->virt,root->isStatic,Relationship::Member,
6391 MemberType::Function,ArgumentList(),root->argList,root->metaData);
6392 auto mmd = toMemberDefMutable(md.get());
6393 mmd->setTagInfo(root->tagInfo());
6394 mmd->setLanguage(root->lang);
6395 mmd->setId(root->id);
6396 mmd->makeImplementationDetail();
6397 mmd->setMemberClass(cd);
6398 mmd->setDefinition(funcDecl);
6400 mmd->addQualifiers(root->qualifiers);
6401 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6402 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6403 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6404 mmd->setDocsForDefinition(!root->proto);
6405 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6406 mmd->addSectionsToDefinition(root->anchors);
6407 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6408 FileDef *fd=root->fileDef();
6409 mmd->setBodyDef(fd);
6410 mmd->setMemberSpecifiers(spec);
6411 mmd->setVhdlSpecifiers(root->vhdlSpec);
6412 mmd->setMemberGroupId(root->mGrpId);
6413 cd->insertMember(md.get());
6414 cd->insertUsedFile(fd);
6415 mmd->setRefItems(root->sli);
6416 mmd->setRequirementReferences(root->rqli);
6417 addIncludeFile(mmd,fd,root);
6418
6420 mn->push_back(std::move(md));
6421 }
6422 else
6423 {
6424 // local objective C method found for class without interface
6425 }
6426}
6427
6428//-------------------------------------------------------------------------------------------
6429
6430static void addMemberFunction(const Entry *root,
6431 MemberName *mn,
6432 const DString &scopeName,
6433 const DString &namespaceName,
6434 const DString &className,
6435 const DString &funcTyp,
6436 const DString &funcName,
6437 const DString &funcArgs,
6438 const DString &funcTempList,
6439 const DString &exceptions,
6440 const DString &type,
6441 const DString &args,
6442 bool isFriend,
6443 TypeSpecifier spec,
6444 const DString &relates,
6445 const DString &funcDecl,
6446 bool overloaded,
6447 bool isFunc)
6448{
6449 AUTO_TRACE();
6450 DString funcType = funcTyp;
6451 int count=0;
6452 int noMatchCount=0;
6453 bool memFound=false;
6454 for (const auto &imd : *mn)
6455 {
6456 MemberDefMutable *md = toMemberDefMutable(imd.get());
6457 if (md==nullptr) continue;
6459 if (cd==nullptr) continue;
6460 //AUTO_TRACE_ADD("member definition found, scope needed='{}' scope='{}' args='{}' fileName='{}'",
6461 // scopeName, cd->name(), md->argsString(), root->fileName);
6462 FileDef *fd=root->fileDef();
6463 NamespaceDef *nd=nullptr;
6464 if (!namespaceName.empty()) nd=getResolvedNamespace(namespaceName);
6465
6466 //printf("scopeName %s->%s\n",qPrint(scopeName),
6467 // qPrint(stripTemplateSpecifiersFromScope(scopeName,false)));
6468
6469 // if the member we are searching for is an enum value that is part of
6470 // a "strong" enum, we need to look into the fields of the enum for a match
6471 size_t enumNamePos=0;
6472 if (md->isEnumValue() && (enumNamePos=className.rfind("::"))!=DString::npos)
6473 {
6474 DString enumName = className.mid(enumNamePos+2);
6475 DString fullScope = className.left(enumNamePos);
6476 if (!namespaceName.empty()) fullScope.prepend(namespaceName+"::");
6477 if (fullScope==cd->name())
6478 {
6479 MemberName *enumMn=Doxygen::memberNameLinkedMap->find(enumName);
6480 //printf("enumMn(%s)=%p\n",qPrint(className),(void*)enumMn);
6481 if (enumMn)
6482 {
6483 for (const auto &emd : *enumMn)
6484 {
6485 memFound = emd->isStrong() && md->getEnumScope()==emd.get();
6486 if (memFound)
6487 {
6488 addMemberDocs(root,md,funcDecl,nullptr,overloaded,spec);
6489 count++;
6490 }
6491 if (memFound) break;
6492 }
6493 }
6494 }
6495 }
6496 if (memFound) break;
6497
6498 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
6499 if (tcd==nullptr && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
6500 {
6501 // don't be fooled by anonymous scopes
6502 tcd=cd;
6503 }
6504 //printf("Looking for %s inside nd=%s result=%s cd=%s\n",
6505 // qPrint(scopeName),nd?qPrint(nd->name()):"<none>",tcd?qPrint(tcd->name()):"",qPrint(cd->name()));
6506
6507 if (cd && tcd==cd) // member's classes match
6508 {
6509 AUTO_TRACE_ADD("class definition '{}' found",cd->name());
6510
6511 // get the template parameter lists found at the member declaration
6512 ArgumentLists declTemplArgs = cd->getTemplateParameterLists();
6513 const ArgumentList &templAl = md->templateArguments();
6514 if (!templAl.empty())
6515 {
6516 declTemplArgs.push_back(templAl);
6517 }
6518
6519 // get the template parameter lists found at the member definition
6520 const ArgumentLists &defTemplArgs = root->tArgLists;
6521 //printf("defTemplArgs=%p\n",defTemplArgs);
6522
6523 // do we replace the decl argument lists with the def argument lists?
6524 bool substDone=false;
6525 ArgumentList argList;
6526
6527 /* substitute the occurrences of class template names in the
6528 * argument list before matching
6529 */
6530 const ArgumentList &mdAl = md->argumentList();
6531 if (declTemplArgs.size()>0 && declTemplArgs.size()==defTemplArgs.size())
6532 {
6533 /* the function definition has template arguments
6534 * and the class definition also has template arguments, so
6535 * we must substitute the template names of the class by that
6536 * of the function definition before matching.
6537 */
6538 substituteTemplatesInArgList(declTemplArgs,defTemplArgs,mdAl,argList);
6539
6540 substDone=true;
6541 }
6542 else /* no template arguments, compare argument lists directly */
6543 {
6544 argList = mdAl;
6545 }
6546
6547 bool matching=
6548 md->isVariable() || md->isTypedef() || // needed for function pointers
6550 md->getClassDef(),md->getFileDef(),md->typeString(),&argList,
6551 cd,fd,root->type,&root->argList,
6552 true,root->lang);
6553
6554 AUTO_TRACE_ADD("matching '{}'<=>'{}' className='{}' namespaceName='{}' result={}",
6555 argListToString(argList,true),argListToString(root->argList,true),className,namespaceName,matching);
6556
6557 if (md->getLanguage()==SrcLangExt::ObjC && md->isVariable() && root->section.isFunction())
6558 {
6559 matching = false; // don't match methods and attributes with the same name
6560 }
6561
6562 // for template member we also need to check the return type
6563 if (!md->templateArguments().empty() && !root->tArgLists.empty())
6564 {
6565 DString memType = md->typeString();
6566 memType.stripPrefix("static "); // see bug700696
6567 funcType=substitute(stripTemplateSpecifiersFromScope(funcType,true),
6568 className+"::",""); // see bug700693 & bug732594
6569 memType=substitute(stripTemplateSpecifiersFromScope(memType,true),
6570 className+"::",""); // see bug758900
6571 if (memType=="auto" && !argList.trailingReturnType().empty())
6572 {
6573 memType = argList.trailingReturnType();
6574 memType.stripPrefix(" -> ");
6575 }
6576 if (funcType=="auto" && !root->argList.trailingReturnType().empty())
6577 {
6578 funcType = root->argList.trailingReturnType();
6579 funcType.stripPrefix(" -> ");
6581 substDone=true;
6582 }
6583 AUTO_TRACE_ADD("Comparing return types '{}'<->'{}' #args {}<->{}",
6584 memType,funcType,md->templateArguments().size(),root->tArgLists.back().size());
6585 if (md->templateArguments().size()!=root->tArgLists.back().size() || memType!=funcType)
6586 {
6587 //printf(" ---> no matching\n");
6588 matching = false;
6589 }
6590 }
6591 else if (defTemplArgs.size()>declTemplArgs.size())
6592 {
6593 AUTO_TRACE_ADD("Different number of template arguments {} vs {}",defTemplArgs.size(),declTemplArgs.size());
6594 // avoid matching a non-template function in a template class against a
6595 // template function with the same name and parameters, see issue #10184
6596 substDone = false;
6597 matching = false;
6598 }
6599 bool rootIsUserDoc = root->section.isMemberDoc();
6600 bool classIsTemplate = scopeIsTemplate(md->getClassDef());
6601 bool mdIsTemplate = md->templateArguments().hasParameters();
6602 bool classOrMdIsTemplate = mdIsTemplate || classIsTemplate;
6603 bool rootIsTemplate = !root->tArgLists.empty();
6604 //printf("classIsTemplate=%d mdIsTemplate=%d rootIsTemplate=%d\n",classIsTemplate,mdIsTemplate,rootIsTemplate);
6605 if (!rootIsUserDoc && // don't check out-of-line @fn references, see bug722457
6606 (mdIsTemplate || rootIsTemplate) && // either md or root is a template
6607 ((classOrMdIsTemplate && !rootIsTemplate) || (!classOrMdIsTemplate && rootIsTemplate))
6608 )
6609 {
6610 // Method with template return type does not match method without return type
6611 // even if the parameters are the same. See also bug709052
6612 AUTO_TRACE_ADD("Comparing return types: template v.s. non-template");
6613 matching = false;
6614 }
6615
6616 AUTO_TRACE_ADD("Match results of matchArguments2='{}' substDone='{}'",matching,substDone);
6617
6618 if (substDone) // found a new argument list
6619 {
6620 if (matching) // replace member's argument list
6621 {
6623 md->moveArgumentList(std::make_unique<ArgumentList>(argList));
6624 }
6625 else // no match
6626 {
6627 if (!funcTempList.empty() &&
6628 isSpecialization(declTemplArgs,defTemplArgs))
6629 {
6630 // check if we are dealing with a partial template
6631 // specialization. In this case we add it to the class
6632 // even though the member arguments do not match.
6633
6634 addMethodToClass(root,cd,type,md->name(),args,isFriend,
6635 md->protection(),md->isStatic(),md->virtualness(),spec,relates);
6636 return;
6637 }
6638 }
6639 }
6640 if (matching)
6641 {
6642 addMemberDocs(root,md,funcDecl,nullptr,overloaded,spec);
6643 count++;
6644 memFound=true;
6645 }
6646 }
6647 else if (cd && cd!=tcd) // we did find a class with the same name as cd
6648 // but in a different namespace
6649 {
6650 noMatchCount++;
6651 }
6652
6653 if (memFound) break;
6654 }
6655 if (count==0 && root->parent() && root->parent()->section.isObjcImpl())
6656 {
6657 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
6658 return;
6659 }
6660 if (count==0 && !(isFriend && funcType=="class"))
6661 {
6662 int candidates=0;
6663 const ClassDef *ecd = nullptr, *ucd = nullptr;
6664 MemberDef *emd = nullptr, *umd = nullptr;
6665 //printf("Assume template class\n");
6666 for (const auto &md : *mn)
6667 {
6668 MemberDef *cmd=md.get();
6670 ClassDefMutable *ccd=cdmdm ? cdmdm->getClassDefMutable() : nullptr;
6671 //printf("ccd->name()==%s className=%s\n",qPrint(ccd->name()),qPrint(className));
6672 if (ccd!=nullptr && rightScopeMatch(ccd->name(),className))
6673 {
6674 const ArgumentList &templAl = md->templateArguments();
6675 if (!root->tArgLists.empty() && !templAl.empty() &&
6676 root->tArgLists.back().size()<=templAl.size())
6677 {
6678 AUTO_TRACE_ADD("add template specialization");
6679 addMethodToClass(root,ccd,type,md->name(),args,isFriend,
6680 root->protection,root->isStatic,root->virt,spec,relates);
6681 return;
6682 }
6683 if (argListToString(md->argumentList(),false,false) ==
6684 argListToString(root->argList,false,false))
6685 { // exact argument list match -> remember
6686 ucd = ecd = ccd;
6687 umd = emd = cmd;
6688 AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': exact match",
6689 className,ccd->name(),md->argsString());
6690 }
6691 else // arguments do not match, but member name and scope do -> remember
6692 {
6693 ucd = ccd;
6694 umd = cmd;
6695 AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': no match",
6696 className,ccd->name(),md->argsString());
6697 }
6698 candidates++;
6699 }
6700 }
6701 bool strictProtoMatching = Config_getBool(STRICT_PROTO_MATCHING);
6702 if (!strictProtoMatching)
6703 {
6704 if (candidates==1 && ucd && umd)
6705 {
6706 // we didn't find an actual match on argument lists, but there is only 1 member with this
6707 // name in the same scope, so that has to be the one.
6708 addMemberDocs(root,toMemberDefMutable(umd),funcDecl,nullptr,overloaded,spec);
6709 return;
6710 }
6711 else if (candidates>1 && ecd && emd)
6712 {
6713 // we didn't find a unique match using type resolution,
6714 // but one of the matches has the exact same signature so
6715 // we take that one.
6716 addMemberDocs(root,toMemberDefMutable(emd),funcDecl,nullptr,overloaded,spec);
6717 return;
6718 }
6719 }
6720
6721 DString warnMsg = "no ";
6722 if (noMatchCount>1) warnMsg+="uniquely ";
6723 warnMsg+="matching class member found for \n";
6724
6725 for (const ArgumentList &al : root->tArgLists)
6726 {
6727 warnMsg+=" template ";
6728 warnMsg+=tempArgListToString(al,root->lang);
6729 warnMsg+='\n';
6730 }
6731
6732 DString fullFuncDecl=funcDecl;
6733 if (isFunc) fullFuncDecl+=argListToString(root->argList,true);
6734
6735 warnMsg+=" ";
6736 warnMsg+=fullFuncDecl;
6737
6738 if (candidates>0 || noMatchCount>=1)
6739 {
6740 warnMsg+="\nPossible candidates:";
6741
6742 NamespaceDef *nd=nullptr;
6743 if (!namespaceName.empty()) nd=getResolvedNamespace(namespaceName);
6744 FileDef *fd=root->fileDef();
6745
6746 for (const auto &md : *mn)
6747 {
6748 const ClassDef *cd=md->getClassDef();
6749 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
6750 if (tcd==nullptr && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
6751 {
6752 // don't be fooled by anonymous scopes
6753 tcd=cd;
6754 }
6755 if (cd!=nullptr && (rightScopeMatch(cd->name(),className) || (cd!=tcd)))
6756 {
6757 warnMsg+='\n';
6758 const ArgumentList &templAl = md->templateArguments();
6759 warnMsg+=" '";
6760 if (templAl.hasParameters())
6761 {
6762 warnMsg+="template ";
6763 warnMsg+=tempArgListToString(templAl,root->lang);
6764 warnMsg+='\n';
6765 warnMsg+=" ";
6766 }
6767 if (!md->typeString().empty())
6768 {
6769 warnMsg+=md->typeString();
6770 warnMsg+=' ';
6771 }
6773 if (!qScope.empty())
6774 warnMsg+=qScope+"::"+md->name();
6775 warnMsg+=md->argsString();
6776 warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
6777 }
6778 }
6779 }
6780 warn(root->fileName,root->startLine,"{}",warnMsg);
6781 }
6782}
6783
6784//-------------------------------------------------------------------------------------------
6785
6786static void addMemberSpecialization(const Entry *root,
6787 MemberName *mn,
6788 ClassDefMutable *cd,
6789 const DString &funcType,
6790 const DString &funcName,
6791 const DString &funcArgs,
6792 const DString &funcDecl,
6793 const DString &exceptions,
6794 TypeSpecifier spec
6795 )
6796{
6797 AUTO_TRACE("funcType={} funcName={} funcArgs={} funcDecl={} spec={}",funcType,funcName,funcArgs,funcDecl,spec);
6798 MemberDef *declMd=nullptr;
6799 for (const auto &md : *mn)
6800 {
6801 if (md->getClassDef()==cd)
6802 {
6803 // TODO: we should probably also check for matching arguments
6804 declMd = md.get();
6805 break;
6806 }
6807 }
6808 MemberType mtype=MemberType::Function;
6809 ArgumentList tArgList;
6810 // getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6811 auto md = createMemberDef(
6812 root->fileName,root->startLine,root->startColumn,
6813 funcType,funcName,funcArgs,exceptions,
6814 declMd ? declMd->protection() : root->protection,
6815 root->virt,root->isStatic,Relationship::Member,
6816 mtype,tArgList,root->argList,root->metaData);
6817 auto mmd = toMemberDefMutable(md.get());
6818 //printf("new specialized member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6819 mmd->setTagInfo(root->tagInfo());
6820 mmd->setLanguage(root->lang);
6821 mmd->setId(root->id);
6822 mmd->setMemberClass(cd);
6823 mmd->setTemplateSpecialization(true);
6824 mmd->setTypeConstraints(root->typeConstr);
6825 mmd->setDefinition(funcDecl);
6827 mmd->addQualifiers(root->qualifiers);
6828 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6829 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6830 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6831 mmd->setDocsForDefinition(!root->proto);
6832 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6833 mmd->addSectionsToDefinition(root->anchors);
6834 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6835 FileDef *fd=root->fileDef();
6836 mmd->setBodyDef(fd);
6837 mmd->setMemberSpecifiers(spec);
6838 mmd->setVhdlSpecifiers(root->vhdlSpec);
6839 mmd->setMemberGroupId(root->mGrpId);
6840 cd->insertMember(md.get());
6841 mmd->setRefItems(root->sli);
6842 mmd->setRequirementReferences(root->rqli);
6843 addIncludeFile(mmd,fd,root);
6844
6845 mn->push_back(std::move(md));
6846}
6847
6848//-------------------------------------------------------------------------------------------
6849
6850static void addOverloaded(const Entry *root,MemberName *mn,
6851 const DString &funcType,const DString &funcName,const DString &funcArgs,
6852 const DString &funcDecl,const DString &exceptions,TypeSpecifier spec)
6853{
6854 // for unique overloaded member we allow the class to be
6855 // omitted, this is to be Qt compatible. Using this should
6856 // however be avoided, because it is error prone
6857 bool sameClass=false;
6858 if (mn->size()>0)
6859 {
6860 // check if all members with the same name are also in the same class
6861 sameClass = std::equal(mn->begin()+1,mn->end(),mn->begin(),
6862 [](const auto &md1,const auto &md2)
6863 { return md1->getClassDef()->name()==md2->getClassDef()->name(); });
6864 }
6865 if (sameClass)
6866 {
6867 MemberDefMutable *mdm = toMemberDefMutable(mn->front().get());
6868 ClassDefMutable *cd = mdm ? mdm->getClassDefMutable() : nullptr;
6869 if (cd==nullptr) return;
6870
6871 MemberType mtype = MemberType::Function;
6872 if (root->mtype==MethodTypes::Signal) mtype=MemberType::Signal;
6873 else if (root->mtype==MethodTypes::Slot) mtype=MemberType::Slot;
6874 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType::DCOP;
6875
6876 // new overloaded member function
6877 std::unique_ptr<ArgumentList> tArgList =
6878 getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6879 //printf("new related member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6880 auto md = createMemberDef(
6881 root->fileName,root->startLine,root->startColumn,
6882 funcType,funcName,funcArgs,exceptions,
6883 root->protection,root->virt,root->isStatic,Relationship::Related,
6884 mtype,tArgList ? *tArgList : ArgumentList(),root->argList,root->metaData);
6885 auto mmd = toMemberDefMutable(md.get());
6886 mmd->setTagInfo(root->tagInfo());
6887 mmd->setLanguage(root->lang);
6888 mmd->setId(root->id);
6889 mmd->setTypeConstraints(root->typeConstr);
6890 mmd->setMemberClass(cd);
6891 mmd->setDefinition(funcDecl);
6893 mmd->addQualifiers(root->qualifiers);
6895 doc+="<p>";
6896 doc+=root->doc;
6897 mmd->setDocumentation(doc,root->docFile,root->docLine);
6898 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6899 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6900 mmd->setDocsForDefinition(!root->proto);
6901 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6902 mmd->addSectionsToDefinition(root->anchors);
6903 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6904 FileDef *fd=root->fileDef();
6905 mmd->setBodyDef(fd);
6906 mmd->setMemberSpecifiers(spec);
6907 mmd->setVhdlSpecifiers(root->vhdlSpec);
6908 mmd->setMemberGroupId(root->mGrpId);
6909 cd->insertMember(md.get());
6910 cd->insertUsedFile(fd);
6911 mmd->setRefItems(root->sli);
6912 mmd->setRequirementReferences(root->rqli);
6913 addIncludeFile(mmd,fd,root);
6914
6915 mn->push_back(std::move(md));
6916 }
6917}
6918
6919static void insertMemberAlias(Definition *outerScope,const MemberDef *md)
6920{
6921 if (outerScope && outerScope!=Doxygen::globalScope)
6922 {
6923 auto aliasMd = createMemberDefAlias(outerScope,md);
6924 if (outerScope->definitionType()==Definition::TypeClass)
6925 {
6926 ClassDefMutable *cdm = toClassDefMutable(outerScope);
6927 if (cdm)
6928 {
6929 cdm->insertMember(aliasMd.get());
6930 }
6931 }
6932 else if (outerScope->definitionType()==Definition::TypeNamespace)
6933 {
6934 NamespaceDefMutable *ndm = toNamespaceDefMutable(outerScope);
6935 if (ndm)
6936 {
6937 ndm->insertMember(aliasMd.get());
6938 }
6939 }
6940 else if (outerScope->definitionType()==Definition::TypeFile)
6941 {
6942 toFileDef(outerScope)->insertMember(aliasMd.get());
6943 }
6944 if (aliasMd)
6945 {
6946 Doxygen::functionNameLinkedMap->add(md->name())->push_back(std::move(aliasMd));
6947 }
6948 }
6949}
6950
6951//-------------------------------------------------------------------------------------------
6952
6953/*! This function tries to find a member (in a documented class/file/namespace)
6954 * that corresponds to the function/variable declaration given in \a funcDecl.
6955 *
6956 * The boolean \a overloaded is used to specify whether or not a standard
6957 * overload documentation line should be generated.
6958 *
6959 * The boolean \a isFunc is a hint that indicates that this is a function
6960 * instead of a variable or typedef.
6961 */
6962static void findMember(const Entry *root,
6963 const DString &relates,
6964 const DString &type,
6965 const DString &args,
6966 DString funcDecl,
6967 bool overloaded,
6968 bool isFunc
6969 )
6970{
6971 AUTO_TRACE("root='{}' funcDecl='{}' related='{}' overload={} isFunc={} mGrpId={} #tArgList={} spec={} lang={}",
6972 root->name, funcDecl, relates, overloaded, isFunc, root->mGrpId, root->tArgLists.size(),
6973 root->spec, root->lang);
6974
6975 DString scopeName;
6976 DString className;
6977 DString namespaceName;
6978 DString funcType;
6979 DString funcName;
6980 DString funcArgs;
6981 DString funcTempList;
6982 DString exceptions;
6983 DString funcSpec;
6984 bool isRelated=false;
6985 bool isMemberOf=false;
6986 bool isFriend=false;
6987 bool done=false;
6988 TypeSpecifier spec = root->spec;
6989 while (!done)
6990 {
6991 done=true;
6992 if (funcDecl.stripPrefix("friend ")) // treat friends as related members
6993 {
6994 isFriend=true;
6995 done=false;
6996 }
6997 if (funcDecl.stripPrefix("inline "))
6998 {
6999 spec.setInline(true);
7000 done=false;
7001 }
7002 if (funcDecl.stripPrefix("explicit "))
7003 {
7004 spec.setExplicit(true);
7005 done=false;
7006 }
7007 if (funcDecl.stripPrefix("mutable "))
7008 {
7009 spec.setMutable(true);
7010 done=false;
7011 }
7012 if (funcDecl.stripPrefix("thread_local "))
7013 {
7014 spec.setThreadLocal(true);
7015 done=false;
7016 }
7017 if (funcDecl.stripPrefix("virtual "))
7018 {
7019 done=false;
7020 }
7021 }
7022
7023 // delete any ; from the function declaration
7024 size_t sep=0;
7025 while ((sep=funcDecl.find(';'))!=DString::npos)
7026 {
7027 funcDecl=(funcDecl.left(sep)+funcDecl.mid(sep+1)).stripWhiteSpace();
7028 }
7029
7030 // make sure the first character is a space to simplify searching.
7031 if (!funcDecl.empty() && funcDecl[0]!=' ') funcDecl.prepend(" ");
7032
7033 // remove some superfluous spaces
7034 funcDecl= substitute(
7035 substitute(
7036 substitute(funcDecl,"~ ","~"),
7037 ":: ","::"
7038 ),
7039 " ::","::"
7040 ).stripWhiteSpace();
7041
7042 //printf("funcDecl='%s'\n",qPrint(funcDecl));
7043 if (isFriend && funcDecl.startsWith("class "))
7044 {
7045 //printf("friend class\n");
7046 funcDecl=funcDecl.mid(6);
7047 funcName = funcDecl;
7048 }
7049 else if (isFriend && funcDecl.startsWith("struct "))
7050 {
7051 funcDecl=funcDecl.mid(7);
7052 funcName = funcDecl;
7053 }
7054 else
7055 {
7056 // extract information from the declarations
7057 parseFuncDecl(funcDecl,root->lang,scopeName,funcType,funcName,
7058 funcArgs,funcTempList,exceptions
7059 );
7060 }
7061
7062 // the class name can also be a namespace name, we decide this later.
7063 // if a related class name is specified and the class name could
7064 // not be derived from the function declaration, then use the
7065 // related field.
7066 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}' funcType='{}' funcName='{}' funcArgs='{}'",
7067 scopeName,className,namespaceName,funcType,funcName,funcArgs);
7068 if (!relates.empty())
7069 { // related member, prefix user specified scope
7070 isRelated=true;
7071 isMemberOf=(root->relatesType == RelatesType::MemberOf);
7072 if (getClass(relates)==nullptr && !scopeName.empty())
7073 {
7074 scopeName= mergeScopes(scopeName,relates);
7075 }
7076 else
7077 {
7078 scopeName = relates;
7079 }
7080 }
7081
7082 if (relates.empty() && root->parent() &&
7083 (root->parent()->section.isScope() || root->parent()->section.isObjcImpl()) &&
7084 !root->parent()->name.empty()) // see if we can combine scopeName
7085 // with the scope in which it was found
7086 {
7087 DString joinedName = root->parent()->name+"::"+scopeName;
7088 if (!scopeName.empty() &&
7089 (getClass(joinedName) || Doxygen::namespaceLinkedMap->find(joinedName)))
7090 {
7091 scopeName = joinedName;
7092 }
7093 else
7094 {
7095 scopeName = mergeScopes(root->parent()->name,scopeName);
7096 }
7097 }
7098 else // see if we can prefix a namespace or class that is used from the file
7099 {
7100 FileDef *fd=root->fileDef();
7101 if (fd)
7102 {
7103 for (const auto &fnd : fd->getUsedNamespaces())
7104 {
7105 DString joinedName = fnd->name()+"::"+scopeName;
7106 if (Doxygen::namespaceLinkedMap->find(joinedName))
7107 {
7108 scopeName=joinedName;
7109 break;
7110 }
7111 }
7112 }
7113 }
7115 removeRedundantWhiteSpace(scopeName),false,&funcSpec,DString(),false);
7116
7117 // funcSpec contains the last template specifiers of the given scope.
7118 // If this method does not have any template arguments or they are
7119 // empty while funcSpec is not empty we assume this is a
7120 // specialization of a method. If not, we clear the funcSpec and treat
7121 // this as a normal method of a template class.
7122 if (!(root->tArgLists.size()>0 &&
7123 root->tArgLists.front().size()==0
7124 )
7125 )
7126 {
7127 funcSpec.clear();
7128 }
7129
7130 //namespaceName=removeAnonymousScopes(namespaceName);
7131 if (!Config_getBool(EXTRACT_ANON_NSPACES) && scopeName.find('@')!=DString::npos) return; // skip stuff in anonymous namespace...
7132
7133 // split scope into a namespace and a class part
7134 extractNamespaceName(scopeName,className,namespaceName,true);
7135 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}'",scopeName,className,namespaceName);
7136
7137 //printf("namespaceName='%s' className='%s'\n",qPrint(namespaceName),qPrint(className));
7138 // merge class and namespace scopes again
7139 scopeName.clear();
7140 if (!namespaceName.empty())
7141 {
7142 if (className.empty())
7143 {
7144 scopeName=namespaceName;
7145 }
7146 else if (!relates.empty() || // relates command with explicit scope
7147 !getClass(className)) // class name only exists in a namespace
7148 {
7149 scopeName=namespaceName+"::"+className;
7150 }
7151 else
7152 {
7153 scopeName=className;
7154 }
7155 }
7156 else if (!className.empty())
7157 {
7158 scopeName=className;
7159 }
7160 //printf("new scope='%s'\n",qPrint(scopeName));
7161
7162 DString tempScopeName=scopeName;
7163 ClassDefMutable *cd=getClassMutable(scopeName);
7164 if (cd)
7165 {
7166 if (funcSpec.empty())
7167 {
7168 uint32_t argListIndex=0;
7169 tempScopeName=cd->qualifiedNameWithTemplateParameters(&root->tArgLists,&argListIndex);
7170 }
7171 else
7172 {
7173 tempScopeName=scopeName+funcSpec;
7174 }
7175 }
7176 //printf("scopeName=%s cd=%p root->tArgLists=%p result=%s\n",
7177 // qPrint(scopeName),cd,root->tArgLists,qPrint(tempScopeName));
7178
7179 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
7180 // rebuild the function declaration (needed to get the scope right).
7181 if (!scopeName.empty() && !isRelated && !isFriend && !Config_getBool(HIDE_SCOPE_NAMES) && root->lang!=SrcLangExt::Python)
7182 {
7183 if (!funcType.empty())
7184 {
7185 if (isFunc) // a function -> we use argList for the arguments
7186 {
7187 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcTempList;
7188 }
7189 else
7190 {
7191 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcArgs;
7192 }
7193 }
7194 else
7195 {
7196 if (isFunc) // a function => we use argList for the arguments
7197 {
7198 funcDecl=tempScopeName+"::"+funcName+funcTempList;
7199 }
7200 else // variable => add 'argument' list
7201 {
7202 funcDecl=tempScopeName+"::"+funcName+funcArgs;
7203 }
7204 }
7205 }
7206 else // build declaration without scope
7207 {
7208 if (!funcType.empty()) // but with a type
7209 {
7210 if (isFunc) // function => omit argument list
7211 {
7212 funcDecl=funcType+" "+funcName+funcTempList;
7213 }
7214 else // variable => add 'argument' list
7215 {
7216 funcDecl=funcType+" "+funcName+funcArgs;
7217 }
7218 }
7219 else // no type
7220 {
7221 if (isFunc)
7222 {
7223 funcDecl=funcName+funcTempList;
7224 }
7225 else
7226 {
7227 funcDecl=funcName+funcArgs;
7228 }
7229 }
7230 }
7231
7232 if (funcType=="template class" && !funcTempList.empty())
7233 return; // ignore explicit template instantiations
7234
7235 AUTO_TRACE_ADD("Parse results: namespaceName='{}' className=`{}` funcType='{}' funcSpec='{}' "
7236 " funcName='{}' funcArgs='{}' funcTempList='{}' funcDecl='{}' relates='{}'"
7237 " exceptions='{}' isRelated={} isMemberOf={} isFriend={} isFunc={}",
7238 namespaceName, className, funcType, funcSpec,
7239 funcName, funcArgs, funcTempList, funcDecl, relates,
7240 exceptions, isRelated, isMemberOf, isFriend, isFunc);
7241
7242 if (!funcName.empty()) // function name is valid
7243 {
7244 // check if 'className' is actually a scoped enum, in which case we need to
7245 // process it as a global, see issue #6471
7246 bool strongEnum = false;
7247 MemberName *mn=nullptr;
7248 if (!className.empty() && (mn=Doxygen::functionNameLinkedMap->find(className)))
7249 {
7250 for (const auto &imd : *mn)
7251 {
7252 MemberDefMutable *md = toMemberDefMutable(imd.get());
7253 Definition *mdScope = nullptr;
7254 if (md && md->isEnumerate() && md->isStrong() && (mdScope=md->getOuterScope()) &&
7255 // need filter for the correct scope, see issue #9668
7256 ((namespaceName.empty() && mdScope==Doxygen::globalScope) || (mdScope->name()==namespaceName)))
7257 {
7258 AUTO_TRACE_ADD("'{}' is a strong enum! (namespace={} md->getOuterScope()->name()={})",md->name(),namespaceName,md->getOuterScope()->name());
7259 strongEnum = true;
7260 // pass the scope name name as a 'namespace' to the findGlobalMember function
7261 if (!namespaceName.empty())
7262 {
7263 namespaceName+="::"+className;
7264 }
7265 else
7266 {
7267 namespaceName=className;
7268 }
7269 }
7270 }
7271 }
7272
7273 if (funcName.startsWith("operator ")) // strip class scope from cast operator
7274 {
7275 funcName = substitute(funcName,className+"::","");
7276 }
7277 mn = nullptr;
7278 if (!funcTempList.empty()) // try with member specialization
7279 {
7280 mn=Doxygen::memberNameLinkedMap->find(funcName+funcTempList);
7281 }
7282 if (mn==nullptr) // try without specialization
7283 {
7284 mn=Doxygen::memberNameLinkedMap->find(funcName);
7285 }
7286 if (!isRelated && !strongEnum && mn) // function name already found
7287 {
7288 AUTO_TRACE_ADD("member name exists ({} members with this name)",mn->size());
7289 if (!className.empty()) // class name is valid
7290 {
7291 if (funcSpec.empty()) // not a member specialization
7292 {
7293 addMemberFunction(root,mn,scopeName,namespaceName,className,funcType,funcName,
7294 funcArgs,funcTempList,exceptions,
7295 type,args,isFriend,spec,relates,funcDecl,overloaded,isFunc);
7296 }
7297 else if (cd) // member specialization
7298 {
7299 addMemberSpecialization(root,mn,cd,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
7300 }
7301 else
7302 {
7303 //printf("*** Specialized member %s of unknown scope %s%s found!\n",
7304 // qPrint(scopeName),qPrint(funcName),qPrint(funcArgs));
7305 }
7306 }
7307 else if (overloaded) // check if the function belongs to only one class
7308 {
7309 addOverloaded(root,mn,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
7310 }
7311 else // unrelated function with the same name as a member
7312 {
7313 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
7314 {
7315 DString fullFuncDecl=funcDecl;
7316 if (isFunc) fullFuncDecl+=argListToString(root->argList,true);
7317 warn(root->fileName,root->startLine,
7318 "Cannot determine class for function\n{}",
7319 fullFuncDecl
7320 );
7321 }
7322 }
7323 }
7324 else if (isRelated && !relates.empty())
7325 {
7326 AUTO_TRACE_ADD("related function scopeName='{}' className='{}'",scopeName,className);
7327 if (className.empty()) className=relates;
7328 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
7329 if ((cd=getClassMutable(scopeName)))
7330 {
7331 bool newMember=true; // assume we have a new member
7332 MemberDefMutable *mdDefine=nullptr;
7333 {
7334 mn = Doxygen::functionNameLinkedMap->find(funcName);
7335 if (mn)
7336 {
7337 for (const auto &imd : *mn)
7338 {
7339 MemberDefMutable *md = toMemberDefMutable(imd.get());
7340 if (md && md->isDefine())
7341 {
7342 mdDefine = md;
7343 break;
7344 }
7345 }
7346 }
7347 }
7348
7349 if (mdDefine) // macro definition is already created by the preprocessor and inserted as a file member
7350 {
7351 //printf("moving #define %s into class %s\n",qPrint(mdDefine->name()),qPrint(cd->name()));
7352
7353 // take mdDefine from the Doxygen::functionNameLinkedMap (without deleting the data)
7354 auto mdDefineTaken = Doxygen::functionNameLinkedMap->take(funcName,mdDefine);
7355 // insert it as a class member
7356 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==nullptr)
7357 {
7358 mn=Doxygen::memberNameLinkedMap->add(funcName);
7359 }
7360
7361 if (mdDefine->getFileDef())
7362 {
7363 mdDefine->getFileDef()->removeMember(mdDefine);
7364 }
7365 mdDefine->makeRelated();
7366 mdDefine->setMemberClass(cd);
7367 mdDefine->moveTo(cd);
7368 cd->insertMember(mdDefine);
7369 // also insert the member as an alias in the parent's scope, so it can be referenced also without cd's scope
7370 insertMemberAlias(cd->getOuterScope(),mdDefine);
7371 mn->push_back(std::move(mdDefineTaken));
7372 }
7373 else // normal member, needs to be created and added to the class
7374 {
7375 FileDef *fd=root->fileDef();
7376
7377 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==nullptr)
7378 {
7379 mn=Doxygen::memberNameLinkedMap->add(funcName);
7380 }
7381 else
7382 {
7383 // see if we got another member with matching arguments
7384 MemberDefMutable *rmd_found = nullptr;
7385 for (const auto &irmd : *mn)
7386 {
7387 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
7388 if (rmd)
7389 {
7390 const ArgumentList &rmdAl = rmd->argumentList();
7391
7392 newMember=
7393 className!=rmd->getOuterScope()->name() ||
7394 !matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmdAl,
7395 cd,fd,root->type,&root->argList,
7396 true,root->lang);
7397 if (!newMember)
7398 {
7399 rmd_found = rmd;
7400 }
7401 }
7402 }
7403 if (rmd_found) // member already exists as rmd -> add docs
7404 {
7405 AUTO_TRACE_ADD("addMemberDocs for related member {}",root->name);
7406 addMemberDocs(root,rmd_found,funcDecl,nullptr,overloaded,spec);
7407 newMember=false;
7408 }
7409 }
7410
7411 if (newMember) // need to create a new member
7412 {
7413 MemberType mtype = MemberType::Function;
7414 switch (root->mtype)
7415 {
7416 case MethodTypes::Method: mtype = MemberType::Function; break;
7417 case MethodTypes::Signal: mtype = MemberType::Signal; break;
7418 case MethodTypes::Slot: mtype = MemberType::Slot; break;
7419 case MethodTypes::DCOP: mtype = MemberType::DCOP; break;
7420 case MethodTypes::Property: mtype = MemberType::Property; break;
7421 case MethodTypes::Event: mtype = MemberType::Event; break;
7422 }
7423
7424 //printf("New related name '%s' '%d'\n",qPrint(funcName),
7425 // root->argList ? (int)root->argList->count() : -1);
7426
7427 // first note that we pass:
7428 // (root->tArgLists ? root->tArgLists->last() : nullptr)
7429 // for the template arguments for the new "member."
7430 // this accurately reflects the template arguments of
7431 // the related function, which don't have to do with
7432 // those of the related class.
7433 auto md = createMemberDef(
7434 root->fileName,root->startLine,root->startColumn,
7435 funcType,funcName,funcArgs,exceptions,
7436 root->protection,root->virt,
7437 root->isStatic,
7438 isMemberOf ? Relationship::Foreign : Relationship::Related,
7439 mtype,
7440 (!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList()),
7441 funcArgs.empty() ? ArgumentList() : root->argList,
7442 root->metaData);
7443 auto mmd = toMemberDefMutable(md.get());
7444
7445 // also insert the member as an alias in the parent's scope, so it can be referenced also without cd's scope
7446 insertMemberAlias(cd->getOuterScope(),md.get());
7447
7448 // we still have the problem that
7449 // MemberDef::writeDocumentation() in memberdef.cpp
7450 // writes the template argument list for the class,
7451 // as if this member is a member of the class.
7452 // fortunately, MemberDef::writeDocumentation() has
7453 // a special mechanism that allows us to totally
7454 // override the set of template argument lists that
7455 // are printed. We use that and set it to the
7456 // template argument lists of the related function.
7457 //
7458 mmd->setDefinitionTemplateParameterLists(root->tArgLists);
7459
7460 mmd->setTagInfo(root->tagInfo());
7461
7462 addIncludeFile(mmd,root->fileDef(),root);
7463 //printf("Related member name='%s' decl='%s' bodyLine='%d'\n",
7464 // qPrint(funcName),qPrint(funcDecl),root->bodyLine);
7465
7466 // try to find the matching line number of the body from the
7467 // global function list
7468 bool found=false;
7469 if (root->bodyLine==-1)
7470 {
7472 if (rmn)
7473 {
7474 const MemberDefMutable *rmd_found=nullptr;
7475 for (const auto &irmd : *rmn)
7476 {
7477 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
7478 if (rmd)
7479 {
7480 const ArgumentList &rmdAl = rmd->argumentList();
7481 // check for matching argument lists
7482 if (
7483 matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmdAl,
7484 cd,fd,root->type,&root->argList,
7485 true,root->lang)
7486 )
7487 {
7488 found=true;
7489 rmd_found = rmd;
7490 break;
7491 }
7492 }
7493 }
7494 if (rmd_found) // member found -> copy line number info
7495 {
7496 mmd->setBodySegment(rmd_found->getDefLine(),rmd_found->getStartBodyLine(),rmd_found->getEndBodyLine());
7497 mmd->setBodyDef(rmd_found->getBodyDef());
7498 //md->setBodyMember(rmd);
7499 }
7500 }
7501 }
7502 if (!found) // line number could not be found or is available in this
7503 // entry
7504 {
7505 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7506 mmd->setBodyDef(fd);
7507 }
7508
7509 //if (root->mGrpId!=-1)
7510 //{
7511 // md->setMemberGroup(memberGroupDict[root->mGrpId]);
7512 //}
7513 mmd->setMemberClass(cd);
7514 mmd->setMemberSpecifiers(spec);
7515 mmd->setVhdlSpecifiers(root->vhdlSpec);
7516 mmd->setDefinition(funcDecl);
7518 mmd->addQualifiers(root->qualifiers);
7519 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7520 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7521 mmd->setDocsForDefinition(!root->proto);
7522 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
7523 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7524 mmd->addSectionsToDefinition(root->anchors);
7525 mmd->setMemberGroupId(root->mGrpId);
7526 mmd->setLanguage(root->lang);
7527 mmd->setId(root->id);
7528 //md->setMemberDefTemplateArguments(root->mtArgList);
7529 cd->insertMember(md.get());
7530 cd->insertUsedFile(fd);
7531 mmd->setRefItems(root->sli);
7532 mmd->setRequirementReferences(root->rqli);
7533 if (root->relatesType==RelatesType::Duplicate) mmd->setRelatedAlso(cd);
7534 addMemberToGroups(root,md.get());
7536 //printf("Adding member=%s\n",qPrint(md->name()));
7537 mn->push_back(std::move(md));
7538 }
7539 if (root->relatesType==RelatesType::Duplicate)
7540 {
7541 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
7542 {
7543 DString fullFuncDecl=funcDecl;
7544 if (isFunc) fullFuncDecl+=argListToString(root->argList,true);
7545 warn(root->fileName,root->startLine,
7546 "Cannot determine file/namespace for relatedalso function\n{}",
7547 fullFuncDecl
7548 );
7549 }
7550 }
7551 }
7552 }
7553 else
7554 {
7555 warn_undoc(root->fileName,root->startLine, "class '{}' for related function '{}' is not documented.", className,funcName);
7556 }
7557 }
7558 else if (root->parent() && root->parent()->section.isObjcImpl())
7559 {
7560 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
7561 }
7562 else // unrelated not overloaded member found
7563 {
7564 bool globMem = findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec);
7565 if (className.empty() && !globMem)
7566 {
7567 warn(root->fileName,root->startLine, "class for member '{}' cannot be found.", funcName);
7568 }
7569 else if (!className.empty() && !globMem)
7570 {
7571 warn(root->fileName,root->startLine,
7572 "member '{}' of class '{}' cannot be found",
7573 funcName,className);
7574 }
7575 }
7576 }
7577 else
7578 {
7579 // this should not be called
7580 warn(root->fileName,root->startLine,"member with no name found.");
7581 }
7582 return;
7583}
7584
7585//----------------------------------------------------------------------
7586// find the members corresponding to the different documentation blocks
7587// that are extracted from the sources.
7588
7589static void filterMemberDocumentation(const Entry *root,const DString &relates)
7590{
7591 AUTO_TRACE("root->type='{}' root->inside='{}' root->name='{}' root->args='{}' section={} root->spec={} root->mGrpId={}",
7592 root->type,root->inside,root->name,root->args,root->section,root->spec,root->mGrpId);
7593 //printf("root->parent()->name=%s\n",qPrint(root->parent()->name));
7594 bool isFunc=true;
7595
7596 DString type = root->type;
7597 DString args = root->args;
7598 int i=-1, l=0;
7599 if ( // detect func variable/typedef to func ptr
7600 (i=findFunctionPtr(type.str(),root->lang,&l))!=-1
7601 )
7602 {
7603 //printf("Fixing function pointer!\n");
7604 // fix type and argument
7605 args.prepend(type.mid(i+l));
7606 type=type.left(i+l);
7607 //printf("Results type=%s,name=%s,args=%s\n",qPrint(type),qPrint(root->name),qPrint(args));
7608 isFunc=false;
7609 }
7610 else if ((type.startsWith("typedef ") && args.find('(')!=DString::npos))
7611 // detect function types marked as functions
7612 {
7613 isFunc=false;
7614 }
7615
7616 //printf("Member %s isFunc=%d\n",qPrint(root->name),isFunc);
7617 if (root->section.isMemberDoc())
7618 {
7619 //printf("Documentation for inline member '%s' found args='%s'\n",
7620 // qPrint(root->name),qPrint(args));
7621 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
7622 if (type.empty())
7623 {
7624 findMember(root,
7625 relates,
7626 type,
7627 args,
7628 root->name + args + root->exception,
7629 false,
7630 isFunc);
7631 }
7632 else
7633 {
7634 findMember(root,
7635 relates,
7636 type,
7637 args,
7638 type + " " + root->name + args + root->exception,
7639 false,
7640 isFunc);
7641 }
7642 }
7643 else if (root->section.isOverloadDoc())
7644 {
7645 //printf("Overloaded member %s found\n",qPrint(root->name));
7646 findMember(root,
7647 relates,
7648 type,
7649 args,
7650 root->name,
7651 true,
7652 isFunc);
7653 }
7654 else if
7655 ((root->section.isFunction() // function
7656 ||
7657 (root->section.isVariable() && // variable
7658 !type.empty() && // with a type
7659 g_compoundKeywords.find(type.str())==g_compoundKeywords.end() // that is not a keyword
7660 // (to skip forward declaration of class etc.)
7661 )
7662 )
7663 )
7664 {
7665 //printf("Documentation for member '%s' found args='%s' excp='%s'\n",
7666 // qPrint(root->name),qPrint(args),qPrint(root->exception));
7667 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
7668 //printf("Inside=%s\n Relates=%s\n",qPrint(root->inside),qPrint(relates));
7669 if (isTypeAClassFriend(type))
7670 {
7671 findMember(root,
7672 relates,
7673 type,
7674 args,
7675 type+" "+root->name,
7676 false,false);
7677
7678 }
7679 else if (!type.empty())
7680 {
7681 findMember(root,
7682 relates,
7683 type,
7684 args,
7685 type+" "+ root->inside + root->name + args + root->exception,
7686 false,isFunc);
7687 }
7688 else
7689 {
7690 findMember(root,
7691 relates,
7692 type,
7693 args,
7694 root->inside + root->name + args + root->exception,
7695 false,isFunc);
7696 }
7697 }
7698 else if (root->section.isDefine() && !relates.empty())
7699 {
7700 findMember(root,
7701 relates,
7702 type,
7703 args,
7704 root->name + args,
7705 false,
7706 !args.empty());
7707 }
7708 else if (root->section.isVariableDoc())
7709 {
7710 //printf("Documentation for variable %s found\n",qPrint(root->name));
7711 //if (!relates.empty()) printf(" Relates %s\n",qPrint(relates));
7712 findMember(root,
7713 relates,
7714 type,
7715 args,
7716 root->name,
7717 false,
7718 false);
7719 }
7720 else if (root->section.isExportedInterface() ||
7721 root->section.isIncludedService())
7722 {
7723 findMember(root,
7724 relates,
7725 type,
7726 args,
7727 type + " " + root->name,
7728 false,
7729 false);
7730 }
7731 else
7732 {
7733 // skip section
7734 //printf("skip section\n");
7735 }
7736}
7737
7738static void findMemberDocumentation(const Entry *root)
7739{
7740 if (root->section.isMemberDoc() ||
7741 root->section.isOverloadDoc() ||
7742 root->section.isFunction() ||
7743 root->section.isVariable() ||
7744 root->section.isVariableDoc() ||
7745 root->section.isDefine() ||
7746 root->section.isIncludedService() ||
7747 root->section.isExportedInterface()
7748 )
7749 {
7750 AUTO_TRACE();
7751 if (root->relatesType==RelatesType::Duplicate && !root->relates.empty())
7752 {
7754 }
7756 }
7757 for (const auto &e : root->children())
7758 {
7759 if (!e->section.isEnum())
7760 {
7761 findMemberDocumentation(e.get());
7762 }
7763 }
7764}
7765
7766//----------------------------------------------------------------------
7767
7768static void findObjCMethodDefinitions(const Entry *root)
7769{
7770 AUTO_TRACE();
7771 for (const auto &objCImpl : root->children())
7772 {
7773 if (objCImpl->section.isObjcImpl())
7774 {
7775 for (const auto &objCMethod : objCImpl->children())
7776 {
7777 if (objCMethod->section.isFunction())
7778 {
7779 //printf(" Found ObjC method definition %s\n",qPrint(objCMethod->name));
7780 findMember(objCMethod.get(),
7781 objCMethod->relates,
7782 objCMethod->type,
7783 objCMethod->args,
7784 objCMethod->type+" "+objCImpl->name+"::"+objCMethod->name+" "+objCMethod->args,
7785 false,true);
7786 objCMethod->section=EntryType::makeEmpty();
7787 }
7788 }
7789 }
7790 }
7791}
7792
7793//----------------------------------------------------------------------
7794// find and add the enumeration to their classes, namespaces or files
7795
7796static void findEnums(const Entry *root)
7797{
7798 if (root->section.isEnum())
7799 {
7800 AUTO_TRACE("name={}",root->name);
7801 ClassDefMutable *cd = nullptr;
7802 FileDef *fd = nullptr;
7803 NamespaceDefMutable *nd = nullptr;
7804 MemberNameLinkedMap *mnsd = nullptr;
7805 bool isGlobal = false;
7806 bool isRelated = false;
7807 bool isMemberOf = false;
7808 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7809
7810 DString name;
7811 DString scope;
7812
7813 if (size_t i = root->name.rfind("::"); i!=DString::npos) // scope is specified
7814 {
7815 scope=root->name.left(i); // extract scope
7816 if (root->lang==SrcLangExt::CSharp)
7817 {
7818 scope = mangleCSharpGenericName(scope);
7819 }
7820 name=root->name.right(root->name.length()-i-2); // extract name
7821 if ((cd=getClassMutable(scope))==nullptr)
7822 {
7824 }
7825 }
7826 else // no scope, check the scope in which the docs where found
7827 {
7828 if (root->parent()->section.isScope() && !root->parent()->name.empty()) // found enum docs inside a compound
7829 {
7830 scope=root->parent()->name;
7831 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7832 }
7833 name=root->name;
7834 }
7835
7836 if (!root->relates.empty())
7837 { // related member, prefix user specified scope
7838 isRelated=true;
7839 isMemberOf=(root->relatesType==RelatesType::MemberOf);
7840 if (getClass(root->relates)==nullptr && !scope.empty())
7841 scope=mergeScopes(scope,root->relates);
7842 else
7843 scope=root->relates;
7844 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7845 }
7846
7847 if (cd && !name.empty()) // found a enum inside a compound
7848 {
7849 //printf("Enum '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7850 fd=nullptr;
7852 isGlobal=false;
7853 }
7854 else if (nd) // found enum inside namespace
7855 {
7857 isGlobal=true;
7858 }
7859 else // found a global enum
7860 {
7861 fd=root->fileDef();
7863 isGlobal=true;
7864 }
7865
7866 if (!name.empty())
7867 {
7868 // new enum type
7869 AUTO_TRACE_ADD("new enum {} at line {} of {}",name,root->bodyLine,root->fileName);
7870 auto md = createMemberDef(
7871 root->fileName,root->startLine,root->startColumn,
7872 DString(),name,DString(),DString(),
7873 root->protection,Specifier::Normal,false,
7874 isMemberOf ? Relationship::Foreign : isRelated ? Relationship::Related : Relationship::Member,
7875 MemberType::Enumeration,
7877 auto mmd = toMemberDefMutable(md.get());
7878 mmd->setTagInfo(root->tagInfo());
7879 mmd->setLanguage(root->lang);
7880 mmd->setId(root->id);
7881 if (!isGlobal) mmd->setMemberClass(cd); else mmd->setFileDef(fd);
7882 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7883 mmd->setBodyDef(root->fileDef());
7884 mmd->setMemberSpecifiers(root->spec);
7885 mmd->setVhdlSpecifiers(root->vhdlSpec);
7886 mmd->setEnumBaseType(root->args);
7887 //printf("Enum %s definition at line %d of %s: protection=%d scope=%s\n",
7888 // qPrint(root->name),root->bodyLine,qPrint(root->fileName),root->protection,cd?qPrint(cd->name()):"<none>");
7889 mmd->addSectionsToDefinition(root->anchors);
7890 mmd->setMemberGroupId(root->mGrpId);
7892 mmd->addQualifiers(root->qualifiers);
7893 //printf("%s::setRefItems(%zu)\n",qPrint(md->name()),root->sli.size());
7894 mmd->setRefItems(root->sli);
7895 mmd->setRequirementReferences(root->rqli);
7896 //printf("found enum %s nd=%p\n",qPrint(md->name()),nd);
7897 bool defSet=false;
7898 addIncludeFile(mmd,root->fileDef(),root);
7899
7900 DString baseType = root->args;
7901 if (!baseType.empty())
7902 {
7903 baseType.prepend(" : ");
7904 }
7905
7906 if (nd)
7907 {
7908 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python)
7909 {
7910 mmd->setDefinition(name+baseType);
7911 }
7912 else
7913 {
7914 mmd->setDefinition(nd->name()+"::"+name+baseType);
7915 }
7916 //printf("definition=%s\n",md->definition());
7917 defSet=true;
7918 mmd->setNamespace(nd);
7919 nd->insertMember(md.get());
7920 }
7921
7922 // even if we have already added the enum to a namespace, we still
7923 // also want to add it to other appropriate places such as file
7924 // or class.
7925 if (isGlobal && (nd==nullptr || !nd->isAnonymous()))
7926 {
7927 if (!defSet) mmd->setDefinition(name+baseType);
7928 if (fd==nullptr && root->parent())
7929 {
7930 fd=root->parent()->fileDef();
7931 }
7932 if (fd)
7933 {
7934 mmd->setFileDef(fd);
7935 fd->insertMember(md.get());
7936 }
7937 }
7938 else if (cd)
7939 {
7940 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python)
7941 {
7942 mmd->setDefinition(name+baseType);
7943 }
7944 else
7945 {
7946 mmd->setDefinition(cd->name()+"::"+name+baseType);
7947 }
7948 cd->insertMember(md.get());
7949 cd->insertUsedFile(fd);
7950 }
7951 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7952 mmd->setDocsForDefinition(!root->proto);
7953 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7954 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7955
7956 //printf("Adding member=%s\n",qPrint(md->name()));
7957 addMemberToGroups(root,md.get());
7959
7960 MemberName *mn = mnsd->add(name);
7961 mn->push_back(std::move(md));
7962 }
7963 }
7964 else
7965 {
7966 for (const auto &e : root->children()) findEnums(e.get());
7967 }
7968}
7969
7970//----------------------------------------------------------------------
7971
7972static void addEnumValuesToEnums(const Entry *root)
7973{
7974 if (root->section.isEnum())
7975 // non anonymous enumeration
7976 {
7977 AUTO_TRACE("name={}",root->name);
7978 ClassDefMutable *cd = nullptr;
7979 FileDef *fd = nullptr;
7980 NamespaceDefMutable *nd = nullptr;
7981 MemberNameLinkedMap *mnsd = nullptr;
7982 bool isGlobal = false;
7983 bool isRelated = false;
7984 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7985
7986 DString name;
7987 DString scope;
7988
7989 if (size_t i = root->name.rfind("::"); i!=DString::npos) // scope is specified
7990 {
7991 scope=root->name.left(i); // extract scope
7992 if (root->lang==SrcLangExt::CSharp)
7993 {
7994 scope = mangleCSharpGenericName(scope);
7995 }
7996 name=root->name.right(root->name.length()-i-2); // extract name
7997 if ((cd=getClassMutable(scope))==nullptr)
7998 {
8000 }
8001 }
8002 else // no scope, check the scope in which the docs where found
8003 {
8004 if (root->parent()->section.isScope() && !root->parent()->name.empty()) // found enum docs inside a compound
8005 {
8006 scope=root->parent()->name;
8007 if (root->lang==SrcLangExt::CSharp)
8008 {
8009 scope = mangleCSharpGenericName(scope);
8010 }
8011 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
8012 }
8013 name=root->name;
8014 }
8015
8016 if (!root->relates.empty())
8017 { // related member, prefix user specified scope
8018 isRelated=true;
8019 if (getClassMutable(root->relates)==nullptr && !scope.empty())
8020 scope=mergeScopes(scope,root->relates);
8021 else
8022 scope=root->relates;
8023 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
8024 }
8025
8026 if (cd && !name.empty()) // found a enum inside a compound
8027 {
8028 //printf("Enum in class '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
8029 fd=nullptr;
8031 isGlobal=false;
8032 }
8033 else if (nd && !nd->isAnonymous()) // found enum inside namespace
8034 {
8035 //printf("Enum in namespace '%s'::'%s'\n",qPrint(nd->name()),qPrint(name));
8037 isGlobal=true;
8038 }
8039 else // found a global enum
8040 {
8041 fd=root->fileDef();
8042 //printf("Enum in file '%s': '%s'\n",qPrint(fd->name()),qPrint(name));
8044 isGlobal=true;
8045 }
8046
8047 if (!name.empty())
8048 {
8049 //printf("** name=%s\n",qPrint(name));
8050 MemberName *mn = mnsd->find(name); // for all members with this name
8051 if (mn)
8052 {
8053 struct EnumValueInfo
8054 {
8055 EnumValueInfo(const DString &n,std::unique_ptr<MemberDef> &&md) :
8056 name(n), member(std::move(md)) {}
8057 DString name;
8058 std::unique_ptr<MemberDef> member;
8059 };
8060 std::vector< EnumValueInfo > extraMembers;
8061 // for each enum in this list
8062 for (const auto &imd : *mn)
8063 {
8064 MemberDefMutable *md = toMemberDefMutable(imd.get());
8065 // use raw pointer in this loop, since we modify mn and can then invalidate mdp.
8066 if (md && md->isEnumerate() && !root->children().empty())
8067 {
8068 AUTO_TRACE_ADD("enum {} with {} children",md->name(),root->children().size());
8069 for (const auto &e : root->children())
8070 {
8071 SrcLangExt sle = root->lang;
8072 bool isJavaLike = sle==SrcLangExt::CSharp || sle==SrcLangExt::Java || sle==SrcLangExt::XML;
8073 if ( isJavaLike || root->spec.isStrong())
8074 {
8075 if (sle == SrcLangExt::Cpp && e->section.isDefine()) continue;
8076 // Unlike classic C/C++ enums, for C++11, C# & Java enum
8077 // values are only visible inside the enum scope, so we must create
8078 // them here and only add them to the enum
8079 //printf("md->qualifiedName()=%s e->name=%s tagInfo=%p name=%s\n",
8080 // qPrint(md->qualifiedName()),qPrint(e->name),(void*)e->tagInfo(),qPrint(e->name));
8081 DString qualifiedName = root->name;
8082 if (size_t i = qualifiedName.rfind("::"); i!=DString::npos && sle==SrcLangExt::CSharp)
8083 {
8084 qualifiedName = mangleCSharpGenericName(qualifiedName.left(i))+qualifiedName.mid(i);
8085 }
8086 if (isJavaLike)
8087 {
8088 qualifiedName=substitute(qualifiedName,"::",".");
8089 }
8090 if (md->qualifiedName()==qualifiedName) // enum value scope matches that of the enum
8091 {
8092 DString fileName = e->fileName;
8093 if (fileName.empty() && e->tagInfo())
8094 {
8095 fileName = e->tagInfo()->tagName;
8096 }
8097 AUTO_TRACE_ADD("strong enum value {}",e->name);
8098 auto fmd = createMemberDef(
8099 fileName,e->startLine,e->startColumn,
8100 e->type,e->name,e->args,DString(),
8101 e->protection, Specifier::Normal,e->isStatic,Relationship::Member,
8102 MemberType::EnumValue,ArgumentList(),ArgumentList(),e->metaData);
8103 auto fmmd = toMemberDefMutable(fmd.get());
8104 NamespaceDef *mnd = md->getNamespaceDef();
8105 if (md->getClassDef())
8106 fmmd->setMemberClass(md->getClassDef());
8107 else if (mnd && (mnd->isLinkable() || mnd->isAnonymous()))
8108 fmmd->setNamespace(mnd);
8109 else if (md->getFileDef())
8110 fmmd->setFileDef(md->getFileDef());
8111 fmmd->setOuterScope(md->getOuterScope());
8112 fmmd->setTagInfo(e->tagInfo());
8113 fmmd->setLanguage(e->lang);
8114 fmmd->setBodySegment(e->startLine,e->bodyLine,e->endBodyLine);
8115 fmmd->setBodyDef(e->fileDef());
8116 fmmd->setId(e->id);
8117 fmmd->setDocumentation(e->doc,e->docFile,e->docLine);
8118 fmmd->setBriefDescription(e->brief,e->briefFile,e->briefLine);
8119 fmmd->addSectionsToDefinition(e->anchors);
8120 fmmd->setInitializer(e->initializer.str());
8121 fmmd->setMaxInitLines(e->initLines);
8122 fmmd->setMemberGroupId(e->mGrpId);
8123 fmmd->setExplicitExternal(e->explicitExternal,fileName,e->startLine,e->startColumn);
8124 fmmd->setRefItems(e->sli);
8125 fmmd->setRequirementReferences(e->rqli);
8126 fmmd->setAnchor();
8127 md->insertEnumField(fmd.get());
8128 fmmd->setEnumScope(md,true);
8129 extraMembers.emplace_back(e->name,std::move(fmd));
8130 }
8131 }
8132 else
8133 {
8134 AUTO_TRACE_ADD("enum value {}",e->name);
8135 //printf("e->name=%s isRelated=%d\n",qPrint(e->name),isRelated);
8136 MemberName *fmn=nullptr;
8137 MemberNameLinkedMap *emnsd = isRelated ? Doxygen::functionNameLinkedMap : mnsd;
8138 if (!e->name.empty() && (fmn=emnsd->find(e->name)))
8139 // get list of members with the same name as the field
8140 {
8141 for (const auto &ifmd : *fmn)
8142 {
8143 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
8144 if (fmd && fmd->isEnumValue() && fmd->getOuterScope()==md->getOuterScope()) // in same scope
8145 {
8146 //printf("found enum value with same name %s in scope %s\n",
8147 // qPrint(fmd->name()),qPrint(fmd->getOuterScope()->name()));
8148 if (nd && !nd->isAnonymous())
8149 {
8150 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
8151 {
8152 const NamespaceDef *fnd=fmd->getNamespaceDef();
8153 if (fnd==nd) // enum value is inside a namespace
8154 {
8155 md->insertEnumField(fmd);
8156 fmd->setEnumScope(md);
8157 }
8158 }
8159 }
8160 else if (isGlobal)
8161 {
8162 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
8163 {
8164 const FileDef *ffd=fmd->getFileDef();
8165 if (ffd==fd && ffd==md->getFileDef()) // enum value has file scope
8166 {
8167 md->insertEnumField(fmd);
8168 fmd->setEnumScope(md);
8169 }
8170 }
8171 }
8172 else if (isRelated && cd) // reparent enum value to
8173 // match the enum's scope
8174 {
8175 md->insertEnumField(fmd); // add field def to list
8176 fmd->setEnumScope(md); // cross ref with enum name
8177 fmd->setEnumClassScope(cd); // cross ref with enum name
8178 fmd->setOuterScope(cd);
8179 fmd->makeRelated();
8180 cd->insertMember(fmd);
8181 }
8182 else
8183 {
8184 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
8185 {
8186 const ClassDef *fcd=fmd->getClassDef();
8187 if (fcd==cd) // enum value is inside a class
8188 {
8189 //printf("Inserting enum field %s in enum scope %s\n",
8190 // qPrint(fmd->name()),qPrint(md->name()));
8191 md->insertEnumField(fmd); // add field def to list
8192 fmd->setEnumScope(md); // cross ref with enum name
8193 }
8194 }
8195 }
8196 }
8197 }
8198 }
8199 }
8200 }
8201 }
8202 }
8203 // move the newly added members into mn
8204 for (auto &e : extraMembers)
8205 {
8206 MemberName *emn=mnsd->add(e.name);
8207 emn->push_back(std::move(e.member));
8208 }
8209 }
8210 }
8211 }
8212 else
8213 {
8214 for (const auto &e : root->children()) addEnumValuesToEnums(e.get());
8215 }
8216}
8217
8218//----------------------------------------------------------------------
8219
8220static void addEnumDocs(const Entry *root,MemberDefMutable *md)
8221{
8222 AUTO_TRACE();
8223 // documentation outside a compound overrides the documentation inside it
8224 {
8225 md->setDocumentation(root->doc,root->docFile,root->docLine);
8226 md->setDocsForDefinition(!root->proto);
8227 }
8228
8229 // brief descriptions inside a compound override the documentation
8230 // outside it
8231 {
8232 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
8233 }
8234
8235 if (md->inbodyDocumentation().empty() || !root->parent()->name.empty())
8236 {
8238 }
8239
8240 if (root->mGrpId!=-1 && md->getMemberGroupId()==-1)
8241 {
8242 md->setMemberGroupId(root->mGrpId);
8243 }
8244
8246 md->setRefItems(root->sli);
8247 md->setRequirementReferences(root->rqli);
8248
8249 const GroupDef *gd=md->getGroupDef();
8250 if (gd==nullptr && !root->groups.empty()) // member not grouped but out-of-line documentation is
8251 {
8252 addMemberToGroups(root,md);
8253 }
8255}
8256
8257//----------------------------------------------------------------------
8258// Search for the name in the associated groups. If a matching member
8259// definition exists, then add the documentation to it and return true,
8260// otherwise false.
8261
8262static bool tryAddEnumDocsToGroupMember(const Entry *root,const DString &name)
8263{
8264 for (const auto &g : root->groups)
8265 {
8266 const GroupDef *gd = Doxygen::groupLinkedMap->find(g.groupname);
8267 if (gd)
8268 {
8269 MemberList *ml = gd->getMemberList(MemberListType::DecEnumMembers());
8270 if (ml)
8271 {
8272 MemberDefMutable *md = toMemberDefMutable(ml->find(name));
8273 if (md)
8274 {
8275 addEnumDocs(root,md);
8276 return true;
8277 }
8278 }
8279 }
8280 else if (!gd && g.pri == Grouping::GROUPING_INGROUP)
8281 {
8282 warn(root->fileName, root->startLine,
8283 "Found non-existing group '{}' for the command '{}', ignoring command",
8284 g.groupname, Grouping::getGroupPriName( g.pri )
8285 );
8286 }
8287 }
8288
8289 return false;
8290}
8291
8292//----------------------------------------------------------------------
8293// find the documentation blocks for the enumerations
8294
8295static void findEnumDocumentation(const Entry *root)
8296{
8297 if (root->section.isEnumDoc() &&
8298 !root->name.empty() &&
8299 root->name.at(0)!='@' // skip anonymous enums
8300 )
8301 {
8302 DString name;
8303 DString scope;
8304 if (size_t i = root->name.rfind("::"); i!=DString::npos) // scope is specified as part of the name
8305 {
8306 name=root->name.mid(i+2); // extract name
8307 scope=root->name.left(i); // extract scope
8308 //printf("Scope='%s' Name='%s'\n",qPrint(scope),qPrint(name));
8309 }
8310 else // just the name
8311 {
8312 name=root->name;
8313 }
8314 if (root->parent()->section.isScope() && !root->parent()->name.empty()) // found enum docs inside a compound
8315 {
8316 if (!scope.empty()) scope.prepend("::");
8317 scope.prepend(root->parent()->name);
8318 }
8319 const ClassDef *cd = getClass(scope);
8321 const FileDef *fd = root->fileDef();
8322 AUTO_TRACE("Found docs for enum with name '{}' and scope '{}' in context '{}' cd='{}', nd='{}' fd='{}'",
8323 name,scope,root->parent()->name,
8324 cd ? cd->name() : DString("<none>"),
8325 nd ? nd->name() : DString("<none>"),
8326 fd ? fd->name() : DString("<none>"));
8327
8328 if (!name.empty())
8329 {
8330 bool found = tryAddEnumDocsToGroupMember(root, name);
8331 if (!found)
8332 {
8334 if (mn)
8335 {
8336 for (const auto &imd : *mn)
8337 {
8338 MemberDefMutable *md = toMemberDefMutable(imd.get());
8339 if (md && md->isEnumerate())
8340 {
8341 const ClassDef *mcd = md->getClassDef();
8342 const NamespaceDef *mnd = md->getNamespaceDef();
8343 const FileDef *mfd = md->getFileDef();
8344 if (cd && mcd==cd)
8345 {
8346 AUTO_TRACE_ADD("Match found for class scope");
8347 addEnumDocs(root,md);
8348 found = true;
8349 break;
8350 }
8351 else if (cd==nullptr && mcd==nullptr && nd!=nullptr && mnd==nd)
8352 {
8353 AUTO_TRACE_ADD("Match found for namespace scope");
8354 addEnumDocs(root,md);
8355 found = true;
8356 break;
8357 }
8358 else if (cd==nullptr && nd==nullptr && mcd==nullptr && mnd==nullptr && fd==mfd)
8359 {
8360 AUTO_TRACE_ADD("Match found for global scope");
8361 addEnumDocs(root,md);
8362 found = true;
8363 break;
8364 }
8365 }
8366 }
8367 }
8368 }
8369 if (!found)
8370 {
8371 warn(root->fileName,root->startLine, "Documentation for undefined enum '{}' found.", name);
8372 }
8373 }
8374 }
8375 for (const auto &e : root->children()) findEnumDocumentation(e.get());
8376}
8377
8378// search for each enum (member or function) in mnl if it has documented
8379// enum values.
8380static void findDEV(const MemberNameLinkedMap &mnsd)
8381{
8382 // for each member name
8383 for (const auto &mn : mnsd)
8384 {
8385 // for each member definition
8386 for (const auto &imd : *mn)
8387 {
8388 MemberDefMutable *md = toMemberDefMutable(imd.get());
8389 if (md && md->isEnumerate()) // member is an enum
8390 {
8391 int documentedEnumValues=0;
8392 // for each enum value
8393 for (const auto &fmd : md->enumFieldList())
8394 {
8395 if (fmd->isLinkableInProject()) documentedEnumValues++;
8396 }
8397 // at least one enum value is documented
8398 if (documentedEnumValues>0) md->setDocumentedEnumValues(true);
8399 }
8400 }
8401 }
8402}
8403
8404// search for each enum (member or function) if it has documented enum
8405// values.
8411
8412//----------------------------------------------------------------------
8413
8415{
8416 auto &index = Index::instance();
8417 // for each class member name
8418 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8419 {
8420 // for each member definition
8421 for (const auto &md : *mn)
8422 {
8423 index.addClassMemberNameToIndex(md.get());
8424 if (md->getModuleDef())
8425 {
8426 index.addModuleMemberNameToIndex(md.get());
8427 }
8428 }
8429 }
8430 // for each file/namespace function name
8431 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8432 {
8433 // for each member definition
8434 for (const auto &md : *mn)
8435 {
8436 if (md->getNamespaceDef())
8437 {
8438 index.addNamespaceMemberNameToIndex(md.get());
8439 }
8440 else
8441 {
8442 index.addFileMemberNameToIndex(md.get());
8443 }
8444 if (md->getModuleDef())
8445 {
8446 index.addModuleMemberNameToIndex(md.get());
8447 }
8448 }
8449 }
8450
8451 index.sortMemberIndexLists();
8452}
8453
8454//----------------------------------------------------------------------
8455
8456static void addToIndices()
8457{
8458 for (const auto &cd : *Doxygen::classLinkedMap)
8459 {
8460 if (cd->isLinkableInProject())
8461 {
8462 Doxygen::indexList->addIndexItem(cd.get(),nullptr);
8463 if (Doxygen::searchIndex.enabled())
8464 {
8465 Doxygen::searchIndex.setCurrentDoc(cd.get(),cd->anchor(),false);
8466 Doxygen::searchIndex.addWord(cd->localName(),true);
8467 }
8468 }
8469 }
8470
8471 for (const auto &cd : *Doxygen::conceptLinkedMap)
8472 {
8473 if (cd->isLinkableInProject())
8474 {
8475 Doxygen::indexList->addIndexItem(cd.get(),nullptr);
8476 if (Doxygen::searchIndex.enabled())
8477 {
8478 Doxygen::searchIndex.setCurrentDoc(cd.get(),cd->anchor(),false);
8479 Doxygen::searchIndex.addWord(cd->localName(),true);
8480 }
8481 }
8482 }
8483
8484 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8485 {
8486 if (nd->isLinkableInProject())
8487 {
8488 Doxygen::indexList->addIndexItem(nd.get(),nullptr);
8489 if (Doxygen::searchIndex.enabled())
8490 {
8491 Doxygen::searchIndex.setCurrentDoc(nd.get(),nd->anchor(),false);
8492 Doxygen::searchIndex.addWord(nd->localName(),true);
8493 }
8494 }
8495 }
8496
8497 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8498 {
8499 for (const auto &fd : *fn)
8500 {
8501 if (Doxygen::searchIndex.enabled() && fd->isLinkableInProject())
8502 {
8503 Doxygen::searchIndex.setCurrentDoc(fd.get(),fd->anchor(),false);
8504 Doxygen::searchIndex.addWord(fd->localName(),true);
8505 }
8506 }
8507 }
8508
8509 auto addWordsForTitle = [](const Definition *d,const DString &anchor,const DString &title)
8510 {
8512 if (Doxygen::searchIndex.enabled())
8513 {
8514 Doxygen::searchIndex.setCurrentDoc(d,anchor,false);
8515 std::string s = title.str();
8516 static const reg::Ex re(R"(\a[\w-]*)");
8517 reg::Iterator it(s,re);
8519 for (; it!=end ; ++it)
8520 {
8521 const auto &match = *it;
8522 std::string matchStr = match.str();
8523 Doxygen::searchIndex.addWord(matchStr,true);
8524 }
8525 }
8526 };
8527
8528 for (const auto &gd : *Doxygen::groupLinkedMap)
8529 {
8530 if (gd->isLinkableInProject())
8531 {
8532 addWordsForTitle(gd.get(),gd->anchor(),gd->groupTitle());
8533 }
8534 }
8535
8536 for (const auto &pd : *Doxygen::pageLinkedMap)
8537 {
8538 if (pd->isLinkableInProject())
8539 {
8540 addWordsForTitle(pd.get(),pd->anchor(),pd->title());
8541 }
8542 }
8543
8545 {
8546 addWordsForTitle(Doxygen::mainPage.get(),Doxygen::mainPage->anchor(),Doxygen::mainPage->title());
8547 }
8548
8549 auto addMemberToSearchIndex = [](const MemberDef *md)
8550 {
8551 if (Doxygen::searchIndex.enabled())
8552 {
8553 Doxygen::searchIndex.setCurrentDoc(md,md->anchor(),false);
8554 DString ln=md->localName();
8555 DString qn=md->qualifiedName();
8557 if (ln!=qn)
8558 {
8560 if (md->getClassDef())
8561 {
8562 Doxygen::searchIndex.addWord(md->getClassDef()->displayName(),true);
8563 }
8564 if (md->getNamespaceDef())
8565 {
8566 Doxygen::searchIndex.addWord(md->getNamespaceDef()->displayName(),true);
8567 }
8568 }
8569 }
8570 };
8571
8572 auto getScope = [](const MemberDef *md)
8573 {
8574 const Definition *scope = nullptr;
8575 if (md->getGroupDef()) scope = md->getGroupDef();
8576 else if (md->getClassDef()) scope = md->getClassDef();
8577 else if (md->getNamespaceDef()) scope = md->getNamespaceDef();
8578 else if (md->getFileDef()) scope = md->getFileDef();
8579 return scope;
8580 };
8581
8582 auto addMemberToIndices = [addMemberToSearchIndex,getScope](const MemberDef *md)
8583 {
8584 if (md->isLinkableInProject())
8585 {
8586 if (!(md->isEnumerate() && md->isAnonymous()))
8587 {
8588 Doxygen::indexList->addIndexItem(getScope(md),md);
8589 addMemberToSearchIndex(md);
8590 }
8591 if (md->isEnumerate())
8592 {
8593 for (const auto &fmd : md->enumFieldList())
8594 {
8595 Doxygen::indexList->addIndexItem(getScope(fmd),fmd);
8596 addMemberToSearchIndex(fmd);
8597 }
8598 }
8599 }
8600 };
8601
8602 // for each class member name
8603 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8604 {
8605 // for each member definition
8606 for (const auto &md : *mn)
8607 {
8608 addMemberToIndices(md.get());
8609 }
8610 }
8611 // for each file/namespace function name
8612 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8613 {
8614 // for each member definition
8615 for (const auto &md : *mn)
8616 {
8617 addMemberToIndices(md.get());
8618 }
8619 }
8620}
8621
8622//----------------------------------------------------------------------
8623
8625{
8626 // for each member name
8627 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8628 {
8629 // for each member definition
8630 for (const auto &imd : *mn)
8631 {
8632 MemberDefMutable *md = toMemberDefMutable(imd.get());
8633 if (md)
8634 {
8636 }
8637 }
8638 }
8639 // for each member name
8640 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8641 {
8642 // for each member definition
8643 for (const auto &imd : *mn)
8644 {
8645 MemberDefMutable *md = toMemberDefMutable(imd.get());
8646 if (md)
8647 {
8649 }
8650 }
8651 }
8652}
8653
8654// recursive helper function looking for reimplements/implemented
8655// by relations between class cd and direct or indirect base class bcd
8657{
8658 for (const auto &mn : cd->memberNameInfoLinkedMap()) // for each member in class cd with a unique name
8659 {
8660 for (const auto &imd : *mn) // for each member with a given name
8661 {
8662 MemberDefMutable *md = toMemberDefMutable(imd->memberDef());
8663 if (md && (md->isFunction() || md->isCSharpProperty())) // filter on reimplementable members
8664 {
8665 ClassDef *mbcd = bcd->classDef;
8666 if (mbcd && mbcd->isLinkable()) // filter on linkable classes
8667 {
8668 const auto &bmn = mbcd->memberNameInfoLinkedMap();
8669 const auto &bmni = bmn.find(mn->memberName());
8670 if (bmni) // there are base class members with the same name
8671 {
8672 for (const auto &ibmd : *bmni) // for base class member with that name
8673 {
8674 MemberDefMutable *bmd = toMemberDefMutable(ibmd->memberDef());
8675 if (bmd) // not part of an inline namespace
8676 {
8677 auto lang = bmd->getLanguage();
8678 auto compType = mbcd->compoundType();
8679 if (bmd->virtualness()!=Specifier::Normal ||
8680 lang==SrcLangExt::Python ||
8681 lang==SrcLangExt::Java ||
8682 lang==SrcLangExt::PHP ||
8683 compType==ClassDef::Interface ||
8684 compType==ClassDef::Protocol)
8685 {
8686 const ArgumentList &bmdAl = bmd->argumentList();
8687 const ArgumentList &mdAl = md->argumentList();
8688 //printf(" Base argList='%s'\n Super argList='%s'\n",
8689 // qPrint(argListToString(bmdAl)),
8690 // qPrint(argListToString(mdAl))
8691 // );
8692 if (
8693 lang==SrcLangExt::Python ||
8694 matchArguments2(bmd->getOuterScope(),bmd->getFileDef(),bmd->typeString(),&bmdAl,
8695 md->getOuterScope(), md->getFileDef(), md->typeString(),&mdAl,
8696 true,lang
8697 )
8698 )
8699 {
8700 if (lang==SrcLangExt::Python && md->name().startsWith("__")) continue; // private members do not reimplement
8701 //printf("match!\n");
8702 const MemberDef *rmd = md->reimplements();
8703 if (rmd==nullptr) // not already assigned
8704 {
8705 //printf("%s: setting (new) reimplements member %s\n",qPrint(md->qualifiedName()),qPrint(bmd->qualifiedName()));
8706 md->setReimplements(bmd);
8707 }
8708 //printf("%s: add reimplementedBy member %s\n",qPrint(bmd->qualifiedName()),qPrint(md->qualifiedName()));
8709 bmd->insertReimplementedBy(md);
8710 }
8711 else
8712 {
8713 //printf("no match!\n");
8714 }
8715 }
8716 }
8717 }
8718 }
8719 }
8720 }
8721 }
8722 }
8723
8724 // do also for indirect base classes
8725 for (const auto &bbcd : bcd->classDef->baseClasses())
8726 {
8728 }
8729}
8730
8731//----------------------------------------------------------------------
8732// computes the relation between all members. For each member 'm'
8733// the members that override the implementation of 'm' are searched and
8734// the member that 'm' overrides is searched.
8735
8737{
8738 for (const auto &cd : *Doxygen::classLinkedMap)
8739 {
8740 if (cd->isLinkable())
8741 {
8742 for (const auto &bcd : cd->baseClasses())
8743 {
8745 }
8746 }
8747 }
8748}
8749
8750//----------------------------------------------------------------------------
8751
8753{
8754 // for each class
8755 for (const auto &cd : *Doxygen::classLinkedMap)
8756 {
8757 // that is a template
8758 for (const auto &ti : cd->getTemplateInstances())
8759 {
8760 ClassDefMutable *tcdm = toClassDefMutable(ti.classDef);
8761 if (tcdm)
8762 {
8763 tcdm->addMembersToTemplateInstance(cd.get(),cd->templateArguments(),ti.templSpec);
8764 }
8765 }
8766 }
8767}
8768
8769//----------------------------------------------------------------------------
8770
8771static void mergeCategories()
8772{
8773 AUTO_TRACE();
8774 // merge members of categories into the class they extend
8775 for (const auto &cd : *Doxygen::classLinkedMap)
8776 {
8777 if (size_t i=cd->name().find('('); i!=DString::npos) // it is an Objective-C category
8778 {
8779 DString baseName=cd->name().left(i);
8780 ClassDefMutable *baseClass=toClassDefMutable(Doxygen::classLinkedMap->find(baseName));
8781 if (baseClass)
8782 {
8783 AUTO_TRACE_ADD("merging members of category {} into {}",cd->name(),baseClass->name());
8784 baseClass->mergeCategory(cd.get());
8785 }
8786 }
8787 }
8788}
8789
8790// builds the list of all members for each class
8791
8793{
8794 // merge the member list of base classes into the inherited classes.
8795 for (const auto &cd : *Doxygen::classLinkedMap)
8796 {
8797 if (// !cd->isReference() && // not an external class
8798 cd->subClasses().empty() && // is a root of the hierarchy
8799 !cd->baseClasses().empty()) // and has at least one base class
8800 {
8801 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8802 if (cdm)
8803 {
8804 //printf("*** merging members for %s\n",qPrint(cd->name()));
8805 cdm->mergeMembers();
8806 }
8807 }
8808 }
8809 // now sort the member list of all members for all classes.
8810 for (const auto &cd : *Doxygen::classLinkedMap)
8811 {
8812 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8813 if (cdm)
8814 {
8815 cdm->sortAllMembersList();
8816 }
8817 }
8818}
8819
8820//----------------------------------------------------------------------------
8821
8823{
8824 auto processSourceFile = [](FileDef *fd,OutputList &ol,ClangTUParser *parser)
8825 {
8826 bool showSources = fd->generateSourceFile() && !Htags::useHtags; // sources need to be shown in the output
8827 bool parseSources = !fd->isReference() && Doxygen::parseSourcesNeeded; // we needed to parse the sources even if we do not show them
8828 if (showSources)
8829 {
8830 msg("Generating code for file {}...\n",fd->docName());
8831 fd->writeSourceHeader(ol);
8832 fd->writeSourceBody(ol,parser);
8833 fd->writeSourceFooter(ol);
8834 }
8835 else if (parseSources)
8836 {
8837 msg("Parsing code for file {}...\n",fd->docName());
8838 fd->parseSource(parser);
8839 }
8840 };
8841 if (!Doxygen::inputNameLinkedMap->empty())
8842 {
8843#if USE_LIBCLANG
8845 {
8846 StringUnorderedSet processedFiles;
8847
8848 // create a dictionary with files to process
8849 StringUnorderedSet filesToProcess;
8850
8851 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8852 {
8853 for (const auto &fd : *fn)
8854 {
8855 filesToProcess.insert(fd->absFilePath().str());
8856 }
8857 }
8858 // process source files (and their include dependencies)
8859 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8860 {
8861 for (const auto &fd : *fn)
8862 {
8863 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt::Cpp &&
8864 (fd->generateSourceFile() ||
8866 )
8867 )
8868 {
8869 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8870 clangParser->parse();
8871 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8872
8873 for (auto incFile : clangParser->filesInSameTU())
8874 {
8875 if (filesToProcess.find(incFile)!=filesToProcess.end() && // part of input
8876 fd->absFilePath()!=incFile && // not same file
8877 processedFiles.find(incFile)==processedFiles.end()) // not yet marked as processed
8878 {
8879 StringVector moreFiles;
8880 bool ambig = false;
8881 FileDef *ifd=Doxygen::inputNameLinkedMap->findFileDef(incFile,ambig);
8882 if (ifd && !ifd->isReference())
8883 {
8884 processSourceFile(ifd,*g_outputList,clangParser.get());
8885 processedFiles.insert(incFile);
8886 }
8887 }
8888 }
8889 processedFiles.insert(fd->absFilePath().str());
8890 }
8891 }
8892 }
8893 // process remaining files
8894 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8895 {
8896 for (const auto &fd : *fn)
8897 {
8898 if (processedFiles.find(fd->absFilePath().str())==processedFiles.end()) // not yet processed
8899 {
8900 if (fd->getLanguage()==SrcLangExt::Cpp) // C/C++ file, use clang parser
8901 {
8902 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8903 clangParser->parse();
8904 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8905 }
8906 else // non C/C++ file, use built-in parser
8907 {
8908 processSourceFile(fd.get(),*g_outputList,nullptr);
8909 }
8910 }
8911 }
8912 }
8913 }
8914 else
8915#endif
8916 {
8917 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8918 if (numThreads>1)
8919 {
8920 msg("Generating code files using {} threads.\n",numThreads);
8921 struct SourceContext
8922 {
8923 SourceContext(FileDef *fd_,bool gen_,const OutputList &ol_)
8924 : fd(fd_), generateSourceFile(gen_), ol(ol_) {}
8925 FileDef *fd;
8926 bool generateSourceFile;
8927 OutputList ol;
8928 };
8929 ThreadPool threadPool(numThreads);
8930 std::vector< std::future< std::shared_ptr<SourceContext> > > results;
8931 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8932 {
8933 for (const auto &fd : *fn)
8934 {
8935 bool generateSourceFile = fd->generateSourceFile() && !Htags::useHtags;
8936 auto ctx = std::make_shared<SourceContext>(fd.get(),generateSourceFile,*g_outputList);
8937 auto processFile = [ctx]()
8938 {
8939 if (ctx->generateSourceFile)
8940 {
8941 msg("Generating code for file {}...\n",ctx->fd->docName());
8942 }
8943 else
8944 {
8945 msg("Parsing code for file {}...\n",ctx->fd->docName());
8946 }
8947 StringVector filesInSameTu;
8948 ctx->fd->getAllIncludeFilesRecursively(filesInSameTu);
8949 if (ctx->generateSourceFile) // sources need to be shown in the output
8950 {
8951 ctx->fd->writeSourceHeader(ctx->ol);
8952 ctx->fd->writeSourceBody(ctx->ol,nullptr);
8953 ctx->fd->writeSourceFooter(ctx->ol);
8954 }
8955 else if (!ctx->fd->isReference() && Doxygen::parseSourcesNeeded)
8956 // we needed to parse the sources even if we do not show them
8957 {
8958 ctx->fd->parseSource(nullptr);
8959 }
8960 return ctx;
8961 };
8962 results.emplace_back(threadPool.queue(processFile));
8963 }
8964 }
8965 for (auto &f : results)
8966 {
8967 auto ctx = f.get();
8968 }
8969 }
8970 else // single threaded version
8971 {
8972 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8973 {
8974 for (const auto &fd : *fn)
8975 {
8976 StringVector filesInSameTu;
8977 fd->getAllIncludeFilesRecursively(filesInSameTu);
8978 processSourceFile(fd.get(),*g_outputList,nullptr);
8979 }
8980 }
8981 }
8982 }
8983 }
8984}
8985
8986//----------------------------------------------------------------------------
8987
8988static void generateFileDocs()
8989{
8990 if (Index::instance().numDocumentedFiles()==0) return;
8991
8992 if (!Doxygen::inputNameLinkedMap->empty())
8993 {
8994 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8995 if (numThreads>1) // multi threaded processing
8996 {
8997 struct DocContext
8998 {
8999 DocContext(FileDef *fd_,const OutputList &ol_)
9000 : fd(fd_), ol(ol_) {}
9001 FileDef *fd;
9002 OutputList ol;
9003 };
9004 ThreadPool threadPool(numThreads);
9005 std::vector< std::future< std::shared_ptr<DocContext> > > results;
9006 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9007 {
9008 for (const auto &fd : *fn)
9009 {
9010 bool doc = fd->isLinkableInProject();
9011 if (doc)
9012 {
9013 auto ctx = std::make_shared<DocContext>(fd.get(),*g_outputList);
9014 auto processFile = [ctx]() {
9015 msg("Generating docs for file {}...\n",ctx->fd->docName());
9016 ctx->fd->writeDocumentation(ctx->ol);
9017 return ctx;
9018 };
9019 results.emplace_back(threadPool.queue(processFile));
9020 }
9021 }
9022 }
9023 for (auto &f : results)
9024 {
9025 auto ctx = f.get();
9026 }
9027 }
9028 else // single threaded processing
9029 {
9030 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9031 {
9032 for (const auto &fd : *fn)
9033 {
9034 bool doc = fd->isLinkableInProject();
9035 if (doc)
9036 {
9037 msg("Generating docs for file {}...\n",fd->docName());
9038 fd->writeDocumentation(*g_outputList);
9039 }
9040 }
9041 }
9042 }
9043 }
9044}
9045
9046//----------------------------------------------------------------------------
9047
9049{
9050 // add source references for class definitions
9051 for (const auto &cd : *Doxygen::classLinkedMap)
9052 {
9053 const FileDef *fd=cd->getBodyDef();
9054 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
9055 {
9056 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),nullptr);
9057 }
9058 }
9059 // add source references for concept definitions
9060 for (const auto &cd : *Doxygen::conceptLinkedMap)
9061 {
9062 const FileDef *fd=cd->getBodyDef();
9063 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
9064 {
9065 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),nullptr);
9066 }
9067 }
9068 // add source references for namespace definitions
9069 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9070 {
9071 const FileDef *fd=nd->getBodyDef();
9072 if (fd && nd->isLinkableInProject() && nd->getStartDefLine()!=-1)
9073 {
9074 const_cast<FileDef*>(fd)->addSourceRef(nd->getStartDefLine(),nd.get(),nullptr);
9075 }
9076 }
9077
9078 // add source references for member names
9079 for (const auto &mn : *Doxygen::memberNameLinkedMap)
9080 {
9081 for (const auto &md : *mn)
9082 {
9083 //printf("class member %s: def=%s body=%d link?=%d\n",
9084 // qPrint(md->name()),
9085 // md->getBodyDef()?qPrint(md->getBodyDef()->name()):"<none>",
9086 // md->getStartBodyLine(),md->isLinkableInProject());
9087 const FileDef *fd=md->getBodyDef();
9088 if (fd &&
9089 md->getStartDefLine()!=-1 &&
9090 md->isLinkableInProject() &&
9092 )
9093 {
9094 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
9095 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
9096 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
9097 }
9098 }
9099 }
9100 for (const auto &mn : *Doxygen::functionNameLinkedMap)
9101 {
9102 for (const auto &md : *mn)
9103 {
9104 const FileDef *fd=md->getBodyDef();
9105 //printf("member %s body=[%d,%d] fd=%p link=%d parseSources=%d\n",
9106 // qPrint(md->name()),
9107 // md->getStartBodyLine(),md->getEndBodyLine(),fd,
9108 // md->isLinkableInProject(),
9109 // Doxygen::parseSourcesNeeded);
9110 if (fd &&
9111 md->getStartDefLine()!=-1 &&
9112 md->isLinkableInProject() &&
9114 )
9115 {
9116 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
9117 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
9118 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
9119 }
9120 }
9121 }
9122}
9123
9124//----------------------------------------------------------------------------
9125
9126// add the macro definitions found during preprocessing as file members
9127static void buildDefineList()
9128{
9129 AUTO_TRACE();
9130 for (const auto &s : g_inputFiles)
9131 {
9132 auto it = Doxygen::macroDefinitions.find(s);
9134 {
9135 for (const auto &def : it->second)
9136 {
9137 auto md = createMemberDef(
9138 def.fileName,def.lineNr,def.columnNr,
9139 "#define",def.name,def.args,DString(),
9140 Protection::Public,Specifier::Normal,false,Relationship::Member,MemberType::Define,
9141 ArgumentList(),ArgumentList(),"");
9142 auto mmd = toMemberDefMutable(md.get());
9143
9144 if (!def.args.empty())
9145 {
9146 mmd->moveArgumentList(stringToArgumentList(SrcLangExt::Cpp, def.args));
9147 }
9148 mmd->setInitializer(def.definition);
9149 mmd->setFileDef(def.fileDef);
9150 mmd->setDefinition("#define "+def.name);
9151
9153 if (def.fileDef)
9154 {
9155 const MemberList *defMl = def.fileDef->getMemberList(MemberListType::DocDefineMembers());
9156 if (defMl)
9157 {
9158 const MemberDef *defMd = defMl->findRev(def.name);
9159 if (defMd) // definition already stored
9160 {
9161 mmd->setRedefineCount(defMd->redefineCount()+1);
9162 }
9163 }
9164 def.fileDef->insertMember(md.get());
9165 }
9166 AUTO_TRACE_ADD("adding macro {} with definition {}",def.name,def.definition);
9167 mn->push_back(std::move(md));
9168 }
9169 }
9170 }
9171}
9172
9173//----------------------------------------------------------------------------
9174
9175static void sortMemberLists()
9176{
9177 // sort class member lists
9178 for (const auto &cd : *Doxygen::classLinkedMap)
9179 {
9180 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9181 if (cdm)
9182 {
9183 cdm->sortMemberLists();
9184 }
9185 }
9186
9187 // sort namespace member lists
9188 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9189 {
9191 if (ndm)
9192 {
9193 ndm->sortMemberLists();
9194 }
9195 }
9196
9197 // sort file member lists
9198 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9199 {
9200 for (const auto &fd : *fn)
9201 {
9202 fd->sortMemberLists();
9203 }
9204 }
9205
9206 // sort group member lists
9207 for (const auto &gd : *Doxygen::groupLinkedMap)
9208 {
9209 gd->sortMemberLists();
9210 }
9211
9213}
9214
9215//----------------------------------------------------------------------------
9216
9217static bool isSymbolHidden(const Definition *d)
9218{
9219 bool hidden = d->isHidden();
9220 const Definition *parent = d->getOuterScope();
9221 return parent ? hidden || isSymbolHidden(parent) : hidden;
9222}
9223
9225{
9226 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9227 if (numThreads>1)
9228 {
9229 ThreadPool threadPool(numThreads);
9230 std::vector < std::future< void > > results;
9231 // queue the work
9232 for (const auto &[name,symList] : *Doxygen::symbolMap)
9233 {
9234 for (const auto &def : symList)
9235 {
9237 if (dm && !isSymbolHidden(def) && !def->isArtificial() && def->isLinkableInProject())
9238 {
9239 auto processTooltip = [dm]() {
9240 dm->computeTooltip();
9241 };
9242 results.emplace_back(threadPool.queue(processTooltip));
9243 }
9244 }
9245 }
9246 // wait for the results
9247 for (auto &f : results)
9248 {
9249 f.get();
9250 }
9251 }
9252 else
9253 {
9254 for (const auto &[name,symList] : *Doxygen::symbolMap)
9255 {
9256 for (const auto &def : symList)
9257 {
9259 if (dm && !isSymbolHidden(def) && !def->isArtificial() && def->isLinkableInProject())
9260 {
9261 dm->computeTooltip();
9262 }
9263 }
9264 }
9265 }
9266}
9267
9268//----------------------------------------------------------------------------
9269
9271{
9272 for (const auto &cd : *Doxygen::classLinkedMap)
9273 {
9274 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9275 if (cdm)
9276 {
9277 cdm->setAnonymousEnumType();
9278 }
9279 }
9280}
9281
9282//----------------------------------------------------------------------------
9283
9284static void countMembers()
9285{
9286 for (const auto &cd : *Doxygen::classLinkedMap)
9287 {
9288 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9289 if (cdm)
9290 {
9291 cdm->countMembers();
9292 }
9293 }
9294
9295 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9296 {
9298 if (ndm)
9299 {
9300 ndm->countMembers();
9301 }
9302 }
9303
9304 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9305 {
9306 for (const auto &fd : *fn)
9307 {
9308 fd->countMembers();
9309 }
9310 }
9311
9312 for (const auto &gd : *Doxygen::groupLinkedMap)
9313 {
9314 gd->countMembers();
9315 }
9316
9317 auto &mm = ModuleManager::instance();
9318 mm.countMembers();
9319}
9320
9321
9322//----------------------------------------------------------------------------
9323// generate the documentation for all classes
9324
9325static void generateDocsForClassList(const std::vector<ClassDefMutable*> &classList)
9326{
9327 AUTO_TRACE();
9328 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9329 if (numThreads>1) // multi threaded processing
9330 {
9331 struct DocContext
9332 {
9333 DocContext(ClassDefMutable *cd_,const OutputList &ol_)
9334 : cd(cd_), ol(ol_) {}
9335 ClassDefMutable *cd;
9336 OutputList ol;
9337 };
9338 ThreadPool threadPool(numThreads);
9339 std::vector< std::future< std::shared_ptr<DocContext> > > results;
9340 for (const auto &cd : classList)
9341 {
9342 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
9343 if (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9344 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
9345 )
9346 {
9347 auto ctx = std::make_shared<DocContext>(cd,*g_outputList);
9348 auto processFile = [ctx]()
9349 {
9350 msg("Generating docs for compound {}...\n",ctx->cd->displayName());
9351
9352 // skip external references, anonymous compounds and
9353 // template instances
9354 if (!ctx->cd->isHidden() && !ctx->cd->isEmbeddedInOuterScope() &&
9355 ctx->cd->isLinkableInProject() && !ctx->cd->isImplicitTemplateInstance())
9356 {
9357 ctx->cd->writeDocumentation(ctx->ol);
9358 ctx->cd->writeMemberList(ctx->ol);
9359 }
9360
9361 // even for undocumented classes, the inner classes can be documented.
9362 ctx->cd->writeDocumentationForInnerClasses(ctx->ol);
9363 return ctx;
9364 };
9365 results.emplace_back(threadPool.queue(processFile));
9366 }
9367 }
9368 for (auto &f : results)
9369 {
9370 auto ctx = f.get();
9371 }
9372 }
9373 else // single threaded processing
9374 {
9375 for (const auto &cd : classList)
9376 {
9377 //printf("cd=%s getOuterScope=%p global=%p hidden=%d embeddedInOuterScope=%d\n",
9378 // qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope,cd->isHidden(),cd->isEmbeddedInOuterScope());
9379 if (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9380 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
9381 )
9382 {
9383 // skip external references, anonymous compounds and
9384 // template instances
9385 if ( !cd->isHidden() && !cd->isEmbeddedInOuterScope() &&
9386 cd->isLinkableInProject() && !cd->isImplicitTemplateInstance())
9387 {
9388 msg("Generating docs for compound {}...\n",cd->displayName());
9389
9390 cd->writeDocumentation(*g_outputList);
9391 cd->writeMemberList(*g_outputList);
9392 }
9393 // even for undocumented classes, the inner classes can be documented.
9394 cd->writeDocumentationForInnerClasses(*g_outputList);
9395 }
9396 }
9397 }
9398}
9399
9400static void addClassAndNestedClasses(std::vector<ClassDefMutable*> &list,ClassDefMutable *cd)
9401{
9402 list.push_back(cd);
9403 for (const auto &innerCdi : cd->getClasses())
9404 {
9405 ClassDefMutable *innerCd = toClassDefMutable(innerCdi);
9406 if (innerCd)
9407 {
9408 AUTO_TRACE("innerCd={} isLinkable={} isImplicitTemplateInstance={} protectLevelVisible={} embeddedInOuterScope={}",
9409 innerCd->name(),innerCd->isLinkableInProject(),innerCd->isImplicitTemplateInstance(),protectionLevelVisible(innerCd->protection()),
9410 innerCd->isEmbeddedInOuterScope());
9411 }
9412 if (innerCd && innerCd->isLinkableInProject() && !innerCd->isImplicitTemplateInstance() &&
9413 protectionLevelVisible(innerCd->protection()) &&
9414 !innerCd->isEmbeddedInOuterScope()
9415 )
9416 {
9417 list.push_back(innerCd);
9418 addClassAndNestedClasses(list,innerCd);
9419 }
9420 }
9421}
9422
9424{
9425 std::vector<ClassDefMutable*> classList;
9426 for (const auto &cdi : *Doxygen::classLinkedMap)
9427 {
9428 ClassDefMutable *cd = toClassDefMutable(cdi.get());
9429 if (cd && (cd->getOuterScope()==nullptr ||
9431 {
9432 addClassAndNestedClasses(classList,cd);
9433 }
9434 }
9435 for (const auto &cdi : *Doxygen::hiddenClassLinkedMap)
9436 {
9437 ClassDefMutable *cd = toClassDefMutable(cdi.get());
9438 if (cd && (cd->getOuterScope()==nullptr ||
9440 {
9441 addClassAndNestedClasses(classList,cd);
9442 }
9443 }
9444 generateDocsForClassList(classList);
9445}
9446
9447//----------------------------------------------------------------------------
9448
9450{
9451 for (const auto &cdi : *Doxygen::conceptLinkedMap)
9452 {
9454
9455 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
9456 if (cd &&
9457 (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9458 cd->getOuterScope()==Doxygen::globalScope // only look at global concepts
9459 ) && !cd->isHidden() && cd->isLinkableInProject()
9460 )
9461 {
9462 msg("Generating docs for concept {}...\n",cd->displayName());
9464 }
9465 }
9466}
9467
9468//----------------------------------------------------------------------------
9469
9471{
9472 for (const auto &mn : *Doxygen::memberNameLinkedMap)
9473 {
9474 for (const auto &imd : *mn)
9475 {
9476 MemberDefMutable *md = toMemberDefMutable(imd.get());
9477 //static int count=0;
9478 //printf("%04d Member '%s'\n",count++,qPrint(md->qualifiedName()));
9479 if (md && md->documentation().empty() && md->briefDescription().empty())
9480 { // no documentation yet
9481 const MemberDef *bmd = md->reimplements();
9482 while (bmd && bmd->documentation().empty() &&
9483 bmd->briefDescription().empty()
9484 )
9485 { // search up the inheritance tree for a documentation member
9486 //printf("bmd=%s class=%s\n",qPrint(bmd->name()),qPrint(bmd->getClassDef()->name()));
9487 bmd = bmd->reimplements();
9488 }
9489 if (bmd) // copy the documentation from the reimplemented member
9490 {
9491 md->setInheritsDocsFrom(bmd);
9492 md->setDocumentation(bmd->documentation(),bmd->docFile(),bmd->docLine());
9494 md->setBriefDescription(bmd->briefDescription(),bmd->briefFile(),bmd->briefLine());
9495 md->copyArgumentNames(bmd);
9497 }
9498 }
9499 }
9500 }
9501}
9502
9503//----------------------------------------------------------------------------
9504
9506{
9507 // for each file
9508 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9509 {
9510 for (const auto &fd : *fn)
9511 {
9512 fd->combineUsingRelations();
9513 }
9514 }
9515
9516 // for each namespace
9517 NamespaceDefSet visitedNamespaces;
9518 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9519 {
9521 if (ndm)
9522 {
9523 ndm->combineUsingRelations(visitedNamespaces);
9524 }
9525 }
9526}
9527
9528//----------------------------------------------------------------------------
9529
9531{
9532 // for each class
9533 for (const auto &cd : *Doxygen::classLinkedMap)
9534 {
9535 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9536 if (cdm)
9537 {
9539 }
9540 }
9541 // for each file
9542 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9543 {
9544 for (const auto &fd : *fn)
9545 {
9546 fd->addMembersToMemberGroup();
9547 }
9548 }
9549 // for each namespace
9550 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9551 {
9553 if (ndm)
9554 {
9556 }
9557 }
9558 // for each group
9559 for (const auto &gd : *Doxygen::groupLinkedMap)
9560 {
9561 gd->addMembersToMemberGroup();
9562 }
9564}
9565
9566//----------------------------------------------------------------------------
9567
9569{
9570 // for each class
9571 for (const auto &cd : *Doxygen::classLinkedMap)
9572 {
9573 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9574 if (cdm)
9575 {
9577 }
9578 }
9579 // for each file
9580 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9581 {
9582 for (const auto &fd : *fn)
9583 {
9584 fd->distributeMemberGroupDocumentation();
9585 }
9586 }
9587 // for each namespace
9588 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9589 {
9591 if (ndm)
9592 {
9594 }
9595 }
9596 // for each group
9597 for (const auto &gd : *Doxygen::groupLinkedMap)
9598 {
9599 gd->distributeMemberGroupDocumentation();
9600 }
9602}
9603
9604//----------------------------------------------------------------------------
9605
9607{
9608 // for each class
9609 for (const auto &cd : *Doxygen::classLinkedMap)
9610 {
9611 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9612 if (cdm)
9613 {
9615 }
9616 }
9617 // for each concept
9618 for (const auto &cd : *Doxygen::conceptLinkedMap)
9619 {
9620 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
9621 if (cdm)
9622 {
9624 }
9625 }
9626 // for each file
9627 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9628 {
9629 for (const auto &fd : *fn)
9630 {
9631 fd->findSectionsInDocumentation();
9632 }
9633 }
9634 // for each namespace
9635 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9636 {
9638 if (ndm)
9639 {
9641 }
9642 }
9643 // for each group
9644 for (const auto &gd : *Doxygen::groupLinkedMap)
9645 {
9646 gd->findSectionsInDocumentation();
9647 }
9648 // for each page
9649 for (const auto &pd : *Doxygen::pageLinkedMap)
9650 {
9651 pd->findSectionsInDocumentation();
9652 }
9653 // for each directory
9654 for (const auto &dd : *Doxygen::dirLinkedMap)
9655 {
9656 dd->findSectionsInDocumentation();
9657 }
9659 if (Doxygen::mainPage) Doxygen::mainPage->findSectionsInDocumentation();
9660}
9661
9662//----------------------------------------------------------------------
9663
9664
9666{
9667 // remove all references to classes from the cache
9668 // as there can be new template instances in the inheritance path
9669 // to this class. Optimization: only remove those classes that
9670 // have inheritance instances as direct or indirect sub classes.
9672
9673 // remove all cached typedef resolutions whose target is a
9674 // template class as this may now be a template instance
9675 // for each global function name
9676 for (const auto &fn : *Doxygen::functionNameLinkedMap)
9677 {
9678 // for each function with that name
9679 for (const auto &ifmd : *fn)
9680 {
9681 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
9682 if (fmd && fmd->isTypedefValCached())
9683 {
9684 const ClassDef *cd = fmd->getCachedTypedefVal();
9685 if (cd->isTemplate()) fmd->invalidateTypedefValCache();
9686 }
9687 }
9688 }
9689 // for each class method name
9690 for (const auto &nm : *Doxygen::memberNameLinkedMap)
9691 {
9692 // for each function with that name
9693 for (const auto &imd : *nm)
9694 {
9695 MemberDefMutable *md = toMemberDefMutable(imd.get());
9696 if (md && md->isTypedefValCached())
9697 {
9698 const ClassDef *cd = md->getCachedTypedefVal();
9699 if (cd->isTemplate()) md->invalidateTypedefValCache();
9700 }
9701 }
9702 }
9703}
9704
9705//----------------------------------------------------------------------------
9706
9708{
9709 // Remove all unresolved references to classes from the cache.
9710 // This is needed before resolving the inheritance relations, since
9711 // it would otherwise not find the inheritance relation
9712 // for C in the example below, as B::I was already found to be unresolvable
9713 // (which is correct if you ignore the inheritance relation between A and B).
9714 //
9715 // class A { class I {} };
9716 // class B : public A {};
9717 // class C : public B::I {};
9719
9720 // for each class method name
9721 for (const auto &nm : *Doxygen::memberNameLinkedMap)
9722 {
9723 // for each function with that name
9724 for (const auto &imd : *nm)
9725 {
9726 MemberDefMutable *md = toMemberDefMutable(imd.get());
9727 if (md)
9728 {
9730 }
9731 }
9732 }
9733
9734}
9735
9736//----------------------------------------------------------------------------
9737// Returns true if the entry and member definition have equal file names,
9738// otherwise false.
9739
9740static bool haveEqualFileNames(const Entry *root, const MemberDef *md)
9741{
9742 if (const FileDef *fd = md->getFileDef())
9743 {
9744 return fd->absFilePath() == root->fileName;
9745 }
9746 return false;
9747}
9748
9749//----------------------------------------------------------------------------
9750
9751static void addDefineDoc(const Entry *root, MemberDefMutable *md)
9752{
9753 md->setDocumentation(root->doc,root->docFile,root->docLine);
9754 md->setDocsForDefinition(!root->proto);
9755 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9756 if (md->inbodyDocumentation().empty())
9757 {
9759 }
9760 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
9761 {
9762 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
9763 md->setBodyDef(root->fileDef());
9764 }
9766 md->setMaxInitLines(root->initLines);
9768 md->setRefItems(root->sli);
9769 md->setRequirementReferences(root->rqli);
9770 md->addQualifiers(root->qualifiers);
9771 if (root->mGrpId!=-1) md->setMemberGroupId(root->mGrpId);
9772 addMemberToGroups(root,md);
9774}
9775
9776//----------------------------------------------------------------------------
9777
9779{
9780 if ((root->section.isDefineDoc() || root->section.isDefine()) && !root->name.empty())
9781 {
9782 //printf("found define '%s' '%s' brief='%s' doc='%s'\n",
9783 // qPrint(root->name),qPrint(root->args),qPrint(root->brief),qPrint(root->doc));
9784
9785 if (root->tagInfo() && !root->name.empty()) // define read from a tag file
9786 {
9787 auto md = createMemberDef(root->tagInfo()->tagName,1,1,
9788 "#define",root->name,root->args,DString(),
9789 Protection::Public,Specifier::Normal,false,Relationship::Member,MemberType::Define,
9790 ArgumentList(),ArgumentList(),"");
9791 auto mmd = toMemberDefMutable(md.get());
9792 mmd->setTagInfo(root->tagInfo());
9793 mmd->setLanguage(root->lang);
9794 mmd->addQualifiers(root->qualifiers);
9795 //printf("Searching for '%s' fd=%p\n",qPrint(filePathName),fd);
9796 mmd->setFileDef(root->parent()->fileDef());
9797 //printf("Adding member=%s\n",qPrint(md->name()));
9799 mn->push_back(std::move(md));
9800 }
9802 if (mn)
9803 {
9804 int count=0;
9805 for (const auto &md : *mn)
9806 {
9807 if (md->memberType()==MemberType::Define) count++;
9808 }
9809 if (count==1)
9810 {
9811 for (const auto &imd : *mn)
9812 {
9813 MemberDefMutable *md = toMemberDefMutable(imd.get());
9814 if (md && md->memberType()==MemberType::Define)
9815 {
9816 addDefineDoc(root,md);
9817 }
9818 }
9819 }
9820 else if (count>1 &&
9821 (!root->doc.empty() ||
9822 !root->brief.empty() ||
9823 root->bodyLine!=-1
9824 )
9825 )
9826 // multiple defines don't know where to add docs
9827 // but maybe they are in different files together with their documentation
9828 {
9829 for (const auto &imd : *mn)
9830 {
9831 MemberDefMutable *md = toMemberDefMutable(imd.get());
9832 if (md && md->memberType()==MemberType::Define)
9833 {
9834 if (haveEqualFileNames(root, md) || isEntryInGroupOfMember(root, md))
9835 // doc and define in the same file or group assume they belong together.
9836 {
9837 addDefineDoc(root,md);
9838 }
9839 }
9840 }
9841 //warn("define {} found in the following files:\n",root->name);
9842 //warn("Cannot determine where to add the documentation found "
9843 // "at line {} of file {}. \n",
9844 // root->startLine,root->fileName);
9845 }
9846 }
9847 else if (!root->doc.empty() || !root->brief.empty()) // define not found
9848 {
9849 bool preEnabled = Config_getBool(ENABLE_PREPROCESSING);
9850 if (preEnabled)
9851 {
9852 warn(root->fileName,root->startLine,"documentation for unknown define {} found.",root->name);
9853 }
9854 else
9855 {
9856 warn(root->fileName,root->startLine, "found documented #define {} but ignoring it because ENABLE_PREPROCESSING is NO.", root->name);
9857 }
9858 }
9859 }
9860 for (const auto &e : root->children()) findDefineDocumentation(e.get());
9861}
9862
9863//----------------------------------------------------------------------------
9864
9865static void findDirDocumentation(const Entry *root)
9866{
9867 if (root->section.isDirDoc())
9868 {
9869 DString normalizedName = root->name;
9870 normalizedName = substitute(normalizedName,"\\","/");
9871 //printf("root->docFile=%s normalizedName=%s\n",
9872 // qPrint(root->docFile),qPrint(normalizedName));
9873 if (root->docFile==normalizedName) // current dir?
9874 {
9875 if (size_t lastSlashPos=normalizedName.rfind('/'); lastSlashPos!=DString::npos) // strip file name
9876 {
9877 normalizedName=normalizedName.left(lastSlashPos);
9878 }
9879 }
9880 if (normalizedName.at(normalizedName.length()-1)!='/')
9881 {
9882 normalizedName+='/';
9883 }
9884 DirDef *matchingDir=nullptr;
9885 for (const auto &dir : *Doxygen::dirLinkedMap)
9886 {
9887 //printf("Dir: %s<->%s\n",qPrint(dir->name()),qPrint(normalizedName));
9888 if (dir->name().right(normalizedName.length())==normalizedName)
9889 {
9890 if (matchingDir)
9891 {
9892 warn(root->fileName,root->startLine,
9893 "\\dir command matches multiple directories.\n"
9894 " Applying the command for directory {}\n"
9895 " Ignoring the command for directory {}",
9896 matchingDir->name(),dir->name()
9897 );
9898 }
9899 else
9900 {
9901 matchingDir=dir.get();
9902 }
9903 }
9904 }
9905 if (matchingDir)
9906 {
9907 //printf("Match for with dir %s #anchor=%zu\n",qPrint(matchingDir->name()),root->anchors.size());
9908 matchingDir->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9909 matchingDir->setDocumentation(root->doc,root->docFile,root->docLine);
9910 matchingDir->setRefItems(root->sli);
9911 matchingDir->setRequirementReferences(root->rqli);
9912 matchingDir->addSectionsToDefinition(root->anchors);
9913 root->commandOverrides.apply_directoryGraph([&](bool b) { matchingDir->overrideDirectoryGraph(b); });
9914 addDirToGroups(root,matchingDir);
9915 }
9916 else
9917 {
9918 warn(root->fileName,root->startLine,"No matching directory found for command \\dir {}",normalizedName);
9919 }
9920 }
9921 for (const auto &e : root->children()) findDirDocumentation(e.get());
9922}
9923
9924//----------------------------------------------------------------------------
9926{
9927 if (root->section.isRequirementDoc())
9928 {
9930 }
9931 for (const auto &e : root->children()) buildRequirementsList(e.get());
9932}
9933
9934//----------------------------------------------------------------------------
9935// create a (sorted) list of separate documentation pages
9936
9937static void buildPageList(Entry *root)
9938{
9939 if (root->section.isPageDoc())
9940 {
9941 if (!root->name.empty())
9942 {
9943 addRelatedPage(root);
9944 }
9945 }
9946 else if (root->section.isMainpageDoc())
9947 {
9948 DString title=root->args.stripWhiteSpace();
9949 if (title.empty()) title=theTranslator->trMainPage();
9950 //DString name = Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9951 DString name = "index";
9952 addRefItem(root->sli,
9953 name,
9954 theTranslator->trPage(true,true),
9955 name,
9956 title,
9957 DString(),nullptr
9958 );
9959 }
9960 for (const auto &e : root->children()) buildPageList(e.get());
9961}
9962
9963// search for the main page defined in this project
9964static void findMainPage(Entry *root)
9965{
9966 if (root->section.isMainpageDoc())
9967 {
9968 if (Doxygen::mainPage==nullptr && root->tagInfo()==nullptr)
9969 {
9970 //printf("mainpage: docLine=%d startLine=%d\n",root->docLine,root->startLine);
9971 //printf("Found main page! \n======\n%s\n=======\n",qPrint(root->doc));
9972 DString title=root->args.stripWhiteSpace();
9973 if (title.empty()) title = Config_getString(PROJECT_NAME);
9974 //DString indexName=Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9975 DString indexName="index";
9977 indexName, root->brief+root->doc+root->inbodyDocs,title);
9978 //setFileNameForSections(root->anchors,"index",Doxygen::mainPage);
9979 Doxygen::mainPage->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9980 Doxygen::mainPage->setBodySegment(root->startLine,root->startLine,-1);
9981 Doxygen::mainPage->setFileName(indexName);
9982 Doxygen::mainPage->setLocalToc(root->localToc);
9984
9986 if (si)
9987 {
9988 if (!si->ref().empty()) // we are from a tag file
9989 {
9990 // a page name is a label as well! but should no be double either
9992 Doxygen::mainPage->name(),
9993 indexName,
9994 root->startLine,
9995 Doxygen::mainPage->title(),
9997 0); // level 0
9998 }
9999 else if (si->lineNr() != -1)
10000 {
10001 warn(root->fileName,root->startLine,"multiple use of section label '{}' for main page, (first occurrence: {}, line {})",
10002 Doxygen::mainPage->name(),si->fileName(),si->lineNr());
10003 }
10004 else
10005 {
10006 warn(root->fileName,root->startLine,"multiple use of section label '{}' for main page, (first occurrence: {})",
10007 Doxygen::mainPage->name(),si->fileName());
10008 }
10009 }
10010 else
10011 {
10012 // a page name is a label as well! but should no be double either
10014 Doxygen::mainPage->name(),
10015 indexName,
10016 root->startLine,
10017 Doxygen::mainPage->title(),
10019 0); // level 0
10020 }
10021 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
10022 }
10023 else if (root->tagInfo()==nullptr)
10024 {
10025 warn(root->fileName,root->startLine,
10026 "found more than one \\mainpage comment block! (first occurrence: {}, line {}), Skipping current block!",
10027 Doxygen::mainPage->docFile(),Doxygen::mainPage->getStartBodyLine());
10028 }
10029 }
10030 for (const auto &e : root->children()) findMainPage(e.get());
10031}
10032
10033// search for the main page imported via tag files and add only the section labels
10034static void findMainPageTagFiles(Entry *root)
10035{
10036 if (root->section.isMainpageDoc())
10037 {
10038 if (Doxygen::mainPage && root->tagInfo())
10039 {
10040 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
10041 }
10042 }
10043 for (const auto &e : root->children()) findMainPageTagFiles(e.get());
10044}
10045
10046static void computePageRelations(Entry *root)
10047{
10048 if ((root->section.isPageDoc() || root->section.isMainpageDoc()) && !root->name.empty())
10049 {
10050 PageDef *pd = root->section.isPageDoc() ?
10052 Doxygen::mainPage.get();
10053 if (pd)
10054 {
10055 for (const BaseInfo &bi : root->extends)
10056 {
10058 if (pd==subPd)
10059 {
10060 term("page defined {} with label {} is a direct "
10061 "subpage of itself! Please remove this cyclic dependency.\n",
10062 warn_line(pd->docFile(),pd->docLine()),pd->name());
10063 }
10064 else if (subPd)
10065 {
10066 pd->addInnerCompound(subPd);
10067 //printf("*** Added subpage relation: %s->%s\n",
10068 // qPrint(pd->name()),qPrint(subPd->name()));
10069 }
10070 }
10071 }
10072 }
10073 for (const auto &e : root->children()) computePageRelations(e.get());
10074}
10075
10077{
10078 for (const auto &pd : *Doxygen::pageLinkedMap)
10079 {
10080 Definition *ppd = pd->getOuterScope();
10081 while (ppd)
10082 {
10083 if (ppd==pd.get())
10084 {
10085 term("page defined {} with label {} is a subpage "
10086 "of itself! Please remove this cyclic dependency.\n",
10087 warn_line(pd->docFile(),pd->docLine()),pd->name());
10088 }
10089 ppd=ppd->getOuterScope();
10090 }
10091 }
10092}
10093
10094//----------------------------------------------------------------------------
10095
10097{
10098 for (const auto &si : SectionManager::instance())
10099 {
10100 //printf("si->label='%s' si->definition=%s si->fileName='%s'\n",
10101 // qPrint(si->label),si->definition?qPrint(si->definition->name()):"<none>",
10102 // qPrint(si->fileName));
10103 PageDef *pd=nullptr;
10104
10105 // hack: the items of a todo/test/bug/deprecated list are all fragments from
10106 // different files, so the resulting section's all have the wrong file
10107 // name (not from the todo/test/bug/deprecated list, but from the file in
10108 // which they are defined). We correct this here by looking at the
10109 // generated section labels!
10111 {
10112 DString label="_"+rl->listName(); // "_todo", "_test", ...
10113 if (si->label().left(label.length())==label)
10114 {
10115 si->setFileName(rl->listName());
10116 si->setGenerated(true);
10117 break;
10118 }
10119 }
10120
10121 //printf("start: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
10122 if (!si->generated())
10123 {
10124 // if this section is in a page and the page is in a group, then we
10125 // have to adjust the link file name to point to the group.
10126 if (!si->fileName().empty() &&
10127 (pd=Doxygen::pageLinkedMap->find(si->fileName())) &&
10128 pd->getGroupDef())
10129 {
10130 si->setFileName(pd->getGroupDef()->getOutputFileBase());
10131 }
10132
10133 if (si->definition())
10134 {
10135 // TODO: there should be one function in Definition that returns
10136 // the file to link to, so we can avoid the following tests.
10137 const GroupDef *gd=nullptr;
10138 if (si->definition()->definitionType()==Definition::TypeMember)
10139 {
10140 gd = (toMemberDef(si->definition()))->getGroupDef();
10141 }
10142
10143 if (gd)
10144 {
10145 si->setFileName(gd->getOutputFileBase());
10146 }
10147 else
10148 {
10149 //si->fileName=si->definition->getOutputFileBase();
10150 //printf("Setting si->fileName to %s\n",qPrint(si->fileName));
10151 }
10152 }
10153 }
10154 //printf("end: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
10155 }
10156}
10157
10158
10159
10160//----------------------------------------------------------------------------
10161// generate all separate documentation pages
10162
10163
10164static void generatePageDocs()
10165{
10166 //printf("documentedPages=%d real=%d\n",documentedPages,Doxygen::pageLinkedMap->count());
10167 if (Index::instance().numDocumentedPages()==0) return;
10168 for (const auto &pd : *Doxygen::pageLinkedMap)
10169 {
10170 if (!pd->getGroupDef() && !pd->isReference())
10171 {
10172 msg("Generating docs for page {}...\n",pd->name());
10173 pd->writeDocumentation(*g_outputList);
10174 }
10175 }
10176}
10177
10178//----------------------------------------------------------------------------
10179// create a (sorted) list & dictionary of example pages
10180
10181static void buildExampleList(Entry *root)
10182{
10183 if ((root->section.isExample() || root->section.isExampleLineno()) && !root->name.empty())
10184 {
10185 if (Doxygen::exampleLinkedMap->find(root->name))
10186 {
10187 warn(root->fileName,root->startLine,"Example {} was already documented. Ignoring documentation found here.",root->name);
10188 }
10189 else
10190 {
10192 createPageDef(root->fileName,root->startLine,
10193 root->name,root->brief+root->doc+root->inbodyDocs,root->args));
10194 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
10195 pd->setFileName(convertNameToFile(pd->name()+"-example",false,true));
10197 pd->setLanguage(root->lang);
10198 pd->setShowLineNo(root->section.isExampleLineno());
10199
10200 //we don't add example to groups
10201 //addExampleToGroups(root,pd);
10202 }
10203 }
10204 for (const auto &e : root->children()) buildExampleList(e.get());
10205}
10206
10207//----------------------------------------------------------------------------
10208// prints the Entry tree (for debugging)
10209
10210void printNavTree(Entry *root,int indent)
10211{
10213 {
10214 DString indentStr;
10215 indentStr.fill(' ',indent);
10216 Debug::print(Debug::Entries,0,"{}{} at {}:{} (sec={}, spec={})\n",
10217 indentStr.empty()?"":indentStr,
10218 root->name.empty()?"<empty>":root->name,
10219 root->fileName,root->startLine,
10220 root->section.to_string(),
10221 root->spec.to_string());
10222 for (const auto &e : root->children())
10223 {
10224 printNavTree(e.get(),indent+2);
10225 }
10226 }
10227}
10228
10229
10230//----------------------------------------------------------------------------
10231// prints the Sections tree (for debugging)
10232
10234{
10236 {
10237 for (const auto &si : SectionManager::instance())
10238 {
10239 Debug::print(Debug::Sections,0,"Section = {}, file = {}, title = {}, type = {}, ref = {}\n",
10240 si->label(),si->fileName(),si->title(),si->type().level(),si->ref());
10241 }
10242 }
10243}
10244
10245
10246//----------------------------------------------------------------------------
10247// generate the example documentation
10248
10250{
10252 for (const auto &pd : *Doxygen::exampleLinkedMap)
10253 {
10254 msg("Generating docs for example {}...\n",pd->name());
10255 SrcLangExt lang = getLanguageFromFileName(pd->name(), SrcLangExt::Unknown);
10256 if (lang != SrcLangExt::Unknown)
10257 {
10258 DString ext = getFileNameExtension(pd->name());
10259 auto intf = Doxygen::parserManager->getCodeParser(ext);
10260 intf->resetCodeParserState();
10261 }
10262 DString n=pd->getOutputFileBase();
10263 startFile(*g_outputList,n,false,n,pd->name());
10265 g_outputList->docify(pd->name());
10268 DString lineNoOptStr;
10269 if (pd->showLineNo())
10270 {
10271 lineNoOptStr="{lineno}";
10272 }
10273 g_outputList->generateDoc(pd->docFile(), // file
10274 pd->docLine(), // startLine
10275 pd.get(), // context
10276 nullptr, // memberDef
10277 (pd->briefDescription().empty()?"":pd->briefDescription()+"\n\n")+
10278 pd->documentation()+"\n\n\\include"+lineNoOptStr+" "+pd->name(), // docs
10279 DocOptions()
10280 .setIndexWords(true)
10281 .setExample(pd->name()));
10282 endFile(*g_outputList); // contains g_outputList->endContents()
10283 }
10285}
10286
10287//----------------------------------------------------------------------------
10288// generate module pages
10289
10291{
10292 for (const auto &gd : *Doxygen::groupLinkedMap)
10293 {
10294 if (!gd->isReference())
10295 {
10296 gd->writeDocumentation(*g_outputList);
10297 }
10298 }
10299}
10300
10301//----------------------------------------------------------------------------
10302// generate module pages
10303
10305{
10306 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10307 if (numThreads>1) // multi threaded processing
10308 {
10309 struct DocContext
10310 {
10311 DocContext(ClassDefMutable *cdm_,const OutputList &ol_)
10312 : cdm(cdm_), ol(ol_) {}
10313 ClassDefMutable *cdm;
10314 OutputList ol;
10315 };
10316 ThreadPool threadPool(numThreads);
10317 std::vector< std::future< std::shared_ptr<DocContext> > > results;
10318 // for each class in the namespace...
10319 for (const auto &cd : classList)
10320 {
10322 if (cdm)
10323 {
10324 auto ctx = std::make_shared<DocContext>(cdm,*g_outputList);
10325 auto processFile = [ctx]()
10326 {
10327 if ( ( ctx->cdm->isLinkableInProject() &&
10328 !ctx->cdm->isImplicitTemplateInstance()
10329 ) // skip external references, anonymous compounds and
10330 // template instances and nested classes
10331 && !ctx->cdm->isHidden() && !ctx->cdm->isEmbeddedInOuterScope()
10332 )
10333 {
10334 msg("Generating docs for compound {}...\n",ctx->cdm->displayName());
10335 ctx->cdm->writeDocumentation(ctx->ol);
10336 ctx->cdm->writeMemberList(ctx->ol);
10337 }
10338 ctx->cdm->writeDocumentationForInnerClasses(ctx->ol);
10339 return ctx;
10340 };
10341 results.emplace_back(threadPool.queue(processFile));
10342 }
10343 }
10344 // wait for the results
10345 for (auto &f : results)
10346 {
10347 auto ctx = f.get();
10348 }
10349 }
10350 else // single threaded processing
10351 {
10352 // for each class in the namespace...
10353 for (const auto &cd : classList)
10354 {
10356 if (cdm)
10357 {
10358 if ( ( cd->isLinkableInProject() &&
10359 !cd->isImplicitTemplateInstance()
10360 ) // skip external references, anonymous compounds and
10361 // template instances and nested classes
10362 && !cd->isHidden() && !cd->isEmbeddedInOuterScope()
10363 )
10364 {
10365 msg("Generating docs for compound {}...\n",cd->displayName());
10366
10369 }
10371 }
10372 }
10373 }
10374}
10375
10377{
10378 // for each concept in the namespace...
10379 for (const auto &cd : conceptList)
10380 {
10382 if ( cdm && cd->isLinkableInProject() && !cd->isHidden())
10383 {
10384 msg("Generating docs for concept {}...\n",cd->name());
10386 }
10387 }
10388}
10389
10391{
10392 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
10393
10394 //writeNamespaceIndex(*g_outputList);
10395
10396 // for each namespace...
10397 for (const auto &nd : *Doxygen::namespaceLinkedMap)
10398 {
10399 if (nd->isLinkableInProject())
10400 {
10402 if (ndm)
10403 {
10404 msg("Generating docs for namespace {}\n",nd->displayName());
10406 }
10407 }
10408
10409 generateNamespaceClassDocs(nd->getClasses());
10410 if (sliceOpt)
10411 {
10412 generateNamespaceClassDocs(nd->getInterfaces());
10413 generateNamespaceClassDocs(nd->getStructs());
10414 generateNamespaceClassDocs(nd->getExceptions());
10415 }
10416 generateNamespaceConceptDocs(nd->getConcepts());
10417 }
10418}
10419
10421{
10422 std::string oldDir = Dir::currentDirPath();
10423 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
10426 {
10427 err("failed to run html help compiler on {}\n", HtmlHelp::hhpFileName);
10428 }
10429 Dir::setCurrent(oldDir);
10430}
10431
10433{
10434 DString args = Qhp::qhpFileName + " -o \"" + Qhp::getQchFileName() + "\"";
10435 std::string oldDir = Dir::currentDirPath();
10436 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
10437
10438 DString qhgLocation=Config_getString(QHG_LOCATION);
10439 if (Debug::isFlagSet(Debug::Qhp)) // produce info for debugging
10440 {
10441 // run qhelpgenerator -v and extract the Qt version used
10442 DString cmd=qhgLocation+ " -v 2>&1";
10443 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
10444 FILE *f=Portable::popen(cmd,"r");
10445 if (!f)
10446 {
10447 err("could not execute {}\n",qhgLocation);
10448 }
10449 else
10450 {
10451 const size_t bufSize = 1024;
10452 char inBuf[bufSize+1];
10453 size_t numRead=fread(inBuf,1,bufSize,f);
10454 inBuf[numRead] = '\0';
10455 Debug::print(Debug::Qhp,0,"{}",inBuf);
10457
10458 int qtVersion=0;
10459 static const reg::Ex versionReg(R"(Qt (\d+)\.(\d+)\.(\d+))");
10460 reg::Match match;
10461 std::string s = inBuf;
10462 if (reg::search(s,match,versionReg))
10463 {
10464 qtVersion = 10000*DString(match[1].str()).toInt() +
10465 100*DString(match[2].str()).toInt() +
10466 DString(match[3].str()).toInt();
10467 }
10468 if (qtVersion>0 && (qtVersion<60000 || qtVersion >= 60205))
10469 {
10470 // dump the output of qhelpgenerator -c file.qhp
10471 // Qt<6 or Qt>=6.2.5 or higher, see https://bugreports.qt.io/browse/QTBUG-101070
10472 cmd=qhgLocation+ " -c " + Qhp::qhpFileName + " 2>&1";
10473 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
10474 f=Portable::popen(cmd,"r");
10475 if (!f)
10476 {
10477 err("could not execute {}\n",qhgLocation);
10478 }
10479 else
10480 {
10481 std::string output;
10482 while ((numRead=fread(inBuf,1,bufSize,f))>0)
10483 {
10484 inBuf[numRead] = '\0';
10485 output += inBuf;
10486 }
10488 Debug::print(Debug::Qhp,0,"{}",output);
10489 }
10490 }
10491 }
10492 }
10493
10494 if (Portable::system(qhgLocation, args, false))
10495 {
10496 err("failed to run qhelpgenerator on {}\n",Qhp::qhpFileName);
10497 }
10498 Dir::setCurrent(oldDir);
10499}
10500
10501//----------------------------------------------------------------------------
10502
10504{
10505 // check dot path
10506 DString dotPath = Config_getString(DOT_PATH);
10507 if (!dotPath.empty())
10508 {
10509 FileInfo fi(dotPath.str());
10510 if (!(fi.exists() && fi.isFile()) )// not an existing user specified path + exec
10511 {
10512 dotPath = dotPath+"/dot"+Portable::commandExtension();
10513 FileInfo dp(dotPath.str());
10514 if (!dp.exists() || !dp.isFile())
10515 {
10516 warn_uncond("the dot tool could not be found as '{}'\n",dotPath);
10517 dotPath = "dot";
10518 dotPath += Portable::commandExtension();
10519 }
10520 else if (dp.isRelative())
10521 {
10522 dotPath = fi.dirPath() + "/" + dotPath;
10523 }
10524 }
10525#if defined(_WIN32) // convert slashes
10526 size_t l=dotPath.length();
10527 for (size_t i=0;i<l;i++) if (dotPath.at(i)=='/') dotPath.at(i)='\\';
10528#endif
10529 }
10530 else
10531 {
10532 dotPath = "dot";
10533 dotPath += Portable::commandExtension();
10534 }
10535 Doxygen::verifiedDotPath = dotPath;
10537}
10538
10539//----------------------------------------------------------------------------
10540
10541/*! Generate a template version of the configuration file.
10542 * If the \a shortList parameter is true a configuration file without
10543 * comments will be generated.
10544 */
10545static void generateConfigFile(const DString &configFile,bool shortList,
10546 bool updateOnly=false)
10547{
10548 std::ofstream f;
10549 bool fileOpened=openOutputFile(configFile,f);
10550 bool writeToStdout=configFile=="-";
10551 if (fileOpened)
10552 {
10553 TextStream t(&f);
10554 Config::writeTemplate(t,shortList,updateOnly);
10555 if (!writeToStdout)
10556 {
10557 if (!updateOnly)
10558 {
10559 msg("\n\nConfiguration file '{}' created.\n\n",configFile);
10560 msg("Now edit the configuration file and enter\n\n");
10561 if (configFile!="Doxyfile" && configFile!="doxyfile")
10562 msg(" doxygen {}\n\n",configFile);
10563 else
10564 msg(" doxygen\n\n");
10565 msg("to generate the documentation for your project\n\n");
10566 }
10567 else
10568 {
10569 msg("\n\nConfiguration file '{}' updated.\n\n",configFile);
10570 }
10571 }
10572 }
10573 else
10574 {
10575 term("Cannot open file {} for writing\n",configFile);
10576 }
10577}
10578
10580{
10581 std::ofstream f;
10582 bool fileOpened=openOutputFile("-",f);
10583 if (fileOpened)
10584 {
10585 TextStream t(&f);
10586 Config::compareDoxyfile(t,diffList);
10587 }
10588 else
10589 {
10590 term("Cannot open stdout for writing\n");
10591 }
10592}
10593
10594//----------------------------------------------------------------------------
10595// read and parse a tag file
10596
10597static void readTagFile(const std::shared_ptr<Entry> &root,const DString &tagLine)
10598{
10599 DString fileName;
10600 DString destName;
10601 if (size_t eqPos = tagLine.find('='); eqPos!=DString::npos) // tag command contains a destination
10602 {
10603 fileName = tagLine.left(eqPos).stripWhiteSpace();
10604 destName = tagLine.mid(eqPos+1).stripWhiteSpace();
10605 if (fileName.empty() || destName.empty()) return;
10606 //printf("insert tagDestination %s->%s\n",qPrint(fi.fileName()),qPrint(destName));
10607 }
10608 else
10609 {
10610 fileName = tagLine;
10611 }
10612
10613 FileInfo fi(fileName.str());
10614 if (!fi.exists() || !fi.isFile())
10615 {
10616 err("Tag file '{}' does not exist or is not a file. Skipping it...\n",fileName);
10617 return;
10618 }
10619
10620 if (Doxygen::tagFileSet.find(fi.absFilePath()) != Doxygen::tagFileSet.end()) return;
10621
10622 Doxygen::tagFileSet.emplace(fi.absFilePath());
10623
10624 if (!destName.empty())
10625 {
10626 Doxygen::tagDestinationMap.emplace(fi.absFilePath(), destName.str());
10627 msg("Reading tag file '{}', location '{}'...\n",fileName,destName);
10628 }
10629 else
10630 {
10631 msg("Reading tag file '{}'...\n",fileName);
10632 }
10633
10634 parseTagFile(root,fi.absFilePath().c_str());
10635}
10636
10637//----------------------------------------------------------------------------
10639{
10640 StringVector latexExtraStyleSheet = Config_getList(LATEX_EXTRA_STYLESHEET);
10641 for (const auto &sheet : latexExtraStyleSheet)
10642 {
10643 std::string fileName = sheet;
10644 if (!fileName.empty())
10645 {
10646 FileInfo fi(fileName);
10647 if (!fi.exists())
10648 {
10649 err("Style sheet '{}' specified by LATEX_EXTRA_STYLESHEET does not exist!\n",fileName);
10650 }
10651 else if (fi.isDir())
10652 {
10653 err("Style sheet '{}' specified by LATEX_EXTRA_STYLESHEET is a directory, it has to be a file!\n", fileName);
10654 }
10655 else
10656 {
10657 DString destFileName = Config_getString(LATEX_OUTPUT)+"/"+fi.fileName();
10659 {
10660 destFileName += LATEX_STYLE_EXTENSION;
10661 }
10662 copyFile(fileName, destFileName);
10663 }
10664 }
10665 }
10666}
10667
10668//----------------------------------------------------------------------------
10669static void copyStyleSheet()
10670{
10671 DString htmlStyleSheet = Config_getString(HTML_STYLESHEET);
10672 if (!htmlStyleSheet.empty())
10673 {
10674 if (!htmlStyleSheet.startsWith("http:") && !htmlStyleSheet.startsWith("https:"))
10675 {
10676 FileInfo fi(htmlStyleSheet.str());
10677 if (!fi.exists())
10678 {
10679 err("Style sheet '{}' specified by HTML_STYLESHEET does not exist!\n",htmlStyleSheet);
10680 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
10681 }
10682 else if (fi.isDir())
10683 {
10684 err("Style sheet '{}' specified by HTML_STYLESHEET is a directory, it has to be a file!\n",htmlStyleSheet);
10685 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
10686 }
10687 else
10688 {
10689 DString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
10690 copyFile(htmlStyleSheet,destFileName);
10691 }
10692 }
10693 }
10694 StringVector htmlExtraStyleSheet = Config_getList(HTML_EXTRA_STYLESHEET);
10695 for (const auto &sheet : htmlExtraStyleSheet)
10696 {
10697 DString fileName(sheet);
10698 if (!fileName.empty() && !fileName.startsWith("http:") && !fileName.startsWith("https:"))
10699 {
10700 FileInfo fi(fileName.str());
10701 if (!fi.exists())
10702 {
10703 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET does not exist!\n",fileName);
10704 }
10705 else if (fi.fileName()=="doxygen.css" || fi.fileName()=="tabs.css" || fi.fileName()=="navtree.css")
10706 {
10707 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET is already a built-in stylesheet. Please use a different name\n",fi.fileName());
10708 }
10709 else if (fi.isDir())
10710 {
10711 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET is a directory, it has to be a file!\n",fileName);
10712 }
10713 else
10714 {
10715 DString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
10716 copyFile(fileName, destFileName);
10717 }
10718 }
10719 }
10720}
10721
10722static void copyLogo(const DString &outputOption, bool toIndex)
10723{
10724 DString projectLogo = projectLogoFile();
10725 if (!projectLogo.empty())
10726 {
10727 FileInfo fi(projectLogo.str());
10728 if (!fi.exists())
10729 {
10730 err("Project logo '{}' specified by PROJECT_LOGO does not exist!\n",projectLogo);
10731 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
10732 }
10733 else if (fi.isDir())
10734 {
10735 err("Project logo '{}' specified by PROJECT_LOGO is a directory, it has to be a file!\n",projectLogo);
10736 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
10737 }
10738 else
10739 {
10740 DString destFileName = outputOption+"/"+fi.fileName();
10741 copyFile(projectLogo,destFileName);
10742 if (toIndex) Doxygen::indexList->addImageFile(fi.fileName());
10743 }
10744 }
10745}
10746
10747static void copyIcon(const DString &outputOption, bool toIndex)
10748{
10749 DString projectIcon = Config_getString(PROJECT_ICON);
10750 if (!projectIcon.empty())
10751 {
10752 FileInfo fi(projectIcon.str());
10753 if (!fi.exists())
10754 {
10755 err("Project icon '{}' specified by PROJECT_ICON does not exist!\n",projectIcon);
10756 projectIcon = Config_updateString(PROJECT_ICON,""); // revert to the default
10757 }
10758 else if (fi.isDir())
10759 {
10760 err("Project icon '{}' specified by PROJECT_ICON is a directory, it has to be a file!\n",projectIcon);
10761 projectIcon = Config_updateString(PROJECT_ICON,""); // revert to the default
10762 }
10763 else
10764 {
10765 DString destFileName = outputOption+"/"+fi.fileName();
10766 copyFile(projectIcon,destFileName);
10767 if (toIndex) Doxygen::indexList->addImageFile(fi.fileName());
10768 }
10769 }
10770}
10771
10772static inline void copyExtraFiles(StringVector files,const DString &filesOption,const DString &outputOption, bool toIndex)
10773{
10774 for (const auto &fileName : files)
10775 {
10776 if (!fileName.empty())
10777 {
10778 FileInfo fi(fileName);
10779 if (!fi.exists())
10780 {
10781 err("Extra file '{}' specified in {} does not exist!\n", fileName,filesOption);
10782 }
10783 else if (fi.isDir())
10784 {
10785 err("Extra file '{}' specified in {} is a directory, it has to be a file!\n", fileName,filesOption);
10786 }
10787 else
10788 {
10789 DString destFileName = outputOption+"/"+fi.fileName();
10790 copyFile(fileName, destFileName);
10791 if (toIndex) Doxygen::indexList->addImageFile(fi.fileName());
10792 }
10793 }
10794 }
10795}
10796
10797//----------------------------------------------------------------------------
10798
10800{
10801 for (const auto &fn : *Doxygen::inputNameLinkedMap)
10802 {
10803 struct FileEntry
10804 {
10805 FileEntry(const DString &p,FileDef *fd) : path(p), fileDef(fd) {}
10806 DString path;
10807 FileDef *fileDef;
10808 };
10809
10810 // collect the entry for which to compute the longest common prefix (LCP) of the path
10811 std::vector<FileEntry> fileEntries;
10812 for (const auto &fd : *fn)
10813 {
10814 if (!fd->isReference()) // skip external references
10815 {
10816 fileEntries.emplace_back(fd->getPath(),fd.get());
10817 }
10818 }
10819
10820 size_t size = fileEntries.size();
10821
10822 if (size==1) // name if unique, so diskname is simply the name
10823 {
10824 FileDef *fd = fileEntries[0].fileDef;
10825 fd->setDiskName(fn->fileName());
10826 }
10827 else if (size>1) // multiple occurrences of the same file name
10828 {
10829 // sort the array
10830 std::stable_sort(fileEntries.begin(),
10831 fileEntries.end(),
10832 [](const FileEntry &fe1,const FileEntry &fe2)
10833 { return dstricmp_sort(fe1.path,fe2.path)<0; }
10834 );
10835
10836 // since the entries are sorted, the common prefix of the whole array is same
10837 // as the common prefix between the first and last entry
10838 const FileEntry &first = fileEntries[0];
10839 const FileEntry &last = fileEntries[size-1];
10840 int first_path_size = static_cast<int>(first.path.size())-1; // -1 to skip trailing slash
10841 int last_path_size = static_cast<int>(last.path.size())-1; // -1 to skip trailing slash
10842 int j=0;
10843 int i=0;
10844 for (i=0;i<first_path_size && i<last_path_size;i++)
10845 {
10846 if (first.path[i]=='/') j=i;
10847 if (first.path[i]!=last.path[i]) break;
10848 }
10849 if (i==first_path_size && i<last_path_size && last.path[i]=='/')
10850 {
10851 // case first='some/path' and last='some/path/more' => match is 'some/path'
10852 j=first_path_size;
10853 }
10854 else if (i==last_path_size && i<first_path_size && first.path[i]=='/')
10855 {
10856 // case first='some/path/more' and last='some/path' => match is 'some/path'
10857 j=last_path_size;
10858 }
10859
10860 // add non-common part of the path to the name
10861 for (auto &fileEntry : fileEntries)
10862 {
10863 DString prefix = fileEntry.path.right(fileEntry.path.length()-j-1);
10864 fileEntry.fileDef->setName(prefix+fn->fileName());
10865 //printf("!!!!!!!! non unique disk name=%s:%s\n",qPrint(prefix),fn->fileName());
10866 fileEntry.fileDef->setDiskName(prefix+fn->fileName());
10867 }
10868 }
10869 }
10870}
10871
10872
10873
10874//----------------------------------------------------------------------------
10875
10876static std::unique_ptr<OutlineParserInterface> getParserForFile(const DString &fn)
10877{
10878 DString fileName=fn;
10879 DString extension;
10880 size_t sep = fileName.rfind('/');
10881 size_t ei = fileName.rfind('.');
10882 if (ei!=DString::npos && (sep==DString::npos || ei>sep)) // matches dir/file.ext but not dir.1/file
10883 {
10884 extension=fileName.mid(ei);
10885 }
10886 else
10887 {
10888 extension = ".no_extension";
10889 }
10890
10891 return Doxygen::parserManager->getOutlineParser(extension);
10892}
10893
10894static std::shared_ptr<Entry> parseFile(OutlineParserInterface &parser,
10895 FileDef *fd,const DString &fn,
10896 ClangTUParser *clangParser,bool newTU)
10897{
10898 DString fileName=fn;
10899 AUTO_TRACE("fileName={}",fileName);
10900 DString extension;
10901 if (size_t ei = fileName.rfind('.'); ei!=DString::npos)
10902 {
10903 extension=fileName.mid(ei);
10904 }
10905 else
10906 {
10907 extension = ".no_extension";
10908 }
10909
10910 FileInfo fi(fileName.str());
10911 std::string preBuf;
10912
10913 if (Config_getBool(ENABLE_PREPROCESSING) &&
10914 parser.needsPreprocessing(extension))
10915 {
10916 Preprocessor preprocessor;
10917 StringVector includePath = Config_getList(INCLUDE_PATH);
10918 for (const auto &s : includePath)
10919 {
10920 std::string absPath = FileInfo(s).absFilePath();
10921 preprocessor.addSearchDir(absPath);
10922 }
10923 std::string inBuf;
10924 msg("Preprocessing {}...\n",fn);
10925 readInputFile(fileName,inBuf);
10926 addTerminalCharIfMissing(inBuf,'\n');
10927 preprocessor.processFile(fileName,inBuf,preBuf);
10928 }
10929 else // no preprocessing
10930 {
10931 msg("Reading {}...\n",fn);
10932 readInputFile(fileName,preBuf);
10933 addTerminalCharIfMissing(preBuf,'\n');
10934 }
10935
10936 std::string convBuf;
10937 convBuf.reserve(preBuf.size()+1024);
10938
10939 // convert multi-line C++ comments to C style comments
10940 convertCppComments(preBuf,convBuf,fileName.str());
10941
10942 std::shared_ptr<Entry> fileRoot = std::make_shared<Entry>();
10943 // use language parse to parse the file
10944 if (clangParser)
10945 {
10946 if (newTU) clangParser->parse();
10947 clangParser->switchToFile(fd);
10948 }
10949 parser.parseInput(fileName,convBuf.data(),fileRoot,clangParser);
10950 fileRoot->setFileDef(fd);
10951 return fileRoot;
10952}
10953
10954//! parse the list of input files
10955static void parseFilesMultiThreading(const std::shared_ptr<Entry> &root)
10956{
10957 AUTO_TRACE();
10958#if USE_LIBCLANG
10960 {
10961 StringUnorderedSet processedFiles;
10962
10963 // create a dictionary with files to process
10964 StringUnorderedSet filesToProcess;
10965 for (const auto &s : g_inputFiles)
10966 {
10967 filesToProcess.insert(s);
10968 }
10969
10970 std::mutex processedFilesLock;
10971 // process source files (and their include dependencies)
10972 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10973 msg("Processing input using {} threads.\n",numThreads);
10974 ThreadPool threadPool(numThreads);
10975 using FutureType = std::vector< std::shared_ptr<Entry> >;
10976 std::vector< std::future< FutureType > > results;
10977 for (const auto &s : g_inputFiles)
10978 {
10979 bool ambig = false;
10980 DString qs = s;
10982 ASSERT(fd!=nullptr);
10983 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt::Cpp) // this is a source file
10984 {
10985 // lambda representing the work to executed by a thread
10986 auto processFile = [qs,&filesToProcess,&processedFilesLock,&processedFiles]() {
10987 bool ambig_l = false;
10988 std::vector< std::shared_ptr<Entry> > roots;
10989 FileDef *fd_l = Doxygen::inputNameLinkedMap->findFileDef(qs,ambig_l);
10990 auto clangParser = ClangParser::instance()->createTUParser(fd_l);
10991 auto parser = getParserForFile(qs);
10992 auto fileRoot { parseFile(*parser.get(),fd_l,qs,clangParser.get(),true) };
10993 roots.push_back(fileRoot);
10994
10995 // Now process any include files in the same translation unit
10996 // first. When libclang is used this is much more efficient.
10997 for (auto incFile : clangParser->filesInSameTU())
10998 {
10999 DString qincFile = incFile;
11000 if (filesToProcess.find(incFile)!=filesToProcess.end())
11001 {
11002 bool needsToBeProcessed = false;
11003 {
11004 std::lock_guard<std::mutex> lock(processedFilesLock);
11005 needsToBeProcessed = processedFiles.find(incFile)==processedFiles.end();
11006 if (needsToBeProcessed) processedFiles.insert(incFile);
11007 }
11008 if (qincFile!=qs && needsToBeProcessed)
11009 {
11010 FileDef *ifd=Doxygen::inputNameLinkedMap->findFileDef(qincFile,ambig_l);
11011 if (ifd && !ifd->isReference())
11012 {
11013 //printf(" Processing %s in same translation unit as %s\n",incFile,qPrint(s));
11014 fileRoot = parseFile(*parser.get(),ifd,qincFile,clangParser.get(),false);
11015 roots.push_back(fileRoot);
11016 }
11017 }
11018 }
11019 }
11020 return roots;
11021 };
11022 // dispatch the work and collect the future results
11023 results.emplace_back(threadPool.queue(processFile));
11024 }
11025 }
11026 // synchronize with the Entry result lists produced and add them to the root
11027 for (auto &f : results)
11028 {
11029 auto l = f.get();
11030 for (auto &e : l)
11031 {
11032 root->moveToSubEntryAndKeep(e);
11033 }
11034 }
11035 // process remaining files
11036 results.clear();
11037 for (const auto &s : g_inputFiles)
11038 {
11039 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
11040 {
11041 // lambda representing the work to executed by a thread
11042 auto processFile = [s]() {
11043 bool ambig = false;
11044 DString qs = s;
11045 std::vector< std::shared_ptr<Entry> > roots;
11047 auto parser { getParserForFile(qs) };
11048 bool useClang = getLanguageFromFileName(qs)==SrcLangExt::Cpp;
11049 if (useClang)
11050 {
11051 auto clangParser = ClangParser::instance()->createTUParser(fd);
11052 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
11053 roots.push_back(fileRoot);
11054 }
11055 else
11056 {
11057 auto fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
11058 roots.push_back(fileRoot);
11059 }
11060 return roots;
11061 };
11062 results.emplace_back(threadPool.queue(processFile));
11063 }
11064 }
11065 // synchronize with the Entry result lists produced and add them to the root
11066 for (auto &f : results)
11067 {
11068 auto l = f.get();
11069 for (auto &e : l)
11070 {
11071 root->moveToSubEntryAndKeep(e);
11072 }
11073 }
11074 }
11075 else // normal processing
11076#endif
11077 {
11078 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
11079 msg("Processing input using {} threads.\n",numThreads);
11080 ThreadPool threadPool(numThreads);
11081 using FutureType = std::shared_ptr<Entry>;
11082 std::vector< std::future< FutureType > > results;
11083 for (const auto &s : g_inputFiles)
11084 {
11085 // lambda representing the work to executed by a thread
11086 auto processFile = [s]() {
11087 bool ambig = false;
11088 DString qs = s;
11090 auto parser = getParserForFile(qs);
11091 auto fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
11092 return fileRoot;
11093 };
11094 // dispatch the work and collect the future results
11095 results.emplace_back(threadPool.queue(processFile));
11096 }
11097 // synchronize with the Entry results produced and add them to the root
11098 for (auto &f : results)
11099 {
11100 root->moveToSubEntryAndKeep(f.get());
11101 }
11102 }
11103}
11104
11105//! parse the list of input files
11106static void parseFilesSingleThreading(const std::shared_ptr<Entry> &root)
11107{
11108 AUTO_TRACE();
11109#if USE_LIBCLANG
11111 {
11112 StringUnorderedSet processedFiles;
11113
11114 // create a dictionary with files to process
11115 StringUnorderedSet filesToProcess;
11116 for (const auto &s : g_inputFiles)
11117 {
11118 filesToProcess.insert(s);
11119 }
11120
11121 // process source files (and their include dependencies)
11122 for (const auto &s : g_inputFiles)
11123 {
11124 bool ambig = false;
11125 DString qs =s;
11127 ASSERT(fd!=nullptr);
11128 if (fd->isSource() && !fd->isReference() && getLanguageFromFileName(qs)==SrcLangExt::Cpp) // this is a source file
11129 {
11130 auto clangParser = ClangParser::instance()->createTUParser(fd);
11131 auto parser { getParserForFile(qs) };
11132 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
11133 root->moveToSubEntryAndKeep(fileRoot);
11134 processedFiles.insert(s);
11135
11136 // Now process any include files in the same translation unit
11137 // first. When libclang is used this is much more efficient.
11138 for (auto incFile : clangParser->filesInSameTU())
11139 {
11140 //printf(" file %s\n",qPrint(incFile));
11141 if (filesToProcess.find(incFile)!=filesToProcess.end() && // file need to be processed
11142 processedFiles.find(incFile)==processedFiles.end()) // and is not processed already
11143 {
11144 FileDef *ifd=Doxygen::inputNameLinkedMap->findFileDef(incFile,ambig);
11145 if (ifd && !ifd->isReference())
11146 {
11147 //printf(" Processing %s in same translation unit as %s\n",qPrint(incFile),qPrint(qs));
11148 fileRoot = parseFile(*parser.get(),ifd,incFile,clangParser.get(),false);
11149 root->moveToSubEntryAndKeep(fileRoot);
11150 processedFiles.insert(incFile);
11151 }
11152 }
11153 }
11154 }
11155 }
11156 // process remaining files
11157 for (const auto &s : g_inputFiles)
11158 {
11159 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
11160 {
11161 bool ambig = false;
11162 DString qs = s;
11164 if (getLanguageFromFileName(qs)==SrcLangExt::Cpp) // not yet processed
11165 {
11166 auto clangParser = ClangParser::instance()->createTUParser(fd);
11167 auto parser { getParserForFile(qs) };
11168 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
11169 root->moveToSubEntryAndKeep(fileRoot);
11170 }
11171 else
11172 {
11173 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(qs) };
11174 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
11175 root->moveToSubEntryAndKeep(fileRoot);
11176 }
11177 processedFiles.insert(s);
11178 }
11179 }
11180 }
11181 else // normal processing
11182#endif
11183 {
11184 for (const auto &s : g_inputFiles)
11185 {
11186 bool ambig = false;
11187 DString qs = s;
11189 ASSERT(fd!=nullptr);
11190 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(qs) };
11191 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
11192 root->moveToSubEntryAndKeep(std::move(fileRoot));
11193 }
11194 }
11195}
11196
11197// resolves a path that may include symlinks, if a recursive symlink is
11198// found an empty string is returned.
11199static std::string resolveSymlink(const std::string &path)
11200{
11201 size_t sepPos=0;
11202 size_t oldPos=0;
11203 StringUnorderedSet nonSymlinks;
11204 StringUnorderedSet known;
11205 DString result(path);
11206 DString oldPrefix = "/";
11207 do
11208 {
11209#if defined(_WIN32)
11210 // UNC path, skip server and share name
11211 if (sepPos==0 && (result.startsWith("//") || result.startsWith("\\\\")))
11212 sepPos = result.find('/',2);
11213 if (sepPos!=DString::npos)
11214 sepPos = result.find('/',sepPos+1);
11215#else
11216 sepPos = result.find('/',sepPos+1);
11217#endif
11218 DString prefix = sepPos==DString::npos ? result : result.left(sepPos);
11219 if (nonSymlinks.find(prefix.str())==nonSymlinks.end())
11220 {
11221 FileInfo fi(prefix.str());
11222 if (fi.isSymLink())
11223 {
11224 DString target = fi.readLink();
11225 bool isRelative = FileInfo(target.str()).isRelative();
11226 if (isRelative)
11227 {
11228 target = Dir::cleanDirPath(oldPrefix.str()+"/"+target.str());
11229 }
11230 if (sepPos!=DString::npos)
11231 {
11232 if (fi.isDir() && !target.empty() && target.at(target.length()-1)!='/')
11233 {
11234 target+='/';
11235 }
11236 target+=result.mid(sepPos);
11237 }
11238 result = Dir::cleanDirPath(target.str());
11239 if (known.find(result.str())!=known.end()) return std::string(); // recursive symlink!
11240 known.insert(result.str());
11241 if (isRelative)
11242 {
11243 sepPos = oldPos;
11244 }
11245 else // link to absolute path
11246 {
11247 sepPos = 0;
11248 oldPrefix = "/";
11249 }
11250 }
11251 else
11252 {
11253 nonSymlinks.insert(prefix.str());
11254 oldPrefix = prefix;
11255 }
11256 oldPos = sepPos;
11257 }
11258 }
11259 while (sepPos!=DString::npos);
11260 return Dir::cleanDirPath(result.str());
11261}
11262
11264
11265//----------------------------------------------------------------------------
11266// Read all files matching at least one pattern in 'patList' in the
11267// directory represented by 'fi'.
11268// The directory is read iff the recursiveFlag is set.
11269// The contents of all files is append to the input string
11270
11271static void readDir(FileInfo *fi,
11272 FileNameLinkedMap *fnMap,
11273 StringUnorderedSet *exclSet,
11274 const StringVector *patList,
11275 const StringVector *exclPatList,
11276 StringVector *resultList,
11277 StringUnorderedSet *resultSet,
11278 bool errorIfNotExist,
11279 bool recursive,
11280 StringUnorderedSet *killSet,
11281 StringUnorderedSet *paths
11282 )
11283{
11284 std::string dirName = fi->absFilePath();
11285 if (paths && !dirName.empty())
11286 {
11287 paths->insert(dirName);
11288 }
11289 //printf("%s isSymLink()=%d\n",qPrint(dirName),fi->isSymLink());
11290 if (fi->isSymLink())
11291 {
11292 dirName = resolveSymlink(dirName);
11293 if (dirName.empty())
11294 {
11295 //printf("RECURSIVE SYMLINK: %s\n",qPrint(dirName));
11296 return; // recursive symlink
11297 }
11298 }
11299
11300 if (g_pathsVisited.find(dirName)!=g_pathsVisited.end())
11301 {
11302 //printf("PATH ALREADY VISITED: %s\n",qPrint(dirName));
11303 return; // already visited path
11304 }
11305 g_pathsVisited.insert(dirName);
11306
11307 Dir dir(dirName);
11308 msg("Searching for files in directory {}\n", fi->absFilePath());
11309 //printf("killSet=%p count=%d\n",killSet,killSet ? (int)killSet->count() : -1);
11310
11311 StringVector dirResultList;
11312
11313 bool caseSenseNames = useCaseSenseNames();
11314
11315 // Process the entries in a fixed order, so that the order in which FileDefs
11316 // are created (which e.g. determines the SHORT_NAMES numbering) and in which
11317 // subdirectories are visited does not depend on the file system.
11318 StringVector entries;
11319 for (const auto &dirEntry : dir.iterator())
11320 {
11321 entries.push_back(dirEntry.path());
11322 }
11323 std::sort(entries.begin(),entries.end());
11324
11325 for (const auto &entryPath : entries)
11326 {
11327 FileInfo cfi(entryPath);
11328 auto checkPatterns = [&]() -> bool
11329 {
11330 return (patList==nullptr || cfi.match(*patList,caseSenseNames)) &&
11331 (exclPatList==nullptr || !cfi.match(*exclPatList,caseSenseNames)) &&
11332 (killSet==nullptr || killSet->find(cfi.absFilePath())==killSet->end());
11333 };
11334
11335 if (exclSet==nullptr || exclSet->find(cfi.absFilePath())==exclSet->end())
11336 { // file should not be excluded
11337 //printf("killSet->find(%s)\n",qPrint(cfi->absFilePath()));
11338 if (Config_getBool(EXCLUDE_SYMLINKS) && cfi.isSymLink())
11339 {
11340 }
11341 else if (!cfi.exists() || !cfi.isReadable())
11342 {
11343 if (errorIfNotExist && checkPatterns())
11344 {
11345 warn_uncond("source '{}' is not a readable file or directory... skipping.\n",cfi.absFilePath());
11346 }
11347 }
11348 else if (cfi.isFile() && checkPatterns())
11349 {
11350 std::string name=cfi.fileName();
11351 std::string path=cfi.dirPath()+"/";
11352 std::string fullName=path+name;
11353 if (fnMap)
11354 {
11355 auto fd = createFileDef(path,name);
11356 FileName *fn=nullptr;
11357 if (!name.empty())
11358 {
11359 fn = fnMap->add(name);
11360 fn->push_back(std::move(fd));
11361 }
11362 }
11363 dirResultList.push_back(fullName);
11364 if (resultSet) resultSet->insert(fullName);
11365 if (killSet) killSet->insert(fullName);
11366 }
11367 else if (recursive &&
11368 cfi.isDir() &&
11369 (exclPatList==nullptr || !cfi.match(*exclPatList,caseSenseNames)) &&
11370 cfi.fileName().at(0)!='.') // skip "." ".." and ".dir"
11371 {
11372 FileInfo acfi(cfi.absFilePath());
11373 readDir(&acfi,fnMap,exclSet,
11374 patList,exclPatList,&dirResultList,resultSet,errorIfNotExist,
11375 recursive,killSet,paths);
11376 }
11377 }
11378 }
11379 if (resultList && !dirResultList.empty())
11380 {
11381 // sort the resulting list to make the order platform independent.
11382 std::stable_sort(dirResultList.begin(),
11383 dirResultList.end(),
11384 [](const auto &f1,const auto &f2) { return dstricmp_sort(f1.c_str(),f2.c_str())<0; });
11385
11386 // append the sorted results to resultList
11387 resultList->insert(resultList->end(), dirResultList.begin(), dirResultList.end());
11388 }
11389}
11390
11391
11392//----------------------------------------------------------------------------
11393// read a file or all files in a directory and append their contents to the
11394// input string. The names of the files are appended to the 'fiList' list.
11395
11397 FileNameLinkedMap *fnMap,
11398 StringUnorderedSet *exclSet,
11399 const StringVector *patList,
11400 const StringVector *exclPatList,
11401 StringVector *resultList,
11402 StringUnorderedSet *resultSet,
11403 bool recursive,
11404 bool errorIfNotExist,
11405 StringUnorderedSet *killSet,
11406 StringUnorderedSet *paths
11407 )
11408{
11409 //printf("killSet count=%d\n",killSet ? (int)killSet->size() : -1);
11410 // strip trailing slashes
11411 if (s.empty()) return;
11412
11413 g_pathsVisited.clear();
11414
11415 FileInfo fi(s.str());
11416 //printf("readFileOrDirectory(%s)\n",s);
11417 {
11418 if (exclSet==nullptr || exclSet->find(fi.absFilePath())==exclSet->end())
11419 {
11420 if (Config_getBool(EXCLUDE_SYMLINKS) && fi.isSymLink())
11421 {
11422 }
11423 else if (!fi.exists() || !fi.isReadable())
11424 {
11425 if (errorIfNotExist)
11426 {
11427 warn_uncond("source '{}' is not a readable file or directory... skipping.\n",s);
11428 }
11429 }
11430 else if (fi.isFile())
11431 {
11432 std::string dirPath = fi.dirPath(true);
11433 std::string filePath = fi.absFilePath();
11434 if (paths && !dirPath.empty())
11435 {
11436 paths->insert(dirPath);
11437 }
11438 //printf("killSet.find(%s)=%d\n",qPrint(fi.absFilePath()),killSet.find(fi.absFilePath())!=killSet.end());
11439 if (killSet==nullptr || killSet->find(filePath)==killSet->end())
11440 {
11441 std::string name=fi.fileName();
11442 if (fnMap)
11443 {
11444 auto fd = createFileDef(dirPath+"/",name);
11445 if (!name.empty())
11446 {
11447 FileName *fn = fnMap->add(name);
11448 fn->push_back(std::move(fd));
11449 }
11450 }
11451 if (resultList || resultSet)
11452 {
11453 if (resultList) resultList->push_back(filePath);
11454 if (resultSet) resultSet->insert(filePath);
11455 }
11456
11457 if (killSet) killSet->insert(fi.absFilePath());
11458 }
11459 }
11460 else if (fi.isDir()) // readable dir
11461 {
11462 readDir(&fi,fnMap,exclSet,patList,
11463 exclPatList,resultList,resultSet,errorIfNotExist,
11464 recursive,killSet,paths);
11465 }
11466 }
11467 }
11468}
11469
11470//----------------------------------------------------------------------------
11471
11473{
11474 DString anchor;
11476 {
11477 MemberDef *md = toMemberDef(d);
11478 anchor=":"+md->anchor();
11479 }
11480 DString scope;
11481 DString fn = d->getOutputFileBase();
11484 {
11485 scope = fn;
11486 }
11487 t << "REPLACE INTO symbols (symbol_id,scope_id,name,file,line) VALUES('"
11488 << fn+anchor << "','"
11489 << scope << "','"
11490 << d->name() << "','"
11491 << d->getDefFileName() << "','"
11492 << d->getDefLine()
11493 << "');\n";
11494}
11495
11496static void dumpSymbolMap()
11497{
11498 std::ofstream f = Portable::openOutputStream("symbols.sql");
11499 if (f.is_open())
11500 {
11501 TextStream t(&f);
11502 for (const auto &[name,symList] : *Doxygen::symbolMap)
11503 {
11504 for (const auto &def : symList)
11505 {
11506 dumpSymbol(t,def);
11507 }
11508 }
11509 }
11510}
11511
11512// print developer options of Doxygen
11513static void devUsage()
11514{
11516 msg("Developer parameters:\n");
11517 msg(" -m dump symbol map\n");
11518 msg(" -b making messages output unbuffered\n");
11519 msg(" -c <file> process input file as a comment block and produce HTML output\n");
11520#if ENABLE_TRACING
11521 msg(" -t [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout)\n");
11522 msg(" -t_time [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout),\n"
11523 " and include time and thread information\n");
11524#endif
11525 msg(" -d <level> enable a debug level, such as (multiple invocations of -d are possible):\n");
11527}
11528
11529
11530//----------------------------------------------------------------------------
11531// print the version of Doxygen
11532
11533static void version(const bool extended)
11534{
11536 DString versionString = getFullVersion();
11537 msg("{}\n",versionString);
11538 if (extended)
11539 {
11540 DString extVers;
11541 if (!extVers.empty()) extVers+= ", ";
11542 extVers += "sqlite3 ";
11543 extVers += sqlite3_libversion();
11544#if USE_LIBCLANG
11545 if (!extVers.empty()) extVers+= ", ";
11546 extVers += "clang support ";
11547 extVers += CLANG_VERSION_STRING;
11548#endif
11549 if (!extVers.empty())
11550 {
11551 if (size_t lastComma = extVers.rfind(','); lastComma != DString::npos)
11552 {
11553 extVers = extVers.replace(lastComma,1," and");
11554 }
11555 msg(" with {}.\n",extVers);
11556 }
11557 }
11558}
11559
11560//----------------------------------------------------------------------------
11561// print the usage of Doxygen
11562
11563static void usage(const DString &name,const DString &versionString)
11564{
11566 msg("Doxygen version {0}\nCopyright Dimitri van Heesch 1997-2025\n\n"
11567 "You can use Doxygen in a number of ways:\n\n"
11568 "1) Use Doxygen to generate a template configuration file*:\n"
11569 " {1} [-s] -g [configName]\n\n"
11570 "2) Use Doxygen to update an old configuration file*:\n"
11571 " {1} [-s] -u [configName]\n\n"
11572 "3) Use Doxygen to generate documentation using an existing "
11573 "configuration file*:\n"
11574 " {1} [configName]\n\n"
11575 "4) Use Doxygen to generate a template file controlling the layout of the\n"
11576 " generated documentation:\n"
11577 " {1} -l [layoutFileName]\n\n"
11578 " In case layoutFileName is omitted DoxygenLayout.xml will be used as filename.\n"
11579 " If - is used for layoutFileName Doxygen will write to standard output.\n\n"
11580 "5) Use Doxygen to generate a template style sheet file for RTF, HTML or Latex.\n"
11581 " RTF: {1} -w rtf styleSheetFile\n"
11582 " HTML: {1} -w html headerFile footerFile styleSheetFile [configFile]\n"
11583 " LaTeX: {1} -w latex headerFile footerFile styleSheetFile [configFile]\n\n"
11584 "6) Use Doxygen to generate a rtf extensions file\n"
11585 " {1} -e rtf extensionsFile\n\n"
11586 " If - is used for extensionsFile Doxygen will write to standard output.\n\n"
11587 "7) Use Doxygen to compare the used configuration file with the template configuration file\n"
11588 " {1} -x [configFile]\n\n"
11589 " Use Doxygen to compare the used configuration file with the template configuration file\n"
11590 " without replacing the environment variables or CMake type replacement variables\n"
11591 " {1} -x_noenv [configFile]\n\n"
11592 "8) Use Doxygen to show a list of built-in emojis.\n"
11593 " {1} -f emoji outputFileName\n\n"
11594 " If - is used for outputFileName Doxygen will write to standard output.\n\n"
11595 "*) If -s is specified the comments of the configuration items in the config file will be omitted.\n"
11596 " If configName is omitted 'Doxyfile' will be used as a default.\n"
11597 " If - is used for configFile Doxygen will write / read the configuration to /from standard output / input.\n\n"
11598 "If -q is used for a Doxygen documentation run, Doxygen will see this as if QUIET=YES has been set.\n\n"
11599 "-v print version string, -V print extended version information\n"
11600 "-h,-? prints usage help information\n"
11601 "{1} -d prints additional usage flags for debugging purposes\n",versionString,name);
11602}
11603
11604//----------------------------------------------------------------------------
11605// read the argument of option 'c' from the comment argument list and
11606// update the option index 'optInd'.
11607
11608static const char *getArg(int argc,char **argv,int &optInd)
11609{
11610 char *s=nullptr;
11611 if (dstrlen(&argv[optInd][2])>0)
11612 s=&argv[optInd][2];
11613 else if (optInd+1<argc && argv[optInd+1][0]!='-')
11614 s=argv[++optInd];
11615 return s;
11616}
11617
11618//----------------------------------------------------------------------------
11619
11620/** @brief /dev/null outline parser */
11622{
11623 public:
11624 void parseInput(const DString &/* file */, const char * /* buf */,const std::shared_ptr<Entry> &, ClangTUParser*) override {}
11625 bool needsPreprocessing(const DString &) const override { return false; }
11626 void parsePrototype(const DString &) override {}
11627};
11628
11629
11630template<class T> std::function< std::unique_ptr<T>() > make_parser_factory()
11631{
11632 return []() { return std::make_unique<T>(); };
11633}
11634
11636{
11637 initResources();
11638 DString lang = Portable::getenv("LC_ALL");
11639 if (!lang.empty()) Portable::setenv("LANG",lang);
11640 std::setlocale(LC_ALL,"");
11641 std::setlocale(LC_CTYPE,"C"); // to get isspace(0xA0)==0, needed for UTF-8
11642 std::setlocale(LC_NUMERIC,"C");
11643
11645
11669
11670 // register any additional parsers here...
11671
11673
11674#if USE_LIBCLANG
11676#endif
11685 Doxygen::pageLinkedMap = new PageLinkedMap; // all doc pages
11686 Doxygen::exampleLinkedMap = new PageLinkedMap; // all examples
11687 //Doxygen::tagDestinationDict.setAutoDelete(true);
11689
11690 // initialization of these globals depends on
11691 // configuration switches so we need to postpone these
11692 Doxygen::globalScope = nullptr;
11702
11703}
11704
11737
11738void readConfiguration(int argc, char **argv)
11739{
11740 DString versionString = getFullVersion();
11741
11742 // helper that calls \a func to write to file \a fileName via a TextStream
11743 auto writeFile = [](const char *fileName,std::function<void(TextStream&)> func) -> bool
11744 {
11745 std::ofstream f;
11746 if (openOutputFile(fileName,f))
11747 {
11748 TextStream t(&f);
11749 func(t);
11750 return true;
11751 }
11752 return false;
11753 };
11754
11755
11756 /**************************************************************************
11757 * Handle arguments *
11758 **************************************************************************/
11759
11760 int optInd=1;
11761 DString configName;
11762 DString traceName;
11763 bool genConfig=false;
11764 bool shortList=false;
11765 bool traceTiming=false;
11767 bool updateConfig=false;
11768 bool quiet = false;
11769 while (optInd<argc && argv[optInd][0]=='-' &&
11770 (isalpha(argv[optInd][1]) || argv[optInd][1]=='?' ||
11771 argv[optInd][1]=='-')
11772 )
11773 {
11774 switch(argv[optInd][1])
11775 {
11776 case 'g':
11777 {
11778 genConfig=true;
11779 }
11780 break;
11781 case 'l':
11782 {
11783 DString layoutName;
11784 if (optInd+1>=argc)
11785 {
11786 layoutName="DoxygenLayout.xml";
11787 }
11788 else
11789 {
11790 layoutName=argv[optInd+1];
11791 }
11792 writeDefaultLayoutFile(layoutName);
11794 exit(0);
11795 }
11796 break;
11797 case 'c':
11798 if (optInd+1>=argc) // no file name given
11799 {
11800 msg("option \"-c\" is missing the file name to read\n");
11801 devUsage();
11803 exit(1);
11804 }
11805 else
11806 {
11807 g_commentFileName=argv[optInd+1];
11808 optInd++;
11809 }
11810 g_singleComment=true;
11811 quiet=true;
11812 break;
11813 case 'd':
11814 {
11815 DString debugLabel=getArg(argc,argv,optInd);
11816 if (debugLabel.empty())
11817 {
11818 devUsage();
11820 exit(0);
11821 }
11822 int retVal = Debug::setFlagStr(debugLabel);
11823 if (!retVal)
11824 {
11825 msg("option \"-d\" has unknown debug specifier: \"{}\".\n",debugLabel);
11826 devUsage();
11828 exit(1);
11829 }
11830 }
11831 break;
11832 case 't':
11833 {
11834#if ENABLE_TRACING
11835 if (!strcmp(argv[optInd]+1,"t_time"))
11836 {
11837 traceTiming = true;
11838 }
11839 else if (!strcmp(argv[optInd]+1,"t"))
11840 {
11841 traceTiming = false;
11842 }
11843 else
11844 {
11845 err("option should be \"-t\" or \"-t_time\", found: \"{}\".\n",argv[optInd]);
11847 exit(1);
11848 }
11849 if (optInd+1>=argc || argv[optInd+1][0] == '-') // no file name given
11850 {
11851 traceName="stdout";
11852 }
11853 else
11854 {
11855 traceName=argv[optInd+1];
11856 optInd++;
11857 }
11858#else
11859 err("support for option \"-t\" has not been compiled in (use a debug build or a release build with tracing enabled).\n");
11861 exit(1);
11862#endif
11863 }
11864 break;
11865 case 'x':
11866 if (!strcmp(argv[optInd]+1,"x_noenv")) diffList=Config::CompareMode::CompressedNoEnv;
11867 else if (!strcmp(argv[optInd]+1,"x")) diffList=Config::CompareMode::Compressed;
11868 else
11869 {
11870 err("option should be \"-x\" or \"-x_noenv\", found: \"{}\".\n",argv[optInd]);
11872 exit(1);
11873 }
11874 break;
11875 case 's':
11876 shortList=true;
11877 break;
11878 case 'u':
11879 updateConfig=true;
11880 break;
11881 case 'e':
11882 {
11883 DString formatName=getArg(argc,argv,optInd);
11884 if (formatName.empty())
11885 {
11886 err("option \"-e\" is missing format specifier rtf.\n");
11888 exit(1);
11889 }
11890 if (dstricmp(formatName.data(),"rtf")==0)
11891 {
11892 if (optInd+1>=argc)
11893 {
11894 err("option \"-e rtf\" is missing an extensions file name\n");
11896 exit(1);
11897 }
11898 writeFile(argv[optInd+1],RTFGenerator::writeExtensionsFile);
11900 exit(0);
11901 }
11902 err("option \"-e\" has invalid format specifier.\n");
11904 exit(1);
11905 }
11906 break;
11907 case 'f':
11908 {
11909 DString listName=getArg(argc,argv,optInd);
11910 if (listName.empty())
11911 {
11912 err("option \"-f\" is missing list specifier.\n");
11914 exit(1);
11915 }
11916 if (dstricmp(listName.data(),"emoji")==0)
11917 {
11918 if (optInd+1>=argc)
11919 {
11920 err("option \"-f emoji\" is missing an output file name\n");
11922 exit(1);
11923 }
11924 writeFile(argv[optInd+1],[](TextStream &t) { EmojiEntityMapper::instance().writeEmojiFile(t); });
11926 exit(0);
11927 }
11928 err("option \"-f\" has invalid list specifier.\n");
11930 exit(1);
11931 }
11932 break;
11933 case 'w':
11934 {
11935 DString formatName=getArg(argc,argv,optInd);
11936 if (formatName.empty())
11937 {
11938 err("option \"-w\" is missing format specifier rtf, html or latex\n");
11940 exit(1);
11941 }
11942 if (dstricmp(formatName.data(),"rtf")==0)
11943 {
11944 if (optInd+1>=argc)
11945 {
11946 err("option \"-w rtf\" is missing a style sheet file name\n");
11948 exit(1);
11949 }
11950 if (!writeFile(argv[optInd+1],RTFGenerator::writeStyleSheetFile))
11951 {
11952 err("error opening RTF style sheet file {}!\n",argv[optInd+1]);
11954 exit(1);
11955 }
11957 exit(0);
11958 }
11959 else if (dstricmp(formatName.data(),"html")==0)
11960 {
11961 Config::init();
11962 if (optInd+4<argc || FileInfo("Doxyfile").exists() || FileInfo("doxyfile").exists())
11963 // explicit config file mentioned or default found on disk
11964 {
11965 DString df = optInd+4<argc ? argv[optInd+4] : (FileInfo("Doxyfile").exists() ? DString("Doxyfile") : DString("doxyfile"));
11966 if (!Config::parse(df)) // parse the config file
11967 {
11968 err("error opening or reading configuration file {}!\n",argv[optInd+4]);
11970 exit(1);
11971 }
11972 }
11973 if (optInd+3>=argc)
11974 {
11975 err("option \"-w html\" does not have enough arguments\n");
11977 exit(1);
11978 }
11979 Config::postProcess(true);
11982 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11983 writeFile(argv[optInd+1],[&](TextStream &t) { HtmlGenerator::writeHeaderFile(t,argv[optInd+3]); });
11984 writeFile(argv[optInd+2],HtmlGenerator::writeFooterFile);
11985 writeFile(argv[optInd+3],HtmlGenerator::writeStyleSheetFile);
11987 exit(0);
11988 }
11989 else if (dstricmp(formatName.data(),"latex")==0)
11990 {
11991 Config::init();
11992 if (optInd+4<argc || FileInfo("Doxyfile").exists() || FileInfo("doxyfile").exists())
11993 {
11994 DString df = optInd+4<argc ? argv[optInd+4] : (FileInfo("Doxyfile").exists() ? DString("Doxyfile") : DString("doxyfile"));
11995 if (!Config::parse(df))
11996 {
11997 err("error opening or reading configuration file {}!\n",argv[optInd+4]);
11999 exit(1);
12000 }
12001 }
12002 if (optInd+3>=argc)
12003 {
12004 err("option \"-w latex\" does not have enough arguments\n");
12006 exit(1);
12007 }
12008 Config::postProcess(true);
12011 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
12012 writeFile(argv[optInd+1],LatexGenerator::writeHeaderFile);
12013 writeFile(argv[optInd+2],LatexGenerator::writeFooterFile);
12014 writeFile(argv[optInd+3],LatexGenerator::writeStyleSheetFile);
12016 exit(0);
12017 }
12018 else
12019 {
12020 err("Illegal format specifier \"{}\": should be one of rtf, html or latex\n",formatName);
12022 exit(1);
12023 }
12024 }
12025 break;
12026 case 'm':
12027 g_dumpSymbolMap = true;
12028 break;
12029 case 'v':
12030 version(false);
12032 exit(0);
12033 break;
12034 case 'V':
12035 version(true);
12037 exit(0);
12038 break;
12039 case '-':
12040 if (dstrcmp(&argv[optInd][2],"help")==0)
12041 {
12042 usage(argv[0],versionString);
12043 exit(0);
12044 }
12045 else if (dstrcmp(&argv[optInd][2],"version")==0)
12046 {
12047 version(false);
12049 exit(0);
12050 }
12051 else if ((dstrcmp(&argv[optInd][2],"Version")==0) ||
12052 (dstrcmp(&argv[optInd][2],"VERSION")==0))
12053 {
12054 version(true);
12056 exit(0);
12057 }
12058 else
12059 {
12060 err("Unknown option \"-{}\"\n",&argv[optInd][1]);
12061 usage(argv[0],versionString);
12062 exit(1);
12063 }
12064 break;
12065 case 'b':
12066 setvbuf(stdout,nullptr,_IONBF,0);
12067 break;
12068 case 'q':
12069 quiet = true;
12070 break;
12071 case 'h':
12072 case '?':
12073 usage(argv[0],versionString);
12074 exit(0);
12075 break;
12076 default:
12077 err("Unknown option \"-{:c}\"\n",argv[optInd][1]);
12078 usage(argv[0],versionString);
12079 exit(1);
12080 }
12081 optInd++;
12082 }
12083
12084 /**************************************************************************
12085 * Parse or generate the config file *
12086 **************************************************************************/
12087
12088 initTracing(traceName.data(),traceTiming);
12089 TRACE("Doxygen version used: {}",getFullVersion());
12090 Config::init();
12091
12092 FileInfo configFileInfo1("Doxyfile"),configFileInfo2("doxyfile");
12093 if (optInd>=argc)
12094 {
12095 if (configFileInfo1.exists())
12096 {
12097 configName="Doxyfile";
12098 }
12099 else if (configFileInfo2.exists())
12100 {
12101 configName="doxyfile";
12102 }
12103 else if (genConfig)
12104 {
12105 configName="Doxyfile";
12106 }
12107 else
12108 {
12109 err("Doxyfile not found and no input file specified!\n");
12110 usage(argv[0],versionString);
12111 exit(1);
12112 }
12113 }
12114 else
12115 {
12116 FileInfo fi(argv[optInd]);
12117 if (fi.exists() || dstrcmp(argv[optInd],"-")==0 || genConfig)
12118 {
12119 configName=argv[optInd];
12120 }
12121 else
12122 {
12123 err("configuration file {} not found!\n",argv[optInd]);
12124 usage(argv[0],versionString);
12125 exit(1);
12126 }
12127 }
12128
12129 if (genConfig)
12130 {
12131 generateConfigFile(configName,shortList);
12133 exit(0);
12134 }
12135
12136 if (!Config::parse(configName,updateConfig,diffList))
12137 {
12138 err("could not open or read configuration file {}!\n",configName);
12140 exit(1);
12141 }
12142
12143 if (diffList!=Config::CompareMode::Full)
12144 {
12146 compareDoxyfile(diffList);
12148 exit(0);
12149 }
12150
12151 if (updateConfig)
12152 {
12154 generateConfigFile(configName,shortList,true);
12156 exit(0);
12157 }
12158
12159 /* Perlmod wants to know the path to the config file.*/
12160 FileInfo configFileInfo(configName.str());
12161 setPerlModDoxyfile(configFileInfo.absFilePath());
12162
12163 /* handle -q option */
12164 if (quiet) Config_updateBool(QUIET,true);
12165}
12166
12167/** check and resolve config options */
12169{
12170 AUTO_TRACE();
12171
12172 Config::postProcess(false);
12176}
12177
12178/** adjust globals that depend on configuration settings. */
12180{
12181 AUTO_TRACE();
12182 Doxygen::globalNamespaceDef = createNamespaceDef("<globalScope>",1,1,"<globalScope>");
12193
12194 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
12195
12196 /* Set the global html file extension. */
12197 Doxygen::htmlFileExtension = Config_getString(HTML_FILE_EXTENSION);
12198
12199
12201 Config_getBool(CALLER_GRAPH) ||
12202 Config_getBool(REFERENCES_RELATION) ||
12203 Config_getBool(REFERENCED_BY_RELATION);
12204
12205 /**************************************************************************
12206 * Add custom extension mappings
12207 **************************************************************************/
12208
12209 StringVector extMaps = Config_getList(EXTENSION_MAPPING);
12210 for (const auto &mapping : extMaps)
12211 {
12212 DString mapStr = mapping;
12213 if (size_t i=mapStr.find('='); i==DString::npos)
12214 {
12215 continue;
12216 }
12217 else
12218 {
12219 DString ext = mapStr.left(i).stripWhiteSpace().lower();
12220 DString language = mapStr.mid(i+1).stripWhiteSpace().lower();
12221 if (ext.empty() || language.empty())
12222 {
12223 continue;
12224 }
12225
12226 if (!updateLanguageMapping(ext,language))
12227 {
12228 err("Failed to map file extension '{}' to unsupported language '{}'.\n"
12229 "Check the EXTENSION_MAPPING setting in the config file.\n",
12230 ext,language);
12231 }
12232 else
12233 {
12234 msg("Adding custom extension mapping: '{}' will be treated as language '{}'\n",
12235 ext,language);
12236 }
12237 }
12238 }
12239 // create input file exncodings
12240
12241 // check INPUT_ENCODING
12242 void *cd = portable_iconv_open("UTF-8",Config_getString(INPUT_ENCODING).data());
12243 if (cd==reinterpret_cast<void *>(-1))
12244 {
12245 term("unsupported character conversion: '{}'->'UTF-8': {}\n"
12246 "Check the 'INPUT_ENCODING' setting in the config file!\n",
12247 Config_getString(INPUT_ENCODING),strerror(errno));
12248 }
12249 else
12250 {
12252 }
12253
12254 // check and split INPUT_FILE_ENCODING
12255 StringVector fileEncod = Config_getList(INPUT_FILE_ENCODING);
12256 for (const auto &mapping : fileEncod)
12257 {
12258 DString mapStr = mapping;
12259 if (size_t i=mapStr.find('='); i==DString::npos)
12260 {
12261 continue;
12262 }
12263 else
12264 {
12265 DString pattern = mapStr.left(i).stripWhiteSpace().lower();
12266 DString encoding = mapStr.mid(i+1).stripWhiteSpace().lower();
12267 if (pattern.empty() || encoding.empty())
12268 {
12269 continue;
12270 }
12271 cd = portable_iconv_open("UTF-8",encoding.data());
12272 if (cd==reinterpret_cast<void *>(-1))
12273 {
12274 term("unsupported character conversion: '{}'->'UTF-8': {}\n"
12275 "Check the 'INPUT_FILE_ENCODING' setting in the config file!\n",
12276 encoding,strerror(errno));
12277 }
12278 else
12279 {
12281 }
12282
12283 Doxygen::inputFileEncodingList.emplace_back(pattern, encoding);
12284 }
12285 }
12286
12287 // add predefined macro name to a dictionary
12288 StringVector expandAsDefinedList = Config_getList(EXPAND_AS_DEFINED);
12289 for (const auto &s : expandAsDefinedList)
12290 {
12292 }
12293
12294 // read aliases and store them in a dictionary
12295 readAliases();
12296
12297 // store number of spaces in a tab into Doxygen::spaces
12298 int tabSize = Config_getInt(TAB_SIZE);
12299 Doxygen::spaces.resize(tabSize);
12300 for (int sp=0; sp<tabSize; sp++) Doxygen::spaces.at(sp)=' ';
12301 Doxygen::spaces.at(tabSize)='\0';
12302}
12303
12304#ifdef HAS_SIGNALS
12305static void stopDoxygen(int)
12306{
12307 signal(SIGINT,SIG_DFL); // Re-register signal handler for default action
12308 Dir thisDir;
12309 msg("Cleaning up...\n");
12310 if (!Doxygen::filterDBFileName.empty())
12311 {
12312 thisDir.remove(Doxygen::filterDBFileName.str());
12313 }
12314 killpg(0,SIGINT);
12316 exitTracing();
12317 exit(1);
12318}
12319#endif
12320
12321static void writeTagFile()
12322{
12323 DString generateTagFile = Config_getString(GENERATE_TAGFILE);
12324 if (generateTagFile.empty()) return;
12325
12326 std::ofstream f = Portable::openOutputStream(generateTagFile);
12327 if (!f.is_open())
12328 {
12329 err("cannot open tag file {} for writing\n", generateTagFile);
12330 return;
12331 }
12332 TextStream tagFile(&f);
12333 tagFile << "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>\n";
12334 tagFile << "<tagfile doxygen_version=\"" << getDoxygenVersion() << "\"";
12335 std::string gitVersion = getGitVersion();
12336 if (!gitVersion.empty())
12337 {
12338 tagFile << " doxygen_gitid=\"" << gitVersion << "\"";
12339 }
12340 tagFile << ">\n";
12341
12342 // for each file
12343 for (const auto &fn : *Doxygen::inputNameLinkedMap)
12344 {
12345 for (const auto &fd : *fn)
12346 {
12347 if (fd->isLinkableInProject()) fd->writeTagFile(tagFile);
12348 }
12349 }
12350 // for each class
12351 for (const auto &cd : *Doxygen::classLinkedMap)
12352 {
12353 ClassDefMutable *cdm = toClassDefMutable(cd.get());
12354 if (cdm && cdm->isLinkableInProject())
12355 {
12356 cdm->writeTagFile(tagFile);
12357 }
12358 }
12359 // for each concept
12360 for (const auto &cd : *Doxygen::conceptLinkedMap)
12361 {
12362 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
12363 if (cdm && cdm->isLinkableInProject())
12364 {
12365 cdm->writeTagFile(tagFile);
12366 }
12367 }
12368 // for each namespace
12369 for (const auto &nd : *Doxygen::namespaceLinkedMap)
12370 {
12372 if (ndm && nd->isLinkableInProject())
12373 {
12374 ndm->writeTagFile(tagFile);
12375 }
12376 }
12377 // for each group
12378 for (const auto &gd : *Doxygen::groupLinkedMap)
12379 {
12380 if (gd->isLinkableInProject()) gd->writeTagFile(tagFile);
12381 }
12382 // for each module
12383 for (const auto &mod : ModuleManager::instance().modules())
12384 {
12385 if (mod->isLinkableInProject()) mod->writeTagFile(tagFile);
12386 }
12387 // for each page
12388 for (const auto &pd : *Doxygen::pageLinkedMap)
12389 {
12390 if (pd->isLinkableInProject()) pd->writeTagFile(tagFile);
12391 }
12392 // for requirements
12394 // for each directory
12395 for (const auto &dd : *Doxygen::dirLinkedMap)
12396 {
12397 if (dd->isLinkableInProject()) dd->writeTagFile(tagFile);
12398 }
12399 if (Doxygen::mainPage) Doxygen::mainPage->writeTagFile(tagFile);
12400
12401 tagFile << "</tagfile>\n";
12402}
12403
12404static void exitDoxygen() noexcept
12405{
12406 if (!g_successfulRun) // premature exit
12407 {
12408 Dir thisDir;
12409 msg("Exiting...\n");
12410 if (!Doxygen::filterDBFileName.empty())
12411 {
12412 thisDir.remove(Doxygen::filterDBFileName.str());
12413 }
12414 }
12415}
12416
12417static DString createOutputDirectory(const DString &baseDirName,
12418 const DString &formatDirName,
12419 const char *defaultDirName)
12420{
12421 DString result = formatDirName;
12422 if (result.empty())
12423 {
12424 result = baseDirName + defaultDirName;
12425 }
12426 else if (formatDirName[0]!='/' && (formatDirName.length()==1 || formatDirName[1]!=':'))
12427 {
12428 result.prepend(baseDirName+"/");
12429 }
12430 Dir formatDir(result.str());
12431 if (!formatDir.exists() && !formatDir.mkdir(result.str()))
12432 {
12433 term("Could not create output directory {}\n", result);
12434 }
12435 return result;
12436}
12437
12439{
12440 StringUnorderedSet killSet;
12441
12442 StringVector exclPatterns = Config_getList(EXCLUDE_PATTERNS);
12443 bool alwaysRecursive = Config_getBool(RECURSIVE);
12444 StringUnorderedSet excludeNameSet;
12445
12446 // gather names of all files in the include path
12447 g_s.begin("Searching for include files...\n");
12448 killSet.clear();
12449 StringVector includePathList = Config_getList(INCLUDE_PATH);
12450 for (const auto &s : includePathList)
12451 {
12452 size_t plSize = Config_getList(INCLUDE_FILE_PATTERNS).size();
12453 StringVector pl = plSize==0 ? Config_getList(FILE_PATTERNS) :
12454 Config_getList(INCLUDE_FILE_PATTERNS);
12455 readFileOrDirectory(s, // s
12457 nullptr, // exclSet
12458 &pl, // patList
12459 &exclPatterns, // exclPatList
12460 nullptr, // resultList
12461 nullptr, // resultSet
12462 false, // INCLUDE_PATH isn't recursive
12463 true, // errorIfNotExist
12464 &killSet); // killSet
12465 }
12466 g_s.end();
12467
12468 g_s.begin("Searching for example files...\n");
12469 killSet.clear();
12470 StringVector examplePathList = Config_getList(EXAMPLE_PATH);
12471 for (const auto &s : examplePathList)
12472 {
12473 StringVector patterns = Config_getList(EXAMPLE_PATTERNS);
12474 readFileOrDirectory(s, // s
12476 nullptr, // exclSet
12477 &patterns, // patList
12478 nullptr, // exclPatList
12479 nullptr, // resultList
12480 nullptr, // resultSet
12481 (alwaysRecursive || Config_getBool(EXAMPLE_RECURSIVE)), // recursive
12482 true, // errorIfNotExist
12483 &killSet); // killSet
12484 }
12485 g_s.end();
12486
12487 g_s.begin("Searching for images...\n");
12488 killSet.clear();
12489 StringVector imagePathList=Config_getList(IMAGE_PATH);
12490 for (const auto &s : imagePathList)
12491 {
12492 readFileOrDirectory(s, // s
12494 nullptr, // exclSet
12495 nullptr, // patList
12496 nullptr, // exclPatList
12497 nullptr, // resultList
12498 nullptr, // resultSet
12499 alwaysRecursive, // recursive
12500 true, // errorIfNotExist
12501 &killSet); // killSet
12502 }
12503 g_s.end();
12504
12505 g_s.begin("Searching for dot files...\n");
12506 killSet.clear();
12507 StringVector dotFileList=Config_getList(DOTFILE_DIRS);
12508 for (const auto &s : dotFileList)
12509 {
12510 readFileOrDirectory(s, // s
12512 nullptr, // exclSet
12513 nullptr, // patList
12514 nullptr, // exclPatList
12515 nullptr, // resultList
12516 nullptr, // resultSet
12517 alwaysRecursive, // recursive
12518 true, // errorIfNotExist
12519 &killSet); // killSet
12520 }
12521 g_s.end();
12522
12523 g_s.begin("Searching for msc files...\n");
12524 killSet.clear();
12525 StringVector mscFileList=Config_getList(MSCFILE_DIRS);
12526 for (const auto &s : mscFileList)
12527 {
12528 readFileOrDirectory(s, // s
12530 nullptr, // exclSet
12531 nullptr, // patList
12532 nullptr, // exclPatList
12533 nullptr, // resultList
12534 nullptr, // resultSet
12535 alwaysRecursive, // recursive
12536 true, // errorIfNotExist
12537 &killSet); // killSet
12538 }
12539 g_s.end();
12540
12541 g_s.begin("Searching for dia files...\n");
12542 killSet.clear();
12543 StringVector diaFileList=Config_getList(DIAFILE_DIRS);
12544 for (const auto &s : diaFileList)
12545 {
12546 readFileOrDirectory(s, // s
12548 nullptr, // exclSet
12549 nullptr, // patList
12550 nullptr, // exclPatList
12551 nullptr, // resultList
12552 nullptr, // resultSet
12553 alwaysRecursive, // recursive
12554 true, // errorIfNotExist
12555 &killSet); // killSet
12556 }
12557 g_s.end();
12558
12559 g_s.begin("Searching for plantuml files...\n");
12560 killSet.clear();
12561 StringVector plantUmlFileList=Config_getList(PLANTUMLFILE_DIRS);
12562 for (const auto &s : plantUmlFileList)
12563 {
12564 readFileOrDirectory(s, // s
12566 nullptr, // exclSet
12567 nullptr, // patList
12568 nullptr, // exclPatList
12569 nullptr, // resultList
12570 nullptr, // resultSet
12571 alwaysRecursive, // recursive
12572 true, // errorIfNotExist
12573 &killSet); // killSet
12574 }
12575 g_s.end();
12576
12577 g_s.begin("Searching for mermaid files...\n");
12578 killSet.clear();
12579 StringVector mermaidFileList=Config_getList(MERMAIDFILE_DIRS);
12580 for (const auto &s : mermaidFileList)
12581 {
12582 readFileOrDirectory(s, // s
12584 nullptr, // exclSet
12585 nullptr, // patList
12586 nullptr, // exclPatList
12587 nullptr, // resultList
12588 nullptr, // resultSet
12589 alwaysRecursive, // recursive
12590 true, // errorIfNotExist
12591 &killSet); // killSet
12592 }
12593 g_s.end();
12594
12595 g_s.begin("Searching for files to exclude\n");
12596 StringVector excludeList = Config_getList(EXCLUDE);
12597 for (const auto &s : excludeList)
12598 {
12599 StringVector filePatterns = Config_getList(FILE_PATTERNS);
12600 readFileOrDirectory(s, // s
12601 nullptr, // fnDict
12602 nullptr, // exclSet
12603 &filePatterns, // patList
12604 nullptr, // exclPatList
12605 nullptr, // resultList
12606 &excludeNameSet, // resultSet
12607 alwaysRecursive, // recursive
12608 false); // errorIfNotExist
12609 }
12610 g_s.end();
12611
12612 /**************************************************************************
12613 * Determine Input Files *
12614 **************************************************************************/
12615
12616 g_s.begin("Searching INPUT for files to process...\n");
12617 killSet.clear();
12618 Doxygen::inputPaths.clear();
12619 StringVector inputList=Config_getList(INPUT);
12620 for (const auto &s : inputList)
12621 {
12622 DString path = s;
12623 size_t l = path.length();
12624 if (l>0)
12625 {
12626 // strip trailing slashes
12627 if (path.at(l-1)=='\\' || path.at(l-1)=='/') path=path.left(l-1);
12628
12629 StringVector filePatterns = Config_getList(FILE_PATTERNS);
12631 path, // s
12633 &excludeNameSet, // exclSet
12634 &filePatterns, // patList
12635 &exclPatterns, // exclPatList
12636 &g_inputFiles, // resultList
12637 nullptr, // resultSet
12638 alwaysRecursive, // recursive
12639 true, // errorIfNotExist
12640 &killSet, // killSet
12641 &Doxygen::inputPaths); // paths
12642 }
12643 }
12644
12645 // Sort the FileDef objects by full path to get a predictable ordering over multiple runs
12646 for (auto &fileName : *Doxygen::inputNameLinkedMap)
12647 {
12648 if (fileName->size()>1)
12649 {
12650 std::stable_sort(fileName->begin(),fileName->end(),[](const auto &f1,const auto &f2)
12651 {
12652 return dstricmp_sort(f1->absFilePath(),f2->absFilePath())<0;
12653 });
12654 }
12655 }
12656 std::stable_sort(Doxygen::inputNameLinkedMap->begin(),
12658 [](const auto &f1,const auto &f2)
12659 {
12660 return dstricmp_sort(f1->front()->absFilePath(),f2->front()->absFilePath())<0;
12661 });
12662 if (Doxygen::inputNameLinkedMap->empty())
12663 {
12664 warn_uncond("No files to be processed, please check your settings, in particular INPUT, FILE_PATTERNS, and RECURSIVE\n");
12665 }
12666 g_s.end();
12667}
12668
12669
12671{
12672 if (Config_getBool(MARKDOWN_SUPPORT))
12673 {
12674 DString mdfileAsMainPage = Config_getString(USE_MDFILE_AS_MAINPAGE);
12675 if (mdfileAsMainPage.empty()) return;
12676 FileInfo fi(mdfileAsMainPage.data());
12677 if (!fi.exists())
12678 {
12679 warn_uncond("Specified markdown mainpage '{}' does not exist\n",mdfileAsMainPage);
12680 return;
12681 }
12682 bool ambig = false;
12683 if (Doxygen::inputNameLinkedMap->findFileDef(fi.absFilePath(),ambig)==nullptr)
12684 {
12685 warn_uncond("Specified markdown mainpage '{}' has not been defined as input file\n",mdfileAsMainPage);
12686 return;
12687 }
12688 }
12689}
12690
12692{
12693 AUTO_TRACE();
12694 std::atexit(exitDoxygen);
12695
12696 Portable::correctPath(Config_getList(EXTERNAL_TOOL_PATH));
12697
12698#if USE_LIBCLANG
12699 Doxygen::clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING);
12700#endif
12701
12702 // we would like to show the versionString earlier, but we first have to handle the configuration file
12703 // to know the value of the QUIET setting.
12704 DString versionString = getFullVersion();
12705 msg("Doxygen version used: {}\n",versionString);
12706
12708
12709 /**************************************************************************
12710 * Make sure the output directory exists
12711 **************************************************************************/
12712 DString outputDirectory = Config_getString(OUTPUT_DIRECTORY);
12713 if (!g_singleComment)
12714 {
12715 if (outputDirectory.empty())
12716 {
12717 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,Dir::currentDirPath());
12718 }
12719 else
12720 {
12721 Dir dir(outputDirectory.str());
12722 if (!dir.exists())
12723 {
12725 if (!dir.mkdir(outputDirectory.str()))
12726 {
12727 term("tag OUTPUT_DIRECTORY: Output directory '{}' does not "
12728 "exist and cannot be created\n",outputDirectory);
12729 }
12730 else
12731 {
12732 msg("Notice: Output directory '{}' does not exist. "
12733 "I have created it for you.\n", outputDirectory);
12734 }
12735 dir.setPath(outputDirectory.str());
12736 }
12737 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,dir.absPath());
12738 }
12739 }
12740 AUTO_TRACE_ADD("outputDirectory={}",outputDirectory);
12741
12742 /**************************************************************************
12743 * Initialize global lists and dictionaries
12744 **************************************************************************/
12745
12746#ifdef HAS_SIGNALS
12747 signal(SIGINT, stopDoxygen);
12748#endif
12749
12750 uint32_t pid = Portable::pid();
12751 Doxygen::filterDBFileName.sprintf("doxygen_filterdb_%d.tmp",pid);
12752 Doxygen::filterDBFileName.prepend(outputDirectory+"/");
12753
12754 /**************************************************************************
12755 * Check/create output directories *
12756 **************************************************************************/
12757
12758 bool generateHtml = Config_getBool(GENERATE_HTML);
12759 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
12760 bool generateXml = Config_getBool(GENERATE_XML);
12761 bool generateLatex = Config_getBool(GENERATE_LATEX);
12762 bool generateRtf = Config_getBool(GENERATE_RTF);
12763 bool generateMan = Config_getBool(GENERATE_MAN);
12764 bool generateSql = Config_getBool(GENERATE_SQLITE3);
12765 DString htmlOutput;
12766 DString docbookOutput;
12767 DString xmlOutput;
12768 DString latexOutput;
12769 DString rtfOutput;
12770 DString manOutput;
12771 DString sqlOutput;
12772
12773 if (!g_singleComment)
12774 {
12775 if (generateHtml)
12776 {
12777 htmlOutput = createOutputDirectory(outputDirectory,Config_getString(HTML_OUTPUT),"/html");
12778 Config_updateString(HTML_OUTPUT,htmlOutput);
12779
12780 DString sitemapUrl = Config_getString(SITEMAP_URL);
12781 bool generateSitemap = !sitemapUrl.empty();
12782 if (generateSitemap && !sitemapUrl.endsWith("/"))
12783 {
12784 Config_updateString(SITEMAP_URL,sitemapUrl+"/");
12785 }
12786
12787 // add HTML indexers that are enabled
12788 bool generateHtmlHelp = Config_getBool(GENERATE_HTMLHELP);
12789 bool generateEclipseHelp = Config_getBool(GENERATE_ECLIPSEHELP);
12790 bool generateQhp = Config_getBool(GENERATE_QHP);
12791 bool generateTreeView = Config_getBool(GENERATE_TREEVIEW);
12792 bool generateDocSet = Config_getBool(GENERATE_DOCSET);
12793 if (generateEclipseHelp) Doxygen::indexList->addIndex<EclipseHelp>();
12794 if (generateHtmlHelp) Doxygen::indexList->addIndex<HtmlHelp>();
12795 if (generateQhp) Doxygen::indexList->addIndex<Qhp>();
12796 if (generateSitemap) Doxygen::indexList->addIndex<Sitemap>();
12797 if (generateTreeView) Doxygen::indexList->addIndex<FTVHelp>(true);
12798 if (generateDocSet) Doxygen::indexList->addIndex<DocSets>();
12801 }
12802
12803 if (generateDocbook)
12804 {
12805 docbookOutput = createOutputDirectory(outputDirectory,Config_getString(DOCBOOK_OUTPUT),"/docbook");
12806 Config_updateString(DOCBOOK_OUTPUT,docbookOutput);
12807 }
12808
12809 if (generateXml)
12810 {
12811 xmlOutput = createOutputDirectory(outputDirectory,Config_getString(XML_OUTPUT),"/xml");
12812 Config_updateString(XML_OUTPUT,xmlOutput);
12813 }
12814
12815 if (generateLatex)
12816 {
12817 latexOutput = createOutputDirectory(outputDirectory,Config_getString(LATEX_OUTPUT), "/latex");
12818 Config_updateString(LATEX_OUTPUT,latexOutput);
12819 }
12820
12821 if (generateRtf)
12822 {
12823 rtfOutput = createOutputDirectory(outputDirectory,Config_getString(RTF_OUTPUT),"/rtf");
12824 Config_updateString(RTF_OUTPUT,rtfOutput);
12825 }
12826
12827 if (generateMan)
12828 {
12829 manOutput = createOutputDirectory(outputDirectory,Config_getString(MAN_OUTPUT),"/man");
12830 Config_updateString(MAN_OUTPUT,manOutput);
12831 }
12832
12833 if (generateSql)
12834 {
12835 sqlOutput = createOutputDirectory(outputDirectory,Config_getString(SQLITE3_OUTPUT),"/sqlite3");
12836 Config_updateString(SQLITE3_OUTPUT,sqlOutput);
12837 }
12838 }
12839
12840 if (Config_getBool(HAVE_DOT))
12841 {
12842 DString curFontPath = Config_getString(DOT_FONTPATH);
12843 if (curFontPath.empty())
12844 {
12845 Portable::getenv("DOTFONTPATH");
12846 DString newFontPath = ".";
12847 if (!curFontPath.empty())
12848 {
12849 newFontPath+=Portable::pathListSeparator();
12850 newFontPath+=curFontPath;
12851 }
12852 Portable::setenv("DOTFONTPATH",qPrint(newFontPath));
12853 }
12854 else
12855 {
12856 Portable::setenv("DOTFONTPATH",qPrint(curFontPath));
12857 }
12858 }
12859
12860 /**************************************************************************
12861 * Handle layout file *
12862 **************************************************************************/
12863
12865 DString layoutFileName = Config_getString(LAYOUT_FILE);
12866 bool defaultLayoutUsed = false;
12867 if (layoutFileName.empty())
12868 {
12869 layoutFileName = Config_updateString(LAYOUT_FILE,"DoxygenLayout.xml");
12870 defaultLayoutUsed = true;
12871 }
12872 AUTO_TRACE_ADD("defaultLayoutUsed={}, layoutFileName={}",defaultLayoutUsed,layoutFileName);
12873
12874 FileInfo fi(layoutFileName.str());
12875 if (fi.exists())
12876 {
12877 msg("Parsing layout file {}...\n",layoutFileName);
12878 LayoutDocManager::instance().parse(layoutFileName);
12879 }
12880 else if (!defaultLayoutUsed)
12881 {
12882 warn_uncond("failed to open layout file '{}' for reading! Using default settings.\n",layoutFileName);
12883 }
12884 printLayout();
12885
12886 /**************************************************************************
12887 * Read and preprocess input *
12888 **************************************************************************/
12889
12890 // prevent search in the output directories
12891 StringVector exclPatterns = Config_getList(EXCLUDE_PATTERNS);
12892 if (generateHtml) exclPatterns.push_back(htmlOutput.str());
12893 if (generateDocbook) exclPatterns.push_back(docbookOutput.str());
12894 if (generateXml) exclPatterns.push_back(xmlOutput.str());
12895 if (generateLatex) exclPatterns.push_back(latexOutput.str());
12896 if (generateRtf) exclPatterns.push_back(rtfOutput.str());
12897 if (generateMan) exclPatterns.push_back(manOutput.str());
12898 Config_updateList(EXCLUDE_PATTERNS,exclPatterns);
12899
12900 if (!g_singleComment)
12901 {
12903
12905 }
12906
12907 // Notice: the order of the function calls below is very important!
12908
12909 if (generateHtml && !Config_getBool(USE_MATHJAX))
12910 {
12912 }
12913 if (generateRtf)
12914 {
12916 }
12917 if (generateDocbook)
12918 {
12920 }
12921
12923
12924 /**************************************************************************
12925 * Handle Tag Files *
12926 **************************************************************************/
12927
12928 std::shared_ptr<Entry> root = std::make_shared<Entry>();
12929
12930 if (!g_singleComment)
12931 {
12932 msg("Reading and parsing tag files\n");
12933 StringVector tagFileList = Config_getList(TAGFILES);
12934 for (const auto &s : tagFileList)
12935 {
12936 readTagFile(root,s.c_str());
12937 }
12938 }
12939
12940 /**************************************************************************
12941 * Parse source files *
12942 **************************************************************************/
12943
12944 addSTLSupport(root);
12945
12946 g_s.begin("Parsing files\n");
12947 if (g_singleComment)
12948 {
12949 //printf("Parsing comment %s\n",qPrint(g_commentFileName));
12950 if (g_commentFileName=="-")
12951 {
12952 std::string text = fileToString(g_commentFileName).str();
12953 addTerminalCharIfMissing(text,'\n');
12954 generateHtmlForComment("stdin.md",text);
12955 }
12956 else if (FileInfo(g_commentFileName.str()).isFile())
12957 {
12958 std::string text;
12960 addTerminalCharIfMissing(text,'\n');
12962 }
12963 else
12964 {
12965 }
12967 exit(0);
12968 }
12969 else
12970 {
12971 if (Config_getInt(NUM_PROC_THREADS)==1)
12972 {
12974 }
12975 else
12976 {
12978 }
12979 }
12980 g_s.end();
12981
12982 /**************************************************************************
12983 * Gather information *
12984 **************************************************************************/
12985
12986 g_s.begin("Building macro definition list...\n");
12988 g_s.end();
12989
12990 g_s.begin("Building group list...\n");
12991 buildGroupList(root.get());
12992 organizeSubGroups(root.get());
12993 g_s.end();
12994
12995 g_s.begin("Building directory list...\n");
12997 findDirDocumentation(root.get());
12998 g_s.end();
12999
13000 g_s.begin("Building namespace list...\n");
13001 buildNamespaceList(root.get());
13002 findUsingDirectives(root.get());
13003 g_s.end();
13004
13005 g_s.begin("Building file list...\n");
13006 buildFileList(root.get());
13007 g_s.end();
13008
13009 g_s.begin("Building class list...\n");
13010 buildClassList(root.get());
13011 g_s.end();
13012
13013 g_s.begin("Building concept list...\n");
13014 buildConceptList(root.get());
13015 g_s.end();
13016
13017 // build list of using declarations here (global list)
13018 buildListOfUsingDecls(root.get());
13019 g_s.end();
13020
13021 g_s.begin("Computing nesting relations for classes...\n");
13023 g_s.end();
13024 // 1.8.2-20121111: no longer add nested classes to the group as well
13025 //distributeClassGroupRelations();
13026
13027 // calling buildClassList may result in cached relations that
13028 // become invalid after resolveClassNestingRelations(), that's why
13029 // we need to clear the cache here
13031 // we don't need the list of using declaration anymore
13032 g_usingDeclarations.clear();
13033
13034 g_s.begin("Associating documentation with classes...\n");
13035 buildClassDocList(root.get());
13036 g_s.end();
13037
13038 g_s.begin("Associating documentation with concepts...\n");
13039 buildConceptDocList(root.get());
13041 g_s.end();
13042
13043 g_s.begin("Associating documentation with modules...\n");
13044 findModuleDocumentation(root.get());
13045 g_s.end();
13046
13047 g_s.begin("Building example list...\n");
13048 buildExampleList(root.get());
13049 g_s.end();
13050
13051 g_s.begin("Searching for enumerations...\n");
13052 findEnums(root.get());
13053 g_s.end();
13054
13055 // Since buildVarList calls isVarWithConstructor
13056 // and this calls getResolvedClass we need to process
13057 // typedefs first so the relations between classes via typedefs
13058 // are properly resolved. See bug 536385 for an example.
13059 g_s.begin("Searching for documented typedefs...\n");
13060 buildTypedefList(root.get());
13061 g_s.end();
13062
13063 if (Config_getBool(OPTIMIZE_OUTPUT_SLICE))
13064 {
13065 g_s.begin("Searching for documented sequences...\n");
13066 buildSequenceList(root.get());
13067 g_s.end();
13068
13069 g_s.begin("Searching for documented dictionaries...\n");
13070 buildDictionaryList(root.get());
13071 g_s.end();
13072 }
13073
13074 g_s.begin("Searching for members imported via using declarations...\n");
13075 // this should be after buildTypedefList in order to properly import
13076 // used typedefs
13077 findUsingDeclarations(root.get(),true); // do for python packages first
13078 findUsingDeclarations(root.get(),false); // then the rest
13079 g_s.end();
13080
13081 g_s.begin("Searching for included using directives...\n");
13083 g_s.end();
13084
13085 g_s.begin("Searching for documented variables...\n");
13086 buildVarList(root.get());
13087 g_s.end();
13088
13089 g_s.begin("Building interface member list...\n");
13090 buildInterfaceAndServiceList(root.get()); // UNO IDL
13091
13092 g_s.begin("Building member list...\n"); // using class info only !
13093 buildFunctionList(root.get());
13094 g_s.end();
13095
13096 g_s.begin("Searching for friends...\n");
13097 findFriends();
13098 g_s.end();
13099
13100 g_s.begin("Searching for documented defines...\n");
13101 findDefineDocumentation(root.get());
13102 g_s.end();
13103
13104 g_s.begin("Computing class inheritance relations...\n");
13105 findClassEntries(root.get());
13107 g_s.end();
13108
13109 g_s.begin("Computing class usage relations...\n");
13111 g_s.end();
13112
13113 g_s.begin("Flushing cached template relations that have become invalid...\n");
13115 g_s.end();
13116
13117 g_s.begin("Warn for undocumented namespaces...\n");
13119 g_s.end();
13120
13121 g_s.begin("Computing class relations...\n");
13124 if (Config_getBool(OPTIMIZE_OUTPUT_VHDL))
13125 {
13127 }
13129 g_classEntries.clear();
13130 g_s.end();
13131
13132 g_s.begin("Add enum values to enums...\n");
13133 addEnumValuesToEnums(root.get());
13134 findEnumDocumentation(root.get());
13135 g_s.end();
13136
13137 g_s.begin("Searching for member function documentation...\n");
13138 findObjCMethodDefinitions(root.get());
13139 findMemberDocumentation(root.get()); // may introduce new members !
13140 findUsingDeclImports(root.get()); // may introduce new members !
13141 g_usingClassMap.clear();
13145 g_s.end();
13146
13147 // moved to after finding and copying documentation,
13148 // as this introduces new members see bug 722654
13149 g_s.begin("Creating members for template instances...\n");
13151 g_s.end();
13152
13153 g_s.begin("Searching for tag less structs...\n");
13155 g_s.end();
13156
13157 g_s.begin("Building page list...\n");
13158 buildPageList(root.get());
13159 g_s.end();
13160
13161 g_s.begin("Building requirements list...\n");
13162 buildRequirementsList(root.get());
13163 g_s.end();
13164
13165 g_s.begin("Search for main page...\n");
13166 findMainPage(root.get());
13167 findMainPageTagFiles(root.get());
13168 g_s.end();
13169
13170 g_s.begin("Computing page relations...\n");
13171 computePageRelations(root.get());
13173 g_s.end();
13174
13175 g_s.begin("Determining the scope of groups...\n");
13176 findGroupScope(root.get());
13177 g_s.end();
13178
13179 g_s.begin("Computing module relations...\n");
13180 auto &mm = ModuleManager::instance();
13181 mm.resolvePartitions();
13182 mm.resolveImports();
13183 mm.collectExportedSymbols();
13184 g_s.end();
13185
13186 auto memberNameComp = [](const MemberNameLinkedMap::Ptr &n1,const MemberNameLinkedMap::Ptr &n2)
13187 {
13188 return dstricmp_sort(n1->memberName().data()+getPrefixIndex(n1->memberName()),
13189 n2->memberName().data()+getPrefixIndex(n2->memberName())
13190 )<0;
13191 };
13192
13193 auto classComp = [](const ClassLinkedMap::Ptr &c1,const ClassLinkedMap::Ptr &c2)
13194 {
13195 if (Config_getBool(SORT_BY_SCOPE_NAME))
13196 {
13197 return dstricmp_sort(c1->name(), c2->name())<0;
13198 }
13199 else
13200 {
13201 int i = dstricmp_sort(c1->className(), c2->className());
13202 return i==0 ? dstricmp_sort(c1->name(), c2->name())<0 : i<0;
13203 }
13204 };
13205
13206 auto namespaceComp = [](const NamespaceLinkedMap::Ptr &n1,const NamespaceLinkedMap::Ptr &n2)
13207 {
13208 return dstricmp_sort(n1->name(),n2->name())<0;
13209 };
13210
13211 auto conceptComp = [](const ConceptLinkedMap::Ptr &c1,const ConceptLinkedMap::Ptr &c2)
13212 {
13213 return dstricmp_sort(c1->name(),c2->name())<0;
13214 };
13215
13216 g_s.begin("Sorting lists...\n");
13217 std::stable_sort(Doxygen::memberNameLinkedMap->begin(),
13219 memberNameComp);
13220 std::stable_sort(Doxygen::functionNameLinkedMap->begin(),
13222 memberNameComp);
13223 std::stable_sort(Doxygen::hiddenClassLinkedMap->begin(),
13225 classComp);
13226 std::stable_sort(Doxygen::classLinkedMap->begin(),
13228 classComp);
13229 std::stable_sort(Doxygen::conceptLinkedMap->begin(),
13231 conceptComp);
13232 std::stable_sort(Doxygen::namespaceLinkedMap->begin(),
13234 namespaceComp);
13235 g_s.end();
13236
13237 g_s.begin("Determining which enums are documented\n");
13239 g_s.end();
13240
13241 g_s.begin("Computing member relations...\n");
13244 g_s.end();
13245
13246 g_s.begin("Building full member lists recursively...\n");
13248 g_s.end();
13249
13250 g_s.begin("Adding members to member groups.\n");
13252 g_s.end();
13253
13254 if (Config_getBool(DISTRIBUTE_GROUP_DOC))
13255 {
13256 g_s.begin("Distributing member group documentation.\n");
13258 g_s.end();
13259 }
13260
13261 g_s.begin("Computing member references...\n");
13263 g_s.end();
13264
13265 if (Config_getBool(INHERIT_DOCS))
13266 {
13267 g_s.begin("Inheriting documentation...\n");
13269 g_s.end();
13270 }
13271
13272
13273 // compute the shortest possible names of all files
13274 // without losing the uniqueness of the file names.
13275 g_s.begin("Generating disk names...\n");
13277 g_s.end();
13278
13279 g_s.begin("Adding source references...\n");
13281 g_s.end();
13282
13283 g_s.begin("Adding xrefitems...\n");
13286 g_s.end();
13287
13288 g_s.begin("Adding requirements...\n");
13291 g_s.end();
13292
13293 g_s.begin("Sorting member lists...\n");
13295 g_s.end();
13296
13297 g_s.begin("Setting anonymous enum type...\n");
13299 g_s.end();
13300
13301 g_s.begin("Computing dependencies between directories...\n");
13303 g_s.end();
13304
13305 g_s.begin("Generating citations page...\n");
13307 g_s.end();
13308
13309 g_s.begin("Counting data structures...\n");
13311 g_s.end();
13312
13313 g_s.begin("Resolving user defined references...\n");
13315 g_s.end();
13316
13317 g_s.begin("Finding anchors and sections in the documentation...\n");
13319 g_s.end();
13320
13321 g_s.begin("Transferring function references...\n");
13323 g_s.end();
13324
13325 g_s.begin("Combining using relations...\n");
13327 g_s.end();
13328
13330 g_s.begin("Adding members to index pages...\n");
13332 addToIndices();
13333 g_s.end();
13334
13335 g_s.begin("Correcting members for VHDL...\n");
13337 g_s.end();
13338
13339 g_s.begin("Computing tooltip texts...\n");
13341 g_s.end();
13342
13343 if (Config_getBool(SORT_GROUP_NAMES))
13344 {
13345 std::stable_sort(Doxygen::groupLinkedMap->begin(),
13347 [](const auto &g1,const auto &g2)
13348 { return g1->groupTitle() < g2->groupTitle(); });
13349
13350 for (const auto &gd : *Doxygen::groupLinkedMap)
13351 {
13352 gd->sortSubGroups();
13353 }
13354 }
13355
13356 printNavTree(root.get(),0);
13358}
13359
13361{
13362 AUTO_TRACE();
13363 /**************************************************************************
13364 * Initialize output generators *
13365 **************************************************************************/
13366
13367 /// add extra languages for which we can only produce syntax highlighted code
13369
13370 //// dump all symbols
13371 if (g_dumpSymbolMap)
13372 {
13373 dumpSymbolMap();
13374 exit(0);
13375 }
13376
13377 bool generateHtml = Config_getBool(GENERATE_HTML);
13378 bool generateLatex = Config_getBool(GENERATE_LATEX);
13379 bool generateMan = Config_getBool(GENERATE_MAN);
13380 bool generateRtf = Config_getBool(GENERATE_RTF);
13381 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
13382
13383
13385 if (generateHtml)
13386 {
13390 }
13391 if (generateLatex)
13392 {
13395 }
13396 if (generateDocbook)
13397 {
13400 }
13401 if (generateMan)
13402 {
13405 }
13406 if (generateRtf)
13407 {
13410 }
13411 if (Config_getBool(USE_HTAGS))
13412 {
13413 Htags::useHtags = true;
13414 DString htmldir = Config_getString(HTML_OUTPUT);
13415 if (!Htags::execute(htmldir))
13416 err("USE_HTAGS is YES but htags(1) failed. \n");
13417 else if (!Htags::loadFilemap(htmldir))
13418 err("htags(1) ended normally but failed to load the filemap. \n");
13419 }
13420
13421 /**************************************************************************
13422 * Generate documentation *
13423 **************************************************************************/
13424
13425 g_s.begin("Generating style sheet...\n");
13426 //printf("writing style info\n");
13427 g_outputList->writeStyleInfo(0); // write first part
13428 g_s.end();
13429
13430 bool searchEngine = Config_getBool(SEARCHENGINE);
13431 bool serverBasedSearch = Config_getBool(SERVER_BASED_SEARCH);
13432
13433 g_s.begin("Generating search indices...\n");
13434 if (searchEngine && !serverBasedSearch && generateHtml)
13435 {
13437 }
13438
13439 // generate search indices (need to do this before writing other HTML
13440 // pages as these contain a drop down menu with options depending on
13441 // what categories we find in this function.
13442 if (generateHtml && searchEngine)
13443 {
13444 DString searchDirName = Config_getString(HTML_OUTPUT)+"/search";
13445 Dir searchDir(searchDirName.str());
13446 if (!searchDir.exists() && !searchDir.mkdir(searchDirName.str()))
13447 {
13448 term("Could not create search results directory '{}' $PWD='{}'\n",
13449 searchDirName,Dir::currentDirPath());
13450 }
13451 HtmlGenerator::writeSearchData(searchDirName);
13452 if (!serverBasedSearch) // client side search index
13453 {
13455 }
13456 }
13457 g_s.end();
13458
13459 // copy static stuff
13460 if (generateHtml)
13461 {
13463 copyLogo(Config_getString(HTML_OUTPUT),true);
13464 copyIcon(Config_getString(HTML_OUTPUT),true);
13465 copyExtraFiles(Config_getList(HTML_EXTRA_FILES),"HTML_EXTRA_FILES",Config_getString(HTML_OUTPUT),true);
13466 }
13467 if (generateLatex)
13468 {
13470 copyLogo(Config_getString(LATEX_OUTPUT),false);
13471 copyIcon(Config_getString(LATEX_OUTPUT),false);
13472 copyExtraFiles(Config_getList(LATEX_EXTRA_FILES),"LATEX_EXTRA_FILES",Config_getString(LATEX_OUTPUT),false);
13473 }
13474 if (generateDocbook)
13475 {
13476 copyLogo(Config_getString(DOCBOOK_OUTPUT),false);
13477 copyIcon(Config_getString(DOCBOOK_OUTPUT),false);
13478 }
13479 if (generateRtf)
13480 {
13481 copyLogo(Config_getString(RTF_OUTPUT),false);
13482 copyIcon(Config_getString(RTF_OUTPUT),false);
13483 copyExtraFiles(Config_getList(RTF_EXTRA_FILES),"RTF_EXTRA_FILES",Config_getString(RTF_OUTPUT),false);
13484 }
13485
13487 if (fm.hasFormulas() && generateHtml
13488 && !Config_getBool(USE_MATHJAX))
13489 {
13490 g_s.begin("Generating images for formulas in HTML...\n");
13491 fm.generateImages(Config_getString(HTML_OUTPUT), true, Config_getEnum(HTML_FORMULA_FORMAT)==HTML_FORMULA_FORMAT_t::svg ?
13493 g_s.end();
13494 }
13495 if (fm.hasFormulas() && generateRtf)
13496 {
13497 g_s.begin("Generating images for formulas in RTF...\n");
13499 g_s.end();
13500 }
13501
13502 if (fm.hasFormulas() && generateDocbook)
13503 {
13504 g_s.begin("Generating images for formulas in Docbook...\n");
13506 g_s.end();
13507 }
13508
13509 g_s.begin("Generating example documentation...\n");
13511 g_s.end();
13512
13513 g_s.begin("Generating file sources...\n");
13515 g_s.end();
13516
13517 g_s.begin("Counting members...\n");
13518 // needs to be done after generating the sources
13519 // but before generating the compound documentation, see bug #12233
13520 countMembers();
13521 g_s.end();
13522
13523 g_s.begin("Generating file documentation...\n");
13525 g_s.end();
13526
13527 g_s.begin("Generating page documentation...\n");
13529 g_s.end();
13530
13531 g_s.begin("Generating group documentation...\n");
13533 g_s.end();
13534
13535 g_s.begin("Generating class documentation...\n");
13537 g_s.end();
13538
13539 g_s.begin("Generating concept documentation...\n");
13541 g_s.end();
13542
13543 g_s.begin("Generating module documentation...\n");
13545 g_s.end();
13546
13547 g_s.begin("Generating namespace documentation...\n");
13549 g_s.end();
13550
13551 if (Config_getBool(GENERATE_LEGEND))
13552 {
13553 g_s.begin("Generating graph info page...\n");
13555 g_s.end();
13556 }
13557
13558 g_s.begin("Generating directory documentation...\n");
13560 g_s.end();
13561
13562 if (g_outputList->size()>0)
13563 {
13565 }
13566
13567 g_s.begin("finalizing index lists...\n");
13569 g_s.end();
13570
13571 g_s.begin("writing tag file...\n");
13572 writeTagFile();
13573 g_s.end();
13574
13575 if (Config_getBool(GENERATE_XML))
13576 {
13577 g_s.begin("Generating XML output...\n");
13579 generateXML();
13581 g_s.end();
13582 }
13583 if (Config_getBool(GENERATE_SQLITE3))
13584 {
13585 g_s.begin("Generating SQLITE3 output...\n");
13587 g_s.end();
13588 }
13589
13590 if (Config_getBool(GENERATE_AUTOGEN_DEF))
13591 {
13592 g_s.begin("Generating AutoGen DEF output...\n");
13593 generateDEF();
13594 g_s.end();
13595 }
13596 if (Config_getBool(GENERATE_PERLMOD))
13597 {
13598 g_s.begin("Generating Perl module output...\n");
13600 g_s.end();
13601 }
13602 if (generateHtml && searchEngine && serverBasedSearch)
13603 {
13604 g_s.begin("Generating search index\n");
13605 if (Doxygen::searchIndex.kind()==SearchIndexIntf::Internal) // write own search index
13606 {
13608 Doxygen::searchIndex.write(Config_getString(HTML_OUTPUT)+"/search/search.idx");
13609 }
13610 else // write data for external search index
13611 {
13613 DString searchDataFile = Config_getString(SEARCHDATA_FILE);
13614 if (searchDataFile.empty())
13615 {
13616 searchDataFile="searchdata.xml";
13617 }
13618 if (!Portable::isAbsolutePath(searchDataFile.data()))
13619 {
13620 searchDataFile.prepend(Config_getString(OUTPUT_DIRECTORY)+"/");
13621 }
13622 Doxygen::searchIndex.write(searchDataFile);
13623 }
13624 g_s.end();
13625 }
13626
13627 if (generateRtf)
13628 {
13629 g_s.begin("Combining RTF output...\n");
13630 if (!RTFGenerator::preProcessFileInplace(Config_getString(RTF_OUTPUT),"refman.rtf"))
13631 {
13632 err("An error occurred during post-processing the RTF files!\n");
13633 }
13634 g_s.end();
13635 }
13636
13637 if (PlantumlManager::instance().needToRun())
13638 {
13639 g_s.begin("Running plantuml with JAVA...\n");
13641 g_s.end();
13642 }
13643
13644 if (MermaidManager::instance().needToRun())
13645 {
13646 g_s.begin("Running mermaid (mmdc)...\n");
13648 g_s.end();
13649 }
13650
13651 if (Config_getBool(HAVE_DOT) && DotManager::instance()->needToRun())
13652 {
13653 g_s.begin("Running dot...\n");
13655 g_s.end();
13656 }
13657
13658 if (generateHtml &&
13659 Config_getBool(GENERATE_HTMLHELP) &&
13660 !Config_getString(HHC_LOCATION).empty())
13661 {
13662 g_s.begin("Running html help compiler...\n");
13664 g_s.end();
13665 }
13666
13667 if ( generateHtml &&
13668 Config_getBool(GENERATE_QHP) &&
13669 !Config_getString(QHG_LOCATION).empty())
13670 {
13671 g_s.begin("Running qhelpgenerator...\n");
13673 g_s.end();
13674 }
13675
13678
13680
13682 {
13683
13684 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
13685 if (numThreads<1) numThreads=1;
13686 msg("Total elapsed time: {:.6f} seconds\n(of which an average of {:.6f} seconds per thread waiting for external tools to finish)\n",
13687 (static_cast<double>(Debug::elapsedTime())),
13688 Portable::getSysElapsedTime()/static_cast<double>(numThreads)
13689 );
13690 g_s.print();
13691
13693 msg("finished...\n");
13695 }
13696 else
13697 {
13698 msg("finished...\n");
13699 }
13700
13701
13702 /**************************************************************************
13703 * Start cleaning up *
13704 **************************************************************************/
13705
13707
13709 Dir thisDir;
13710 thisDir.remove(Doxygen::filterDBFileName.str());
13712 exitTracing();
13714 delete Doxygen::clangUsrMap;
13715 g_successfulRun=true;
13716
13717 //dumpDocNodeSizes();
13718}
void readAliases()
Definition aliases.cpp:162
constexpr auto prefix
Definition anchor.cpp:47
This class contains the information about the argument of a function or template.
Definition arguments.h:27
DString defval
Definition arguments.h:47
DString array
Definition arguments.h:46
DString name
Definition arguments.h:45
DString type
Definition arguments.h:43
This class represents an function or template argument list.
Definition arguments.h:66
RefQualifierType refQualifier() const
Definition arguments.h:117
bool noParameters() const
Definition arguments.h:118
bool pureSpecifier() const
Definition arguments.h:114
iterator end()
Definition arguments.h:95
bool hasParameters() const
Definition arguments.h:77
DString trailingReturnType() const
Definition arguments.h:115
bool isDeleted() const
Definition arguments.h:116
size_t size() const
Definition arguments.h:101
void setPureSpecifier(bool b)
Definition arguments.h:122
bool constSpecifier() const
Definition arguments.h:112
void setTrailingReturnType(const DString &s)
Definition arguments.cpp:40
void push_back(const Argument &a)
Definition arguments.h:103
bool empty() const
Definition arguments.h:100
void setConstSpecifier(bool b)
Definition arguments.h:120
void setRefQualifier(RefQualifierType t)
Definition arguments.h:127
void setIsDeleted(bool b)
Definition arguments.h:126
iterator begin()
Definition arguments.h:94
bool volatileSpecifier() const
Definition arguments.h:113
void setNoParameters(bool b)
Definition arguments.h:128
void setVolatileSpecifier(bool b)
Definition arguments.h:121
static CitationManager & instance()
Definition cite.cpp:90
void clear()
clears the database
Definition cite.cpp:115
void generatePage()
Generate the citations page.
Definition cite.cpp:336
std::unique_ptr< ClangTUParser > createTUParser(const FileDef *fd) const
static ClangParser * instance()
Returns the one and only instance of the class.
Clang parser object for a single translation unit, which consists of a source file and the directly o...
Definition clangparser.h:25
void switchToFile(const FileDef *fd)
Switches to another file within the translation unit started with start().
void parse()
Parse the file given at construction time as a translation unit This file should already be preproces...
A abstract class representing of a compound symbol.
Definition classdef.h:100
virtual DString requiresClause() const =0
virtual const MemberDef * getMemberByName(const DString &) const =0
Returns the member with the given name.
virtual const ArgumentList & templateArguments() const =0
Returns the template arguments of this class.
virtual void writeDocumentation(OutputList &ol) const =0
virtual void writeMemberList(OutputList &ol) const =0
virtual bool isTemplate() const =0
Returns true if this class is a template.
virtual const BaseClassList & baseClasses() const =0
Returns the list of base classes from which this class directly inherits.
virtual int isBaseClass(const ClassDef *bcd, bool followInstances, const DString &templSpec=DString()) const =0
Returns true iff bcd is a direct or indirect base class of this class.
virtual const TemplateInstanceList & getTemplateInstances() const =0
Returns a sorted dictionary with all template instances found for this template class.
virtual ArgumentLists getTemplateParameterLists() const =0
Returns the template parameter lists that form the template declaration of this class.
virtual Protection protection() const =0
Return the protection level (Public,Protected,Private) in which this compound was found.
virtual DString compoundTypeString() const =0
Returns the type of compound as a string.
virtual MemberList * getMemberList(MemberListType lt) const =0
Returns the members in the list identified by lt.
virtual std::unique_ptr< ClassDef > deepCopy(const DString &name) const =0
virtual bool isForwardDeclared() const =0
Returns true if this class represents a forward declaration of a template class.
virtual bool isSubClass(ClassDef *bcd, int level=0) const =0
Returns true iff bcd is a direct or indirect sub class of this class.
virtual void moveTo(Definition *)=0
virtual const TemplateNameMap & getTemplateBaseClassNames() const =0
virtual DString qualifiedNameWithTemplateParameters(const ArgumentLists *actualParams=nullptr, uint32_t *actualParamIndex=nullptr) const =0
virtual bool isEmbeddedInOuterScope() const =0
virtual const MemberNameInfoLinkedMap & memberNameInfoLinkedMap() const =0
Returns a dictionary of all members.
virtual bool isImplicitTemplateInstance() const =0
virtual const ClassDef * templateMaster() const =0
Returns the template master of which this class is an instance.
CompoundType
The various compound types.
Definition classdef.h:105
@ Singleton
Definition classdef.h:113
@ Interface
Definition classdef.h:108
@ Exception
Definition classdef.h:111
virtual CompoundType compoundType() const =0
Returns the type of compound this is, i.e. class/struct/union/...
virtual bool containsOverload(const MemberDef *md) const =0
virtual ClassLinkedRefMap getClasses() const =0
returns the classes nested into this class
virtual FileDef * getFileDef() const =0
Returns the file in which this compound's definition can be found.
virtual void writeTagFile(TextStream &) const =0
virtual void writeDocumentationForInnerClasses(OutputList &ol) const =0
virtual void computeAnchors()=0
virtual void addTypeConstraints()=0
virtual void overrideCollaborationGraph(bool e)=0
virtual void addUsedClass(ClassDef *cd, const DString &accessName, Protection prot)=0
virtual void insertExplicitTemplateInstance(ClassDef *instance, const DString &spec)=0
virtual void countMembers()=0
virtual void addMembersToMemberGroup()=0
virtual void setPrimaryConstructorParams(const ArgumentList &list)=0
virtual void setTemplateBaseClassNames(const TemplateNameMap &templateNames)=0
virtual ClassDef * insertTemplateInstance(const DString &fileName, int startLine, size_t startColumn, const DString &templSpec, bool &freshInstance)=0
virtual void setClassName(const DString &name)=0
virtual void setFileDef(FileDef *fd)=0
virtual void reclassifyMember(MemberDefMutable *md, MemberType t)=0
virtual void setTemplateArguments(const ArgumentList &al)=0
virtual void setTemplateMaster(const ClassDef *tm)=0
virtual void makeTemplateArgument(bool b=true)=0
virtual void mergeCategory(ClassDef *category)=0
virtual void addQualifiers(const StringVector &qualifiers)=0
virtual void setClassSpecifier(TypeSpecifier spec)=0
virtual void insertUsedFile(const FileDef *)=0
virtual void setTaglessParent(const ClassDef *cd)=0
virtual void setUsedOnly(bool b)=0
virtual void sortMemberLists()=0
virtual void setProtection(Protection p)=0
virtual void setTypeConstraints(const ArgumentList &al)=0
virtual void overrideInheritanceGraph(CLASS_GRAPH_t e)=0
virtual void setAnonymousEnumType()=0
virtual void setCompoundType(CompoundType t)=0
virtual void distributeMemberGroupDocumentation()=0
virtual void setMetaData(const DString &md)=0
virtual void findSectionsInDocumentation()=0
virtual void insertMember(MemberDef *)=0
virtual void sortAllMembersList()=0
virtual void mergeMembers()=0
virtual void setIsStatic(bool b)=0
virtual void addMembersToTemplateInstance(const ClassDef *cd, const ArgumentList &templateArguments, const DString &templSpec)=0
virtual void insertSubClass(ClassDef *, Protection p, Specifier s, const DString &t=DString())=0
virtual void addUsedByClass(ClassDef *cd, const DString &accessName, Protection prot)=0
virtual void setSubGrouping(bool enabled)=0
virtual void setRequiresClause(const DString &req)=0
virtual void insertBaseClass(ClassDef *, const DString &name, Protection p, Specifier s, const DString &t=DString())=0
virtual void setHasNamedVariable(bool b)=0
virtual void addDocPart(const DString &doc, int lineNr, size_t colNr)=0
virtual void addCodePart(const DString &code, int lineNr, size_t colNr)=0
virtual void setFileDef(FileDef *fd)=0
virtual void writeTagFile(TextStream &)=0
virtual void writeDocumentation(OutputList &ol)=0
virtual void setGroupId(int id)=0
virtual void setInitializer(const DString &init)=0
virtual void findSectionsInDocumentation()=0
virtual void setTemplateArguments(const ArgumentList &al)=0
A String class for use with Doxygen wrapping std::string and adding some additional functionality off...
Definition dstring.h:84
void clear()
Definition dstring.h:214
DString & setNum(short n)
Definition dstring.h:552
void resize(size_t newlen)
Definition dstring.h:209
size_t rfind(char c, size_t pos=npos) const
Definition dstring.h:244
DString fill(char c, size_t len)
Fills a string with a predefined character.
Definition dstring.h:278
DString mid(size_t index, size_t len=npos) const
Definition dstring.h:318
DString lower() const
Definition dstring.h:326
bool empty() const
Returns true iff the string is empty (std::string compatible alias for isEmpty()).
Definition dstring.h:148
DString & replace(size_t index, size_t len, const char *s)
Definition dstring.cpp:154
DString substr(size_t pos=0, size_t count=npos) const
Returns a substring of length count starting at pos.
Definition dstring.h:223
static constexpr size_t npos
value used to indicate 'not found' or 'to the end of the string', matching std::string::npos
Definition dstring.h:178
char & at(size_t i)
Returns a reference to the character at index i.
Definition dstring.h:686
bool findAndRemoveWord(const char *word)
removes occurrences of whole word from this string, while keeping internal spaces and reducing multip...
Definition dstring.cpp:660
DString right(size_t len) const
Definition dstring.h:311
size_t size() const
Returns the length of the string, not counting the 0-terminator.
Definition dstring.h:154
DString & prepend(const char *s)
Definition dstring.h:515
int contains(char c, bool cs=true) const
Definition dstring.cpp:85
size_t find(char c, size_t pos=0) const
Definition dstring.h:239
DString & sprintf(const char *format,...)
Definition dstring.cpp:34
int toInt(bool *ok=nullptr, int base=10) const
Definition dstring.cpp:191
@ ExplicitSize
Definition dstring.h:131
DString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
Definition dstring.h:337
DString left(size_t len) const
Definition dstring.h:306
const std::string & str() const
Definition dstring.h:645
bool stripPrefix(const DString &prefix)
Definition dstring.h:290
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition dstring.h:157
bool startsWith(const char *s) const
Definition dstring.h:600
bool endsWith(const char *s) const
Definition dstring.h:617
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition dstring.h:151
@ ExtCmd
Definition debug.h:37
@ Sections
Definition debug.h:49
@ Time
Definition debug.h:36
@ Qhp
Definition debug.h:45
@ Entries
Definition debug.h:48
static void printFlags()
Definition debug.cpp:137
static void clearFlag(const DebugMask mask)
Definition debug.cpp:122
static bool isFlagSet(const DebugMask mask)
Definition debug.cpp:132
static void print(DebugMask mask, int prio, fmt::format_string< Args... > fmt, Args &&... args)
Definition debug.h:78
static double elapsedTime()
Definition debug.cpp:200
static void startTimer()
Definition debug.cpp:195
static bool setFlagStr(const DString &label)
Definition debug.cpp:103
static void setFlag(const DebugMask mask)
Definition debug.cpp:117
The common base class of all entity definitions found in the sources.
Definition definition.h:77
virtual DString briefDescription(bool abbreviate=false) const =0
virtual DString inbodyFile() const =0
virtual int getEndBodyLine() const =0
virtual DString briefFile() const =0
virtual SrcLangExt getLanguage() const =0
Returns the programming language this definition was written in.
virtual int docLine() const =0
virtual DString getDefFileName() const =0
virtual DString documentation() const =0
virtual bool isLinkable() const =0
virtual DString inbodyDocumentation() const =0
virtual int getDefLine() const =0
virtual DefType definitionType() const =0
virtual const DString & name() const =0
virtual const DString & localName() const =0
virtual int inbodyLine() const =0
virtual const FileDef * getBodyDef() const =0
virtual int briefLine() const =0
virtual bool hasDocumentation() const =0
virtual bool isLinkableInProject() const =0
virtual size_t getDefColumn() const =0
virtual bool isAnonymous() const =0
virtual DString displayName(bool includeScope=true) const =0
virtual DString qualifiedName() const =0
virtual bool isHidden() const =0
virtual DString anchor() const =0
virtual int getStartDefLine() const =0
virtual const GroupList & partOfGroups() const =0
virtual bool isArtificial() const =0
virtual Definition * getOuterScope() const =0
virtual DString docFile() const =0
virtual int getStartBodyLine() const =0
virtual bool isReference() const =0
virtual const Definition * findInnerCompound(const DString &name) const =0
virtual DString getOutputFileBase() const =0
virtual void mergeReferencedBy(const Definition *other)=0
virtual void setName(const DString &name)=0
virtual void setExported(bool b)=0
virtual void setBodySegment(int defLine, int bls, int ble)=0
virtual void setHidden(bool b)=0
virtual void setReference(const DString &r)=0
virtual void mergeReferences(const Definition *other)=0
virtual void setInbodyDocumentation(const DString &d, const DString &docFile, int docLine)=0
virtual void setDocumentation(const DString &d, const DString &docFile, int docLine, bool stripWhiteSpace=true)=0
virtual void addInnerCompound(Definition *d)=0
virtual void setDefFile(const DString &df, int defLine, size_t defColumn)=0
virtual void addSectionsToDefinition(const std::vector< const SectionInfo * > &anchorList)=0
virtual void setLanguage(SrcLangExt lang)=0
virtual void setOuterScope(Definition *d)=0
virtual void setArtificial(bool b)=0
virtual void setBriefDescription(const DString &b, const DString &briefFile, int briefLine)=0
virtual void makePartOfGroup(GroupDef *gd)=0
virtual void setBodyDef(const FileDef *fd)=0
virtual void setRequirementReferences(const RequirementRefs &rqli)=0
virtual void setId(const DString &name)=0
virtual void setRefItems(const RefItemVector &sli)=0
virtual void computeTooltip()=0
A model of a directory symbol.
Definition dirdef.h:108
virtual void overrideDirectoryGraph(bool e)=0
Class representing a directory in the file system.
Definition dir.h:73
static std::string currentDirPath()
Definition dir.cpp:348
std::string absPath() const
Definition dir.cpp:370
bool mkdir(const std::string &path, bool acceptsAbsPath=true) const
Definition dir.cpp:301
void setPath(const std::string &path)
Definition dir.cpp:235
bool remove(const std::string &path, bool acceptsAbsPath=true) const
Definition dir.cpp:320
DirIterator iterator() const
Definition dir.cpp:245
static std::string cleanDirPath(const std::string &path)
Definition dir.cpp:363
static bool setCurrent(const std::string &path)
Definition dir.cpp:356
bool exists() const
Definition dir.cpp:263
A linked map of directories.
Definition dirdef.h:173
A class that generates docset files.
Definition docsets.h:35
static void init()
bool run()
Definition dot.cpp:119
static DotManager * instance()
Definition dot.cpp:78
static NamespaceLinkedMap * namespaceLinkedMap
Definition doxygen.h:108
static ConceptLinkedMap * conceptLinkedMap
Definition doxygen.h:90
static bool suppressDocWarnings
Definition doxygen.h:123
static FileNameLinkedMap * plantUmlFileNameLinkedMap
Definition doxygen.h:102
static bool parseSourcesNeeded
Definition doxygen.h:116
static StringUnorderedSet inputPaths
Definition doxygen.h:96
static std::unique_ptr< PageDef > mainPage
Definition doxygen.h:93
static bool clangAssistedParsing
Definition doxygen.h:129
static StringUnorderedSet expandAsDefinedSet
Definition doxygen.h:112
static FileNameLinkedMap * inputNameLinkedMap
Definition doxygen.h:97
static DString filterDBFileName
Definition doxygen.h:124
static ParserManager * parserManager
Definition doxygen.h:122
static InputFileEncodingList inputFileEncodingList
Definition doxygen.h:131
static DString verifiedDotPath
Definition doxygen.h:130
static ClassLinkedMap * classLinkedMap
Definition doxygen.h:88
static MemberNameLinkedMap * functionNameLinkedMap
Definition doxygen.h:105
static PageLinkedMap * exampleLinkedMap
Definition doxygen.h:91
static FileNameLinkedMap * dotFileNameLinkedMap
Definition doxygen.h:99
static NamespaceDefMutable * globalScope
Definition doxygen.h:114
static FileNameLinkedMap * imageNameLinkedMap
Definition doxygen.h:98
static FileNameLinkedMap * mscFileNameLinkedMap
Definition doxygen.h:100
static FileNameLinkedMap * mermaidFileNameLinkedMap
Definition doxygen.h:103
static MemberGroupInfoMap memberGroupInfoMap
Definition doxygen.h:111
static IndexList * indexList
Definition doxygen.h:125
static StaticInitMap staticInitMap
Definition doxygen.h:134
static StringMap tagDestinationMap
Definition doxygen.h:109
static std::mutex countFlowKeywordsMutex
Definition doxygen.h:132
static ClassLinkedMap * hiddenClassLinkedMap
Definition doxygen.h:89
static FileNameLinkedMap * diaFileNameLinkedMap
Definition doxygen.h:101
static DString spaces
Definition doxygen.h:126
static PageLinkedMap * pageLinkedMap
Definition doxygen.h:92
static bool generatingXmlOutput
Definition doxygen.h:127
static std::unique_ptr< NamespaceDef > globalNamespaceDef
Definition doxygen.h:113
static DString htmlFileExtension
Definition doxygen.h:115
static DefinesPerFileList macroDefinitions
Definition doxygen.h:128
static DirLinkedMap * dirLinkedMap
Definition doxygen.h:120
static NamespaceAliasInfoMap namespaceAliasMap
Definition doxygen.h:106
static MemberNameLinkedMap * memberNameLinkedMap
Definition doxygen.h:104
static SymbolMap< Definition > * symbolMap
Definition doxygen.h:118
static StringUnorderedSet tagFileSet
Definition doxygen.h:110
static FileNameLinkedMap * includeNameLinkedMap
Definition doxygen.h:94
static FileNameLinkedMap * exampleNameLinkedMap
Definition doxygen.h:95
static SearchIndexIntf searchIndex
Definition doxygen.h:117
static DirRelationLinkedMap dirRelations
Definition doxygen.h:121
static std::mutex addExampleMutex
Definition doxygen.h:133
static ClangUsrMap * clangUsrMap
Definition doxygen.h:119
static GroupLinkedMap * groupLinkedMap
Definition doxygen.h:107
Generator for Eclipse help files.
Definition eclipsehelp.h:42
static EmojiEntityMapper & instance()
Returns the one and only instance of the Emoji entity mapper.
Definition emoji.cpp:1981
void writeEmojiFile(TextStream &t)
Writes the list of supported emojis to the given file.
Definition emoji.cpp:2002
Represents an unstructured piece of information, about an entity found in the sources.
Definition entry.h:115
TextStream initializer
initial value (for variables)
Definition entry.h:197
VhdlSpecifier vhdlSpec
VHDL specifiers.
Definition entry.h:182
bool subGrouping
automatically group class members?
Definition entry.h:187
RequirementRefs rqli
references to requirements
Definition entry.h:227
const std::vector< std::shared_ptr< Entry > > & children() const
Definition entry.h:138
bool proto
prototype ?
Definition entry.h:186
GroupDocType groupDocType
Definition entry.h:231
int docLine
line number at which the documentation was found
Definition entry.h:201
DString includeName
include name (3 arg of \class)
Definition entry.h:199
DString bitfields
member's bit fields
Definition entry.h:192
ArgumentList typeConstr
where clause (C#) for type constraints
Definition entry.h:215
void markAsProcessed() const
Definition entry.h:166
int endBodyLine
line number where the definition ends
Definition entry.h:218
DString write
property write accessor
Definition entry.h:212
bool exported
is the symbol exported from a C++20 module
Definition entry.h:188
const TagInfo * tagInfo() const
Definition entry.h:176
DString includeFile
include file (2 arg of \class, must be unique)
Definition entry.h:198
DString fileName
file this entry was extracted from
Definition entry.h:223
ArgumentLists tArgLists
template argument declarations
Definition entry.h:195
DString docFile
file in which the documentation was found
Definition entry.h:202
LocalToc localToc
Definition entry.h:233
MethodTypes mtype
signal, slot, (dcop) method, or property?
Definition entry.h:180
@ GROUPDOC_NORMAL
defgroup
Definition entry.h:120
DString args
member argument string
Definition entry.h:191
SrcLangExt lang
programming language in which this entry was found
Definition entry.h:228
Entry * parent() const
Definition entry.h:133
DString inside
name of the class in which documents are found
Definition entry.h:213
DString doc
documentation block (partly parsed)
Definition entry.h:200
DString req
C++20 requires clause.
Definition entry.h:235
bool explicitExternal
explicitly defined as external?
Definition entry.h:185
DString brief
brief description (doc block)
Definition entry.h:203
std::vector< const SectionInfo * > anchors
list of anchors defined in this entry
Definition entry.h:222
RelatesType relatesType
how relates is handled
Definition entry.h:210
std::vector< Grouping > groups
list of groups this entry belongs to
Definition entry.h:221
CommandOverrides commandOverrides
store info for commands whose default can be overridden
Definition entry.h:189
int startLine
start line of entry in the source
Definition entry.h:224
size_t startColumn
start column of entry in the source
Definition entry.h:225
ArgumentList argList
member arguments as a list
Definition entry.h:194
DString type
member type
Definition entry.h:172
int inbodyLine
line number at which the body doc was found
Definition entry.h:207
EntryType section
entry type (see Sections);
Definition entry.h:171
int bodyLine
line number of the body in the source
Definition entry.h:216
DString exception
throw specification
Definition entry.h:214
DString relates
related class (doc block)
Definition entry.h:209
int mGrpId
member group id
Definition entry.h:219
std::vector< BaseInfo > extends
list of base classes
Definition entry.h:220
DString inbodyFile
file in which the body doc was found
Definition entry.h:208
Specifier virt
virtualness of the entry
Definition entry.h:190
DString metaData
Slice metadata.
Definition entry.h:234
std::vector< std::string > qualifiers
qualifiers specified with the qualifier command
Definition entry.h:236
DString name
member name
Definition entry.h:173
RefItemVector sli
special lists (test/todo/bug/deprecated/..) this entry is in
Definition entry.h:226
Protection protection
class protection
Definition entry.h:179
bool artificial
Artificially introduced item.
Definition entry.h:230
bool hidden
does this represent an entity that is hidden from the output
Definition entry.h:229
DString inbodyDocs
documentation inside the body of a function
Definition entry.h:206
int briefLine
line number at which the brief desc. was found
Definition entry.h:204
FileDef * fileDef() const
Definition entry.h:168
DString id
libclang id
Definition entry.h:232
int initLines
define/variable initializer lines to show
Definition entry.h:183
bool isStatic
static ?
Definition entry.h:184
TypeSpecifier spec
class/member specifiers
Definition entry.h:181
DString briefFile
file in which the brief desc. was found
Definition entry.h:205
DString read
property read accessor
Definition entry.h:211
Wrapper class for the Entry type.
Definition types.h:856
constexpr bool isCompoundDoc() const noexcept
Definition types.h:866
constexpr bool isFile() const noexcept
Definition types.h:865
constexpr bool isDoc() const noexcept
Definition types.h:867
ENTRY_TYPES constexpr bool isCompound() const noexcept
Definition types.h:863
std::string to_string() const
Definition types.h:868
static EntryType guessSection(const DString &name)
Definition types.cpp:22
constexpr bool isScope() const noexcept
Definition types.h:864
A class that generates a dynamic tree view side panel.
Definition ftvhelp.h:41
A model of a file symbol.
Definition filedef.h:97
virtual void addUsingDeclaration(const Definition *d)=0
virtual void removeMember(MemberDef *md)=0
virtual void insertClass(ClassDef *cd)=0
virtual void setDiskName(const DString &name)=0
virtual void insertConcept(ConceptDef *cd)=0
virtual void overrideIncludeGraph(bool e)=0
virtual void writeSourceHeader(OutputList &ol)=0
virtual bool generateSourceFile() const =0
virtual DString absFilePath() const =0
virtual const DString & docName() const =0
virtual const LinkedRefMap< NamespaceDef > & getUsedNamespaces() const =0
virtual bool isSource() const =0
virtual void parseSource(ClangTUParser *clangParser)=0
virtual void getAllIncludeFilesRecursively(StringVector &incFiles) const =0
virtual void writeSourceFooter(OutputList &ol)=0
virtual void writeSourceBody(OutputList &ol, ClangTUParser *clangParser)=0
virtual void addUsingDirective(NamespaceDef *nd)=0
virtual void overrideIncludedByGraph(bool e)=0
virtual void insertMember(MemberDef *md)=0
virtual void insertNamespace(NamespaceDef *nd)=0
Minimal replacement for QFileInfo.
Definition fileinfo.h:26
std::string readLink() const
Definition fileinfo.cpp:88
bool isRelative() const
Definition fileinfo.cpp:62
bool isSymLink() const
Definition fileinfo.cpp:81
bool exists() const
Definition fileinfo.cpp:34
bool match(const PatternList &patList, bool caseSenseNames, PatternElem *elem=nullptr, PatternGet getter=[](const std::string &s){ return s;}) const
Match the file name against a list of patterns.
Definition fileinfo.h:60
std::string fileName() const
Definition fileinfo.cpp:122
bool isReadable() const
Definition fileinfo.cpp:48
bool isDir() const
Definition fileinfo.cpp:74
bool isFile() const
Definition fileinfo.cpp:67
std::string dirPath(bool absPath=true) const
Definition fileinfo.cpp:141
std::string absFilePath() const
Definition fileinfo.cpp:105
Class representing all files with a certain base name.
Definition filename.h:31
Ordered dictionary of FileName objects.
Definition filename.h:70
DString showFileDefMatches(const DString &n) const
Returns a list of file definitions in fnMap that match the file name n.
Definition filename.cpp:131
FileDef * findFileDef(const DString &n, bool &ambig) const
Returns the file definition in fnMap that matches the file name n.
Definition filename.cpp:38
bool hasFormulas() const
Definition formula.cpp:720
void initFromRepository(const DString &dir)
Definition formula.cpp:60
void checkRepositories()
Definition formula.cpp:173
static FormulaManager & instance()
Definition formula.cpp:54
void generateImages(const DString &outputDir, bool toIndex, Format format, HighDPI hd=HighDPI::Off)
Definition formula.cpp:635
A model of a group of symbols.
Definition groupdef.h:48
virtual DString groupTitle() const =0
virtual void overrideGroupGraph(bool e)=0
virtual bool addClass(ClassDef *def)=0
virtual bool containsFile(const FileDef *def) const =0
virtual bool addNamespace(NamespaceDef *def)=0
virtual void setGroupScope(Definition *d)=0
virtual void addFile(FileDef *def)=0
virtual void setGroupTitle(const DString &newtitle)=0
virtual MemberList * getMemberList(MemberListType lt) const =0
virtual bool hasGroupTitle() const =0
Generator for HTML output.
Definition htmlgen.h:93
static void writeSearchData(const DString &dir)
Definition htmlgen.cpp:1455
static void init()
Definition htmlgen.cpp:1275
static void writeSearchPage()
Definition htmlgen.cpp:3272
static void writeHeaderFile(TextStream &t, const DString &cssname)
Definition htmlgen.cpp:1623
static void writeFooterFile(TextStream &t)
Definition htmlgen.cpp:1629
static void writeTabData()
Additional initialization after indices have been created.
Definition htmlgen.cpp:1439
static void writeExternalSearchPage()
Definition htmlgen.cpp:3371
static void writeStyleSheetFile(TextStream &t)
Definition htmlgen.cpp:1617
A class that generated the HTML Help specific files.
Definition htmlhelp.h:37
static const DString hhpFileName
Definition htmlhelp.h:90
static Index & instance()
Definition index.cpp:110
void countDataStructures()
Definition index.cpp:266
A list of index interfaces.
Definition indexlist.h:65
void initialize()
Definition indexlist.h:101
void addIndexItem(const Definition *context, const MemberDef *md, const DString &sectionAnchor=DString(), const DString &title=DString())
Definition indexlist.h:118
void addIndex(As &&... args)
Add an index generator to the list, using a syntax similar to std::make_unique<T>().
Definition indexlist.h:98
void addImageFile(const DString &name)
Definition indexlist.h:124
void finalize()
Definition indexlist.h:104
Generator for LaTeX output.
Definition latexgen.h:93
static void writeFooterFile(TextStream &t)
Definition latexgen.cpp:698
static void writeStyleSheetFile(TextStream &t)
Definition latexgen.cpp:704
static void writeHeaderFile(TextStream &t)
Definition latexgen.cpp:692
static void init()
Definition latexgen.cpp:634
void parse(const DString &fileName, const char *data=nullptr)
Parses a user provided layout.
Definition layout.cpp:1472
static LayoutDocManager & instance()
Returns a reference to this singleton.
Definition layout.cpp:1439
void clear()
Definition linkedmap.h:212
std::unique_ptr< RefList > Ptr
Definition linkedmap.h:38
size_t size() const
Definition linkedmap.h:210
T * add(const char *k, Args &&... args)
Definition linkedmap.h:90
const T * find(const std::string &key) const
Definition linkedmap.h:47
Container class representing a vector of objects with keys.
Definition linkedmap.h:232
const T * find(const std::string &key) const
Definition linkedmap.h:243
bool empty() const
Definition linkedmap.h:374
Generator for Man page output.
Definition mangen.h:69
static void init()
Definition mangen.cpp:275
A model of a class/file/namespace member symbol.
Definition memberdef.h:45
virtual DString requiresClause() const =0
virtual bool isFriend() const =0
virtual DString argsString() const =0
virtual bool isForeign() const =0
virtual bool isRelated() const =0
virtual DString definition() const =0
virtual const ClassDef * getCachedTypedefVal() const =0
virtual const ClassDef * getClassDef() const =0
virtual DString excpString() const =0
virtual const ArgumentList & templateArguments() const =0
virtual const DString & initializer() const =0
virtual GroupDef * getGroupDef()=0
virtual bool isCSharpProperty() const =0
virtual bool isTypedef() const =0
virtual const MemberVector & enumFieldList() const =0
virtual void moveTo(Definition *)=0
virtual const FileDef * getFileDef() const =0
virtual const ArgumentList & argumentList() const =0
virtual bool isStrongEnumValue() const =0
virtual VhdlSpecifier getVhdlSpecifiers() const =0
virtual bool isFunction() const =0
virtual bool isExternal() const =0
virtual int getMemberGroupId() const =0
virtual DString bitfieldString() const =0
virtual bool isStatic() const =0
virtual const MemberDef * reimplements() const =0
virtual StringVector getQualifiers() const =0
virtual bool isTypedefValCached() const =0
virtual bool isDocsForDefinition() const =0
virtual bool isDefine() const =0
virtual const NamespaceDef * getNamespaceDef() const =0
virtual bool isObjCProperty() const =0
virtual Protection protection() const =0
virtual TypeSpecifier getMemberSpecifiers() const =0
virtual bool isEnumerate() const =0
virtual MemberType memberType() const =0
virtual ClassDef * relatedAlso() const =0
virtual bool isVariable() const =0
virtual bool isStrong() const =0
virtual DString typeString() const =0
virtual Specifier virtualness(int count=0) const =0
virtual int redefineCount() const =0
virtual int initializerLines() const =0
virtual const MemberDef * getEnumScope() const =0
virtual bool isEnumValue() const =0
virtual bool isPrototype() const =0
virtual void setRequiresClause(const DString &req)=0
virtual void setClassDefOfAnonymousType(const ClassDef *cd)=0
virtual void setMemberClass(ClassDef *cd)=0
virtual void setProtection(Protection p)=0
virtual void setDefinition(const DString &d)=0
virtual void setMemberGroupId(int id)=0
virtual void setDocumentedEnumValues(bool value)=0
virtual void setMemberSpecifiers(TypeSpecifier s)=0
virtual ClassDefMutable * getClassDefMutable()=0
virtual void setArgsString(const DString &as)=0
virtual void setEnumScope(MemberDef *md, bool livesInsideEnum=false)=0
virtual void setDefinitionTemplateParameterLists(const ArgumentLists &lists)=0
virtual void invalidateTypedefValCache()=0
virtual void setVhdlSpecifiers(VhdlSpecifier s)=0
virtual void setEnumClassScope(ClassDef *cd)=0
virtual void setInitializer(const DString &i)=0
virtual void setMaxInitLines(int lines)=0
virtual void setInheritsDocsFrom(const MemberDef *md)=0
virtual void setFileDef(FileDef *fd)=0
virtual void setPrototype(bool p, const DString &df, int line, size_t column)=0
virtual void setRelatedAlso(ClassDef *cd)=0
virtual void setAnonymousMemberForDeclaration(const MemberDef *md)=0
virtual void overrideReferencesRelation(bool e)=0
virtual void setAnonymousMemberForDetails(const MemberDef *md)=0
virtual void makeForeign()=0
virtual void overrideReferencedByRelation(bool e)=0
virtual void setDocsForDefinition(bool b)=0
virtual void overrideCallGraph(bool e)=0
virtual void overrideInlineSource(bool e)=0
virtual void setBitfields(const DString &s)=0
virtual void copyArgumentNames(const MemberDef *bmd)=0
virtual void overrideEnumValues(bool e)=0
virtual void mergeMemberSpecifiers(TypeSpecifier s)=0
virtual void addQualifiers(const StringVector &qualifiers)=0
virtual void setExplicitExternal(bool b, const DString &df, int line, size_t column)=0
virtual void insertEnumField(MemberDef *md)=0
virtual void moveDeclArgumentList(std::unique_ptr< ArgumentList > al)=0
virtual void setDeclFile(const DString &df, int line, size_t column)=0
virtual void setAnonymousEnumType(const MemberDef *md)=0
virtual void overrideCallerGraph(bool e)=0
virtual void setReimplements(MemberDef *md)=0
virtual void invalidateCachedArgumentTypes()=0
virtual void setNamespace(NamespaceDef *nd)=0
virtual void moveArgumentList(std::unique_ptr< ArgumentList > al)=0
virtual void makeRelated()=0
virtual void insertReimplementedBy(MemberDef *md)=0
virtual void setAnchor()=0
A list of MemberDef objects as shown in documentation sections.
Definition memberlist.h:126
Wrapper class for the MemberListType type.
Definition types.h:346
Ptr & front()
Definition membername.h:49
size_t size() const
Definition membername.h:46
iterator begin()
Definition membername.h:35
iterator end()
Definition membername.h:36
void push_back(Ptr &&p)
Definition membername.h:52
Ordered dictionary of MemberName objects.
Definition membername.h:61
MemberName::Ptr take(const DString &key, const MemberDef *value)
Definition membername.h:63
const MemberDef * findRev(const DString &name) const
Definition memberlist.h:107
const MemberDef * find(const DString &name) const
Definition memberlist.h:94
void run()
Run mmdc tool for all collected diagrams.
Definition mermaid.cpp:255
static MermaidManager & instance()
Definition mermaid.cpp:38
void sortMemberLists()
static ModuleManager & instance()
void addDocs(const Entry *root)
void addConceptToModule(const Entry *root, ConceptDef *cd)
void addClassToModule(const Entry *root, ClassDef *cd)
void addMemberToModule(const Entry *root, MemberDef *md)
void writeDocumentation(OutputList &ol)
void addMembersToMemberGroup()
void findSectionsInDocumentation()
void distributeMemberGroupDocumentation()
An abstract interface of a namespace symbol.
virtual const LinkedRefMap< NamespaceDef > & getUsedNamespaces() const =0
virtual bool isInline() const =0
virtual void insertUsedFile(FileDef *fd)=0
virtual void findSectionsInDocumentation()=0
virtual void countMembers()=0
virtual void addUsingDirective(NamespaceDef *nd)=0
virtual void setFileName(const DString &fn)=0
virtual void insertMember(MemberDef *md)=0
virtual void setMetaData(const DString &m)=0
virtual void addUsingDeclaration(const Definition *d)=0
virtual void distributeMemberGroupDocumentation()=0
virtual void writeTagFile(TextStream &)=0
virtual void writeDocumentation(OutputList &ol)=0
virtual void setInline(bool isInline)=0
virtual void computeAnchors()=0
virtual void combineUsingRelations(NamespaceDefSet &visitedNamespace)=0
virtual void sortMemberLists()=0
virtual void addMembersToMemberGroup()=0
/dev/null outline parser
void parseInput(const DString &, const char *, const std::shared_ptr< Entry > &, ClangTUParser *) override
Parses a single input file with the goal to build an Entry tree.
void parsePrototype(const DString &) override
Callback function called by the comment block scanner.
bool needsPreprocessing(const DString &) const override
Returns true if the language identified by extension needs the C preprocessor to be run before feed t...
Abstract interface for outline parsers.
Definition parserintf.h:41
virtual bool needsPreprocessing(const DString &extension) const =0
Returns true if the language identified by extension needs the C preprocessor to be run before feed t...
virtual void parseInput(const DString &fileName, const char *fileBuf, const std::shared_ptr< Entry > &root, ClangTUParser *clangParser)=0
Parses a single input file with the goal to build an Entry tree.
Class representing a list of output generators that are written to in parallel.
Definition outputlist.h:311
void disable(OutputType o)
void add()
Definition outputlist.h:348
void enable(OutputType o)
size_t size() const
Definition outputlist.h:357
void docify(const DString &s)
Definition outputlist.h:433
void writeStyleInfo(int part)
Definition outputlist.h:391
void generateDoc(const DString &fileName, int startLine, const Definition *ctx, const MemberDef *md, const DString &docStr, const DocOptions &options)
void cleanup()
Definition outputlist.h:748
void startContents()
Definition outputlist.h:614
A model of a page symbol.
Definition pagedef.h:27
virtual void setLocalToc(const LocalToc &tl)=0
virtual void setFileName(const DString &name)=0
virtual void setShowLineNo(bool)=0
virtual void setPageScope(Definition *)=0
virtual const GroupDef * getGroupDef() const =0
Manages programming language parsers.
Definition parserintf.h:182
std::unique_ptr< CodeParserInterface > getCodeParser(const DString &extension)
Gets the interface to the parser associated with a given extension.
Definition parserintf.h:253
void registerParser(const DString &name, const OutlineParserFactory &outlineParserFactory, const CodeParserFactory &codeParserFactory)
Registers an additional parser.
Definition parserintf.h:215
std::unique_ptr< OutlineParserInterface > getOutlineParser(const DString &extension)
Gets the interface to the parser associated with a given extension.
Definition parserintf.h:244
static PlantumlManager & instance()
Definition plantuml.cpp:236
void run()
Run plant UML tool for all images.
Definition plantuml.cpp:432
void processFile(const DString &fileName, const std::string &input, std::string &output)
Definition pre.l:4302
void addSearchDir(const DString &dir)
Definition pre.l:4284
Definition qhp.h:29
static DString getQchFileName()
Definition qhp.cpp:430
static const DString qhpFileName
Definition qhp.h:49
Generator for RTF output.
Definition rtfgen.h:80
static void init()
Definition rtfgen.cpp:464
static bool preProcessFileInplace(const DString &path, const DString &name)
This is an API to a VERY brittle RTF preprocessor that combines nested RTF files.
Definition rtfgen.cpp:2468
static void writeStyleSheetFile(TextStream &t)
Definition rtfgen.cpp:397
static void writeExtensionsFile(TextStream &t)
Definition rtfgen.cpp:412
static RefListManager & instance()
Definition reflist.h:122
static RequirementManager & instance()
void writeTagFile(TextStream &tagFile)
void addRequirement(Entry *e)
Abstract proxy interface for non-javascript based search indices.
void addWord(const DString &word, bool hiPriority)
void setCurrentDoc(const Definition *ctx, const DString &anchor, bool isSourceFile)
void write(const DString &file)
class that provide information about a section.
Definition section.h:58
DString fileName() const
Definition section.h:74
int lineNr() const
Definition section.h:73
DString ref() const
Definition section.h:72
SectionInfo * replace(const DString &label, const DString &fileName, int lineNr, const DString &title, SectionType type, int level, const DString &ref=DString())
Definition section.h:157
SectionInfo * add(const SectionInfo &si)
Definition section.h:139
static SectionManager & instance()
returns a reference to the singleton
Definition section.h:179
static constexpr int Page
Definition section.h:31
std::vector< stat > stats
Definition doxygen.cpp:273
void print()
Definition doxygen.cpp:250
void begin(const char *name)
Definition doxygen.cpp:237
void end()
Definition doxygen.cpp:243
std::chrono::steady_clock::time_point startTime
Definition doxygen.cpp:274
static void showCacheUsage()
Show usage of the type lookup cache.
static void clearTypeLookupCache(ClearScope scope)
ClassDefMutable * resolveClassMutable(const Definition *scope, const DString &name, bool mayBeUnlinkable=false, bool mayBeHidden=false)
Wrapper around resolveClass that returns a mutable interface to the class object or a nullptr if the ...
const ClassDef * resolveClass(const Definition *scope, const DString &name, bool maybeUnlinkable=false, bool mayBeHidden=false)
Find the class definition matching name within the scope set.
const Definition * resolveSymbol(const Definition *scope, const DString &name, const DString &args=DString(), bool checkCV=false, bool insideCode=false, bool onlyLinkable=false)
Find the symbool definition matching name within the scope set.
DString getTemplateSpec() const
In case a call to resolveClass() points to a template specialization, the template part is return via...
const MemberDef * getTypedef() const
In case a call to resolveClass() resolves to a type member (e.g. an enum) this method will return it.
This struct is used to capture the tag file information for an Entry.
Definition entry.h:101
DString tagName
Definition entry.h:103
DString fileName
Definition entry.h:104
Text streaming class that buffers data.
Definition textstream.h:36
std::string str() const
Return the contents of the buffer as a std::string object.
Definition textstream.h:232
Class managing a pool of worker threads.
Definition threadpool.h:48
auto queue(F &&f, Args &&... args) -> std::future< decltype(f(args...))>
Queue the callable function f for the threads to execute.
Definition threadpool.h:77
virtual DString trOverloadText()=0
virtual DString trPage(bool first_capital, bool singular)=0
virtual DString trMainPage()=0
Wrapper class for a number of boolean properties.
Definition types.h:694
std::string to_string() const
Definition types.h:738
static void correctMemberProperties(MemberDefMutable *md)
static void computeVhdlComponentRelations()
ClassDefMutable * toClassDefMutable(Definition *d)
ClassDef * getClass(const DString &n)
std::unique_ptr< ClassDef > createClassDefAlias(const Definition *newScope, const ClassDef *cd)
Definition classdef.cpp:813
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:582
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:465
Class representing a regular expression.
Definition regex.h:39
Class to iterate through matches.
Definition regex.h:239
Object representing the matching results.
Definition regex.h:154
First pass comment processing.
void convertCppComments(const std::string &inBuf, std::string &outBuf, const std::string &fn)
Converts the comments in a file.
ConceptDefMutable * toConceptDefMutable(Definition *d)
std::unique_ptr< ConceptDef > createConceptDef(const DString &fileName, int startLine, size_t startColumn, const DString &name, const DString &tagRef, const DString &tagFile)
ConceptDefMutable * getConceptMutable(const DString &key)
Definition conceptdef.h:106
#define Config_getInt(name)
Definition config.h:34
#define Config_getList(name)
Definition config.h:38
#define Config_updateString(name, value)
Definition config.h:39
#define Config_updateBool(name, value)
Definition config.h:40
#define Config_getBool(name)
Definition config.h:33
#define Config_getString(name)
Definition config.h:32
#define Config_updateList(name,...)
Definition config.h:43
#define Config_getEnum(name)
Definition config.h:35
std::set< std::string > StringSet
Definition containers.h:31
std::unordered_set< std::string > StringUnorderedSet
Definition containers.h:29
std::map< std::string, std::string > StringMap
Definition containers.h:30
std::vector< std::string > StringVector
Definition containers.h:33
void parseFuncDecl(const DString &decl, const SrcLangExt lang, DString &clName, DString &type, DString &name, DString &args, DString &funcTempList, DString &exceptions)
Definition declinfo.l:318
std::unique_ptr< ArgumentList > stringToArgumentList(SrcLangExt lang, const DString &argsString, DString *extraTypeChars=nullptr)
Definition defargs.l:821
void generateDEF()
Definition defgen.cpp:463
std::unordered_map< std::string, DefineList > DefinesPerFileList
Definition define.h:50
Definition * toDefinition(DefinitionMutable *dm)
DefinitionMutable * toDefinitionMutable(Definition *d)
DirIterator begin(DirIterator it) noexcept
Definition dir.cpp:176
DirIterator end(const DirIterator &) noexcept
Definition dir.cpp:181
void buildDirectories()
Definition dirdef.cpp:1097
void computeDirDependencies()
Definition dirdef.cpp:1171
void generateDirDocs(OutputList &ol)
Definition dirdef.cpp:1188
#define AUTO_TRACE_ADD(...)
Definition docnode.cpp:54
#define AUTO_TRACE(...)
Definition docnode.cpp:53
#define AUTO_TRACE_EXIT(...)
Definition docnode.cpp:55
constexpr DocNodeVariant * parent(DocNodeVariant *n)
returns the parent node of a given node n or nullptr if the node has no parent.
Definition docnode.h:1335
static void findInheritedTemplateInstances()
Definition doxygen.cpp:5531
void printNavTree(Entry *root, int indent)
static void addClassToContext(const Entry *root)
Definition doxygen.cpp:950
static void makeTemplateInstanceRelation(const Entry *root, ClassDefMutable *cd)
Definition doxygen.cpp:5546
static StringUnorderedSet g_pathsVisited(1009)
static void buildGroupList(const Entry *root)
Definition doxygen.cpp:540
static void insertMemberAlias(Definition *outerScope, const MemberDef *md)
Definition doxygen.cpp:6919
static void findUsingDeclarations(const Entry *root, bool filterPythonPackages)
Definition doxygen.cpp:2304
static void flushCachedTemplateRelations()
Definition doxygen.cpp:9665
static void copyLatexStyleSheet()
static void generateDocsForClassList(const std::vector< ClassDefMutable * > &classList)
Definition doxygen.cpp:9325
static int findFunctionPtr(const std::string &type, SrcLangExt lang, int *pLength=nullptr)
Definition doxygen.cpp:3148
static bool isSpecialization(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists)
Definition doxygen.cpp:6217
static void computeTemplateClassRelations()
Definition doxygen.cpp:5625
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:8380
static void runQHelpGenerator()
static void addConceptToContext(const Entry *root)
Definition doxygen.cpp:1175
static void addRelatedPage(Entry *root)
Definition doxygen.cpp:436
void initDoxygen()
static StringVector g_inputFiles
Definition doxygen.cpp:191
void printSectionsTree()
class Statistics g_s
static void generateXRefPages()
Definition doxygen.cpp:5804
static Definition * buildScopeFromQualifiedName(const DString &name_, SrcLangExt lang, const TagInfo *tagInfo)
Definition doxygen.cpp:724
static void findUsingDeclImports(const Entry *root)
Definition doxygen.cpp:2457
static void copyStyleSheet()
static void addIncludeFile(Def *def, FileDef *ifd, const Entry *root)
Definition doxygen.cpp:311
FindBaseClassRelation_Mode
Definition doxygen.cpp:290
@ Undocumented
Definition doxygen.cpp:293
@ TemplateInstances
Definition doxygen.cpp:291
@ DocumentedOnly
Definition doxygen.cpp:292
void distributeClassGroupRelations()
Definition doxygen.cpp:1511
static void generateGroupDocs()
static void findDirDocumentation(const Entry *root)
Definition doxygen.cpp:9865
void checkConfiguration()
check and resolve config options
static bool findClassRelation(const Entry *root, Definition *context, ClassDefMutable *cd, const BaseInfo *bi, const TemplateNameMap &templateNames, FindBaseClassRelation_Mode mode, bool isArtificial)
Definition doxygen.cpp:5132
static void resolveTemplateInstanceInType(const Entry *root, const Definition *scope, const MemberDef *md)
Definition doxygen.cpp:5062
static void organizeSubGroupsFiltered(const Entry *root, bool additional)
Definition doxygen.cpp:580
static void warnUndocumentedNamespaces()
Definition doxygen.cpp:5580
static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments, const std::string &name)
Definition doxygen.cpp:4741
static void buildConceptList(const Entry *root)
Definition doxygen.cpp:1336
static void addMemberSpecialization(const Entry *root, MemberName *mn, ClassDefMutable *cd, const DString &funcType, const DString &funcName, const DString &funcArgs, const DString &funcDecl, const DString &exceptions, TypeSpecifier spec)
Definition doxygen.cpp:6786
static void resolveClassNestingRelations()
Definition doxygen.cpp:1391
static void generateNamespaceConceptDocs(const ConceptLinkedRefMap &conceptList)
static MemberDef * addVariableToFile(const Entry *root, MemberType mtype, const DString &scope, const DString &type, const DString &name, const DString &args)
Definition doxygen.cpp:2895
static void findClassEntries(const Entry *root)
Definition doxygen.cpp:5496
static void vhdlCorrectMemberProperties()
Definition doxygen.cpp:8624
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:6430
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:6331
static void copyLogo(const DString &outputOption, bool toIndex)
static void computeMemberReferences()
Definition doxygen.cpp:5694
static void transferRelatedFunctionDocumentation()
Definition doxygen.cpp:4655
static void addMembersToMemberGroup()
Definition doxygen.cpp:9530
static void findMainPageTagFiles(Entry *root)
static void addLocalObjCMethod(const Entry *root, const DString &scopeName, const DString &funcType, const DString &funcName, const DString &funcArgs, const DString &exceptions, const DString &funcDecl, TypeSpecifier spec)
Definition doxygen.cpp:6375
static void distributeConceptGroups()
Definition doxygen.cpp:1358
static NamespaceDef * findUsedNamespace(const LinkedRefMap< NamespaceDef > &unl, const DString &name)
Definition doxygen.cpp:2134
static void transferFunctionDocumentation()
Definition doxygen.cpp:4574
static void setAnonymousEnumType()
Definition doxygen.cpp:9270
static void sortMemberLists()
Definition doxygen.cpp:9175
static void findMember(const Entry *root, const DString &relates, const DString &type, const DString &args, DString funcDecl, bool overloaded, bool isFunc)
Definition doxygen.cpp:6962
static void createTemplateInstanceMembers()
Definition doxygen.cpp:8752
void transferStaticInstanceInitializers()
Definition doxygen.cpp:4704
static void findObjCMethodDefinitions(const Entry *root)
Definition doxygen.cpp:7768
static void addMemberDocs(const Entry *root, MemberDefMutable *md, const DString &funcDecl, const ArgumentList *al, bool over_load, TypeSpecifier spec)
Definition doxygen.cpp:5818
static void dumpSymbolMap()
static void buildTypedefList(const Entry *root)
Definition doxygen.cpp:3640
static void findGroupScope(const Entry *root)
Definition doxygen.cpp:555
static void generateFileDocs()
Definition doxygen.cpp:8988
static int findEndOfTemplate(const DString &s, size_t startPos)
Definition doxygen.cpp:3351
static void findDefineDocumentation(Entry *root)
Definition doxygen.cpp:9778
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:4804
void parseInput()
static void findMemberDocumentation(const Entry *root)
Definition doxygen.cpp:7738
static void distributeMemberGroupDocumentation()
Definition doxygen.cpp:9568
static void generateNamespaceClassDocs(const ClassLinkedRefMap &classList)
static void addEnumValuesToEnums(const Entry *root)
Definition doxygen.cpp:7972
static void generatePageDocs()
static void resolveUserReferences()
static void buildRequirementsList(Entry *root)
Definition doxygen.cpp:9925
static void compareDoxyfile(Config::CompareMode diffList)
static void addPageToContext(PageDef *pd, Entry *root)
Definition doxygen.cpp:417
static std::shared_ptr< Entry > parseFile(OutlineParserInterface &parser, FileDef *fd, const DString &fn, ClangTUParser *clangParser, bool newTU)
static void buildVarList(const Entry *root)
Definition doxygen.cpp:3777
static void copyIcon(const DString &outputOption, bool toIndex)
static void buildSequenceList(const Entry *root)
Definition doxygen.cpp:3740
static void generateFileSources()
Definition doxygen.cpp:8822
static void copyExtraFiles(StringVector files, const DString &filesOption, const DString &outputOption, bool toIndex)
static void generateClassDocs()
Definition doxygen.cpp:9423
static int findTemplateSpecializationPosition(const DString &name)
Definition doxygen.cpp:5102
static void buildNamespaceList(const Entry *root)
Definition doxygen.cpp:1963
static void findIncludedUsingDirectives()
Definition doxygen.cpp:2707
static void addDefineDoc(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:9751
static void countMembers()
Definition doxygen.cpp:9284
void clearAll()
Definition doxygen.cpp:205
static void devUsage()
static ClassDef * findClassWithinClassContext(Definition *context, ClassDef *cd, const DString &name)
Definition doxygen.cpp:4769
static void organizeSubGroups(const Entry *root)
Definition doxygen.cpp:599
static void applyMemberOverrideOptions(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:2396
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:6850
static void findFriends()
Definition doxygen.cpp:4477
static void findEnums(const Entry *root)
Definition doxygen.cpp:7796
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:9400
static ClassDefMutable * createTagLessInstance(const Definition *root, const ClassDef *templ, const DString &fieldName, bool hasNamedVariable)
Definition doxygen.cpp:1572
static void addEnumDocs(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:8220
static void addListReferences()
Definition doxygen.cpp:5795
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:6009
static bool isClassSection(const Entry *root)
Definition doxygen.cpp:5474
static void processTagLessClasses(const Definition *root, const Container *def, const TagContainer *tagParent, MemberListType varFilter, MemberListType typeFilter, const DString &prefix, int count)
Look through the members of class cd and its public members.
Definition doxygen.cpp:1878
static void buildGroupListFiltered(const Entry *root, bool additional, bool includeExternal)
Definition doxygen.cpp:467
static void runHtmlHelpCompiler()
static void addMembersToIndex()
Definition doxygen.cpp:8414
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:5505
static void findMainPage(Entry *root)
Definition doxygen.cpp:9964
static ClassDef::CompoundType convertToCompoundType(EntryType section, TypeSpecifier specifier)
Definition doxygen.cpp:908
DString stripTemplateSpecifiers(const DString &s)
Definition doxygen.cpp:697
static void findUsingDirectives(const Entry *root)
Definition doxygen.cpp:2147
static void addInterfaceOrServiceToServiceOrSingleton(const Entry *root, ClassDefMutable *cd, DString const &rname)
Definition doxygen.cpp:3809
static bool g_successfulRun
Definition doxygen.cpp:194
static bool tryAddEnumDocsToGroupMember(const Entry *root, const DString &name)
Definition doxygen.cpp:8262
static void addSourceReferences()
Definition doxygen.cpp:9048
static void associateVariableWithAnonymousEnumType(const MemberDef *md, const Container *cd, const MemberDef *enumTypeMember, MemberListType mlFilter)
Definition doxygen.cpp:1545
static void createUsingMemberImportForClass(const Entry *root, ClassDefMutable *cd, const MemberDef *md, const DString &fileName, const DString &memName)
Definition doxygen.cpp:2408
static DString substituteTemplatesInString(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists, const std::string &src)
Definition doxygen.cpp:6247
std::function< std::unique_ptr< T >() > make_parser_factory()
static void buildExampleList(Entry *root)
static void inheritDocumentation()
Definition doxygen.cpp:9470
static void flushUnresolvedRelations()
Definition doxygen.cpp:9707
static bool isSymbolHidden(const Definition *d)
Definition doxygen.cpp:9217
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:5011
static void findDocumentedEnumValues()
Definition doxygen.cpp:8406
static void findTagLessClasses()
Definition doxygen.cpp:1911
static void generateDiskNames()
static void addToIndices()
Definition doxygen.cpp:8456
static void computeClassRelations()
Definition doxygen.cpp:5600
static void buildFunctionList(const Entry *root)
Definition doxygen.cpp:4172
static void checkPageRelations()
static void addGlobalFunction(const Entry *root, const DString &rname, const DString &sc)
Definition doxygen.cpp:4062
static void findModuleDocumentation(const Entry *root)
Definition doxygen.cpp:1326
static MemberDef * addVariableToClass(const Entry *root, ClassDefMutable *cd, MemberType mtype, const DString &type, const DString &name, const DString &args, Protection prot, Relationship related)
Definition doxygen.cpp:2724
static void readTagFile(const std::shared_ptr< Entry > &root, const DString &tagLine)
static void findEnumDocumentation(const Entry *root)
Definition doxygen.cpp:8295
static void computePageRelations(Entry *root)
static void processTagLessClassesForList(const Definition *root, const Container *def, const TagContainer *tagParent, MemberListType varFilter, MemberListType typeFilter, const DString &prefix, int count)
Definition doxygen.cpp:1771
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:3925
void initResources()
static bool isVarWithConstructor(const Entry *root)
Definition doxygen.cpp:3208
static StringSet g_usingDeclarations
Definition doxygen.cpp:193
static void buildDictionaryList(const Entry *root)
Definition doxygen.cpp:3758
static bool haveEqualFileNames(const Entry *root, const MemberDef *md)
Definition doxygen.cpp:9740
void readFileOrDirectory(const DString &s, FileNameLinkedMap *fnMap, StringUnorderedSet *exclSet, const StringVector *patList, const StringVector *exclPatList, StringVector *resultList, StringUnorderedSet *resultSet, bool recursive, bool errorIfNotExist, StringUnorderedSet *killSet, StringUnorderedSet *paths)
static void generateNamespaceDocs()
static void buildClassDocList(const Entry *root)
Definition doxygen.cpp:1161
static void buildPageList(Entry *root)
Definition doxygen.cpp:9937
static void writeTagFile()
static void addRequirementReferences()
Definition doxygen.cpp:5787
static void computeVerifiedDotPath()
static bool g_singleComment
Definition doxygen.cpp:197
static void findSectionsInDocumentation()
Definition doxygen.cpp:9606
static void mergeCategories()
Definition doxygen.cpp:8771
static const StringUnorderedSet g_compoundKeywords
Definition doxygen.cpp:202
static bool scopeIsTemplate(const Definition *d)
Definition doxygen.cpp:6233
static void buildFileList(const Entry *root)
Definition doxygen.cpp:611
static void buildClassList(const Entry *root)
Definition doxygen.cpp:1151
static void usage(const DString &name, const DString &versionString)
static void findUsedTemplateInstances()
Definition doxygen.cpp:5564
static void computeTooltipTexts()
Definition doxygen.cpp:9224
static void addVariable(const Entry *root, int isFuncPtr=-1)
Definition doxygen.cpp:3419
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:4960
static void parseFilesSingleThreading(const std::shared_ptr< Entry > &root)
parse the list of input files
static void buildCompleteMemberLists()
Definition doxygen.cpp:8792
static const ClassDef * findClassDefinition(FileDef *fd, NamespaceDef *nd, const DString &scopeName)
Definition doxygen.cpp:5970
static void filterMemberDocumentation(const Entry *root, const DString &relates)
Definition doxygen.cpp:7589
static void generateConceptDocs()
Definition doxygen.cpp:9449
static bool isRecursiveBaseClass(const DString &scope, const DString &name)
Definition doxygen.cpp:5091
static std::unique_ptr< OutlineParserInterface > getParserForFile(const DString &fn)
static void combineUsingRelations()
Definition doxygen.cpp:9505
static const char * getArg(int argc, char **argv, int &optInd)
std::unique_ptr< ArgumentList > getTemplateArgumentsFromName(const DString &name, const ArgumentLists &tArgLists)
Definition doxygen.cpp:878
static void checkMarkdownMainfile()
static std::unordered_map< std::string, std::vector< ClassDefMutable * > > g_usingClassMap
Definition doxygen.cpp:2455
static void buildConceptDocList(const Entry *root)
Definition doxygen.cpp:1346
static bool isEntryInGroupOfMember(const Entry *root, const MemberDef *md, bool allowNoGroup=false)
Definition doxygen.cpp:5985
static void applyToAllDefinitions(Func func)
Definition doxygen.cpp:5730
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:4607
static void buildDefineList()
Definition doxygen.cpp:9127
static void buildInterfaceAndServiceList(const Entry *root)
Definition doxygen.cpp:3873
static Definition * findScopeFromQualifiedName(NamespaceDefMutable *startScope, const DString &n, FileDef *fileScope, const TagInfo *tagInfo)
Definition doxygen.cpp:793
static void computeMemberRelations()
Definition doxygen.cpp:8736
static void buildListOfUsingDecls(const Entry *root)
Definition doxygen.cpp:2291
static void computeMemberRelationsForBaseClass(const ClassDef *cd, const BaseClassDef *bcd)
Definition doxygen.cpp:8656
static std::multimap< std::string, const Entry * > g_classEntries
Definition doxygen.cpp:190
std::vector< InputFileEncoding > InputFileEncodingList
Definition doxygen.h:73
std::unordered_map< std::string, BodyInfo > StaticInitMap
Definition doxygen.h:77
std::unordered_map< std::string, NamespaceAliasInfo > NamespaceAliasInfoMap
Definition doxygen.h:79
std::unordered_map< std::string, const Definition * > ClangUsrMap
Definition doxygen.h:75
DString substitute(const DString &s, const DString &src, const DString &dst)
substitute all occurrences of src in s by dst
Definition dstring.cpp:485
int dstricmp(const char *s1, const char *s2)
Definition dstring.cpp:444
uint32_t dstrlen(const char *str)
Returns the length of string str, or 0 if a null pointer is passed.
Definition dstring.h:39
int dstricmp_sort(const char *str1, const char *str2)
Definition dstring.h:67
int dstrcmp(const char *str1, const char *str2)
Definition dstring.h:50
const char * qPrint(const char *s)
Definition dstring.h:783
FileDef * toFileDef(Definition *d)
Definition filedef.cpp:2033
std::unique_ptr< FileDef > createFileDef(const DString &p, const DString &n, const DString &ref, const DString &dn)
Definition filedef.cpp:269
std::unordered_set< const FileDef * > FileDefSet
Definition filedef.h:41
void addNamespaceToGroups(const Entry *root, NamespaceDef *nd)
void addGroupToGroups(const Entry *root, GroupDef *subGroup)
void addClassToGroups(const Entry *root, ClassDef *cd)
void addDirToGroups(const Entry *root, DirDef *dd)
std::unique_ptr< GroupDef > createGroupDef(const DString &fileName, int line, const DString &name, const DString &title, const DString &refFileName)
Definition groupdef.cpp:184
void addConceptToGroups(const Entry *root, ConceptDef *cd)
void addMemberToGroups(const Entry *root, MemberDef *md)
void writeGraphInfo(OutputList &ol)
Definition index.cpp:4097
void endTitle(OutputList &ol, const DString &fileName, const DString &name)
Definition index.cpp:398
void writeIndexHierarchy(OutputList &ol)
Definition index.cpp:5811
void endFile(OutputList &ol, bool skipNavIndex, bool skipEndContents, const DString &navPath)
Definition index.cpp:431
void startTitle(OutputList &ol, const DString &fileName, const DefinitionMutable *def)
Definition index.cpp:388
void startFile(OutputList &ol, const DString &name, bool isSource, const DString &manName, const DString &title, HighlightedItem hli, bool additionalIndices, const DString &altSidebarName, int hierarchyLevel, const DString &allMembersFile)
Definition index.cpp:405
Translator * theTranslator
Definition language.cpp:76
void setTranslator(OUTPUT_LANGUAGE_t langName)
Definition language.cpp:78
#define LATEX_STYLE_EXTENSION
Definition latexgen.h:21
void writeDefaultLayoutFile(const DString &fileName)
Definition layout.cpp:1736
void printLayout()
Definition layout.cpp:1824
std::unique_ptr< MemberDef > createMemberDef(const DString &defFileName, int defLine, size_t defColumn, const DString &type, const DString &name, const DString &args, const DString &excp, Protection prot, Specifier virt, bool stat, Relationship related, MemberType t, const ArgumentList &tal, const ArgumentList &al, const DString &metaData)
Factory method to create a new instance of a MemberDef.
std::unique_ptr< MemberDef > createMemberDefAlias(const Definition *newScope, const MemberDef *aliasMd)
MemberDefMutable * toMemberDefMutable(Definition *d)
void mergeMemberOverrideOptions(MemberDefMutable *md1, MemberDefMutable *md2)
void combineDeclarationAndDefinition(MemberDefMutable *mdec, MemberDefMutable *mdef)
MemberDef * toMemberDef(Definition *d)
std::unordered_map< int, std::unique_ptr< MemberGroupInfo > > MemberGroupInfoMap
#define DOX_NOGROUP
Definition membergroup.h:26
void initWarningFormat()
Definition message.cpp:233
void warn_flush()
Definition message.cpp:226
DString warn_line(const DString &file, int line)
Definition message.cpp:211
void finishWarnExit()
Definition message.cpp:291
#define warn_undoc(file, line, fmt,...)
Definition message.h:102
#define warn_uncond(fmt,...)
Definition message.h:122
#define warn(file, line, fmt,...)
Definition message.h:97
#define msg(fmt,...)
Definition message.h:94
#define err(fmt,...)
Definition message.h:127
#define ASSERT(x)
Definition message.h:142
#define term(fmt,...)
Definition message.h:137
void postProcess(bool clearHeaderAndFooter, CompareMode compareMode=CompareMode::Full)
CompareMode
Definition config.h:54
void checkAndCorrect(bool quiet, const bool check)
void compareDoxyfile(TextStream &t, CompareMode compareMode)
void writeTemplate(TextStream &t, bool shortList, bool updateOnly=false)
void deinit()
void init()
void updateObsolete()
bool parse(const DString &fileName, bool update=false, CompareMode compareMode=CompareMode::Full)
void correctPath(const StringVector &list)
Correct a possible wrong PATH variable.
Definition portable.cpp:532
FILE * popen(const DString &name, const DString &type)
Definition portable.cpp:495
double getSysElapsedTime()
Definition portable.cpp:113
void setenv(const DString &variable, const DString &value)
Definition portable.cpp:302
uint32_t pid()
Definition portable.cpp:264
int system(const DString &command, const DString &args, bool commandHasConsole=true)
Definition portable.cpp:121
int pclose(FILE *stream)
Definition portable.cpp:504
DString getenv(const DString &variable)
Definition portable.cpp:337
bool isAbsolutePath(const DString &fileName)
Definition portable.cpp:513
DString pathListSeparator()
Definition portable.cpp:399
std::ofstream openOutputStream(const DString &name, bool append=false)
Definition portable.cpp:681
const char * commandExtension()
Definition portable.cpp:477
void setShortDir()
Definition portable.cpp:569
DString trunc(const DString &s, size_t numChars=15)
Definition trace.h:56
std::unique_ptr< NamespaceDef > createNamespaceDef(const DString &defFileName, int defLine, size_t defColumn, const DString &name, const DString &ref, const DString &refFile, const DString &type, bool isPublished)
Factory method to create new NamespaceDef instance.
std::unique_ptr< NamespaceDef > createNamespaceDefAlias(const Definition *newScope, const NamespaceDef *nd)
Factory method to create an alias of an existing namespace.
void replaceNamespaceAliases(DString &name)
NamespaceDef * getResolvedNamespace(const DString &name)
NamespaceDef * toNamespaceDef(Definition *d)
NamespaceDefMutable * toNamespaceDefMutable(Definition *d)
NamespaceDefMutable * getResolvedNamespaceMutable(const DString &key)
std::unordered_set< const NamespaceDef * > NamespaceDefSet
bool search(std::string_view str, Match &match, const Ex &re, size_t pos)
Search in a given string str starting at position pos for a match against regular expression re.
Definition regex.cpp:850
std::unique_ptr< PageDef > createPageDef(const DString &f, int l, const DString &n, const DString &d, const DString &t)
Definition pagedef.cpp:89
void generatePerlMod()
void setPerlModDoxyfile(const DString &qs)
Portable versions of functions that are platform dependent.
int portable_iconv_close(void *cd)
void * portable_iconv_open(const char *tocode, const char *fromcode)
void addRefItem(const RefItemVector &sli, const DString &key, const DString &prefix, const DString &name, const DString &title, const DString &args, const Definition *scope)
Definition reflist.h:136
void initSearchIndexer()
void finalizeSearchIndexer()
void createJavaScriptSearchIndex()
void writeJavaScriptSearchIndex()
Javascript based search engine.
void generateHtmlForComment(const std::string &fn, const std::string &text)
Helper for implemented the -c option of doxygen, which produces HTML output for a given doxygen forma...
void generateSqlite3()
void addSTLSupport(std::shared_ptr< Entry > &root)
Add stub entries for the most used classes in the standard template library.
Some helper functions for std::string.
void addTerminalCharIfMissing(std::string &s, char c)
Definition stringutil.h:87
Class that contains information about an inheritance relation.
Definition classdef.h:51
ClassDef * classDef
Class definition that this relation inherits from.
Definition classdef.h:56
This class stores information about an inheritance relation.
Definition entry.h:88
Protection prot
inheritance type
Definition entry.h:93
Specifier virt
virtualness
Definition entry.h:94
DString name
the name of the base class
Definition entry.h:92
Data associated with description found in the body.
Definition definition.h:64
Helper class to pass options when calling OutputList::generateDoc().
Definition docoptions.h:24
Grouping info.
Definition types.h:227
DString groupname
name of the group
Definition types.h:257
static constexpr const char * getGroupPriName(GroupPri_t priority) noexcept
Definition types.h:240
@ GROUPING_INGROUP
membership in group was defined by @ingroup
Definition types.h:236
GroupPri_t pri
priority of this definition
Definition types.h:258
static bool execute(const DString &htmldir)
Definition htags.cpp:41
static bool loadFilemap(const DString &htmldir)
Definition htags.cpp:110
static bool useHtags
Definition htags.h:23
stat(const char *n, double el)
Definition doxygen.cpp:271
const char * name
Definition doxygen.cpp:268
void parseTagFile(const std::shared_ptr< Entry > &root, const char *fullName)
void exitTracing()
Definition trace.cpp:55
void initTracing(const DString &logFile, bool timing)
Definition trace.cpp:25
#define TRACE(...)
Definition trace.h:77
MemberType
Definition types.h:569
bool isTypeAClassFriend(const DString &type)
Definition types.h:920
Protection
Definition types.h:32
SrcLangExt
Definition types.h:207
Relationship
Definition types.h:167
Specifier
Definition types.h:80
bool useCaseSenseNames()
Returns true if the names of the symbols can be case sensitive.
Definition util.cpp:2680
DString substituteTemplateArgumentsInString(const DString &nm, const ArgumentList &formalArgs, const ArgumentList *actualArgs)
Definition util.cpp:3525
bool protectionLevelVisible(Protection prot)
Definition util.cpp:4672
DString stripFromIncludePath(const DString &path)
Definition util.cpp:227
DString mergeScopes(const DString &leftScope, const DString &rightScope)
Definition util.cpp:3754
DString filterTitle(const DString &title)
Definition util.cpp:4455
bool matchTemplateArguments(const ArgumentList &srcAl, const ArgumentList &dstAl)
Definition util.cpp:1833
void addCodeOnlyMappings()
Definition util.cpp:4163
bool rightScopeMatch(const DString &scope, const DString &name)
Definition util.cpp:729
bool checkIfTypedef(const Definition *scope, const FileDef *fileScope, const DString &n)
Definition util.cpp:4272
DString replaceAnonymousScopes(const DString &s, const DString &replacement)
Definition util.cpp:155
void cleanupInlineGraphs()
Definition util.cpp:5405
int computeQualifiedIndex(const DString &name)
Return the index of the last :: in the string name that is still before the first <.
Definition util.cpp:5300
DString removeRedundantWhiteSpace(const DString &s)
Definition util.cpp:426
bool checkExtension(const DString &fName, const DString &ext)
Definition util.cpp:3927
DString convertNameToFile(const DString &name, bool allowDots, bool allowUnderscore)
Definition util.cpp:2864
bool leftScopeMatch(const DString &scope, const DString &name)
Definition util.cpp:740
DString tempArgListToString(const ArgumentList &al, SrcLangExt lang, bool includeDefault)
Definition util.cpp:903
DString getFileNameExtension(const DString &fn)
Definition util.cpp:4211
DString resolveTypeDef(const Definition *context, const DString &qualifiedName, const Definition **typedefContext)
Definition util.cpp:232
bool readInputFile(const DString &fileName, std::string &contents, bool filter, bool isSourceCode)
read a file name fileName and optionally filter and transcode it
Definition util.cpp:4375
DString normalizeNonTemplateArgumentsInString(const DString &name, const Definition *context, const ArgumentList &formalArgs)
Definition util.cpp:3466
SrcLangExt getLanguageFromFileName(const DString &fileName, SrcLangExt defLang)
Definition util.cpp:4169
bool matchArguments2(const Definition *srcScope, const FileDef *srcFileScope, const DString &srcReturnType, const ArgumentList *srcAl, const Definition *dstScope, const FileDef *dstFileScope, const DString &dstReturnType, const ArgumentList *dstAl, bool checkCV, SrcLangExt lang)
Definition util.cpp:1589
void initDefaultExtensionMapping()
Definition util.cpp:4096
void addHtmlExtensionIfMissing(DString &fName)
Definition util.cpp:3932
DString fileToString(const DString &name, bool filter, bool isSourceCode)
Definition util.cpp:1053
void extractNamespaceName(const DString &scopeName, DString &className, DString &namespaceName, bool allowEmptyClass)
Definition util.cpp:3027
DString stripTemplateSpecifiersFromScope(const DString &fullName, bool parentOnly, DString *pLastScopeStripped, DString scopeName, bool allowArtificial)
Definition util.cpp:3687
DString argListToString(const ArgumentList &al, bool useCanonicalType, bool showDefVals)
Definition util.cpp:859
DString projectLogoFile()
Definition util.cpp:2581
bool copyFile(const DString &src, const DString &dest)
Copies the contents of file with name src to the newly created file with name dest.
Definition util.cpp:4620
int getPrefixIndex(const DString &name)
Definition util.cpp:2652
DString getLanguageSpecificSeparator(SrcLangExt lang, bool classScope)
Definition util.cpp:4630
bool updateLanguageMapping(const DString &extension, const DString &language)
Definition util.cpp:4064
DString mangleCSharpGenericName(const DString &name)
Definition util.cpp:5341
void mergeArguments(ArgumentList &srcAl, ArgumentList &dstAl, bool forceNameOverwrite)
Definition util.cpp:1689
int getScopeFragment(const DString &s, int p, int *l)
Definition util.cpp:3799
int extractClassNameFromType(const DString &type, int &pos, DString &name, DString &templSpec, SrcLangExt lang)
Definition util.cpp:3381
bool openOutputFile(const DString &outFile, std::ofstream &f)
Definition util.cpp:4987
DString stripAnonymousNamespaceScope(const DString &s)
Definition util.cpp:167
A bunch of utility functions.
void generateXML()
Definition xmlgen.cpp:2331