Doxygen
Loading...
Searching...
No Matches
doxygen.cpp
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * Copyright (C) 1997-2015 by Dimitri van Heesch.
4 *
5 * Permission to use, copy, modify, and distribute this software and its
6 * documentation under the terms of the GNU General Public License is hereby
7 * granted. No representations are made about the suitability of this software
8 * for any purpose. It is provided "as is" without express or implied warranty.
9 * See the GNU General Public License for more details.
10 *
11 * Documents produced by Doxygen are derivative works derived from the
12 * input used in their production; they are not affected by this license.
13 *
14 */
15
16#include <cstdio>
17#include <cstdlib>
18#include <cerrno>
19#include <sys/stat.h>
20
21#include <algorithm>
22#include <unordered_map>
23#include <memory>
24#include <cinttypes>
25#include <chrono>
26#include <clocale>
27#include <locale>
28
29#include "aliases.h"
30#include "arguments.h"
31#include "cite.h"
32#include "clangparser.h"
33#include "classlist.h"
34#include "cmdmapper.h"
35#include "code.h"
36#include "commentcnv.h"
37#include "conceptdef.h"
38#include "config.h"
39#include "debug.h"
40#include "declinfo.h"
41#include "defargs.h"
42#include "defgen.h"
43#include "dir.h"
44#include "dirdef.h"
45#include "docbookgen.h"
46#include "docparser.h"
47#include "docsets.h"
48#include "dot.h"
49#include "doxygen.h"
50#include "eclipsehelp.h"
51#include "emoji.h"
52#include "entry.h"
53#include "fileinfo.h"
54#include "filename.h"
55#include "fileparser.h"
56#include "formula.h"
57#include "fortrancode.h"
58#include "fortranscanner.h"
59#include "ftvhelp.h"
60#include "groupdef.h"
61#include "htags.h"
62#include "htmlgen.h"
63#include "htmlhelp.h"
64#include "index.h"
65#include "indexlist.h"
66#include "language.h"
67#include "latexgen.h"
68#include "layout.h"
69#include "lexcode.h"
70#include "lexscanner.h"
71#include "mangen.h"
72#include "markdown.h"
73#include "membergroup.h"
74#include "memberlist.h"
75#include "membername.h"
76#include "message.h"
77#include "moduledef.h"
78#include "msc.h"
79#include "namespacedef.h"
80#include "outputlist.h"
81#include "pagedef.h"
82#include "parserintf.h"
83#include "perlmodgen.h"
84#include "plantuml.h"
85#include "portable.h"
86#include "pre.h"
87#include "pycode.h"
88#include "pyscanner.h"
89#include "qhp.h"
90#include "reflist.h"
91#include "regex.h"
92#include "requirement.h"
93#include "rtfgen.h"
94#include "scanner.h"
95#include "searchindex_js.h"
96#include "settings.h"
97#include "singlecomment.h"
98#include "sitemap.h"
99#include "sqlcode.h"
100#include "sqlite3gen.h"
101#include "stlsupport.h"
102#include "stringutil.h"
103#include "symbolresolver.h"
104#include "tagreader.h"
105#include "threadpool.h"
106#include "trace.h"
107#include "util.h"
108#include "version.h"
109#include "vhdlcode.h"
110#include "vhdldocgen.h"
111#include "vhdljjparser.h"
112#include "xmlcode.h"
113#include "xmlgen.h"
114
115#include <sqlite3.h>
116
117#if USE_LIBCLANG
118#if defined(__GNUC__)
119#pragma GCC diagnostic push
120#pragma GCC diagnostic ignored "-Wshadow"
121#endif
122#include <clang/Basic/Version.h>
123#if defined(__GNUC__)
124#pragma GCC diagnostic pop
125#endif
126#endif
127
128// provided by the generated file resources.cpp
129extern void initResources();
130
131#if !defined(_WIN32) || defined(__CYGWIN__)
132#include <signal.h>
133#define HAS_SIGNALS
134#endif
135
136// globally accessible variables
148FileNameLinkedMap *Doxygen::includeNameLinkedMap = nullptr; // include names
154FileNameLinkedMap *Doxygen::plantUmlFileNameLinkedMap = nullptr;// plantuml files
156StringMap Doxygen::tagDestinationMap; // all tag locations
157StringUnorderedSet Doxygen::tagFileSet; // all tag file names
158StringUnorderedSet Doxygen::expandAsDefinedSet; // all macros that should be expanded
159MemberGroupInfoMap Doxygen::memberGroupInfoMap; // dictionary of the member groups heading
160std::unique_ptr<PageDef> Doxygen::mainPage;
161std::unique_ptr<NamespaceDef> Doxygen::globalNamespaceDef;
183std::mutex Doxygen::addExampleMutex;
185
186// locally accessible globals
187static std::multimap< std::string, const Entry* > g_classEntries;
189static OutputList *g_outputList = nullptr; // list of output generating objects
190static StringSet g_usingDeclarations; // used classes
191static bool g_successfulRun = FALSE;
192static bool g_dumpSymbolMap = FALSE;
194static bool g_singleComment=false;
195
196
197
198// keywords recognized as compounds
200{ "template class", "template struct", "class", "struct", "union", "interface", "exception" };
201
203{
204 g_inputFiles.clear();
205 //g_excludeNameDict.clear();
206 //delete g_outputList; g_outputList=nullptr;
207
212 Doxygen::pageLinkedMap->clear();
225 Doxygen::mainPage.reset();
227}
228
230{
231 public:
233 void begin(const char *name)
234 {
235 msg("{}", name);
236 stats.emplace_back(name,0);
237 startTime = std::chrono::steady_clock::now();
238 }
239 void end()
240 {
241 std::chrono::steady_clock::time_point endTime = std::chrono::steady_clock::now();
242 stats.back().elapsed = static_cast<double>(std::chrono::duration_cast<
243 std::chrono::microseconds>(endTime - startTime).count())/1000000.0;
244 warn_flush();
245 }
246 void print()
247 {
248 bool restore=FALSE;
250 {
252 restore=TRUE;
253 }
254 msg("----------------------\n");
255 for (const auto &s : stats)
256 {
257 msg("Spent {:.6f} seconds in {}",s.elapsed,s.name);
258 }
259 if (restore) Debug::setFlag(Debug::Time);
260 }
261 private:
262 struct stat
263 {
264 const char *name;
265 double elapsed;
266 //stat() : name(nullptr),elapsed(0) {}
267 stat(const char *n, double el) : name(n),elapsed(el) {}
268 };
269 std::vector<stat> stats;
270 std::chrono::steady_clock::time_point startTime;
272
273
274static void addMemberDocs(const Entry *root,MemberDefMutable *md, const QCString &funcDecl,
275 const ArgumentList *al,bool over_load,TypeSpecifier spec);
276static void findMember(const Entry *root,
277 const QCString &relates,
278 const QCString &type,
279 const QCString &args,
280 QCString funcDecl,
281 bool overloaded,
282 bool isFunc
283 );
284
291
292
293static bool findClassRelation(
294 const Entry *root,
295 Definition *context,
296 ClassDefMutable *cd,
297 const BaseInfo *bi,
298 const TemplateNameMap &templateNames,
299 /*bool insertUndocumented*/
301 bool isArtificial
302 );
303
304//----------------------------------------------------------------------------
305
307 FileDef *fileScope,const TagInfo *tagInfo);
308static void resolveTemplateInstanceInType(const Entry *root,const Definition *scope,const MemberDef *md);
309
310static void addPageToContext(PageDef *pd,Entry *root)
311{
312 if (root->parent()) // add the page to it's scope
313 {
314 QCString scope = root->parent()->name;
315 if (root->parent()->section.isPackageDoc())
316 {
317 scope=substitute(scope,".","::");
318 }
319 scope = stripAnonymousNamespaceScope(scope);
320 scope+="::"+pd->name();
322 if (d)
323 {
324 pd->setPageScope(d);
325 }
326 }
327}
328
329static void addRelatedPage(Entry *root)
330{
331 GroupDef *gd=nullptr;
332 for (const Grouping &g : root->groups)
333 {
334 if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname))) break;
335 }
336 //printf("---> addRelatedPage() %s gd=%p\n",qPrint(root->name),gd);
337 QCString doc=root->doc+root->inbodyDocs;
338
339 PageDef *pd = addRelatedPage(root->name, // name
340 root->args, // ptitle
341 doc, // doc
342 root->docFile, // fileName
343 root->docLine, // docLine
344 root->startLine, // startLine
345 root->sli, // sli
346 gd, // gd
347 root->tagInfo(), // tagInfo
348 FALSE, // xref
349 root->lang // lang
350 );
351 if (pd)
352 {
353 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
355 pd->setLocalToc(root->localToc);
356 addPageToContext(pd,root);
357 }
358}
359
360static void buildGroupListFiltered(const Entry *root,bool additional, bool includeExternal)
361{
362 if (root->section.isGroupDoc() && !root->name.isEmpty() &&
363 ((!includeExternal && root->tagInfo()==nullptr) ||
364 ( includeExternal && root->tagInfo()!=nullptr))
365 )
366 {
367 AUTO_TRACE("additional={} includeExternal={}",additional,includeExternal);
368 if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
369 (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
370 {
371 GroupDef *gd = Doxygen::groupLinkedMap->find(root->name);
372 AUTO_TRACE_ADD("Processing group '{}':'{}' gd={}", root->type,root->name,(void*)gd);
373
374 if (gd)
375 {
376 if ( !gd->hasGroupTitle() )
377 {
378 gd->setGroupTitle( root->type );
379 }
380 else if ( root->type.length() > 0 && root->name != root->type && gd->groupTitle() != root->type )
381 {
382 warn( root->fileName,root->startLine,
383 "group {}: ignoring title \"{}\" that does not match old title \"{}\"",
384 root->name, root->type, gd->groupTitle() );
385 }
386 gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
387 gd->setDocumentation( root->doc, root->docFile, root->docLine );
388 gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
390 gd->setRefItems(root->sli);
392 gd->setLanguage(root->lang);
394 {
395 root->commandOverrides.apply_groupGraph([&](bool b) { gd->overrideGroupGraph(b); });
396 }
397 }
398 else
399 {
400 if (root->tagInfo())
401 {
402 gd = Doxygen::groupLinkedMap->add(root->name,
403 std::unique_ptr<GroupDef>(
404 createGroupDef(root->fileName,root->startLine,root->name,root->type,root->tagInfo()->fileName)));
405 gd->setReference(root->tagInfo()->tagName);
406 }
407 else
408 {
409 gd = Doxygen::groupLinkedMap->add(root->name,
410 std::unique_ptr<GroupDef>(
411 createGroupDef(root->fileName,root->startLine,root->name,root->type)));
412 }
413 gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
414 // allow empty docs for group
415 gd->setDocumentation(!root->doc.isEmpty() ? root->doc : QCString(" "),root->docFile,root->docLine,FALSE);
416 gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
418 gd->setRefItems(root->sli);
420 gd->setLanguage(root->lang);
422 {
423 root->commandOverrides.apply_groupGraph([&](bool b) { gd->overrideGroupGraph(b); });
424 }
425 }
426 }
427 }
428 for (const auto &e : root->children()) buildGroupListFiltered(e.get(),additional,includeExternal);
429}
430
431static void buildGroupList(const Entry *root)
432{
433 // --- first process only local groups
434 // first process the @defgroups blocks
436 // then process the @addtogroup, @weakgroup blocks
438
439 // --- then also process external groups
440 // first process the @defgroups blocks
442 // then process the @addtogroup, @weakgroup blocks
444}
445
446static void findGroupScope(const Entry *root)
447{
448 if (root->section.isGroupDoc() && !root->name.isEmpty() &&
449 root->parent() && !root->parent()->name.isEmpty())
450 {
451 GroupDef *gd = Doxygen::groupLinkedMap->find(root->name);
452 if (gd)
453 {
454 QCString scope = root->parent()->name;
455 if (root->parent()->section.isPackageDoc())
456 {
457 scope=substitute(scope,".","::");
458 }
459 scope = stripAnonymousNamespaceScope(scope);
460 scope+="::"+gd->name();
462 if (d)
463 {
464 gd->setGroupScope(d);
465 }
466 }
467 }
468 for (const auto &e : root->children()) findGroupScope(e.get());
469}
470
471static void organizeSubGroupsFiltered(const Entry *root,bool additional)
472{
473 if (root->section.isGroupDoc() && !root->name.isEmpty())
474 {
475 AUTO_TRACE("additional={}",additional);
476 if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
477 (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
478 {
479 GroupDef *gd = Doxygen::groupLinkedMap->find(root->name);
480 if (gd)
481 {
482 AUTO_TRACE_ADD("adding {} to group {}",root->name,gd->name());
483 addGroupToGroups(root,gd);
484 }
485 }
486 }
487 for (const auto &e : root->children()) organizeSubGroupsFiltered(e.get(),additional);
488}
489
490static void organizeSubGroups(const Entry *root)
491{
492 //printf("Defining groups\n");
493 // first process the @defgroups blocks
495 //printf("Additional groups\n");
496 // then process the @addtogroup, @weakgroup blocks
498}
499
500//----------------------------------------------------------------------
501
502static void buildFileList(const Entry *root)
503{
504 if ((root->section.isFileDoc() || (root->section.isFile() && Config_getBool(EXTRACT_ALL))) &&
505 !root->name.isEmpty() && !root->tagInfo() // skip any file coming from tag files
506 )
507 {
508 bool ambig = false;
510 if (!fd || ambig)
511 {
512 bool save_ambig = ambig;
513 // use the directory of the file to see if the described file is in the same
514 // directory as the describing file.
515 QCString fn = root->fileName;
516 int newIndex=fn.findRev('/');
517 if (newIndex<0)
518 {
519 fn = root->name;
520 }
521 else
522 {
523 fn = fn.left(newIndex)+"/"+root->name;
524 }
526 if (!fd) ambig = save_ambig;
527 }
528 //printf("**************** root->name=%s fd=%p\n",qPrint(root->name),(void*)fd);
529 if (fd && !ambig)
530 {
531 //printf("Adding documentation!\n");
532 // using FALSE in setDocumentation is small hack to make sure a file
533 // is documented even if a \file command is used without further
534 // documentation
535 fd->setDocumentation(root->doc,root->docFile,root->docLine,FALSE);
536 fd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
538 fd->setRefItems(root->sli);
540 root->commandOverrides.apply_includeGraph ([&](bool b) { fd->overrideIncludeGraph(b); });
541 root->commandOverrides.apply_includedByGraph([&](bool b) { fd->overrideIncludedByGraph(b); });
542 for (const Grouping &g : root->groups)
543 {
544 GroupDef *gd=nullptr;
545 if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname)))
546 {
547 if (!gd->containsFile(fd))
548 {
549 gd->addFile(fd);
550 fd->makePartOfGroup(gd);
551 //printf("File %s: in group %s\n",qPrint(fd->name()),qPrint(gd->name()));
552 }
553 }
554 else if (!gd && g.pri == Grouping::GROUPING_INGROUP)
555 {
556 warn(root->fileName, root->startLine,
557 "Found non-existing group '{}' for the command '{}', ignoring command",
559 );
560 }
561 }
562 }
563 else
564 {
566 text.sprintf("the name '%s' supplied as "
567 "the argument in the \\file statement ",
568 qPrint(root->name));
569 if (ambig) // name is ambiguous
570 {
571 text+="matches the following input files:\n";
573 text+="\n";
574 text+="Please use a more specific name by "
575 "including a (larger) part of the path!";
576 }
577 else // name is not an input file
578 {
579 text+="is not an input file";
580 }
581 warn(root->fileName,root->startLine,"{}", text);
582 }
583 }
584 for (const auto &e : root->children()) buildFileList(e.get());
585}
586
587template<class DefMutable>
588static void addIncludeFile(DefMutable *cd,FileDef *ifd,const Entry *root)
589{
590 if (
591 (!root->doc.stripWhiteSpace().isEmpty() ||
592 !root->brief.stripWhiteSpace().isEmpty() ||
593 Config_getBool(EXTRACT_ALL)
594 ) && root->protection!=Protection::Private
595 )
596 {
597 //printf(">>>>>> includeFile=%s\n",qPrint(root->includeFile));
598
599 bool local=Config_getBool(FORCE_LOCAL_INCLUDES);
600 QCString includeFile = root->includeFile;
601 if (!includeFile.isEmpty() && includeFile.at(0)=='"')
602 {
603 local = TRUE;
604 includeFile=includeFile.mid(1,includeFile.length()-2);
605 }
606 else if (!includeFile.isEmpty() && includeFile.at(0)=='<')
607 {
608 local = FALSE;
609 includeFile=includeFile.mid(1,includeFile.length()-2);
610 }
611
612 bool ambig = false;
613 FileDef *fd=nullptr;
614 // see if we need to include a verbatim copy of the header file
615 //printf("root->includeFile=%s\n",qPrint(root->includeFile));
616 if (!includeFile.isEmpty() &&
617 (fd=findFileDef(Doxygen::inputNameLinkedMap,includeFile,ambig))==nullptr
618 )
619 { // explicit request
620 QCString text;
621 text.sprintf("the name '%s' supplied as "
622 "the argument of the \\class, \\struct, \\union, or \\include command ",
623 qPrint(includeFile)
624 );
625 if (ambig) // name is ambiguous
626 {
627 text+="matches the following input files:\n";
629 text+="\n";
630 text+="Please use a more specific name by "
631 "including a (larger) part of the path!";
632 }
633 else // name is not an input file
634 {
635 text+="is not an input file";
636 }
637 warn(root->fileName,root->startLine, "{}", text);
638 }
639 else if (includeFile.isEmpty() && ifd &&
640 // see if the file extension makes sense
641 guessSection(ifd->name()).isHeader())
642 { // implicit assumption
643 fd=ifd;
644 }
645
646 // if a file is found, we mark it as a source file.
647 if (fd)
648 {
649 QCString iName = !root->includeName.isEmpty() ?
650 root->includeName : includeFile;
651 if (!iName.isEmpty()) // user specified include file
652 {
653 if (iName.at(0)=='<') local=FALSE; // explicit override
654 else if (iName.at(0)=='"') local=TRUE;
655 if (iName.at(0)=='"' || iName.at(0)=='<')
656 {
657 iName=iName.mid(1,iName.length()-2); // strip quotes or brackets
658 }
659 if (iName.isEmpty())
660 {
661 iName=fd->name();
662 }
663 }
664 else if (!Config_getList(STRIP_FROM_INC_PATH).empty())
665 {
667 }
668 else // use name of the file containing the class definition
669 {
670 iName=fd->name();
671 }
672 if (fd->generateSourceFile()) // generate code for header
673 {
674 cd->setIncludeFile(fd,iName,local,!root->includeName.isEmpty());
675 }
676 else // put #include in the class documentation without link
677 {
678 cd->setIncludeFile(nullptr,iName,local,TRUE);
679 }
680 }
681 }
682}
683
684
686{
687 size_t l = s.length();
688 int count=0;
689 int round=0;
690 QCString result;
691 for (size_t i=0;i<l;i++)
692 {
693 char c=s.at(i);
694 if (c=='(') round++;
695 else if (c==')' && round>0) round--;
696 else if (c=='<' && round==0) count++;
697 if (count==0)
698 {
699 result+=c;
700 }
701 if (c=='>' && round==0 && count>0) count--;
702 }
703 //printf("stripTemplateSpecifiers(%s)=%s\n",qPrint(s),qPrint(result));
704 return result;
705}
706
707/*! returns the Definition object belonging to the first \a level levels of
708 * full qualified name \a name. Creates an artificial scope if the scope is
709 * not found and set the parent/child scope relation if the scope is found.
710 */
711[[maybe_unused]]
712static Definition *buildScopeFromQualifiedName(const QCString &name_,SrcLangExt lang,const TagInfo *tagInfo)
713{
714 QCString name = stripTemplateSpecifiers(name_);
715 name.stripPrefix("::");
716 int level = name.contains("::");
717 //printf("buildScopeFromQualifiedName(%s) level=%d\n",qPrint(name),level);
718 int i=0, p=0, l=0;
720 QCString fullScope;
721 while (i<level)
722 {
723 int idx=getScopeFragment(name,p,&l);
724 if (idx==-1) return prevScope;
725 QCString nsName = name.mid(idx,l);
726 if (nsName.isEmpty()) return prevScope;
727 if (!fullScope.isEmpty()) fullScope+="::";
728 fullScope+=nsName;
729 NamespaceDef *nd=Doxygen::namespaceLinkedMap->find(fullScope);
730 DefinitionMutable *innerScope = toDefinitionMutable(nd);
731 ClassDef *cd=nullptr;
732 if (nd==nullptr) cd = getClass(fullScope);
733 if (nd==nullptr && cd) // scope is a class
734 {
735 innerScope = toDefinitionMutable(cd);
736 }
737 else if (nd==nullptr && cd==nullptr && fullScope.find('<')==-1) // scope is not known and could be a namespace!
738 {
739 // introduce bogus namespace
740 //printf("++ adding dummy namespace %s to %s tagInfo=%p\n",qPrint(nsName),qPrint(prevScope->name()),(void*)tagInfo);
741 NamespaceDefMutable *newNd=
743 Doxygen::namespaceLinkedMap->add(fullScope,
745 "[generated]",1,1,fullScope,
746 tagInfo?tagInfo->tagName:QCString(),
747 tagInfo?tagInfo->fileName:QCString())));
748 if (newNd)
749 {
750 newNd->setLanguage(lang);
751 newNd->setArtificial(TRUE);
752 // add namespace to the list
753 innerScope = newNd;
754 }
755 }
756 else // scope is a namespace
757 {
758 }
759 if (innerScope)
760 {
761 // make the parent/child scope relation
762 DefinitionMutable *prevScopeMutable = toDefinitionMutable(prevScope);
763 if (prevScopeMutable)
764 {
765 prevScopeMutable->addInnerCompound(toDefinition(innerScope));
766 }
767 innerScope->setOuterScope(prevScope);
768 }
769 else // current scope is a class, so return only the namespace part...
770 {
771 return prevScope;
772 }
773 // proceed to the next scope fragment
774 p=idx+l+2;
775 prevScope=toDefinition(innerScope);
776 i++;
777 }
778 return prevScope;
779}
780
782 FileDef *fileScope,const TagInfo *tagInfo)
783{
784 //printf("<findScopeFromQualifiedName(%s,%s)\n",startScope ? qPrint(startScope->name()) : 0, qPrint(n));
785 Definition *resultScope=toDefinition(startScope);
786 if (resultScope==nullptr) resultScope=Doxygen::globalScope;
788 int l1 = 0;
789 int i1 = getScopeFragment(scope,0,&l1);
790 if (i1==-1)
791 {
792 //printf(">no fragments!\n");
793 return resultScope;
794 }
795 int p=i1+l1,l2=0,i2=0;
796 while ((i2=getScopeFragment(scope,p,&l2))!=-1)
797 {
798 QCString nestedNameSpecifier = scope.mid(i1,l1);
799 Definition *orgScope = resultScope;
800 //printf(" nestedNameSpecifier=%s\n",qPrint(nestedNameSpecifier));
801 resultScope = const_cast<Definition*>(resultScope->findInnerCompound(nestedNameSpecifier));
802 //printf(" resultScope=%p\n",resultScope);
803 if (resultScope==nullptr)
804 {
805 if (orgScope==Doxygen::globalScope && fileScope && !fileScope->getUsedNamespaces().empty())
806 // also search for used namespaces
807 {
808 for (const auto &nd : fileScope->getUsedNamespaces())
809 {
811 if (mnd)
812 {
813 resultScope = findScopeFromQualifiedName(toNamespaceDefMutable(nd),n,fileScope,tagInfo);
814 if (resultScope!=nullptr) break;
815 }
816 }
817 if (resultScope)
818 {
819 // for a nested class A::I in used namespace N, we get
820 // N::A::I while looking for A, so we should compare
821 // resultScope->name() against scope.left(i2+l2)
822 //printf(" -> result=%s scope=%s\n",qPrint(resultScope->name()),qPrint(scope));
823 if (rightScopeMatch(resultScope->name(),scope.left(i2+l2)))
824 {
825 break;
826 }
827 goto nextFragment;
828 }
829 }
830
831 // also search for used classes. Complication: we haven't been able
832 // to put them in the right scope yet, because we are still resolving
833 // the scope relations!
834 // Therefore loop through all used classes and see if there is a right
835 // scope match between the used class and nestedNameSpecifier.
836 for (const auto &usedName : g_usingDeclarations)
837 {
838 //printf("Checking using class %s\n",qPrint(usedName));
839 if (rightScopeMatch(usedName,nestedNameSpecifier))
840 {
841 // ui.currentKey() is the fully qualified name of nestedNameSpecifier
842 // so use this instead.
843 QCString fqn = usedName + scope.right(scope.length()-p);
844 resultScope = buildScopeFromQualifiedName(fqn,startScope->getLanguage(),nullptr);
845 //printf("Creating scope from fqn=%s result %p\n",qPrint(fqn),resultScope);
846 if (resultScope)
847 {
848 //printf("> Match! resultScope=%s\n",qPrint(resultScope->name()));
849 return resultScope;
850 }
851 }
852 }
853
854 //printf("> name %s not found in scope %s\n",qPrint(nestedNameSpecifier),qPrint(orgScope->name()));
855 return nullptr;
856 }
857 nextFragment:
858 i1=i2;
859 l1=l2;
860 p=i2+l2;
861 }
862 //printf(">findScopeFromQualifiedName scope %s\n",qPrint(resultScope->name()));
863 return resultScope;
864}
865
866std::unique_ptr<ArgumentList> getTemplateArgumentsFromName(
867 const QCString &name,
868 const ArgumentLists &tArgLists)
869{
870 // for each scope fragment, check if it is a template and advance through
871 // the list if so.
872 int i=0, p=0;
873 auto alIt = tArgLists.begin();
874 while ((i=name.find("::",p))!=-1 && alIt!=tArgLists.end())
875 {
876 NamespaceDef *nd = Doxygen::namespaceLinkedMap->find(name.left(i));
877 if (nd==nullptr)
878 {
879 ClassDef *cd = getClass(name.left(i));
880 if (cd)
881 {
882 if (!cd->templateArguments().empty())
883 {
884 ++alIt;
885 }
886 }
887 }
888 p=i+2;
889 }
890 return alIt!=tArgLists.end() ?
891 std::make_unique<ArgumentList>(*alIt) :
892 std::unique_ptr<ArgumentList>();
893}
894
895static
897{
899
900 if (specifier.isStruct())
902 else if (specifier.isUnion())
903 sec=ClassDef::Union;
904 else if (specifier.isCategory())
906 else if (specifier.isInterface())
908 else if (specifier.isProtocol())
910 else if (specifier.isException())
912 else if (specifier.isService())
914 else if (specifier.isSingleton())
916
917 if (section.isUnionDoc())
918 sec=ClassDef::Union;
919 else if (section.isStructDoc())
921 else if (section.isInterfaceDoc())
923 else if (section.isProtocolDoc())
925 else if (section.isCategoryDoc())
927 else if (section.isExceptionDoc())
929 else if (section.isServiceDoc())
931 else if (section.isSingletonDoc())
933
934 return sec;
935}
936
937
938static void addClassToContext(const Entry *root)
939{
940 AUTO_TRACE("name={}",root->name);
941 FileDef *fd = root->fileDef();
942
943 QCString scName;
944 if (root->parent()->section.isScope())
945 {
946 scName=root->parent()->name;
947 }
948 // name without parent's scope
949 QCString fullName = root->name;
950
951 // strip off any template parameters (but not those for specializations)
952 int idx=fullName.find('>');
953 if (idx!=-1 && root->lang==SrcLangExt::CSharp) // mangle A<S,T>::N as A-2-g::N
954 {
955 fullName = mangleCSharpGenericName(fullName.left(idx+1))+fullName.mid(idx+1);
956 }
957 fullName=stripTemplateSpecifiersFromScope(fullName);
958
959 // name with scope (if not present already)
960 QCString qualifiedName = fullName;
961 if (!scName.isEmpty() && !leftScopeMatch(scName,fullName))
962 {
963 qualifiedName.prepend(scName+"::");
964 }
965
966 // see if we already found the class before
967 ClassDefMutable *cd = getClassMutable(qualifiedName);
968
969 AUTO_TRACE_ADD("Found class with name '{}', qualifiedName '{}'", cd ? cd->name() : root->name, qualifiedName);
970
971 if (cd)
972 {
973 fullName=cd->name();
974 AUTO_TRACE_ADD("Existing class '{}'",cd->name());
975 //if (cd->templateArguments()==0)
976 //{
977 // //printf("existing ClassDef tempArgList=%p specScope=%s\n",root->tArgList,qPrint(root->scopeSpec));
978 // cd->setTemplateArguments(tArgList);
979 //}
980
981 cd->setDocumentation(root->doc,root->docFile,root->docLine);
982 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
983 root->commandOverrides.apply_collaborationGraph([&](bool b ) { cd->overrideCollaborationGraph(b); });
984 root->commandOverrides.apply_inheritanceGraph ([&](CLASS_GRAPH_t gt) { cd->overrideInheritanceGraph(gt); });
985
986 if (!root->spec.isForwardDecl() && cd->isForwardDeclared())
987 {
988 cd->setDefFile(root->fileName,root->startLine,root->startColumn);
989 if (root->bodyLine!=-1)
990 {
991 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
992 cd->setBodyDef(fd);
993 }
994 }
995
996 if (cd->templateArguments().empty() || (cd->isForwardDeclared() && !root->spec.isForwardDecl()))
997 {
998 // this happens if a template class declared with @class is found
999 // before the actual definition or if a forward declaration has different template
1000 // parameter names.
1001 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(cd->name(),root->tArgLists);
1002 if (tArgList)
1003 {
1004 cd->setTemplateArguments(*tArgList);
1005 }
1006 }
1007 if (cd->requiresClause().isEmpty() && !root->req.isEmpty())
1008 {
1009 cd->setRequiresClause(root->req);
1010 }
1011
1013
1014 cd->setMetaData(root->metaData);
1015 }
1016 else // new class
1017 {
1019
1020 QCString className;
1021 QCString namespaceName;
1022 extractNamespaceName(fullName,className,namespaceName);
1023
1024 AUTO_TRACE_ADD("New class: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1025 fullName, namespaceName, className, Trace::trunc(root->brief), Trace::trunc(root->doc));
1026
1027 QCString tagName;
1028 QCString refFileName;
1029 const TagInfo *tagInfo = root->tagInfo();
1030 if (tagInfo)
1031 {
1032 tagName = tagInfo->tagName;
1033 refFileName = tagInfo->fileName;
1034 if (fullName.find("::")!=-1)
1035 // symbols imported via tag files may come without the parent scope,
1036 // so we artificially create it here
1037 {
1038 buildScopeFromQualifiedName(fullName,root->lang,tagInfo);
1039 }
1040 }
1041 std::unique_ptr<ArgumentList> tArgList;
1042 int i=0;
1043 if ((root->lang==SrcLangExt::CSharp || root->lang==SrcLangExt::Java) &&
1044 (i=fullName.find('<'))!=-1)
1045 {
1046 // a Java/C# generic class looks like a C++ specialization, so we need to split the
1047 // name and template arguments here
1048 tArgList = stringToArgumentList(root->lang,fullName.mid(i));
1049 if (i!=-1 && root->lang==SrcLangExt::CSharp) // in C# A, A<T>, and A<T,S> are different classes, so we need some way to disguish them using this name mangling
1050 // A -> A
1051 // A<T> -> A-1-g
1052 // A<T,S> -> A-2-g
1053 {
1054 fullName=mangleCSharpGenericName(fullName);
1055 }
1056 else
1057 {
1058 fullName=fullName.left(i);
1059 }
1060 }
1061 else
1062 {
1063 tArgList = getTemplateArgumentsFromName(fullName,root->tArgLists);
1064 }
1065 // add class to the list
1066 cd = toClassDefMutable(
1067 Doxygen::classLinkedMap->add(fullName,
1068 createClassDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1069 fullName,sec,tagName,refFileName,TRUE,root->spec.isEnum()) ));
1070 if (cd)
1071 {
1072 AUTO_TRACE_ADD("New class '{}' type={} #tArgLists={} tagInfo={} hidden={} artificial={}",
1073 fullName,cd->compoundTypeString(),root->tArgLists.size(),
1074 fmt::ptr(tagInfo),root->hidden,root->artificial);
1075 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1076 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1077 cd->setLanguage(root->lang);
1078 cd->setId(root->id);
1079 cd->setHidden(root->hidden);
1080 cd->setArtificial(root->artificial);
1081 cd->setClassSpecifier(root->spec);
1082 if (root->lang==SrcLangExt::CSharp && !root->args.isEmpty())
1083 {
1085 }
1086 cd->addQualifiers(root->qualifiers);
1087 cd->setTypeConstraints(root->typeConstr);
1088 root->commandOverrides.apply_collaborationGraph([&](bool b ) { cd->overrideCollaborationGraph(b); });
1089 root->commandOverrides.apply_inheritanceGraph ([&](CLASS_GRAPH_t gt) { cd->overrideInheritanceGraph(gt); });
1090
1091 if (tArgList)
1092 {
1093 cd->setTemplateArguments(*tArgList);
1094 }
1095 cd->setRequiresClause(root->req);
1096 cd->setProtection(root->protection);
1097 cd->setIsStatic(root->isStatic);
1098
1099 // file definition containing the class cd
1100 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1101 cd->setBodyDef(fd);
1102
1103 cd->setMetaData(root->metaData);
1104
1105 cd->insertUsedFile(fd);
1106 }
1107 else
1108 {
1109 AUTO_TRACE_ADD("Class {} not added, already exists as alias", fullName);
1110 }
1111 }
1112
1113 if (cd)
1114 {
1116 if (!root->subGrouping) cd->setSubGrouping(FALSE);
1117 if (!root->spec.isForwardDecl())
1118 {
1119 if (cd->hasDocumentation())
1120 {
1121 addIncludeFile(cd,fd,root);
1122 }
1123 if (fd && root->section.isCompound())
1124 {
1125 AUTO_TRACE_ADD("Inserting class {} in file {} (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1126 cd->setFileDef(fd);
1127 fd->insertClass(cd);
1128 }
1129 }
1130 addClassToGroups(root,cd);
1132 cd->setRefItems(root->sli);
1133 cd->setRequirementReferences(root->rqli);
1134 }
1135}
1136
1137//----------------------------------------------------------------------
1138// build a list of all classes mentioned in the documentation
1139// and all classes that have a documentation block before their definition.
1140static void buildClassList(const Entry *root)
1141{
1142 if ((root->section.isCompound() || root->section.isObjcImpl()) && !root->name.isEmpty())
1143 {
1144 AUTO_TRACE();
1145 addClassToContext(root);
1146 }
1147 for (const auto &e : root->children()) buildClassList(e.get());
1148}
1149
1150static void buildClassDocList(const Entry *root)
1151{
1152 if ((root->section.isCompoundDoc()) && !root->name.isEmpty())
1153 {
1154 AUTO_TRACE();
1155 addClassToContext(root);
1156 }
1157 for (const auto &e : root->children()) buildClassDocList(e.get());
1158}
1159
1160//----------------------------------------------------------------------
1161// build a list of all classes mentioned in the documentation
1162// and all classes that have a documentation block before their definition.
1163
1164static void addConceptToContext(const Entry *root)
1165{
1166 AUTO_TRACE();
1167 FileDef *fd = root->fileDef();
1168
1169 QCString scName;
1170 if (root->parent()->section.isScope())
1171 {
1172 scName=root->parent()->name;
1173 }
1174
1175 // name with scope (if not present already)
1176 QCString qualifiedName = root->name;
1177 if (!scName.isEmpty() && !leftScopeMatch(qualifiedName,scName))
1178 {
1179 qualifiedName.prepend(scName+"::");
1180 }
1181
1182 // see if we already found the concept before
1183 ConceptDefMutable *cd = getConceptMutable(qualifiedName);
1184
1185 AUTO_TRACE_ADD("Found concept with name '{}' (qualifiedName='{}')", cd ? cd->name() : root->name, qualifiedName);
1186
1187 if (cd)
1188 {
1189 qualifiedName=cd->name();
1190 AUTO_TRACE_ADD("Existing concept '{}'",cd->name());
1191
1192 cd->setDocumentation(root->doc,root->docFile,root->docLine);
1193 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1194
1195 addIncludeFile(cd,fd,root);
1196 }
1197 else // new concept
1198 {
1199 QCString className;
1200 QCString namespaceName;
1201 extractNamespaceName(qualifiedName,className,namespaceName);
1202
1203 AUTO_TRACE_ADD("New concept: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1204 qualifiedName,namespaceName,className,root->brief,root->doc);
1205
1206 QCString tagName;
1207 QCString refFileName;
1208 const TagInfo *tagInfo = root->tagInfo();
1209 if (tagInfo)
1210 {
1211 tagName = tagInfo->tagName;
1212 refFileName = tagInfo->fileName;
1213 if (qualifiedName.find("::")!=-1)
1214 // symbols imported via tag files may come without the parent scope,
1215 // so we artificially create it here
1216 {
1217 buildScopeFromQualifiedName(qualifiedName,root->lang,tagInfo);
1218 }
1219 }
1220 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(qualifiedName,root->tArgLists);
1221 // add concept to the list
1223 Doxygen::conceptLinkedMap->add(qualifiedName,
1224 createConceptDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1225 qualifiedName,tagName,refFileName)));
1226 if (cd)
1227 {
1228 AUTO_TRACE_ADD("New concept '{}' #tArgLists={} tagInfo={}",
1229 qualifiedName,root->tArgLists.size(),fmt::ptr(tagInfo));
1230 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1231 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1232 cd->setLanguage(root->lang);
1233 cd->setId(root->id);
1234 cd->setHidden(root->hidden);
1235 cd->setGroupId(root->mGrpId);
1236 if (tArgList)
1237 {
1238 cd->setTemplateArguments(*tArgList);
1239 }
1240 cd->setInitializer(root->initializer.str());
1241 // file definition containing the class cd
1242 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1243 cd->setBodyDef(fd);
1245 cd->setRefItems(root->sli);
1246 cd->setRequirementReferences(root->rqli);
1247 addIncludeFile(cd,fd,root);
1248
1249 // also add namespace to the correct structural context
1250 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,qualifiedName,nullptr,tagInfo);
1252 {
1254 if (dm)
1255 {
1256 dm->addInnerCompound(cd);
1257 }
1258 cd->setOuterScope(d);
1259 }
1260 for (const auto &ce : root->children())
1261 {
1262 //printf("Concept %s has child %s\n",qPrint(root->name),qPrint(ce->section.to_string()));
1263 if (ce->section.isConceptDocPart())
1264 {
1265 cd->addSectionsToDefinition(ce->anchors);
1266 cd->setRefItems(ce->sli);
1267 cd->setRequirementReferences(ce->rqli);
1268 if (!ce->brief.isEmpty())
1269 {
1270 cd->addDocPart(ce->brief,ce->startLine,ce->startColumn);
1271 //printf(" brief=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->brief),ce->startLine,ce->startColumn);
1272 }
1273 if (!ce->doc.isEmpty())
1274 {
1275 cd->addDocPart(ce->doc,ce->startLine,ce->startColumn);
1276 //printf(" doc=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->doc),ce->startLine,ce->startColumn);
1277 }
1278 }
1279 else if (ce->section.isConceptCodePart())
1280 {
1281 cd->addCodePart(ce->initializer.str(),ce->startLine,ce->startColumn);
1282 //printf(" code=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->initializer.str()),ce->startLine,ce->startColumn);
1283 }
1284 }
1285 }
1286 else
1287 {
1288 AUTO_TRACE_ADD("Concept '{}' not added, already exists (as alias)", qualifiedName);
1289 }
1290 }
1291
1292 if (cd)
1293 {
1295 for (const auto &ce : root->children())
1296 {
1297 if (ce->section.isConceptDocPart())
1298 {
1299 cd->addSectionsToDefinition(ce->anchors);
1300 }
1301 }
1302 if (fd)
1303 {
1304 AUTO_TRACE_ADD("Inserting concept '{}' in file '{}' (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1305 cd->setFileDef(fd);
1306 fd->insertConcept(cd);
1307 }
1308 addConceptToGroups(root,cd);
1310 cd->setRefItems(root->sli);
1311 cd->setRequirementReferences(root->rqli);
1312 }
1313}
1314
1315static void findModuleDocumentation(const Entry *root)
1316{
1317 if (root->section.isModuleDoc())
1318 {
1319 AUTO_TRACE();
1321 }
1322 for (const auto &e : root->children()) findModuleDocumentation(e.get());
1323}
1324
1325static void buildConceptList(const Entry *root)
1326{
1327 if (root->section.isConcept())
1328 {
1329 AUTO_TRACE();
1330 addConceptToContext(root);
1331 }
1332 for (const auto &e : root->children()) buildConceptList(e.get());
1333}
1334
1335static void buildConceptDocList(const Entry *root)
1336{
1337 if (root->section.isConceptDoc())
1338 {
1339 AUTO_TRACE();
1340 addConceptToContext(root);
1341 }
1342 for (const auto &e : root->children()) buildConceptDocList(e.get());
1343}
1344
1345// This routine is to allow @ingroup X @{ concept A; concept B; @} to work
1346// (same also works for variable and functions because of logic in MemberGroup::insertMember)
1348{
1349 AUTO_TRACE();
1350 for (const auto &cd : *Doxygen::conceptLinkedMap)
1351 {
1352 if (cd->groupId()!=DOX_NOGROUP)
1353 {
1354 for (const auto &ocd : *Doxygen::conceptLinkedMap)
1355 {
1356 if (cd!=ocd && cd->groupId()==ocd->groupId() &&
1357 !cd->partOfGroups().empty() && ocd->partOfGroups().empty())
1358 {
1359 ConceptDefMutable *ocdm = toConceptDefMutable(ocd.get());
1360 if (ocdm)
1361 {
1362 for (const auto &gd : cd->partOfGroups())
1363 {
1364 if (gd)
1365 {
1366 AUTO_TRACE_ADD("making concept '{}' part of group '{}'",ocdm->name(),gd->name());
1367 ocdm->makePartOfGroup(gd);
1368 gd->addConcept(ocd.get());
1369 }
1370 }
1371 }
1372 }
1373 }
1374 }
1375 }
1376}
1377
1378//----------------------------------------------------------------------
1379
1381{
1382 ClassDefSet visitedClasses;
1383
1384 bool done=FALSE;
1385 //int iteration=0;
1386 while (!done)
1387 {
1388 done=TRUE;
1389 //++iteration;
1390 struct ClassAlias
1391 {
1392 ClassAlias(const QCString &name,std::unique_ptr<ClassDef> cd,DefinitionMutable *ctx) :
1393 aliasFullName(name),aliasCd(std::move(cd)), aliasContext(ctx) {}
1394 QCString aliasFullName;
1395 std::unique_ptr<ClassDef> aliasCd;
1396 DefinitionMutable *aliasContext;
1397 };
1398 std::vector<ClassAlias> aliases;
1399 for (const auto &icd : *Doxygen::classLinkedMap)
1400 {
1401 ClassDefMutable *cd = toClassDefMutable(icd.get());
1402 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1403 {
1404 QCString name = stripAnonymousNamespaceScope(icd->name());
1405 //printf("processing=%s, iteration=%d\n",qPrint(cd->name()),iteration);
1406 // also add class to the correct structural context
1408 name,icd->getFileDef(),nullptr);
1409 if (d)
1410 {
1411 //printf("****** adding %s to scope %s in iteration %d\n",qPrint(cd->name()),qPrint(d->name()),iteration);
1413 if (dm)
1414 {
1415 dm->addInnerCompound(cd);
1416 }
1417 cd->setOuterScope(d);
1418
1419 // for inline namespace add an alias of the class to the outer scope
1421 {
1423 //printf("nd->isInline()=%d\n",nd->isInline());
1424 if (nd && nd->isInline())
1425 {
1426 d = d->getOuterScope();
1427 if (d)
1428 {
1429 dm = toDefinitionMutable(d);
1430 if (dm)
1431 {
1432 auto aliasCd = createClassDefAlias(d,cd);
1433 QCString aliasFullName = d->qualifiedName()+"::"+aliasCd->localName();
1434 aliases.emplace_back(aliasFullName,std::move(aliasCd),dm);
1435 //printf("adding %s to %s as %s\n",qPrint(aliasCd->name()),qPrint(d->name()),qPrint(aliasFullName));
1436 }
1437 }
1438 }
1439 else
1440 {
1441 break;
1442 }
1443 }
1444
1445 visitedClasses.insert(icd.get());
1446 done=FALSE;
1447 }
1448 //else
1449 //{
1450 // printf("****** ignoring %s: scope not (yet) found in iteration %d\n",qPrint(cd->name()),iteration);
1451 //}
1452 }
1453 }
1454 // add aliases
1455 for (auto &alias : aliases)
1456 {
1457 ClassDef *aliasCd = Doxygen::classLinkedMap->add(alias.aliasFullName,std::move(alias.aliasCd));
1458 if (aliasCd)
1459 {
1460 alias.aliasContext->addInnerCompound(aliasCd);
1461 }
1462 }
1463 }
1464
1465 //give warnings for unresolved compounds
1466 for (const auto &icd : *Doxygen::classLinkedMap)
1467 {
1468 ClassDefMutable *cd = toClassDefMutable(icd.get());
1469 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1470 {
1472 /// create the scope artificially
1473 // anyway, so we can at least relate scopes properly.
1474 Definition *d = buildScopeFromQualifiedName(name,cd->getLanguage(),nullptr);
1475 if (d && d!=cd && !cd->getDefFileName().isEmpty())
1476 // avoid recursion in case of redundant scopes, i.e: namespace N { class N::C {}; }
1477 // for this case doxygen assumes the existence of a namespace N::N in which C is to be found!
1478 // also avoid warning for stuff imported via a tagfile.
1479 {
1481 if (dm)
1482 {
1483 dm->addInnerCompound(cd);
1484 }
1485 cd->setOuterScope(d);
1486 warn(cd->getDefFileName(),cd->getDefLine(),
1487 "Incomplete input: scope for class {} not found!{}",name,
1488 name.startsWith("std::") ? " Try enabling BUILTIN_STL_SUPPORT." : ""
1489 );
1490 }
1491 }
1492 }
1493}
1494
1496{
1497 //bool inlineGroupedClasses = Config_getBool(INLINE_GROUPED_CLASSES);
1498 //if (!inlineGroupedClasses) return;
1499 //printf("** distributeClassGroupRelations()\n");
1500
1501 ClassDefSet visitedClasses;
1502 for (const auto &cd : *Doxygen::classLinkedMap)
1503 {
1504 //printf("Checking %s\n",qPrint(cd->name()));
1505 // distribute the group to nested classes as well
1506 if (visitedClasses.find(cd.get())==visitedClasses.end() && !cd->partOfGroups().empty())
1507 {
1508 //printf(" Candidate for merging\n");
1509 GroupDef *gd = cd->partOfGroups().front();
1510 for (auto &ncd : cd->getClasses())
1511 {
1513 if (ncdm && ncdm->partOfGroups().empty())
1514 {
1515 //printf(" Adding %s to group '%s'\n",qPrint(ncd->name()),
1516 // gd->groupTitle());
1517 ncdm->makePartOfGroup(gd);
1518 gd->addClass(ncdm);
1519 }
1520 }
1521 visitedClasses.insert(cd.get()); // only visit every class once
1522 }
1523 }
1524}
1525
1526//----------------------------
1527
1528static ClassDefMutable *createTagLessInstance(const ClassDef *rootCd,const ClassDef *templ,const QCString &fieldName)
1529{
1530 QCString fullName = removeAnonymousScopes(templ->name());
1531 if (fullName.endsWith("::")) fullName=fullName.left(fullName.length()-2);
1532 fullName+="."+fieldName;
1533
1534 //printf("** adding class %s based on %s\n",qPrint(fullName),qPrint(templ->name()));
1536 Doxygen::classLinkedMap->add(fullName,
1538 templ->getDefLine(),
1539 templ->getDefColumn(),
1540 fullName,
1541 templ->compoundType())));
1542 if (cd)
1543 {
1544 cd->setDocumentation(templ->documentation(),templ->docFile(),templ->docLine()); // copy docs to definition
1545 cd->setBriefDescription(templ->briefDescription(),templ->briefFile(),templ->briefLine());
1546 cd->setLanguage(templ->getLanguage());
1547 cd->setBodySegment(templ->getDefLine(),templ->getStartBodyLine(),templ->getEndBodyLine());
1548 cd->setBodyDef(templ->getBodyDef());
1549
1550 cd->setOuterScope(rootCd->getOuterScope());
1551 if (rootCd->getOuterScope()!=Doxygen::globalScope)
1552 {
1553 DefinitionMutable *outerScope = toDefinitionMutable(rootCd->getOuterScope());
1554 if (outerScope)
1555 {
1556 outerScope->addInnerCompound(cd);
1557 }
1558 }
1559
1560 FileDef *fd = templ->getFileDef();
1561 if (fd)
1562 {
1563 cd->setFileDef(fd);
1564 fd->insertClass(cd);
1565 }
1566 for (auto &gd : rootCd->partOfGroups())
1567 {
1568 cd->makePartOfGroup(gd);
1569 gd->addClass(cd);
1570 }
1571
1572 MemberList *ml = templ->getMemberList(MemberListType::PubAttribs());
1573 if (ml)
1574 {
1575 for (const auto &md : *ml)
1576 {
1577 //printf(" Member %s type=%s\n",qPrint(md->name()),md->typeString());
1578 auto newMd = createMemberDef(md->getDefFileName(),md->getDefLine(),md->getDefColumn(),
1579 md->typeString(),md->name(),md->argsString(),md->excpString(),
1580 md->protection(),md->virtualness(),md->isStatic(),Relationship::Member,
1581 md->memberType(),
1582 ArgumentList(),ArgumentList(),"");
1583 MemberDefMutable *imd = toMemberDefMutable(newMd.get());
1584 imd->setMemberClass(cd);
1585 imd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
1586 imd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
1587 imd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
1588 imd->setMemberSpecifiers(md->getMemberSpecifiers());
1589 imd->setVhdlSpecifiers(md->getVhdlSpecifiers());
1590 imd->setMemberGroupId(md->getMemberGroupId());
1591 imd->setInitializer(md->initializer());
1592 imd->setRequiresClause(md->requiresClause());
1593 imd->setMaxInitLines(md->initializerLines());
1594 imd->setBitfields(md->bitfieldString());
1595 imd->setLanguage(md->getLanguage());
1596 cd->insertMember(imd);
1597 MemberName *mn = Doxygen::memberNameLinkedMap->add(md->name());
1598 mn->push_back(std::move(newMd));
1599 }
1600 }
1601 }
1602 return cd;
1603}
1604
1605/** Look through the members of class \a cd and its public members.
1606 * If there is a member m of a tag less struct/union,
1607 * then we create a duplicate of the struct/union with the name of the
1608 * member to identify it.
1609 * So if cd has name S, then the tag less struct/union will get name S.m
1610 * Since tag less structs can be nested we need to call this function
1611 * recursively. Later on we need to patch the member types so we keep
1612 * track of the hierarchy of classes we create.
1613 */
1614static void processTagLessClasses(const ClassDef *rootCd,
1615 const ClassDef *cd,
1616 ClassDefMutable *tagParentCd,
1617 const QCString &prefix,int count)
1618{
1619 //printf("%d: processTagLessClasses %s\n",count,qPrint(cd->name()));
1620 //printf("checking members for %s\n",qPrint(cd->name()));
1621 if (tagParentCd && !cd->getClasses().empty())
1622 {
1623 MemberList *ml = cd->getMemberList(MemberListType::PubAttribs());
1624 if (ml)
1625 {
1626 int pos=0;
1627 for (const auto &md : *ml)
1628 {
1629 QCString type = md->typeString();
1630 if (type.find("::@")!=-1) // member of tag less struct/union
1631 {
1632 for (const auto &icd : cd->getClasses())
1633 {
1634 //printf(" member %s: type='%s'\n",qPrint(md->name()),qPrint(type));
1635 //printf(" comparing '%s'<->'%s'\n",qPrint(type),qPrint(icd->name()));
1636 if (type.find(icd->name())!=-1) // matching tag less struct/union
1637 {
1638 QCString name = md->name();
1639 if (md->isAnonymous()) name = "__unnamed" + QCString().setNum(pos++)+"__";
1640 if (!prefix.isEmpty()) name.prepend(prefix+".");
1641 //printf(" found %s for class %s\n",qPrint(name),qPrint(cd->name()));
1642 ClassDefMutable *ncd = createTagLessInstance(rootCd,icd,name);
1643 if (ncd)
1644 {
1645 processTagLessClasses(rootCd,icd,ncd,name,count+1);
1646 //printf(" addTagged %s to %s\n",qPrint(ncd->name()),qPrint(tagParentCd->name()));
1647 ncd->setTagLessReference(icd);
1648
1649 // replace tag-less type for generated/original member
1650 // by newly created class name.
1651 // note the difference between changing cd and tagParentCd.
1652 // for the initial call this is the same pointer, but for
1653 // recursive calls cd is the original tag-less struct (of which
1654 // there is only one instance) and tagParentCd is the newly
1655 // generated tagged struct of which there can be multiple instances!
1656 MemberList *pml = tagParentCd->getMemberList(MemberListType::PubAttribs());
1657 if (pml)
1658 {
1659 for (const auto &pmd : *pml)
1660 {
1662 if (pmdm && pmd->name()==md->name())
1663 {
1664 pmdm->setAccessorType(ncd,substitute(pmd->typeString(),icd->name(),ncd->name()));
1665 //pmd->setType(substitute(pmd->typeString(),icd->name(),ncd->name()));
1666 }
1667 }
1668 }
1669 }
1670 }
1671 }
1672 }
1673 }
1674 }
1675 }
1676}
1677
1678static void findTagLessClasses(std::vector<ClassDefMutable*> &candidates,ClassDef *cd)
1679{
1680 for (const auto &icd : cd->getClasses())
1681 {
1682 if (icd->name().find("@")==-1) // process all non-anonymous inner classes
1683 {
1684 findTagLessClasses(candidates,icd);
1685 }
1686 }
1687
1689 if (cdm)
1690 {
1691 candidates.push_back(cdm);
1692 }
1693}
1694
1696{
1697 std::vector<ClassDefMutable *> candidates;
1698 for (auto &cd : *Doxygen::classLinkedMap)
1699 {
1700 Definition *scope = cd->getOuterScope();
1701 if (scope && scope->definitionType()!=Definition::TypeClass) // that is not nested
1702 {
1703 findTagLessClasses(candidates,cd.get());
1704 }
1705 }
1706
1707 // since processTagLessClasses is potentially adding classes to Doxygen::classLinkedMap
1708 // we need to call it outside of the loop above, otherwise the iterator gets invalidated!
1709 for (auto &cd : candidates)
1710 {
1711 processTagLessClasses(cd,cd,cd,"",0); // process tag less inner struct/classes
1712 }
1713}
1714
1715
1716//----------------------------------------------------------------------
1717// build a list of all namespaces mentioned in the documentation
1718// and all namespaces that have a documentation block before their definition.
1719static void buildNamespaceList(const Entry *root)
1720{
1721 if (
1722 (root->section.isNamespace() ||
1723 root->section.isNamespaceDoc() ||
1724 root->section.isPackageDoc()
1725 ) &&
1726 !root->name.isEmpty()
1727 )
1728 {
1729 AUTO_TRACE("name={}",root->name);
1730
1731 QCString fName = root->name;
1732 if (root->section.isPackageDoc())
1733 {
1734 fName=substitute(fName,".","::");
1735 }
1736
1737 QCString fullName = stripAnonymousNamespaceScope(fName);
1738 if (!fullName.isEmpty())
1739 {
1740 AUTO_TRACE_ADD("Found namespace {} in {} at line {}",root->name,root->fileName,root->startLine);
1741 NamespaceDef *ndi = Doxygen::namespaceLinkedMap->find(fullName);
1742 if (ndi) // existing namespace
1743 {
1745 if (nd) // non-inline namespace
1746 {
1747 AUTO_TRACE_ADD("Existing namespace");
1748 nd->setDocumentation(root->doc,root->docFile,root->docLine);
1749 nd->setName(fullName); // change name to match docs
1751 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1752 if (nd->getLanguage()==SrcLangExt::Unknown)
1753 {
1754 nd->setLanguage(root->lang);
1755 }
1756 if (root->tagInfo()==nullptr && nd->isReference() && !(root->doc.isEmpty() && root->brief.isEmpty()))
1757 // if we previously found namespace nd in a tag file and now we find a
1758 // documented namespace with the same name in the project, then remove
1759 // the tag file reference
1760 {
1761 nd->setReference("");
1762 nd->setFileName(fullName);
1763 }
1764 nd->setMetaData(root->metaData);
1765
1766 // file definition containing the namespace nd
1767 FileDef *fd=root->fileDef();
1768 if (nd->isArtificial())
1769 {
1770 nd->setArtificial(FALSE); // found namespace explicitly, so cannot be artificial
1771 nd->setDefFile(root->fileName,root->startLine,root->startColumn);
1772 }
1773 // insert the namespace in the file definition
1774 if (fd) fd->insertNamespace(nd);
1775 addNamespaceToGroups(root,nd);
1776 nd->setRefItems(root->sli);
1777 nd->setRequirementReferences(root->rqli);
1778 }
1779 }
1780 else // fresh namespace
1781 {
1782 QCString tagName;
1783 QCString tagFileName;
1784 const TagInfo *tagInfo = root->tagInfo();
1785 if (tagInfo)
1786 {
1787 tagName = tagInfo->tagName;
1788 tagFileName = tagInfo->fileName;
1789 }
1790 AUTO_TRACE_ADD("new namespace {} lang={} tagName={}",fullName,langToString(root->lang),tagName);
1791 // add namespace to the list
1793 Doxygen::namespaceLinkedMap->add(fullName,
1794 createNamespaceDef(tagInfo?tagName:root->fileName,root->startLine,
1795 root->startColumn,fullName,tagName,tagFileName,
1796 root->type,root->spec.isPublished())));
1797 if (nd)
1798 {
1799 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1800 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1802 nd->setHidden(root->hidden);
1803 nd->setArtificial(root->artificial);
1804 nd->setLanguage(root->lang);
1805 nd->setId(root->id);
1806 nd->setMetaData(root->metaData);
1807 nd->setInline(root->spec.isInline());
1808 nd->setExported(root->exported);
1809
1810 addNamespaceToGroups(root,nd);
1811 nd->setRefItems(root->sli);
1812 nd->setRequirementReferences(root->rqli);
1813
1814 // file definition containing the namespace nd
1815 FileDef *fd=root->fileDef();
1816 // insert the namespace in the file definition
1817 if (fd) fd->insertNamespace(nd);
1818
1819 // the empty string test is needed for extract all case
1820 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1821 nd->insertUsedFile(fd);
1822 nd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1823 nd->setBodyDef(fd);
1824
1825 // also add namespace to the correct structural context
1826 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,fullName,nullptr,tagInfo);
1827 AUTO_TRACE_ADD("adding namespace {} to context {}",nd->name(),d ? d->name() : QCString("<none>"));
1828 if (d==nullptr) // we didn't find anything, create the scope artificially
1829 // anyway, so we can at least relate scopes properly.
1830 {
1831 d = buildScopeFromQualifiedName(fullName,nd->getLanguage(),tagInfo);
1833 if (dm)
1834 {
1835 dm->addInnerCompound(nd);
1836 }
1837 nd->setOuterScope(d);
1838 // TODO: Due to the order in which the tag file is written
1839 // a nested class can be found before its parent!
1840 }
1841 else
1842 {
1844 if (dm)
1845 {
1846 dm->addInnerCompound(nd);
1847 }
1848 nd->setOuterScope(d);
1849 // in case of d is an inline namespace, alias insert nd in the part scope of d.
1851 {
1852 NamespaceDef *pnd = toNamespaceDef(d);
1853 if (pnd && pnd->isInline())
1854 {
1855 d = d->getOuterScope();
1856 if (d)
1857 {
1858 dm = toDefinitionMutable(d);
1859 if (dm)
1860 {
1861 auto aliasNd = createNamespaceDefAlias(d,nd);
1862 dm->addInnerCompound(aliasNd.get());
1863 QCString aliasName = aliasNd->name();
1864 AUTO_TRACE_ADD("adding alias {} to {}",aliasName,d->name());
1865 Doxygen::namespaceLinkedMap->add(aliasName,std::move(aliasNd));
1866 }
1867 }
1868 else
1869 {
1870 break;
1871 }
1872 }
1873 else
1874 {
1875 break;
1876 }
1877 }
1878 }
1879 }
1880 }
1881 }
1882 }
1883 for (const auto &e : root->children()) buildNamespaceList(e.get());
1884}
1885
1886//----------------------------------------------------------------------
1887
1889 const QCString &name)
1890{
1891 NamespaceDef *usingNd =nullptr;
1892 for (auto &und : unl)
1893 {
1894 QCString uScope=und->name()+"::";
1895 usingNd = getResolvedNamespace(uScope+name);
1896 if (usingNd!=nullptr) break;
1897 }
1898 return usingNd;
1899}
1900
1901static void findUsingDirectives(const Entry *root)
1902{
1903 if (root->section.isUsingDir())
1904 {
1905 AUTO_TRACE("Found using directive {} at line {} of {}",root->name,root->startLine,root->fileName);
1906 QCString name=substitute(root->name,".","::");
1907 if (name.endsWith("::"))
1908 {
1909 name=name.left(name.length()-2);
1910 }
1911 if (!name.isEmpty())
1912 {
1913 NamespaceDef *usingNd = nullptr;
1914 NamespaceDefMutable *nd = nullptr;
1915 FileDef *fd = root->fileDef();
1916 QCString nsName;
1917
1918 // see if the using statement was found inside a namespace or inside
1919 // the global file scope.
1920 if (root->parent() && root->parent()->section.isNamespace() &&
1921 (fd==nullptr || fd->getLanguage()!=SrcLangExt::Java) // not a .java file
1922 )
1923 {
1924 nsName=stripAnonymousNamespaceScope(root->parent()->name);
1925 if (!nsName.isEmpty())
1926 {
1927 nd = getResolvedNamespaceMutable(nsName);
1928 }
1929 }
1930
1931 // find the scope in which the 'using' namespace is defined by prepending
1932 // the possible scopes in which the using statement was found, starting
1933 // with the most inner scope and going to the most outer scope (i.e.
1934 // file scope).
1935 int scopeOffset = static_cast<int>(nsName.length());
1936 do
1937 {
1938 QCString scope=scopeOffset>0 ?
1939 nsName.left(scopeOffset)+"::" : QCString();
1940 usingNd = getResolvedNamespace(scope+name);
1941 //printf("Trying with scope='%s' usingNd=%p\n",(scope+qPrint(name)),usingNd);
1942 if (scopeOffset==0)
1943 {
1944 scopeOffset=-1;
1945 }
1946 else if ((scopeOffset=nsName.findRev("::",scopeOffset-1))==-1)
1947 {
1948 scopeOffset=0;
1949 }
1950 } while (scopeOffset>=0 && usingNd==nullptr);
1951
1952 if (usingNd==nullptr && nd) // not found, try used namespaces in this scope
1953 // or in one of the parent namespace scopes
1954 {
1955 const NamespaceDefMutable *pnd = nd;
1956 while (pnd && usingNd==nullptr)
1957 {
1958 // also try with one of the used namespaces found earlier
1960
1961 // goto the parent
1962 Definition *s = pnd->getOuterScope();
1964 {
1966 }
1967 else
1968 {
1969 pnd = nullptr;
1970 }
1971 }
1972 }
1973 if (usingNd==nullptr && fd) // still nothing, also try used namespace in the
1974 // global scope
1975 {
1976 usingNd = findUsedNamespace(fd->getUsedNamespaces(),name);
1977 }
1978
1979 //printf("%s -> %s\n",qPrint(name),usingNd?qPrint(usingNd->name()):"<none>");
1980
1981 // add the namespace the correct scope
1982 if (usingNd)
1983 {
1984 //printf("using fd=%p nd=%p\n",fd,nd);
1985 if (nd)
1986 {
1987 //printf("Inside namespace %s\n",qPrint(nd->name()));
1988 nd->addUsingDirective(usingNd);
1989 }
1990 else if (fd)
1991 {
1992 //printf("Inside file %s\n",qPrint(fd->name()));
1993 fd->addUsingDirective(usingNd);
1994 }
1995 }
1996 else // unknown namespace, but add it anyway.
1997 {
1998 AUTO_TRACE_ADD("new unknown namespace {} lang={} hidden={}",name,langToString(root->lang),root->hidden);
1999 // add namespace to the list
2002 createNamespaceDef(root->fileName,root->startLine,root->startColumn,name)));
2003 if (nd)
2004 {
2005 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
2006 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2008 nd->setHidden(root->hidden);
2009 nd->setArtificial(TRUE);
2010 nd->setLanguage(root->lang);
2011 nd->setId(root->id);
2012 nd->setMetaData(root->metaData);
2013 nd->setInline(root->spec.isInline());
2014 nd->setExported(root->exported);
2015
2016 for (const Grouping &g : root->groups)
2017 {
2018 GroupDef *gd=nullptr;
2019 if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname)))
2020 gd->addNamespace(nd);
2021 }
2022
2023 // insert the namespace in the file definition
2024 if (fd)
2025 {
2026 fd->insertNamespace(nd);
2027 fd->addUsingDirective(nd);
2028 }
2029
2030 // the empty string test is needed for extract all case
2031 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2032 nd->insertUsedFile(fd);
2033 nd->setRefItems(root->sli);
2034 nd->setRequirementReferences(root->rqli);
2035 }
2036 }
2037 }
2038 }
2039 for (const auto &e : root->children()) findUsingDirectives(e.get());
2040}
2041
2042//----------------------------------------------------------------------
2043
2044static void buildListOfUsingDecls(const Entry *root)
2045{
2046 if (root->section.isUsingDecl() &&
2047 !root->parent()->section.isCompound() // not a class/struct member
2048 )
2049 {
2050 QCString name = substitute(root->name,".","::");
2051 g_usingDeclarations.insert(name.str());
2052 }
2053 for (const auto &e : root->children()) buildListOfUsingDecls(e.get());
2054}
2055
2056
2057static void findUsingDeclarations(const Entry *root,bool filterPythonPackages)
2058{
2059 if (root->section.isUsingDecl() &&
2060 !root->parent()->section.isCompound() && // not a class/struct member
2061 (!filterPythonPackages || (root->lang==SrcLangExt::Python && root->fileName.endsWith("__init__.py")))
2062 )
2063 {
2064 AUTO_TRACE("Found using declaration '{}' at line {} of {} inside section {}",
2065 root->name,root->startLine,root->fileName,root->parent()->section);
2066 if (!root->name.isEmpty())
2067 {
2068 const Definition *usingDef = nullptr;
2069 NamespaceDefMutable *nd = nullptr;
2070 FileDef *fd = root->fileDef();
2071 QCString scName;
2072
2073 // see if the using statement was found inside a namespace or inside
2074 // the global file scope.
2075 if (root->parent()->section.isNamespace())
2076 {
2077 scName=root->parent()->name;
2078 if (!scName.isEmpty())
2079 {
2080 nd = getResolvedNamespaceMutable(scName);
2081 }
2082 }
2083
2084 // Assume the using statement was used to import a class.
2085 // Find the scope in which the 'using' namespace is defined by prepending
2086 // the possible scopes in which the using statement was found, starting
2087 // with the most inner scope and going to the most outer scope (i.e.
2088 // file scope).
2089
2090 QCString name = substitute(root->name,".","::"); //Java/C# scope->internal
2091
2092 SymbolResolver resolver;
2093 const Definition *scope = nd;
2094 if (nd==nullptr) scope = fd;
2095 usingDef = resolver.resolveSymbol(scope,name);
2096
2097 //printf("usingDef(scope=%s,name=%s)=%s\n",qPrint(nd?nd->qualifiedName():""),qPrint(name),usingDef?qPrint(usingDef->qualifiedName()):"nullptr");
2098
2099 if (!usingDef)
2100 {
2101 usingDef = getClass(name); // try direct lookup, this is needed to get
2102 // builtin STL classes to properly resolve, e.g.
2103 // vector -> std::vector
2104 }
2105 if (!usingDef)
2106 {
2107 usingDef = Doxygen::hiddenClassLinkedMap->find(name); // check if it is already hidden
2108 }
2109#if 0
2110 if (!usingDef)
2111 {
2112 AUTO_TRACE_ADD("New using class '{}' (sec={})! #tArgLists={}",
2113 name,root->section,root->tArgLists.size());
2116 createClassDef( "<using>",1,1, name, ClassDef::Class)));
2117 if (usingCd)
2118 {
2119 usingCd->setArtificial(TRUE);
2120 usingCd->setLanguage(root->lang);
2121 usingDef = usingCd;
2122 }
2123 }
2124#endif
2125 else
2126 {
2127 AUTO_TRACE_ADD("Found used type '{}' in scope='{}'",
2128 usingDef->name(), nd ? nd->name(): fd ? fd->name() : QCString("<unknown>"));
2129 }
2130
2131 if (usingDef)
2132 {
2133 if (nd)
2134 {
2135 nd->addUsingDeclaration(usingDef);
2136 }
2137 else if (fd)
2138 {
2139 fd->addUsingDeclaration(usingDef);
2140 }
2141 }
2142 }
2143 }
2144 for (const auto &e : root->children()) findUsingDeclarations(e.get(),filterPythonPackages);
2145}
2146
2147//----------------------------------------------------------------------
2148
2150{
2151 root->commandOverrides.apply_callGraph ([&](bool b) { md->overrideCallGraph(b); });
2152 root->commandOverrides.apply_callerGraph ([&](bool b) { md->overrideCallerGraph(b); });
2153 root->commandOverrides.apply_referencedByRelation([&](bool b) { md->overrideReferencedByRelation(b); });
2154 root->commandOverrides.apply_referencesRelation ([&](bool b) { md->overrideReferencesRelation(b); });
2155 root->commandOverrides.apply_inlineSource ([&](bool b) { md->overrideInlineSource(b); });
2156 root->commandOverrides.apply_enumValues ([&](bool b) { md->overrideEnumValues(b); });
2157}
2158
2159//----------------------------------------------------------------------
2160
2162 const QCString &fileName,const QCString &memName)
2163{
2164 AUTO_TRACE("creating new member {} for class {}",memName,cd->name());
2165 const ArgumentList &templAl = md->templateArguments();
2166 const ArgumentList &al = md->argumentList();
2167 auto newMd = createMemberDef(
2168 fileName,root->startLine,root->startColumn,
2169 md->typeString(),memName,md->argsString(),
2170 md->excpString(),root->protection,root->virt,
2171 md->isStatic(),Relationship::Member,md->memberType(),
2172 templAl,al,root->metaData
2173 );
2174 auto newMmd = toMemberDefMutable(newMd.get());
2175 newMmd->setMemberClass(cd);
2176 cd->insertMember(newMd.get());
2177 if (!root->doc.isEmpty() || !root->brief.isEmpty())
2178 {
2179 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2180 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2181 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2182 }
2183 else
2184 {
2185 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2186 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2187 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2188 }
2189 newMmd->setDefinition(md->definition());
2190 applyMemberOverrideOptions(root,newMmd);
2191 newMmd->addQualifiers(root->qualifiers);
2192 newMmd->setBitfields(md->bitfieldString());
2193 newMmd->addSectionsToDefinition(root->anchors);
2194 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2195 newMmd->setBodyDef(md->getBodyDef());
2196 newMmd->setInitializer(md->initializer());
2197 newMmd->setRequiresClause(md->requiresClause());
2198 newMmd->setMaxInitLines(md->initializerLines());
2199 newMmd->setMemberGroupId(root->mGrpId);
2200 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2201 newMmd->setVhdlSpecifiers(md->getVhdlSpecifiers());
2202 newMmd->setLanguage(root->lang);
2203 newMmd->setId(root->id);
2204 MemberName *mn = Doxygen::memberNameLinkedMap->add(memName);
2205 mn->push_back(std::move(newMd));
2206}
2207
2208static std::unordered_map<std::string,std::vector<ClassDefMutable*>> g_usingClassMap;
2209
2210static void findUsingDeclImports(const Entry *root)
2211{
2212 if (root->section.isUsingDecl() &&
2213 root->parent()->section.isCompound() // in a class/struct member
2214 )
2215 {
2216 AUTO_TRACE("Found using declaration '{}' inside section {}", root->name, root->parent()->section);
2217 QCString fullName=removeRedundantWhiteSpace(root->parent()->name);
2218 fullName=stripAnonymousNamespaceScope(fullName);
2219 fullName=stripTemplateSpecifiersFromScope(fullName);
2220 ClassDefMutable *cd = getClassMutable(fullName);
2221 if (cd)
2222 {
2223 AUTO_TRACE_ADD("found class '{}'",cd->name());
2224 int i=root->name.findRev("::");
2225 if (i!=-1)
2226 {
2227 QCString scope=root->name.left(i);
2228 QCString memName=root->name.right(root->name.length()-i-2);
2229 SymbolResolver resolver;
2230 const ClassDef *bcd = resolver.resolveClass(cd,scope); // todo: file in fileScope parameter
2231 AUTO_TRACE_ADD("name={} scope={} bcd={}",scope,cd?cd->name():"<none>",bcd?bcd->name():"<none>");
2232 if (bcd && bcd!=cd)
2233 {
2234 AUTO_TRACE_ADD("found class '{}' memName='{}'",bcd->name(),memName);
2236 const MemberNameInfo *mni = mnlm.find(memName);
2237 if (mni)
2238 {
2239 for (auto &mi : *mni)
2240 {
2241 const MemberDef *md = mi->memberDef();
2242 if (md && md->protection()!=Protection::Private)
2243 {
2244 AUTO_TRACE_ADD("found member '{}'",mni->memberName());
2245 QCString fileName = root->fileName;
2246 if (fileName.isEmpty() && root->tagInfo())
2247 {
2248 fileName = root->tagInfo()->tagName;
2249 }
2250 if (!cd->containsOverload(md))
2251 {
2252 createUsingMemberImportForClass(root,cd,md,fileName,memName);
2253 // also insert the member into copies of the class
2254 auto it = g_usingClassMap.find(cd->qualifiedName().str());
2255 if (it != g_usingClassMap.end())
2256 {
2257 for (const auto &copyCd : it->second)
2258 {
2259 createUsingMemberImportForClass(root,copyCd,md,fileName,memName);
2260 }
2261 }
2262 }
2263 }
2264 }
2265 }
2266 }
2267 }
2268 }
2269 }
2270 else if (root->section.isUsingDecl() &&
2271 (root->parent()->section.isNamespace() || root->parent()->section.isEmpty()) && // namespace or global member
2272 root->lang==SrcLangExt::Cpp // do we also want this for e.g. Fortran? (see test case 095)
2273 )
2274 {
2275 AUTO_TRACE("Found using declaration '{}' inside section {}", root->name, root->parent()->section);
2276 Definition *scope = nullptr;
2277 NamespaceDefMutable *nd = nullptr;
2278 FileDef *fd = root->parent()->fileDef();
2279 if (!root->parent()->name.isEmpty())
2280 {
2281 QCString fullName=removeRedundantWhiteSpace(root->parent()->name);
2282 fullName=stripAnonymousNamespaceScope(fullName);
2284 scope = nd;
2285 }
2286 else
2287 {
2288 scope = fd;
2289 }
2290 if (scope)
2291 {
2292 AUTO_TRACE_ADD("found scope '{}'",scope->name());
2293 SymbolResolver resolver;
2294 const Definition *def = resolver.resolveSymbol(root->name.startsWith("::") ? nullptr : scope,root->name);
2295 if (def && def->definitionType()==Definition::TypeMember)
2296 {
2297 int i=root->name.findRev("::");
2298 QCString memName;
2299 if (i!=-1)
2300 {
2301 memName = root->name.right(root->name.length()-i-2);
2302 }
2303 else
2304 {
2305 memName = root->name;
2306 }
2307 const MemberDef *md = toMemberDef(def);
2308 AUTO_TRACE_ADD("found member '{}' for name '{}'",md->qualifiedName(),root->name);
2309 QCString fileName = root->fileName;
2310 if (fileName.isEmpty() && root->tagInfo())
2311 {
2312 fileName = root->tagInfo()->tagName;
2313 }
2314 const ArgumentList &templAl = md->templateArguments();
2315 const ArgumentList &al = md->argumentList();
2316
2317 auto newMd = createMemberDef(
2318 fileName,root->startLine,root->startColumn,
2319 md->typeString(),memName,md->argsString(),
2320 md->excpString(),root->protection,root->virt,
2321 md->isStatic(),Relationship::Member,md->memberType(),
2322 templAl,al,root->metaData
2323 );
2324 auto newMmd = toMemberDefMutable(newMd.get());
2325 if (nd)
2326 {
2327 newMmd->setNamespace(nd);
2328 nd->insertMember(newMd.get());
2329 }
2330 if (fd)
2331 {
2332 newMmd->setFileDef(fd);
2333 fd->insertMember(newMd.get());
2334 }
2335 if (!root->doc.isEmpty() || !root->brief.isEmpty())
2336 {
2337 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2338 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2339 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2340 }
2341 else
2342 {
2343 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2344 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2345 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2346 }
2347 newMmd->setDefinition(md->definition());
2348 applyMemberOverrideOptions(root,newMmd);
2349 newMmd->addQualifiers(root->qualifiers);
2350 newMmd->setBitfields(md->bitfieldString());
2351 newMmd->addSectionsToDefinition(root->anchors);
2352 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2353 newMmd->setBodyDef(md->getBodyDef());
2354 newMmd->setInitializer(md->initializer());
2355 newMmd->setRequiresClause(md->requiresClause());
2356 newMmd->setMaxInitLines(md->initializerLines());
2357 newMmd->setMemberGroupId(root->mGrpId);
2358 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2359 newMmd->setVhdlSpecifiers(md->getVhdlSpecifiers());
2360 newMmd->setLanguage(root->lang);
2361 newMmd->setId(root->id);
2362 MemberName *mn = Doxygen::functionNameLinkedMap->add(memName);
2363 mn->push_back(std::move(newMd));
2364#if 0 // insert an alias instead of a copy
2365 const MemberDef *md = toMemberDef(def);
2366 AUTO_TRACE_ADD("found member '{}' for name '{}'",md->qualifiedName(),root->name);
2367 auto aliasMd = createMemberDefAlias(nd,md);
2368 QCString aliasFullName = nd->qualifiedName()+"::"+aliasMd->localName();
2369 if (nd && aliasMd.get())
2370 {
2371 nd->insertMember(aliasMd.get());
2372 }
2373 if (fd && aliasMd.get())
2374 {
2375 fd->insertMember(aliasMd.get());
2376 }
2377 MemberName *mn = Doxygen::memberNameLinkedMap->add(aliasFullName);
2378 mn->push_back(std::move(aliasMd));
2379#endif
2380 }
2381 else if (def && def->definitionType()==Definition::TypeClass)
2382 {
2383 const ClassDef *cd = toClassDef(def);
2384 QCString copyFullName;
2385 if (nd==nullptr)
2386 {
2387 copyFullName = cd->localName();
2388 }
2389 else
2390 {
2391 copyFullName = nd->qualifiedName()+"::"+cd->localName();
2392 }
2393 if (Doxygen::classLinkedMap->find(copyFullName)==nullptr)
2394 {
2396 Doxygen::classLinkedMap->add(copyFullName,
2397 cd->deepCopy(copyFullName)));
2398 AUTO_TRACE_ADD("found class '{}' for name '{}' copy '{}' obj={}",cd->qualifiedName(),root->name,copyFullName,(void*)ncdm);
2399 g_usingClassMap[cd->qualifiedName().str()].push_back(ncdm);
2400 if (ncdm)
2401 {
2402 if (nd) ncdm->moveTo(nd);
2403 if ((!root->doc.isEmpty() || !root->brief.isEmpty())) // use docs at using statement
2404 {
2405 ncdm->setDocumentation(root->doc,root->docFile,root->docLine);
2406 ncdm->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2407 }
2408 else // use docs from used class
2409 {
2410 ncdm->setDocumentation(cd->documentation(),cd->docFile(),cd->docLine());
2412 }
2413 if (nd)
2414 {
2415 nd->addInnerCompound(ncdm);
2416 nd->addUsingDeclaration(ncdm);
2417 }
2418 if (fd)
2419 {
2420 if (ncdm) ncdm->setFileDef(fd);
2421 fd->insertClass(ncdm);
2422 fd->addUsingDeclaration(ncdm);
2423 }
2424 }
2425 }
2426#if 0 // insert an alias instead of a copy
2427 auto aliasCd = createClassDefAlias(nd,cd);
2428 QCString aliasFullName;
2429 if (nd==nullptr)
2430 {
2431 aliasFullName = aliasCd->localName();
2432 }
2433 else
2434 {
2435 aliasFullName = nd->qualifiedName()+"::"+aliasCd->localName();
2436 }
2437 AUTO_TRACE_ADD("found class '{}' for name '{}' aliasFullName='{}'",cd->qualifiedName(),root->name,aliasFullName);
2438 auto acd = Doxygen::classLinkedMap->add(aliasFullName,std::move(aliasCd));
2439 if (nd && acd)
2440 {
2441 nd->addInnerCompound(acd);
2442 }
2443 if (fd && acd)
2444 {
2445 fd->insertClass(acd);
2446 }
2447#endif
2448 }
2449 else if (scope)
2450 {
2451 AUTO_TRACE_ADD("no symbol with name '{}' in scope {}",root->name,scope->name());
2452 }
2453 }
2454 }
2455 for (const auto &e : root->children()) findUsingDeclImports(e.get());
2456}
2457
2458//----------------------------------------------------------------------
2459
2461{
2462 FileDefSet visitedFiles;
2463 // then recursively add using directives found in #include files
2464 // to files that have not been visited.
2465 for (const auto &fn : *Doxygen::inputNameLinkedMap)
2466 {
2467 for (const auto &fd : *fn)
2468 {
2469 //printf("----- adding using directives for file %s\n",qPrint(fd->name()));
2470 fd->addIncludedUsingDirectives(visitedFiles);
2471 }
2472 }
2473}
2474
2475//----------------------------------------------------------------------
2476
2478 const Entry *root,
2479 ClassDefMutable *cd,
2480 MemberType mtype,
2481 const QCString &type,
2482 const QCString &name,
2483 const QCString &args,
2484 bool fromAnnScope,
2485 MemberDef *fromAnnMemb,
2486 Protection prot,
2487 Relationship related)
2488{
2490 QCString scopeSeparator="::";
2491 SrcLangExt lang = cd->getLanguage();
2492 if (lang==SrcLangExt::Java || lang==SrcLangExt::CSharp)
2493 {
2494 qualScope = substitute(qualScope,"::",".");
2495 scopeSeparator=".";
2496 }
2497 AUTO_TRACE("class variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} ann={} init='{}'",
2498 root->fileName, type, qualScope, name, args, root->protection, mtype, lang, fromAnnScope, root->initializer.str());
2499
2500 QCString def;
2501 if (!type.isEmpty())
2502 {
2503 if (related!=Relationship::Member || mtype==MemberType::Friend || Config_getBool(HIDE_SCOPE_NAMES))
2504 {
2505 if (root->spec.isAlias()) // turn 'typedef B A' into 'using A'
2506 {
2507 if (lang==SrcLangExt::Python)
2508 {
2509 def="type "+name+args;
2510 }
2511 else
2512 {
2513 def="using "+name;
2514 }
2515 }
2516 else
2517 {
2518 def=type+" "+name+args;
2519 }
2520 }
2521 else
2522 {
2523 if (root->spec.isAlias()) // turn 'typedef B C::A' into 'using C::A'
2524 {
2525 if (lang==SrcLangExt::Python)
2526 {
2527 def="type "+qualScope+scopeSeparator+name+args;
2528 }
2529 else
2530 {
2531 def="using "+qualScope+scopeSeparator+name;
2532 }
2533 }
2534 else
2535 {
2536 def=type+" "+qualScope+scopeSeparator+name+args;
2537 }
2538 }
2539 }
2540 else
2541 {
2542 if (Config_getBool(HIDE_SCOPE_NAMES))
2543 {
2544 def=name+args;
2545 }
2546 else
2547 {
2548 def=qualScope+scopeSeparator+name+args;
2549 }
2550 }
2551 def.stripPrefix("static ");
2552
2553 // see if the member is already found in the same scope
2554 // (this may be the case for a static member that is initialized
2555 // outside the class)
2557 if (mn)
2558 {
2559 for (const auto &imd : *mn)
2560 {
2561 //printf("md->getClassDef()=%p cd=%p type=[%s] md->typeString()=[%s]\n",
2562 // md->getClassDef(),cd,qPrint(type),md->typeString());
2563 MemberDefMutable *md = toMemberDefMutable(imd.get());
2564 if (md &&
2565 md->getClassDef()==cd &&
2566 ((lang==SrcLangExt::Python && type.isEmpty() && !md->typeString().isEmpty()) ||
2568 // member already in the scope
2569 {
2570
2571 if (root->lang==SrcLangExt::ObjC &&
2572 root->mtype==MethodTypes::Property &&
2574 { // Objective-C 2.0 property
2575 // turn variable into a property
2576 md->setProtection(root->protection);
2578 }
2579 addMemberDocs(root,md,def,nullptr,FALSE,root->spec);
2580 AUTO_TRACE_ADD("Member already found!");
2581 return md;
2582 }
2583 }
2584 }
2585
2586 QCString fileName = root->fileName;
2587 if (fileName.isEmpty() && root->tagInfo())
2588 {
2589 fileName = root->tagInfo()->tagName;
2590 }
2591
2592 // new member variable, typedef or enum value
2593 auto md = createMemberDef(
2594 fileName,root->startLine,root->startColumn,
2595 type,name,args,root->exception,
2596 prot,Specifier::Normal,root->isStatic,related,
2597 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2598 ArgumentList(), root->metaData);
2599 auto mmd = toMemberDefMutable(md.get());
2600 mmd->setTagInfo(root->tagInfo());
2601 mmd->setMemberClass(cd); // also sets outer scope (i.e. getOuterScope())
2602 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2603 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2604 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2605 mmd->setDefinition(def);
2606 mmd->setBitfields(root->bitfields);
2607 mmd->addSectionsToDefinition(root->anchors);
2608 mmd->setFromAnonymousScope(fromAnnScope);
2609 mmd->setFromAnonymousMember(fromAnnMemb);
2610 //md->setIndentDepth(indentDepth);
2611 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2612 mmd->setInitializer(root->initializer.str());
2613 mmd->setMaxInitLines(root->initLines);
2614 mmd->setMemberGroupId(root->mGrpId);
2615 mmd->setMemberSpecifiers(root->spec);
2616 mmd->setVhdlSpecifiers(root->vhdlSpec);
2617 mmd->setReadAccessor(root->read);
2618 mmd->setWriteAccessor(root->write);
2620 mmd->setHidden(root->hidden);
2621 mmd->setArtificial(root->artificial);
2622 mmd->setLanguage(root->lang);
2623 mmd->setId(root->id);
2624 addMemberToGroups(root,md.get());
2626 mmd->setBodyDef(root->fileDef());
2627 mmd->addQualifiers(root->qualifiers);
2628
2629 AUTO_TRACE_ADD("Adding new member to class '{}'",cd->name());
2630 cd->insertMember(md.get());
2631 mmd->setRefItems(root->sli);
2632 mmd->setRequirementReferences(root->rqli);
2633
2634 cd->insertUsedFile(root->fileDef());
2635 root->markAsProcessed();
2636
2637 if (mtype==MemberType::Typedef)
2638 {
2639 resolveTemplateInstanceInType(root,cd,md.get());
2640 }
2641
2642 // add the member to the global list
2643 MemberDef *result = md.get();
2644 mn = Doxygen::memberNameLinkedMap->add(name);
2645 mn->push_back(std::move(md));
2646
2647 return result;
2648}
2649
2650//----------------------------------------------------------------------
2651
2653 const Entry *root,
2654 MemberType mtype,
2655 const QCString &scope,
2656 const QCString &type,
2657 const QCString &name,
2658 const QCString &args,
2659 bool fromAnnScope,
2660 MemberDef *fromAnnMemb)
2661{
2662 AUTO_TRACE("global variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} init='{}'",
2663 root->fileName, type, scope, name, args, root->protection, mtype, root->lang, root->initializer.str());
2664
2665 FileDef *fd = root->fileDef();
2666
2667 // see if we have a typedef that should hide a struct or union
2668 if (mtype==MemberType::Typedef && Config_getBool(TYPEDEF_HIDES_STRUCT))
2669 {
2670 QCString ttype = type;
2671 ttype.stripPrefix("typedef ");
2672 if (ttype.stripPrefix("struct ") || ttype.stripPrefix("union "))
2673 {
2674 static const reg::Ex re(R"(\a\w*)");
2675 reg::Match match;
2676 const std::string &typ = ttype.str();
2677 if (reg::search(typ,match,re))
2678 {
2679 QCString typeValue = match.str();
2680 ClassDefMutable *cd = getClassMutable(typeValue);
2681 if (cd)
2682 {
2683 // this typedef should hide compound name cd, so we
2684 // change the name that is displayed from cd.
2685 cd->setClassName(name);
2686 cd->setDocumentation(root->doc,root->docFile,root->docLine);
2687 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2688 return nullptr;
2689 }
2690 }
2691 }
2692 }
2693
2694 // see if the function is inside a namespace
2695 NamespaceDefMutable *nd = nullptr;
2696 if (!scope.isEmpty())
2697 {
2698 if (scope.find('@')!=-1) return nullptr; // anonymous scope!
2699 nd = getResolvedNamespaceMutable(scope);
2700 }
2701 QCString def;
2702
2703 // determine the definition of the global variable
2704 if (nd && !nd->isAnonymous() &&
2705 !Config_getBool(HIDE_SCOPE_NAMES)
2706 )
2707 // variable is inside a namespace, so put the scope before the name
2708 {
2709 SrcLangExt lang = nd->getLanguage();
2711
2712 if (!type.isEmpty())
2713 {
2714 if (root->spec.isAlias()) // turn 'typedef B NS::A' into 'using NS::A'
2715 {
2716 if (lang==SrcLangExt::Python)
2717 {
2718 def="type "+nd->name()+sep+name+args;
2719 }
2720 else
2721 {
2722 def="using "+nd->name()+sep+name;
2723 }
2724 }
2725 else // normal member
2726 {
2727 def=type+" "+nd->name()+sep+name+args;
2728 }
2729 }
2730 else
2731 {
2732 def=nd->name()+sep+name+args;
2733 }
2734 }
2735 else
2736 {
2737 if (!type.isEmpty() && !root->name.isEmpty())
2738 {
2739 if (name.at(0)=='@') // dummy variable representing anonymous union
2740 {
2741 def=type;
2742 }
2743 else
2744 {
2745 if (root->spec.isAlias()) // turn 'typedef B A' into 'using A'
2746 {
2747 if (root->lang==SrcLangExt::Python)
2748 {
2749 def="type "+root->name+args;
2750 }
2751 else
2752 {
2753 def="using "+root->name;
2754 }
2755 }
2756 else // normal member
2757 {
2758 def=type+" "+name+args;
2759 }
2760 }
2761 }
2762 else
2763 {
2764 def=name+args;
2765 }
2766 }
2767 def.stripPrefix("static ");
2768
2770 if (mn)
2771 {
2772 //QCString nscope=removeAnonymousScopes(scope);
2773 //NamespaceDef *nd=nullptr;
2774 //if (!nscope.isEmpty())
2775 if (!scope.isEmpty())
2776 {
2777 nd = getResolvedNamespaceMutable(scope);
2778 }
2779 for (const auto &imd : *mn)
2780 {
2781 MemberDefMutable *md = toMemberDefMutable(imd.get());
2782 if (md &&
2783 ((nd==nullptr && md->getNamespaceDef()==nullptr && md->getFileDef() &&
2784 root->fileName==md->getFileDef()->absFilePath()
2785 ) // both variable names in the same file
2786 || (nd!=nullptr && md->getNamespaceDef()==nd) // both in same namespace
2787 )
2788 && !md->isDefine() // function style #define's can be "overloaded" by typedefs or variables
2789 && !md->isEnumerate() // in C# an enum value and enum can have the same name
2790 )
2791 // variable already in the scope
2792 {
2793 bool isPHPArray = md->getLanguage()==SrcLangExt::PHP &&
2794 md->argsString()!=args &&
2795 args.find('[')!=-1;
2796 bool staticsInDifferentFiles =
2797 root->isStatic && md->isStatic() &&
2798 root->fileName!=md->getDefFileName();
2799
2800 if (md->getFileDef() &&
2801 !isPHPArray && // not a php array
2802 !staticsInDifferentFiles
2803 )
2804 // not a php array variable
2805 {
2806 AUTO_TRACE_ADD("variable already found: scope='{}'",md->getOuterScope()->name());
2807 addMemberDocs(root,md,def,nullptr,FALSE,root->spec);
2808 md->setRefItems(root->sli);
2809 md->setRequirementReferences(root->rqli);
2810 // if md is a variable forward declaration and root is the definition that
2811 // turn md into the definition
2812 if (!root->explicitExternal && md->isExternal())
2813 {
2814 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
2816 }
2817 // if md is the definition and root point at a declaration, then add the
2818 // declaration info
2819 else if (root->explicitExternal && !md->isExternal())
2820 {
2821 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
2822 }
2823 return md;
2824 }
2825 }
2826 }
2827 }
2828
2829 QCString fileName = root->fileName;
2830 if (fileName.isEmpty() && root->tagInfo())
2831 {
2832 fileName = root->tagInfo()->tagName;
2833 }
2834
2835 AUTO_TRACE_ADD("new variable, namespace='{}'",nd?nd->name():QCString("<global>"));
2836 // new global variable, enum value or typedef
2837 auto md = createMemberDef(
2838 fileName,root->startLine,root->startColumn,
2839 type,name,args,QCString(),
2840 root->protection, Specifier::Normal,root->isStatic,Relationship::Member,
2841 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2842 root->argList, root->metaData);
2843 auto mmd = toMemberDefMutable(md.get());
2844 mmd->setTagInfo(root->tagInfo());
2845 mmd->setMemberSpecifiers(root->spec);
2846 mmd->setVhdlSpecifiers(root->vhdlSpec);
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->addSectionsToDefinition(root->anchors);
2851 mmd->setFromAnonymousScope(fromAnnScope);
2852 mmd->setFromAnonymousMember(fromAnnMemb);
2853 mmd->setInitializer(root->initializer.str());
2854 mmd->setMaxInitLines(root->initLines);
2855 mmd->setMemberGroupId(root->mGrpId);
2856 mmd->setDefinition(def);
2857 mmd->setLanguage(root->lang);
2858 mmd->setId(root->id);
2860 mmd->setExplicitExternal(root->explicitExternal,fileName,root->startLine,root->startColumn);
2861 mmd->addQualifiers(root->qualifiers);
2862 //md->setOuterScope(fd);
2863 if (!root->explicitExternal)
2864 {
2865 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2866 mmd->setBodyDef(fd);
2867 }
2868 addMemberToGroups(root,md.get());
2870
2871 mmd->setRefItems(root->sli);
2872 mmd->setRequirementReferences(root->rqli);
2873 if (nd && !nd->isAnonymous())
2874 {
2875 mmd->setNamespace(nd);
2876 nd->insertMember(md.get());
2877 }
2878
2879 // add member to the file (we do this even if we have already inserted
2880 // it into the namespace.
2881 if (fd)
2882 {
2883 mmd->setFileDef(fd);
2884 fd->insertMember(md.get());
2885 }
2886
2887 root->markAsProcessed();
2888
2889 if (mtype==MemberType::Typedef)
2890 {
2891 resolveTemplateInstanceInType(root,nd,md.get());
2892 }
2893
2894 // add member definition to the list of globals
2895 MemberDef *result = md.get();
2896 mn = Doxygen::functionNameLinkedMap->add(name);
2897 mn->push_back(std::move(md));
2898
2899
2900
2901 return result;
2902}
2903
2904/*! See if the return type string \a type is that of a function pointer
2905 * \returns -1 if this is not a function pointer variable or
2906 * the index at which the closing brace of (...*name) was found.
2907 */
2908static int findFunctionPtr(const std::string &type,SrcLangExt lang, int *pLength=nullptr)
2909{
2910 AUTO_TRACE("type='{}' lang={}",type,lang);
2911 if (lang == SrcLangExt::Fortran || lang == SrcLangExt::VHDL)
2912 {
2913 return -1; // Fortran and VHDL do not have function pointers
2914 }
2915
2916 static const reg::Ex re(R"(\‍([^)]*[*&^][^)]*\))");
2917 reg::Match match;
2918 size_t i=std::string::npos;
2919 size_t l=0;
2920 if (reg::search(type,match,re)) // contains (...*...) or (...&...) or (...^...)
2921 {
2922 i = match.position();
2923 l = match.length();
2924 }
2925 if (i!=std::string::npos)
2926 {
2927 size_t di = type.find("decltype(");
2928 if (di!=std::string::npos && di<i)
2929 {
2930 i = std::string::npos;
2931 }
2932 }
2933 size_t bb=type.find('<');
2934 size_t be=type.rfind('>');
2935 bool templFp = false;
2936 if (be!=std::string::npos) {
2937 size_t cc_ast = type.find("::*");
2938 size_t cc_amp = type.find("::&");
2939 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>::*)'
2940 }
2941
2942 if (!type.empty() && // return type is non-empty
2943 i!=std::string::npos && // contains (...*...)
2944 type.find("operator")==std::string::npos && // not an operator
2945 (type.find(")(")==std::string::npos || type.find("typedef ")!=std::string::npos) &&
2946 // not a function pointer return type
2947 (!(bb<i && i<be) || templFp) // bug665855: avoid treating "typedef A<void (T*)> type" as a function pointer
2948 )
2949 {
2950 if (pLength) *pLength=static_cast<int>(l);
2951 //printf("findFunctionPtr=%d\n",(int)i);
2952 AUTO_TRACE_EXIT("result={}",i);
2953 return static_cast<int>(i);
2954 }
2955 else
2956 {
2957 //printf("findFunctionPtr=%d\n",-1);
2958 AUTO_TRACE_EXIT("result=-1");
2959 return -1;
2960 }
2961}
2962
2963//--------------------------------------------------------------------------------------
2964
2965/*! Returns TRUE iff \a type is a class within scope \a context.
2966 * Used to detect variable declarations that look like function prototypes.
2967 */
2968static bool isVarWithConstructor(const Entry *root)
2969{
2970 bool result = false;
2971 bool typeIsClass = false;
2972 bool typePtrType = false;
2973 QCString type;
2974 Definition *ctx = nullptr;
2975 FileDef *fd = root->fileDef();
2976 SymbolResolver resolver(fd);
2977
2978 AUTO_TRACE("isVarWithConstructor({})",root->name);
2979 if (root->parent()->section.isCompound())
2980 { // inside a class
2981 result=FALSE;
2982 AUTO_TRACE_EXIT("inside class: result={}",result);
2983 return result;
2984 }
2985 else if ((fd != nullptr) && (fd->name().endsWith(".c") || fd->name().endsWith(".h")))
2986 { // inside a .c file
2987 result=FALSE;
2988 AUTO_TRACE_EXIT("inside C file: result={}",result);
2989 return result;
2990 }
2991 if (root->type.isEmpty())
2992 {
2993 result=FALSE;
2994 AUTO_TRACE_EXIT("no type: result={}",result);
2995 return result;
2996 }
2997 if (!root->parent()->name.isEmpty())
2998 {
2999 ctx=Doxygen::namespaceLinkedMap->find(root->parent()->name);
3000 }
3001 type = root->type;
3002 // remove qualifiers
3003 findAndRemoveWord(type,"const");
3004 findAndRemoveWord(type,"static");
3005 findAndRemoveWord(type,"volatile");
3006 typePtrType = type.find('*')!=-1 || type.find('&')!=-1;
3007 if (!typePtrType)
3008 {
3009 typeIsClass = resolver.resolveClass(ctx,type)!=nullptr;
3010 int ti=0;
3011 if (!typeIsClass && (ti=type.find('<'))!=-1)
3012 {
3013 typeIsClass=resolver.resolveClass(ctx,type.left(ti))!=nullptr;
3014 }
3015 }
3016 if (typeIsClass) // now we still have to check if the arguments are
3017 // types or values. Since we do not have complete type info
3018 // we need to rely on heuristics :-(
3019 {
3020 if (root->argList.empty())
3021 {
3022 result=FALSE; // empty arg list -> function prototype.
3023 AUTO_TRACE_EXIT("empty arg list: result={}",result);
3024 return result;
3025 }
3026 for (const Argument &a : root->argList)
3027 {
3028 static const reg::Ex initChars(R"([\d"'&*!^]+)");
3029 reg::Match match;
3030 if (!a.name.isEmpty() || !a.defval.isEmpty())
3031 {
3032 std::string name = a.name.str();
3033 if (reg::search(name,match,initChars) && match.position()==0)
3034 {
3035 result=TRUE;
3036 }
3037 else
3038 {
3039 result=FALSE; // arg has (type,name) pair -> function prototype
3040 }
3041 AUTO_TRACE_EXIT("function prototype: result={}",result);
3042 return result;
3043 }
3044 if (!a.type.isEmpty() &&
3045 (a.type.at(a.type.length()-1)=='*' ||
3046 a.type.at(a.type.length()-1)=='&'))
3047 // type ends with * or & => pointer or reference
3048 {
3049 result=FALSE;
3050 AUTO_TRACE_EXIT("pointer or reference: result={}",result);
3051 return result;
3052 }
3053 if (a.type.isEmpty() || resolver.resolveClass(ctx,a.type)!=nullptr)
3054 {
3055 result=FALSE; // arg type is a known type
3056 AUTO_TRACE_EXIT("known type: result={}",result);
3057 return result;
3058 }
3059 if (checkIfTypedef(ctx,fd,a.type))
3060 {
3061 result=FALSE; // argument is a typedef
3062 AUTO_TRACE_EXIT("typedef: result={}",result);
3063 return result;
3064 }
3065 std::string atype = a.type.str();
3066 if (reg::search(atype,match,initChars) && match.position()==0)
3067 {
3068 result=TRUE; // argument type starts with typical initializer char
3069 AUTO_TRACE_EXIT("argument with init char: result={}",result);
3070 return result;
3071 }
3072 std::string resType=resolveTypeDef(ctx,a.type).str();
3073 if (resType.empty()) resType=atype;
3074 static const reg::Ex idChars(R"(\a\w*)");
3075 if (reg::search(resType,match,idChars) && match.position()==0) // resType starts with identifier
3076 {
3077 resType=match.str();
3078 if (resType=="int" || resType=="long" ||
3079 resType=="float" || resType=="double" ||
3080 resType=="char" || resType=="void" ||
3081 resType=="signed" || resType=="unsigned" ||
3082 resType=="const" || resType=="volatile" )
3083 {
3084 result=FALSE; // type keyword -> function prototype
3085 AUTO_TRACE_EXIT("type keyword: result={}",result);
3086 return result;
3087 }
3088 }
3089 }
3090 result=TRUE;
3091 }
3092
3093 AUTO_TRACE_EXIT("end: result={}",result);
3094 return result;
3095}
3096
3097//--------------------------------------------------------------------------------------
3098
3099/*! Searches for the end of a template in prototype \a s starting from
3100 * character position \a startPos. If the end was found the position
3101 * of the closing > is returned, otherwise -1 is returned.
3102 *
3103 * Handles exotic cases such as
3104 * \code
3105 * Class<(id<0)>
3106 * Class<bits<<2>
3107 * Class<"<">
3108 * Class<'<'>
3109 * Class<(")<")>
3110 * \endcode
3111 */
3112static int findEndOfTemplate(const QCString &s,size_t startPos)
3113{
3114 // locate end of template
3115 size_t e=startPos;
3116 int brCount=1;
3117 int roundCount=0;
3118 size_t len = s.length();
3119 bool insideString=FALSE;
3120 bool insideChar=FALSE;
3121 char pc = 0;
3122 while (e<len && brCount!=0)
3123 {
3124 char c=s.at(e);
3125 switch(c)
3126 {
3127 case '<':
3128 if (!insideString && !insideChar)
3129 {
3130 if (e<len-1 && s.at(e+1)=='<')
3131 e++;
3132 else if (roundCount==0)
3133 brCount++;
3134 }
3135 break;
3136 case '>':
3137 if (!insideString && !insideChar)
3138 {
3139 if (e<len-1 && s.at(e+1)=='>')
3140 e++;
3141 else if (roundCount==0)
3142 brCount--;
3143 }
3144 break;
3145 case '(':
3146 if (!insideString && !insideChar)
3147 roundCount++;
3148 break;
3149 case ')':
3150 if (!insideString && !insideChar)
3151 roundCount--;
3152 break;
3153 case '"':
3154 if (!insideChar)
3155 {
3156 if (insideString && pc!='\\')
3157 insideString=FALSE;
3158 else
3159 insideString=TRUE;
3160 }
3161 break;
3162 case '\'':
3163 if (!insideString)
3164 {
3165 if (insideChar && pc!='\\')
3166 insideChar=FALSE;
3167 else
3168 insideChar=TRUE;
3169 }
3170 break;
3171 }
3172 pc = c;
3173 e++;
3174 }
3175 return brCount==0 ? static_cast<int>(e) : -1;
3176}
3177
3178//--------------------------------------------------------------------------------------
3179
3180static void addVariable(const Entry *root,int isFuncPtr=-1)
3181{
3182 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
3183
3184 AUTO_TRACE("VARIABLE_SEC: type='{}' name='{}' args='{}' bodyLine={} endBodyLine={} mGrpId={} relates='{}'",
3185 root->type, root->name, root->args, root->bodyLine, root->endBodyLine, root->mGrpId, root->relates);
3186 //printf("root->parent->name=%s\n",qPrint(root->parent->name));
3187
3188 QCString type = root->type;
3189 QCString name = root->name;
3190 QCString args = root->args;
3191 if (type.isEmpty() && name.find("operator")==-1 &&
3192 (name.find('*')!=-1 || name.find('&')!=-1))
3193 {
3194 // recover from parse error caused by redundant braces
3195 // like in "int *(var[10]);", which is parsed as
3196 // type="" name="int *" args="(var[10])"
3197
3198 type=name;
3199 std::string sargs = args.str();
3200 static const reg::Ex reName(R"(\a\w*)");
3201 reg::Match match;
3202 if (reg::search(sargs,match,reName))
3203 {
3204 name = match.str(); // e.g. 'var' in '(var[10])'
3205 sargs = match.suffix().str(); // e.g. '[10]) in '(var[10])'
3206 size_t j = sargs.find(')');
3207 if (j!=std::string::npos) args=sargs.substr(0,j); // extract, e.g '[10]' from '[10])'
3208 }
3209 }
3210 else
3211 {
3212 int i=isFuncPtr;
3213 if (i==-1 && (root->spec.isAlias())==0) i=findFunctionPtr(type.str(),root->lang); // for typedefs isFuncPtr is not yet set
3214 AUTO_TRACE_ADD("functionPtr={}",i!=-1?"yes":"no");
3215 if (i>=0) // function pointer
3216 {
3217 int ai = type.find('[',i);
3218 if (ai>i) // function pointer array
3219 {
3220 args.prepend(type.right(type.length()-ai));
3221 type=type.left(ai);
3222 }
3223 else if (type.find(')',i)!=-1) // function ptr, not variable like "int (*bla)[10]"
3224 {
3225 type=type.left(type.length()-1);
3226 args.prepend(") ");
3227 }
3228 }
3229 }
3230 AUTO_TRACE_ADD("after correction: type='{}' name='{}' args='{}'",type,name,args);
3231
3232 QCString scope;
3233 name=removeRedundantWhiteSpace(name);
3234
3235 // find the scope of this variable
3236 int index = computeQualifiedIndex(name);
3237 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3238 // grouped members are stored with full scope
3239 {
3240 buildScopeFromQualifiedName(name.left(index+2),root->lang,root->tagInfo());
3241 scope=name.left(index);
3242 name=name.mid(index+2);
3243 }
3244 else
3245 {
3246 Entry *p = root->parent();
3247 while (p->section.isScope())
3248 {
3249 QCString scopeName = p->name;
3250 if (!scopeName.isEmpty())
3251 {
3252 scope.prepend(scopeName);
3253 break;
3254 }
3255 p=p->parent();
3256 }
3257 }
3258
3259 QCString type_s = type;
3260 type=type.stripWhiteSpace();
3261 ClassDefMutable *cd=nullptr;
3262 bool isRelated=FALSE;
3263 bool isMemberOf=FALSE;
3264
3265 QCString classScope=stripAnonymousNamespaceScope(scope);
3266 if (root->lang==SrcLangExt::CSharp)
3267 {
3268 classScope=mangleCSharpGenericName(classScope);
3269 }
3270 else
3271 {
3272 classScope=stripTemplateSpecifiersFromScope(classScope,FALSE);
3273 }
3274 QCString annScopePrefix=scope.left(scope.length()-classScope.length());
3275
3276
3277 // Look for last :: not part of template specifier
3278 int p=-1;
3279 for (size_t i=0;i<name.length()-1;i++)
3280 {
3281 if (name[i]==':' && name[i+1]==':')
3282 {
3283 p=static_cast<int>(i);
3284 }
3285 else if (name[i]=='<') // skip over template parts,
3286 // i.e. A::B<C::D> => p=1 and
3287 // A<B::C>::D => p=8
3288 {
3289 int e = findEndOfTemplate(name,i+1);
3290 if (e!=-1) i=static_cast<int>(e);
3291 }
3292 }
3293
3294 if (p!=-1) // found it
3295 {
3296 if (type=="friend class" || type=="friend struct" ||
3297 type=="friend union")
3298 {
3299 cd=getClassMutable(scope);
3300 if (cd)
3301 {
3302 addVariableToClass(root, // entry
3303 cd, // class to add member to
3304 MemberType::Friend, // type of member
3305 type, // type value as string
3306 name, // name of the member
3307 args, // arguments as string
3308 FALSE, // from Anonymous scope
3309 nullptr, // anonymous member
3310 Protection::Public, // protection
3311 Relationship::Member // related to a class
3312 );
3313 }
3314 }
3315 if (root->bodyLine!=-1 && root->endBodyLine!=-1) // store the body location for later use
3316 {
3317 Doxygen::staticInitMap.emplace(name.str(),BodyInfo{root->startLine,root->bodyLine,root->endBodyLine});
3318 }
3319
3320
3321 AUTO_TRACE_ADD("static variable {} body=[{}..{}]",name,root->bodyLine,root->endBodyLine);
3322 return; /* skip this member, because it is a
3323 * static variable definition (always?), which will be
3324 * found in a class scope as well, but then we know the
3325 * correct protection level, so only then it will be
3326 * inserted in the correct list!
3327 */
3328 }
3329
3331 if (type=="@")
3333 else if (type_s.startsWith("typedef "))
3334 mtype=MemberType::Typedef;
3335 else if (type_s.startsWith("friend "))
3336 mtype=MemberType::Friend;
3337 else if (root->mtype==MethodTypes::Property)
3339 else if (root->mtype==MethodTypes::Event)
3340 mtype=MemberType::Event;
3341 else if (type.find("sequence<") != -1)
3342 mtype=sliceOpt ? MemberType::Sequence : MemberType::Typedef;
3343 else if (type.find("dictionary<") != -1)
3345
3346 if (!root->relates.isEmpty()) // related variable
3347 {
3348 isRelated=TRUE;
3349 isMemberOf=(root->relatesType==RelatesType::MemberOf);
3350 if (getClass(root->relates)==nullptr && !scope.isEmpty())
3351 scope=mergeScopes(scope,root->relates);
3352 else
3353 scope=root->relates;
3354 }
3355
3356 cd=getClassMutable(scope);
3357 if (cd==nullptr && classScope!=scope) cd=getClassMutable(classScope);
3358 if (cd)
3359 {
3360 MemberDef *md=nullptr;
3361
3362 // if cd is an anonymous (=tag less) scope we insert the member
3363 // into a non-anonymous parent scope as well. This is needed to
3364 // be able to refer to it using \var or \fn
3365
3366 //int indentDepth=0;
3367 int si=scope.find('@');
3368 //int anonyScopes = 0;
3369 //bool added=FALSE;
3370
3371 bool inlineSimpleStructs = Config_getBool(INLINE_SIMPLE_STRUCTS);
3372 Relationship relationship = isMemberOf ? Relationship::Foreign :
3373 isRelated ? Relationship::Related :
3374 Relationship::Member ;
3375 if (si!=-1 && !inlineSimpleStructs) // anonymous scope or type
3376 {
3377 QCString pScope;
3378 ClassDefMutable *pcd=nullptr;
3379 pScope = scope.left(std::max(si-2,0)); // scope without tag less parts
3380 if (!pScope.isEmpty())
3381 pScope.prepend(annScopePrefix);
3382 else if (annScopePrefix.length()>2)
3383 pScope=annScopePrefix.left(annScopePrefix.length()-2);
3384 if (name.at(0)!='@')
3385 {
3386 if (!pScope.isEmpty() && (pcd=getClassMutable(pScope)))
3387 {
3388 AUTO_TRACE_ADD("Adding anonymous member to scope '{}'",pScope);
3389 md=addVariableToClass(root, // entry
3390 pcd, // class to add member to
3391 mtype, // member type
3392 type, // type value as string
3393 name, // member name
3394 args, // arguments as string
3395 TRUE, // from anonymous scope
3396 nullptr, // from anonymous member
3397 root->protection,
3398 relationship
3399 );
3400 //added=TRUE;
3401 }
3402 else // anonymous scope inside namespace or file => put variable in the global scope
3403 {
3404 if (mtype==MemberType::Variable)
3405 {
3406 AUTO_TRACE_ADD("Adding anonymous member to global scope '{}'");
3407 md=addVariableToFile(root,mtype,pScope,type,name,args,TRUE,nullptr);
3408 }
3409 //added=TRUE;
3410 }
3411 }
3412 }
3413
3414 addVariableToClass(root, // entry
3415 cd, // class to add member to
3416 mtype, // member type
3417 type, // type value as string
3418 name, // name of the member
3419 args, // arguments as string
3420 FALSE, // from anonymous scope
3421 md, // from anonymous member
3422 root->protection,
3423 relationship
3424 );
3425 }
3426 else if (!name.isEmpty()) // global variable
3427 {
3428 addVariableToFile(root,mtype,scope,type,name,args,FALSE,/*nullptr,*/nullptr);
3429 }
3430
3431}
3432
3433//----------------------------------------------------------------------
3434// Searches the Entry tree for typedef documentation sections.
3435// If found they are stored in their class or in the global list.
3436static void buildTypedefList(const Entry *root)
3437{
3438 //printf("buildVarList(%s)\n",qPrint(rootNav->name()));
3439 if (!root->name.isEmpty() &&
3440 root->section.isVariable() &&
3441 root->type.find("typedef ")!=-1 // its a typedef
3442 )
3443 {
3444 AUTO_TRACE();
3446 QCString scope;
3447 int index = computeQualifiedIndex(rname);
3448 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3449 // grouped members are stored with full scope
3450 {
3451 buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
3452 scope=rname.left(index);
3453 rname=rname.mid(index+2);
3454 }
3455 else
3456 {
3457 scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
3458 }
3461 MemberName *mn = Doxygen::functionNameLinkedMap->find(rname);
3462 bool found=false;
3463 if (mn) // symbol with the same name already found
3464 {
3465 for (auto &imd : *mn)
3466 {
3467 if (!imd->isTypedef())
3468 continue;
3469
3470 QCString rtype = root->type;
3471 rtype.stripPrefix("typedef ");
3472
3473 // merge the typedefs only if they're not both grouped, and both are
3474 // either part of the same class, part of the same namespace, or both
3475 // are global (i.e., neither in a class or a namespace)
3476 bool notBothGrouped = root->groups.empty() || imd->getGroupDef()==nullptr; // see example #100
3477 bool bothSameScope = (!cd && !nd) || (cd && imd->getClassDef() == cd) || (nd && imd->getNamespaceDef() == nd);
3478 //printf("imd->isTypedef()=%d imd->typeString()=%s root->type=%s\n",imd->isTypedef(),
3479 // qPrint(imd->typeString()),qPrint(root->type));
3480 if (notBothGrouped && bothSameScope && imd->typeString()==rtype)
3481 {
3482 MemberDefMutable *md = toMemberDefMutable(imd.get());
3483 if (md)
3484 {
3485 md->setDocumentation(root->doc,root->docFile,root->docLine);
3487 md->setDocsForDefinition(!root->proto);
3488 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3490 md->setRefItems(root->sli);
3491 md->setRequirementReferences(root->rqli);
3492 md->addQualifiers(root->qualifiers);
3493
3494 // merge ingroup specifiers
3495 if (md->getGroupDef()==nullptr && !root->groups.empty())
3496 {
3497 addMemberToGroups(root,md);
3498 }
3499 else if (md->getGroupDef()!=nullptr && root->groups.empty())
3500 {
3501 //printf("existing member is grouped, new member not\n");
3502 }
3503 else if (md->getGroupDef()!=nullptr && !root->groups.empty())
3504 {
3505 //printf("both members are grouped\n");
3506 }
3507 found=true;
3508 break;
3509 }
3510 }
3511 }
3512 }
3513 if (found)
3514 {
3515 AUTO_TRACE_ADD("typedef '{}' already found",rname);
3516 // mark the entry as processed, as we copied everything from it elsewhere
3517 // also, otherwise, due to containing `typedef` it may later get treated
3518 // as a function typedef in filterMemberDocumentation, which is incorrect
3519 root->markAsProcessed();
3520 }
3521 else
3522 {
3523 AUTO_TRACE_ADD("new typedef '{}'",rname);
3524 addVariable(root);
3525 }
3526
3527 }
3528 for (const auto &e : root->children())
3529 if (!e->section.isEnum())
3530 buildTypedefList(e.get());
3531}
3532
3533//----------------------------------------------------------------------
3534// Searches the Entry tree for sequence documentation sections.
3535// If found they are stored in the global list.
3536static void buildSequenceList(const Entry *root)
3537{
3538 if (!root->name.isEmpty() &&
3539 root->section.isVariable() &&
3540 root->type.find("sequence<")!=-1 // it's a sequence
3541 )
3542 {
3543 AUTO_TRACE();
3544 addVariable(root);
3545 }
3546 for (const auto &e : root->children())
3547 if (!e->section.isEnum())
3548 buildSequenceList(e.get());
3549}
3550
3551//----------------------------------------------------------------------
3552// Searches the Entry tree for dictionary documentation sections.
3553// If found they are stored in the global list.
3554static void buildDictionaryList(const Entry *root)
3555{
3556 if (!root->name.isEmpty() &&
3557 root->section.isVariable() &&
3558 root->type.find("dictionary<")!=-1 // it's a dictionary
3559 )
3560 {
3561 AUTO_TRACE();
3562 addVariable(root);
3563 }
3564 for (const auto &e : root->children())
3565 if (!e->section.isEnum())
3566 buildDictionaryList(e.get());
3567}
3568
3569//----------------------------------------------------------------------
3570// Searches the Entry tree for Variable documentation sections.
3571// If found they are stored in their class or in the global list.
3572
3573static void buildVarList(const Entry *root)
3574{
3575 //printf("buildVarList(%s) section=%08x\n",qPrint(rootNav->name()),rootNav->section());
3576 int isFuncPtr=-1;
3577 if (!root->name.isEmpty() &&
3578 (root->type.isEmpty() || g_compoundKeywords.find(root->type.str())==g_compoundKeywords.end()) &&
3579 (
3580 (root->section.isVariable() && // it's a variable
3581 root->type.find("typedef ")==-1 // and not a typedef
3582 ) ||
3583 (root->section.isFunction() && // or maybe a function pointer variable
3584 (isFuncPtr=findFunctionPtr(root->type.str(),root->lang))!=-1
3585 ) ||
3586 (root->section.isFunction() && // class variable initialized by constructor
3588 )
3589 )
3590 ) // documented variable
3591 {
3592 AUTO_TRACE();
3593 addVariable(root,isFuncPtr);
3594 }
3595 for (const auto &e : root->children())
3596 if (!e->section.isEnum())
3597 buildVarList(e.get());
3598}
3599
3600//----------------------------------------------------------------------
3601// Searches the Entry tree for Interface sections (UNO IDL only).
3602// If found they are stored in their service or in the global list.
3603//
3604
3606 const Entry *root,
3607 ClassDefMutable *cd,
3608 QCString const& rname)
3609{
3610 FileDef *fd = root->fileDef();
3611 enum MemberType type = root->section.isExportedInterface() ? MemberType::Interface : MemberType::Service;
3612 QCString fileName = root->fileName;
3613 if (fileName.isEmpty() && root->tagInfo())
3614 {
3615 fileName = root->tagInfo()->tagName;
3616 }
3617 auto md = createMemberDef(
3618 fileName, root->startLine, root->startColumn, root->type, rname,
3619 "", "", root->protection, root->virt, root->isStatic, Relationship::Member,
3620 type, ArgumentList(), root->argList, root->metaData);
3621 auto mmd = toMemberDefMutable(md.get());
3622 mmd->setTagInfo(root->tagInfo());
3623 mmd->setMemberClass(cd);
3624 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3625 mmd->setDocsForDefinition(false);
3626 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3627 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3628 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3629 mmd->setMemberSpecifiers(root->spec);
3630 mmd->setVhdlSpecifiers(root->vhdlSpec);
3631 mmd->setMemberGroupId(root->mGrpId);
3632 mmd->setTypeConstraints(root->typeConstr);
3633 mmd->setLanguage(root->lang);
3634 mmd->setBodyDef(fd);
3635 mmd->setFileDef(fd);
3636 mmd->addSectionsToDefinition(root->anchors);
3637 QCString const def = root->type + " " + rname;
3638 mmd->setDefinition(def);
3640 mmd->addQualifiers(root->qualifiers);
3641
3642 AUTO_TRACE("Interface member: fileName='{}' type='{}' name='{}' mtype='{}' prot={} virt={} state={} proto={} def='{}'",
3643 fileName,root->type,rname,type,root->protection,root->virt,root->isStatic,root->proto,def);
3644
3645 // add member to the class cd
3646 cd->insertMember(md.get());
3647 // also add the member as a "base" (to get nicer diagrams)
3648 // "optional" interface/service get Protected which turns into dashed line
3649 BaseInfo base(rname,
3650 root->spec.isOptional() ? Protection::Protected : Protection::Public, Specifier::Normal);
3651 TemplateNameMap templateNames;
3652 findClassRelation(root,cd,cd,&base,templateNames,DocumentedOnly,true) ||
3653 findClassRelation(root,cd,cd,&base,templateNames,Undocumented,true);
3654 // add file to list of used files
3655 cd->insertUsedFile(fd);
3656
3657 addMemberToGroups(root,md.get());
3659 root->markAsProcessed();
3660 mmd->setRefItems(root->sli);
3661 mmd->setRequirementReferences(root->rqli);
3662
3663 // add member to the global list of all members
3664 MemberName *mn = Doxygen::memberNameLinkedMap->add(rname);
3665 mn->push_back(std::move(md));
3666}
3667
3668static void buildInterfaceAndServiceList(const Entry *root)
3669{
3670 if (root->section.isExportedInterface() || root->section.isIncludedService())
3671 {
3672 AUTO_TRACE("Exported interface/included service: type='{}' scope='{}' name='{}' args='{}'"
3673 " relates='{}' relatesType='{}' file='{}' line={} bodyLine={} #tArgLists={}"
3674 " mGrpId={} spec={} proto={} docFile='{}'",
3675 root->type, root->parent()->name, root->name, root->args,
3676 root->relates, root->relatesType, root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(),
3677 root->mGrpId, root->spec, root->proto, root->docFile);
3678
3680
3681 if (!rname.isEmpty())
3682 {
3683 QCString scope = root->parent()->name;
3684 ClassDefMutable *cd = getClassMutable(scope);
3685 assert(cd);
3686 if (cd && ((ClassDef::Interface == cd->compoundType()) ||
3687 (ClassDef::Service == cd->compoundType()) ||
3689 {
3691 }
3692 else
3693 {
3694 assert(false); // was checked by scanner.l
3695 }
3696 }
3697 else if (rname.isEmpty())
3698 {
3699 warn(root->fileName,root->startLine,
3700 "Illegal member name found.");
3701 }
3702 }
3703 // can only have these in IDL anyway
3704 switch (root->lang)
3705 {
3706 case SrcLangExt::Unknown: // fall through (root node always is Unknown)
3707 case SrcLangExt::IDL:
3708 for (const auto &e : root->children()) buildInterfaceAndServiceList(e.get());
3709 break;
3710 default:
3711 return; // nothing to do here
3712 }
3713}
3714
3715
3716//----------------------------------------------------------------------
3717// Searches the Entry tree for Function sections.
3718// If found they are stored in their class or in the global list.
3719
3720static void addMethodToClass(const Entry *root,ClassDefMutable *cd,
3721 const QCString &rtype,const QCString &rname,const QCString &rargs,
3722 bool isFriend,
3723 Protection protection,bool stat,Specifier virt,TypeSpecifier spec,
3724 const QCString &relates
3725 )
3726{
3727 FileDef *fd=root->fileDef();
3728
3729 QCString type = rtype;
3730 QCString args = rargs;
3731
3733 name.stripPrefix("::");
3734
3736 if (isFriend) mtype=MemberType::Friend;
3737 else if (root->mtype==MethodTypes::Signal) mtype=MemberType::Signal;
3738 else if (root->mtype==MethodTypes::Slot) mtype=MemberType::Slot;
3739 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType::DCOP;
3740
3741 // strip redundant template specifier for constructors
3742 int i = -1;
3743 int j = -1;
3744 if ((fd==nullptr || fd->getLanguage()==SrcLangExt::Cpp) &&
3745 !name.startsWith("operator ") && // not operator
3746 (i=name.find('<'))!=-1 && // containing <
3747 (j=name.find('>'))!=-1 && // or >
3748 (j!=i+2 || name.at(i+1)!='=') // but not the C++20 spaceship operator <=>
3749 )
3750 {
3751 name=name.left(i);
3752 }
3753
3754 QCString fileName = root->fileName;
3755 if (fileName.isEmpty() && root->tagInfo())
3756 {
3757 fileName = root->tagInfo()->tagName;
3758 }
3759
3760 //printf("root->name='%s; args='%s' root->argList='%s'\n",
3761 // qPrint(root->name),qPrint(args),qPrint(argListToString(root->argList))
3762 // );
3763
3764 // adding class member
3765 Relationship relationship = relates.isEmpty() ? Relationship::Member :
3766 root->relatesType==RelatesType::MemberOf ? Relationship::Foreign :
3767 Relationship::Related ;
3768 auto md = createMemberDef(
3769 fileName,root->startLine,root->startColumn,
3770 type,name,args,root->exception,
3771 protection,virt,
3772 stat && root->relatesType!=RelatesType::MemberOf,
3773 relationship,
3774 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3775 root->argList, root->metaData);
3776 auto mmd = toMemberDefMutable(md.get());
3777 mmd->setTagInfo(root->tagInfo());
3778 mmd->setMemberClass(cd);
3779 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3780 mmd->setDocsForDefinition(!root->proto);
3781 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3782 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3783 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3784 mmd->setMemberSpecifiers(spec);
3785 mmd->setVhdlSpecifiers(root->vhdlSpec);
3786 mmd->setMemberGroupId(root->mGrpId);
3787 mmd->setTypeConstraints(root->typeConstr);
3788 mmd->setLanguage(root->lang);
3789 mmd->setRequiresClause(root->req);
3790 mmd->setId(root->id);
3791 mmd->setBodyDef(fd);
3792 mmd->setFileDef(fd);
3793 mmd->addSectionsToDefinition(root->anchors);
3794 QCString def;
3796 SrcLangExt lang = cd->getLanguage();
3797 QCString scopeSeparator=getLanguageSpecificSeparator(lang);
3798 if (scopeSeparator!="::")
3799 {
3800 qualScope = substitute(qualScope,"::",scopeSeparator);
3801 }
3802 if (lang==SrcLangExt::PHP)
3803 {
3804 // for PHP we use Class::method and Namespace\method
3805 scopeSeparator="::";
3806 }
3807 if (!relates.isEmpty() || isFriend || Config_getBool(HIDE_SCOPE_NAMES))
3808 {
3809 if (!type.isEmpty())
3810 {
3811 def=type+" "+name; //+optArgs;
3812 }
3813 else
3814 {
3815 def=name; //+optArgs;
3816 }
3817 }
3818 else
3819 {
3820 if (!type.isEmpty())
3821 {
3822 def=type+" "+qualScope+scopeSeparator+name; //+optArgs;
3823 }
3824 else
3825 {
3826 def=qualScope+scopeSeparator+name; //+optArgs;
3827 }
3828 }
3829 def.stripPrefix("friend ");
3830 mmd->setDefinition(def);
3832 mmd->addQualifiers(root->qualifiers);
3833
3834 AUTO_TRACE("function member: type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
3835 type, qualScope, rname, args, root->proto, def);
3836
3837 // add member to the class cd
3838 cd->insertMember(md.get());
3839 // add file to list of used files
3840 cd->insertUsedFile(fd);
3841
3842 addMemberToGroups(root,md.get());
3844 root->markAsProcessed();
3845 mmd->setRefItems(root->sli);
3846 mmd->setRequirementReferences(root->rqli);
3847
3848 // add member to the global list of all members
3849 //printf("Adding member=%s class=%s\n",qPrint(md->name()),qPrint(cd->name()));
3851 mn->push_back(std::move(md));
3852}
3853
3854//------------------------------------------------------------------------------------------
3855
3856static void addGlobalFunction(const Entry *root,const QCString &rname,const QCString &sc)
3857{
3858 QCString scope = sc;
3859
3860 // new global function
3862 auto md = createMemberDef(
3863 root->fileName,root->startLine,root->startColumn,
3864 root->type,name,root->args,root->exception,
3865 root->protection,root->virt,root->isStatic,Relationship::Member,
3867 !root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3868 root->argList,root->metaData);
3869 auto mmd = toMemberDefMutable(md.get());
3870 mmd->setTagInfo(root->tagInfo());
3871 mmd->setLanguage(root->lang);
3872 mmd->setId(root->id);
3873 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3874 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3875 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3876 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
3877 mmd->setDocsForDefinition(!root->proto);
3878 mmd->setTypeConstraints(root->typeConstr);
3879 //md->setBody(root->body);
3880 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3881 FileDef *fd=root->fileDef();
3882 mmd->setBodyDef(fd);
3883 mmd->addSectionsToDefinition(root->anchors);
3884 mmd->setMemberSpecifiers(root->spec);
3885 mmd->setVhdlSpecifiers(root->vhdlSpec);
3886 mmd->setMemberGroupId(root->mGrpId);
3887 mmd->setRequiresClause(root->req);
3888 mmd->setExplicitExternal(root->explicitExternal,root->fileName,root->startLine,root->startColumn);
3889
3890 NamespaceDefMutable *nd = nullptr;
3891 // see if the function is inside a namespace that was not part of
3892 // the name already (in that case nd should be non-zero already)
3893 if (root->parent()->section.isNamespace())
3894 {
3895 //QCString nscope=removeAnonymousScopes(root->parent()->name);
3896 QCString nscope=root->parent()->name;
3897 if (!nscope.isEmpty())
3898 {
3899 nd = getResolvedNamespaceMutable(nscope);
3900 }
3901 }
3902 else if (root->parent()->section.isGroupDoc() && !scope.isEmpty())
3903 {
3905 }
3906
3907 if (!scope.isEmpty())
3908 {
3910 if (sep!="::")
3911 {
3912 scope = substitute(scope,"::",sep);
3913 }
3914 scope+=sep;
3915 }
3916
3917 if (Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python) scope = "";
3918 QCString def;
3919 //QCString optArgs = root->argList.empty() ? QCString() : root->args;
3920 if (!root->type.isEmpty())
3921 {
3922 def=root->type+" "+scope+name; //+optArgs;
3923 }
3924 else
3925 {
3926 def=scope+name; //+optArgs;
3927 }
3928 AUTO_TRACE("new non-member function type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
3929 root->type,scope,rname,root->args,root->proto,def);
3930 mmd->setDefinition(def);
3932 mmd->addQualifiers(root->qualifiers);
3933
3934 mmd->setRefItems(root->sli);
3935 mmd->setRequirementReferences(root->rqli);
3936 if (nd && !nd->name().isEmpty() && nd->name().at(0)!='@')
3937 {
3938 // add member to namespace
3939 mmd->setNamespace(nd);
3940 nd->insertMember(md.get());
3941 }
3942 if (fd)
3943 {
3944 // add member to the file (we do this even if we have already
3945 // inserted it into the namespace)
3946 mmd->setFileDef(fd);
3947 fd->insertMember(md.get());
3948 }
3949
3950 addMemberToGroups(root,md.get());
3952 if (root->relatesType == RelatesType::Simple) // if this is a relatesalso command,
3953 // allow find Member to pick it up
3954 {
3955 root->markAsProcessed(); // Otherwise we have finished with this entry.
3956 }
3957
3958 // add member to the list of file members
3960 mn->push_back(std::move(md));
3961}
3962
3963//------------------------------------------------------------------------------------------
3964
3965static void buildFunctionList(const Entry *root)
3966{
3967 if (root->section.isFunction())
3968 {
3969 AUTO_TRACE("member function: type='{}' scope='{}' name='{}' args='{}' relates='{}' relatesType='{}'"
3970 " file='{}' line={} bodyLine={} #tArgLists={} mGrpId={}"
3971 " spec={} proto={} docFile='{}'",
3972 root->type, root->parent()->name, root->name, root->args, root->relates, root->relatesType,
3973 root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(), root->mGrpId,
3974 root->spec, root->proto, root->docFile);
3975
3976 bool isFriend=root->type=="friend" || root->type.find("friend ")!=-1;
3978 //printf("rname=%s\n",qPrint(rname));
3979
3980 QCString scope;
3981 int index = computeQualifiedIndex(rname);
3982 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3983 // grouped members are stored with full scope
3984 {
3985 buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
3986 scope=rname.left(index);
3987 rname=rname.mid(index+2);
3988 }
3989 else
3990 {
3991 scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
3992 }
3993 if (!rname.isEmpty() && scope.find('@')==-1)
3994 {
3995 // check if this function's parent is a class
3996 if (root->lang==SrcLangExt::CSharp)
3997 {
3998 scope=mangleCSharpGenericName(scope);
3999 }
4000 else
4001 {
4003 }
4004
4005 FileDef *rfd=root->fileDef();
4006
4007 int memIndex=rname.findRev("::");
4008
4010 if (cd && scope+"::"==rname.left(scope.length()+2)) // found A::f inside A
4011 {
4012 // strip scope from name
4013 rname=rname.right(rname.length()-root->parent()->name.length()-2);
4014 }
4015
4016 bool isMember=FALSE;
4017 if (memIndex!=-1)
4018 {
4019 int ts=rname.find('<');
4020 int te=rname.find('>');
4021 if (memIndex>0 && (ts==-1 || te==-1))
4022 {
4023 // note: the following code was replaced by inMember=TRUE to deal with a
4024 // function rname='X::foo' of class X inside a namespace also called X...
4025 // bug id 548175
4026 //nd = Doxygen::namespaceLinkedMap->find(rname.left(memIndex));
4027 //isMember = nd==nullptr;
4028 //if (nd)
4029 //{
4030 // // strip namespace scope from name
4031 // scope=rname.left(memIndex);
4032 // rname=rname.right(rname.length()-memIndex-2);
4033 //}
4034 isMember = TRUE;
4035 }
4036 else
4037 {
4038 isMember=memIndex<ts || memIndex>te;
4039 }
4040 }
4041
4042 if (!root->parent()->name.isEmpty() && root->parent()->section.isCompound() && cd)
4043 {
4044 AUTO_TRACE_ADD("member '{}' of class '{}'", rname,cd->name());
4045 addMethodToClass(root,cd,root->type,rname,root->args,isFriend,
4046 root->protection,root->isStatic,root->virt,root->spec,root->relates);
4047 }
4048 else if (root->parent()->section.isObjcImpl() && cd)
4049 {
4050 const MemberDef *md = cd->getMemberByName(rname);
4051 if (md)
4052 {
4053 MemberDefMutable *mdm = toMemberDefMutable(const_cast<MemberDef*>(md));
4054 if (mdm)
4055 {
4056 mdm->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
4057 mdm->setBodyDef(root->fileDef());
4058 }
4059 }
4060 }
4061 else if (!root->parent()->section.isCompound() && !root->parent()->section.isObjcImpl() &&
4062 !isMember &&
4063 (root->relates.isEmpty() || root->relatesType==RelatesType::Duplicate) &&
4064 !root->type.startsWith("extern ") && !root->type.startsWith("typedef ")
4065 )
4066 // no member => unrelated function
4067 {
4068 /* check the uniqueness of the function name in the file.
4069 * A file could contain a function prototype and a function definition
4070 * or even multiple function prototypes.
4071 */
4072 bool found=FALSE;
4073 MemberDef *md_found=nullptr;
4074 MemberName *mn = Doxygen::functionNameLinkedMap->find(rname);
4075 if (mn)
4076 {
4077 AUTO_TRACE_ADD("function '{}' already found",rname);
4078 for (const auto &imd : *mn)
4079 {
4080 MemberDefMutable *md = toMemberDefMutable(imd.get());
4081 if (md)
4082 {
4083 const NamespaceDef *mnd = md->getNamespaceDef();
4084 NamespaceDef *rnd = nullptr;
4085 //printf("root namespace=%s\n",qPrint(rootNav->parent()->name()));
4086 QCString fullScope = scope;
4087 QCString parentScope = root->parent()->name;
4088 if (!parentScope.isEmpty() && !leftScopeMatch(parentScope,scope))
4089 {
4090 if (!scope.isEmpty()) fullScope.prepend("::");
4091 fullScope.prepend(parentScope);
4092 }
4093 //printf("fullScope=%s\n",qPrint(fullScope));
4094 rnd = getResolvedNamespace(fullScope);
4095 const FileDef *mfd = md->getFileDef();
4096 QCString nsName,rnsName;
4097 if (mnd) nsName = mnd->name();
4098 if (rnd) rnsName = rnd->name();
4099 //printf("matching arguments for %s%s %s%s\n",
4100 // qPrint(md->name()),md->argsString(),qPrint(rname),qPrint(argListToString(root->argList)));
4101 const ArgumentList &mdAl = md->argumentList();
4102 const ArgumentList &mdTempl = md->templateArguments();
4103
4104 // in case of template functions, we need to check if the
4105 // functions have the same number of template parameters
4106 bool sameTemplateArgs = TRUE;
4107 bool matchingReturnTypes = TRUE;
4108 bool sameRequiresClause = TRUE;
4109 if (!mdTempl.empty() && !root->tArgLists.empty())
4110 {
4111 sameTemplateArgs = matchTemplateArguments(mdTempl,root->tArgLists.back());
4112 if (md->typeString()!=removeRedundantWhiteSpace(root->type))
4113 {
4114 matchingReturnTypes = FALSE;
4115 }
4116 if (md->requiresClause()!=root->req)
4117 {
4118 sameRequiresClause = FALSE;
4119 }
4120 }
4121 else if (!mdTempl.empty() || !root->tArgLists.empty())
4122 { // if one has template parameters and the other doesn't then that also counts as a
4123 // difference
4124 sameTemplateArgs = FALSE;
4125 }
4126
4127 bool staticsInDifferentFiles =
4128 root->isStatic && md->isStatic() && root->fileName!=md->getDefFileName();
4129
4130 if (sameTemplateArgs &&
4131 matchingReturnTypes &&
4132 sameRequiresClause &&
4133 !staticsInDifferentFiles &&
4134 matchArguments2(md->getOuterScope(),mfd,md->typeString(),&mdAl,
4135 rnd ? rnd : Doxygen::globalScope,rfd,root->type,&root->argList,
4136 FALSE,root->lang)
4137 )
4138 {
4139 GroupDef *gd=nullptr;
4140 if (!root->groups.empty() && !root->groups.front().groupname.isEmpty())
4141 {
4142 gd = Doxygen::groupLinkedMap->find(root->groups.front().groupname);
4143 }
4144 //printf("match!\n");
4145 //printf("mnd=%p rnd=%p nsName=%s rnsName=%s\n",mnd,rnd,qPrint(nsName),qPrint(rnsName));
4146 // see if we need to create a new member
4147 found=(mnd && rnd && nsName==rnsName) || // members are in the same namespace
4148 ((mnd==nullptr && rnd==nullptr && mfd!=nullptr && // no external reference and
4149 mfd->absFilePath()==root->fileName // prototype in the same file
4150 )
4151 );
4152 // otherwise, allow a duplicate global member with the same argument list
4153 if (!found && gd && gd==md->getGroupDef() && nsName==rnsName)
4154 {
4155 // member is already in the group, so we don't want to add it again.
4156 found=TRUE;
4157 }
4158
4159 AUTO_TRACE_ADD("combining function with prototype found={} in namespace '{}'",found,nsName);
4160
4161 if (found)
4162 {
4163 // merge argument lists
4164 ArgumentList mergedArgList = root->argList;
4165 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.isEmpty());
4166 // merge documentation
4167 if (md->documentation().isEmpty() && !root->doc.isEmpty())
4168 {
4169 if (root->proto)
4170 {
4172 }
4173 else
4174 {
4176 }
4177 }
4178
4179 md->setDocumentation(root->doc,root->docFile,root->docLine);
4181 md->setDocsForDefinition(!root->proto);
4182 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
4183 {
4184 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
4185 md->setBodyDef(rfd);
4186 }
4187
4188 if (md->briefDescription().isEmpty() && !root->brief.isEmpty())
4189 {
4190 md->setArgsString(root->args);
4191 }
4192 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
4193
4195
4197 md->addQualifiers(root->qualifiers);
4198
4199 // merge ingroup specifiers
4200 if (md->getGroupDef()==nullptr && !root->groups.empty())
4201 {
4202 addMemberToGroups(root,md);
4203 }
4204 else if (md->getGroupDef()!=nullptr && root->groups.empty())
4205 {
4206 //printf("existing member is grouped, new member not\n");
4207 }
4208 else if (md->getGroupDef()!=nullptr && !root->groups.empty())
4209 {
4210 //printf("both members are grouped\n");
4211 }
4213
4214 // if md is a declaration and root is the corresponding
4215 // definition, then turn md into a definition.
4216 if (md->isPrototype() && !root->proto)
4217 {
4218 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
4219 md->setPrototype(FALSE,root->fileName,root->startLine,root->startColumn);
4220 }
4221 // if md is already the definition, then add the declaration info
4222 else if (!md->isPrototype() && root->proto)
4223 {
4224 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
4225 }
4226 }
4227 }
4228 }
4229 if (found)
4230 {
4231 md_found = md;
4232 break;
4233 }
4234 }
4235 }
4236 if (!found) /* global function is unique with respect to the file */
4237 {
4238 addGlobalFunction(root,rname,scope);
4239 }
4240 else
4241 {
4242 FileDef *fd=root->fileDef();
4243 if (fd)
4244 {
4245 // add member to the file (we do this even if we have already
4246 // inserted it into the namespace)
4247 fd->insertMember(md_found);
4248 }
4249 }
4250
4251 AUTO_TRACE_ADD("unrelated function type='{}' name='{}' args='{}'",root->type,rname,root->args);
4252 }
4253 else
4254 {
4255 AUTO_TRACE_ADD("function '{}' is not processed",rname);
4256 }
4257 }
4258 else if (rname.isEmpty())
4259 {
4260 warn(root->fileName,root->startLine,
4261 "Illegal member name found."
4262 );
4263 }
4264 }
4265 for (const auto &e : root->children()) buildFunctionList(e.get());
4266}
4267
4268//----------------------------------------------------------------------
4269
4270static void findFriends()
4271{
4272 AUTO_TRACE();
4273 for (const auto &fn : *Doxygen::functionNameLinkedMap) // for each global function name
4274 {
4275 MemberName *mn = Doxygen::memberNameLinkedMap->find(fn->memberName());
4276 if (mn)
4277 { // there are members with the same name
4278 // for each function with that name
4279 for (const auto &ifmd : *fn)
4280 {
4281 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
4282 // for each member with that name
4283 for (const auto &immd : *mn)
4284 {
4285 MemberDefMutable *mmd = toMemberDefMutable(immd.get());
4286 //printf("Checking for matching arguments
4287 // mmd->isRelated()=%d mmd->isFriend()=%d mmd->isFunction()=%d\n",
4288 // mmd->isRelated(),mmd->isFriend(),mmd->isFunction());
4289 if (fmd && mmd &&
4290 (mmd->isFriend() || (mmd->isRelated() && mmd->isFunction())) &&
4291 matchArguments2(mmd->getOuterScope(), mmd->getFileDef(), mmd->typeString(), &mmd->argumentList(),
4292 fmd->getOuterScope(), fmd->getFileDef(), fmd->typeString(), &fmd->argumentList(),
4293 TRUE,mmd->getLanguage()
4294 )
4295
4296 ) // if the member is related and the arguments match then the
4297 // function is actually a friend.
4298 {
4299 AUTO_TRACE_ADD("Merging related global and member '{}' isFriend={} isRelated={} isFunction={}",
4300 mmd->name(),mmd->isFriend(),mmd->isRelated(),mmd->isFunction());
4301 const ArgumentList &mmdAl = mmd->argumentList();
4302 const ArgumentList &fmdAl = fmd->argumentList();
4303 mergeArguments(const_cast<ArgumentList&>(fmdAl),const_cast<ArgumentList&>(mmdAl));
4304
4305 // reset argument lists to add missing default parameters
4306 QCString mmdAlStr = argListToString(mmdAl);
4307 QCString fmdAlStr = argListToString(fmdAl);
4308 mmd->setArgsString(mmdAlStr);
4309 fmd->setArgsString(fmdAlStr);
4310 mmd->moveDeclArgumentList(std::make_unique<ArgumentList>(mmdAl));
4311 fmd->moveDeclArgumentList(std::make_unique<ArgumentList>(fmdAl));
4312 AUTO_TRACE_ADD("friend args='{}' member args='{}'",argListToString(fmd->argumentList()),argListToString(mmd->argumentList()));
4313
4314 if (!fmd->documentation().isEmpty())
4315 {
4316 mmd->setDocumentation(fmd->documentation(),fmd->docFile(),fmd->docLine());
4317 }
4318 else if (!mmd->documentation().isEmpty())
4319 {
4320 fmd->setDocumentation(mmd->documentation(),mmd->docFile(),mmd->docLine());
4321 }
4322 if (mmd->briefDescription().isEmpty() && !fmd->briefDescription().isEmpty())
4323 {
4324 mmd->setBriefDescription(fmd->briefDescription(),fmd->briefFile(),fmd->briefLine());
4325 }
4326 else if (!mmd->briefDescription().isEmpty() && !fmd->briefDescription().isEmpty())
4327 {
4328 fmd->setBriefDescription(mmd->briefDescription(),mmd->briefFile(),mmd->briefLine());
4329 }
4330 if (!fmd->inbodyDocumentation().isEmpty())
4331 {
4333 }
4334 else if (!mmd->inbodyDocumentation().isEmpty())
4335 {
4337 }
4338 //printf("body mmd %d fmd %d\n",mmd->getStartBodyLine(),fmd->getStartBodyLine());
4339 if (mmd->getStartBodyLine()==-1 && fmd->getStartBodyLine()!=-1)
4340 {
4341 mmd->setBodySegment(fmd->getDefLine(),fmd->getStartBodyLine(),fmd->getEndBodyLine());
4342 mmd->setBodyDef(fmd->getBodyDef());
4343 //mmd->setBodyMember(fmd);
4344 }
4345 else if (mmd->getStartBodyLine()!=-1 && fmd->getStartBodyLine()==-1)
4346 {
4347 fmd->setBodySegment(mmd->getDefLine(),mmd->getStartBodyLine(),mmd->getEndBodyLine());
4348 fmd->setBodyDef(mmd->getBodyDef());
4349 //fmd->setBodyMember(mmd);
4350 }
4352
4354
4355 mmd->addQualifiers(fmd->getQualifiers());
4356 fmd->addQualifiers(mmd->getQualifiers());
4357
4358 }
4359 }
4360 }
4361 }
4362 }
4363}
4364
4365//----------------------------------------------------------------------
4366
4368{
4369 AUTO_TRACE();
4370
4371 // find matching function declaration and definitions.
4372 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4373 {
4374 //printf("memberName=%s count=%zu\n",qPrint(mn->memberName()),mn->size());
4375 /* find a matching function declaration and definition for this function */
4376 for (const auto &imdec : *mn)
4377 {
4378 MemberDefMutable *mdec = toMemberDefMutable(imdec.get());
4379 if (mdec &&
4380 (mdec->isPrototype() ||
4381 (mdec->isVariable() && mdec->isExternal())
4382 ))
4383 {
4384 for (const auto &imdef : *mn)
4385 {
4386 MemberDefMutable *mdef = toMemberDefMutable(imdef.get());
4387 if (mdef && mdec!=mdef &&
4388 mdec->getNamespaceDef()==mdef->getNamespaceDef())
4389 {
4391 }
4392 }
4393 }
4394 }
4395 }
4396}
4397
4398//----------------------------------------------------------------------
4399
4401{
4402 AUTO_TRACE();
4403 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4404 {
4405 MemberDefMutable *mdef=nullptr,*mdec=nullptr;
4406 /* find a matching function declaration and definition for this function */
4407 for (const auto &imd : *mn)
4408 {
4409 MemberDefMutable *md = toMemberDefMutable(imd.get());
4410 if (md)
4411 {
4412 if (md->isPrototype())
4413 mdec=md;
4414 else if (md->isVariable() && md->isExternal())
4415 mdec=md;
4416
4417 if (md->isFunction() && !md->isStatic() && !md->isPrototype())
4418 mdef=md;
4419 else if (md->isVariable() && !md->isExternal() && !md->isStatic())
4420 mdef=md;
4421 }
4422
4423 if (mdef && mdec) break;
4424 }
4425 if (mdef && mdec)
4426 {
4427 const ArgumentList &mdefAl = mdef->argumentList();
4428 const ArgumentList &mdecAl = mdec->argumentList();
4429 if (
4430 matchArguments2(mdef->getOuterScope(),mdef->getFileDef(),mdef->typeString(),const_cast<ArgumentList*>(&mdefAl),
4431 mdec->getOuterScope(),mdec->getFileDef(),mdec->typeString(),const_cast<ArgumentList*>(&mdecAl),
4432 TRUE,mdef->getLanguage()
4433 )
4434 ) /* match found */
4435 {
4436 AUTO_TRACE_ADD("merging references for mdec={} mdef={}",mdec->name(),mdef->name());
4437 mdef->mergeReferences(mdec);
4438 mdec->mergeReferences(mdef);
4439 mdef->mergeReferencedBy(mdec);
4440 mdec->mergeReferencedBy(mdef);
4441 }
4442 }
4443 }
4444}
4445
4446//----------------------------------------------------------------------
4447
4449{
4450 AUTO_TRACE();
4451 // find match between function declaration and definition for
4452 // related functions
4453 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4454 {
4455 /* find a matching function declaration and definition for this function */
4456 // for each global function
4457 for (const auto &imd : *mn)
4458 {
4459 MemberDefMutable *md = toMemberDefMutable(imd.get());
4460 if (md)
4461 {
4462 //printf(" Function '%s'\n",qPrint(md->name()));
4463 MemberName *rmn = Doxygen::memberNameLinkedMap->find(md->name());
4464 if (rmn) // check if there is a member with the same name
4465 {
4466 //printf(" Member name found\n");
4467 // for each member with the same name
4468 for (const auto &irmd : *rmn)
4469 {
4470 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
4471 //printf(" Member found: related='%d'\n",rmd->isRelated());
4472 if (rmd &&
4473 (rmd->isRelated() || rmd->isForeign()) && // related function
4474 matchArguments2( md->getOuterScope(), md->getFileDef(), md->typeString(), &md->argumentList(),
4475 rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmd->argumentList(),
4476 TRUE,md->getLanguage()
4477 )
4478 )
4479 {
4480 AUTO_TRACE_ADD("Found related member '{}'",md->name());
4481 if (rmd->relatedAlso())
4482 md->setRelatedAlso(rmd->relatedAlso());
4483 else if (rmd->isForeign())
4484 md->makeForeign();
4485 else
4486 md->makeRelated();
4487 }
4488 }
4489 }
4490 }
4491 }
4492 }
4493}
4494
4495//----------------------------------------------------------------------
4496
4498{
4499 AUTO_TRACE();
4500 for (const auto &[qualifiedName,bodyInfo] : Doxygen::staticInitMap)
4501 {
4502 size_t i=qualifiedName.rfind("::");
4503 if (i!=std::string::npos)
4504 {
4505 QCString scope = qualifiedName.substr(0,i);
4506 QCString name = qualifiedName.substr(i+2);
4507 MemberName *mn = Doxygen::memberNameLinkedMap->find(name);
4508 if (mn)
4509 {
4510 for (const auto &imd : *mn)
4511 {
4512 MemberDefMutable *md = toMemberDefMutable(imd.get());
4513 if (md && md->qualifiedName().str()==qualifiedName && md->isVariable())
4514 {
4515 AUTO_TRACE_ADD("found static member {} body [{}..{}]\n",
4516 md->qualifiedName(),bodyInfo.startLine,bodyInfo.endLine);
4517 md->setBodySegment(bodyInfo.defLine,
4518 bodyInfo.startLine,
4519 bodyInfo.endLine);
4520 }
4521 }
4522 }
4523 }
4524 }
4525}
4526
4527//----------------------------------------------------------------------
4528
4529/*! make a dictionary of all template arguments of class cd
4530 * that are part of the base class name.
4531 * Example: A template class A with template arguments <R,S,T>
4532 * that inherits from B<T,T,S> will have T and S in the dictionary.
4533 */
4534static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments,const std::string &name)
4535{
4536 std::map<std::string,int> templateNames;
4537 int count=0;
4538 for (const Argument &arg : templateArguments)
4539 {
4540 static const reg::Ex re(R"(\a[\w:]*)");
4541 reg::Iterator it(name,re);
4543 for (; it!=end ; ++it)
4544 {
4545 const auto &match = *it;
4546 std::string n = match.str();
4547 if (n==arg.name.str())
4548 {
4549 if (templateNames.find(n)==templateNames.end())
4550 {
4551 templateNames.emplace(n,count);
4552 }
4553 }
4554 }
4555 }
4556 return templateNames;
4557}
4558
4559/*! Searches a class from within \a context and \a cd and returns its
4560 * definition if found (otherwise nullptr is returned).
4561 */
4563{
4564 ClassDef *result=nullptr;
4565 if (cd==nullptr)
4566 {
4567 return result;
4568 }
4569 FileDef *fd=cd->getFileDef();
4570 SymbolResolver resolver(fd);
4571 if (context && cd!=context)
4572 {
4573 result = const_cast<ClassDef*>(resolver.resolveClass(context,name,true,true));
4574 }
4575 //printf("1. result=%p\n",result);
4576 if (result==nullptr)
4577 {
4578 result = const_cast<ClassDef*>(resolver.resolveClass(cd,name,true,true));
4579 }
4580 //printf("2. result=%p\n",result);
4581 if (result==nullptr) // try direct class, needed for namespaced classes imported via tag files (see bug624095)
4582 {
4583 result = getClass(name);
4584 }
4585 //printf("3. result=%p\n",result);
4586 //printf("** Trying to find %s within context %s class %s result=%s lookup=%p\n",
4587 // qPrint(name),
4588 // context ? qPrint(context->name()) : "<none>",
4589 // cd ? qPrint(cd->name()) : "<none>",
4590 // result ? qPrint(result->name()) : "<none>",
4591 // Doxygen::classLinkedMap->find(name)
4592 // );
4593 return result;
4594}
4595
4596
4597static void findUsedClassesForClass(const Entry *root,
4598 Definition *context,
4599 ClassDefMutable *masterCd,
4600 ClassDefMutable *instanceCd,
4601 bool isArtificial,
4602 const ArgumentList *actualArgs = nullptr,
4603 const TemplateNameMap &templateNames = TemplateNameMap()
4604 )
4605{
4606 AUTO_TRACE();
4607 const ArgumentList &formalArgs = masterCd->templateArguments();
4608 for (auto &mni : masterCd->memberNameInfoLinkedMap())
4609 {
4610 for (auto &mi : *mni)
4611 {
4612 const MemberDef *md=mi->memberDef();
4613 if (md->isVariable() || md->isObjCProperty()) // for each member variable in this class
4614 {
4615 AUTO_TRACE_ADD("Found variable '{}' in class '{}'",md->name(),masterCd->name());
4616 QCString type = normalizeNonTemplateArgumentsInString(md->typeString(),masterCd,formalArgs);
4617 QCString typedefValue = md->getLanguage()==SrcLangExt::Java ? type : resolveTypeDef(masterCd,type);
4618 if (!typedefValue.isEmpty())
4619 {
4620 type = typedefValue;
4621 }
4622 int pos=0;
4623 QCString usedClassName;
4624 QCString templSpec;
4625 bool found=FALSE;
4626 // the type can contain template variables, replace them if present
4627 type = substituteTemplateArgumentsInString(type,formalArgs,actualArgs);
4628
4629 //printf(" template substitution gives=%s\n",qPrint(type));
4630 while (!found && extractClassNameFromType(type,pos,usedClassName,templSpec,root->lang)!=-1)
4631 {
4632 // find the type (if any) that matches usedClassName
4633 SymbolResolver resolver(masterCd->getFileDef());
4634 const ClassDefMutable *typeCd = resolver.resolveClassMutable(masterCd,usedClassName,false,true);
4635 //printf("====> usedClassName=%s -> typeCd=%s\n",
4636 // qPrint(usedClassName),typeCd?qPrint(typeCd->name()):"<none>");
4637 if (typeCd)
4638 {
4639 usedClassName = typeCd->name();
4640 }
4641
4642 // replace any namespace aliases
4643 replaceNamespaceAliases(usedClassName);
4644 // add any template arguments to the class
4645 QCString usedName = removeRedundantWhiteSpace(usedClassName+templSpec);
4646 //printf(" usedName=%s usedClassName=%s templSpec=%s\n",qPrint(usedName),qPrint(usedClassName),qPrint(templSpec));
4647
4648 TemplateNameMap formTemplateNames;
4649 if (templateNames.empty())
4650 {
4651 formTemplateNames = getTemplateArgumentsInName(formalArgs,usedName.str());
4652 }
4653 BaseInfo bi(usedName,Protection::Public,Specifier::Normal);
4654 findClassRelation(root,context,instanceCd,&bi,formTemplateNames,TemplateInstances,isArtificial);
4655
4656 for (const Argument &arg : masterCd->templateArguments())
4657 {
4658 if (arg.name==usedName) // type is a template argument
4659 {
4660 ClassDef *usedCd = Doxygen::hiddenClassLinkedMap->find(usedName);
4661 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4662 if (usedCd==nullptr)
4663 {
4664 usedCdm = toClassDefMutable(
4665 Doxygen::hiddenClassLinkedMap->add(usedName,
4667 masterCd->getDefFileName(),masterCd->getDefLine(),
4668 masterCd->getDefColumn(),
4669 usedName,
4670 ClassDef::Class)));
4671 if (usedCdm)
4672 {
4673 //printf("making %s a template argument!!!\n",qPrint(usedCd->name()));
4674 usedCdm->makeTemplateArgument();
4675 usedCdm->setUsedOnly(TRUE);
4676 usedCdm->setLanguage(masterCd->getLanguage());
4677 usedCd = usedCdm;
4678 }
4679 }
4680 if (usedCd)
4681 {
4682 found=TRUE;
4683 AUTO_TRACE_ADD("case 1: adding used class '{}'", usedCd->name());
4684 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4685 if (usedCdm)
4686 {
4687 if (isArtificial) usedCdm->setArtificial(TRUE);
4688 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4689 }
4690 }
4691 }
4692 }
4693
4694 if (!found)
4695 {
4696 ClassDef *usedCd=findClassWithinClassContext(context,masterCd,usedName);
4697 //printf("Looking for used class %s: result=%s master=%s\n",
4698 // qPrint(usedName),usedCd?qPrint(usedCd->name()):"<none>",masterCd?qPrint(masterCd->name()):"<none>");
4699
4700 if (usedCd)
4701 {
4702 found=TRUE;
4703 AUTO_TRACE_ADD("case 2: adding used class '{}'", usedCd->name());
4704 instanceCd->addUsedClass(usedCd,md->name(),md->protection()); // class exists
4705 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4706 if (usedCdm)
4707 {
4708 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4709 }
4710 }
4711 }
4712 }
4713 if (!found && !type.isEmpty()) // used class is not documented in any scope
4714 {
4715 ClassDef *usedCd = Doxygen::hiddenClassLinkedMap->find(type);
4716 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4717 if (usedCd==nullptr && !Config_getBool(HIDE_UNDOC_RELATIONS))
4718 {
4719 if (type.endsWith("(*") || type.endsWith("(^")) // type is a function pointer
4720 {
4721 type+=md->argsString();
4722 }
4723 AUTO_TRACE_ADD("New undocumented used class '{}'", type);
4724 usedCdm = toClassDefMutable(
4727 masterCd->getDefFileName(),masterCd->getDefLine(),
4728 masterCd->getDefColumn(),
4729 type,ClassDef::Class)));
4730 if (usedCdm)
4731 {
4732 usedCdm->setUsedOnly(TRUE);
4733 usedCdm->setLanguage(masterCd->getLanguage());
4734 usedCd = usedCdm;
4735 }
4736 }
4737 if (usedCd)
4738 {
4739 AUTO_TRACE_ADD("case 3: adding used class '{}'", usedCd->name());
4740 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4741 if (usedCdm)
4742 {
4743 if (isArtificial) usedCdm->setArtificial(TRUE);
4744 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4745 }
4746 }
4747 }
4748 }
4749 }
4750 }
4751}
4752
4754 const Entry *root,
4755 Definition *context,
4756 ClassDefMutable *masterCd,
4757 ClassDefMutable *instanceCd,
4759 bool isArtificial,
4760 const ArgumentList *actualArgs = nullptr,
4761 const TemplateNameMap &templateNames=TemplateNameMap()
4762 )
4763{
4764 AUTO_TRACE("name={}",root->name);
4765 // The base class could ofcouse also be a non-nested class
4766 const ArgumentList &formalArgs = masterCd->templateArguments();
4767 for (const BaseInfo &bi : root->extends)
4768 {
4769 //printf("masterCd=%s bi.name='%s' #actualArgs=%d\n",
4770 // qPrint(masterCd->localName()),qPrint(bi.name),actualArgs ? (int)actualArgs->size() : -1);
4771 TemplateNameMap formTemplateNames;
4772 if (templateNames.empty())
4773 {
4774 formTemplateNames = getTemplateArgumentsInName(formalArgs,bi.name.str());
4775 }
4776 BaseInfo tbi = bi;
4777 tbi.name = substituteTemplateArgumentsInString(bi.name,formalArgs,actualArgs);
4778 //printf("masterCd=%p instanceCd=%p bi->name=%s tbi.name=%s\n",(void*)masterCd,(void*)instanceCd,qPrint(bi.name),qPrint(tbi.name));
4779
4780 if (mode==DocumentedOnly)
4781 {
4782 // find a documented base class in the correct scope
4783 if (!findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,DocumentedOnly,isArtificial))
4784 {
4785 // 1.8.2: decided to show inheritance relations even if not documented,
4786 // we do make them artificial, so they do not appear in the index
4787 //if (!Config_getBool(HIDE_UNDOC_RELATIONS))
4788 bool b = Config_getBool(HIDE_UNDOC_RELATIONS) ? TRUE : isArtificial;
4789 //{
4790 // no documented base class -> try to find an undocumented one
4791 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,Undocumented,b);
4792 //}
4793 }
4794 }
4795 else if (mode==TemplateInstances)
4796 {
4797 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,TemplateInstances,isArtificial);
4798 }
4799 }
4800}
4801
4802//----------------------------------------------------------------------
4803
4804static void findTemplateInstanceRelation(const Entry *root,
4805 Definition *context,
4806 ClassDefMutable *templateClass,const QCString &templSpec,
4807 const TemplateNameMap &templateNames,
4808 bool isArtificial)
4809{
4810 AUTO_TRACE("Derived from template '{}' with parameters '{}' isArtificial={}",
4811 templateClass->name(),templSpec,isArtificial);
4812
4813 QCString tempArgsStr = tempArgListToString(templateClass->templateArguments(),root->lang,false);
4814 bool existingClass = templSpec==tempArgsStr;
4815 if (existingClass) return; // avoid recursion
4816
4817 bool freshInstance=FALSE;
4818 ClassDefMutable *instanceClass = toClassDefMutable(
4819 templateClass->insertTemplateInstance(
4820 root->fileName,root->startLine,root->startColumn,templSpec,freshInstance));
4821 if (instanceClass)
4822 {
4823 if (freshInstance)
4824 {
4825 instanceClass->setArtificial(TRUE);
4826 instanceClass->setLanguage(root->lang);
4827
4828 AUTO_TRACE_ADD("found fresh instance '{}'",instanceClass->name());
4829 instanceClass->setTemplateBaseClassNames(templateNames);
4830
4831 // search for new template instances caused by base classes of
4832 // instanceClass
4833 auto it_pair = g_classEntries.equal_range(templateClass->name().str());
4834 for (auto it=it_pair.first ; it!=it_pair.second ; ++it)
4835 {
4836 const Entry *templateRoot = it->second;
4837 AUTO_TRACE_ADD("template root found '{}' templSpec='{}'",templateRoot->name,templSpec);
4838 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(root->lang,templSpec);
4839 findBaseClassesForClass(templateRoot,context,templateClass,instanceClass,
4840 TemplateInstances,isArtificial,templArgs.get(),templateNames);
4841
4842 findUsedClassesForClass(templateRoot,context,templateClass,instanceClass,
4843 isArtificial,templArgs.get(),templateNames);
4844 }
4845 }
4846 else
4847 {
4848 AUTO_TRACE_ADD("instance already exists");
4849 }
4850 }
4851}
4852
4853//----------------------------------------------------------------------
4854
4855static void resolveTemplateInstanceInType(const Entry *root,const Definition *scope,const MemberDef *md)
4856{
4857 // For a statement like 'using X = T<A>', add a template instance 'T<A>' as a symbol, so it can
4858 // be used to match arguments (see issue #11111)
4859 AUTO_TRACE();
4860 QCString ttype = md->typeString();
4861 ttype.stripPrefix("typedef ");
4862 int ti=ttype.find('<');
4863 if (ti!=-1)
4864 {
4865 QCString templateClassName = ttype.left(ti);
4866 SymbolResolver resolver(root->fileDef());
4867 ClassDefMutable *baseClass = resolver.resolveClassMutable(scope ? scope : Doxygen::globalScope,
4868 templateClassName, true, true);
4869 AUTO_TRACE_ADD("templateClassName={} baseClass={}",templateClassName,baseClass?baseClass->name():"<none>");
4870 if (baseClass)
4871 {
4872 const ArgumentList &tl = baseClass->templateArguments();
4873 TemplateNameMap templateNames = getTemplateArgumentsInName(tl,templateClassName.str());
4875 baseClass,
4876 ttype.mid(ti),
4877 templateNames,
4878 baseClass->isArtificial());
4879 }
4880 }
4881}
4882
4883//----------------------------------------------------------------------
4884
4885static bool isRecursiveBaseClass(const QCString &scope,const QCString &name)
4886{
4887 QCString n=name;
4888 int index=n.find('<');
4889 if (index!=-1)
4890 {
4891 n=n.left(index);
4892 }
4893 bool result = rightScopeMatch(scope,n);
4894 return result;
4895}
4896
4898{
4899 if (name.isEmpty()) return 0;
4900 int l = static_cast<int>(name.length());
4901 if (name[l-1]=='>') // search backward to find the matching <, allowing nested <...> and strings.
4902 {
4903 int count=1;
4904 int i=l-2;
4905 char insideQuote=0;
4906 while (count>0 && i>=0)
4907 {
4908 char c = name[i--];
4909 switch (c)
4910 {
4911 case '>': if (!insideQuote) count++; break;
4912 case '<': if (!insideQuote) count--; break;
4913 case '\'': if (!insideQuote) insideQuote=c;
4914 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
4915 break;
4916 case '"': if (!insideQuote) insideQuote=c;
4917 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
4918 break;
4919 default: break;
4920 }
4921 }
4922 if (i>=0) l=i+1;
4923 }
4924 return l;
4925}
4926
4928 const Entry *root,
4929 Definition *context,
4930 ClassDefMutable *cd,
4931 const BaseInfo *bi,
4932 const TemplateNameMap &templateNames,
4934 bool isArtificial
4935 )
4936{
4937 AUTO_TRACE("name={} base={} isArtificial={} mode={}",cd->name(),bi->name,isArtificial,(int)mode);
4938
4939 QCString biName=bi->name;
4940 bool explicitGlobalScope=FALSE;
4941 if (biName.startsWith("::")) // explicit global scope
4942 {
4943 biName=biName.right(biName.length()-2);
4944 explicitGlobalScope=TRUE;
4945 }
4946
4947 Entry *parentNode=root->parent();
4948 bool lastParent=FALSE;
4949 do // for each parent scope, starting with the largest scope
4950 // (in case of nested classes)
4951 {
4952 QCString scopeName= parentNode ? parentNode->name : QCString();
4953 int scopeOffset=explicitGlobalScope ? 0 : static_cast<int>(scopeName.length());
4954 do // try all parent scope prefixes, starting with the largest scope
4955 {
4956 //printf("scopePrefix='%s' biName='%s'\n",
4957 // qPrint(scopeName.left(scopeOffset)),qPrint(biName));
4958
4959 QCString baseClassName=biName;
4960 if (scopeOffset>0)
4961 {
4962 baseClassName.prepend(scopeName.left(scopeOffset)+"::");
4963 }
4964 if (root->lang==SrcLangExt::CSharp)
4965 {
4966 baseClassName = mangleCSharpGenericName(baseClassName);
4967 }
4968 AUTO_TRACE_ADD("cd='{}' baseClassName='{}'",cd->name(),baseClassName);
4969 SymbolResolver resolver(cd->getFileDef());
4970 ClassDefMutable *baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
4971 baseClassName,
4972 mode==Undocumented,
4973 true
4974 );
4975 const MemberDef *baseClassTypeDef = resolver.getTypedef();
4976 QCString templSpec = resolver.getTemplateSpec();
4977 //printf("baseClassName=%s baseClass=%p cd=%p explicitGlobalScope=%d\n",
4978 // qPrint(baseClassName),baseClass,cd,explicitGlobalScope);
4979 //printf(" scope='%s' baseClassName='%s' baseClass=%s templSpec=%s\n",
4980 // cd ? qPrint(cd->name()):"<none>",
4981 // qPrint(baseClassName),
4982 // baseClass?qPrint(baseClass->name()):"<none>",
4983 // qPrint(templSpec)
4984 // );
4985 //if (baseClassName.left(root->name.length())!=root->name ||
4986 // baseClassName.at(root->name.length())!='<'
4987 // ) // Check for base class with the same name.
4988 // // If found then look in the outer scope for a match
4989 // // and prevent recursion.
4990 if (!isRecursiveBaseClass(root->name,baseClassName)
4991 || explicitGlobalScope
4992 // sadly isRecursiveBaseClass always true for UNO IDL ifc/svc members
4993 // (i.e. this is needed for addInterfaceOrServiceToServiceOrSingleton)
4994 || (root->lang==SrcLangExt::IDL &&
4995 (root->section.isExportedInterface() ||
4996 root->section.isIncludedService()))
4997 )
4998 {
4999 AUTO_TRACE_ADD("class relation '{}' inherited/used by '{}' found prot={} virt={} templSpec='{}'",
5000 baseClassName, root->name, bi->prot, bi->virt, templSpec);
5001
5002 int i=findTemplateSpecializationPosition(baseClassName);
5003 int si=baseClassName.findRev("::",i);
5004 if (si==-1) si=0;
5005 if (baseClass==nullptr && static_cast<size_t>(i)!=baseClassName.length())
5006 // base class has template specifiers
5007 {
5008 // TODO: here we should try to find the correct template specialization
5009 // but for now, we only look for the unspecialized base class.
5010 int e=findEndOfTemplate(baseClassName,i+1);
5011 //printf("baseClass==0 i=%d e=%d\n",i,e);
5012 if (e!=-1) // end of template was found at e
5013 {
5014 templSpec = removeRedundantWhiteSpace(baseClassName.mid(i,e-i));
5015 baseClassName = baseClassName.left(i)+baseClassName.right(baseClassName.length()-e);
5016 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
5017 baseClassName,
5018 mode==Undocumented,
5019 true
5020 );
5021 baseClassTypeDef = resolver.getTypedef();
5022 //printf("baseClass=%p -> baseClass=%s templSpec=%s\n",
5023 // baseClass,qPrint(baseClassName),qPrint(templSpec));
5024 }
5025 }
5026 else if (baseClass && !templSpec.isEmpty()) // we have a known class, but also
5027 // know it is a template, so see if
5028 // we can also link to the explicit
5029 // instance (for instance if a class
5030 // derived from a template argument)
5031 {
5032 //printf("baseClass=%s templSpec=%s\n",qPrint(baseClass->name()),qPrint(templSpec));
5033 ClassDefMutable *templClass=getClassMutable(baseClass->name()+templSpec);
5034 if (templClass)
5035 {
5036 // use the template instance instead of the template base.
5037 baseClass = templClass;
5038 templSpec.clear();
5039 }
5040 }
5041
5042 //printf("cd=%p baseClass=%p\n",cd,baseClass);
5043 bool found=baseClass!=nullptr && (baseClass!=cd || mode==TemplateInstances);
5044 AUTO_TRACE_ADD("1. found={}",found);
5045 if (!found && si!=-1)
5046 {
5047 // replace any namespace aliases
5048 replaceNamespaceAliases(baseClassName);
5049 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
5050 baseClassName,
5051 mode==Undocumented,
5052 true
5053 );
5054 baseClassTypeDef = resolver.getTypedef();
5055 found=baseClass!=nullptr && baseClass!=cd;
5056 if (found) templSpec = resolver.getTemplateSpec();
5057 }
5058 AUTO_TRACE_ADD("2. found={}",found);
5059
5060 if (!found)
5061 {
5062 baseClass=toClassDefMutable(findClassWithinClassContext(context,cd,baseClassName));
5063 //printf("findClassWithinClassContext(%s,%s)=%p\n",
5064 // qPrint(cd->name()),qPrint(baseClassName),baseClass);
5065 found = baseClass!=nullptr && baseClass!=cd;
5066
5067 }
5068 AUTO_TRACE_ADD("3. found={}",found);
5069 if (!found)
5070 {
5071 // for PHP the "use A\B as C" construct map class C to A::B, so we lookup
5072 // the class name also in the alias mapping.
5073 auto it = Doxygen::namespaceAliasMap.find(baseClassName.str());
5074 if (it!=Doxygen::namespaceAliasMap.end()) // see if it is indeed a class.
5075 {
5076 baseClass=getClassMutable(it->second.alias);
5077 found = baseClass!=nullptr && baseClass!=cd;
5078 }
5079 }
5080 bool isATemplateArgument = templateNames.find(biName.str())!=templateNames.end();
5081
5082 AUTO_TRACE_ADD("4. found={}",found);
5083 if (found)
5084 {
5085 AUTO_TRACE_ADD("Documented base class '{}' templSpec='{}'",biName,templSpec);
5086 // add base class to this class
5087
5088 // if templSpec is not empty then we should "instantiate"
5089 // the template baseClass. A new ClassDef should be created
5090 // to represent the instance. To be able to add the (instantiated)
5091 // members and documentation of a template class
5092 // (inserted in that template class at a later stage),
5093 // the template should know about its instances.
5094 // the instantiation process, should be done in a recursive way,
5095 // since instantiating a template may introduce new inheritance
5096 // relations.
5097 if (!templSpec.isEmpty() && mode==TemplateInstances)
5098 {
5099 // if baseClass is actually a typedef then we should not
5100 // instantiate it, since typedefs are in a different namespace
5101 // see bug531637 for an example where this would otherwise hang
5102 // Doxygen
5103 if (baseClassTypeDef==nullptr)
5104 {
5105 //printf(" => findTemplateInstanceRelation: %s\n",qPrint(baseClass->name()));
5106 findTemplateInstanceRelation(root,context,baseClass,templSpec,templateNames,baseClass->isArtificial());
5107 }
5108 }
5109 else if (mode==DocumentedOnly || mode==Undocumented)
5110 {
5111 //printf(" => insert base class\n");
5112 QCString usedName;
5113 if (baseClassTypeDef)
5114 {
5115 usedName=biName;
5116 //printf("***** usedName=%s templSpec=%s\n",qPrint(usedName),qPrint(templSpec));
5117 }
5118 Protection prot = bi->prot;
5119 if (Config_getBool(SIP_SUPPORT)) prot=Protection::Public;
5120 if (cd!=baseClass && !cd->isSubClass(baseClass) && baseClass->isBaseClass(cd,true,templSpec)==0) // check for recursion, see bug690787
5121 {
5122 AUTO_TRACE_ADD("insertBaseClass name={} prot={} virt={} templSpec={}",usedName,prot,bi->virt,templSpec);
5123 cd->insertBaseClass(baseClass,usedName,prot,bi->virt,templSpec);
5124 // add this class as super class to the base class
5125 baseClass->insertSubClass(cd,prot,bi->virt,templSpec);
5126 }
5127 else
5128 {
5129 warn(root->fileName,root->startLine,
5130 "Detected potential recursive class relation "
5131 "between class {} and base class {}!",
5132 cd->name(),baseClass->name()
5133 );
5134 }
5135 }
5136 return TRUE;
5137 }
5138 else if (mode==Undocumented && (scopeOffset==0 || isATemplateArgument))
5139 {
5140 AUTO_TRACE_ADD("New undocumented base class '{}' baseClassName='{}' templSpec='{}' isArtificial={}",
5141 biName,baseClassName,templSpec,isArtificial);
5142 baseClass=nullptr;
5143 if (isATemplateArgument)
5144 {
5145 baseClass = toClassDefMutable(Doxygen::hiddenClassLinkedMap->find(baseClassName));
5146 if (baseClass==nullptr) // not found (or alias)
5147 {
5148 baseClass= toClassDefMutable(
5149 Doxygen::hiddenClassLinkedMap->add(baseClassName,
5150 createClassDef(root->fileName,root->startLine,root->startColumn,
5151 baseClassName,
5152 ClassDef::Class)));
5153 if (baseClass) // really added (not alias)
5154 {
5155 if (isArtificial) baseClass->setArtificial(TRUE);
5156 baseClass->setLanguage(root->lang);
5157 }
5158 }
5159 }
5160 else
5161 {
5162 baseClass = toClassDefMutable(Doxygen::classLinkedMap->find(baseClassName));
5163 //printf("*** classDDict->find(%s)=%p biName=%s templSpec=%s\n",
5164 // qPrint(baseClassName),baseClass,qPrint(biName),qPrint(templSpec));
5165 if (baseClass==nullptr) // not found (or alias)
5166 {
5167 baseClass = toClassDefMutable(
5168 Doxygen::classLinkedMap->add(baseClassName,
5169 createClassDef(root->fileName,root->startLine,root->startColumn,
5170 baseClassName,
5171 ClassDef::Class)));
5172 if (baseClass) // really added (not alias)
5173 {
5174 if (isArtificial) baseClass->setArtificial(TRUE);
5175 baseClass->setLanguage(root->lang);
5176 si = baseClassName.findRev("::");
5177 if (si!=-1) // class is nested
5178 {
5179 Definition *sd = findScopeFromQualifiedName(Doxygen::globalScope,baseClassName.left(si),nullptr,root->tagInfo());
5180 if (sd==nullptr || sd==Doxygen::globalScope) // outer scope not found
5181 {
5182 baseClass->setArtificial(TRUE); // see bug678139
5183 }
5184 }
5185 }
5186 }
5187 }
5188 if (baseClass)
5189 {
5190 if (biName.endsWith("-p"))
5191 {
5192 biName="<"+biName.left(biName.length()-2)+">";
5193 }
5194 if (!cd->isSubClass(baseClass) && cd!=baseClass && cd->isBaseClass(baseClass,true,templSpec)==0) // check for recursion
5195 {
5196 AUTO_TRACE_ADD("insertBaseClass name={} prot={} virt={} templSpec={}",biName,bi->prot,bi->virt,templSpec);
5197 // add base class to this class
5198 cd->insertBaseClass(baseClass,biName,bi->prot,bi->virt,templSpec);
5199 // add this class as super class to the base class
5200 baseClass->insertSubClass(cd,bi->prot,bi->virt,templSpec);
5201 }
5202 // the undocumented base was found in this file
5203 baseClass->insertUsedFile(root->fileDef());
5204
5205 Definition *scope = buildScopeFromQualifiedName(baseClass->name(),root->lang,nullptr);
5206 if (scope!=baseClass)
5207 {
5208 baseClass->setOuterScope(scope);
5209 }
5210
5211 if (baseClassName.endsWith("-p"))
5212 {
5214 }
5215 return TRUE;
5216 }
5217 else
5218 {
5219 AUTO_TRACE_ADD("Base class '{}' not created (alias?)",biName);
5220 }
5221 }
5222 else
5223 {
5224 AUTO_TRACE_ADD("Base class '{}' not found",biName);
5225 }
5226 }
5227 else
5228 {
5229 if (mode!=TemplateInstances)
5230 {
5231 warn(root->fileName,root->startLine,
5232 "Detected potential recursive class relation "
5233 "between class {} and base class {}!",
5234 root->name,baseClassName
5235 );
5236 }
5237 // for mode==TemplateInstance this case is quite common and
5238 // indicates a relation between a template class and a template
5239 // instance with the same name.
5240 }
5241 if (scopeOffset==0)
5242 {
5243 scopeOffset=-1;
5244 }
5245 else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1)
5246 {
5247 scopeOffset=0;
5248 }
5249 //printf("new scopeOffset='%d'",scopeOffset);
5250 } while (scopeOffset>=0);
5251
5252 if (parentNode==nullptr)
5253 {
5254 lastParent=TRUE;
5255 }
5256 else
5257 {
5258 parentNode=parentNode->parent();
5259 }
5260 } while (lastParent);
5261
5262 return FALSE;
5263}
5264
5265//----------------------------------------------------------------------
5266// Computes the base and super classes for each class in the tree
5267
5268static bool isClassSection(const Entry *root)
5269{
5270 if ( !root->name.isEmpty() )
5271 {
5272 if (root->section.isCompound())
5273 // is it a compound (class, struct, union, interface ...)
5274 {
5275 return TRUE;
5276 }
5277 else if (root->section.isCompoundDoc())
5278 // is it a documentation block with inheritance info.
5279 {
5280 bool hasExtends = !root->extends.empty();
5281 if (hasExtends) return TRUE;
5282 }
5283 }
5284 return FALSE;
5285}
5286
5287
5288/*! Builds a dictionary of all entry nodes in the tree starting with \a root
5289 */
5290static void findClassEntries(const Entry *root)
5291{
5292 if (isClassSection(root))
5293 {
5294 g_classEntries.emplace(root->name.str(),root);
5295 }
5296 for (const auto &e : root->children()) findClassEntries(e.get());
5297}
5298
5300{
5301 // strip any anonymous scopes first
5304 int i=0;
5305 if ((root->lang==SrcLangExt::CSharp || root->lang==SrcLangExt::Java) &&
5306 (i=bName.find('<'))!=-1)
5307 {
5308 // a Java/C# generic class looks like a C++ specialization, so we need to strip the
5309 // template part before looking for matches
5310 if (root->lang==SrcLangExt::CSharp)
5311 {
5312 bName = mangleCSharpGenericName(root->name);
5313 }
5314 else
5315 {
5316 bName = bName.left(i);
5317 }
5318 }
5319 return bName;
5320}
5321
5322/*! Using the dictionary build by findClassEntries(), this
5323 * function will look for additional template specialization that
5324 * exists as inheritance relations only. These instances will be
5325 * added to the template they are derived from.
5326 */
5328{
5329 AUTO_TRACE();
5330 ClassDefSet visitedClasses;
5331 for (const auto &[name,root] : g_classEntries)
5332 {
5333 QCString bName = extractClassName(root);
5334 ClassDefMutable *cdm = getClassMutable(bName);
5335 if (cdm)
5336 {
5338 }
5339 }
5340}
5341
5343{
5344 AUTO_TRACE("root->name={} cd={}",root->name,cd->name());
5345 int i = root->name.find('<');
5346 int j = root->name.findRev('>');
5347 int k = root->name.find("::",j+1); // A<T::B> => ok, A<T>::B => nok
5348 if (i!=-1 && j!=-1 && k==-1 && root->lang!=SrcLangExt::CSharp && root->lang!=SrcLangExt::Java)
5349 {
5350 ClassDefMutable *master = getClassMutable(root->name.left(i));
5351 if (master && master!=cd && !cd->templateMaster())
5352 {
5353 AUTO_TRACE_ADD("class={} master={}",cd->name(),cd->templateMaster()?cd->templateMaster()->name():"<none>",master->name());
5354 cd->setTemplateMaster(master);
5355 master->insertExplicitTemplateInstance(cd,root->name.mid(i));
5356 }
5357 }
5358}
5359
5361{
5362 AUTO_TRACE();
5363 for (const auto &[name,root] : g_classEntries)
5364 {
5365 QCString bName = extractClassName(root);
5366 ClassDefMutable *cdm = getClassMutable(bName);
5367 if (cdm)
5368 {
5369 findUsedClassesForClass(root,cdm,cdm,cdm,TRUE);
5371 cdm->addTypeConstraints();
5372 }
5373 }
5374}
5375
5377{
5378 AUTO_TRACE();
5379 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5380 {
5381 if (!nd->hasDocumentation())
5382 {
5383 if ((guessSection(nd->getDefFileName()).isHeader() ||
5384 nd->getLanguage() == SrcLangExt::Fortran) && // Fortran doesn't have header files.
5385 !Config_getBool(HIDE_UNDOC_NAMESPACES) // undocumented namespaces are visible
5386 )
5387 {
5388 warn_undoc(nd->getDefFileName(),nd->getDefLine(), "{} {} is not documented.",
5389 nd->getLanguage() == SrcLangExt::Fortran ? "Module" : "Namespace",
5390 nd->name());
5391 }
5392 }
5393 }
5394}
5395
5397{
5398 AUTO_TRACE();
5399 for (const auto &[name,root] : g_classEntries)
5400 {
5401 QCString bName = extractClassName(root);
5402 ClassDefMutable *cd = getClassMutable(bName);
5403 if (cd)
5404 {
5406 }
5407 size_t numMembers = cd ? cd->memberNameInfoLinkedMap().size() : 0;
5408 if ((cd==nullptr || (!cd->hasDocumentation() && !cd->isReference())) && numMembers>0 && !bName.endsWith("::"))
5409 {
5410 if (!root->name.isEmpty() && root->name.find('@')==-1 && // normal name
5411 (guessSection(root->fileName).isHeader() ||
5412 Config_getBool(EXTRACT_LOCAL_CLASSES)) && // not defined in source file
5413 protectionLevelVisible(root->protection) && // hidden by protection
5414 !Config_getBool(HIDE_UNDOC_CLASSES) // undocumented class are visible
5415 )
5416 warn_undoc(root->fileName,root->startLine, "Compound {} is not documented.", root->name);
5417 }
5418 }
5419}
5420
5422{
5423 AUTO_TRACE();
5424 for (const auto &[name,root] : g_classEntries)
5425 {
5429 // strip any anonymous scopes first
5430 if (cd && !cd->getTemplateInstances().empty())
5431 {
5432 AUTO_TRACE_ADD("Template class '{}'",cd->name());
5433 for (const auto &ti : cd->getTemplateInstances()) // for each template instance
5434 {
5435 ClassDefMutable *tcd=toClassDefMutable(ti.classDef);
5436 if (tcd)
5437 {
5438 AUTO_TRACE_ADD("Template instance '{}'",tcd->name());
5439 QCString templSpec = ti.templSpec;
5440 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(tcd->getLanguage(),templSpec);
5441 for (const BaseInfo &bi : root->extends)
5442 {
5443 // check if the base class is a template argument
5444 BaseInfo tbi = bi;
5445 const ArgumentList &tl = cd->templateArguments();
5446 if (!tl.empty())
5447 {
5448 TemplateNameMap baseClassNames = tcd->getTemplateBaseClassNames();
5449 TemplateNameMap templateNames = getTemplateArgumentsInName(tl,bi.name.str());
5450 // for each template name that we inherit from we need to
5451 // substitute the formal with the actual arguments
5452 TemplateNameMap actualTemplateNames;
5453 for (const auto &tn_kv : templateNames)
5454 {
5455 size_t templIndex = tn_kv.second;
5456 Argument actArg;
5457 bool hasActArg=FALSE;
5458 if (templIndex<templArgs->size())
5459 {
5460 actArg=templArgs->at(templIndex);
5461 hasActArg=TRUE;
5462 }
5463 if (hasActArg &&
5464 baseClassNames.find(actArg.type.str())!=baseClassNames.end() &&
5465 actualTemplateNames.find(actArg.type.str())==actualTemplateNames.end()
5466 )
5467 {
5468 actualTemplateNames.emplace(actArg.type.str(),static_cast<int>(templIndex));
5469 }
5470 }
5471
5472 tbi.name = substituteTemplateArgumentsInString(bi.name,tl,templArgs.get());
5473 // find a documented base class in the correct scope
5474 if (!findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,DocumentedOnly,FALSE))
5475 {
5476 // no documented base class -> try to find an undocumented one
5477 findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,Undocumented,TRUE);
5478 }
5479 }
5480 }
5481 }
5482 }
5483 }
5484 }
5485}
5486
5487//-----------------------------------------------------------------------
5488// compute the references (anchors in HTML) for each function in the file
5489
5491{
5492 AUTO_TRACE();
5493 for (const auto &cd : *Doxygen::classLinkedMap)
5494 {
5495 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5496 if (cdm)
5497 {
5498 cdm->computeAnchors();
5499 }
5500 }
5501 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5502 {
5503 for (const auto &fd : *fn)
5504 {
5505 fd->computeAnchors();
5506 }
5507 }
5508 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5509 {
5511 if (ndm)
5512 {
5513 ndm->computeAnchors();
5514 }
5515 }
5516 for (const auto &gd : *Doxygen::groupLinkedMap)
5517 {
5518 gd->computeAnchors();
5519 }
5520}
5521
5522//----------------------------------------------------------------------
5523
5524
5525template<typename Func>
5526static void applyToAllDefinitions(Func func)
5527{
5528 for (const auto &cd : *Doxygen::classLinkedMap)
5529 {
5530 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5531 if (cdm)
5532 {
5533 func(cdm);
5534 }
5535 }
5536
5537 for (const auto &cd : *Doxygen::conceptLinkedMap)
5538 {
5539 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
5540 if (cdm)
5541 {
5542 func(cdm);
5543 }
5544 }
5545
5546 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5547 {
5548 for (const auto &fd : *fn)
5549 {
5550 func(fd.get());
5551 }
5552 }
5553
5554 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5555 {
5557 if (ndm)
5558 {
5559 func(ndm);
5560 }
5561 }
5562
5563 for (const auto &gd : *Doxygen::groupLinkedMap)
5564 {
5565 func(gd.get());
5566 }
5567
5568 for (const auto &pd : *Doxygen::pageLinkedMap)
5569 {
5570 func(pd.get());
5571 }
5572
5573 for (const auto &dd : *Doxygen::dirLinkedMap)
5574 {
5575 func(dd.get());
5576 }
5577
5578 func(&ModuleManager::instance());
5579}
5580
5581//----------------------------------------------------------------------
5582
5584{
5585 AUTO_TRACE();
5586 applyToAllDefinitions([](auto* obj) { obj->addRequirementReferences(); });
5587}
5588
5589//----------------------------------------------------------------------
5590
5592{
5593 AUTO_TRACE();
5594 applyToAllDefinitions([](auto* obj) { obj->addListReferences(); });
5595}
5596
5597
5598//----------------------------------------------------------------------
5599
5601{
5602 AUTO_TRACE();
5604 {
5605 rl->generatePage();
5606 }
5607}
5608
5609//----------------------------------------------------------------------
5610// Copy the documentation in entry 'root' to member definition 'md' and
5611// set the function declaration of the member to 'funcDecl'. If the boolean
5612// over_load is set the standard overload text is added.
5613
5614static void addMemberDocs(const Entry *root,
5615 MemberDefMutable *md, const QCString &funcDecl,
5616 const ArgumentList *al,
5617 bool over_load,
5618 TypeSpecifier spec
5619 )
5620{
5621 if (md==nullptr) return;
5622 AUTO_TRACE("scope='{}' name='{}' args='{}' funcDecl='{}' mSpec={}",
5623 root->parent()->name,md->name(),md->argsString(),funcDecl,spec);
5624 if (!root->section.isDoc()) // @fn or @var does not need to specify the complete definition, so don't overwrite it
5625 {
5626 QCString fDecl=funcDecl;
5627 // strip extern specifier
5628 fDecl.stripPrefix("extern ");
5629 md->setDefinition(fDecl);
5630 }
5632 md->addQualifiers(root->qualifiers);
5634 const NamespaceDef *nd=md->getNamespaceDef();
5635 QCString fullName;
5636 if (cd)
5637 fullName = cd->name();
5638 else if (nd)
5639 fullName = nd->name();
5640
5641 if (!fullName.isEmpty()) fullName+="::";
5642 fullName+=md->name();
5643 FileDef *rfd=root->fileDef();
5644
5645 // TODO determine scope based on root not md
5646 Definition *rscope = md->getOuterScope();
5647
5648 const ArgumentList &mdAl = md->argumentList();
5649 if (al)
5650 {
5651 ArgumentList mergedAl = *al;
5652 //printf("merging arguments (1) docs=%d\n",root->doc.isEmpty());
5653 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedAl,!root->doc.isEmpty());
5654 }
5655 else
5656 {
5657 if (
5658 matchArguments2( md->getOuterScope(), md->getFileDef(),md->typeString(),const_cast<ArgumentList*>(&mdAl),
5659 rscope,rfd,root->type,&root->argList,
5660 TRUE, root->lang
5661 )
5662 )
5663 {
5664 //printf("merging arguments (2)\n");
5665 ArgumentList mergedArgList = root->argList;
5666 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.isEmpty());
5667 }
5668 }
5669 if (over_load) // the \overload keyword was used
5670 {
5672 if (!root->doc.isEmpty())
5673 {
5674 doc+="<p>";
5675 doc+=root->doc;
5676 }
5677 md->setDocumentation(doc,root->docFile,root->docLine);
5679 md->setDocsForDefinition(!root->proto);
5680 }
5681 else
5682 {
5683 //printf("overwrite!\n");
5684 md->setDocumentation(root->doc,root->docFile,root->docLine);
5685 md->setDocsForDefinition(!root->proto);
5686
5687 //printf("overwrite!\n");
5688 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
5689
5690 if (
5691 (md->inbodyDocumentation().isEmpty() ||
5692 !root->parent()->name.isEmpty()
5693 ) && !root->inbodyDocs.isEmpty()
5694 )
5695 {
5697 }
5698 }
5699
5700 //printf("initializer: '%s'(isEmpty=%d) '%s'(isEmpty=%d)\n",
5701 // qPrint(md->initializer()),md->initializer().isEmpty(),
5702 // qPrint(root->initializer),root->initializer.isEmpty()
5703 // );
5704 std::string rootInit = root->initializer.str();
5705 if (md->initializer().isEmpty() && !rootInit.empty())
5706 {
5707 //printf("setInitializer\n");
5708 md->setInitializer(rootInit);
5709 }
5710 if (md->requiresClause().isEmpty() && !root->req.isEmpty())
5711 {
5712 md->setRequiresClause(root->req);
5713 }
5714
5715 md->setMaxInitLines(root->initLines);
5716
5717 if (rfd)
5718 {
5719 if ((md->getStartBodyLine()==-1 && root->bodyLine!=-1)
5720 )
5721 {
5722 //printf("Setting new body segment [%d,%d]\n",root->bodyLine,root->endBodyLine);
5723 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
5724 md->setBodyDef(rfd);
5725 }
5726
5727 md->setRefItems(root->sli);
5728 md->setRequirementReferences(root->rqli);
5729 }
5730
5732 md->addQualifiers(root->qualifiers);
5733
5734 md->mergeMemberSpecifiers(spec);
5736 addMemberToGroups(root,md);
5738 if (cd) cd->insertUsedFile(rfd);
5739 //printf("root->mGrpId=%d\n",root->mGrpId);
5740 if (root->mGrpId!=-1)
5741 {
5742 if (md->getMemberGroupId()!=-1)
5743 {
5744 if (md->getMemberGroupId()!=root->mGrpId)
5745 {
5746 warn(root->fileName,root->startLine,
5747 "member {} belongs to two different groups. The second one found here will be ignored.",
5748 md->name()
5749 );
5750 }
5751 }
5752 else // set group id
5753 {
5754 //printf("setMemberGroupId=%d md=%s\n",root->mGrpId,qPrint(md->name()));
5755 md->setMemberGroupId(root->mGrpId);
5756 }
5757 }
5758 md->addQualifiers(root->qualifiers);
5759}
5760
5761//----------------------------------------------------------------------
5762// find a class definition given the scope name and (optionally) a
5763// template list specifier
5764
5766 const QCString &scopeName)
5767{
5768 SymbolResolver resolver(fd);
5769 const ClassDef *tcd = resolver.resolveClass(nd,scopeName,true,true);
5770 //printf("findClassDefinition(fd=%s,ns=%s,scopeName=%s)='%s'\n",
5771 // qPrint(fd?fd->name():""),qPrint(nd?nd->name():""),
5772 // qPrint(scopeName),qPrint(tcd?tcd->name():""));
5773 return tcd;
5774}
5775
5776//----------------------------------------------------------------------------
5777// Returns TRUE, if the entry belongs to the group of the member definition,
5778// otherwise FALSE.
5779
5780static bool isEntryInGroupOfMember(const Entry *root,const MemberDef *md,bool allowNoGroup=false)
5781{
5782 const GroupDef *gd = md->getGroupDef();
5783 if (!gd)
5784 {
5785 return allowNoGroup;
5786 }
5787
5788 for (const auto &g : root->groups)
5789 {
5790 if (g.groupname == gd->name())
5791 {
5792 return true; // matching group
5793 }
5794 }
5795
5796 return false;
5797}
5798
5799//----------------------------------------------------------------------
5800// Adds the documentation contained in 'root' to a global function
5801// with name 'name' and argument list 'args' (for overloading) and
5802// function declaration 'decl' to the corresponding member definition.
5803
5804static bool findGlobalMember(const Entry *root,
5805 const QCString &namespaceName,
5806 const QCString &type,
5807 const QCString &name,
5808 const QCString &tempArg,
5809 const QCString &,
5810 const QCString &decl,
5811 TypeSpecifier /* spec */)
5812{
5813 AUTO_TRACE("namespace='{}' type='{}' name='{}' tempArg='{}' decl='{}'",namespaceName,type,name,tempArg,decl);
5814 QCString n=name;
5815 if (n.isEmpty()) return FALSE;
5816 if (n.find("::")!=-1) return FALSE; // skip undefined class members
5817 MemberName *mn=Doxygen::functionNameLinkedMap->find(n+tempArg); // look in function dictionary
5818 if (mn==nullptr)
5819 {
5820 mn=Doxygen::functionNameLinkedMap->find(n); // try without template arguments
5821 }
5822 if (mn) // function name defined
5823 {
5824 AUTO_TRACE_ADD("Found symbol name");
5825 //int count=0;
5826 bool found=FALSE;
5827 for (const auto &md : *mn)
5828 {
5829 // If the entry has groups, then restrict the search to members which are
5830 // in one of the groups of the entry. If md is not associated with a group yet,
5831 // allow this documentation entry to add the group info.
5832 if (!root->groups.empty() && !isEntryInGroupOfMember(root, md.get(), true))
5833 {
5834 continue;
5835 }
5836
5837 const NamespaceDef *nd=nullptr;
5838 if (md->isAlias() && md->getOuterScope() &&
5839 md->getOuterScope()->definitionType()==Definition::TypeNamespace)
5840 {
5841 nd = toNamespaceDef(md->getOuterScope());
5842 }
5843 else
5844 {
5845 nd = md->getNamespaceDef();
5846 }
5847
5848 // special case for strong enums
5849 int enumNamePos=0;
5850 if (nd && md->isEnumValue() && (enumNamePos=namespaceName.findRev("::"))!=-1)
5851 { // md part of a strong enum in a namespace?
5852 QCString enumName = namespaceName.mid(enumNamePos+2);
5853 if (namespaceName.left(enumNamePos)==nd->name())
5854 {
5855 MemberName *enumMn=Doxygen::functionNameLinkedMap->find(enumName);
5856 if (enumMn)
5857 {
5858 for (const auto &emd : *enumMn)
5859 {
5860 found = emd->isStrong() && md->getEnumScope()==emd.get();
5861 if (found)
5862 {
5863 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,nullptr,FALSE,root->spec);
5864 break;
5865 }
5866 }
5867 }
5868 }
5869 if (found)
5870 {
5871 break;
5872 }
5873 }
5874 else if (nd==nullptr && md->isEnumValue()) // md part of global strong enum?
5875 {
5876 MemberName *enumMn=Doxygen::functionNameLinkedMap->find(namespaceName);
5877 if (enumMn)
5878 {
5879 for (const auto &emd : *enumMn)
5880 {
5881 found = emd->isStrong() && md->getEnumScope()==emd.get();
5882 if (found)
5883 {
5884 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,nullptr,FALSE,root->spec);
5885 break;
5886 }
5887 }
5888 }
5889 }
5890
5891 const FileDef *fd=root->fileDef();
5892 //printf("File %s\n",fd ? qPrint(fd->name()) : "<none>");
5894 if (fd)
5895 {
5896 nl = fd->getUsedNamespaces();
5897 }
5898 //printf("NamespaceList %p\n",nl);
5899
5900 // search in the list of namespaces that are imported via a
5901 // using declaration
5902 bool viaUsingDirective = nd && nl.find(nd->qualifiedName())!=nullptr;
5903
5904 if ((namespaceName.isEmpty() && nd==nullptr) || // not in a namespace
5905 (nd && nd->name()==namespaceName) || // or in the same namespace
5906 viaUsingDirective // member in 'using' namespace
5907 )
5908 {
5909 AUTO_TRACE_ADD("Try to add member '{}' to scope '{}'",md->name(),namespaceName);
5910
5911 NamespaceDef *rnd = nullptr;
5912 if (!namespaceName.isEmpty()) rnd = Doxygen::namespaceLinkedMap->find(namespaceName);
5913
5914 const ArgumentList &mdAl = md.get()->argumentList();
5915 bool matching=
5916 (mdAl.empty() && root->argList.empty()) ||
5917 md->isVariable() || md->isTypedef() || /* in case of function pointers */
5918 matchArguments2(md->getOuterScope(),md->getFileDef(),md->typeString(),&mdAl,
5919 rnd ? rnd : Doxygen::globalScope,fd,root->type,&root->argList,
5920 FALSE,root->lang);
5921
5922 // for template members we need to check if the number of
5923 // template arguments is the same, otherwise we are dealing with
5924 // different functions.
5925 if (matching && !root->tArgLists.empty())
5926 {
5927 const ArgumentList &mdTempl = md->templateArguments();
5928 if (root->tArgLists.back().size()!=mdTempl.size())
5929 {
5930 matching=FALSE;
5931 }
5932 }
5933
5934 //printf("%s<->%s\n",
5935 // qPrint(argListToString(md->argumentList())),
5936 // qPrint(argListToString(root->argList)));
5937
5938 // For static members we also check if the comment block was found in
5939 // the same file. This is needed because static members with the same
5940 // name can be in different files. Thus it would be wrong to just
5941 // put the comment block at the first syntactically matching member. If
5942 // the comment block belongs to a group of the static member, then add
5943 // the documentation even if it is in a different file.
5944 if (matching && md->isStatic() &&
5945 md->getDefFileName()!=root->fileName &&
5946 mn->size()>1 &&
5947 !isEntryInGroupOfMember(root,md.get()))
5948 {
5949 matching = FALSE;
5950 }
5951
5952 // for template member we also need to check the return type and requires
5953 if (!md->templateArguments().empty() && !root->tArgLists.empty())
5954 {
5955 //printf("Comparing return types '%s'<->'%s'\n",
5956 // md->typeString(),type);
5957 //printf("%s: Comparing '%s'<=>'%s'\n",qPrint(md->name()),qPrint(md->requiresClause()),qPrint(root->req));
5958 if (md->templateArguments().size()!=root->tArgLists.back().size() ||
5959 md->typeString()!=type ||
5960 md->requiresClause()!=root->req)
5961 {
5962 //printf(" ---> no matching\n");
5963 matching = FALSE;
5964 }
5965 }
5966
5967 if (matching) // add docs to the member
5968 {
5969 AUTO_TRACE_ADD("Match found");
5970 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,&root->argList,FALSE,root->spec);
5971 found=TRUE;
5972 break;
5973 }
5974 }
5975 }
5976 if (!found && root->relatesType!=RelatesType::Duplicate && root->section.isFunction()) // no match
5977 {
5978 QCString fullFuncDecl=decl;
5979 if (!root->argList.empty()) fullFuncDecl+=argListToString(root->argList,TRUE);
5980 QCString warnMsg = "no matching file member found for \n"+fullFuncDecl;
5981 if (mn->size()>0)
5982 {
5983 warnMsg+="\nPossible candidates:";
5984 for (const auto &md : *mn)
5985 {
5986 warnMsg+="\n '";
5987 warnMsg+=replaceAnonymousScopes(md->declaration());
5988 warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
5989 }
5990 }
5991 warn(root->fileName,root->startLine, "{}", qPrint(warnMsg));
5992 }
5993 }
5994 else // got docs for an undefined member!
5995 {
5996 if (root->type!="friend class" &&
5997 root->type!="friend struct" &&
5998 root->type!="friend union" &&
5999 root->type!="friend" &&
6000 (!Config_getBool(TYPEDEF_HIDES_STRUCT) ||
6001 root->type.find("typedef ")==-1)
6002 )
6003 {
6004 warn(root->fileName,root->startLine,
6005 "documented symbol '{}' was not declared or defined.",qPrint(decl)
6006 );
6007 }
6008 }
6009 return TRUE;
6010}
6011
6013 const ArgumentLists &srcTempArgLists,
6014 const ArgumentLists &dstTempArgLists
6015 )
6016{
6017 auto srcIt = srcTempArgLists.begin();
6018 auto dstIt = dstTempArgLists.begin();
6019 while (srcIt!=srcTempArgLists.end() && dstIt!=dstTempArgLists.end())
6020 {
6021 if ((*srcIt).size()!=(*dstIt).size()) return TRUE;
6022 ++srcIt;
6023 ++dstIt;
6024 }
6025 return FALSE;
6026}
6027
6028static bool scopeIsTemplate(const Definition *d)
6029{
6030 bool result=FALSE;
6031 //printf("> scopeIsTemplate(%s)\n",qPrint(d?d->name():"null"));
6033 {
6034 auto cd = toClassDef(d);
6035 result = cd->templateArguments().hasParameters() || cd->templateMaster()!=nullptr ||
6037 }
6038 //printf("< scopeIsTemplate=%d\n",result);
6039 return result;
6040}
6041
6043 const ArgumentLists &srcTempArgLists,
6044 const ArgumentLists &dstTempArgLists,
6045 const std::string &src
6046 )
6047{
6048 std::string dst;
6049 static const reg::Ex re(R"(\a\w*)");
6050 reg::Iterator it(src,re);
6052 //printf("type=%s\n",qPrint(sa->type));
6053 size_t p=0;
6054 for (; it!=end ; ++it) // for each word in srcType
6055 {
6056 const auto &match = *it;
6057 size_t i = match.position();
6058 size_t l = match.length();
6059 bool found=FALSE;
6060 dst+=src.substr(p,i-p);
6061 std::string name=match.str();
6062
6063 auto srcIt = srcTempArgLists.begin();
6064 auto dstIt = dstTempArgLists.begin();
6065 while (srcIt!=srcTempArgLists.end() && !found)
6066 {
6067 const ArgumentList *tdAli = nullptr;
6068 std::vector<Argument>::const_iterator tdaIt;
6069 if (dstIt!=dstTempArgLists.end())
6070 {
6071 tdAli = &(*dstIt);
6072 tdaIt = tdAli->begin();
6073 ++dstIt;
6074 }
6075
6076 const ArgumentList &tsaLi = *srcIt;
6077 for (auto tsaIt = tsaLi.begin(); tsaIt!=tsaLi.end() && !found; ++tsaIt)
6078 {
6079 Argument tsa = *tsaIt;
6080 const Argument *tda = nullptr;
6081 if (tdAli && tdaIt!=tdAli->end())
6082 {
6083 tda = &(*tdaIt);
6084 ++tdaIt;
6085 }
6086 //if (tda) printf("tsa=%s|%s tda=%s|%s\n",
6087 // qPrint(tsa.type),qPrint(tsa.name),
6088 // qPrint(tda->type),qPrint(tda->name));
6089 if (name==tsa.name.str())
6090 {
6091 if (tda && tda->name.isEmpty())
6092 {
6093 QCString tdaName = tda->name;
6094 QCString tdaType = tda->type;
6095 int vc=0;
6096 if (tdaType.startsWith("class ")) vc=6;
6097 else if (tdaType.startsWith("typename ")) vc=9;
6098 if (vc>0) // convert type=="class T" to type=="class" name=="T"
6099 {
6100 tdaName = tdaType.mid(vc);
6101 }
6102 if (!tdaName.isEmpty())
6103 {
6104 name=tdaName.str(); // substitute
6105 found=TRUE;
6106 }
6107 }
6108 }
6109 }
6110
6111 //printf(" srcList='%s' dstList='%s faList='%s'\n",
6112 // qPrint(argListToString(srclali.current())),
6113 // qPrint(argListToString(dstlali.current())),
6114 // funcTempArgList ? qPrint(argListToString(funcTempArgList)) : "<none>");
6115 ++srcIt;
6116 }
6117 dst+=name;
6118 p=i+l;
6119 }
6120 dst+=src.substr(p);
6121 //printf(" substituteTemplatesInString(%s)=%s\n",
6122 // qPrint(src),qPrint(dst));
6123 return dst;
6124}
6125
6127 const ArgumentLists &srcTempArgLists,
6128 const ArgumentLists &dstTempArgLists,
6129 const ArgumentList &src,
6130 ArgumentList &dst
6131 )
6132{
6133 auto dstIt = dst.begin();
6134 for (const Argument &sa : src)
6135 {
6136 QCString dstType = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.type.str());
6137 QCString dstArray = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.array.str());
6138 if (dstIt == dst.end())
6139 {
6140 Argument da = sa;
6141 da.type = dstType;
6142 da.array = dstArray;
6143 dst.push_back(da);
6144 dstIt = dst.end();
6145 }
6146 else
6147 {
6148 Argument da = *dstIt;
6149 da.type = dstType;
6150 da.array = dstArray;
6151 ++dstIt;
6152 }
6153 }
6158 srcTempArgLists,dstTempArgLists,
6159 src.trailingReturnType().str()));
6160 dst.setIsDeleted(src.isDeleted());
6161 dst.setRefQualifier(src.refQualifier());
6162 dst.setNoParameters(src.noParameters());
6163 //printf("substituteTemplatesInArgList: replacing %s with %s\n",
6164 // qPrint(argListToString(src)),qPrint(argListToString(dst))
6165 // );
6166}
6167
6168//-------------------------------------------------------------------------------------------
6169
6170static void addLocalObjCMethod(const Entry *root,
6171 const QCString &scopeName,
6172 const QCString &funcType,const QCString &funcName,const QCString &funcArgs,
6173 const QCString &exceptions,const QCString &funcDecl,
6174 TypeSpecifier spec)
6175{
6176 AUTO_TRACE();
6177 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
6178 ClassDefMutable *cd=nullptr;
6179 if (Config_getBool(EXTRACT_LOCAL_METHODS) && (cd=getClassMutable(scopeName)))
6180 {
6181 AUTO_TRACE_ADD("Local objective C method '{}' scopeName='{}'",root->name,scopeName);
6182 auto md = createMemberDef(
6183 root->fileName,root->startLine,root->startColumn,
6184 funcType,funcName,funcArgs,exceptions,
6185 root->protection,root->virt,root->isStatic,Relationship::Member,
6187 auto mmd = toMemberDefMutable(md.get());
6188 mmd->setTagInfo(root->tagInfo());
6189 mmd->setLanguage(root->lang);
6190 mmd->setId(root->id);
6191 mmd->makeImplementationDetail();
6192 mmd->setMemberClass(cd);
6193 mmd->setDefinition(funcDecl);
6195 mmd->addQualifiers(root->qualifiers);
6196 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6197 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6198 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6199 mmd->setDocsForDefinition(!root->proto);
6200 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6201 mmd->addSectionsToDefinition(root->anchors);
6202 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6203 FileDef *fd=root->fileDef();
6204 mmd->setBodyDef(fd);
6205 mmd->setMemberSpecifiers(spec);
6206 mmd->setVhdlSpecifiers(root->vhdlSpec);
6207 mmd->setMemberGroupId(root->mGrpId);
6208 cd->insertMember(md.get());
6209 cd->insertUsedFile(fd);
6210 mmd->setRefItems(root->sli);
6211 mmd->setRequirementReferences(root->rqli);
6212
6214 mn->push_back(std::move(md));
6215 }
6216 else
6217 {
6218 // local objective C method found for class without interface
6219 }
6220}
6221
6222//-------------------------------------------------------------------------------------------
6223
6224static void addMemberFunction(const Entry *root,
6225 MemberName *mn,
6226 const QCString &scopeName,
6227 const QCString &namespaceName,
6228 const QCString &className,
6229 const QCString &funcTyp,
6230 const QCString &funcName,
6231 const QCString &funcArgs,
6232 const QCString &funcTempList,
6233 const QCString &exceptions,
6234 const QCString &type,
6235 const QCString &args,
6236 bool isFriend,
6237 TypeSpecifier spec,
6238 const QCString &relates,
6239 const QCString &funcDecl,
6240 bool overloaded,
6241 bool isFunc)
6242{
6243 AUTO_TRACE();
6244 QCString funcType = funcTyp;
6245 int count=0;
6246 int noMatchCount=0;
6247 bool memFound=FALSE;
6248 for (const auto &imd : *mn)
6249 {
6250 MemberDefMutable *md = toMemberDefMutable(imd.get());
6251 if (md==nullptr) continue;
6253 if (cd==nullptr) continue;
6254 //AUTO_TRACE_ADD("member definition found, scope needed='{}' scope='{}' args='{}' fileName='{}'",
6255 // scopeName, cd->name(), md->argsString(), root->fileName);
6256 FileDef *fd=root->fileDef();
6257 NamespaceDef *nd=nullptr;
6258 if (!namespaceName.isEmpty()) nd=getResolvedNamespace(namespaceName);
6259
6260 //printf("scopeName %s->%s\n",qPrint(scopeName),
6261 // qPrint(stripTemplateSpecifiersFromScope(scopeName,FALSE)));
6262
6263 // if the member we are searching for is an enum value that is part of
6264 // a "strong" enum, we need to look into the fields of the enum for a match
6265 int enumNamePos=0;
6266 if (md->isEnumValue() && (enumNamePos=className.findRev("::"))!=-1)
6267 {
6268 QCString enumName = className.mid(enumNamePos+2);
6269 QCString fullScope = className.left(enumNamePos);
6270 if (!namespaceName.isEmpty()) fullScope.prepend(namespaceName+"::");
6271 if (fullScope==cd->name())
6272 {
6273 MemberName *enumMn=Doxygen::memberNameLinkedMap->find(enumName);
6274 //printf("enumMn(%s)=%p\n",qPrint(className),(void*)enumMn);
6275 if (enumMn)
6276 {
6277 for (const auto &emd : *enumMn)
6278 {
6279 memFound = emd->isStrong() && md->getEnumScope()==emd.get();
6280 if (memFound)
6281 {
6282 addMemberDocs(root,md,funcDecl,nullptr,overloaded,spec);
6283 count++;
6284 }
6285 if (memFound) break;
6286 }
6287 }
6288 }
6289 }
6290 if (memFound) break;
6291
6292 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
6293 if (tcd==nullptr && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
6294 {
6295 // don't be fooled by anonymous scopes
6296 tcd=cd;
6297 }
6298 //printf("Looking for %s inside nd=%s result=%s cd=%s\n",
6299 // qPrint(scopeName),nd?qPrint(nd->name()):"<none>",tcd?qPrint(tcd->name()):"",qPrint(cd->name()));
6300
6301 if (cd && tcd==cd) // member's classes match
6302 {
6303 AUTO_TRACE_ADD("class definition '{}' found",cd->name());
6304
6305 // get the template parameter lists found at the member declaration
6306 ArgumentLists declTemplArgs = cd->getTemplateParameterLists();
6307 const ArgumentList &templAl = md->templateArguments();
6308 if (!templAl.empty())
6309 {
6310 declTemplArgs.push_back(templAl);
6311 }
6312
6313 // get the template parameter lists found at the member definition
6314 const ArgumentLists &defTemplArgs = root->tArgLists;
6315 //printf("defTemplArgs=%p\n",defTemplArgs);
6316
6317 // do we replace the decl argument lists with the def argument lists?
6318 bool substDone=false;
6319 ArgumentList argList;
6320
6321 /* substitute the occurrences of class template names in the
6322 * argument list before matching
6323 */
6324 const ArgumentList &mdAl = md->argumentList();
6325 if (declTemplArgs.size()>0 && declTemplArgs.size()==defTemplArgs.size())
6326 {
6327 /* the function definition has template arguments
6328 * and the class definition also has template arguments, so
6329 * we must substitute the template names of the class by that
6330 * of the function definition before matching.
6331 */
6332 substituteTemplatesInArgList(declTemplArgs,defTemplArgs,mdAl,argList);
6333
6334 substDone=TRUE;
6335 }
6336 else /* no template arguments, compare argument lists directly */
6337 {
6338 argList = mdAl;
6339 }
6340
6341 bool matching=
6342 md->isVariable() || md->isTypedef() || // needed for function pointers
6344 md->getClassDef(),md->getFileDef(),md->typeString(),&argList,
6345 cd,fd,root->type,&root->argList,
6346 TRUE,root->lang);
6347
6348 AUTO_TRACE_ADD("matching '{}'<=>'{}' className='{}' namespaceName='{}' result={}",
6349 argListToString(argList,TRUE),argListToString(root->argList,TRUE),className,namespaceName,matching);
6350
6351 if (md->getLanguage()==SrcLangExt::ObjC && md->isVariable() && root->section.isFunction())
6352 {
6353 matching = FALSE; // don't match methods and attributes with the same name
6354 }
6355
6356 // for template member we also need to check the return type
6357 if (!md->templateArguments().empty() && !root->tArgLists.empty())
6358 {
6359 QCString memType = md->typeString();
6360 memType.stripPrefix("static "); // see bug700696
6362 className+"::",""); // see bug700693 & bug732594
6364 className+"::",""); // see bug758900
6365 if (memType=="auto" && !argList.trailingReturnType().isEmpty())
6366 {
6367 memType = argList.trailingReturnType();
6368 memType.stripPrefix(" -> ");
6369 }
6370 if (funcType=="auto" && !root->argList.trailingReturnType().isEmpty())
6371 {
6372 funcType = root->argList.trailingReturnType();
6373 funcType.stripPrefix(" -> ");
6375 substDone=true;
6376 }
6377 AUTO_TRACE_ADD("Comparing return types '{}'<->'{}' #args {}<->{}",
6378 memType,funcType,md->templateArguments().size(),root->tArgLists.back().size());
6379 if (md->templateArguments().size()!=root->tArgLists.back().size() || memType!=funcType)
6380 {
6381 //printf(" ---> no matching\n");
6382 matching = FALSE;
6383 }
6384 }
6385 else if (defTemplArgs.size()>declTemplArgs.size())
6386 {
6387 AUTO_TRACE_ADD("Different number of template arguments {} vs {}",defTemplArgs.size(),declTemplArgs.size());
6388 // avoid matching a non-template function in a template class against a
6389 // template function with the same name and parameters, see issue #10184
6390 substDone = false;
6391 matching = false;
6392 }
6393 bool rootIsUserDoc = root->section.isMemberDoc();
6394 bool classIsTemplate = scopeIsTemplate(md->getClassDef());
6395 bool mdIsTemplate = md->templateArguments().hasParameters();
6396 bool classOrMdIsTemplate = mdIsTemplate || classIsTemplate;
6397 bool rootIsTemplate = !root->tArgLists.empty();
6398 //printf("classIsTemplate=%d mdIsTemplate=%d rootIsTemplate=%d\n",classIsTemplate,mdIsTemplate,rootIsTemplate);
6399 if (!rootIsUserDoc && // don't check out-of-line @fn references, see bug722457
6400 (mdIsTemplate || rootIsTemplate) && // either md or root is a template
6401 ((classOrMdIsTemplate && !rootIsTemplate) || (!classOrMdIsTemplate && rootIsTemplate))
6402 )
6403 {
6404 // Method with template return type does not match method without return type
6405 // even if the parameters are the same. See also bug709052
6406 AUTO_TRACE_ADD("Comparing return types: template v.s. non-template");
6407 matching = FALSE;
6408 }
6409
6410 AUTO_TRACE_ADD("Match results of matchArguments2='{}' substDone='{}'",matching,substDone);
6411
6412 if (substDone) // found a new argument list
6413 {
6414 if (matching) // replace member's argument list
6415 {
6417 md->moveArgumentList(std::make_unique<ArgumentList>(argList));
6418 }
6419 else // no match
6420 {
6421 if (!funcTempList.isEmpty() &&
6422 isSpecialization(declTemplArgs,defTemplArgs))
6423 {
6424 // check if we are dealing with a partial template
6425 // specialization. In this case we add it to the class
6426 // even though the member arguments do not match.
6427
6428 addMethodToClass(root,cd,type,md->name(),args,isFriend,
6429 md->protection(),md->isStatic(),md->virtualness(),spec,relates);
6430 return;
6431 }
6432 }
6433 }
6434 if (matching)
6435 {
6436 addMemberDocs(root,md,funcDecl,nullptr,overloaded,spec);
6437 count++;
6438 memFound=TRUE;
6439 }
6440 }
6441 else if (cd && cd!=tcd) // we did find a class with the same name as cd
6442 // but in a different namespace
6443 {
6444 noMatchCount++;
6445 }
6446
6447 if (memFound) break;
6448 }
6449 if (count==0 && root->parent() && root->parent()->section.isObjcImpl())
6450 {
6451 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
6452 return;
6453 }
6454 if (count==0 && !(isFriend && funcType=="class"))
6455 {
6456 int candidates=0;
6457 const ClassDef *ecd = nullptr, *ucd = nullptr;
6458 MemberDef *emd = nullptr, *umd = nullptr;
6459 //printf("Assume template class\n");
6460 for (const auto &md : *mn)
6461 {
6462 MemberDef *cmd=md.get();
6464 ClassDefMutable *ccd=cdmdm ? cdmdm->getClassDefMutable() : nullptr;
6465 //printf("ccd->name()==%s className=%s\n",qPrint(ccd->name()),qPrint(className));
6466 if (ccd!=nullptr && rightScopeMatch(ccd->name(),className))
6467 {
6468 const ArgumentList &templAl = md->templateArguments();
6469 if (!root->tArgLists.empty() && !templAl.empty() &&
6470 root->tArgLists.back().size()<=templAl.size())
6471 {
6472 AUTO_TRACE_ADD("add template specialization");
6473 addMethodToClass(root,ccd,type,md->name(),args,isFriend,
6474 root->protection,root->isStatic,root->virt,spec,relates);
6475 return;
6476 }
6477 if (argListToString(md->argumentList(),FALSE,FALSE) ==
6479 { // exact argument list match -> remember
6480 ucd = ecd = ccd;
6481 umd = emd = cmd;
6482 AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': exact match",
6483 className,ccd->name(),md->argsString());
6484 }
6485 else // arguments do not match, but member name and scope do -> remember
6486 {
6487 ucd = ccd;
6488 umd = cmd;
6489 AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': no match",
6490 className,ccd->name(),md->argsString());
6491 }
6492 candidates++;
6493 }
6494 }
6495 bool strictProtoMatching = Config_getBool(STRICT_PROTO_MATCHING);
6496 if (!strictProtoMatching)
6497 {
6498 if (candidates==1 && ucd && umd)
6499 {
6500 // we didn't find an actual match on argument lists, but there is only 1 member with this
6501 // name in the same scope, so that has to be the one.
6502 addMemberDocs(root,toMemberDefMutable(umd),funcDecl,nullptr,overloaded,spec);
6503 return;
6504 }
6505 else if (candidates>1 && ecd && emd)
6506 {
6507 // we didn't find a unique match using type resolution,
6508 // but one of the matches has the exact same signature so
6509 // we take that one.
6510 addMemberDocs(root,toMemberDefMutable(emd),funcDecl,nullptr,overloaded,spec);
6511 return;
6512 }
6513 }
6514
6515 QCString warnMsg = "no ";
6516 if (noMatchCount>1) warnMsg+="uniquely ";
6517 warnMsg+="matching class member found for \n";
6518
6519 for (const ArgumentList &al : root->tArgLists)
6520 {
6521 warnMsg+=" template ";
6522 warnMsg+=tempArgListToString(al,root->lang);
6523 warnMsg+='\n';
6524 }
6525
6526 QCString fullFuncDecl=funcDecl;
6527 if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
6528
6529 warnMsg+=" ";
6530 warnMsg+=fullFuncDecl;
6531
6532 if (candidates>0 || noMatchCount>=1)
6533 {
6534 warnMsg+="\nPossible candidates:";
6535
6536 NamespaceDef *nd=nullptr;
6537 if (!namespaceName.isEmpty()) nd=getResolvedNamespace(namespaceName);
6538 FileDef *fd=root->fileDef();
6539
6540 for (const auto &md : *mn)
6541 {
6542 const ClassDef *cd=md->getClassDef();
6543 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
6544 if (tcd==nullptr && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
6545 {
6546 // don't be fooled by anonymous scopes
6547 tcd=cd;
6548 }
6549 if (cd!=nullptr && (rightScopeMatch(cd->name(),className) || (cd!=tcd)))
6550 {
6551 warnMsg+='\n';
6552 const ArgumentList &templAl = md->templateArguments();
6553 warnMsg+=" '";
6554 if (templAl.hasParameters())
6555 {
6556 warnMsg+="template ";
6557 warnMsg+=tempArgListToString(templAl,root->lang);
6558 warnMsg+='\n';
6559 warnMsg+=" ";
6560 }
6561 if (!md->typeString().isEmpty())
6562 {
6563 warnMsg+=md->typeString();
6564 warnMsg+=' ';
6565 }
6567 if (!qScope.isEmpty())
6568 warnMsg+=qScope+"::"+md->name();
6569 warnMsg+=md->argsString();
6570 warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
6571 }
6572 }
6573 }
6574 warn(root->fileName,root->startLine,"{}",warnMsg);
6575 }
6576}
6577
6578//-------------------------------------------------------------------------------------------
6579
6580static void addMemberSpecialization(const Entry *root,
6581 MemberName *mn,
6582 ClassDefMutable *cd,
6583 const QCString &funcType,
6584 const QCString &funcName,
6585 const QCString &funcArgs,
6586 const QCString &funcDecl,
6587 const QCString &exceptions,
6588 TypeSpecifier spec
6589 )
6590{
6591 AUTO_TRACE("funcType={} funcName={} funcArgs={} funcDecl={} spec={}",funcType,funcName,funcArgs,funcDecl,spec);
6592 MemberDef *declMd=nullptr;
6593 for (const auto &md : *mn)
6594 {
6595 if (md->getClassDef()==cd)
6596 {
6597 // TODO: we should probably also check for matching arguments
6598 declMd = md.get();
6599 break;
6600 }
6601 }
6603 ArgumentList tArgList;
6604 // getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6605 auto md = createMemberDef(
6606 root->fileName,root->startLine,root->startColumn,
6607 funcType,funcName,funcArgs,exceptions,
6608 declMd ? declMd->protection() : root->protection,
6609 root->virt,root->isStatic,Relationship::Member,
6610 mtype,tArgList,root->argList,root->metaData);
6611 auto mmd = toMemberDefMutable(md.get());
6612 //printf("new specialized member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6613 mmd->setTagInfo(root->tagInfo());
6614 mmd->setLanguage(root->lang);
6615 mmd->setId(root->id);
6616 mmd->setMemberClass(cd);
6617 mmd->setTemplateSpecialization(TRUE);
6618 mmd->setTypeConstraints(root->typeConstr);
6619 mmd->setDefinition(funcDecl);
6621 mmd->addQualifiers(root->qualifiers);
6622 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6623 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6624 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6625 mmd->setDocsForDefinition(!root->proto);
6626 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6627 mmd->addSectionsToDefinition(root->anchors);
6628 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6629 FileDef *fd=root->fileDef();
6630 mmd->setBodyDef(fd);
6631 mmd->setMemberSpecifiers(spec);
6632 mmd->setVhdlSpecifiers(root->vhdlSpec);
6633 mmd->setMemberGroupId(root->mGrpId);
6634 cd->insertMember(md.get());
6635 mmd->setRefItems(root->sli);
6636 mmd->setRequirementReferences(root->rqli);
6637
6638 mn->push_back(std::move(md));
6639}
6640
6641//-------------------------------------------------------------------------------------------
6642
6643static void addOverloaded(const Entry *root,MemberName *mn,
6644 const QCString &funcType,const QCString &funcName,const QCString &funcArgs,
6645 const QCString &funcDecl,const QCString &exceptions,TypeSpecifier spec)
6646{
6647 // for unique overloaded member we allow the class to be
6648 // omitted, this is to be Qt compatible. Using this should
6649 // however be avoided, because it is error prone
6650 bool sameClass=false;
6651 if (mn->size()>0)
6652 {
6653 // check if all members with the same name are also in the same class
6654 sameClass = std::equal(mn->begin()+1,mn->end(),mn->begin(),
6655 [](const auto &md1,const auto &md2)
6656 { return md1->getClassDef()->name()==md2->getClassDef()->name(); });
6657 }
6658 if (sameClass)
6659 {
6660 MemberDefMutable *mdm = toMemberDefMutable(mn->front().get());
6661 ClassDefMutable *cd = mdm ? mdm->getClassDefMutable() : nullptr;
6662 if (cd==nullptr) return;
6663
6665 if (root->mtype==MethodTypes::Signal) mtype=MemberType::Signal;
6666 else if (root->mtype==MethodTypes::Slot) mtype=MemberType::Slot;
6667 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType::DCOP;
6668
6669 // new overloaded member function
6670 std::unique_ptr<ArgumentList> tArgList =
6671 getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6672 //printf("new related member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6673 auto md = createMemberDef(
6674 root->fileName,root->startLine,root->startColumn,
6675 funcType,funcName,funcArgs,exceptions,
6676 root->protection,root->virt,root->isStatic,Relationship::Related,
6677 mtype,tArgList ? *tArgList : ArgumentList(),root->argList,root->metaData);
6678 auto mmd = toMemberDefMutable(md.get());
6679 mmd->setTagInfo(root->tagInfo());
6680 mmd->setLanguage(root->lang);
6681 mmd->setId(root->id);
6682 mmd->setTypeConstraints(root->typeConstr);
6683 mmd->setMemberClass(cd);
6684 mmd->setDefinition(funcDecl);
6686 mmd->addQualifiers(root->qualifiers);
6688 doc+="<p>";
6689 doc+=root->doc;
6690 mmd->setDocumentation(doc,root->docFile,root->docLine);
6691 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6692 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6693 mmd->setDocsForDefinition(!root->proto);
6694 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6695 mmd->addSectionsToDefinition(root->anchors);
6696 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6697 FileDef *fd=root->fileDef();
6698 mmd->setBodyDef(fd);
6699 mmd->setMemberSpecifiers(spec);
6700 mmd->setVhdlSpecifiers(root->vhdlSpec);
6701 mmd->setMemberGroupId(root->mGrpId);
6702 cd->insertMember(md.get());
6703 cd->insertUsedFile(fd);
6704 mmd->setRefItems(root->sli);
6705 mmd->setRequirementReferences(root->rqli);
6706
6707 mn->push_back(std::move(md));
6708 }
6709}
6710
6711static void insertMemberAlias(Definition *outerScope,const MemberDef *md)
6712{
6713 if (outerScope && outerScope!=Doxygen::globalScope)
6714 {
6715 auto aliasMd = createMemberDefAlias(outerScope,md);
6716 if (outerScope->definitionType()==Definition::TypeClass)
6717 {
6718 ClassDefMutable *cdm = toClassDefMutable(outerScope);
6719 if (cdm)
6720 {
6721 cdm->insertMember(aliasMd.get());
6722 }
6723 }
6724 else if (outerScope->definitionType()==Definition::TypeNamespace)
6725 {
6726 NamespaceDefMutable *ndm = toNamespaceDefMutable(outerScope);
6727 if (ndm)
6728 {
6729 ndm->insertMember(aliasMd.get());
6730 }
6731 }
6732 else if (outerScope->definitionType()==Definition::TypeFile)
6733 {
6734 toFileDef(outerScope)->insertMember(aliasMd.get());
6735 }
6736 if (aliasMd)
6737 {
6738 Doxygen::functionNameLinkedMap->add(md->name())->push_back(std::move(aliasMd));
6739 }
6740 }
6741}
6742
6743//-------------------------------------------------------------------------------------------
6744
6745/*! This function tries to find a member (in a documented class/file/namespace)
6746 * that corresponds to the function/variable declaration given in \a funcDecl.
6747 *
6748 * The boolean \a overloaded is used to specify whether or not a standard
6749 * overload documentation line should be generated.
6750 *
6751 * The boolean \a isFunc is a hint that indicates that this is a function
6752 * instead of a variable or typedef.
6753 */
6754static void findMember(const Entry *root,
6755 const QCString &relates,
6756 const QCString &type,
6757 const QCString &args,
6758 QCString funcDecl,
6759 bool overloaded,
6760 bool isFunc
6761 )
6762{
6763 AUTO_TRACE("root='{}' funcDecl='{}' related='{}' overload={} isFunc={} mGrpId={} #tArgList={} spec={} lang={}",
6764 root->name, funcDecl, relates, overloaded, isFunc, root->mGrpId, root->tArgLists.size(),
6765 root->spec, root->lang);
6766
6767 QCString scopeName;
6768 QCString className;
6769 QCString namespaceName;
6770 QCString funcType;
6771 QCString funcName;
6772 QCString funcArgs;
6773 QCString funcTempList;
6774 QCString exceptions;
6775 QCString funcSpec;
6776 bool isRelated=false;
6777 bool isMemberOf=false;
6778 bool isFriend=false;
6779 bool done=false;
6780 TypeSpecifier spec = root->spec;
6781 while (!done)
6782 {
6783 done=true;
6784 if (funcDecl.stripPrefix("friend ")) // treat friends as related members
6785 {
6786 isFriend=true;
6787 done=false;
6788 }
6789 if (funcDecl.stripPrefix("inline "))
6790 {
6791 spec.setInline(true);
6792 done=false;
6793 }
6794 if (funcDecl.stripPrefix("explicit "))
6795 {
6796 spec.setExplicit(true);
6797 done=false;
6798 }
6799 if (funcDecl.stripPrefix("mutable "))
6800 {
6801 spec.setMutable(true);
6802 done=false;
6803 }
6804 if (funcDecl.stripPrefix("thread_local "))
6805 {
6806 spec.setThreadLocal(true);
6807 done=false;
6808 }
6809 if (funcDecl.stripPrefix("virtual "))
6810 {
6811 done=false;
6812 }
6813 }
6814
6815 // delete any ; from the function declaration
6816 int sep=0;
6817 while ((sep=funcDecl.find(';'))!=-1)
6818 {
6819 funcDecl=(funcDecl.left(sep)+funcDecl.right(funcDecl.length()-sep-1)).stripWhiteSpace();
6820 }
6821
6822 // make sure the first character is a space to simplify searching.
6823 if (!funcDecl.isEmpty() && funcDecl[0]!=' ') funcDecl.prepend(" ");
6824
6825 // remove some superfluous spaces
6826 funcDecl= substitute(
6827 substitute(
6828 substitute(funcDecl,"~ ","~"),
6829 ":: ","::"
6830 ),
6831 " ::","::"
6832 ).stripWhiteSpace();
6833
6834 //printf("funcDecl='%s'\n",qPrint(funcDecl));
6835 if (isFriend && funcDecl.startsWith("class "))
6836 {
6837 //printf("friend class\n");
6838 funcDecl=funcDecl.right(funcDecl.length()-6);
6839 funcName = funcDecl;
6840 }
6841 else if (isFriend && funcDecl.startsWith("struct "))
6842 {
6843 funcDecl=funcDecl.right(funcDecl.length()-7);
6844 funcName = funcDecl;
6845 }
6846 else
6847 {
6848 // extract information from the declarations
6849 parseFuncDecl(funcDecl,root->lang,scopeName,funcType,funcName,
6850 funcArgs,funcTempList,exceptions
6851 );
6852 }
6853
6854 // the class name can also be a namespace name, we decide this later.
6855 // if a related class name is specified and the class name could
6856 // not be derived from the function declaration, then use the
6857 // related field.
6858 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}' funcType='{}' funcName='{}' funcArgs='{}'",
6859 scopeName,className,namespaceName,funcType,funcName,funcArgs);
6860 if (!relates.isEmpty())
6861 { // related member, prefix user specified scope
6862 isRelated=TRUE;
6863 isMemberOf=(root->relatesType == RelatesType::MemberOf);
6864 if (getClass(relates)==nullptr && !scopeName.isEmpty())
6865 {
6866 scopeName= mergeScopes(scopeName,relates);
6867 }
6868 else
6869 {
6870 scopeName = relates;
6871 }
6872 }
6873
6874 if (relates.isEmpty() && root->parent() &&
6875 (root->parent()->section.isScope() || root->parent()->section.isObjcImpl()) &&
6876 !root->parent()->name.isEmpty()) // see if we can combine scopeName
6877 // with the scope in which it was found
6878 {
6879 QCString joinedName = root->parent()->name+"::"+scopeName;
6880 if (!scopeName.isEmpty() &&
6881 (getClass(joinedName) || Doxygen::namespaceLinkedMap->find(joinedName)))
6882 {
6883 scopeName = joinedName;
6884 }
6885 else
6886 {
6887 scopeName = mergeScopes(root->parent()->name,scopeName);
6888 }
6889 }
6890 else // see if we can prefix a namespace or class that is used from the file
6891 {
6892 FileDef *fd=root->fileDef();
6893 if (fd)
6894 {
6895 for (const auto &fnd : fd->getUsedNamespaces())
6896 {
6897 QCString joinedName = fnd->name()+"::"+scopeName;
6898 if (Doxygen::namespaceLinkedMap->find(joinedName))
6899 {
6900 scopeName=joinedName;
6901 break;
6902 }
6903 }
6904 }
6905 }
6907 removeRedundantWhiteSpace(scopeName),false,&funcSpec,QCString(),false);
6908
6909 // funcSpec contains the last template specifiers of the given scope.
6910 // If this method does not have any template arguments or they are
6911 // empty while funcSpec is not empty we assume this is a
6912 // specialization of a method. If not, we clear the funcSpec and treat
6913 // this as a normal method of a template class.
6914 if (!(root->tArgLists.size()>0 &&
6915 root->tArgLists.front().size()==0
6916 )
6917 )
6918 {
6919 funcSpec.clear();
6920 }
6921
6922 //namespaceName=removeAnonymousScopes(namespaceName);
6923 if (!Config_getBool(EXTRACT_ANON_NSPACES) && scopeName.find('@')!=-1) return; // skip stuff in anonymous namespace...
6924
6925 // split scope into a namespace and a class part
6926 extractNamespaceName(scopeName,className,namespaceName,TRUE);
6927 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}'",scopeName,className,namespaceName);
6928
6929 //printf("namespaceName='%s' className='%s'\n",qPrint(namespaceName),qPrint(className));
6930 // merge class and namespace scopes again
6931 scopeName.clear();
6932 if (!namespaceName.isEmpty())
6933 {
6934 if (className.isEmpty())
6935 {
6936 scopeName=namespaceName;
6937 }
6938 else if (!relates.isEmpty() || // relates command with explicit scope
6939 !getClass(className)) // class name only exists in a namespace
6940 {
6941 scopeName=namespaceName+"::"+className;
6942 }
6943 else
6944 {
6945 scopeName=className;
6946 }
6947 }
6948 else if (!className.isEmpty())
6949 {
6950 scopeName=className;
6951 }
6952 //printf("new scope='%s'\n",qPrint(scopeName));
6953
6954 QCString tempScopeName=scopeName;
6955 ClassDefMutable *cd=getClassMutable(scopeName);
6956 if (cd)
6957 {
6958 if (funcSpec.isEmpty())
6959 {
6960 uint32_t argListIndex=0;
6961 tempScopeName=cd->qualifiedNameWithTemplateParameters(&root->tArgLists,&argListIndex);
6962 }
6963 else
6964 {
6965 tempScopeName=scopeName+funcSpec;
6966 }
6967 }
6968 //printf("scopeName=%s cd=%p root->tArgLists=%p result=%s\n",
6969 // qPrint(scopeName),cd,root->tArgLists,qPrint(tempScopeName));
6970
6971 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
6972 // rebuild the function declaration (needed to get the scope right).
6973 if (!scopeName.isEmpty() && !isRelated && !isFriend && !Config_getBool(HIDE_SCOPE_NAMES) && root->lang!=SrcLangExt::Python)
6974 {
6975 if (!funcType.isEmpty())
6976 {
6977 if (isFunc) // a function -> we use argList for the arguments
6978 {
6979 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcTempList;
6980 }
6981 else
6982 {
6983 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcArgs;
6984 }
6985 }
6986 else
6987 {
6988 if (isFunc) // a function => we use argList for the arguments
6989 {
6990 funcDecl=tempScopeName+"::"+funcName+funcTempList;
6991 }
6992 else // variable => add 'argument' list
6993 {
6994 funcDecl=tempScopeName+"::"+funcName+funcArgs;
6995 }
6996 }
6997 }
6998 else // build declaration without scope
6999 {
7000 if (!funcType.isEmpty()) // but with a type
7001 {
7002 if (isFunc) // function => omit argument list
7003 {
7004 funcDecl=funcType+" "+funcName+funcTempList;
7005 }
7006 else // variable => add 'argument' list
7007 {
7008 funcDecl=funcType+" "+funcName+funcArgs;
7009 }
7010 }
7011 else // no type
7012 {
7013 if (isFunc)
7014 {
7015 funcDecl=funcName+funcTempList;
7016 }
7017 else
7018 {
7019 funcDecl=funcName+funcArgs;
7020 }
7021 }
7022 }
7023
7024 if (funcType=="template class" && !funcTempList.isEmpty())
7025 return; // ignore explicit template instantiations
7026
7027 AUTO_TRACE_ADD("Parse results: namespaceName='{}' className=`{}` funcType='{}' funcSpec='{}' "
7028 " funcName='{}' funcArgs='{}' funcTempList='{}' funcDecl='{}' relates='{}'"
7029 " exceptions='{}' isRelated={} isMemberOf={} isFriend={} isFunc={}",
7030 namespaceName, className, funcType, funcSpec,
7031 funcName, funcArgs, funcTempList, funcDecl, relates,
7032 exceptions, isRelated, isMemberOf, isFriend, isFunc);
7033
7034 if (!funcName.isEmpty()) // function name is valid
7035 {
7036 // check if 'className' is actually a scoped enum, in which case we need to
7037 // process it as a global, see issue #6471
7038 bool strongEnum = false;
7039 MemberName *mn=nullptr;
7040 if (!className.isEmpty() && (mn=Doxygen::functionNameLinkedMap->find(className)))
7041 {
7042 for (const auto &imd : *mn)
7043 {
7044 MemberDefMutable *md = toMemberDefMutable(imd.get());
7045 Definition *mdScope = nullptr;
7046 if (md && md->isEnumerate() && md->isStrong() && (mdScope=md->getOuterScope()) &&
7047 // need filter for the correct scope, see issue #9668
7048 ((namespaceName.isEmpty() && mdScope==Doxygen::globalScope) || (mdScope->name()==namespaceName)))
7049 {
7050 AUTO_TRACE_ADD("'{}' is a strong enum! (namespace={} md->getOuterScope()->name()={})",md->name(),namespaceName,md->getOuterScope()->name());
7051 strongEnum = true;
7052 // pass the scope name name as a 'namespace' to the findGlobalMember function
7053 if (!namespaceName.isEmpty())
7054 {
7055 namespaceName+="::"+className;
7056 }
7057 else
7058 {
7059 namespaceName=className;
7060 }
7061 }
7062 }
7063 }
7064
7065 if (funcName.startsWith("operator ")) // strip class scope from cast operator
7066 {
7067 funcName = substitute(funcName,className+"::","");
7068 }
7069 mn = nullptr;
7070 if (!funcTempList.isEmpty()) // try with member specialization
7071 {
7072 mn=Doxygen::memberNameLinkedMap->find(funcName+funcTempList);
7073 }
7074 if (mn==nullptr) // try without specialization
7075 {
7076 mn=Doxygen::memberNameLinkedMap->find(funcName);
7077 }
7078 if (!isRelated && !strongEnum && mn) // function name already found
7079 {
7080 AUTO_TRACE_ADD("member name exists ({} members with this name)",mn->size());
7081 if (!className.isEmpty()) // class name is valid
7082 {
7083 if (funcSpec.isEmpty()) // not a member specialization
7084 {
7085 addMemberFunction(root,mn,scopeName,namespaceName,className,funcType,funcName,
7086 funcArgs,funcTempList,exceptions,
7087 type,args,isFriend,spec,relates,funcDecl,overloaded,isFunc);
7088 }
7089 else if (cd) // member specialization
7090 {
7091 addMemberSpecialization(root,mn,cd,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
7092 }
7093 else
7094 {
7095 //printf("*** Specialized member %s of unknown scope %s%s found!\n",
7096 // qPrint(scopeName),qPrint(funcName),qPrint(funcArgs));
7097 }
7098 }
7099 else if (overloaded) // check if the function belongs to only one class
7100 {
7101 addOverloaded(root,mn,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
7102 }
7103 else // unrelated function with the same name as a member
7104 {
7105 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
7106 {
7107 QCString fullFuncDecl=funcDecl;
7108 if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
7109 warn(root->fileName,root->startLine,
7110 "Cannot determine class for function\n{}",
7111 fullFuncDecl
7112 );
7113 }
7114 }
7115 }
7116 else if (isRelated && !relates.isEmpty())
7117 {
7118 AUTO_TRACE_ADD("related function scopeName='{}' className='{}'",scopeName,className);
7119 if (className.isEmpty()) className=relates;
7120 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
7121 if ((cd=getClassMutable(scopeName)))
7122 {
7123 bool newMember=TRUE; // assume we have a new member
7124 MemberDefMutable *mdDefine=nullptr;
7125 {
7126 mn = Doxygen::functionNameLinkedMap->find(funcName);
7127 if (mn)
7128 {
7129 for (const auto &imd : *mn)
7130 {
7131 MemberDefMutable *md = toMemberDefMutable(imd.get());
7132 if (md && md->isDefine())
7133 {
7134 mdDefine = md;
7135 break;
7136 }
7137 }
7138 }
7139 }
7140
7141 if (mdDefine) // macro definition is already created by the preprocessor and inserted as a file member
7142 {
7143 //printf("moving #define %s into class %s\n",qPrint(mdDefine->name()),qPrint(cd->name()));
7144
7145 // take mdDefine from the Doxygen::functionNameLinkedMap (without deleting the data)
7146 auto mdDefineTaken = Doxygen::functionNameLinkedMap->take(funcName,mdDefine);
7147 // insert it as a class member
7148 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==nullptr)
7149 {
7150 mn=Doxygen::memberNameLinkedMap->add(funcName);
7151 }
7152
7153 if (mdDefine->getFileDef())
7154 {
7155 mdDefine->getFileDef()->removeMember(mdDefine);
7156 }
7157 mdDefine->makeRelated();
7158 mdDefine->setMemberClass(cd);
7159 mdDefine->moveTo(cd);
7160 cd->insertMember(mdDefine);
7161 // also insert the member as an alias in the parent's scope, so it can be referenced also without cd's scope
7162 insertMemberAlias(cd->getOuterScope(),mdDefine);
7163 mn->push_back(std::move(mdDefineTaken));
7164 }
7165 else // normal member, needs to be created and added to the class
7166 {
7167 FileDef *fd=root->fileDef();
7168
7169 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==nullptr)
7170 {
7171 mn=Doxygen::memberNameLinkedMap->add(funcName);
7172 }
7173 else
7174 {
7175 // see if we got another member with matching arguments
7176 MemberDefMutable *rmd_found = nullptr;
7177 for (const auto &irmd : *mn)
7178 {
7179 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
7180 if (rmd)
7181 {
7182 const ArgumentList &rmdAl = rmd->argumentList();
7183
7184 newMember=
7185 className!=rmd->getOuterScope()->name() ||
7186 !matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmdAl,
7187 cd,fd,root->type,&root->argList,
7188 TRUE,root->lang);
7189 if (!newMember)
7190 {
7191 rmd_found = rmd;
7192 }
7193 }
7194 }
7195 if (rmd_found) // member already exists as rmd -> add docs
7196 {
7197 AUTO_TRACE_ADD("addMemberDocs for related member {}",root->name);
7198 addMemberDocs(root,rmd_found,funcDecl,nullptr,overloaded,spec);
7199 newMember=false;
7200 }
7201 }
7202
7203 if (newMember) // need to create a new member
7204 {
7206 switch (root->mtype)
7207 {
7208 case MethodTypes::Method: mtype = MemberType::Function; break;
7209 case MethodTypes::Signal: mtype = MemberType::Signal; break;
7210 case MethodTypes::Slot: mtype = MemberType::Slot; break;
7211 case MethodTypes::DCOP: mtype = MemberType::DCOP; break;
7212 case MethodTypes::Property: mtype = MemberType::Property; break;
7213 case MethodTypes::Event: mtype = MemberType::Event; break;
7214 }
7215
7216 //printf("New related name '%s' '%d'\n",qPrint(funcName),
7217 // root->argList ? (int)root->argList->count() : -1);
7218
7219 // first note that we pass:
7220 // (root->tArgLists ? root->tArgLists->last() : nullptr)
7221 // for the template arguments for the new "member."
7222 // this accurately reflects the template arguments of
7223 // the related function, which don't have to do with
7224 // those of the related class.
7225 auto md = createMemberDef(
7226 root->fileName,root->startLine,root->startColumn,
7227 funcType,funcName,funcArgs,exceptions,
7228 root->protection,root->virt,
7229 root->isStatic,
7230 isMemberOf ? Relationship::Foreign : Relationship::Related,
7231 mtype,
7232 (!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList()),
7233 funcArgs.isEmpty() ? ArgumentList() : root->argList,
7234 root->metaData);
7235 auto mmd = toMemberDefMutable(md.get());
7236
7237 // also insert the member as an alias in the parent's scope, so it can be referenced also without cd's scope
7238 insertMemberAlias(cd->getOuterScope(),md.get());
7239
7240 // we still have the problem that
7241 // MemberDef::writeDocumentation() in memberdef.cpp
7242 // writes the template argument list for the class,
7243 // as if this member is a member of the class.
7244 // fortunately, MemberDef::writeDocumentation() has
7245 // a special mechanism that allows us to totally
7246 // override the set of template argument lists that
7247 // are printed. We use that and set it to the
7248 // template argument lists of the related function.
7249 //
7250 mmd->setDefinitionTemplateParameterLists(root->tArgLists);
7251
7252 mmd->setTagInfo(root->tagInfo());
7253
7254 //printf("Related member name='%s' decl='%s' bodyLine='%d'\n",
7255 // qPrint(funcName),qPrint(funcDecl),root->bodyLine);
7256
7257 // try to find the matching line number of the body from the
7258 // global function list
7259 bool found=FALSE;
7260 if (root->bodyLine==-1)
7261 {
7262 MemberName *rmn=Doxygen::functionNameLinkedMap->find(funcName);
7263 if (rmn)
7264 {
7265 const MemberDefMutable *rmd_found=nullptr;
7266 for (const auto &irmd : *rmn)
7267 {
7268 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
7269 if (rmd)
7270 {
7271 const ArgumentList &rmdAl = rmd->argumentList();
7272 // check for matching argument lists
7273 if (
7274 matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmdAl,
7275 cd,fd,root->type,&root->argList,
7276 TRUE,root->lang)
7277 )
7278 {
7279 found=TRUE;
7280 rmd_found = rmd;
7281 break;
7282 }
7283 }
7284 }
7285 if (rmd_found) // member found -> copy line number info
7286 {
7287 mmd->setBodySegment(rmd_found->getDefLine(),rmd_found->getStartBodyLine(),rmd_found->getEndBodyLine());
7288 mmd->setBodyDef(rmd_found->getBodyDef());
7289 //md->setBodyMember(rmd);
7290 }
7291 }
7292 }
7293 if (!found) // line number could not be found or is available in this
7294 // entry
7295 {
7296 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7297 mmd->setBodyDef(fd);
7298 }
7299
7300 //if (root->mGrpId!=-1)
7301 //{
7302 // md->setMemberGroup(memberGroupDict[root->mGrpId]);
7303 //}
7304 mmd->setMemberClass(cd);
7305 mmd->setMemberSpecifiers(spec);
7306 mmd->setVhdlSpecifiers(root->vhdlSpec);
7307 mmd->setDefinition(funcDecl);
7309 mmd->addQualifiers(root->qualifiers);
7310 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7311 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7312 mmd->setDocsForDefinition(!root->proto);
7313 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
7314 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7315 mmd->addSectionsToDefinition(root->anchors);
7316 mmd->setMemberGroupId(root->mGrpId);
7317 mmd->setLanguage(root->lang);
7318 mmd->setId(root->id);
7319 //md->setMemberDefTemplateArguments(root->mtArgList);
7320 cd->insertMember(md.get());
7321 cd->insertUsedFile(fd);
7322 mmd->setRefItems(root->sli);
7323 mmd->setRequirementReferences(root->rqli);
7324 if (root->relatesType==RelatesType::Duplicate) mmd->setRelatedAlso(cd);
7325 addMemberToGroups(root,md.get());
7327 //printf("Adding member=%s\n",qPrint(md->name()));
7328 mn->push_back(std::move(md));
7329 }
7330 if (root->relatesType==RelatesType::Duplicate)
7331 {
7332 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
7333 {
7334 QCString fullFuncDecl=funcDecl;
7335 if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
7336 warn(root->fileName,root->startLine,
7337 "Cannot determine file/namespace for relatedalso function\n{}",
7338 fullFuncDecl
7339 );
7340 }
7341 }
7342 }
7343 }
7344 else
7345 {
7346 warn_undoc(root->fileName,root->startLine, "class '{}' for related function '{}' is not documented.", className,funcName);
7347 }
7348 }
7349 else if (root->parent() && root->parent()->section.isObjcImpl())
7350 {
7351 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
7352 }
7353 else // unrelated not overloaded member found
7354 {
7355 bool globMem = findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec);
7356 if (className.isEmpty() && !globMem)
7357 {
7358 warn(root->fileName,root->startLine, "class for member '{}' cannot be found.", funcName);
7359 }
7360 else if (!className.isEmpty() && !globMem)
7361 {
7362 warn(root->fileName,root->startLine,
7363 "member '{}' of class '{}' cannot be found",
7364 funcName,className);
7365 }
7366 }
7367 }
7368 else
7369 {
7370 // this should not be called
7371 warn(root->fileName,root->startLine,"member with no name found.");
7372 }
7373 return;
7374}
7375
7376//----------------------------------------------------------------------
7377// find the members corresponding to the different documentation blocks
7378// that are extracted from the sources.
7379
7380static void filterMemberDocumentation(const Entry *root,const QCString &relates)
7381{
7382 AUTO_TRACE("root->type='{}' root->inside='{}' root->name='{}' root->args='{}' section={} root->spec={} root->mGrpId={}",
7383 root->type,root->inside,root->name,root->args,root->section,root->spec,root->mGrpId);
7384 //printf("root->parent()->name=%s\n",qPrint(root->parent()->name));
7385 bool isFunc=TRUE;
7386
7387 QCString type = root->type;
7388 QCString args = root->args;
7389 int i=-1, l=0;
7390 if ( // detect func variable/typedef to func ptr
7391 (i=findFunctionPtr(type.str(),root->lang,&l))!=-1
7392 )
7393 {
7394 //printf("Fixing function pointer!\n");
7395 // fix type and argument
7396 args.prepend(type.right(type.length()-i-l));
7397 type=type.left(i+l);
7398 //printf("Results type=%s,name=%s,args=%s\n",qPrint(type),qPrint(root->name),qPrint(args));
7399 isFunc=FALSE;
7400 }
7401 else if ((type.startsWith("typedef ") && args.find('(')!=-1))
7402 // detect function types marked as functions
7403 {
7404 isFunc=FALSE;
7405 }
7406
7407 //printf("Member %s isFunc=%d\n",qPrint(root->name),isFunc);
7408 if (root->section.isMemberDoc())
7409 {
7410 //printf("Documentation for inline member '%s' found args='%s'\n",
7411 // qPrint(root->name),qPrint(args));
7412 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
7413 if (type.isEmpty())
7414 {
7415 findMember(root,
7416 relates,
7417 type,
7418 args,
7419 root->name + args + root->exception,
7420 FALSE,
7421 isFunc);
7422 }
7423 else
7424 {
7425 findMember(root,
7426 relates,
7427 type,
7428 args,
7429 type + " " + root->name + args + root->exception,
7430 FALSE,
7431 isFunc);
7432 }
7433 }
7434 else if (root->section.isOverloadDoc())
7435 {
7436 //printf("Overloaded member %s found\n",qPrint(root->name));
7437 findMember(root,
7438 relates,
7439 type,
7440 args,
7441 root->name,
7442 TRUE,
7443 isFunc);
7444 }
7445 else if
7446 ((root->section.isFunction() // function
7447 ||
7448 (root->section.isVariable() && // variable
7449 !type.isEmpty() && // with a type
7450 g_compoundKeywords.find(type.str())==g_compoundKeywords.end() // that is not a keyword
7451 // (to skip forward declaration of class etc.)
7452 )
7453 )
7454 )
7455 {
7456 //printf("Documentation for member '%s' found args='%s' excp='%s'\n",
7457 // qPrint(root->name),qPrint(args),qPrint(root->exception));
7458 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
7459 //printf("Inside=%s\n Relates=%s\n",qPrint(root->inside),qPrint(relates));
7460 if (type=="friend class" || type=="friend struct" ||
7461 type=="friend union")
7462 {
7463 findMember(root,
7464 relates,
7465 type,
7466 args,
7467 type+" "+root->name,
7468 FALSE,FALSE);
7469
7470 }
7471 else if (!type.isEmpty())
7472 {
7473 findMember(root,
7474 relates,
7475 type,
7476 args,
7477 type+" "+ root->inside + root->name + args + root->exception,
7478 FALSE,isFunc);
7479 }
7480 else
7481 {
7482 findMember(root,
7483 relates,
7484 type,
7485 args,
7486 root->inside + root->name + args + root->exception,
7487 FALSE,isFunc);
7488 }
7489 }
7490 else if (root->section.isDefine() && !relates.isEmpty())
7491 {
7492 findMember(root,
7493 relates,
7494 type,
7495 args,
7496 root->name + args,
7497 FALSE,
7498 !args.isEmpty());
7499 }
7500 else if (root->section.isVariableDoc())
7501 {
7502 //printf("Documentation for variable %s found\n",qPrint(root->name));
7503 //if (!relates.isEmpty()) printf(" Relates %s\n",qPrint(relates));
7504 findMember(root,
7505 relates,
7506 type,
7507 args,
7508 root->name,
7509 FALSE,
7510 FALSE);
7511 }
7512 else if (root->section.isExportedInterface() ||
7513 root->section.isIncludedService())
7514 {
7515 findMember(root,
7516 relates,
7517 type,
7518 args,
7519 type + " " + root->name,
7520 FALSE,
7521 FALSE);
7522 }
7523 else
7524 {
7525 // skip section
7526 //printf("skip section\n");
7527 }
7528}
7529
7530static void findMemberDocumentation(const Entry *root)
7531{
7532 if (root->section.isMemberDoc() ||
7533 root->section.isOverloadDoc() ||
7534 root->section.isFunction() ||
7535 root->section.isVariable() ||
7536 root->section.isVariableDoc() ||
7537 root->section.isDefine() ||
7538 root->section.isIncludedService() ||
7539 root->section.isExportedInterface()
7540 )
7541 {
7542 AUTO_TRACE();
7543 if (root->relatesType==RelatesType::Duplicate && !root->relates.isEmpty())
7544 {
7546 }
7548 }
7549 for (const auto &e : root->children())
7550 {
7551 if (!e->section.isEnum())
7552 {
7553 findMemberDocumentation(e.get());
7554 }
7555 }
7556}
7557
7558//----------------------------------------------------------------------
7559
7560static void findObjCMethodDefinitions(const Entry *root)
7561{
7562 AUTO_TRACE();
7563 for (const auto &objCImpl : root->children())
7564 {
7565 if (objCImpl->section.isObjcImpl())
7566 {
7567 for (const auto &objCMethod : objCImpl->children())
7568 {
7569 if (objCMethod->section.isFunction())
7570 {
7571 //printf(" Found ObjC method definition %s\n",qPrint(objCMethod->name));
7572 findMember(objCMethod.get(),
7573 objCMethod->relates,
7574 objCMethod->type,
7575 objCMethod->args,
7576 objCMethod->type+" "+objCImpl->name+"::"+objCMethod->name+" "+objCMethod->args,
7577 FALSE,TRUE);
7578 objCMethod->section=EntryType::makeEmpty();
7579 }
7580 }
7581 }
7582 }
7583}
7584
7585//----------------------------------------------------------------------
7586// find and add the enumeration to their classes, namespaces or files
7587
7588static void findEnums(const Entry *root)
7589{
7590 if (root->section.isEnum())
7591 {
7592 AUTO_TRACE("name={}",root->name);
7593 ClassDefMutable *cd = nullptr;
7594 FileDef *fd = nullptr;
7595 NamespaceDefMutable *nd = nullptr;
7596 MemberNameLinkedMap *mnsd = nullptr;
7597 bool isGlobal = false;
7598 bool isRelated = false;
7599 bool isMemberOf = false;
7600 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7601
7602 QCString name;
7603 QCString scope;
7604
7605 int i = root->name.findRev("::");
7606 if (i!=-1) // scope is specified
7607 {
7608 scope=root->name.left(i); // extract scope
7609 if (root->lang==SrcLangExt::CSharp)
7610 {
7611 scope = mangleCSharpGenericName(scope);
7612 }
7613 name=root->name.right(root->name.length()-i-2); // extract name
7614 if ((cd=getClassMutable(scope))==nullptr)
7615 {
7617 }
7618 }
7619 else // no scope, check the scope in which the docs where found
7620 {
7621 if (root->parent()->section.isScope() && !root->parent()->name.isEmpty()) // found enum docs inside a compound
7622 {
7623 scope=root->parent()->name;
7624 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7625 }
7626 name=root->name;
7627 }
7628
7629 if (!root->relates.isEmpty())
7630 { // related member, prefix user specified scope
7631 isRelated=TRUE;
7632 isMemberOf=(root->relatesType==RelatesType::MemberOf);
7633 if (getClass(root->relates)==nullptr && !scope.isEmpty())
7634 scope=mergeScopes(scope,root->relates);
7635 else
7636 scope=root->relates;
7637 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7638 }
7639
7640 if (cd && !name.isEmpty()) // found a enum inside a compound
7641 {
7642 //printf("Enum '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7643 fd=nullptr;
7645 isGlobal=false;
7646 }
7647 else if (nd) // found enum inside namespace
7648 {
7650 isGlobal=true;
7651 }
7652 else // found a global enum
7653 {
7654 fd=root->fileDef();
7656 isGlobal=true;
7657 }
7658
7659 if (!name.isEmpty())
7660 {
7661 // new enum type
7662 AUTO_TRACE_ADD("new enum {} at line {} of {}",name,root->bodyLine,root->fileName);
7663 auto md = createMemberDef(
7664 root->fileName,root->startLine,root->startColumn,
7665 QCString(),name,QCString(),QCString(),
7666 root->protection,Specifier::Normal,FALSE,
7667 isMemberOf ? Relationship::Foreign : isRelated ? Relationship::Related : Relationship::Member,
7670 auto mmd = toMemberDefMutable(md.get());
7671 mmd->setTagInfo(root->tagInfo());
7672 mmd->setLanguage(root->lang);
7673 mmd->setId(root->id);
7674 if (!isGlobal) mmd->setMemberClass(cd); else mmd->setFileDef(fd);
7675 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7676 mmd->setBodyDef(root->fileDef());
7677 mmd->setMemberSpecifiers(root->spec);
7678 mmd->setVhdlSpecifiers(root->vhdlSpec);
7679 mmd->setEnumBaseType(root->args);
7680 //printf("Enum %s definition at line %d of %s: protection=%d scope=%s\n",
7681 // qPrint(root->name),root->bodyLine,qPrint(root->fileName),root->protection,cd?qPrint(cd->name()):"<none>");
7682 mmd->addSectionsToDefinition(root->anchors);
7683 mmd->setMemberGroupId(root->mGrpId);
7685 mmd->addQualifiers(root->qualifiers);
7686 //printf("%s::setRefItems(%zu)\n",qPrint(md->name()),root->sli.size());
7687 mmd->setRefItems(root->sli);
7688 mmd->setRequirementReferences(root->rqli);
7689 //printf("found enum %s nd=%p\n",qPrint(md->name()),nd);
7690 bool defSet=FALSE;
7691
7692 QCString baseType = root->args;
7693 if (!baseType.isEmpty())
7694 {
7695 baseType.prepend(" : ");
7696 }
7697
7698 if (nd)
7699 {
7700 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python)
7701 {
7702 mmd->setDefinition(name+baseType);
7703 }
7704 else
7705 {
7706 mmd->setDefinition(nd->name()+"::"+name+baseType);
7707 }
7708 //printf("definition=%s\n",md->definition());
7709 defSet=TRUE;
7710 mmd->setNamespace(nd);
7711 nd->insertMember(md.get());
7712 }
7713
7714 // even if we have already added the enum to a namespace, we still
7715 // also want to add it to other appropriate places such as file
7716 // or class.
7717 if (isGlobal && (nd==nullptr || !nd->isAnonymous()))
7718 {
7719 if (!defSet) mmd->setDefinition(name+baseType);
7720 if (fd==nullptr && root->parent())
7721 {
7722 fd=root->parent()->fileDef();
7723 }
7724 if (fd)
7725 {
7726 mmd->setFileDef(fd);
7727 fd->insertMember(md.get());
7728 }
7729 }
7730 else if (cd)
7731 {
7732 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python)
7733 {
7734 mmd->setDefinition(name+baseType);
7735 }
7736 else
7737 {
7738 mmd->setDefinition(cd->name()+"::"+name+baseType);
7739 }
7740 cd->insertMember(md.get());
7741 cd->insertUsedFile(fd);
7742 }
7743 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7744 mmd->setDocsForDefinition(!root->proto);
7745 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7746 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7747
7748 //printf("Adding member=%s\n",qPrint(md->name()));
7749 addMemberToGroups(root,md.get());
7751
7752 MemberName *mn = mnsd->add(name);
7753 mn->push_back(std::move(md));
7754 }
7755 }
7756 else
7757 {
7758 for (const auto &e : root->children()) findEnums(e.get());
7759 }
7760}
7761
7762//----------------------------------------------------------------------
7763
7764static void addEnumValuesToEnums(const Entry *root)
7765{
7766 if (root->section.isEnum())
7767 // non anonymous enumeration
7768 {
7769 AUTO_TRACE("name={}",root->name);
7770 ClassDefMutable *cd = nullptr;
7771 FileDef *fd = nullptr;
7772 NamespaceDefMutable *nd = nullptr;
7773 MemberNameLinkedMap *mnsd = nullptr;
7774 bool isGlobal = false;
7775 bool isRelated = false;
7776 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7777
7778 QCString name;
7779 QCString scope;
7780
7781 int i = root->name.findRev("::");
7782 if (i!=-1) // scope is specified
7783 {
7784 scope=root->name.left(i); // extract scope
7785 if (root->lang==SrcLangExt::CSharp)
7786 {
7787 scope = mangleCSharpGenericName(scope);
7788 }
7789 name=root->name.right(root->name.length()-i-2); // extract name
7790 if ((cd=getClassMutable(scope))==nullptr)
7791 {
7793 }
7794 }
7795 else // no scope, check the scope in which the docs where found
7796 {
7797 if (root->parent()->section.isScope() && !root->parent()->name.isEmpty()) // found enum docs inside a compound
7798 {
7799 scope=root->parent()->name;
7800 if (root->lang==SrcLangExt::CSharp)
7801 {
7802 scope = mangleCSharpGenericName(scope);
7803 }
7804 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7805 }
7806 name=root->name;
7807 }
7808
7809 if (!root->relates.isEmpty())
7810 { // related member, prefix user specified scope
7811 isRelated=TRUE;
7812 if (getClassMutable(root->relates)==nullptr && !scope.isEmpty())
7813 scope=mergeScopes(scope,root->relates);
7814 else
7815 scope=root->relates;
7816 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7817 }
7818
7819 if (cd && !name.isEmpty()) // found a enum inside a compound
7820 {
7821 //printf("Enum in class '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7822 fd=nullptr;
7824 isGlobal=false;
7825 }
7826 else if (nd && !nd->isAnonymous()) // found enum inside namespace
7827 {
7828 //printf("Enum in namespace '%s'::'%s'\n",qPrint(nd->name()),qPrint(name));
7830 isGlobal=true;
7831 }
7832 else // found a global enum
7833 {
7834 fd=root->fileDef();
7835 //printf("Enum in file '%s': '%s'\n",qPrint(fd->name()),qPrint(name));
7837 isGlobal=true;
7838 }
7839
7840 if (!name.isEmpty())
7841 {
7842 //printf("** name=%s\n",qPrint(name));
7843 MemberName *mn = mnsd->find(name); // for all members with this name
7844 if (mn)
7845 {
7846 struct EnumValueInfo
7847 {
7848 EnumValueInfo(const QCString &n,std::unique_ptr<MemberDef> &&md) :
7849 name(n), member(std::move(md)) {}
7850 QCString name;
7851 std::unique_ptr<MemberDef> member;
7852 };
7853 std::vector< EnumValueInfo > extraMembers;
7854 // for each enum in this list
7855 for (const auto &imd : *mn)
7856 {
7857 MemberDefMutable *md = toMemberDefMutable(imd.get());
7858 // use raw pointer in this loop, since we modify mn and can then invalidate mdp.
7859 if (md && md->isEnumerate() && !root->children().empty())
7860 {
7861 AUTO_TRACE_ADD("enum {} with {} children",md->name(),root->children().size());
7862 for (const auto &e : root->children())
7863 {
7864 SrcLangExt sle = root->lang;
7865 bool isJavaLike = sle==SrcLangExt::CSharp || sle==SrcLangExt::Java || sle==SrcLangExt::XML;
7866 if ( isJavaLike || root->spec.isStrong())
7867 {
7868 if (sle == SrcLangExt::Cpp && e->section.isDefine()) continue;
7869 // Unlike classic C/C++ enums, for C++11, C# & Java enum
7870 // values are only visible inside the enum scope, so we must create
7871 // them here and only add them to the enum
7872 //printf("md->qualifiedName()=%s e->name=%s tagInfo=%p name=%s\n",
7873 // qPrint(md->qualifiedName()),qPrint(e->name),(void*)e->tagInfo(),qPrint(e->name));
7874 QCString qualifiedName = root->name;
7875 i = qualifiedName.findRev("::");
7876 if (i!=-1 && sle==SrcLangExt::CSharp)
7877 {
7878 qualifiedName = mangleCSharpGenericName(qualifiedName.left(i))+qualifiedName.mid(i);
7879 }
7880 if (isJavaLike)
7881 {
7882 qualifiedName=substitute(qualifiedName,"::",".");
7883 }
7884 if (md->qualifiedName()==qualifiedName) // enum value scope matches that of the enum
7885 {
7886 QCString fileName = e->fileName;
7887 if (fileName.isEmpty() && e->tagInfo())
7888 {
7889 fileName = e->tagInfo()->tagName;
7890 }
7891 AUTO_TRACE_ADD("strong enum value {}",e->name);
7892 auto fmd = createMemberDef(
7893 fileName,e->startLine,e->startColumn,
7894 e->type,e->name,e->args,QCString(),
7895 e->protection, Specifier::Normal,e->isStatic,Relationship::Member,
7897 auto fmmd = toMemberDefMutable(fmd.get());
7898 NamespaceDef *mnd = md->getNamespaceDef();
7899 if (md->getClassDef())
7900 fmmd->setMemberClass(md->getClassDef());
7901 else if (mnd && (mnd->isLinkable() || mnd->isAnonymous()))
7902 fmmd->setNamespace(mnd);
7903 else if (md->getFileDef())
7904 fmmd->setFileDef(md->getFileDef());
7905 fmmd->setOuterScope(md->getOuterScope());
7906 fmmd->setTagInfo(e->tagInfo());
7907 fmmd->setLanguage(e->lang);
7908 fmmd->setBodySegment(e->startLine,e->bodyLine,e->endBodyLine);
7909 fmmd->setBodyDef(e->fileDef());
7910 fmmd->setId(e->id);
7911 fmmd->setDocumentation(e->doc,e->docFile,e->docLine);
7912 fmmd->setBriefDescription(e->brief,e->briefFile,e->briefLine);
7913 fmmd->addSectionsToDefinition(e->anchors);
7914 fmmd->setInitializer(e->initializer.str());
7915 fmmd->setMaxInitLines(e->initLines);
7916 fmmd->setMemberGroupId(e->mGrpId);
7917 fmmd->setExplicitExternal(e->explicitExternal,fileName,e->startLine,e->startColumn);
7918 fmmd->setRefItems(e->sli);
7919 fmmd->setRequirementReferences(e->rqli);
7920 fmmd->setAnchor();
7921 md->insertEnumField(fmd.get());
7922 fmmd->setEnumScope(md,TRUE);
7923 extraMembers.emplace_back(e->name,std::move(fmd));
7924 }
7925 }
7926 else
7927 {
7928 AUTO_TRACE_ADD("enum value {}",e->name);
7929 //printf("e->name=%s isRelated=%d\n",qPrint(e->name),isRelated);
7930 MemberName *fmn=nullptr;
7931 MemberNameLinkedMap *emnsd = isRelated ? Doxygen::functionNameLinkedMap : mnsd;
7932 if (!e->name.isEmpty() && (fmn=emnsd->find(e->name)))
7933 // get list of members with the same name as the field
7934 {
7935 for (const auto &ifmd : *fmn)
7936 {
7937 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
7938 if (fmd && fmd->isEnumValue() && fmd->getOuterScope()==md->getOuterScope()) // in same scope
7939 {
7940 //printf("found enum value with same name %s in scope %s\n",
7941 // qPrint(fmd->name()),qPrint(fmd->getOuterScope()->name()));
7942 if (nd && !nd->isAnonymous())
7943 {
7944 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
7945 {
7946 const NamespaceDef *fnd=fmd->getNamespaceDef();
7947 if (fnd==nd) // enum value is inside a namespace
7948 {
7949 md->insertEnumField(fmd);
7950 fmd->setEnumScope(md);
7951 }
7952 }
7953 }
7954 else if (isGlobal)
7955 {
7956 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
7957 {
7958 const FileDef *ffd=fmd->getFileDef();
7959 if (ffd==fd && ffd==md->getFileDef()) // enum value has file scope
7960 {
7961 md->insertEnumField(fmd);
7962 fmd->setEnumScope(md);
7963 }
7964 }
7965 }
7966 else if (isRelated && cd) // reparent enum value to
7967 // match the enum's scope
7968 {
7969 md->insertEnumField(fmd); // add field def to list
7970 fmd->setEnumScope(md); // cross ref with enum name
7971 fmd->setEnumClassScope(cd); // cross ref with enum name
7972 fmd->setOuterScope(cd);
7973 fmd->makeRelated();
7974 cd->insertMember(fmd);
7975 }
7976 else
7977 {
7978 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
7979 {
7980 const ClassDef *fcd=fmd->getClassDef();
7981 if (fcd==cd) // enum value is inside a class
7982 {
7983 //printf("Inserting enum field %s in enum scope %s\n",
7984 // qPrint(fmd->name()),qPrint(md->name()));
7985 md->insertEnumField(fmd); // add field def to list
7986 fmd->setEnumScope(md); // cross ref with enum name
7987 }
7988 }
7989 }
7990 }
7991 }
7992 }
7993 }
7994 }
7995 }
7996 }
7997 // move the newly added members into mn
7998 for (auto &e : extraMembers)
7999 {
8000 MemberName *emn=mnsd->add(e.name);
8001 emn->push_back(std::move(e.member));
8002 }
8003 }
8004 }
8005 }
8006 else
8007 {
8008 for (const auto &e : root->children()) addEnumValuesToEnums(e.get());
8009 }
8010}
8011
8012//----------------------------------------------------------------------
8013
8014static void addEnumDocs(const Entry *root,MemberDefMutable *md)
8015{
8016 AUTO_TRACE();
8017 // documentation outside a compound overrides the documentation inside it
8018 {
8019 md->setDocumentation(root->doc,root->docFile,root->docLine);
8020 md->setDocsForDefinition(!root->proto);
8021 }
8022
8023 // brief descriptions inside a compound override the documentation
8024 // outside it
8025 {
8026 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
8027 }
8028
8029 if (md->inbodyDocumentation().isEmpty() || !root->parent()->name.isEmpty())
8030 {
8032 }
8033
8034 if (root->mGrpId!=-1 && md->getMemberGroupId()==-1)
8035 {
8036 md->setMemberGroupId(root->mGrpId);
8037 }
8038
8040 md->setRefItems(root->sli);
8041 md->setRequirementReferences(root->rqli);
8042
8043 const GroupDef *gd=md->getGroupDef();
8044 if (gd==nullptr && !root->groups.empty()) // member not grouped but out-of-line documentation is
8045 {
8046 addMemberToGroups(root,md);
8047 }
8049}
8050
8051//----------------------------------------------------------------------
8052// Search for the name in the associated groups. If a matching member
8053// definition exists, then add the documentation to it and return TRUE,
8054// otherwise FALSE.
8055
8056static bool tryAddEnumDocsToGroupMember(const Entry *root,const QCString &name)
8057{
8058 for (const auto &g : root->groups)
8059 {
8060 const GroupDef *gd = Doxygen::groupLinkedMap->find(g.groupname);
8061 if (gd)
8062 {
8063 MemberList *ml = gd->getMemberList(MemberListType::DecEnumMembers());
8064 if (ml)
8065 {
8066 MemberDefMutable *md = toMemberDefMutable(ml->find(name));
8067 if (md)
8068 {
8069 addEnumDocs(root,md);
8070 return TRUE;
8071 }
8072 }
8073 }
8074 else if (!gd && g.pri == Grouping::GROUPING_INGROUP)
8075 {
8076 warn(root->fileName, root->startLine,
8077 "Found non-existing group '{}' for the command '{}', ignoring command",
8078 g.groupname, Grouping::getGroupPriName( g.pri )
8079 );
8080 }
8081 }
8082
8083 return FALSE;
8084}
8085
8086//----------------------------------------------------------------------
8087// find the documentation blocks for the enumerations
8088
8089static void findEnumDocumentation(const Entry *root)
8090{
8091 if (root->section.isEnumDoc() &&
8092 !root->name.isEmpty() &&
8093 root->name.at(0)!='@' // skip anonymous enums
8094 )
8095 {
8096 QCString name;
8097 QCString scope;
8098 int i = root->name.findRev("::");
8099 if (i!=-1) // scope is specified as part of the name
8100 {
8101 name=root->name.right(root->name.length()-i-2); // extract name
8102 scope=root->name.left(i); // extract scope
8103 //printf("Scope='%s' Name='%s'\n",qPrint(scope),qPrint(name));
8104 }
8105 else // just the name
8106 {
8107 name=root->name;
8108 }
8109 if (root->parent()->section.isScope() && !root->parent()->name.isEmpty()) // found enum docs inside a compound
8110 {
8111 if (!scope.isEmpty()) scope.prepend("::");
8112 scope.prepend(root->parent()->name);
8113 }
8114 const ClassDef *cd = getClass(scope);
8115 const NamespaceDef *nd=Doxygen::namespaceLinkedMap->find(scope);
8116 const FileDef *fd = root->fileDef();
8117 AUTO_TRACE("Found docs for enum with name '{}' and scope '{}' in context '{}' cd='{}', nd='{}' fd='{}'",
8118 name,scope,root->parent()->name,
8119 cd ? cd->name() : QCString("<none>"),
8120 nd ? nd->name() : QCString("<none>"),
8121 fd ? fd->name() : QCString("<none>"));
8122
8123 if (!name.isEmpty())
8124 {
8125 bool found = tryAddEnumDocsToGroupMember(root, name);
8126 if (!found)
8127 {
8128 MemberName *mn = cd ? Doxygen::memberNameLinkedMap->find(name) : Doxygen::functionNameLinkedMap->find(name);
8129 if (mn)
8130 {
8131 for (const auto &imd : *mn)
8132 {
8133 MemberDefMutable *md = toMemberDefMutable(imd.get());
8134 if (md && md->isEnumerate())
8135 {
8136 const ClassDef *mcd = md->getClassDef();
8137 const NamespaceDef *mnd = md->getNamespaceDef();
8138 const FileDef *mfd = md->getFileDef();
8139 if (cd && mcd==cd)
8140 {
8141 AUTO_TRACE_ADD("Match found for class scope");
8142 addEnumDocs(root,md);
8143 found = TRUE;
8144 break;
8145 }
8146 else if (cd==nullptr && mcd==nullptr && nd!=nullptr && mnd==nd)
8147 {
8148 AUTO_TRACE_ADD("Match found for namespace scope");
8149 addEnumDocs(root,md);
8150 found = TRUE;
8151 break;
8152 }
8153 else if (cd==nullptr && nd==nullptr && mcd==nullptr && mnd==nullptr && fd==mfd)
8154 {
8155 AUTO_TRACE_ADD("Match found for global scope");
8156 addEnumDocs(root,md);
8157 found = TRUE;
8158 break;
8159 }
8160 }
8161 }
8162 }
8163 }
8164 if (!found)
8165 {
8166 warn(root->fileName,root->startLine, "Documentation for undefined enum '{}' found.", name);
8167 }
8168 }
8169 }
8170 for (const auto &e : root->children()) findEnumDocumentation(e.get());
8171}
8172
8173// search for each enum (member or function) in mnl if it has documented
8174// enum values.
8175static void findDEV(const MemberNameLinkedMap &mnsd)
8176{
8177 // for each member name
8178 for (const auto &mn : mnsd)
8179 {
8180 // for each member definition
8181 for (const auto &imd : *mn)
8182 {
8183 MemberDefMutable *md = toMemberDefMutable(imd.get());
8184 if (md && md->isEnumerate()) // member is an enum
8185 {
8186 int documentedEnumValues=0;
8187 // for each enum value
8188 for (const auto &fmd : md->enumFieldList())
8189 {
8190 if (fmd->isLinkableInProject()) documentedEnumValues++;
8191 }
8192 // at least one enum value is documented
8193 if (documentedEnumValues>0) md->setDocumentedEnumValues(TRUE);
8194 }
8195 }
8196 }
8197}
8198
8199// search for each enum (member or function) if it has documented enum
8200// values.
8206
8207//----------------------------------------------------------------------
8208
8210{
8211 auto &index = Index::instance();
8212 // for each class member name
8213 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8214 {
8215 // for each member definition
8216 for (const auto &md : *mn)
8217 {
8218 index.addClassMemberNameToIndex(md.get());
8219 if (md->getModuleDef())
8220 {
8221 index.addModuleMemberNameToIndex(md.get());
8222 }
8223 }
8224 }
8225 // for each file/namespace function name
8226 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8227 {
8228 // for each member definition
8229 for (const auto &md : *mn)
8230 {
8231 if (md->getNamespaceDef())
8232 {
8233 index.addNamespaceMemberNameToIndex(md.get());
8234 }
8235 else
8236 {
8237 index.addFileMemberNameToIndex(md.get());
8238 }
8239 if (md->getModuleDef())
8240 {
8241 index.addModuleMemberNameToIndex(md.get());
8242 }
8243 }
8244 }
8245
8246 index.sortMemberIndexLists();
8247}
8248
8249//----------------------------------------------------------------------
8250
8251static void addToIndices()
8252{
8253 for (const auto &cd : *Doxygen::classLinkedMap)
8254 {
8255 if (cd->isLinkableInProject())
8256 {
8257 Doxygen::indexList->addIndexItem(cd.get(),nullptr);
8258 if (Doxygen::searchIndex.enabled())
8259 {
8260 Doxygen::searchIndex.setCurrentDoc(cd.get(),cd->anchor(),FALSE);
8261 Doxygen::searchIndex.addWord(cd->localName(),TRUE);
8262 }
8263 }
8264 }
8265
8266 for (const auto &cd : *Doxygen::conceptLinkedMap)
8267 {
8268 if (cd->isLinkableInProject())
8269 {
8270 Doxygen::indexList->addIndexItem(cd.get(),nullptr);
8271 if (Doxygen::searchIndex.enabled())
8272 {
8273 Doxygen::searchIndex.setCurrentDoc(cd.get(),cd->anchor(),FALSE);
8274 Doxygen::searchIndex.addWord(cd->localName(),TRUE);
8275 }
8276 }
8277 }
8278
8279 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8280 {
8281 if (nd->isLinkableInProject())
8282 {
8283 Doxygen::indexList->addIndexItem(nd.get(),nullptr);
8284 if (Doxygen::searchIndex.enabled())
8285 {
8286 Doxygen::searchIndex.setCurrentDoc(nd.get(),nd->anchor(),FALSE);
8287 Doxygen::searchIndex.addWord(nd->localName(),TRUE);
8288 }
8289 }
8290 }
8291
8292 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8293 {
8294 for (const auto &fd : *fn)
8295 {
8296 if (Doxygen::searchIndex.enabled() && fd->isLinkableInProject())
8297 {
8298 Doxygen::searchIndex.setCurrentDoc(fd.get(),fd->anchor(),FALSE);
8299 Doxygen::searchIndex.addWord(fd->localName(),TRUE);
8300 }
8301 }
8302 }
8303
8304 auto addWordsForTitle = [](const Definition *d,const QCString &anchor,const QCString &title)
8305 {
8306 Doxygen::indexList->addIndexItem(d,nullptr,QCString(),filterTitle(title));
8307 if (Doxygen::searchIndex.enabled())
8308 {
8309 Doxygen::searchIndex.setCurrentDoc(d,anchor,false);
8310 std::string s = title.str();
8311 static const reg::Ex re(R"(\a[\w-]*)");
8312 reg::Iterator it(s,re);
8314 for (; it!=end ; ++it)
8315 {
8316 const auto &match = *it;
8317 std::string matchStr = match.str();
8318 Doxygen::searchIndex.addWord(matchStr,true);
8319 }
8320 }
8321 };
8322
8323 for (const auto &gd : *Doxygen::groupLinkedMap)
8324 {
8325 if (gd->isLinkableInProject())
8326 {
8327 addWordsForTitle(gd.get(),gd->anchor(),gd->groupTitle());
8328 }
8329 }
8330
8331 for (const auto &pd : *Doxygen::pageLinkedMap)
8332 {
8333 if (pd->isLinkableInProject())
8334 {
8335 addWordsForTitle(pd.get(),pd->anchor(),pd->title());
8336 }
8337 }
8338
8340 {
8341 addWordsForTitle(Doxygen::mainPage.get(),Doxygen::mainPage->anchor(),Doxygen::mainPage->title());
8342 }
8343
8344 auto addMemberToSearchIndex = [](const MemberDef *md)
8345 {
8346 if (Doxygen::searchIndex.enabled())
8347 {
8348 Doxygen::searchIndex.setCurrentDoc(md,md->anchor(),FALSE);
8349 QCString ln=md->localName();
8350 QCString qn=md->qualifiedName();
8351 Doxygen::searchIndex.addWord(ln,TRUE);
8352 if (ln!=qn)
8353 {
8354 Doxygen::searchIndex.addWord(qn,TRUE);
8355 if (md->getClassDef())
8356 {
8357 Doxygen::searchIndex.addWord(md->getClassDef()->displayName(),TRUE);
8358 }
8359 if (md->getNamespaceDef())
8360 {
8361 Doxygen::searchIndex.addWord(md->getNamespaceDef()->displayName(),TRUE);
8362 }
8363 }
8364 }
8365 };
8366
8367 auto getScope = [](const MemberDef *md)
8368 {
8369 const Definition *scope = nullptr;
8370 if (md->getGroupDef()) scope = md->getGroupDef();
8371 else if (md->getClassDef()) scope = md->getClassDef();
8372 else if (md->getNamespaceDef()) scope = md->getNamespaceDef();
8373 else if (md->getFileDef()) scope = md->getFileDef();
8374 return scope;
8375 };
8376
8377 auto addMemberToIndices = [addMemberToSearchIndex,getScope](const MemberDef *md)
8378 {
8379 if (md->isLinkableInProject())
8380 {
8381 if (!(md->isEnumerate() && md->isAnonymous()))
8382 {
8383 Doxygen::indexList->addIndexItem(getScope(md),md);
8385 }
8386 if (md->isEnumerate())
8387 {
8388 for (const auto &fmd : md->enumFieldList())
8389 {
8390 Doxygen::indexList->addIndexItem(getScope(fmd),fmd);
8392 }
8393 }
8394 }
8395 };
8396
8397 // for each class member name
8398 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8399 {
8400 // for each member definition
8401 for (const auto &md : *mn)
8402 {
8403 addMemberToIndices(md.get());
8404 }
8405 }
8406 // for each file/namespace function name
8407 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8408 {
8409 // for each member definition
8410 for (const auto &md : *mn)
8411 {
8412 addMemberToIndices(md.get());
8413 }
8414 }
8415}
8416
8417//----------------------------------------------------------------------
8418
8420{
8421 // for each member name
8422 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8423 {
8424 // for each member definition
8425 for (const auto &imd : *mn)
8426 {
8427 MemberDefMutable *md = toMemberDefMutable(imd.get());
8428 if (md)
8429 {
8431 }
8432 }
8433 }
8434 // for each member name
8435 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8436 {
8437 // for each member definition
8438 for (const auto &imd : *mn)
8439 {
8440 MemberDefMutable *md = toMemberDefMutable(imd.get());
8441 if (md)
8442 {
8444 }
8445 }
8446 }
8447}
8448
8449// recursive helper function looking for reimplements/implemented
8450// by relations between class cd and direct or indirect base class bcd
8452{
8453 for (const auto &mn : cd->memberNameInfoLinkedMap()) // for each member in class cd with a unique name
8454 {
8455 for (const auto &imd : *mn) // for each member with a given name
8456 {
8457 MemberDefMutable *md = toMemberDefMutable(imd->memberDef());
8458 if (md && (md->isFunction() || md->isCSharpProperty())) // filter on reimplementable members
8459 {
8460 ClassDef *mbcd = bcd->classDef;
8461 if (mbcd && mbcd->isLinkable()) // filter on linkable classes
8462 {
8463 const auto &bmn = mbcd->memberNameInfoLinkedMap();
8464 const auto &bmni = bmn.find(mn->memberName());
8465 if (bmni) // there are base class members with the same name
8466 {
8467 for (const auto &ibmd : *bmni) // for base class member with that name
8468 {
8469 MemberDefMutable *bmd = toMemberDefMutable(ibmd->memberDef());
8470 if (bmd) // not part of an inline namespace
8471 {
8472 auto lang = bmd->getLanguage();
8473 auto compType = mbcd->compoundType();
8474 if (bmd->virtualness()!=Specifier::Normal ||
8475 lang==SrcLangExt::Python ||
8476 lang==SrcLangExt::Java ||
8477 lang==SrcLangExt::PHP ||
8478 compType==ClassDef::Interface ||
8479 compType==ClassDef::Protocol)
8480 {
8481 const ArgumentList &bmdAl = bmd->argumentList();
8482 const ArgumentList &mdAl = md->argumentList();
8483 //printf(" Base argList='%s'\n Super argList='%s'\n",
8484 // qPrint(argListToString(bmdAl)),
8485 // qPrint(argListToString(mdAl))
8486 // );
8487 if (
8488 lang==SrcLangExt::Python ||
8489 matchArguments2(bmd->getOuterScope(),bmd->getFileDef(),bmd->typeString(),&bmdAl,
8490 md->getOuterScope(), md->getFileDef(), md->typeString(),&mdAl,
8491 TRUE,lang
8492 )
8493 )
8494 {
8495 if (lang==SrcLangExt::Python && md->name().startsWith("__")) continue; // private members do not reimplement
8496 //printf("match!\n");
8497 const MemberDef *rmd = md->reimplements();
8498 if (rmd==nullptr) // not already assigned
8499 {
8500 //printf("%s: setting (new) reimplements member %s\n",qPrint(md->qualifiedName()),qPrint(bmd->qualifiedName()));
8501 md->setReimplements(bmd);
8502 }
8503 //printf("%s: add reimplementedBy member %s\n",qPrint(bmd->qualifiedName()),qPrint(md->qualifiedName()));
8504 bmd->insertReimplementedBy(md);
8505 }
8506 else
8507 {
8508 //printf("no match!\n");
8509 }
8510 }
8511 }
8512 }
8513 }
8514 }
8515 }
8516 }
8517 }
8518
8519 // do also for indirect base classes
8520 for (const auto &bbcd : bcd->classDef->baseClasses())
8521 {
8523 }
8524}
8525
8526//----------------------------------------------------------------------
8527// computes the relation between all members. For each member 'm'
8528// the members that override the implementation of 'm' are searched and
8529// the member that 'm' overrides is searched.
8530
8532{
8533 for (const auto &cd : *Doxygen::classLinkedMap)
8534 {
8535 if (cd->isLinkable())
8536 {
8537 for (const auto &bcd : cd->baseClasses())
8538 {
8540 }
8541 }
8542 }
8543}
8544
8545//----------------------------------------------------------------------------
8546
8548{
8549 // for each class
8550 for (const auto &cd : *Doxygen::classLinkedMap)
8551 {
8552 // that is a template
8553 for (const auto &ti : cd->getTemplateInstances())
8554 {
8555 ClassDefMutable *tcdm = toClassDefMutable(ti.classDef);
8556 if (tcdm)
8557 {
8558 tcdm->addMembersToTemplateInstance(cd.get(),cd->templateArguments(),ti.templSpec);
8559 }
8560 }
8561 }
8562}
8563
8564//----------------------------------------------------------------------------
8565
8566static void mergeCategories()
8567{
8568 AUTO_TRACE();
8569 // merge members of categories into the class they extend
8570 for (const auto &cd : *Doxygen::classLinkedMap)
8571 {
8572 int i=cd->name().find('(');
8573 if (i!=-1) // it is an Objective-C category
8574 {
8575 QCString baseName=cd->name().left(i);
8576 ClassDefMutable *baseClass=toClassDefMutable(Doxygen::classLinkedMap->find(baseName));
8577 if (baseClass)
8578 {
8579 AUTO_TRACE_ADD("merging members of category {} into {}",cd->name(),baseClass->name());
8580 baseClass->mergeCategory(cd.get());
8581 }
8582 }
8583 }
8584}
8585
8586// builds the list of all members for each class
8587
8589{
8590 // merge the member list of base classes into the inherited classes.
8591 for (const auto &cd : *Doxygen::classLinkedMap)
8592 {
8593 if (// !cd->isReference() && // not an external class
8594 cd->subClasses().empty() && // is a root of the hierarchy
8595 !cd->baseClasses().empty()) // and has at least one base class
8596 {
8597 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8598 if (cdm)
8599 {
8600 //printf("*** merging members for %s\n",qPrint(cd->name()));
8601 cdm->mergeMembers();
8602 }
8603 }
8604 }
8605 // now sort the member list of all members for all classes.
8606 for (const auto &cd : *Doxygen::classLinkedMap)
8607 {
8608 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8609 if (cdm)
8610 {
8611 cdm->sortAllMembersList();
8612 }
8613 }
8614}
8615
8616//----------------------------------------------------------------------------
8617
8619{
8620 auto processSourceFile = [](FileDef *fd,OutputList &ol,ClangTUParser *parser)
8621 {
8622 bool showSources = fd->generateSourceFile() && !Htags::useHtags; // sources need to be shown in the output
8623 bool parseSources = !fd->isReference() && Doxygen::parseSourcesNeeded; // we needed to parse the sources even if we do not show them
8624 if (showSources)
8625 {
8626 msg("Generating code for file {}...\n",fd->docName());
8627 fd->writeSourceHeader(ol);
8628 fd->writeSourceBody(ol,parser);
8629 fd->writeSourceFooter(ol);
8630 }
8631 else if (parseSources)
8632 {
8633 msg("Parsing code for file {}...\n",fd->docName());
8634 fd->parseSource(parser);
8635 }
8636 };
8637 if (!Doxygen::inputNameLinkedMap->empty())
8638 {
8639#if USE_LIBCLANG
8641 {
8642 StringUnorderedSet processedFiles;
8643
8644 // create a dictionary with files to process
8645 StringUnorderedSet filesToProcess;
8646
8647 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8648 {
8649 for (const auto &fd : *fn)
8650 {
8651 filesToProcess.insert(fd->absFilePath().str());
8652 }
8653 }
8654 // process source files (and their include dependencies)
8655 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8656 {
8657 for (const auto &fd : *fn)
8658 {
8659 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt::Cpp &&
8660 (fd->generateSourceFile() ||
8662 )
8663 )
8664 {
8665 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8666 clangParser->parse();
8667 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8668
8669 for (auto incFile : clangParser->filesInSameTU())
8670 {
8671 if (filesToProcess.find(incFile)!=filesToProcess.end() && // part of input
8672 fd->absFilePath()!=incFile && // not same file
8673 processedFiles.find(incFile)==processedFiles.end()) // not yet marked as processed
8674 {
8675 StringVector moreFiles;
8676 bool ambig = false;
8678 if (ifd && !ifd->isReference())
8679 {
8680 processSourceFile(ifd,*g_outputList,clangParser.get());
8681 processedFiles.insert(incFile);
8682 }
8683 }
8684 }
8685 processedFiles.insert(fd->absFilePath().str());
8686 }
8687 }
8688 }
8689 // process remaining files
8690 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8691 {
8692 for (const auto &fd : *fn)
8693 {
8694 if (processedFiles.find(fd->absFilePath().str())==processedFiles.end()) // not yet processed
8695 {
8696 if (fd->getLanguage()==SrcLangExt::Cpp) // C/C++ file, use clang parser
8697 {
8698 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8699 clangParser->parse();
8700 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8701 }
8702 else // non C/C++ file, use built-in parser
8703 {
8704 processSourceFile(fd.get(),*g_outputList,nullptr);
8705 }
8706 }
8707 }
8708 }
8709 }
8710 else
8711#endif
8712 {
8713 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8714 if (numThreads>1)
8715 {
8716 msg("Generating code files using {} threads.\n",numThreads);
8717 struct SourceContext
8718 {
8719 SourceContext(FileDef *fd_,bool gen_,const OutputList &ol_)
8720 : fd(fd_), generateSourceFile(gen_), ol(ol_) {}
8721 FileDef *fd;
8722 bool generateSourceFile;
8723 OutputList ol;
8724 };
8725 ThreadPool threadPool(numThreads);
8726 std::vector< std::future< std::shared_ptr<SourceContext> > > results;
8727 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8728 {
8729 for (const auto &fd : *fn)
8730 {
8731 bool generateSourceFile = fd->generateSourceFile() && !Htags::useHtags;
8732 auto ctx = std::make_shared<SourceContext>(fd.get(),generateSourceFile,*g_outputList);
8733 auto processFile = [ctx]()
8734 {
8735 if (ctx->generateSourceFile)
8736 {
8737 msg("Generating code for file {}...\n",ctx->fd->docName());
8738 }
8739 else
8740 {
8741 msg("Parsing code for file {}...\n",ctx->fd->docName());
8742 }
8743 StringVector filesInSameTu;
8744 ctx->fd->getAllIncludeFilesRecursively(filesInSameTu);
8745 if (ctx->generateSourceFile) // sources need to be shown in the output
8746 {
8747 ctx->fd->writeSourceHeader(ctx->ol);
8748 ctx->fd->writeSourceBody(ctx->ol,nullptr);
8749 ctx->fd->writeSourceFooter(ctx->ol);
8750 }
8751 else if (!ctx->fd->isReference() && Doxygen::parseSourcesNeeded)
8752 // we needed to parse the sources even if we do not show them
8753 {
8754 ctx->fd->parseSource(nullptr);
8755 }
8756 return ctx;
8757 };
8758 results.emplace_back(threadPool.queue(processFile));
8759 }
8760 }
8761 for (auto &f : results)
8762 {
8763 auto ctx = f.get();
8764 }
8765 }
8766 else // single threaded version
8767 {
8768 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8769 {
8770 for (const auto &fd : *fn)
8771 {
8772 StringVector filesInSameTu;
8773 fd->getAllIncludeFilesRecursively(filesInSameTu);
8774 processSourceFile(fd.get(),*g_outputList,nullptr);
8775 }
8776 }
8777 }
8778 }
8779 }
8780}
8781
8782//----------------------------------------------------------------------------
8783
8784static void generateFileDocs()
8785{
8786 if (Index::instance().numDocumentedFiles()==0) return;
8787
8788 if (!Doxygen::inputNameLinkedMap->empty())
8789 {
8790 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8791 if (numThreads>1) // multi threaded processing
8792 {
8793 struct DocContext
8794 {
8795 DocContext(FileDef *fd_,const OutputList &ol_)
8796 : fd(fd_), ol(ol_) {}
8797 FileDef *fd;
8798 OutputList ol;
8799 };
8800 ThreadPool threadPool(numThreads);
8801 std::vector< std::future< std::shared_ptr<DocContext> > > results;
8802 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8803 {
8804 for (const auto &fd : *fn)
8805 {
8806 bool doc = fd->isLinkableInProject();
8807 if (doc)
8808 {
8809 auto ctx = std::make_shared<DocContext>(fd.get(),*g_outputList);
8810 auto processFile = [ctx]() {
8811 msg("Generating docs for file {}...\n",ctx->fd->docName());
8812 ctx->fd->writeDocumentation(ctx->ol);
8813 return ctx;
8814 };
8815 results.emplace_back(threadPool.queue(processFile));
8816 }
8817 }
8818 }
8819 for (auto &f : results)
8820 {
8821 auto ctx = f.get();
8822 }
8823 }
8824 else // single threaded processing
8825 {
8826 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8827 {
8828 for (const auto &fd : *fn)
8829 {
8830 bool doc = fd->isLinkableInProject();
8831 if (doc)
8832 {
8833 msg("Generating docs for file {}...\n",fd->docName());
8834 fd->writeDocumentation(*g_outputList);
8835 }
8836 }
8837 }
8838 }
8839 }
8840}
8841
8842//----------------------------------------------------------------------------
8843
8845{
8846 // add source references for class definitions
8847 for (const auto &cd : *Doxygen::classLinkedMap)
8848 {
8849 const FileDef *fd=cd->getBodyDef();
8850 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8851 {
8852 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),nullptr);
8853 }
8854 }
8855 // add source references for concept definitions
8856 for (const auto &cd : *Doxygen::conceptLinkedMap)
8857 {
8858 const FileDef *fd=cd->getBodyDef();
8859 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8860 {
8861 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),nullptr);
8862 }
8863 }
8864 // add source references for namespace definitions
8865 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8866 {
8867 const FileDef *fd=nd->getBodyDef();
8868 if (fd && nd->isLinkableInProject() && nd->getStartDefLine()!=-1)
8869 {
8870 const_cast<FileDef*>(fd)->addSourceRef(nd->getStartDefLine(),nd.get(),nullptr);
8871 }
8872 }
8873
8874 // add source references for member names
8875 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8876 {
8877 for (const auto &md : *mn)
8878 {
8879 //printf("class member %s: def=%s body=%d link?=%d\n",
8880 // qPrint(md->name()),
8881 // md->getBodyDef()?qPrint(md->getBodyDef()->name()):"<none>",
8882 // md->getStartBodyLine(),md->isLinkableInProject());
8883 const FileDef *fd=md->getBodyDef();
8884 if (fd &&
8885 md->getStartDefLine()!=-1 &&
8886 md->isLinkableInProject() &&
8888 )
8889 {
8890 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8891 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8892 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8893 }
8894 }
8895 }
8896 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8897 {
8898 for (const auto &md : *mn)
8899 {
8900 const FileDef *fd=md->getBodyDef();
8901 //printf("member %s body=[%d,%d] fd=%p link=%d parseSources=%d\n",
8902 // qPrint(md->name()),
8903 // md->getStartBodyLine(),md->getEndBodyLine(),fd,
8904 // md->isLinkableInProject(),
8905 // Doxygen::parseSourcesNeeded);
8906 if (fd &&
8907 md->getStartDefLine()!=-1 &&
8908 md->isLinkableInProject() &&
8910 )
8911 {
8912 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8913 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8914 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8915 }
8916 }
8917 }
8918}
8919
8920//----------------------------------------------------------------------------
8921
8922// add the macro definitions found during preprocessing as file members
8923static void buildDefineList()
8924{
8925 AUTO_TRACE();
8926 for (const auto &s : g_inputFiles)
8927 {
8928 auto it = Doxygen::macroDefinitions.find(s);
8930 {
8931 for (const auto &def : it->second)
8932 {
8933 auto md = createMemberDef(
8934 def.fileName,def.lineNr,def.columnNr,
8935 "#define",def.name,def.args,QCString(),
8936 Protection::Public,Specifier::Normal,FALSE,Relationship::Member,MemberType::Define,
8937 ArgumentList(),ArgumentList(),"");
8938 auto mmd = toMemberDefMutable(md.get());
8939
8940 if (!def.args.isEmpty())
8941 {
8942 mmd->moveArgumentList(stringToArgumentList(SrcLangExt::Cpp, def.args));
8943 }
8944 mmd->setInitializer(def.definition);
8945 mmd->setFileDef(def.fileDef);
8946 mmd->setDefinition("#define "+def.name);
8947
8948 MemberName *mn=Doxygen::functionNameLinkedMap->add(def.name);
8949 if (def.fileDef)
8950 {
8951 const MemberList *defMl = def.fileDef->getMemberList(MemberListType::DocDefineMembers());
8952 if (defMl)
8953 {
8954 const MemberDef *defMd = defMl->findRev(def.name);
8955 if (defMd) // definition already stored
8956 {
8957 mmd->setRedefineCount(defMd->redefineCount()+1);
8958 }
8959 }
8960 def.fileDef->insertMember(md.get());
8961 }
8962 AUTO_TRACE_ADD("adding macro {} with definition {}",def.name,def.definition);
8963 mn->push_back(std::move(md));
8964 }
8965 }
8966 }
8967}
8968
8969//----------------------------------------------------------------------------
8970
8971static void sortMemberLists()
8972{
8973 // sort class member lists
8974 for (const auto &cd : *Doxygen::classLinkedMap)
8975 {
8976 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8977 if (cdm)
8978 {
8979 cdm->sortMemberLists();
8980 }
8981 }
8982
8983 // sort namespace member lists
8984 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8985 {
8987 if (ndm)
8988 {
8989 ndm->sortMemberLists();
8990 }
8991 }
8992
8993 // sort file member lists
8994 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8995 {
8996 for (const auto &fd : *fn)
8997 {
8998 fd->sortMemberLists();
8999 }
9000 }
9001
9002 // sort group member lists
9003 for (const auto &gd : *Doxygen::groupLinkedMap)
9004 {
9005 gd->sortMemberLists();
9006 }
9007
9009}
9010
9011//----------------------------------------------------------------------------
9012
9013static bool isSymbolHidden(const Definition *d)
9014{
9015 bool hidden = d->isHidden();
9016 const Definition *parent = d->getOuterScope();
9017 return parent ? hidden || isSymbolHidden(parent) : hidden;
9018}
9019
9021{
9022 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9023 if (numThreads>1)
9024 {
9025 ThreadPool threadPool(numThreads);
9026 std::vector < std::future< void > > results;
9027 // queue the work
9028 for (const auto &[name,symList] : *Doxygen::symbolMap)
9029 {
9030 for (const auto &def : symList)
9031 {
9033 if (dm && !isSymbolHidden(def) && !def->isArtificial() && def->isLinkableInProject())
9034 {
9035 auto processTooltip = [dm]() {
9036 dm->computeTooltip();
9037 };
9038 results.emplace_back(threadPool.queue(processTooltip));
9039 }
9040 }
9041 }
9042 // wait for the results
9043 for (auto &f : results)
9044 {
9045 f.get();
9046 }
9047 }
9048 else
9049 {
9050 for (const auto &[name,symList] : *Doxygen::symbolMap)
9051 {
9052 for (const auto &def : symList)
9053 {
9055 if (dm && !isSymbolHidden(def) && !def->isArtificial() && def->isLinkableInProject())
9056 {
9057 dm->computeTooltip();
9058 }
9059 }
9060 }
9061 }
9062}
9063
9064//----------------------------------------------------------------------------
9065
9067{
9068 for (const auto &cd : *Doxygen::classLinkedMap)
9069 {
9070 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9071 if (cdm)
9072 {
9073 cdm->setAnonymousEnumType();
9074 }
9075 }
9076}
9077
9078//----------------------------------------------------------------------------
9079
9080static void countMembers()
9081{
9082 for (const auto &cd : *Doxygen::classLinkedMap)
9083 {
9084 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9085 if (cdm)
9086 {
9087 cdm->countMembers();
9088 }
9089 }
9090
9091 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9092 {
9094 if (ndm)
9095 {
9096 ndm->countMembers();
9097 }
9098 }
9099
9100 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9101 {
9102 for (const auto &fd : *fn)
9103 {
9104 fd->countMembers();
9105 }
9106 }
9107
9108 for (const auto &gd : *Doxygen::groupLinkedMap)
9109 {
9110 gd->countMembers();
9111 }
9112
9113 auto &mm = ModuleManager::instance();
9114 mm.countMembers();
9115}
9116
9117
9118//----------------------------------------------------------------------------
9119// generate the documentation for all classes
9120
9121static void generateDocsForClassList(const std::vector<ClassDefMutable*> &classList)
9122{
9123 AUTO_TRACE();
9124 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9125 if (numThreads>1) // multi threaded processing
9126 {
9127 struct DocContext
9128 {
9129 DocContext(ClassDefMutable *cd_,const OutputList &ol_)
9130 : cd(cd_), ol(ol_) {}
9131 ClassDefMutable *cd;
9132 OutputList ol;
9133 };
9134 ThreadPool threadPool(numThreads);
9135 std::vector< std::future< std::shared_ptr<DocContext> > > results;
9136 for (const auto &cd : classList)
9137 {
9138 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
9139 if (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9140 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
9141 )
9142 {
9143 auto ctx = std::make_shared<DocContext>(cd,*g_outputList);
9144 auto processFile = [ctx]()
9145 {
9146 msg("Generating docs for compound {}...\n",ctx->cd->displayName());
9147
9148 // skip external references, anonymous compounds and
9149 // template instances
9150 if (!ctx->cd->isHidden() && !ctx->cd->isEmbeddedInOuterScope() &&
9151 ctx->cd->isLinkableInProject() && !ctx->cd->isImplicitTemplateInstance())
9152 {
9153 ctx->cd->writeDocumentation(ctx->ol);
9154 ctx->cd->writeMemberList(ctx->ol);
9155 }
9156
9157 // even for undocumented classes, the inner classes can be documented.
9158 ctx->cd->writeDocumentationForInnerClasses(ctx->ol);
9159 return ctx;
9160 };
9161 results.emplace_back(threadPool.queue(processFile));
9162 }
9163 }
9164 for (auto &f : results)
9165 {
9166 auto ctx = f.get();
9167 }
9168 }
9169 else // single threaded processing
9170 {
9171 for (const auto &cd : classList)
9172 {
9173 //printf("cd=%s getOuterScope=%p global=%p hidden=%d embeddedInOuterScope=%d\n",
9174 // qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope,cd->isHidden(),cd->isEmbeddedInOuterScope());
9175 if (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9176 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
9177 )
9178 {
9179 // skip external references, anonymous compounds and
9180 // template instances
9181 if ( !cd->isHidden() && !cd->isEmbeddedInOuterScope() &&
9182 cd->isLinkableInProject() && !cd->isImplicitTemplateInstance())
9183 {
9184 msg("Generating docs for compound {}...\n",cd->displayName());
9185
9186 cd->writeDocumentation(*g_outputList);
9187 cd->writeMemberList(*g_outputList);
9188 }
9189 // even for undocumented classes, the inner classes can be documented.
9190 cd->writeDocumentationForInnerClasses(*g_outputList);
9191 }
9192 }
9193 }
9194}
9195
9196static void addClassAndNestedClasses(std::vector<ClassDefMutable*> &list,ClassDefMutable *cd)
9197{
9198 list.push_back(cd);
9199 for (const auto &innerCdi : cd->getClasses())
9200 {
9201 ClassDefMutable *innerCd = toClassDefMutable(innerCdi);
9202 if (innerCd)
9203 {
9204 AUTO_TRACE("innerCd={} isLinkable={} isImplicitTemplateInstance={} protectLevelVisible={} embeddedInOuterScope={}",
9205 innerCd->name(),innerCd->isLinkableInProject(),innerCd->isImplicitTemplateInstance(),protectionLevelVisible(innerCd->protection()),
9206 innerCd->isEmbeddedInOuterScope());
9207 }
9208 if (innerCd && innerCd->isLinkableInProject() && !innerCd->isImplicitTemplateInstance() &&
9209 protectionLevelVisible(innerCd->protection()) &&
9210 !innerCd->isEmbeddedInOuterScope()
9211 )
9212 {
9213 list.push_back(innerCd);
9214 addClassAndNestedClasses(list,innerCd);
9215 }
9216 }
9217}
9218
9220{
9221 std::vector<ClassDefMutable*> classList;
9222 for (const auto &cdi : *Doxygen::classLinkedMap)
9223 {
9224 ClassDefMutable *cd = toClassDefMutable(cdi.get());
9225 if (cd && (cd->getOuterScope()==nullptr ||
9227 {
9228 addClassAndNestedClasses(classList,cd);
9229 }
9230 }
9231 for (const auto &cdi : *Doxygen::hiddenClassLinkedMap)
9232 {
9233 ClassDefMutable *cd = toClassDefMutable(cdi.get());
9234 if (cd && (cd->getOuterScope()==nullptr ||
9236 {
9237 addClassAndNestedClasses(classList,cd);
9238 }
9239 }
9240 generateDocsForClassList(classList);
9241}
9242
9243//----------------------------------------------------------------------------
9244
9246{
9247 for (const auto &cdi : *Doxygen::conceptLinkedMap)
9248 {
9250
9251 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
9252 if (cd &&
9253 (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9254 cd->getOuterScope()==Doxygen::globalScope // only look at global concepts
9255 ) && !cd->isHidden() && cd->isLinkableInProject()
9256 )
9257 {
9258 msg("Generating docs for concept {}...\n",cd->displayName());
9260 }
9261 }
9262}
9263
9264//----------------------------------------------------------------------------
9265
9267{
9268 for (const auto &mn : *Doxygen::memberNameLinkedMap)
9269 {
9270 for (const auto &imd : *mn)
9271 {
9272 MemberDefMutable *md = toMemberDefMutable(imd.get());
9273 //static int count=0;
9274 //printf("%04d Member '%s'\n",count++,qPrint(md->qualifiedName()));
9275 if (md && md->documentation().isEmpty() && md->briefDescription().isEmpty())
9276 { // no documentation yet
9277 const MemberDef *bmd = md->reimplements();
9278 while (bmd && bmd->documentation().isEmpty() &&
9279 bmd->briefDescription().isEmpty()
9280 )
9281 { // search up the inheritance tree for a documentation member
9282 //printf("bmd=%s class=%s\n",qPrint(bmd->name()),qPrint(bmd->getClassDef()->name()));
9283 bmd = bmd->reimplements();
9284 }
9285 if (bmd) // copy the documentation from the reimplemented member
9286 {
9287 md->setInheritsDocsFrom(bmd);
9288 md->setDocumentation(bmd->documentation(),bmd->docFile(),bmd->docLine());
9290 md->setBriefDescription(bmd->briefDescription(),bmd->briefFile(),bmd->briefLine());
9291 md->copyArgumentNames(bmd);
9293 }
9294 }
9295 }
9296 }
9297}
9298
9299//----------------------------------------------------------------------------
9300
9302{
9303 // for each file
9304 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9305 {
9306 for (const auto &fd : *fn)
9307 {
9308 fd->combineUsingRelations();
9309 }
9310 }
9311
9312 // for each namespace
9313 NamespaceDefSet visitedNamespaces;
9314 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9315 {
9317 if (ndm)
9318 {
9319 ndm->combineUsingRelations(visitedNamespaces);
9320 }
9321 }
9322}
9323
9324//----------------------------------------------------------------------------
9325
9327{
9328 // for each class
9329 for (const auto &cd : *Doxygen::classLinkedMap)
9330 {
9331 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9332 if (cdm)
9333 {
9335 }
9336 }
9337 // for each file
9338 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9339 {
9340 for (const auto &fd : *fn)
9341 {
9342 fd->addMembersToMemberGroup();
9343 }
9344 }
9345 // for each namespace
9346 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9347 {
9349 if (ndm)
9350 {
9352 }
9353 }
9354 // for each group
9355 for (const auto &gd : *Doxygen::groupLinkedMap)
9356 {
9357 gd->addMembersToMemberGroup();
9358 }
9360}
9361
9362//----------------------------------------------------------------------------
9363
9365{
9366 // for each class
9367 for (const auto &cd : *Doxygen::classLinkedMap)
9368 {
9369 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9370 if (cdm)
9371 {
9373 }
9374 }
9375 // for each file
9376 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9377 {
9378 for (const auto &fd : *fn)
9379 {
9380 fd->distributeMemberGroupDocumentation();
9381 }
9382 }
9383 // for each namespace
9384 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9385 {
9387 if (ndm)
9388 {
9390 }
9391 }
9392 // for each group
9393 for (const auto &gd : *Doxygen::groupLinkedMap)
9394 {
9395 gd->distributeMemberGroupDocumentation();
9396 }
9398}
9399
9400//----------------------------------------------------------------------------
9401
9403{
9404 // for each class
9405 for (const auto &cd : *Doxygen::classLinkedMap)
9406 {
9407 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9408 if (cdm)
9409 {
9411 }
9412 }
9413 // for each concept
9414 for (const auto &cd : *Doxygen::conceptLinkedMap)
9415 {
9416 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
9417 if (cdm)
9418 {
9420 }
9421 }
9422 // for each file
9423 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9424 {
9425 for (const auto &fd : *fn)
9426 {
9427 fd->findSectionsInDocumentation();
9428 }
9429 }
9430 // for each namespace
9431 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9432 {
9434 if (ndm)
9435 {
9437 }
9438 }
9439 // for each group
9440 for (const auto &gd : *Doxygen::groupLinkedMap)
9441 {
9442 gd->findSectionsInDocumentation();
9443 }
9444 // for each page
9445 for (const auto &pd : *Doxygen::pageLinkedMap)
9446 {
9447 pd->findSectionsInDocumentation();
9448 }
9449 // for each directory
9450 for (const auto &dd : *Doxygen::dirLinkedMap)
9451 {
9452 dd->findSectionsInDocumentation();
9453 }
9455 if (Doxygen::mainPage) Doxygen::mainPage->findSectionsInDocumentation();
9456}
9457
9458//----------------------------------------------------------------------
9459
9460
9462{
9463 // remove all references to classes from the cache
9464 // as there can be new template instances in the inheritance path
9465 // to this class. Optimization: only remove those classes that
9466 // have inheritance instances as direct or indirect sub classes.
9467 StringVector elementsToRemove;
9468 for (const auto &ci : *Doxygen::typeLookupCache)
9469 {
9470 const LookupInfo &li = ci.second;
9471 if (li.definition)
9472 {
9473 elementsToRemove.push_back(ci.first);
9474 }
9475 }
9476 for (const auto &k : elementsToRemove)
9477 {
9478 Doxygen::typeLookupCache->remove(k);
9479 }
9480
9481 // remove all cached typedef resolutions whose target is a
9482 // template class as this may now be a template instance
9483 // for each global function name
9484 for (const auto &fn : *Doxygen::functionNameLinkedMap)
9485 {
9486 // for each function with that name
9487 for (const auto &ifmd : *fn)
9488 {
9489 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
9490 if (fmd && fmd->isTypedefValCached())
9491 {
9492 const ClassDef *cd = fmd->getCachedTypedefVal();
9493 if (cd->isTemplate()) fmd->invalidateTypedefValCache();
9494 }
9495 }
9496 }
9497 // for each class method name
9498 for (const auto &nm : *Doxygen::memberNameLinkedMap)
9499 {
9500 // for each function with that name
9501 for (const auto &imd : *nm)
9502 {
9503 MemberDefMutable *md = toMemberDefMutable(imd.get());
9504 if (md && md->isTypedefValCached())
9505 {
9506 const ClassDef *cd = md->getCachedTypedefVal();
9507 if (cd->isTemplate()) md->invalidateTypedefValCache();
9508 }
9509 }
9510 }
9511}
9512
9513//----------------------------------------------------------------------------
9514
9516{
9517 // Remove all unresolved references to classes from the cache.
9518 // This is needed before resolving the inheritance relations, since
9519 // it would otherwise not find the inheritance relation
9520 // for C in the example below, as B::I was already found to be unresolvable
9521 // (which is correct if you ignore the inheritance relation between A and B).
9522 //
9523 // class A { class I {} };
9524 // class B : public A {};
9525 // class C : public B::I {};
9526
9527 StringVector elementsToRemove;
9528 for (const auto &ci : *Doxygen::typeLookupCache)
9529 {
9530 const LookupInfo &li = ci.second;
9531 if (li.definition==nullptr && li.typeDef==nullptr)
9532 {
9533 elementsToRemove.push_back(ci.first);
9534 }
9535 }
9536 for (const auto &k : elementsToRemove)
9537 {
9538 Doxygen::typeLookupCache->remove(k);
9539 }
9540
9541 // for each global function name
9542 for (const auto &fn : *Doxygen::functionNameLinkedMap)
9543 {
9544 // for each function with that name
9545 for (const auto &ifmd : *fn)
9546 {
9547 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
9548 if (fmd)
9549 {
9551 }
9552 }
9553 }
9554 // for each class method name
9555 for (const auto &nm : *Doxygen::memberNameLinkedMap)
9556 {
9557 // for each function with that name
9558 for (const auto &imd : *nm)
9559 {
9560 MemberDefMutable *md = toMemberDefMutable(imd.get());
9561 if (md)
9562 {
9564 }
9565 }
9566 }
9567
9568}
9569
9570//----------------------------------------------------------------------------
9571// Returns TRUE if the entry and member definition have equal file names,
9572// otherwise FALSE.
9573
9574static bool haveEqualFileNames(const Entry *root, const MemberDef *md)
9575{
9576 if (const FileDef *fd = md->getFileDef())
9577 {
9578 return fd->absFilePath() == root->fileName;
9579 }
9580 return false;
9581}
9582
9583//----------------------------------------------------------------------------
9584
9585static void addDefineDoc(const Entry *root, MemberDefMutable *md)
9586{
9587 md->setDocumentation(root->doc,root->docFile,root->docLine);
9588 md->setDocsForDefinition(!root->proto);
9589 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9590 if (md->inbodyDocumentation().isEmpty())
9591 {
9593 }
9594 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
9595 {
9596 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
9597 md->setBodyDef(root->fileDef());
9598 }
9600 md->setMaxInitLines(root->initLines);
9602 md->setRefItems(root->sli);
9603 md->setRequirementReferences(root->rqli);
9604 md->addQualifiers(root->qualifiers);
9605 if (root->mGrpId!=-1) md->setMemberGroupId(root->mGrpId);
9606 addMemberToGroups(root,md);
9608}
9609
9610//----------------------------------------------------------------------------
9611
9613{
9614 if ((root->section.isDefineDoc() || root->section.isDefine()) && !root->name.isEmpty())
9615 {
9616 //printf("found define '%s' '%s' brief='%s' doc='%s'\n",
9617 // qPrint(root->name),qPrint(root->args),qPrint(root->brief),qPrint(root->doc));
9618
9619 if (root->tagInfo() && !root->name.isEmpty()) // define read from a tag file
9620 {
9621 auto md = createMemberDef(root->tagInfo()->tagName,1,1,
9622 "#define",root->name,root->args,QCString(),
9623 Protection::Public,Specifier::Normal,FALSE,Relationship::Member,MemberType::Define,
9624 ArgumentList(),ArgumentList(),"");
9625 auto mmd = toMemberDefMutable(md.get());
9626 mmd->setTagInfo(root->tagInfo());
9627 mmd->setLanguage(root->lang);
9628 mmd->addQualifiers(root->qualifiers);
9629 //printf("Searching for '%s' fd=%p\n",qPrint(filePathName),fd);
9630 mmd->setFileDef(root->parent()->fileDef());
9631 //printf("Adding member=%s\n",qPrint(md->name()));
9633 mn->push_back(std::move(md));
9634 }
9636 if (mn)
9637 {
9638 int count=0;
9639 for (const auto &md : *mn)
9640 {
9641 if (md->memberType()==MemberType::Define) count++;
9642 }
9643 if (count==1)
9644 {
9645 for (const auto &imd : *mn)
9646 {
9647 MemberDefMutable *md = toMemberDefMutable(imd.get());
9648 if (md && md->memberType()==MemberType::Define)
9649 {
9650 addDefineDoc(root,md);
9651 }
9652 }
9653 }
9654 else if (count>1 &&
9655 (!root->doc.isEmpty() ||
9656 !root->brief.isEmpty() ||
9657 root->bodyLine!=-1
9658 )
9659 )
9660 // multiple defines don't know where to add docs
9661 // but maybe they are in different files together with their documentation
9662 {
9663 for (const auto &imd : *mn)
9664 {
9665 MemberDefMutable *md = toMemberDefMutable(imd.get());
9666 if (md && md->memberType()==MemberType::Define)
9667 {
9668 if (haveEqualFileNames(root, md) || isEntryInGroupOfMember(root, md))
9669 // doc and define in the same file or group assume they belong together.
9670 {
9671 addDefineDoc(root,md);
9672 }
9673 }
9674 }
9675 //warn("define {} found in the following files:\n",root->name);
9676 //warn("Cannot determine where to add the documentation found "
9677 // "at line {} of file {}. \n",
9678 // root->startLine,root->fileName);
9679 }
9680 }
9681 else if (!root->doc.isEmpty() || !root->brief.isEmpty()) // define not found
9682 {
9683 bool preEnabled = Config_getBool(ENABLE_PREPROCESSING);
9684 if (preEnabled)
9685 {
9686 warn(root->fileName,root->startLine,"documentation for unknown define {} found.",root->name);
9687 }
9688 else
9689 {
9690 warn(root->fileName,root->startLine, "found documented #define {} but ignoring it because ENABLE_PREPROCESSING is NO.", root->name);
9691 }
9692 }
9693 }
9694 for (const auto &e : root->children()) findDefineDocumentation(e.get());
9695}
9696
9697//----------------------------------------------------------------------------
9698
9699static void findDirDocumentation(const Entry *root)
9700{
9701 if (root->section.isDirDoc())
9702 {
9703 QCString normalizedName = root->name;
9704 normalizedName = substitute(normalizedName,"\\","/");
9705 //printf("root->docFile=%s normalizedName=%s\n",
9706 // qPrint(root->docFile),qPrint(normalizedName));
9707 if (root->docFile==normalizedName) // current dir?
9708 {
9709 int lastSlashPos=normalizedName.findRev('/');
9710 if (lastSlashPos!=-1) // strip file name
9711 {
9712 normalizedName=normalizedName.left(lastSlashPos);
9713 }
9714 }
9715 if (normalizedName.at(normalizedName.length()-1)!='/')
9716 {
9717 normalizedName+='/';
9718 }
9719 DirDef *matchingDir=nullptr;
9720 for (const auto &dir : *Doxygen::dirLinkedMap)
9721 {
9722 //printf("Dir: %s<->%s\n",qPrint(dir->name()),qPrint(normalizedName));
9723 if (dir->name().right(normalizedName.length())==normalizedName)
9724 {
9725 if (matchingDir)
9726 {
9727 warn(root->fileName,root->startLine,
9728 "\\dir command matches multiple directories.\n"
9729 " Applying the command for directory {}\n"
9730 " Ignoring the command for directory {}",
9731 matchingDir->name(),dir->name()
9732 );
9733 }
9734 else
9735 {
9736 matchingDir=dir.get();
9737 }
9738 }
9739 }
9740 if (matchingDir)
9741 {
9742 //printf("Match for with dir %s #anchor=%zu\n",qPrint(matchingDir->name()),root->anchors.size());
9743 matchingDir->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9744 matchingDir->setDocumentation(root->doc,root->docFile,root->docLine);
9745 matchingDir->setRefItems(root->sli);
9746 matchingDir->setRequirementReferences(root->rqli);
9747 matchingDir->addSectionsToDefinition(root->anchors);
9748 root->commandOverrides.apply_directoryGraph([&](bool b) { matchingDir->overrideDirectoryGraph(b); });
9749 addDirToGroups(root,matchingDir);
9750 }
9751 else
9752 {
9753 warn(root->fileName,root->startLine,"No matching directory found for command \\dir {}",normalizedName);
9754 }
9755 }
9756 for (const auto &e : root->children()) findDirDocumentation(e.get());
9757}
9758
9759//----------------------------------------------------------------------------
9761{
9762 if (root->section.isRequirementDoc())
9763 {
9765 }
9766 for (const auto &e : root->children()) buildRequirementsList(e.get());
9767}
9768
9769//----------------------------------------------------------------------------
9770// create a (sorted) list of separate documentation pages
9771
9772static void buildPageList(Entry *root)
9773{
9774 if (root->section.isPageDoc())
9775 {
9776 if (!root->name.isEmpty())
9777 {
9778 addRelatedPage(root);
9779 }
9780 }
9781 else if (root->section.isMainpageDoc())
9782 {
9783 QCString title=root->args.stripWhiteSpace();
9784 if (title.isEmpty()) title=theTranslator->trMainPage();
9785 //QCString name = Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9786 QCString name = "index";
9787 addRefItem(root->sli,
9788 name,
9789 theTranslator->trPage(TRUE,TRUE),
9790 name,
9791 title,
9792 QCString(),nullptr
9793 );
9794 }
9795 for (const auto &e : root->children()) buildPageList(e.get());
9796}
9797
9798// search for the main page defined in this project
9799static void findMainPage(Entry *root)
9800{
9801 if (root->section.isMainpageDoc())
9802 {
9803 if (Doxygen::mainPage==nullptr && root->tagInfo()==nullptr)
9804 {
9805 //printf("mainpage: docLine=%d startLine=%d\n",root->docLine,root->startLine);
9806 //printf("Found main page! \n======\n%s\n=======\n",qPrint(root->doc));
9807 QCString title=root->args.stripWhiteSpace();
9808 if (title.isEmpty()) title = Config_getString(PROJECT_NAME);
9809 //QCString indexName=Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9810 QCString indexName="index";
9812 indexName, root->brief+root->doc+root->inbodyDocs,title);
9813 //setFileNameForSections(root->anchors,"index",Doxygen::mainPage);
9814 Doxygen::mainPage->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9815 Doxygen::mainPage->setBodySegment(root->startLine,root->startLine,-1);
9816 Doxygen::mainPage->setFileName(indexName);
9817 Doxygen::mainPage->setLocalToc(root->localToc);
9819
9821 if (si)
9822 {
9823 if (!si->ref().isEmpty()) // we are from a tag file
9824 {
9825 // a page name is a label as well! but should no be double either
9827 Doxygen::mainPage->name(),
9828 indexName,
9829 root->startLine,
9830 Doxygen::mainPage->title(),
9832 0); // level 0
9833 }
9834 else if (si->lineNr() != -1)
9835 {
9836 warn(root->fileName,root->startLine,"multiple use of section label '{}' for main page, (first occurrence: {}, line {})",
9837 Doxygen::mainPage->name(),si->fileName(),si->lineNr());
9838 }
9839 else
9840 {
9841 warn(root->fileName,root->startLine,"multiple use of section label '{}' for main page, (first occurrence: {})",
9842 Doxygen::mainPage->name(),si->fileName());
9843 }
9844 }
9845 else
9846 {
9847 // a page name is a label as well! but should no be double either
9849 Doxygen::mainPage->name(),
9850 indexName,
9851 root->startLine,
9852 Doxygen::mainPage->title(),
9854 0); // level 0
9855 }
9856 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9857 }
9858 else if (root->tagInfo()==nullptr)
9859 {
9860 warn(root->fileName,root->startLine,
9861 "found more than one \\mainpage comment block! (first occurrence: {}, line {}), Skipping current block!",
9862 Doxygen::mainPage->docFile(),Doxygen::mainPage->getStartBodyLine());
9863 }
9864 }
9865 for (const auto &e : root->children()) findMainPage(e.get());
9866}
9867
9868// search for the main page imported via tag files and add only the section labels
9869static void findMainPageTagFiles(Entry *root)
9870{
9871 if (root->section.isMainpageDoc())
9872 {
9873 if (Doxygen::mainPage && root->tagInfo())
9874 {
9875 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9876 }
9877 }
9878 for (const auto &e : root->children()) findMainPageTagFiles(e.get());
9879}
9880
9881static void computePageRelations(Entry *root)
9882{
9883 if ((root->section.isPageDoc() || root->section.isMainpageDoc()) && !root->name.isEmpty())
9884 {
9885 PageDef *pd = root->section.isPageDoc() ?
9886 Doxygen::pageLinkedMap->find(root->name) :
9887 Doxygen::mainPage.get();
9888 if (pd)
9889 {
9890 for (const BaseInfo &bi : root->extends)
9891 {
9892 PageDef *subPd = Doxygen::pageLinkedMap->find(bi.name);
9893 if (pd==subPd)
9894 {
9895 term("page defined {} with label {} is a direct "
9896 "subpage of itself! Please remove this cyclic dependency.\n",
9897 warn_line(pd->docFile(),pd->docLine()),pd->name());
9898 }
9899 else if (subPd)
9900 {
9901 pd->addInnerCompound(subPd);
9902 //printf("*** Added subpage relation: %s->%s\n",
9903 // qPrint(pd->name()),qPrint(subPd->name()));
9904 }
9905 }
9906 }
9907 }
9908 for (const auto &e : root->children()) computePageRelations(e.get());
9909}
9910
9912{
9913 for (const auto &pd : *Doxygen::pageLinkedMap)
9914 {
9915 Definition *ppd = pd->getOuterScope();
9916 while (ppd)
9917 {
9918 if (ppd==pd.get())
9919 {
9920 term("page defined {} with label {} is a subpage "
9921 "of itself! Please remove this cyclic dependency.\n",
9922 warn_line(pd->docFile(),pd->docLine()),pd->name());
9923 }
9924 ppd=ppd->getOuterScope();
9925 }
9926 }
9927}
9928
9929//----------------------------------------------------------------------------
9930
9932{
9933 for (const auto &si : SectionManager::instance())
9934 {
9935 //printf("si->label='%s' si->definition=%s si->fileName='%s'\n",
9936 // qPrint(si->label),si->definition?qPrint(si->definition->name()):"<none>",
9937 // qPrint(si->fileName));
9938 PageDef *pd=nullptr;
9939
9940 // hack: the items of a todo/test/bug/deprecated list are all fragments from
9941 // different files, so the resulting section's all have the wrong file
9942 // name (not from the todo/test/bug/deprecated list, but from the file in
9943 // which they are defined). We correct this here by looking at the
9944 // generated section labels!
9946 {
9947 QCString label="_"+rl->listName(); // "_todo", "_test", ...
9948 if (si->label().left(label.length())==label)
9949 {
9950 si->setFileName(rl->listName());
9951 si->setGenerated(TRUE);
9952 break;
9953 }
9954 }
9955
9956 //printf("start: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
9957 if (!si->generated())
9958 {
9959 // if this section is in a page and the page is in a group, then we
9960 // have to adjust the link file name to point to the group.
9961 if (!si->fileName().isEmpty() &&
9962 (pd=Doxygen::pageLinkedMap->find(si->fileName())) &&
9963 pd->getGroupDef())
9964 {
9965 si->setFileName(pd->getGroupDef()->getOutputFileBase());
9966 }
9967
9968 if (si->definition())
9969 {
9970 // TODO: there should be one function in Definition that returns
9971 // the file to link to, so we can avoid the following tests.
9972 const GroupDef *gd=nullptr;
9973 if (si->definition()->definitionType()==Definition::TypeMember)
9974 {
9975 gd = (toMemberDef(si->definition()))->getGroupDef();
9976 }
9977
9978 if (gd)
9979 {
9980 si->setFileName(gd->getOutputFileBase());
9981 }
9982 else
9983 {
9984 //si->fileName=si->definition->getOutputFileBase();
9985 //printf("Setting si->fileName to %s\n",qPrint(si->fileName));
9986 }
9987 }
9988 }
9989 //printf("end: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
9990 }
9991}
9992
9993
9994
9995//----------------------------------------------------------------------------
9996// generate all separate documentation pages
9997
9998
9999static void generatePageDocs()
10000{
10001 //printf("documentedPages=%d real=%d\n",documentedPages,Doxygen::pageLinkedMap->count());
10002 if (Index::instance().numDocumentedPages()==0) return;
10003 for (const auto &pd : *Doxygen::pageLinkedMap)
10004 {
10005 if (!pd->getGroupDef() && !pd->isReference())
10006 {
10007 msg("Generating docs for page {}...\n",pd->name());
10008 pd->writeDocumentation(*g_outputList);
10009 }
10010 }
10011}
10012
10013//----------------------------------------------------------------------------
10014// create a (sorted) list & dictionary of example pages
10015
10016static void buildExampleList(Entry *root)
10017{
10018 if ((root->section.isExample() || root->section.isExampleLineno()) && !root->name.isEmpty())
10019 {
10020 if (Doxygen::exampleLinkedMap->find(root->name))
10021 {
10022 warn(root->fileName,root->startLine,"Example {} was already documented. Ignoring documentation found here.",root->name);
10023 }
10024 else
10025 {
10026 PageDef *pd = Doxygen::exampleLinkedMap->add(root->name,
10027 createPageDef(root->fileName,root->startLine,
10028 root->name,root->brief+root->doc+root->inbodyDocs,root->args));
10029 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
10030 pd->setFileName(convertNameToFile(pd->name()+"-example",FALSE,TRUE));
10032 pd->setLanguage(root->lang);
10033 pd->setShowLineNo(root->section.isExampleLineno());
10034
10035 //we don't add example to groups
10036 //addExampleToGroups(root,pd);
10037 }
10038 }
10039 for (const auto &e : root->children()) buildExampleList(e.get());
10040}
10041
10042//----------------------------------------------------------------------------
10043// prints the Entry tree (for debugging)
10044
10045void printNavTree(Entry *root,int indent)
10046{
10048 {
10049 QCString indentStr;
10050 indentStr.fill(' ',indent);
10051 Debug::print(Debug::Entries,0,"{}{} at {}:{} (sec={}, spec={})\n",
10052 indentStr.isEmpty()?"":indentStr,
10053 root->name.isEmpty()?"<empty>":root->name,
10054 root->fileName,root->startLine,
10055 root->section.to_string(),
10056 root->spec.to_string());
10057 for (const auto &e : root->children())
10058 {
10059 printNavTree(e.get(),indent+2);
10060 }
10061 }
10062}
10063
10064
10065//----------------------------------------------------------------------------
10066// prints the Sections tree (for debugging)
10067
10069{
10071 {
10072 for (const auto &si : SectionManager::instance())
10073 {
10074 Debug::print(Debug::Sections,0,"Section = {}, file = {}, title = {}, type = {}, ref = {}\n",
10075 si->label(),si->fileName(),si->title(),si->type().level(),si->ref());
10076 }
10077 }
10078}
10079
10080
10081//----------------------------------------------------------------------------
10082// generate the example documentation
10083
10085{
10086 g_outputList->disable(OutputType::Man);
10087 for (const auto &pd : *Doxygen::exampleLinkedMap)
10088 {
10089 msg("Generating docs for example {}...\n",pd->name());
10090 SrcLangExt lang = getLanguageFromFileName(pd->name(), SrcLangExt::Unknown);
10091 if (lang != SrcLangExt::Unknown)
10092 {
10093 QCString ext = getFileNameExtension(pd->name());
10094 auto intf = Doxygen::parserManager->getCodeParser(ext);
10095 intf->resetCodeParserState();
10096 }
10097 QCString n=pd->getOutputFileBase();
10098 startFile(*g_outputList,n,false,n,pd->name());
10100 g_outputList->docify(pd->name());
10102 g_outputList->startContents();
10103 QCString lineNoOptStr;
10104 if (pd->showLineNo())
10105 {
10106 lineNoOptStr="{lineno}";
10107 }
10108 g_outputList->generateDoc(pd->docFile(), // file
10109 pd->docLine(), // startLine
10110 pd.get(), // context
10111 nullptr, // memberDef
10112 (pd->briefDescription().isEmpty()?"":pd->briefDescription()+"\n\n")+
10113 pd->documentation()+"\n\n\\include"+lineNoOptStr+" "+pd->name(), // docs
10114 DocOptions()
10115 .setIndexWords(true)
10116 .setExample(pd->name()));
10117 endFile(*g_outputList); // contains g_outputList->endContents()
10118 }
10120}
10121
10122//----------------------------------------------------------------------------
10123// generate module pages
10124
10126{
10127 for (const auto &gd : *Doxygen::groupLinkedMap)
10128 {
10129 if (!gd->isReference())
10130 {
10131 gd->writeDocumentation(*g_outputList);
10132 }
10133 }
10134}
10135
10136//----------------------------------------------------------------------------
10137// generate module pages
10138
10140{
10141 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10142 if (numThreads>1) // multi threaded processing
10143 {
10144 struct DocContext
10145 {
10146 DocContext(ClassDefMutable *cdm_,const OutputList &ol_)
10147 : cdm(cdm_), ol(ol_) {}
10148 ClassDefMutable *cdm;
10149 OutputList ol;
10150 };
10151 ThreadPool threadPool(numThreads);
10152 std::vector< std::future< std::shared_ptr<DocContext> > > results;
10153 // for each class in the namespace...
10154 for (const auto &cd : classList)
10155 {
10157 if (cdm)
10158 {
10159 auto ctx = std::make_shared<DocContext>(cdm,*g_outputList);
10160 auto processFile = [ctx]()
10161 {
10162 if ( ( ctx->cdm->isLinkableInProject() &&
10163 !ctx->cdm->isImplicitTemplateInstance()
10164 ) // skip external references, anonymous compounds and
10165 // template instances and nested classes
10166 && !ctx->cdm->isHidden() && !ctx->cdm->isEmbeddedInOuterScope()
10167 )
10168 {
10169 msg("Generating docs for compound {}...\n",ctx->cdm->displayName());
10170 ctx->cdm->writeDocumentation(ctx->ol);
10171 ctx->cdm->writeMemberList(ctx->ol);
10172 }
10173 ctx->cdm->writeDocumentationForInnerClasses(ctx->ol);
10174 return ctx;
10175 };
10176 results.emplace_back(threadPool.queue(processFile));
10177 }
10178 }
10179 // wait for the results
10180 for (auto &f : results)
10181 {
10182 auto ctx = f.get();
10183 }
10184 }
10185 else // single threaded processing
10186 {
10187 // for each class in the namespace...
10188 for (const auto &cd : classList)
10189 {
10191 if (cdm)
10192 {
10193 if ( ( cd->isLinkableInProject() &&
10194 !cd->isImplicitTemplateInstance()
10195 ) // skip external references, anonymous compounds and
10196 // template instances and nested classes
10197 && !cd->isHidden() && !cd->isEmbeddedInOuterScope()
10198 )
10199 {
10200 msg("Generating docs for compound {}...\n",cd->displayName());
10201
10204 }
10206 }
10207 }
10208 }
10209}
10210
10212{
10213 // for each concept in the namespace...
10214 for (const auto &cd : conceptList)
10215 {
10217 if ( cdm && cd->isLinkableInProject() && !cd->isHidden())
10218 {
10219 msg("Generating docs for concept {}...\n",cd->name());
10221 }
10222 }
10223}
10224
10226{
10227 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
10228
10229 //writeNamespaceIndex(*g_outputList);
10230
10231 // for each namespace...
10232 for (const auto &nd : *Doxygen::namespaceLinkedMap)
10233 {
10234 if (nd->isLinkableInProject())
10235 {
10237 if (ndm)
10238 {
10239 msg("Generating docs for namespace {}\n",nd->displayName());
10241 }
10242 }
10243
10244 generateNamespaceClassDocs(nd->getClasses());
10245 if (sliceOpt)
10246 {
10247 generateNamespaceClassDocs(nd->getInterfaces());
10248 generateNamespaceClassDocs(nd->getStructs());
10249 generateNamespaceClassDocs(nd->getExceptions());
10250 }
10251 generateNamespaceConceptDocs(nd->getConcepts());
10252 }
10253}
10254
10256{
10257 std::string oldDir = Dir::currentDirPath();
10258 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
10261 {
10262 err("failed to run html help compiler on {}\n", HtmlHelp::hhpFileName);
10263 }
10264 Dir::setCurrent(oldDir);
10265}
10266
10268{
10269 QCString args = Qhp::qhpFileName + " -o \"" + Qhp::getQchFileName() + "\"";
10270 std::string oldDir = Dir::currentDirPath();
10271 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
10272
10273 QCString qhgLocation=Config_getString(QHG_LOCATION);
10274 if (Debug::isFlagSet(Debug::Qhp)) // produce info for debugging
10275 {
10276 // run qhelpgenerator -v and extract the Qt version used
10277 QCString cmd=qhgLocation+ " -v 2>&1";
10278 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
10279 FILE *f=Portable::popen(cmd,"r");
10280 if (!f)
10281 {
10282 err("could not execute {}\n",qhgLocation);
10283 }
10284 else
10285 {
10286 const size_t bufSize = 1024;
10287 char inBuf[bufSize+1];
10288 size_t numRead=fread(inBuf,1,bufSize,f);
10289 inBuf[numRead] = '\0';
10290 Debug::print(Debug::Qhp,0,"{}",inBuf);
10292
10293 int qtVersion=0;
10294 static const reg::Ex versionReg(R"(Qt (\d+)\.(\d+)\.(\d+))");
10295 reg::Match match;
10296 std::string s = inBuf;
10297 if (reg::search(s,match,versionReg))
10298 {
10299 qtVersion = 10000*QCString(match[1].str()).toInt() +
10300 100*QCString(match[2].str()).toInt() +
10301 QCString(match[3].str()).toInt();
10302 }
10303 if (qtVersion>0 && (qtVersion<60000 || qtVersion >= 60205))
10304 {
10305 // dump the output of qhelpgenerator -c file.qhp
10306 // Qt<6 or Qt>=6.2.5 or higher, see https://bugreports.qt.io/browse/QTBUG-101070
10307 cmd=qhgLocation+ " -c " + Qhp::qhpFileName + " 2>&1";
10308 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
10309 f=Portable::popen(cmd,"r");
10310 if (!f)
10311 {
10312 err("could not execute {}\n",qhgLocation);
10313 }
10314 else
10315 {
10316 std::string output;
10317 while ((numRead=fread(inBuf,1,bufSize,f))>0)
10318 {
10319 inBuf[numRead] = '\0';
10320 output += inBuf;
10321 }
10323 Debug::print(Debug::Qhp,0,"{}",output);
10324 }
10325 }
10326 }
10327 }
10328
10329 if (Portable::system(qhgLocation, args, FALSE))
10330 {
10331 err("failed to run qhelpgenerator on {}\n",Qhp::qhpFileName);
10332 }
10333 Dir::setCurrent(oldDir);
10334}
10335
10336//----------------------------------------------------------------------------
10337
10339{
10340 // check dot path
10341 QCString dotPath = Config_getString(DOT_PATH);
10342 if (!dotPath.isEmpty())
10343 {
10344 FileInfo fi(dotPath.str());
10345 if (!(fi.exists() && fi.isFile()) )// not an existing user specified path + exec
10346 {
10347 dotPath = dotPath+"/dot"+Portable::commandExtension();
10348 FileInfo dp(dotPath.str());
10349 if (!dp.exists() || !dp.isFile())
10350 {
10351 warn_uncond("the dot tool could not be found as '{}'\n",dotPath);
10352 dotPath = "dot";
10353 dotPath += Portable::commandExtension();
10354 }
10355 }
10356#if defined(_WIN32) // convert slashes
10357 size_t l=dotPath.length();
10358 for (size_t i=0;i<l;i++) if (dotPath.at(i)=='/') dotPath.at(i)='\\';
10359#endif
10360 }
10361 else
10362 {
10363 dotPath = "dot";
10364 dotPath += Portable::commandExtension();
10365 }
10366 Doxygen::verifiedDotPath = dotPath;
10368}
10369
10370//----------------------------------------------------------------------------
10371
10372/*! Generate a template version of the configuration file.
10373 * If the \a shortList parameter is TRUE a configuration file without
10374 * comments will be generated.
10375 */
10376static void generateConfigFile(const QCString &configFile,bool shortList,
10377 bool updateOnly=FALSE)
10378{
10379 std::ofstream f;
10380 bool fileOpened=openOutputFile(configFile,f);
10381 bool writeToStdout=configFile=="-";
10382 if (fileOpened)
10383 {
10384 TextStream t(&f);
10385 Config::writeTemplate(t,shortList,updateOnly);
10386 if (!writeToStdout)
10387 {
10388 if (!updateOnly)
10389 {
10390 msg("\n\nConfiguration file '{}' created.\n\n",configFile);
10391 msg("Now edit the configuration file and enter\n\n");
10392 if (configFile!="Doxyfile" && configFile!="doxyfile")
10393 msg(" doxygen {}\n\n",configFile);
10394 else
10395 msg(" doxygen\n\n");
10396 msg("to generate the documentation for your project\n\n");
10397 }
10398 else
10399 {
10400 msg("\n\nConfiguration file '{}' updated.\n\n",configFile);
10401 }
10402 }
10403 }
10404 else
10405 {
10406 term("Cannot open file {} for writing\n",configFile);
10407 }
10408}
10409
10411{
10412 std::ofstream f;
10413 bool fileOpened=openOutputFile("-",f);
10414 if (fileOpened)
10415 {
10416 TextStream t(&f);
10417 Config::compareDoxyfile(t,diffList);
10418 }
10419 else
10420 {
10421 term("Cannot open stdout for writing\n");
10422 }
10423}
10424
10425//----------------------------------------------------------------------------
10426// read and parse a tag file
10427
10428static void readTagFile(const std::shared_ptr<Entry> &root,const QCString &tagLine)
10429{
10430 QCString fileName;
10431 QCString destName;
10432 int eqPos = tagLine.find('=');
10433 if (eqPos!=-1) // tag command contains a destination
10434 {
10435 fileName = tagLine.left(eqPos).stripWhiteSpace();
10436 destName = tagLine.right(tagLine.length()-eqPos-1).stripWhiteSpace();
10437 if (fileName.isEmpty() || destName.isEmpty()) return;
10438 //printf("insert tagDestination %s->%s\n",qPrint(fi.fileName()),qPrint(destName));
10439 }
10440 else
10441 {
10442 fileName = tagLine;
10443 }
10444
10445 FileInfo fi(fileName.str());
10446 if (!fi.exists() || !fi.isFile())
10447 {
10448 err("Tag file '{}' does not exist or is not a file. Skipping it...\n",fileName);
10449 return;
10450 }
10451
10452 if (Doxygen::tagFileSet.find(fi.absFilePath()) != Doxygen::tagFileSet.end()) return;
10453
10454 Doxygen::tagFileSet.emplace(fi.absFilePath());
10455
10456 if (!destName.isEmpty())
10457 {
10458 Doxygen::tagDestinationMap.emplace(fi.absFilePath(), destName.str());
10459 msg("Reading tag file '{}', location '{}'...\n",fileName,destName);
10460 }
10461 else
10462 {
10463 msg("Reading tag file '{}'...\n",fileName);
10464 }
10465
10466 parseTagFile(root,fi.absFilePath().c_str());
10467}
10468
10469//----------------------------------------------------------------------------
10471{
10472 const StringVector &latexExtraStyleSheet = Config_getList(LATEX_EXTRA_STYLESHEET);
10473 for (const auto &sheet : latexExtraStyleSheet)
10474 {
10475 std::string fileName = sheet;
10476 if (!fileName.empty())
10477 {
10478 FileInfo fi(fileName);
10479 if (!fi.exists())
10480 {
10481 err("Style sheet '{}' specified by LATEX_EXTRA_STYLESHEET does not exist!\n",fileName);
10482 }
10483 else if (fi.isDir())
10484 {
10485 err("Style sheet '{}' specified by LATEX_EXTRA_STYLESHEET is a directory, it has to be a file!\n", fileName);
10486 }
10487 else
10488 {
10489 QCString destFileName = Config_getString(LATEX_OUTPUT)+"/"+fi.fileName();
10491 {
10492 destFileName += LATEX_STYLE_EXTENSION;
10493 }
10494 copyFile(fileName, destFileName);
10495 }
10496 }
10497 }
10498}
10499
10500//----------------------------------------------------------------------------
10501static void copyStyleSheet()
10502{
10503 QCString htmlStyleSheet = Config_getString(HTML_STYLESHEET);
10504 if (!htmlStyleSheet.isEmpty())
10505 {
10506 if (!htmlStyleSheet.startsWith("http:") && !htmlStyleSheet.startsWith("https:"))
10507 {
10508 FileInfo fi(htmlStyleSheet.str());
10509 if (!fi.exists())
10510 {
10511 err("Style sheet '{}' specified by HTML_STYLESHEET does not exist!\n",htmlStyleSheet);
10512 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
10513 }
10514 else if (fi.isDir())
10515 {
10516 err("Style sheet '{}' specified by HTML_STYLESHEET is a directory, it has to be a file!\n",htmlStyleSheet);
10517 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
10518 }
10519 else
10520 {
10521 QCString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
10522 copyFile(htmlStyleSheet,destFileName);
10523 }
10524 }
10525 }
10526 const StringVector &htmlExtraStyleSheet = Config_getList(HTML_EXTRA_STYLESHEET);
10527 for (const auto &sheet : htmlExtraStyleSheet)
10528 {
10529 QCString fileName(sheet);
10530 if (!fileName.isEmpty() && !fileName.startsWith("http:") && !fileName.startsWith("https:"))
10531 {
10532 FileInfo fi(fileName.str());
10533 if (!fi.exists())
10534 {
10535 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET does not exist!\n",fileName);
10536 }
10537 else if (fi.fileName()=="doxygen.css" || fi.fileName()=="tabs.css" || fi.fileName()=="navtree.css")
10538 {
10539 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET is already a built-in stylesheet. Please use a different name\n",fi.fileName());
10540 }
10541 else if (fi.isDir())
10542 {
10543 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET is a directory, it has to be a file!\n",fileName);
10544 }
10545 else
10546 {
10547 QCString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
10548 copyFile(fileName, destFileName);
10549 }
10550 }
10551 }
10552}
10553
10554static void copyLogo(const QCString &outputOption)
10555{
10556 QCString projectLogo = projectLogoFile();
10557 if (!projectLogo.isEmpty())
10558 {
10559 FileInfo fi(projectLogo.str());
10560 if (!fi.exists())
10561 {
10562 err("Project logo '{}' specified by PROJECT_LOGO does not exist!\n",projectLogo);
10563 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
10564 }
10565 else if (fi.isDir())
10566 {
10567 err("Project logo '{}' specified by PROJECT_LOGO is a directory, it has to be a file!\n",projectLogo);
10568 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
10569 }
10570 else
10571 {
10572 QCString destFileName = outputOption+"/"+fi.fileName();
10573 copyFile(projectLogo,destFileName);
10574 Doxygen::indexList->addImageFile(fi.fileName());
10575 }
10576 }
10577}
10578
10579static void copyIcon(const QCString &outputOption)
10580{
10581 QCString projectIcon = Config_getString(PROJECT_ICON);
10582 if (!projectIcon.isEmpty())
10583 {
10584 FileInfo fi(projectIcon.str());
10585 if (!fi.exists())
10586 {
10587 err("Project icon '{}' specified by PROJECT_ICON does not exist!\n",projectIcon);
10588 projectIcon = Config_updateString(PROJECT_ICON,""); // revert to the default
10589 }
10590 else if (fi.isDir())
10591 {
10592 err("Project icon '{}' specified by PROJECT_ICON is a directory, it has to be a file!\n",projectIcon);
10593 projectIcon = Config_updateString(PROJECT_ICON,""); // revert to the default
10594 }
10595 else
10596 {
10597 QCString destFileName = outputOption+"/"+fi.fileName();
10598 copyFile(projectIcon,destFileName);
10599 Doxygen::indexList->addImageFile(fi.fileName());
10600 }
10601 }
10602}
10603
10604static void copyExtraFiles(const StringVector &files,const QCString &filesOption,const QCString &outputOption)
10605{
10606 for (const auto &fileName : files)
10607 {
10608 if (!fileName.empty())
10609 {
10610 FileInfo fi(fileName);
10611 if (!fi.exists())
10612 {
10613 err("Extra file '{}' specified in {} does not exist!\n", fileName,filesOption);
10614 }
10615 else if (fi.isDir())
10616 {
10617 err("Extra file '{}' specified in {} is a directory, it has to be a file!\n", fileName,filesOption);
10618 }
10619 else
10620 {
10621 QCString destFileName = outputOption+"/"+fi.fileName();
10622 Doxygen::indexList->addImageFile(fi.fileName());
10623 copyFile(fileName, destFileName);
10624 }
10625 }
10626 }
10627}
10628
10629//----------------------------------------------------------------------------
10630
10632{
10633 for (const auto &fn : *Doxygen::inputNameLinkedMap)
10634 {
10635 struct FileEntry
10636 {
10637 FileEntry(const QCString &p,FileDef *fd) : path(p), fileDef(fd) {}
10638 QCString path;
10639 FileDef *fileDef;
10640 };
10641
10642 // collect the entry for which to compute the longest common prefix (LCP) of the path
10643 std::vector<FileEntry> fileEntries;
10644 for (const auto &fd : *fn)
10645 {
10646 if (!fd->isReference()) // skip external references
10647 {
10648 fileEntries.emplace_back(fd->getPath(),fd.get());
10649 }
10650 }
10651
10652 size_t size = fileEntries.size();
10653
10654 if (size==1) // name if unique, so diskname is simply the name
10655 {
10656 FileDef *fd = fileEntries[0].fileDef;
10657 fd->setDiskName(fn->fileName());
10658 }
10659 else if (size>1) // multiple occurrences of the same file name
10660 {
10661 // sort the array
10662 std::stable_sort(fileEntries.begin(),
10663 fileEntries.end(),
10664 [](const FileEntry &fe1,const FileEntry &fe2)
10665 { return qstricmp_sort(fe1.path,fe2.path)<0; }
10666 );
10667
10668 // since the entries are sorted, the common prefix of the whole array is same
10669 // as the common prefix between the first and last entry
10670 const FileEntry &first = fileEntries[0];
10671 const FileEntry &last = fileEntries[size-1];
10672 int first_path_size = static_cast<int>(first.path.size())-1; // -1 to skip trailing slash
10673 int last_path_size = static_cast<int>(last.path.size())-1; // -1 to skip trailing slash
10674 int j=0;
10675 int i=0;
10676 for (i=0;i<first_path_size && i<last_path_size;i++)
10677 {
10678 if (first.path[i]=='/') j=i;
10679 if (first.path[i]!=last.path[i]) break;
10680 }
10681 if (i==first_path_size && i<last_path_size && last.path[i]=='/')
10682 {
10683 // case first='some/path' and last='some/path/more' => match is 'some/path'
10684 j=first_path_size;
10685 }
10686 else if (i==last_path_size && i<first_path_size && first.path[i]=='/')
10687 {
10688 // case first='some/path/more' and last='some/path' => match is 'some/path'
10689 j=last_path_size;
10690 }
10691
10692 // add non-common part of the path to the name
10693 for (auto &fileEntry : fileEntries)
10694 {
10695 QCString prefix = fileEntry.path.right(fileEntry.path.length()-j-1);
10696 fileEntry.fileDef->setName(prefix+fn->fileName());
10697 //printf("!!!!!!!! non unique disk name=%s:%s\n",qPrint(prefix),fn->fileName());
10698 fileEntry.fileDef->setDiskName(prefix+fn->fileName());
10699 }
10700 }
10701 }
10702}
10703
10704
10705
10706//----------------------------------------------------------------------------
10707
10708static std::unique_ptr<OutlineParserInterface> getParserForFile(const QCString &fn)
10709{
10710 QCString fileName=fn;
10711 QCString extension;
10712 int sep = fileName.findRev('/');
10713 int ei = fileName.findRev('.');
10714 if (ei!=-1 && (sep==-1 || ei>sep)) // matches dir/file.ext but not dir.1/file
10715 {
10716 extension=fileName.right(fileName.length()-ei);
10717 }
10718 else
10719 {
10720 extension = ".no_extension";
10721 }
10722
10723 return Doxygen::parserManager->getOutlineParser(extension);
10724}
10725
10726static std::shared_ptr<Entry> parseFile(OutlineParserInterface &parser,
10727 FileDef *fd,const QCString &fn,
10728 ClangTUParser *clangParser,bool newTU)
10729{
10730 QCString fileName=fn;
10731 AUTO_TRACE("fileName={}",fileName);
10732 QCString extension;
10733 int ei = fileName.findRev('.');
10734 if (ei!=-1)
10735 {
10736 extension=fileName.right(fileName.length()-ei);
10737 }
10738 else
10739 {
10740 extension = ".no_extension";
10741 }
10742
10743 FileInfo fi(fileName.str());
10744 std::string preBuf;
10745
10746 if (Config_getBool(ENABLE_PREPROCESSING) &&
10747 parser.needsPreprocessing(extension))
10748 {
10749 Preprocessor preprocessor;
10750 const StringVector &includePath = Config_getList(INCLUDE_PATH);
10751 for (const auto &s : includePath)
10752 {
10753 std::string absPath = FileInfo(s).absFilePath();
10754 preprocessor.addSearchDir(absPath);
10755 }
10756 std::string inBuf;
10757 msg("Preprocessing {}...\n",fn);
10758 readInputFile(fileName,inBuf);
10759 addTerminalCharIfMissing(inBuf,'\n');
10760 preprocessor.processFile(fileName,inBuf,preBuf);
10761 }
10762 else // no preprocessing
10763 {
10764 msg("Reading {}...\n",fn);
10765 readInputFile(fileName,preBuf);
10766 addTerminalCharIfMissing(preBuf,'\n');
10767 }
10768
10769 std::string convBuf;
10770 convBuf.reserve(preBuf.size()+1024);
10771
10772 // convert multi-line C++ comments to C style comments
10773 convertCppComments(preBuf,convBuf,fileName.str());
10774
10775 std::shared_ptr<Entry> fileRoot = std::make_shared<Entry>();
10776 // use language parse to parse the file
10777 if (clangParser)
10778 {
10779 if (newTU) clangParser->parse();
10780 clangParser->switchToFile(fd);
10781 }
10782 parser.parseInput(fileName,convBuf.data(),fileRoot,clangParser);
10783 fileRoot->setFileDef(fd);
10784 return fileRoot;
10785}
10786
10787//! parse the list of input files
10788static void parseFilesMultiThreading(const std::shared_ptr<Entry> &root)
10789{
10790 AUTO_TRACE();
10791#if USE_LIBCLANG
10793 {
10794 StringUnorderedSet processedFiles;
10795
10796 // create a dictionary with files to process
10797 StringUnorderedSet filesToProcess;
10798 for (const auto &s : g_inputFiles)
10799 {
10800 filesToProcess.insert(s);
10801 }
10802
10803 std::mutex processedFilesLock;
10804 // process source files (and their include dependencies)
10805 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10806 msg("Processing input using {} threads.\n",numThreads);
10807 ThreadPool threadPool(numThreads);
10808 using FutureType = std::vector< std::shared_ptr<Entry> >;
10809 std::vector< std::future< FutureType > > results;
10810 for (const auto &s : g_inputFiles)
10811 {
10812 bool ambig = false;
10813 QCString qs = s;
10815 ASSERT(fd!=nullptr);
10816 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt::Cpp) // this is a source file
10817 {
10818 // lambda representing the work to executed by a thread
10819 auto processFile = [qs,&filesToProcess,&processedFilesLock,&processedFiles]() {
10820 bool ambig_l = false;
10821 std::vector< std::shared_ptr<Entry> > roots;
10823 auto clangParser = ClangParser::instance()->createTUParser(fd_l);
10824 auto parser = getParserForFile(qs);
10825 auto fileRoot { parseFile(*parser.get(),fd_l,qs,clangParser.get(),true) };
10826 roots.push_back(fileRoot);
10827
10828 // Now process any include files in the same translation unit
10829 // first. When libclang is used this is much more efficient.
10830 for (auto incFile : clangParser->filesInSameTU())
10831 {
10832 QCString qincFile = incFile;
10833 if (filesToProcess.find(incFile)!=filesToProcess.end())
10834 {
10835 bool needsToBeProcessed = false;
10836 {
10837 std::lock_guard<std::mutex> lock(processedFilesLock);
10838 needsToBeProcessed = processedFiles.find(incFile)==processedFiles.end();
10839 if (needsToBeProcessed) processedFiles.insert(incFile);
10840 }
10841 if (qincFile!=qs && needsToBeProcessed)
10842 {
10843 FileDef *ifd=findFileDef(Doxygen::inputNameLinkedMap,qincFile,ambig_l);
10844 if (ifd && !ifd->isReference())
10845 {
10846 //printf(" Processing %s in same translation unit as %s\n",incFile,qPrint(s));
10847 fileRoot = parseFile(*parser.get(),ifd,qincFile,clangParser.get(),false);
10848 roots.push_back(fileRoot);
10849 }
10850 }
10851 }
10852 }
10853 return roots;
10854 };
10855 // dispatch the work and collect the future results
10856 results.emplace_back(threadPool.queue(processFile));
10857 }
10858 }
10859 // synchronize with the Entry result lists produced and add them to the root
10860 for (auto &f : results)
10861 {
10862 auto l = f.get();
10863 for (auto &e : l)
10864 {
10865 root->moveToSubEntryAndKeep(e);
10866 }
10867 }
10868 // process remaining files
10869 results.clear();
10870 for (const auto &s : g_inputFiles)
10871 {
10872 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
10873 {
10874 // lambda representing the work to executed by a thread
10875 auto processFile = [s]() {
10876 bool ambig = false;
10877 QCString qs = s;
10878 std::vector< std::shared_ptr<Entry> > roots;
10880 auto parser { getParserForFile(qs) };
10881 bool useClang = getLanguageFromFileName(qs)==SrcLangExt::Cpp;
10882 if (useClang)
10883 {
10884 auto clangParser = ClangParser::instance()->createTUParser(fd);
10885 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
10886 roots.push_back(fileRoot);
10887 }
10888 else
10889 {
10890 auto fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10891 roots.push_back(fileRoot);
10892 }
10893 return roots;
10894 };
10895 results.emplace_back(threadPool.queue(processFile));
10896 }
10897 }
10898 // synchronize with the Entry result lists produced and add them to the root
10899 for (auto &f : results)
10900 {
10901 auto l = f.get();
10902 for (auto &e : l)
10903 {
10904 root->moveToSubEntryAndKeep(e);
10905 }
10906 }
10907 }
10908 else // normal processing
10909#endif
10910 {
10911 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10912 msg("Processing input using {} threads.\n",numThreads);
10913 ThreadPool threadPool(numThreads);
10914 using FutureType = std::shared_ptr<Entry>;
10915 std::vector< std::future< FutureType > > results;
10916 for (const auto &s : g_inputFiles)
10917 {
10918 // lambda representing the work to executed by a thread
10919 auto processFile = [s]() {
10920 bool ambig = false;
10921 QCString qs = s;
10923 auto parser = getParserForFile(qs);
10924 auto fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10925 return fileRoot;
10926 };
10927 // dispatch the work and collect the future results
10928 results.emplace_back(threadPool.queue(processFile));
10929 }
10930 // synchronize with the Entry results produced and add them to the root
10931 for (auto &f : results)
10932 {
10933 root->moveToSubEntryAndKeep(f.get());
10934 }
10935 }
10936}
10937
10938//! parse the list of input files
10939static void parseFilesSingleThreading(const std::shared_ptr<Entry> &root)
10940{
10941 AUTO_TRACE();
10942#if USE_LIBCLANG
10944 {
10945 StringUnorderedSet processedFiles;
10946
10947 // create a dictionary with files to process
10948 StringUnorderedSet filesToProcess;
10949 for (const auto &s : g_inputFiles)
10950 {
10951 filesToProcess.insert(s);
10952 }
10953
10954 // process source files (and their include dependencies)
10955 for (const auto &s : g_inputFiles)
10956 {
10957 bool ambig = false;
10958 QCString qs =s;
10960 ASSERT(fd!=nullptr);
10961 if (fd->isSource() && !fd->isReference() && getLanguageFromFileName(qs)==SrcLangExt::Cpp) // this is a source file
10962 {
10963 auto clangParser = ClangParser::instance()->createTUParser(fd);
10964 auto parser { getParserForFile(qs) };
10965 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
10966 root->moveToSubEntryAndKeep(fileRoot);
10967 processedFiles.insert(s);
10968
10969 // Now process any include files in the same translation unit
10970 // first. When libclang is used this is much more efficient.
10971 for (auto incFile : clangParser->filesInSameTU())
10972 {
10973 //printf(" file %s\n",qPrint(incFile));
10974 if (filesToProcess.find(incFile)!=filesToProcess.end() && // file need to be processed
10975 processedFiles.find(incFile)==processedFiles.end()) // and is not processed already
10976 {
10978 if (ifd && !ifd->isReference())
10979 {
10980 //printf(" Processing %s in same translation unit as %s\n",qPrint(incFile),qPrint(qs));
10981 fileRoot = parseFile(*parser.get(),ifd,incFile,clangParser.get(),false);
10982 root->moveToSubEntryAndKeep(fileRoot);
10983 processedFiles.insert(incFile);
10984 }
10985 }
10986 }
10987 }
10988 }
10989 // process remaining files
10990 for (const auto &s : g_inputFiles)
10991 {
10992 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
10993 {
10994 bool ambig = false;
10995 QCString qs = s;
10997 if (getLanguageFromFileName(qs)==SrcLangExt::Cpp) // not yet processed
10998 {
10999 auto clangParser = ClangParser::instance()->createTUParser(fd);
11000 auto parser { getParserForFile(qs) };
11001 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
11002 root->moveToSubEntryAndKeep(fileRoot);
11003 }
11004 else
11005 {
11006 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(qs) };
11007 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
11008 root->moveToSubEntryAndKeep(fileRoot);
11009 }
11010 processedFiles.insert(s);
11011 }
11012 }
11013 }
11014 else // normal processing
11015#endif
11016 {
11017 for (const auto &s : g_inputFiles)
11018 {
11019 bool ambig = false;
11020 QCString qs = s;
11022 ASSERT(fd!=nullptr);
11023 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(qs) };
11024 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
11025 root->moveToSubEntryAndKeep(std::move(fileRoot));
11026 }
11027 }
11028}
11029
11030// resolves a path that may include symlinks, if a recursive symlink is
11031// found an empty string is returned.
11032static std::string resolveSymlink(const std::string &path)
11033{
11034 int sepPos=0;
11035 int oldPos=0;
11036 StringUnorderedSet nonSymlinks;
11037 StringUnorderedSet known;
11038 QCString result(path);
11039 QCString oldPrefix = "/";
11040 do
11041 {
11042#if defined(_WIN32)
11043 // UNC path, skip server and share name
11044 if (sepPos==0 && (result.startsWith("//") || result.startsWith("\\\\")))
11045 sepPos = result.find('/',2);
11046 if (sepPos!=-1)
11047 sepPos = result.find('/',sepPos+1);
11048#else
11049 sepPos = result.find('/',sepPos+1);
11050#endif
11051 QCString prefix = sepPos==-1 ? result : result.left(sepPos);
11052 if (nonSymlinks.find(prefix.str())==nonSymlinks.end())
11053 {
11054 FileInfo fi(prefix.str());
11055 if (fi.isSymLink())
11056 {
11057 QCString target = fi.readLink();
11058 bool isRelative = FileInfo(target.str()).isRelative();
11059 if (isRelative)
11060 {
11061 target = Dir::cleanDirPath(oldPrefix.str()+"/"+target.str());
11062 }
11063 if (sepPos!=-1)
11064 {
11065 if (fi.isDir() && target.length()>0 && target.at(target.length()-1)!='/')
11066 {
11067 target+='/';
11068 }
11069 target+=result.mid(sepPos);
11070 }
11071 result = Dir::cleanDirPath(target.str());
11072 if (known.find(result.str())!=known.end()) return std::string(); // recursive symlink!
11073 known.insert(result.str());
11074 if (isRelative)
11075 {
11076 sepPos = oldPos;
11077 }
11078 else // link to absolute path
11079 {
11080 sepPos = 0;
11081 oldPrefix = "/";
11082 }
11083 }
11084 else
11085 {
11086 nonSymlinks.insert(prefix.str());
11087 oldPrefix = prefix;
11088 }
11089 oldPos = sepPos;
11090 }
11091 }
11092 while (sepPos!=-1);
11093 return Dir::cleanDirPath(result.str());
11094}
11095
11097
11098//----------------------------------------------------------------------------
11099// Read all files matching at least one pattern in 'patList' in the
11100// directory represented by 'fi'.
11101// The directory is read iff the recursiveFlag is set.
11102// The contents of all files is append to the input string
11103
11104static void readDir(FileInfo *fi,
11105 FileNameLinkedMap *fnMap,
11106 StringUnorderedSet *exclSet,
11107 const StringVector *patList,
11108 const StringVector *exclPatList,
11109 StringVector *resultList,
11110 StringUnorderedSet *resultSet,
11111 bool errorIfNotExist,
11112 bool recursive,
11113 StringUnorderedSet *killSet,
11114 StringUnorderedSet *paths
11115 )
11116{
11117 std::string dirName = fi->absFilePath();
11118 if (paths && !dirName.empty())
11119 {
11120 paths->insert(dirName);
11121 }
11122 //printf("%s isSymLink()=%d\n",qPrint(dirName),fi->isSymLink());
11123 if (fi->isSymLink())
11124 {
11125 dirName = resolveSymlink(dirName);
11126 if (dirName.empty())
11127 {
11128 //printf("RECURSIVE SYMLINK: %s\n",qPrint(dirName));
11129 return; // recursive symlink
11130 }
11131 }
11132
11133 if (g_pathsVisited.find(dirName)!=g_pathsVisited.end())
11134 {
11135 //printf("PATH ALREADY VISITED: %s\n",qPrint(dirName));
11136 return; // already visited path
11137 }
11138 g_pathsVisited.insert(dirName);
11139
11140 Dir dir(dirName);
11141 msg("Searching for files in directory {}\n", fi->absFilePath());
11142 //printf("killSet=%p count=%d\n",killSet,killSet ? (int)killSet->count() : -1);
11143
11144 StringVector dirResultList;
11145
11146 for (const auto &dirEntry : dir.iterator())
11147 {
11148 FileInfo cfi(dirEntry.path());
11149 auto checkPatterns = [&]() -> bool
11150 {
11151 return (patList==nullptr || patternMatch(cfi,*patList)) &&
11152 (exclPatList==nullptr || !patternMatch(cfi,*exclPatList)) &&
11153 (killSet==nullptr || killSet->find(cfi.absFilePath())==killSet->end());
11154 };
11155
11156 if (exclSet==nullptr || exclSet->find(cfi.absFilePath())==exclSet->end())
11157 { // file should not be excluded
11158 //printf("killSet->find(%s)\n",qPrint(cfi->absFilePath()));
11159 if (Config_getBool(EXCLUDE_SYMLINKS) && cfi.isSymLink())
11160 {
11161 }
11162 else if (!cfi.exists() || !cfi.isReadable())
11163 {
11164 if (errorIfNotExist && checkPatterns())
11165 {
11166 warn_uncond("source '{}' is not a readable file or directory... skipping.\n",cfi.absFilePath());
11167 }
11168 }
11169 else if (cfi.isFile() && checkPatterns())
11170 {
11171 std::string name=cfi.fileName();
11172 std::string path=cfi.dirPath()+"/";
11173 std::string fullName=path+name;
11174 if (fnMap)
11175 {
11176 auto fd = createFileDef(path,name);
11177 FileName *fn=nullptr;
11178 if (!name.empty())
11179 {
11180 fn = fnMap->add(name,fullName);
11181 fn->push_back(std::move(fd));
11182 }
11183 }
11184 dirResultList.push_back(fullName);
11185 if (resultSet) resultSet->insert(fullName);
11186 if (killSet) killSet->insert(fullName);
11187 }
11188 else if (recursive &&
11189 cfi.isDir() &&
11190 (exclPatList==nullptr || !patternMatch(cfi,*exclPatList)) &&
11191 cfi.fileName().at(0)!='.') // skip "." ".." and ".dir"
11192 {
11193 FileInfo acfi(cfi.absFilePath());
11194 readDir(&acfi,fnMap,exclSet,
11195 patList,exclPatList,&dirResultList,resultSet,errorIfNotExist,
11196 recursive,killSet,paths);
11197 }
11198 }
11199 }
11200 if (resultList && !dirResultList.empty())
11201 {
11202 // sort the resulting list to make the order platform independent.
11203 std::stable_sort(dirResultList.begin(),
11204 dirResultList.end(),
11205 [](const auto &f1,const auto &f2) { return qstricmp_sort(f1.c_str(),f2.c_str())<0; });
11206
11207 // append the sorted results to resultList
11208 resultList->insert(resultList->end(), dirResultList.begin(), dirResultList.end());
11209 }
11210}
11211
11212
11213//----------------------------------------------------------------------------
11214// read a file or all files in a directory and append their contents to the
11215// input string. The names of the files are appended to the 'fiList' list.
11216
11218 FileNameLinkedMap *fnMap,
11219 StringUnorderedSet *exclSet,
11220 const StringVector *patList,
11221 const StringVector *exclPatList,
11222 StringVector *resultList,
11223 StringUnorderedSet *resultSet,
11224 bool recursive,
11225 bool errorIfNotExist,
11226 StringUnorderedSet *killSet,
11227 StringUnorderedSet *paths
11228 )
11229{
11230 //printf("killSet count=%d\n",killSet ? (int)killSet->size() : -1);
11231 // strip trailing slashes
11232 if (s.isEmpty()) return;
11233
11234 g_pathsVisited.clear();
11235
11236 FileInfo fi(s.str());
11237 //printf("readFileOrDirectory(%s)\n",s);
11238 {
11239 if (exclSet==nullptr || exclSet->find(fi.absFilePath())==exclSet->end())
11240 {
11241 if (Config_getBool(EXCLUDE_SYMLINKS) && fi.isSymLink())
11242 {
11243 }
11244 else if (!fi.exists() || !fi.isReadable())
11245 {
11246 if (errorIfNotExist)
11247 {
11248 warn_uncond("source '{}' is not a readable file or directory... skipping.\n",s);
11249 }
11250 }
11251 else if (fi.isFile())
11252 {
11253 std::string dirPath = fi.dirPath(true);
11254 std::string filePath = fi.absFilePath();
11255 if (paths && !dirPath.empty())
11256 {
11257 paths->insert(dirPath);
11258 }
11259 //printf("killSet.find(%s)=%d\n",qPrint(fi.absFilePath()),killSet.find(fi.absFilePath())!=killSet.end());
11260 if (killSet==nullptr || killSet->find(filePath)==killSet->end())
11261 {
11262 std::string name=fi.fileName();
11263 if (fnMap)
11264 {
11265 auto fd = createFileDef(dirPath+"/",name);
11266 if (!name.empty())
11267 {
11268 FileName *fn = fnMap->add(name,filePath);
11269 fn->push_back(std::move(fd));
11270 }
11271 }
11272 if (resultList || resultSet)
11273 {
11274 if (resultList) resultList->push_back(filePath);
11275 if (resultSet) resultSet->insert(filePath);
11276 }
11277
11278 if (killSet) killSet->insert(fi.absFilePath());
11279 }
11280 }
11281 else if (fi.isDir()) // readable dir
11282 {
11283 readDir(&fi,fnMap,exclSet,patList,
11284 exclPatList,resultList,resultSet,errorIfNotExist,
11285 recursive,killSet,paths);
11286 }
11287 }
11288 }
11289}
11290
11291//----------------------------------------------------------------------------
11292
11294{
11295 QCString anchor;
11297 {
11298 MemberDef *md = toMemberDef(d);
11299 anchor=":"+md->anchor();
11300 }
11301 QCString scope;
11302 QCString fn = d->getOutputFileBase();
11305 {
11306 scope = fn;
11307 }
11308 t << "REPLACE INTO symbols (symbol_id,scope_id,name,file,line) VALUES('"
11309 << fn+anchor << "','"
11310 << scope << "','"
11311 << d->name() << "','"
11312 << d->getDefFileName() << "','"
11313 << d->getDefLine()
11314 << "');\n";
11315}
11316
11317static void dumpSymbolMap()
11318{
11319 std::ofstream f = Portable::openOutputStream("symbols.sql");
11320 if (f.is_open())
11321 {
11322 TextStream t(&f);
11323 for (const auto &[name,symList] : *Doxygen::symbolMap)
11324 {
11325 for (const auto &def : symList)
11326 {
11327 dumpSymbol(t,def);
11328 }
11329 }
11330 }
11331}
11332
11333// print developer options of Doxygen
11334static void devUsage()
11335{
11337 msg("Developer parameters:\n");
11338 msg(" -m dump symbol map\n");
11339 msg(" -b making messages output unbuffered\n");
11340 msg(" -c <file> process input file as a comment block and produce HTML output\n");
11341#if ENABLE_TRACING
11342 msg(" -t [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout)\n");
11343 msg(" -t_time [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout),\n"
11344 " and include time and thread information\n");
11345#endif
11346 msg(" -d <level> enable a debug level, such as (multiple invocations of -d are possible):\n");
11348}
11349
11350
11351//----------------------------------------------------------------------------
11352// print the version of Doxygen
11353
11354static void version(const bool extended)
11355{
11357 QCString versionString = getFullVersion();
11358 msg("{}\n",versionString);
11359 if (extended)
11360 {
11361 QCString extVers;
11362 if (!extVers.isEmpty()) extVers+= ", ";
11363 extVers += "sqlite3 ";
11364 extVers += sqlite3_libversion();
11365#if USE_LIBCLANG
11366 if (!extVers.isEmpty()) extVers+= ", ";
11367 extVers += "clang support ";
11368 extVers += CLANG_VERSION_STRING;
11369#endif
11370 if (!extVers.isEmpty())
11371 {
11372 int lastComma = extVers.findRev(',');
11373 if (lastComma != -1) extVers = extVers.replace(lastComma,1," and");
11374 msg(" with {}.\n",extVers);
11375 }
11376 }
11377}
11378
11379//----------------------------------------------------------------------------
11380// print the usage of Doxygen
11381
11382static void usage(const QCString &name,const QCString &versionString)
11383{
11385 msg("Doxygen version {0}\nCopyright Dimitri van Heesch 1997-2025\n\n"
11386 "You can use Doxygen in a number of ways:\n\n"
11387 "1) Use Doxygen to generate a template configuration file*:\n"
11388 " {1} [-s] -g [configName]\n\n"
11389 "2) Use Doxygen to update an old configuration file*:\n"
11390 " {1} [-s] -u [configName]\n\n"
11391 "3) Use Doxygen to generate documentation using an existing "
11392 "configuration file*:\n"
11393 " {1} [configName]\n\n"
11394 "4) Use Doxygen to generate a template file controlling the layout of the\n"
11395 " generated documentation:\n"
11396 " {1} -l [layoutFileName]\n\n"
11397 " In case layoutFileName is omitted DoxygenLayout.xml will be used as filename.\n"
11398 " If - is used for layoutFileName Doxygen will write to standard output.\n\n"
11399 "5) Use Doxygen to generate a template style sheet file for RTF, HTML or Latex.\n"
11400 " RTF: {1} -w rtf styleSheetFile\n"
11401 " HTML: {1} -w html headerFile footerFile styleSheetFile [configFile]\n"
11402 " LaTeX: {1} -w latex headerFile footerFile styleSheetFile [configFile]\n\n"
11403 "6) Use Doxygen to generate a rtf extensions file\n"
11404 " {1} -e rtf extensionsFile\n\n"
11405 " If - is used for extensionsFile Doxygen will write to standard output.\n\n"
11406 "7) Use Doxygen to compare the used configuration file with the template configuration file\n"
11407 " {1} -x [configFile]\n\n"
11408 " Use Doxygen to compare the used configuration file with the template configuration file\n"
11409 " without replacing the environment variables or CMake type replacement variables\n"
11410 " {1} -x_noenv [configFile]\n\n"
11411 "8) Use Doxygen to show a list of built-in emojis.\n"
11412 " {1} -f emoji outputFileName\n\n"
11413 " If - is used for outputFileName Doxygen will write to standard output.\n\n"
11414 "*) If -s is specified the comments of the configuration items in the config file will be omitted.\n"
11415 " If configName is omitted 'Doxyfile' will be used as a default.\n"
11416 " If - is used for configFile Doxygen will write / read the configuration to /from standard output / input.\n\n"
11417 "If -q is used for a Doxygen documentation run, Doxygen will see this as if QUIET=YES has been set.\n\n"
11418 "-v print version string, -V print extended version information\n"
11419 "-h,-? prints usage help information\n"
11420 "{1} -d prints additional usage flags for debugging purposes\n",versionString,name);
11421}
11422
11423//----------------------------------------------------------------------------
11424// read the argument of option 'c' from the comment argument list and
11425// update the option index 'optInd'.
11426
11427static const char *getArg(int argc,char **argv,int &optInd)
11428{
11429 char *s=nullptr;
11430 if (qstrlen(&argv[optInd][2])>0)
11431 s=&argv[optInd][2];
11432 else if (optInd+1<argc && argv[optInd+1][0]!='-')
11433 s=argv[++optInd];
11434 return s;
11435}
11436
11437//----------------------------------------------------------------------------
11438
11439/** @brief /dev/null outline parser */
11441{
11442 public:
11443 void parseInput(const QCString &/* file */, const char * /* buf */,const std::shared_ptr<Entry> &, ClangTUParser*) override {}
11444 bool needsPreprocessing(const QCString &) const override { return FALSE; }
11445 void parsePrototype(const QCString &) override {}
11446};
11447
11448
11449template<class T> std::function< std::unique_ptr<T>() > make_parser_factory()
11450{
11451 return []() { return std::make_unique<T>(); };
11452}
11453
11455{
11456 initResources();
11457 QCString lang = Portable::getenv("LC_ALL");
11458 if (!lang.isEmpty()) Portable::setenv("LANG",lang);
11459 std::setlocale(LC_ALL,"");
11460 std::setlocale(LC_CTYPE,"C"); // to get isspace(0xA0)==0, needed for UTF-8
11461 std::setlocale(LC_NUMERIC,"C");
11462
11464
11488
11489 // register any additional parsers here...
11490
11492
11493#if USE_LIBCLANG
11495#endif
11504 Doxygen::pageLinkedMap = new PageLinkedMap; // all doc pages
11505 Doxygen::exampleLinkedMap = new PageLinkedMap; // all examples
11506 //Doxygen::tagDestinationDict.setAutoDelete(TRUE);
11508
11509 // initialization of these globals depends on
11510 // configuration switches so we need to postpone these
11511 Doxygen::globalScope = nullptr;
11520
11521}
11522
11554
11555static int computeIdealCacheParam(size_t v)
11556{
11557 //printf("computeIdealCacheParam(v=%u)\n",v);
11558
11559 int r=0;
11560 while (v!=0)
11561 {
11562 v >>= 1;
11563 r++;
11564 }
11565 // r = log2(v)
11566
11567 // convert to a valid cache size value
11568 return std::max(0,std::min(r-16,9));
11569}
11570
11571void readConfiguration(int argc, char **argv)
11572{
11573 QCString versionString = getFullVersion();
11574
11575 // helper that calls \a func to write to file \a fileName via a TextStream
11576 auto writeFile = [](const char *fileName,std::function<void(TextStream&)> func) -> bool
11577 {
11578 std::ofstream f;
11579 if (openOutputFile(fileName,f))
11580 {
11581 TextStream t(&f);
11582 func(t);
11583 return true;
11584 }
11585 return false;
11586 };
11587
11588
11589 /**************************************************************************
11590 * Handle arguments *
11591 **************************************************************************/
11592
11593 int optInd=1;
11594 QCString configName;
11595 QCString traceName;
11596 bool genConfig=false;
11597 bool shortList=false;
11598 bool traceTiming=false;
11600 bool updateConfig=false;
11601 bool quiet = false;
11602 while (optInd<argc && argv[optInd][0]=='-' &&
11603 (isalpha(argv[optInd][1]) || argv[optInd][1]=='?' ||
11604 argv[optInd][1]=='-')
11605 )
11606 {
11607 switch(argv[optInd][1])
11608 {
11609 case 'g':
11610 {
11611 genConfig=TRUE;
11612 }
11613 break;
11614 case 'l':
11615 {
11616 QCString layoutName;
11617 if (optInd+1>=argc)
11618 {
11619 layoutName="DoxygenLayout.xml";
11620 }
11621 else
11622 {
11623 layoutName=argv[optInd+1];
11624 }
11625 writeDefaultLayoutFile(layoutName);
11627 exit(0);
11628 }
11629 break;
11630 case 'c':
11631 if (optInd+1>=argc) // no file name given
11632 {
11633 msg("option \"-c\" is missing the file name to read\n");
11634 devUsage();
11636 exit(1);
11637 }
11638 else
11639 {
11640 g_commentFileName=argv[optInd+1];
11641 optInd++;
11642 }
11643 g_singleComment=true;
11644 quiet=true;
11645 break;
11646 case 'd':
11647 {
11648 QCString debugLabel=getArg(argc,argv,optInd);
11649 if (debugLabel.isEmpty())
11650 {
11651 devUsage();
11653 exit(0);
11654 }
11655 int retVal = Debug::setFlagStr(debugLabel);
11656 if (!retVal)
11657 {
11658 msg("option \"-d\" has unknown debug specifier: \"{}\".\n",debugLabel);
11659 devUsage();
11661 exit(1);
11662 }
11663 }
11664 break;
11665 case 't':
11666 {
11667#if ENABLE_TRACING
11668 if (!strcmp(argv[optInd]+1,"t_time"))
11669 {
11670 traceTiming = true;
11671 }
11672 else if (!strcmp(argv[optInd]+1,"t"))
11673 {
11674 traceTiming = false;
11675 }
11676 else
11677 {
11678 err("option should be \"-t\" or \"-t_time\", found: \"{}\".\n",argv[optInd]);
11680 exit(1);
11681 }
11682 if (optInd+1>=argc || argv[optInd+1][0] == '-') // no file name given
11683 {
11684 traceName="stdout";
11685 }
11686 else
11687 {
11688 traceName=argv[optInd+1];
11689 optInd++;
11690 }
11691#else
11692 err("support for option \"-t\" has not been compiled in (use a debug build or a release build with tracing enabled).\n");
11694 exit(1);
11695#endif
11696 }
11697 break;
11698 case 'x':
11699 if (!strcmp(argv[optInd]+1,"x_noenv")) diffList=Config::CompareMode::CompressedNoEnv;
11700 else if (!strcmp(argv[optInd]+1,"x")) diffList=Config::CompareMode::Compressed;
11701 else
11702 {
11703 err("option should be \"-x\" or \"-x_noenv\", found: \"{}\".\n",argv[optInd]);
11705 exit(1);
11706 }
11707 break;
11708 case 's':
11709 shortList=TRUE;
11710 break;
11711 case 'u':
11712 updateConfig=TRUE;
11713 break;
11714 case 'e':
11715 {
11716 QCString formatName=getArg(argc,argv,optInd);
11717 if (formatName.isEmpty())
11718 {
11719 err("option \"-e\" is missing format specifier rtf.\n");
11721 exit(1);
11722 }
11723 if (qstricmp(formatName.data(),"rtf")==0)
11724 {
11725 if (optInd+1>=argc)
11726 {
11727 err("option \"-e rtf\" is missing an extensions file name\n");
11729 exit(1);
11730 }
11731 writeFile(argv[optInd+1],RTFGenerator::writeExtensionsFile);
11733 exit(0);
11734 }
11735 err("option \"-e\" has invalid format specifier.\n");
11737 exit(1);
11738 }
11739 break;
11740 case 'f':
11741 {
11742 QCString listName=getArg(argc,argv,optInd);
11743 if (listName.isEmpty())
11744 {
11745 err("option \"-f\" is missing list specifier.\n");
11747 exit(1);
11748 }
11749 if (qstricmp(listName.data(),"emoji")==0)
11750 {
11751 if (optInd+1>=argc)
11752 {
11753 err("option \"-f emoji\" is missing an output file name\n");
11755 exit(1);
11756 }
11757 writeFile(argv[optInd+1],[](TextStream &t) { EmojiEntityMapper::instance().writeEmojiFile(t); });
11759 exit(0);
11760 }
11761 err("option \"-f\" has invalid list specifier.\n");
11763 exit(1);
11764 }
11765 break;
11766 case 'w':
11767 {
11768 QCString formatName=getArg(argc,argv,optInd);
11769 if (formatName.isEmpty())
11770 {
11771 err("option \"-w\" is missing format specifier rtf, html or latex\n");
11773 exit(1);
11774 }
11775 if (qstricmp(formatName.data(),"rtf")==0)
11776 {
11777 if (optInd+1>=argc)
11778 {
11779 err("option \"-w rtf\" is missing a style sheet file name\n");
11781 exit(1);
11782 }
11783 if (!writeFile(argv[optInd+1],RTFGenerator::writeStyleSheetFile))
11784 {
11785 err("error opening RTF style sheet file {}!\n",argv[optInd+1]);
11787 exit(1);
11788 }
11790 exit(0);
11791 }
11792 else if (qstricmp(formatName.data(),"html")==0)
11793 {
11794 Config::init();
11795 if (optInd+4<argc || FileInfo("Doxyfile").exists() || FileInfo("doxyfile").exists())
11796 // explicit config file mentioned or default found on disk
11797 {
11798 QCString df = optInd+4<argc ? argv[optInd+4] : (FileInfo("Doxyfile").exists() ? QCString("Doxyfile") : QCString("doxyfile"));
11799 if (!Config::parse(df)) // parse the config file
11800 {
11801 err("error opening or reading configuration file {}!\n",argv[optInd+4]);
11803 exit(1);
11804 }
11805 }
11806 if (optInd+3>=argc)
11807 {
11808 err("option \"-w html\" does not have enough arguments\n");
11810 exit(1);
11811 }
11815 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11816 writeFile(argv[optInd+1],[&](TextStream &t) { HtmlGenerator::writeHeaderFile(t,argv[optInd+3]); });
11817 writeFile(argv[optInd+2],HtmlGenerator::writeFooterFile);
11818 writeFile(argv[optInd+3],HtmlGenerator::writeStyleSheetFile);
11820 exit(0);
11821 }
11822 else if (qstricmp(formatName.data(),"latex")==0)
11823 {
11824 Config::init();
11825 if (optInd+4<argc || FileInfo("Doxyfile").exists() || FileInfo("doxyfile").exists())
11826 {
11827 QCString df = optInd+4<argc ? argv[optInd+4] : (FileInfo("Doxyfile").exists() ? QCString("Doxyfile") : QCString("doxyfile"));
11828 if (!Config::parse(df))
11829 {
11830 err("error opening or reading configuration file {}!\n",argv[optInd+4]);
11832 exit(1);
11833 }
11834 }
11835 if (optInd+3>=argc)
11836 {
11837 err("option \"-w latex\" does not have enough arguments\n");
11839 exit(1);
11840 }
11844 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11845 writeFile(argv[optInd+1],LatexGenerator::writeHeaderFile);
11846 writeFile(argv[optInd+2],LatexGenerator::writeFooterFile);
11847 writeFile(argv[optInd+3],LatexGenerator::writeStyleSheetFile);
11849 exit(0);
11850 }
11851 else
11852 {
11853 err("Illegal format specifier \"{}\": should be one of rtf, html or latex\n",formatName);
11855 exit(1);
11856 }
11857 }
11858 break;
11859 case 'm':
11861 break;
11862 case 'v':
11863 version(false);
11865 exit(0);
11866 break;
11867 case 'V':
11868 version(true);
11870 exit(0);
11871 break;
11872 case '-':
11873 if (qstrcmp(&argv[optInd][2],"help")==0)
11874 {
11875 usage(argv[0],versionString);
11876 exit(0);
11877 }
11878 else if (qstrcmp(&argv[optInd][2],"version")==0)
11879 {
11880 version(false);
11882 exit(0);
11883 }
11884 else if ((qstrcmp(&argv[optInd][2],"Version")==0) ||
11885 (qstrcmp(&argv[optInd][2],"VERSION")==0))
11886 {
11887 version(true);
11889 exit(0);
11890 }
11891 else
11892 {
11893 err("Unknown option \"-{}\"\n",&argv[optInd][1]);
11894 usage(argv[0],versionString);
11895 exit(1);
11896 }
11897 break;
11898 case 'b':
11899 setvbuf(stdout,nullptr,_IONBF,0);
11900 break;
11901 case 'q':
11902 quiet = true;
11903 break;
11904 case 'h':
11905 case '?':
11906 usage(argv[0],versionString);
11907 exit(0);
11908 break;
11909 default:
11910 err("Unknown option \"-{:c}\"\n",argv[optInd][1]);
11911 usage(argv[0],versionString);
11912 exit(1);
11913 }
11914 optInd++;
11915 }
11916
11917 /**************************************************************************
11918 * Parse or generate the config file *
11919 **************************************************************************/
11920
11921 initTracing(traceName.data(),traceTiming);
11922 TRACE("Doxygen version used: {}",getFullVersion());
11923 Config::init();
11924
11925 FileInfo configFileInfo1("Doxyfile"),configFileInfo2("doxyfile");
11926 if (optInd>=argc)
11927 {
11928 if (configFileInfo1.exists())
11929 {
11930 configName="Doxyfile";
11931 }
11932 else if (configFileInfo2.exists())
11933 {
11934 configName="doxyfile";
11935 }
11936 else if (genConfig)
11937 {
11938 configName="Doxyfile";
11939 }
11940 else
11941 {
11942 err("Doxyfile not found and no input file specified!\n");
11943 usage(argv[0],versionString);
11944 exit(1);
11945 }
11946 }
11947 else
11948 {
11949 FileInfo fi(argv[optInd]);
11950 if (fi.exists() || qstrcmp(argv[optInd],"-")==0 || genConfig)
11951 {
11952 configName=argv[optInd];
11953 }
11954 else
11955 {
11956 err("configuration file {} not found!\n",argv[optInd]);
11957 usage(argv[0],versionString);
11958 exit(1);
11959 }
11960 }
11961
11962 if (genConfig)
11963 {
11964 generateConfigFile(configName,shortList);
11966 exit(0);
11967 }
11968
11969 if (!Config::parse(configName,updateConfig,diffList))
11970 {
11971 err("could not open or read configuration file {}!\n",configName);
11973 exit(1);
11974 }
11975
11976 if (diffList!=Config::CompareMode::Full)
11977 {
11979 compareDoxyfile(diffList);
11981 exit(0);
11982 }
11983
11984 if (updateConfig)
11985 {
11987 generateConfigFile(configName,shortList,TRUE);
11989 exit(0);
11990 }
11991
11992 /* Perlmod wants to know the path to the config file.*/
11993 FileInfo configFileInfo(configName.str());
11994 setPerlModDoxyfile(configFileInfo.absFilePath());
11995
11996 /* handle -q option */
11997 if (quiet) Config_updateBool(QUIET,TRUE);
11998}
11999
12000/** check and resolve config options */
12002{
12003 AUTO_TRACE();
12004
12009}
12010
12011/** adjust globals that depend on configuration settings. */
12013{
12014 AUTO_TRACE();
12015 Doxygen::globalNamespaceDef = createNamespaceDef("<globalScope>",1,1,"<globalScope>");
12025
12026 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
12027
12028 /* Set the global html file extension. */
12029 Doxygen::htmlFileExtension = Config_getString(HTML_FILE_EXTENSION);
12030
12031
12033 Config_getBool(CALLER_GRAPH) ||
12034 Config_getBool(REFERENCES_RELATION) ||
12035 Config_getBool(REFERENCED_BY_RELATION);
12036
12037 /**************************************************************************
12038 * Add custom extension mappings
12039 **************************************************************************/
12040
12041 const StringVector &extMaps = Config_getList(EXTENSION_MAPPING);
12042 for (const auto &mapping : extMaps)
12043 {
12044 QCString mapStr = mapping;
12045 int i=mapStr.find('=');
12046 if (i==-1)
12047 {
12048 continue;
12049 }
12050 else
12051 {
12052 QCString ext = mapStr.left(i).stripWhiteSpace().lower();
12053 QCString language = mapStr.mid(i+1).stripWhiteSpace().lower();
12054 if (ext.isEmpty() || language.isEmpty())
12055 {
12056 continue;
12057 }
12058
12059 if (!updateLanguageMapping(ext,language))
12060 {
12061 err("Failed to map file extension '{}' to unsupported language '{}'.\n"
12062 "Check the EXTENSION_MAPPING setting in the config file.\n",
12063 ext,language);
12064 }
12065 else
12066 {
12067 msg("Adding custom extension mapping: '{}' will be treated as language '{}'\n",
12068 ext,language);
12069 }
12070 }
12071 }
12072 // create input file exncodings
12073
12074 // check INPUT_ENCODING
12075 void *cd = portable_iconv_open("UTF-8",Config_getString(INPUT_ENCODING).data());
12076 if (cd==reinterpret_cast<void *>(-1))
12077 {
12078 term("unsupported character conversion: '{}'->'UTF-8': {}\n"
12079 "Check the 'INPUT_ENCODING' setting in the config file!\n",
12080 Config_getString(INPUT_ENCODING),strerror(errno));
12081 }
12082 else
12083 {
12085 }
12086
12087 // check and split INPUT_FILE_ENCODING
12088 const StringVector &fileEncod = Config_getList(INPUT_FILE_ENCODING);
12089 for (const auto &mapping : fileEncod)
12090 {
12091 QCString mapStr = mapping;
12092 int i=mapStr.find('=');
12093 if (i==-1)
12094 {
12095 continue;
12096 }
12097 else
12098 {
12099 QCString pattern = mapStr.left(i).stripWhiteSpace().lower();
12100 QCString encoding = mapStr.mid(i+1).stripWhiteSpace().lower();
12101 if (pattern.isEmpty() || encoding.isEmpty())
12102 {
12103 continue;
12104 }
12105 cd = portable_iconv_open("UTF-8",encoding.data());
12106 if (cd==reinterpret_cast<void *>(-1))
12107 {
12108 term("unsupported character conversion: '{}'->'UTF-8': {}\n"
12109 "Check the 'INPUT_FILE_ENCODING' setting in the config file!\n",
12110 encoding,strerror(errno));
12111 }
12112 else
12113 {
12115 }
12116
12117 Doxygen::inputFileEncodingList.emplace_back(pattern, encoding);
12118 }
12119 }
12120
12121 // add predefined macro name to a dictionary
12122 const StringVector &expandAsDefinedList =Config_getList(EXPAND_AS_DEFINED);
12123 for (const auto &s : expandAsDefinedList)
12124 {
12126 }
12127
12128 // read aliases and store them in a dictionary
12129 readAliases();
12130
12131 // store number of spaces in a tab into Doxygen::spaces
12132 int tabSize = Config_getInt(TAB_SIZE);
12133 Doxygen::spaces.resize(tabSize);
12134 for (int sp=0; sp<tabSize; sp++) Doxygen::spaces.at(sp)=' ';
12135 Doxygen::spaces.at(tabSize)='\0';
12136}
12137
12138#ifdef HAS_SIGNALS
12139static void stopDoxygen(int)
12140{
12141 signal(SIGINT,SIG_DFL); // Re-register signal handler for default action
12142 Dir thisDir;
12143 msg("Cleaning up...\n");
12144 if (!Doxygen::filterDBFileName.isEmpty())
12145 {
12146 thisDir.remove(Doxygen::filterDBFileName.str());
12147 }
12148 killpg(0,SIGINT);
12150 exitTracing();
12151 exit(1);
12152}
12153#endif
12154
12155static void writeTagFile()
12156{
12157 QCString generateTagFile = Config_getString(GENERATE_TAGFILE);
12158 if (generateTagFile.isEmpty()) return;
12159
12160 std::ofstream f = Portable::openOutputStream(generateTagFile);
12161 if (!f.is_open())
12162 {
12163 err("cannot open tag file {} for writing\n", generateTagFile);
12164 return;
12165 }
12166 TextStream tagFile(&f);
12167 tagFile << "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>\n";
12168 tagFile << "<tagfile doxygen_version=\"" << getDoxygenVersion() << "\"";
12169 std::string gitVersion = getGitVersion();
12170 if (!gitVersion.empty())
12171 {
12172 tagFile << " doxygen_gitid=\"" << gitVersion << "\"";
12173 }
12174 tagFile << ">\n";
12175
12176 // for each file
12177 for (const auto &fn : *Doxygen::inputNameLinkedMap)
12178 {
12179 for (const auto &fd : *fn)
12180 {
12181 if (fd->isLinkableInProject()) fd->writeTagFile(tagFile);
12182 }
12183 }
12184 // for each class
12185 for (const auto &cd : *Doxygen::classLinkedMap)
12186 {
12187 ClassDefMutable *cdm = toClassDefMutable(cd.get());
12188 if (cdm && cdm->isLinkableInProject())
12189 {
12190 cdm->writeTagFile(tagFile);
12191 }
12192 }
12193 // for each concept
12194 for (const auto &cd : *Doxygen::conceptLinkedMap)
12195 {
12196 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
12197 if (cdm && cdm->isLinkableInProject())
12198 {
12199 cdm->writeTagFile(tagFile);
12200 }
12201 }
12202 // for each namespace
12203 for (const auto &nd : *Doxygen::namespaceLinkedMap)
12204 {
12206 if (ndm && nd->isLinkableInProject())
12207 {
12208 ndm->writeTagFile(tagFile);
12209 }
12210 }
12211 // for each group
12212 for (const auto &gd : *Doxygen::groupLinkedMap)
12213 {
12214 if (gd->isLinkableInProject()) gd->writeTagFile(tagFile);
12215 }
12216 // for each module
12217 for (const auto &mod : ModuleManager::instance().modules())
12218 {
12219 if (mod->isLinkableInProject()) mod->writeTagFile(tagFile);
12220 }
12221 // for each page
12222 for (const auto &pd : *Doxygen::pageLinkedMap)
12223 {
12224 if (pd->isLinkableInProject()) pd->writeTagFile(tagFile);
12225 }
12226 // for requirements
12228 // for each directory
12229 for (const auto &dd : *Doxygen::dirLinkedMap)
12230 {
12231 if (dd->isLinkableInProject()) dd->writeTagFile(tagFile);
12232 }
12233 if (Doxygen::mainPage) Doxygen::mainPage->writeTagFile(tagFile);
12234
12235 tagFile << "</tagfile>\n";
12236}
12237
12238static void exitDoxygen() noexcept
12239{
12240 if (!g_successfulRun) // premature exit
12241 {
12242 Dir thisDir;
12243 msg("Exiting...\n");
12244 if (!Doxygen::filterDBFileName.isEmpty())
12245 {
12246 thisDir.remove(Doxygen::filterDBFileName.str());
12247 }
12248 }
12249}
12250
12251static QCString createOutputDirectory(const QCString &baseDirName,
12252 const QCString &formatDirName,
12253 const char *defaultDirName)
12254{
12255 QCString result = formatDirName;
12256 if (result.isEmpty())
12257 {
12258 result = baseDirName + defaultDirName;
12259 }
12260 else if (formatDirName[0]!='/' && (formatDirName.length()==1 || formatDirName[1]!=':'))
12261 {
12262 result.prepend(baseDirName+"/");
12263 }
12264 Dir formatDir(result.str());
12265 if (!formatDir.exists() && !formatDir.mkdir(result.str()))
12266 {
12267 term("Could not create output directory {}\n", result);
12268 }
12269 return result;
12270}
12271
12273{
12274 StringUnorderedSet killSet;
12275
12276 const StringVector &exclPatterns = Config_getList(EXCLUDE_PATTERNS);
12277 bool alwaysRecursive = Config_getBool(RECURSIVE);
12278 StringUnorderedSet excludeNameSet;
12279
12280 // gather names of all files in the include path
12281 g_s.begin("Searching for include files...\n");
12282 killSet.clear();
12283 const StringVector &includePathList = Config_getList(INCLUDE_PATH);
12284 for (const auto &s : includePathList)
12285 {
12286 size_t plSize = Config_getList(INCLUDE_FILE_PATTERNS).size();
12287 const StringVector &pl = plSize==0 ? Config_getList(FILE_PATTERNS) :
12288 Config_getList(INCLUDE_FILE_PATTERNS);
12289 readFileOrDirectory(s, // s
12291 nullptr, // exclSet
12292 &pl, // patList
12293 &exclPatterns, // exclPatList
12294 nullptr, // resultList
12295 nullptr, // resultSet
12296 false, // INCLUDE_PATH isn't recursive
12297 TRUE, // errorIfNotExist
12298 &killSet); // killSet
12299 }
12300 g_s.end();
12301
12302 g_s.begin("Searching for example files...\n");
12303 killSet.clear();
12304 const StringVector &examplePathList = Config_getList(EXAMPLE_PATH);
12305 for (const auto &s : examplePathList)
12306 {
12307 readFileOrDirectory(s, // s
12309 nullptr, // exclSet
12310 &Config_getList(EXAMPLE_PATTERNS), // patList
12311 nullptr, // exclPatList
12312 nullptr, // resultList
12313 nullptr, // resultSet
12314 (alwaysRecursive || Config_getBool(EXAMPLE_RECURSIVE)), // recursive
12315 TRUE, // errorIfNotExist
12316 &killSet); // killSet
12317 }
12318 g_s.end();
12319
12320 g_s.begin("Searching for images...\n");
12321 killSet.clear();
12322 const StringVector &imagePathList=Config_getList(IMAGE_PATH);
12323 for (const auto &s : imagePathList)
12324 {
12325 readFileOrDirectory(s, // s
12327 nullptr, // exclSet
12328 nullptr, // patList
12329 nullptr, // exclPatList
12330 nullptr, // resultList
12331 nullptr, // resultSet
12332 alwaysRecursive, // recursive
12333 TRUE, // errorIfNotExist
12334 &killSet); // killSet
12335 }
12336 g_s.end();
12337
12338 g_s.begin("Searching for dot files...\n");
12339 killSet.clear();
12340 const StringVector &dotFileList=Config_getList(DOTFILE_DIRS);
12341 for (const auto &s : dotFileList)
12342 {
12343 readFileOrDirectory(s, // s
12345 nullptr, // exclSet
12346 nullptr, // patList
12347 nullptr, // exclPatList
12348 nullptr, // resultList
12349 nullptr, // resultSet
12350 alwaysRecursive, // recursive
12351 TRUE, // errorIfNotExist
12352 &killSet); // killSet
12353 }
12354 g_s.end();
12355
12356 g_s.begin("Searching for msc files...\n");
12357 killSet.clear();
12358 const StringVector &mscFileList=Config_getList(MSCFILE_DIRS);
12359 for (const auto &s : mscFileList)
12360 {
12361 readFileOrDirectory(s, // s
12363 nullptr, // exclSet
12364 nullptr, // patList
12365 nullptr, // exclPatList
12366 nullptr, // resultList
12367 nullptr, // resultSet
12368 alwaysRecursive, // recursive
12369 TRUE, // errorIfNotExist
12370 &killSet); // killSet
12371 }
12372 g_s.end();
12373
12374 g_s.begin("Searching for dia files...\n");
12375 killSet.clear();
12376 const StringVector &diaFileList=Config_getList(DIAFILE_DIRS);
12377 for (const auto &s : diaFileList)
12378 {
12379 readFileOrDirectory(s, // s
12381 nullptr, // exclSet
12382 nullptr, // patList
12383 nullptr, // exclPatList
12384 nullptr, // resultList
12385 nullptr, // resultSet
12386 alwaysRecursive, // recursive
12387 TRUE, // errorIfNotExist
12388 &killSet); // killSet
12389 }
12390 g_s.end();
12391
12392 g_s.begin("Searching for plantuml files...\n");
12393 killSet.clear();
12394 const StringVector &plantUmlFileList=Config_getList(PLANTUMLFILE_DIRS);
12395 for (const auto &s : plantUmlFileList)
12396 {
12397 readFileOrDirectory(s, // s
12399 nullptr, // exclSet
12400 nullptr, // patList
12401 nullptr, // exclPatList
12402 nullptr, // resultList
12403 nullptr, // resultSet
12404 alwaysRecursive, // recursive
12405 TRUE, // errorIfNotExist
12406 &killSet); // killSet
12407 }
12408 g_s.end();
12409
12410 g_s.begin("Searching for files to exclude\n");
12411 const StringVector &excludeList = Config_getList(EXCLUDE);
12412 for (const auto &s : excludeList)
12413 {
12414 readFileOrDirectory(s, // s
12415 nullptr, // fnDict
12416 nullptr, // exclSet
12417 &Config_getList(FILE_PATTERNS), // patList
12418 nullptr, // exclPatList
12419 nullptr, // resultList
12420 &excludeNameSet, // resultSet
12421 alwaysRecursive, // recursive
12422 FALSE); // errorIfNotExist
12423 }
12424 g_s.end();
12425
12426 /**************************************************************************
12427 * Determine Input Files *
12428 **************************************************************************/
12429
12430 g_s.begin("Searching INPUT for files to process...\n");
12431 killSet.clear();
12432 Doxygen::inputPaths.clear();
12433 const StringVector &inputList=Config_getList(INPUT);
12434 for (const auto &s : inputList)
12435 {
12436 QCString path = s;
12437 size_t l = path.length();
12438 if (l>0)
12439 {
12440 // strip trailing slashes
12441 if (path.at(l-1)=='\\' || path.at(l-1)=='/') path=path.left(l-1);
12442
12444 path, // s
12446 &excludeNameSet, // exclSet
12447 &Config_getList(FILE_PATTERNS), // patList
12448 &exclPatterns, // exclPatList
12449 &g_inputFiles, // resultList
12450 nullptr, // resultSet
12451 alwaysRecursive, // recursive
12452 TRUE, // errorIfNotExist
12453 &killSet, // killSet
12454 &Doxygen::inputPaths); // paths
12455 }
12456 }
12457
12458 // Sort the FileDef objects by full path to get a predictable ordering over multiple runs
12459 std::stable_sort(Doxygen::inputNameLinkedMap->begin(),
12461 [](const auto &f1,const auto &f2)
12462 {
12463 return qstricmp_sort(f1->fullName(),f2->fullName())<0;
12464 });
12465 for (auto &fileName : *Doxygen::inputNameLinkedMap)
12466 {
12467 if (fileName->size()>1)
12468 {
12469 std::stable_sort(fileName->begin(),fileName->end(),[](const auto &f1,const auto &f2)
12470 {
12471 return qstricmp_sort(f1->absFilePath(),f2->absFilePath())<0;
12472 });
12473 }
12474 }
12475 if (Doxygen::inputNameLinkedMap->empty())
12476 {
12477 warn_uncond("No files to be processed, please check your settings, in particular INPUT, FILE_PATTERNS, and RECURSIVE\n");
12478 }
12479 g_s.end();
12480}
12481
12482
12484{
12485 if (Config_getBool(MARKDOWN_SUPPORT))
12486 {
12487 QCString mdfileAsMainPage = Config_getString(USE_MDFILE_AS_MAINPAGE);
12488 if (mdfileAsMainPage.isEmpty()) return;
12489 FileInfo fi(mdfileAsMainPage.data());
12490 if (!fi.exists())
12491 {
12492 warn_uncond("Specified markdown mainpage '{}' does not exist\n",mdfileAsMainPage);
12493 return;
12494 }
12495 bool ambig = false;
12496 if (findFileDef(Doxygen::inputNameLinkedMap,fi.absFilePath(),ambig)==nullptr)
12497 {
12498 warn_uncond("Specified markdown mainpage '{}' has not been defined as input file\n",mdfileAsMainPage);
12499 return;
12500 }
12501 }
12502}
12503
12505{
12506 AUTO_TRACE();
12507 std::atexit(exitDoxygen);
12508
12509 Portable::correctPath(Config_getList(EXTERNAL_TOOL_PATH));
12510
12511#if USE_LIBCLANG
12512 Doxygen::clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING);
12513#endif
12514
12515 // we would like to show the versionString earlier, but we first have to handle the configuration file
12516 // to know the value of the QUIET setting.
12517 QCString versionString = getFullVersion();
12518 msg("Doxygen version used: {}\n",versionString);
12519
12521
12522 /**************************************************************************
12523 * Make sure the output directory exists
12524 **************************************************************************/
12525 QCString outputDirectory = Config_getString(OUTPUT_DIRECTORY);
12526 if (!g_singleComment)
12527 {
12528 if (outputDirectory.isEmpty())
12529 {
12530 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,Dir::currentDirPath());
12531 }
12532 else
12533 {
12534 Dir dir(outputDirectory.str());
12535 if (!dir.exists())
12536 {
12538 if (!dir.mkdir(outputDirectory.str()))
12539 {
12540 term("tag OUTPUT_DIRECTORY: Output directory '{}' does not "
12541 "exist and cannot be created\n",outputDirectory);
12542 }
12543 else
12544 {
12545 msg("Notice: Output directory '{}' does not exist. "
12546 "I have created it for you.\n", outputDirectory);
12547 }
12548 dir.setPath(outputDirectory.str());
12549 }
12550 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,dir.absPath());
12551 }
12552 }
12553 AUTO_TRACE_ADD("outputDirectory={}",outputDirectory);
12554
12555 /**************************************************************************
12556 * Initialize global lists and dictionaries
12557 **************************************************************************/
12558
12559 // also scale lookup cache with SYMBOL_CACHE_SIZE
12560 int cacheSize = Config_getInt(LOOKUP_CACHE_SIZE);
12561 if (cacheSize<0) cacheSize=0;
12562 if (cacheSize>9) cacheSize=9;
12563 uint32_t lookupSize = 65536 << cacheSize;
12566
12567#ifdef HAS_SIGNALS
12568 signal(SIGINT, stopDoxygen);
12569#endif
12570
12571 uint32_t pid = Portable::pid();
12572 Doxygen::filterDBFileName.sprintf("doxygen_filterdb_%d.tmp",pid);
12573 Doxygen::filterDBFileName.prepend(outputDirectory+"/");
12574
12575 /**************************************************************************
12576 * Check/create output directories *
12577 **************************************************************************/
12578
12579 bool generateHtml = Config_getBool(GENERATE_HTML);
12580 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
12581 bool generateXml = Config_getBool(GENERATE_XML);
12582 bool generateLatex = Config_getBool(GENERATE_LATEX);
12583 bool generateRtf = Config_getBool(GENERATE_RTF);
12584 bool generateMan = Config_getBool(GENERATE_MAN);
12585 bool generateSql = Config_getBool(GENERATE_SQLITE3);
12586 QCString htmlOutput;
12587 QCString docbookOutput;
12588 QCString xmlOutput;
12589 QCString latexOutput;
12590 QCString rtfOutput;
12591 QCString manOutput;
12592 QCString sqlOutput;
12593
12594 if (!g_singleComment)
12595 {
12596 if (generateHtml)
12597 {
12598 htmlOutput = createOutputDirectory(outputDirectory,Config_getString(HTML_OUTPUT),"/html");
12599 Config_updateString(HTML_OUTPUT,htmlOutput);
12600
12601 QCString sitemapUrl = Config_getString(SITEMAP_URL);
12602 bool generateSitemap = !sitemapUrl.isEmpty();
12603 if (generateSitemap && !sitemapUrl.endsWith("/"))
12604 {
12605 Config_updateString(SITEMAP_URL,sitemapUrl+"/");
12606 }
12607
12608 // add HTML indexers that are enabled
12609 bool generateHtmlHelp = Config_getBool(GENERATE_HTMLHELP);
12610 bool generateEclipseHelp = Config_getBool(GENERATE_ECLIPSEHELP);
12611 bool generateQhp = Config_getBool(GENERATE_QHP);
12612 bool generateTreeView = Config_getBool(GENERATE_TREEVIEW);
12613 bool generateDocSet = Config_getBool(GENERATE_DOCSET);
12614 if (generateEclipseHelp) Doxygen::indexList->addIndex<EclipseHelp>();
12615 if (generateHtmlHelp) Doxygen::indexList->addIndex<HtmlHelp>();
12616 if (generateQhp) Doxygen::indexList->addIndex<Qhp>();
12617 if (generateSitemap) Doxygen::indexList->addIndex<Sitemap>();
12618 if (generateTreeView) Doxygen::indexList->addIndex<FTVHelp>(TRUE);
12619 if (generateDocSet) Doxygen::indexList->addIndex<DocSets>();
12620 Doxygen::indexList->addIndex<Crawlmap>();
12621 Doxygen::indexList->initialize();
12622 }
12623
12624 if (generateDocbook)
12625 {
12626 docbookOutput = createOutputDirectory(outputDirectory,Config_getString(DOCBOOK_OUTPUT),"/docbook");
12627 Config_updateString(DOCBOOK_OUTPUT,docbookOutput);
12628 }
12629
12630 if (generateXml)
12631 {
12632 xmlOutput = createOutputDirectory(outputDirectory,Config_getString(XML_OUTPUT),"/xml");
12633 Config_updateString(XML_OUTPUT,xmlOutput);
12634 }
12635
12636 if (generateLatex)
12637 {
12638 latexOutput = createOutputDirectory(outputDirectory,Config_getString(LATEX_OUTPUT), "/latex");
12639 Config_updateString(LATEX_OUTPUT,latexOutput);
12640 }
12641
12642 if (generateRtf)
12643 {
12644 rtfOutput = createOutputDirectory(outputDirectory,Config_getString(RTF_OUTPUT),"/rtf");
12645 Config_updateString(RTF_OUTPUT,rtfOutput);
12646 }
12647
12648 if (generateMan)
12649 {
12650 manOutput = createOutputDirectory(outputDirectory,Config_getString(MAN_OUTPUT),"/man");
12651 Config_updateString(MAN_OUTPUT,manOutput);
12652 }
12653
12654 if (generateSql)
12655 {
12656 sqlOutput = createOutputDirectory(outputDirectory,Config_getString(SQLITE3_OUTPUT),"/sqlite3");
12657 Config_updateString(SQLITE3_OUTPUT,sqlOutput);
12658 }
12659 }
12660
12661 if (Config_getBool(HAVE_DOT))
12662 {
12663 QCString curFontPath = Config_getString(DOT_FONTPATH);
12664 if (curFontPath.isEmpty())
12665 {
12666 Portable::getenv("DOTFONTPATH");
12667 QCString newFontPath = ".";
12668 if (!curFontPath.isEmpty())
12669 {
12670 newFontPath+=Portable::pathListSeparator();
12671 newFontPath+=curFontPath;
12672 }
12673 Portable::setenv("DOTFONTPATH",qPrint(newFontPath));
12674 }
12675 else
12676 {
12677 Portable::setenv("DOTFONTPATH",qPrint(curFontPath));
12678 }
12679 }
12680
12681 /**************************************************************************
12682 * Handle layout file *
12683 **************************************************************************/
12684
12686 QCString layoutFileName = Config_getString(LAYOUT_FILE);
12687 bool defaultLayoutUsed = FALSE;
12688 if (layoutFileName.isEmpty())
12689 {
12690 layoutFileName = Config_updateString(LAYOUT_FILE,"DoxygenLayout.xml");
12691 defaultLayoutUsed = TRUE;
12692 }
12693 AUTO_TRACE_ADD("defaultLayoutUsed={}, layoutFileName={}",defaultLayoutUsed,layoutFileName);
12694
12695 FileInfo fi(layoutFileName.str());
12696 if (fi.exists())
12697 {
12698 msg("Parsing layout file {}...\n",layoutFileName);
12699 LayoutDocManager::instance().parse(layoutFileName);
12700 }
12701 else if (!defaultLayoutUsed)
12702 {
12703 warn_uncond("failed to open layout file '{}' for reading! Using default settings.\n",layoutFileName);
12704 }
12705 printLayout();
12706
12707 /**************************************************************************
12708 * Read and preprocess input *
12709 **************************************************************************/
12710
12711 // prevent search in the output directories
12712 StringVector exclPatterns = Config_getList(EXCLUDE_PATTERNS);
12713 if (generateHtml) exclPatterns.push_back(htmlOutput.str());
12714 if (generateDocbook) exclPatterns.push_back(docbookOutput.str());
12715 if (generateXml) exclPatterns.push_back(xmlOutput.str());
12716 if (generateLatex) exclPatterns.push_back(latexOutput.str());
12717 if (generateRtf) exclPatterns.push_back(rtfOutput.str());
12718 if (generateMan) exclPatterns.push_back(manOutput.str());
12719 Config_updateList(EXCLUDE_PATTERNS,exclPatterns);
12720
12721 if (!g_singleComment)
12722 {
12724
12726 }
12727
12728 // Notice: the order of the function calls below is very important!
12729
12730 if (generateHtml && !Config_getBool(USE_MATHJAX))
12731 {
12733 }
12734 if (generateRtf)
12735 {
12737 }
12738 if (generateDocbook)
12739 {
12741 }
12742
12744
12745 /**************************************************************************
12746 * Handle Tag Files *
12747 **************************************************************************/
12748
12749 std::shared_ptr<Entry> root = std::make_shared<Entry>();
12750
12751 if (!g_singleComment)
12752 {
12753 msg("Reading and parsing tag files\n");
12754 const StringVector &tagFileList = Config_getList(TAGFILES);
12755 for (const auto &s : tagFileList)
12756 {
12757 readTagFile(root,s.c_str());
12758 }
12759 }
12760
12761 /**************************************************************************
12762 * Parse source files *
12763 **************************************************************************/
12764
12765 addSTLSupport(root);
12766
12767 g_s.begin("Parsing files\n");
12768 if (g_singleComment)
12769 {
12770 //printf("Parsing comment %s\n",qPrint(g_commentFileName));
12771 if (g_commentFileName=="-")
12772 {
12773 std::string text = fileToString(g_commentFileName).str();
12774 addTerminalCharIfMissing(text,'\n');
12775 generateHtmlForComment("stdin.md",text);
12776 }
12777 else if (FileInfo(g_commentFileName.str()).isFile())
12778 {
12779 std::string text;
12781 addTerminalCharIfMissing(text,'\n');
12783 }
12784 else
12785 {
12786 }
12788 exit(0);
12789 }
12790 else
12791 {
12792 if (Config_getInt(NUM_PROC_THREADS)==1)
12793 {
12795 }
12796 else
12797 {
12799 }
12800 }
12801 g_s.end();
12802
12803 /**************************************************************************
12804 * Gather information *
12805 **************************************************************************/
12806
12807 g_s.begin("Building macro definition list...\n");
12809 g_s.end();
12810
12811 g_s.begin("Building group list...\n");
12812 buildGroupList(root.get());
12813 organizeSubGroups(root.get());
12814 g_s.end();
12815
12816 g_s.begin("Building directory list...\n");
12818 findDirDocumentation(root.get());
12819 g_s.end();
12820
12821 g_s.begin("Building namespace list...\n");
12822 buildNamespaceList(root.get());
12823 findUsingDirectives(root.get());
12824 g_s.end();
12825
12826 g_s.begin("Building file list...\n");
12827 buildFileList(root.get());
12828 g_s.end();
12829
12830 g_s.begin("Building class list...\n");
12831 buildClassList(root.get());
12832 g_s.end();
12833
12834 g_s.begin("Building concept list...\n");
12835 buildConceptList(root.get());
12836 g_s.end();
12837
12838 // build list of using declarations here (global list)
12839 buildListOfUsingDecls(root.get());
12840 g_s.end();
12841
12842 g_s.begin("Computing nesting relations for classes...\n");
12844 g_s.end();
12845 // 1.8.2-20121111: no longer add nested classes to the group as well
12846 //distributeClassGroupRelations();
12847
12848 // calling buildClassList may result in cached relations that
12849 // become invalid after resolveClassNestingRelations(), that's why
12850 // we need to clear the cache here
12851 Doxygen::typeLookupCache->clear();
12852 // we don't need the list of using declaration anymore
12853 g_usingDeclarations.clear();
12854
12855 g_s.begin("Associating documentation with classes...\n");
12856 buildClassDocList(root.get());
12857 g_s.end();
12858
12859 g_s.begin("Associating documentation with concepts...\n");
12860 buildConceptDocList(root.get());
12862 g_s.end();
12863
12864 g_s.begin("Associating documentation with modules...\n");
12865 findModuleDocumentation(root.get());
12866 g_s.end();
12867
12868 g_s.begin("Building example list...\n");
12869 buildExampleList(root.get());
12870 g_s.end();
12871
12872 g_s.begin("Searching for enumerations...\n");
12873 findEnums(root.get());
12874 g_s.end();
12875
12876 // Since buildVarList calls isVarWithConstructor
12877 // and this calls getResolvedClass we need to process
12878 // typedefs first so the relations between classes via typedefs
12879 // are properly resolved. See bug 536385 for an example.
12880 g_s.begin("Searching for documented typedefs...\n");
12881 buildTypedefList(root.get());
12882 g_s.end();
12883
12884 if (Config_getBool(OPTIMIZE_OUTPUT_SLICE))
12885 {
12886 g_s.begin("Searching for documented sequences...\n");
12887 buildSequenceList(root.get());
12888 g_s.end();
12889
12890 g_s.begin("Searching for documented dictionaries...\n");
12891 buildDictionaryList(root.get());
12892 g_s.end();
12893 }
12894
12895 g_s.begin("Searching for members imported via using declarations...\n");
12896 // this should be after buildTypedefList in order to properly import
12897 // used typedefs
12898 findUsingDeclarations(root.get(),TRUE); // do for python packages first
12899 findUsingDeclarations(root.get(),FALSE); // then the rest
12900 g_s.end();
12901
12902 g_s.begin("Searching for included using directives...\n");
12904 g_s.end();
12905
12906 g_s.begin("Searching for documented variables...\n");
12907 buildVarList(root.get());
12908 g_s.end();
12909
12910 g_s.begin("Building interface member list...\n");
12911 buildInterfaceAndServiceList(root.get()); // UNO IDL
12912
12913 g_s.begin("Building member list...\n"); // using class info only !
12914 buildFunctionList(root.get());
12915 g_s.end();
12916
12917 g_s.begin("Searching for friends...\n");
12918 findFriends();
12919 g_s.end();
12920
12921 g_s.begin("Searching for documented defines...\n");
12922 findDefineDocumentation(root.get());
12923 g_s.end();
12924
12925 g_s.begin("Computing class inheritance relations...\n");
12926 findClassEntries(root.get());
12928 g_s.end();
12929
12930 g_s.begin("Computing class usage relations...\n");
12932 g_s.end();
12933
12934 if (Config_getBool(INLINE_SIMPLE_STRUCTS))
12935 {
12936 g_s.begin("Searching for tag less structs...\n");
12938 g_s.end();
12939 }
12940
12941 g_s.begin("Flushing cached template relations that have become invalid...\n");
12943 g_s.end();
12944
12945 g_s.begin("Warn for undocumented namespaces...\n");
12947 g_s.end();
12948
12949 g_s.begin("Computing class relations...\n");
12952 if (Config_getBool(OPTIMIZE_OUTPUT_VHDL))
12953 {
12955 }
12957 g_classEntries.clear();
12958 g_s.end();
12959
12960 g_s.begin("Add enum values to enums...\n");
12961 addEnumValuesToEnums(root.get());
12962 findEnumDocumentation(root.get());
12963 g_s.end();
12964
12965 g_s.begin("Searching for member function documentation...\n");
12966 findObjCMethodDefinitions(root.get());
12967 findMemberDocumentation(root.get()); // may introduce new members !
12968 findUsingDeclImports(root.get()); // may introduce new members !
12969 g_usingClassMap.clear();
12973 g_s.end();
12974
12975 // moved to after finding and copying documentation,
12976 // as this introduces new members see bug 722654
12977 g_s.begin("Creating members for template instances...\n");
12979 g_s.end();
12980
12981 g_s.begin("Building page list...\n");
12982 buildPageList(root.get());
12983 g_s.end();
12984
12985 g_s.begin("Building requirements list...\n");
12986 buildRequirementsList(root.get());
12987 g_s.end();
12988
12989 g_s.begin("Search for main page...\n");
12990 findMainPage(root.get());
12991 findMainPageTagFiles(root.get());
12992 g_s.end();
12993
12994 g_s.begin("Computing page relations...\n");
12995 computePageRelations(root.get());
12997 g_s.end();
12998
12999 g_s.begin("Determining the scope of groups...\n");
13000 findGroupScope(root.get());
13001 g_s.end();
13002
13003 g_s.begin("Computing module relations...\n");
13004 auto &mm = ModuleManager::instance();
13005 mm.resolvePartitions();
13006 mm.resolveImports();
13007 mm.collectExportedSymbols();
13008 g_s.end();
13009
13010 auto memberNameComp = [](const MemberNameLinkedMap::Ptr &n1,const MemberNameLinkedMap::Ptr &n2)
13011 {
13012 return qstricmp_sort(n1->memberName().data()+getPrefixIndex(n1->memberName()),
13013 n2->memberName().data()+getPrefixIndex(n2->memberName())
13014 )<0;
13015 };
13016
13017 auto classComp = [](const ClassLinkedMap::Ptr &c1,const ClassLinkedMap::Ptr &c2)
13018 {
13019 if (Config_getBool(SORT_BY_SCOPE_NAME))
13020 {
13021 return qstricmp_sort(c1->name(), c2->name())<0;
13022 }
13023 else
13024 {
13025 int i = qstricmp_sort(c1->className(), c2->className());
13026 return i==0 ? qstricmp_sort(c1->name(), c2->name())<0 : i<0;
13027 }
13028 };
13029
13030 auto namespaceComp = [](const NamespaceLinkedMap::Ptr &n1,const NamespaceLinkedMap::Ptr &n2)
13031 {
13032 return qstricmp_sort(n1->name(),n2->name())<0;
13033 };
13034
13035 auto conceptComp = [](const ConceptLinkedMap::Ptr &c1,const ConceptLinkedMap::Ptr &c2)
13036 {
13037 return qstricmp_sort(c1->name(),c2->name())<0;
13038 };
13039
13040 g_s.begin("Sorting lists...\n");
13041 std::stable_sort(Doxygen::memberNameLinkedMap->begin(),
13043 memberNameComp);
13044 std::stable_sort(Doxygen::functionNameLinkedMap->begin(),
13046 memberNameComp);
13047 std::stable_sort(Doxygen::hiddenClassLinkedMap->begin(),
13049 classComp);
13050 std::stable_sort(Doxygen::classLinkedMap->begin(),
13052 classComp);
13053 std::stable_sort(Doxygen::conceptLinkedMap->begin(),
13055 conceptComp);
13056 std::stable_sort(Doxygen::namespaceLinkedMap->begin(),
13058 namespaceComp);
13059 g_s.end();
13060
13061 g_s.begin("Determining which enums are documented\n");
13063 g_s.end();
13064
13065 g_s.begin("Computing member relations...\n");
13068 g_s.end();
13069
13070 g_s.begin("Building full member lists recursively...\n");
13072 g_s.end();
13073
13074 g_s.begin("Adding members to member groups.\n");
13076 g_s.end();
13077
13078 if (Config_getBool(DISTRIBUTE_GROUP_DOC))
13079 {
13080 g_s.begin("Distributing member group documentation.\n");
13082 g_s.end();
13083 }
13084
13085 g_s.begin("Computing member references...\n");
13087 g_s.end();
13088
13089 if (Config_getBool(INHERIT_DOCS))
13090 {
13091 g_s.begin("Inheriting documentation...\n");
13093 g_s.end();
13094 }
13095
13096
13097 // compute the shortest possible names of all files
13098 // without losing the uniqueness of the file names.
13099 g_s.begin("Generating disk names...\n");
13101 g_s.end();
13102
13103 g_s.begin("Adding source references...\n");
13105 g_s.end();
13106
13107 g_s.begin("Adding xrefitems...\n");
13110 g_s.end();
13111
13112 g_s.begin("Adding requirements...\n");
13115 g_s.end();
13116
13117 g_s.begin("Sorting member lists...\n");
13119 g_s.end();
13120
13121 g_s.begin("Setting anonymous enum type...\n");
13123 g_s.end();
13124
13125 g_s.begin("Computing dependencies between directories...\n");
13127 g_s.end();
13128
13129 g_s.begin("Generating citations page...\n");
13131 g_s.end();
13132
13133 g_s.begin("Counting members...\n");
13134 countMembers();
13135 g_s.end();
13136
13137 g_s.begin("Counting data structures...\n");
13139 g_s.end();
13140
13141 g_s.begin("Resolving user defined references...\n");
13143 g_s.end();
13144
13145 g_s.begin("Finding anchors and sections in the documentation...\n");
13147 g_s.end();
13148
13149 g_s.begin("Transferring function references...\n");
13151 g_s.end();
13152
13153 g_s.begin("Combining using relations...\n");
13155 g_s.end();
13156
13158 g_s.begin("Adding members to index pages...\n");
13160 addToIndices();
13161 g_s.end();
13162
13163 g_s.begin("Correcting members for VHDL...\n");
13165 g_s.end();
13166
13167 g_s.begin("Computing tooltip texts...\n");
13169 g_s.end();
13170
13171 if (Config_getBool(SORT_GROUP_NAMES))
13172 {
13173 std::stable_sort(Doxygen::groupLinkedMap->begin(),
13175 [](const auto &g1,const auto &g2)
13176 { return g1->groupTitle() < g2->groupTitle(); });
13177
13178 for (const auto &gd : *Doxygen::groupLinkedMap)
13179 {
13180 gd->sortSubGroups();
13181 }
13182 }
13183
13184 printNavTree(root.get(),0);
13186}
13187
13189{
13190 AUTO_TRACE();
13191 /**************************************************************************
13192 * Initialize output generators *
13193 **************************************************************************/
13194
13195 /// add extra languages for which we can only produce syntax highlighted code
13197
13198 //// dump all symbols
13199 if (g_dumpSymbolMap)
13200 {
13201 dumpSymbolMap();
13202 exit(0);
13203 }
13204
13205 bool generateHtml = Config_getBool(GENERATE_HTML);
13206 bool generateLatex = Config_getBool(GENERATE_LATEX);
13207 bool generateMan = Config_getBool(GENERATE_MAN);
13208 bool generateRtf = Config_getBool(GENERATE_RTF);
13209 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
13210
13211
13213 if (generateHtml)
13214 {
13218 }
13219 if (generateLatex)
13220 {
13223 }
13224 if (generateDocbook)
13225 {
13228 }
13229 if (generateMan)
13230 {
13231 g_outputList->add<ManGenerator>();
13233 }
13234 if (generateRtf)
13235 {
13236 g_outputList->add<RTFGenerator>();
13238 }
13239 if (Config_getBool(USE_HTAGS))
13240 {
13242 QCString htmldir = Config_getString(HTML_OUTPUT);
13243 if (!Htags::execute(htmldir))
13244 err("USE_HTAGS is YES but htags(1) failed. \n");
13245 else if (!Htags::loadFilemap(htmldir))
13246 err("htags(1) ended normally but failed to load the filemap. \n");
13247 }
13248
13249 /**************************************************************************
13250 * Generate documentation *
13251 **************************************************************************/
13252
13253 g_s.begin("Generating style sheet...\n");
13254 //printf("writing style info\n");
13255 g_outputList->writeStyleInfo(0); // write first part
13256 g_s.end();
13257
13258 bool searchEngine = Config_getBool(SEARCHENGINE);
13259 bool serverBasedSearch = Config_getBool(SERVER_BASED_SEARCH);
13260
13261 g_s.begin("Generating search indices...\n");
13262 if (searchEngine && !serverBasedSearch && generateHtml)
13263 {
13265 }
13266
13267 // generate search indices (need to do this before writing other HTML
13268 // pages as these contain a drop down menu with options depending on
13269 // what categories we find in this function.
13270 if (generateHtml && searchEngine)
13271 {
13272 QCString searchDirName = Config_getString(HTML_OUTPUT)+"/search";
13273 Dir searchDir(searchDirName.str());
13274 if (!searchDir.exists() && !searchDir.mkdir(searchDirName.str()))
13275 {
13276 term("Could not create search results directory '{}' $PWD='{}'\n",
13277 searchDirName,Dir::currentDirPath());
13278 }
13279 HtmlGenerator::writeSearchData(searchDirName);
13280 if (!serverBasedSearch) // client side search index
13281 {
13283 }
13284 }
13285 g_s.end();
13286
13287 // copy static stuff
13288 if (generateHtml)
13289 {
13291 copyLogo(Config_getString(HTML_OUTPUT));
13292 copyIcon(Config_getString(HTML_OUTPUT));
13293 copyExtraFiles(Config_getList(HTML_EXTRA_FILES),"HTML_EXTRA_FILES",Config_getString(HTML_OUTPUT));
13294 }
13295 if (generateLatex)
13296 {
13298 copyLogo(Config_getString(LATEX_OUTPUT));
13299 copyIcon(Config_getString(LATEX_OUTPUT));
13300 copyExtraFiles(Config_getList(LATEX_EXTRA_FILES),"LATEX_EXTRA_FILES",Config_getString(LATEX_OUTPUT));
13301 }
13302 if (generateDocbook)
13303 {
13304 copyLogo(Config_getString(DOCBOOK_OUTPUT));
13305 copyIcon(Config_getString(DOCBOOK_OUTPUT));
13306 }
13307 if (generateRtf)
13308 {
13309 copyLogo(Config_getString(RTF_OUTPUT));
13310 copyIcon(Config_getString(RTF_OUTPUT));
13311 copyExtraFiles(Config_getList(RTF_EXTRA_FILES),"RTF_EXTRA_FILES",Config_getString(RTF_OUTPUT));
13312 }
13313
13315 if (fm.hasFormulas() && generateHtml
13316 && !Config_getBool(USE_MATHJAX))
13317 {
13318 g_s.begin("Generating images for formulas in HTML...\n");
13319 fm.generateImages(Config_getString(HTML_OUTPUT), Config_getEnum(HTML_FORMULA_FORMAT)==HTML_FORMULA_FORMAT_t::svg ?
13321 g_s.end();
13322 }
13323 if (fm.hasFormulas() && generateRtf)
13324 {
13325 g_s.begin("Generating images for formulas in RTF...\n");
13327 g_s.end();
13328 }
13329
13330 if (fm.hasFormulas() && generateDocbook)
13331 {
13332 g_s.begin("Generating images for formulas in Docbook...\n");
13334 g_s.end();
13335 }
13336
13337 g_s.begin("Generating example documentation...\n");
13339 g_s.end();
13340
13341 g_s.begin("Generating file sources...\n");
13343 g_s.end();
13344
13345 g_s.begin("Generating file documentation...\n");
13347 g_s.end();
13348
13349 g_s.begin("Generating page documentation...\n");
13351 g_s.end();
13352
13353 g_s.begin("Generating group documentation...\n");
13355 g_s.end();
13356
13357 g_s.begin("Generating class documentation...\n");
13359 g_s.end();
13360
13361 g_s.begin("Generating concept documentation...\n");
13363 g_s.end();
13364
13365 g_s.begin("Generating module documentation...\n");
13367 g_s.end();
13368
13369 g_s.begin("Generating namespace documentation...\n");
13371 g_s.end();
13372
13373 if (Config_getBool(GENERATE_LEGEND))
13374 {
13375 g_s.begin("Generating graph info page...\n");
13377 g_s.end();
13378 }
13379
13380 g_s.begin("Generating directory documentation...\n");
13382 g_s.end();
13383
13384 if (g_outputList->size()>0)
13385 {
13387 }
13388
13389 g_s.begin("finalizing index lists...\n");
13390 Doxygen::indexList->finalize();
13391 g_s.end();
13392
13393 g_s.begin("writing tag file...\n");
13394 writeTagFile();
13395 g_s.end();
13396
13397 if (Config_getBool(GENERATE_XML))
13398 {
13399 g_s.begin("Generating XML output...\n");
13401 generateXML();
13403 g_s.end();
13404 }
13405 if (Config_getBool(GENERATE_SQLITE3))
13406 {
13407 g_s.begin("Generating SQLITE3 output...\n");
13409 g_s.end();
13410 }
13411
13412 if (Config_getBool(GENERATE_AUTOGEN_DEF))
13413 {
13414 g_s.begin("Generating AutoGen DEF output...\n");
13415 generateDEF();
13416 g_s.end();
13417 }
13418 if (Config_getBool(GENERATE_PERLMOD))
13419 {
13420 g_s.begin("Generating Perl module output...\n");
13422 g_s.end();
13423 }
13424 if (generateHtml && searchEngine && serverBasedSearch)
13425 {
13426 g_s.begin("Generating search index\n");
13427 if (Doxygen::searchIndex.kind()==SearchIndexIntf::Internal) // write own search index
13428 {
13430 Doxygen::searchIndex.write(Config_getString(HTML_OUTPUT)+"/search/search.idx");
13431 }
13432 else // write data for external search index
13433 {
13435 QCString searchDataFile = Config_getString(SEARCHDATA_FILE);
13436 if (searchDataFile.isEmpty())
13437 {
13438 searchDataFile="searchdata.xml";
13439 }
13440 if (!Portable::isAbsolutePath(searchDataFile.data()))
13441 {
13442 searchDataFile.prepend(Config_getString(OUTPUT_DIRECTORY)+"/");
13443 }
13444 Doxygen::searchIndex.write(searchDataFile);
13445 }
13446 g_s.end();
13447 }
13448
13449 if (generateRtf)
13450 {
13451 g_s.begin("Combining RTF output...\n");
13452 if (!RTFGenerator::preProcessFileInplace(Config_getString(RTF_OUTPUT),"refman.rtf"))
13453 {
13454 err("An error occurred during post-processing the RTF files!\n");
13455 }
13456 g_s.end();
13457 }
13458
13459 g_s.begin("Running plantuml with JAVA...\n");
13461 g_s.end();
13462
13463 if (Config_getBool(HAVE_DOT))
13464 {
13465 g_s.begin("Running dot...\n");
13467 g_s.end();
13468 }
13469
13470 if (generateHtml &&
13471 Config_getBool(GENERATE_HTMLHELP) &&
13472 !Config_getString(HHC_LOCATION).isEmpty())
13473 {
13474 g_s.begin("Running html help compiler...\n");
13476 g_s.end();
13477 }
13478
13479 if ( generateHtml &&
13480 Config_getBool(GENERATE_QHP) &&
13481 !Config_getString(QHG_LOCATION).isEmpty())
13482 {
13483 g_s.begin("Running qhelpgenerator...\n");
13485 g_s.end();
13486 }
13487
13488 g_outputList->cleanup();
13490
13491 msg("type lookup cache used {}/{} hits={} misses={}\n",
13493 Doxygen::typeLookupCache->capacity(),
13495 Doxygen::typeLookupCache->misses());
13496 msg("symbol lookup cache used {}/{} hits={} misses={}\n",
13498 Doxygen::symbolLookupCache->capacity(),
13500 Doxygen::symbolLookupCache->misses());
13501 int typeCacheParam = computeIdealCacheParam(static_cast<size_t>(Doxygen::typeLookupCache->misses()*2/3)); // part of the cache is flushed, hence the 2/3 correction factor
13502 int symbolCacheParam = computeIdealCacheParam(static_cast<size_t>(Doxygen::symbolLookupCache->misses()));
13503 int cacheParam = std::max(typeCacheParam,symbolCacheParam);
13504 if (cacheParam>Config_getInt(LOOKUP_CACHE_SIZE))
13505 {
13506 msg("Note: based on cache misses the ideal setting for LOOKUP_CACHE_SIZE is {} at the cost of higher memory usage.\n",cacheParam);
13507 }
13508
13510 {
13511
13512 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
13513 if (numThreads<1) numThreads=1;
13514 msg("Total elapsed time: {:.6f} seconds\n(of which an average of {:.6f} seconds per thread waiting for external tools to finish)\n",
13515 (static_cast<double>(Debug::elapsedTime())),
13516 Portable::getSysElapsedTime()/static_cast<double>(numThreads)
13517 );
13518 g_s.print();
13519
13521 msg("finished...\n");
13523 }
13524 else
13525 {
13526 msg("finished...\n");
13527 }
13528
13529
13530 /**************************************************************************
13531 * Start cleaning up *
13532 **************************************************************************/
13533
13535
13537 Dir thisDir;
13538 thisDir.remove(Doxygen::filterDBFileName.str());
13540 exitTracing();
13542 delete Doxygen::clangUsrMap;
13544
13545 //dumpDocNodeSizes();
13546}
void readAliases()
Definition aliases.cpp:161
constexpr auto prefix
Definition anchor.cpp:44
std::vector< ArgumentList > ArgumentLists
Definition arguments.h:147
This class represents an function or template argument list.
Definition arguments.h:65
RefQualifierType refQualifier() const
Definition arguments.h:116
bool noParameters() const
Definition arguments.h:117
bool pureSpecifier() const
Definition arguments.h:113
iterator end()
Definition arguments.h:94
void setTrailingReturnType(const QCString &s)
Definition arguments.cpp:36
bool hasParameters() const
Definition arguments.h:76
bool isDeleted() const
Definition arguments.h:115
QCString trailingReturnType() const
Definition arguments.h:114
size_t size() const
Definition arguments.h:100
void setPureSpecifier(bool b)
Definition arguments.h:121
bool constSpecifier() const
Definition arguments.h:111
void push_back(const Argument &a)
Definition arguments.h:102
bool empty() const
Definition arguments.h:99
void setConstSpecifier(bool b)
Definition arguments.h:119
void setRefQualifier(RefQualifierType t)
Definition arguments.h:126
void setIsDeleted(bool b)
Definition arguments.h:125
iterator begin()
Definition arguments.h:93
bool volatileSpecifier() const
Definition arguments.h:112
void setNoParameters(bool b)
Definition arguments.h:127
void setVolatileSpecifier(bool b)
Definition arguments.h:120
Definition cache.h:32
static CitationManager & instance()
Definition cite.cpp:85
void clear()
clears the database
Definition cite.cpp:110
void generatePage()
Generate the citations page.
Definition cite.cpp:331
std::unique_ptr< ClangTUParser > createTUParser(const FileDef *fd) const
static ClangParser * instance()
Returns the one and only instance of the class.
Clang parser object for a single translation unit, which consists of a source file and the directly o...
Definition clangparser.h:25
void switchToFile(const FileDef *fd)
Switches to another file within the translation unit started with start().
void parse()
Parse the file given at construction time as a translation unit This file should already be preproces...
A abstract class representing of a compound symbol.
Definition classdef.h:104
virtual const ArgumentList & templateArguments() const =0
Returns the template arguments of this class.
virtual QCString compoundTypeString() const =0
Returns the type of compound as a string.
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 const MemberDef * getMemberByName(const QCString &) const =0
Returns the member with the given name.
virtual const TemplateInstanceList & getTemplateInstances() const =0
Returns a sorted dictionary with all template instances found for this template class.
virtual int isBaseClass(const ClassDef *bcd, bool followInstances, const QCString &templSpec=QCString()) const =0
Returns TRUE iff bcd is a direct or indirect base class of this 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 MemberList * getMemberList(MemberListType lt) const =0
Returns the members in the list identified by lt.
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 bool isEmbeddedInOuterScope() const =0
virtual const MemberNameInfoLinkedMap & memberNameInfoLinkedMap() const =0
Returns a dictionary of all members.
virtual bool isImplicitTemplateInstance() const =0
virtual QCString qualifiedNameWithTemplateParameters(const ArgumentLists *actualParams=nullptr, uint32_t *actualParamIndex=nullptr) const =0
virtual std::unique_ptr< ClassDef > deepCopy(const QCString &name) const =0
virtual const ClassDef * templateMaster() const =0
Returns the template master of which this class is an instance.
CompoundType
The various compound types.
Definition classdef.h:109
@ Singleton
Definition classdef.h:117
@ Interface
Definition classdef.h:112
@ Exception
Definition classdef.h:115
virtual CompoundType compoundType() const =0
Returns the type of compound this is, i.e.
virtual bool containsOverload(const MemberDef *md) const =0
virtual ClassLinkedRefMap getClasses() const =0
returns the classes nested into this class
virtual FileDef * getFileDef() const =0
Returns the namespace this compound is in, or 0 if it has a global scope.
virtual QCString requiresClause() const =0
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 setClassName(const QCString &name)=0
virtual void countMembers()=0
virtual void addMembersToMemberGroup()=0
virtual void makeTemplateArgument(bool b=TRUE)=0
virtual void setPrimaryConstructorParams(const ArgumentList &list)=0
virtual void setTemplateBaseClassNames(const TemplateNameMap &templateNames)=0
virtual void insertExplicitTemplateInstance(ClassDef *instance, const QCString &spec)=0
virtual void setMetaData(const QCString &md)=0
virtual void setFileDef(FileDef *fd)=0
virtual void addUsedClass(ClassDef *cd, const QCString &accessName, Protection prot)=0
virtual void reclassifyMember(MemberDefMutable *md, MemberType t)=0
virtual ClassDef * insertTemplateInstance(const QCString &fileName, int startLine, int startColumn, const QCString &templSpec, bool &freshInstance)=0
virtual void insertBaseClass(ClassDef *, const QCString &name, Protection p, Specifier s, const QCString &t=QCString())=0
virtual void setTemplateArguments(const ArgumentList &al)=0
virtual void setTemplateMaster(const ClassDef *tm)=0
virtual void mergeCategory(ClassDef *category)=0
virtual void addQualifiers(const StringVector &qualifiers)=0
virtual void setClassSpecifier(TypeSpecifier spec)=0
virtual void insertSubClass(ClassDef *, Protection p, Specifier s, const QCString &t=QCString())=0
virtual void insertUsedFile(const FileDef *)=0
virtual void setRequiresClause(const QCString &req)=0
virtual void setTagLessReference(const ClassDef *cd)=0
virtual void setUsedOnly(bool b)=0
virtual void sortMemberLists()=0
virtual void setProtection(Protection p)=0
virtual void setTypeConstraints(const ArgumentList &al)=0
virtual void overrideInheritanceGraph(CLASS_GRAPH_t e)=0
virtual void setAnonymousEnumType()=0
virtual void setCompoundType(CompoundType t)=0
virtual void distributeMemberGroupDocumentation()=0
virtual void findSectionsInDocumentation()=0
virtual void addUsedByClass(ClassDef *cd, const QCString &accessName, Protection prot)=0
virtual void insertMember(MemberDef *)=0
virtual void sortAllMembersList()=0
virtual void addMembersToTemplateInstance(const ClassDef *cd, const ArgumentList &templateArguments, const QCString &templSpec)=0
virtual void mergeMembers()=0
virtual void setIsStatic(bool b)=0
virtual void setSubGrouping(bool enabled)=0
virtual void addCodePart(const QCString &code, int lineNr, int colNr)=0
virtual void setFileDef(FileDef *fd)=0
virtual void writeTagFile(TextStream &)=0
virtual void setInitializer(const QCString &init)=0
virtual void addDocPart(const QCString &doc, int lineNr, int colNr)=0
virtual void writeDocumentation(OutputList &ol)=0
virtual void setGroupId(int id)=0
virtual void findSectionsInDocumentation()=0
virtual void setTemplateArguments(const ArgumentList &al)=0
@ ExtCmd
Definition debug.h:36
@ Sections
Definition debug.h:48
@ Time
Definition debug.h:35
@ Qhp
Definition debug.h:44
@ Entries
Definition debug.h:47
static void printFlags()
Definition debug.cpp: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:76
static double elapsedTime()
Definition debug.cpp:200
static void startTimer()
Definition debug.cpp:195
static bool setFlagStr(const QCString &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 QCString docFile() const =0
virtual const QCString & localName() const =0
virtual int getEndBodyLine() const =0
virtual SrcLangExt getLanguage() const =0
Returns the programming language this definition was written in.
virtual int docLine() const =0
virtual QCString getDefFileName() const =0
virtual bool isLinkable() const =0
virtual int getDefLine() const =0
virtual DefType definitionType() const =0
virtual QCString anchor() 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 QCString briefDescription(bool abbreviate=FALSE) const =0
virtual bool isAnonymous() const =0
virtual bool isHidden() const =0
virtual const Definition * findInnerCompound(const QCString &name) const =0
virtual int getStartDefLine() const =0
virtual const GroupList & partOfGroups() const =0
virtual QCString documentation() const =0
virtual QCString qualifiedName() const =0
virtual QCString displayName(bool includeScope=TRUE) const =0
virtual bool isArtificial() const =0
virtual QCString briefFile() const =0
virtual QCString getOutputFileBase() const =0
virtual Definition * getOuterScope() const =0
virtual int getStartBodyLine() const =0
virtual int getDefColumn() const =0
virtual bool isReference() const =0
virtual QCString inbodyDocumentation() const =0
virtual QCString inbodyFile() const =0
virtual const QCString & name() const =0
virtual void mergeReferencedBy(const Definition *other)=0
virtual void setExported(bool b)=0
virtual void setBodySegment(int defLine, int bls, int ble)=0
virtual void setName(const QCString &name)=0
virtual void setHidden(bool b)=0
virtual void mergeReferences(const Definition *other)=0
virtual void setDocumentation(const QCString &d, const QCString &docFile, int docLine, bool stripWhiteSpace=TRUE)=0
virtual void setDefFile(const QCString &df, int defLine, int defColumn)=0
virtual void addInnerCompound(Definition *d)=0
virtual void addSectionsToDefinition(const std::vector< const SectionInfo * > &anchorList)=0
virtual void setInbodyDocumentation(const QCString &d, const QCString &docFile, int docLine)=0
virtual void setLanguage(SrcLangExt lang)=0
virtual void setOuterScope(Definition *d)=0
virtual void setArtificial(bool b)=0
virtual void setId(const QCString &name)=0
virtual void makePartOfGroup(GroupDef *gd)=0
virtual void setBodyDef(const FileDef *fd)=0
virtual void setBriefDescription(const QCString &b, const QCString &briefFile, int briefLine)=0
virtual void setReference(const QCString &r)=0
virtual void setRequirementReferences(const RequirementRefs &rqli)=0
virtual void setRefItems(const RefItemVector &sli)=0
virtual void computeTooltip()=0
A model of a directory symbol.
Definition dirdef.h:110
virtual void overrideDirectoryGraph(bool e)=0
Class representing a directory in the file system.
Definition dir.h:75
static std::string currentDirPath()
Definition dir.cpp:342
std::string absPath() const
Definition dir.cpp:364
bool mkdir(const std::string &path, bool acceptsAbsPath=true) const
Definition dir.cpp:295
void setPath(const std::string &path)
Definition dir.cpp:229
bool remove(const std::string &path, bool acceptsAbsPath=true) const
Definition dir.cpp:314
DirIterator iterator() const
Definition dir.cpp:239
static std::string cleanDirPath(const std::string &path)
Definition dir.cpp:357
static bool setCurrent(const std::string &path)
Definition dir.cpp:350
bool exists() const
Definition dir.cpp:257
A linked map of directories.
Definition dirdef.h:175
A class that generates docset files.
Definition docsets.h:36
static void init()
bool run()
Definition dot.cpp:128
static DotManager * instance()
Definition dot.cpp:78
static NamespaceLinkedMap * namespaceLinkedMap
Definition doxygen.h:114
static ConceptLinkedMap * conceptLinkedMap
Definition doxygen.h:97
static bool suppressDocWarnings
Definition doxygen.h:131
static FileNameLinkedMap * plantUmlFileNameLinkedMap
Definition doxygen.h:109
static bool parseSourcesNeeded
Definition doxygen.h:122
static StringUnorderedSet inputPaths
Definition doxygen.h:103
static std::unique_ptr< PageDef > mainPage
Definition doxygen.h:100
static bool clangAssistedParsing
Definition doxygen.h:137
static StringUnorderedSet expandAsDefinedSet
Definition doxygen.h:118
static FileNameLinkedMap * inputNameLinkedMap
Definition doxygen.h:104
static ParserManager * parserManager
Definition doxygen.h:130
static Cache< std::string, LookupInfo > * typeLookupCache
Definition doxygen.h:126
static InputFileEncodingList inputFileEncodingList
Definition doxygen.h:139
static ClassLinkedMap * classLinkedMap
Definition doxygen.h:95
static MemberNameLinkedMap * functionNameLinkedMap
Definition doxygen.h:111
static PageLinkedMap * exampleLinkedMap
Definition doxygen.h:98
static FileNameLinkedMap * dotFileNameLinkedMap
Definition doxygen.h:106
static NamespaceDefMutable * globalScope
Definition doxygen.h:120
static FileNameLinkedMap * imageNameLinkedMap
Definition doxygen.h:105
static FileNameLinkedMap * mscFileNameLinkedMap
Definition doxygen.h:107
static QCString verifiedDotPath
Definition doxygen.h:138
static MemberGroupInfoMap memberGroupInfoMap
Definition doxygen.h:117
static QCString spaces
Definition doxygen.h:134
static IndexList * indexList
Definition doxygen.h:133
static StaticInitMap staticInitMap
Definition doxygen.h:142
static Cache< std::string, LookupInfo > * symbolLookupCache
Definition doxygen.h:127
static StringMap tagDestinationMap
Definition doxygen.h:115
static std::mutex countFlowKeywordsMutex
Definition doxygen.h:140
static ClassLinkedMap * hiddenClassLinkedMap
Definition doxygen.h:96
static FileNameLinkedMap * diaFileNameLinkedMap
Definition doxygen.h:108
static QCString htmlFileExtension
Definition doxygen.h:121
static QCString filterDBFileName
Definition doxygen.h:132
static PageLinkedMap * pageLinkedMap
Definition doxygen.h:99
static bool generatingXmlOutput
Definition doxygen.h:135
static std::unique_ptr< NamespaceDef > globalNamespaceDef
Definition doxygen.h:119
static DefinesPerFileList macroDefinitions
Definition doxygen.h:136
static DirLinkedMap * dirLinkedMap
Definition doxygen.h:128
static NamespaceAliasInfoMap namespaceAliasMap
Definition doxygen.h:112
static MemberNameLinkedMap * memberNameLinkedMap
Definition doxygen.h:110
static SymbolMap< Definition > * symbolMap
Definition doxygen.h:124
static StringUnorderedSet tagFileSet
Definition doxygen.h:116
static FileNameLinkedMap * includeNameLinkedMap
Definition doxygen.h:101
static FileNameLinkedMap * exampleNameLinkedMap
Definition doxygen.h:102
static SearchIndexIntf searchIndex
Definition doxygen.h:123
static DirRelationLinkedMap dirRelations
Definition doxygen.h:129
static std::mutex addExampleMutex
Definition doxygen.h:141
static ClangUsrMap * clangUsrMap
Definition doxygen.h:125
static GroupLinkedMap * groupLinkedMap
Definition doxygen.h:113
Generator for Eclipse help files.
Definition eclipsehelp.h:44
static EmojiEntityMapper & instance()
Returns the one and only instance of the Emoji entity mapper.
Definition emoji.cpp:1978
void writeEmojiFile(TextStream &t)
Writes the list of supported emojis to the given file.
Definition emoji.cpp:1999
Represents an unstructured piece of information, about an entity found in the sources.
Definition entry.h:117
TextStream initializer
initial value (for variables)
Definition entry.h:198
VhdlSpecifier vhdlSpec
VHDL specifiers.
Definition entry.h:184
bool subGrouping
automatically group class members?
Definition entry.h:189
RequirementRefs rqli
references to requirements
Definition entry.h:228
const std::vector< std::shared_ptr< Entry > > & children() const
Definition entry.h:140
bool proto
prototype ?
Definition entry.h:188
GroupDocType groupDocType
Definition entry.h:232
QCString metaData
Slice metadata.
Definition entry.h:235
int docLine
line number at which the documentation was found
Definition entry.h:202
QCString bitfields
member's bit fields
Definition entry.h:194
ArgumentList typeConstr
where clause (C#) for type constraints
Definition entry.h:216
void markAsProcessed() const
Definition entry.h:168
int endBodyLine
line number where the definition ends
Definition entry.h:219
bool exported
is the symbol exported from a C++20 module
Definition entry.h:190
const TagInfo * tagInfo() const
Definition entry.h:178
QCString includeName
include name (3 arg of \class)
Definition entry.h:200
QCString id
libclang id
Definition entry.h:233
ArgumentLists tArgLists
template argument declarations
Definition entry.h:196
LocalToc localToc
Definition entry.h:234
MethodTypes mtype
signal, slot, (dcop) method, or property?
Definition entry.h:182
@ GROUPDOC_NORMAL
defgroup
Definition entry.h:122
SrcLangExt lang
programming language in which this entry was found
Definition entry.h:229
Entry * parent() const
Definition entry.h:135
QCString inbodyDocs
documentation inside the body of a function
Definition entry.h:207
int startColumn
start column of entry in the source
Definition entry.h:226
QCString relates
related class (doc block)
Definition entry.h:210
bool explicitExternal
explicitly defined as external?
Definition entry.h:187
std::vector< const SectionInfo * > anchors
list of anchors defined in this entry
Definition entry.h:223
QCString fileName
file this entry was extracted from
Definition entry.h:224
RelatesType relatesType
how relates is handled
Definition entry.h:211
QCString write
property write accessor
Definition entry.h:213
QCString args
member argument string
Definition entry.h:193
QCString type
member type
Definition entry.h:174
std::vector< Grouping > groups
list of groups this entry belongs to
Definition entry.h:222
CommandOverrides commandOverrides
store info for commands whose default can be overridden
Definition entry.h:191
QCString exception
throw specification
Definition entry.h:215
int startLine
start line of entry in the source
Definition entry.h:225
QCString req
C++20 requires clause.
Definition entry.h:236
ArgumentList argList
member arguments as a list
Definition entry.h:195
QCString name
member name
Definition entry.h:175
QCString includeFile
include file (2 arg of \class, must be unique)
Definition entry.h:199
int inbodyLine
line number at which the body doc was found
Definition entry.h:208
EntryType section
entry type (see Sections);
Definition entry.h:173
QCString briefFile
file in which the brief desc. was found
Definition entry.h:206
int bodyLine
line number of the body in the source
Definition entry.h:217
int mGrpId
member group id
Definition entry.h:220
std::vector< BaseInfo > extends
list of base classes
Definition entry.h:221
Specifier virt
virtualness of the entry
Definition entry.h:192
std::vector< std::string > qualifiers
qualifiers specified with the qualifier command
Definition entry.h:237
QCString doc
documentation block (partly parsed)
Definition entry.h:201
QCString read
property read accessor
Definition entry.h:212
RefItemVector sli
special lists (test/todo/bug/deprecated/..) this entry is in
Definition entry.h:227
QCString docFile
file in which the documentation was found
Definition entry.h:203
Protection protection
class protection
Definition entry.h:181
bool artificial
Artificially introduced item.
Definition entry.h:231
bool hidden
does this represent an entity that is hidden from the output
Definition entry.h:230
QCString brief
brief description (doc block)
Definition entry.h:204
int briefLine
line number at which the brief desc. was found
Definition entry.h:205
FileDef * fileDef() const
Definition entry.h:170
int initLines
define/variable initializer lines to show
Definition entry.h:185
bool isStatic
static ?
Definition entry.h:186
QCString inbodyFile
file in which the body doc was found
Definition entry.h:209
TypeSpecifier spec
class/member specifiers
Definition entry.h:183
QCString inside
name of the class in which documents are found
Definition entry.h:214
Wrapper class for the Entry type.
Definition types.h:816
constexpr bool isCompoundDoc() const noexcept
Definition types.h:826
constexpr bool isFile() const noexcept
Definition types.h:825
constexpr bool isDoc() const noexcept
Definition types.h:827
ENTRY_TYPES constexpr bool isCompound() const noexcept
Definition types.h:823
std::string to_string() const
Definition types.h:828
constexpr bool isScope() const noexcept
Definition types.h:824
A class that generates a dynamic tree view side panel.
Definition ftvhelp.h:41
A model of a file symbol.
Definition filedef.h:99
virtual void addUsingDeclaration(const Definition *d)=0
virtual void removeMember(MemberDef *md)=0
virtual void insertClass(ClassDef *cd)=0
virtual void insertConcept(ConceptDef *cd)=0
virtual void overrideIncludeGraph(bool e)=0
virtual void writeSourceHeader(OutputList &ol)=0
virtual bool generateSourceFile() const =0
virtual const LinkedRefMap< NamespaceDef > & getUsedNamespaces() const =0
virtual QCString absFilePath() const =0
virtual bool isSource() const =0
virtual void parseSource(ClangTUParser *clangParser)=0
virtual void getAllIncludeFilesRecursively(StringVector &incFiles) const =0
virtual void setDiskName(const QCString &name)=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 const QCString & docName() const =0
virtual void insertMember(MemberDef *md)=0
virtual void insertNamespace(NamespaceDef *nd)=0
Minimal replacement for QFileInfo.
Definition fileinfo.h:23
std::string readLink() const
Definition fileinfo.cpp:84
bool isRelative() const
Definition fileinfo.cpp:58
bool isSymLink() const
Definition fileinfo.cpp:77
bool exists() const
Definition fileinfo.cpp:30
std::string fileName() const
Definition fileinfo.cpp:118
bool isReadable() const
Definition fileinfo.cpp:44
bool isDir() const
Definition fileinfo.cpp:70
bool isFile() const
Definition fileinfo.cpp:63
std::string dirPath(bool absPath=true) const
Definition fileinfo.cpp:137
std::string absFilePath() const
Definition fileinfo.cpp:101
Class representing all files with a certain base name.
Definition filename.h:30
Ordered dictionary of FileName objects.
Definition filename.h:73
void initFromRepository(const QCString &dir)
Definition formula.cpp:59
bool hasFormulas() const
Definition formula.cpp:719
void checkRepositories()
Definition formula.cpp:172
static FormulaManager & instance()
Definition formula.cpp:53
void generateImages(const QCString &outputDir, Format format, HighDPI hd=HighDPI::Off)
Definition formula.cpp:635
A model of a group of symbols.
Definition groupdef.h:52
virtual QCString 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 MemberList * getMemberList(MemberListType lt) const =0
virtual void setGroupTitle(const QCString &newtitle)=0
virtual bool hasGroupTitle() const =0
Generator for HTML output.
Definition htmlgen.h:96
static void init()
Definition htmlgen.cpp:1197
static void writeSearchPage()
Definition htmlgen.cpp:3197
static void writeFooterFile(TextStream &t)
Definition htmlgen.cpp:1540
static void writeTabData()
Additional initialization after indices have been created.
Definition htmlgen.cpp:1357
static void writeSearchData(const QCString &dir)
Definition htmlgen.cpp:1366
static void writeExternalSearchPage()
Definition htmlgen.cpp:3296
static void writeStyleSheetFile(TextStream &t)
Definition htmlgen.cpp:1528
static void writeHeaderFile(TextStream &t, const QCString &cssname)
Definition htmlgen.cpp:1534
A class that generated the HTML Help specific files.
Definition htmlhelp.h:36
static const QCString hhpFileName
Definition htmlhelp.h:89
static Index & instance()
Definition index.cpp:108
void countDataStructures()
Definition index.cpp:264
A list of index interfaces.
Definition indexlist.h:64
Generator for LaTeX output.
Definition latexgen.h:94
static void writeFooterFile(TextStream &t)
Definition latexgen.cpp:696
static void writeStyleSheetFile(TextStream &t)
Definition latexgen.cpp:702
static void writeHeaderFile(TextStream &t)
Definition latexgen.cpp:690
static void init()
Definition latexgen.cpp:632
static LayoutDocManager & instance()
Returns a reference to this singleton.
Definition layout.cpp:1437
void parse(const QCString &fileName, const char *data=nullptr)
Parses a user provided layout.
Definition layout.cpp:1470
void clear()
Definition linkedmap.h:212
std::unique_ptr< RefList > Ptr
Definition linkedmap.h:38
size_t size() const
Definition linkedmap.h:210
T * add(const char *k, Args &&... args)
Definition linkedmap.h:90
const T * find(const std::string &key) const
Definition linkedmap.h:47
Container class representing a vector of objects with keys.
Definition linkedmap.h:232
const T * find(const std::string &key) const
Definition linkedmap.h:243
bool empty() const
Definition linkedmap.h:374
Generator for Man page output.
Definition mangen.h:69
static void init()
Definition mangen.cpp:272
A model of a class/file/namespace member symbol.
Definition memberdef.h:48
virtual QCString typeString() const =0
virtual QCString requiresClause() const =0
virtual bool isFriend() const =0
virtual bool isForeign() const =0
virtual QCString definition() const =0
virtual bool isRelated() const =0
virtual const ClassDef * getCachedTypedefVal() const =0
virtual QCString excpString() const =0
virtual const ClassDef * getClassDef() const =0
virtual const ArgumentList & templateArguments() 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 bool isStatic() const =0
virtual const MemberDef * reimplements() const =0
virtual StringVector getQualifiers() const =0
virtual QCString bitfieldString() 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 QCString argsString() 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 const QCString & initializer() const =0
virtual void setMemberClass(ClassDef *cd)=0
virtual void setProtection(Protection p)=0
virtual void setMemberGroupId(int id)=0
virtual void setDocumentedEnumValues(bool value)=0
virtual void setMemberSpecifiers(TypeSpecifier s)=0
virtual void setDefinition(const QCString &d)=0
virtual ClassDefMutable * getClassDefMutable()=0
virtual void setExplicitExternal(bool b, const QCString &df, int line, int column)=0
virtual void setAccessorType(ClassDef *cd, const QCString &t)=0
virtual void setDefinitionTemplateParameterLists(const ArgumentLists &lists)=0
virtual void invalidateTypedefValCache()=0
virtual void setBitfields(const QCString &s)=0
virtual void setVhdlSpecifiers(VhdlSpecifier s)=0
virtual void setEnumScope(MemberDef *md, bool livesInsideEnum=FALSE)=0
virtual void setEnumClassScope(ClassDef *cd)=0
virtual void setMaxInitLines(int lines)=0
virtual void setInheritsDocsFrom(const MemberDef *md)=0
virtual void setRelatedAlso(ClassDef *cd)=0
virtual void setPrototype(bool p, const QCString &df, int line, int column)=0
virtual void overrideReferencesRelation(bool e)=0
virtual void makeForeign()=0
virtual void overrideReferencedByRelation(bool e)=0
virtual void setDocsForDefinition(bool b)=0
virtual void setRequiresClause(const QCString &req)=0
virtual void overrideCallGraph(bool e)=0
virtual void overrideInlineSource(bool e)=0
virtual void setArgsString(const QCString &as)=0
virtual void setInitializer(const QCString &i)=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 insertEnumField(MemberDef *md)=0
virtual void moveDeclArgumentList(std::unique_ptr< ArgumentList > al)=0
virtual void overrideCallerGraph(bool e)=0
virtual void setReimplements(MemberDef *md)=0
virtual void setDeclFile(const QCString &df, int line, int column)=0
virtual void invalidateCachedArgumentTypes()=0
virtual void moveArgumentList(std::unique_ptr< ArgumentList > al)=0
virtual void makeRelated()=0
virtual void insertReimplementedBy(MemberDef *md)=0
A list of MemberDef objects as shown in documentation sections.
Definition memberlist.h:125
Ptr & front()
Definition membername.h:51
size_t size() const
Definition membername.h:48
iterator begin()
Definition membername.h:37
iterator end()
Definition membername.h:38
void push_back(Ptr &&p)
Definition membername.h:54
Ordered dictionary of MemberName objects.
Definition membername.h:63
const MemberDef * find(const QCString &name) const
Definition memberlist.h:93
const MemberDef * findRev(const QCString &name) const
Definition memberlist.h:106
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 setMetaData(const QCString &m)=0
virtual void findSectionsInDocumentation()=0
virtual void countMembers()=0
virtual void addUsingDirective(NamespaceDef *nd)=0
virtual void insertMember(MemberDef *md)=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 setFileName(const QCString &fn)=0
virtual void sortMemberLists()=0
virtual void addMembersToMemberGroup()=0
/dev/null outline parser
bool needsPreprocessing(const QCString &) const override
Returns TRUE if the language identified by extension needs the C preprocessor to be run before feed t...
void parseInput(const QCString &, 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 QCString &) override
Callback function called by the comment block scanner.
Abstract interface for outline parsers.
Definition parserintf.h:42
virtual bool needsPreprocessing(const QCString &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 QCString &fileName, const char *fileBuf, const std::shared_ptr< Entry > &root, ClangTUParser *clangParser)=0
Parses a single input file with the goal to build an Entry tree.
Class representing a list of output generators that are written to in parallel.
Definition outputlist.h:315
A model of a page symbol.
Definition pagedef.h:26
virtual void setLocalToc(const LocalToc &tl)=0
virtual void setFileName(const QCString &name)=0
virtual void setShowLineNo(bool)=0
virtual void setPageScope(Definition *)=0
virtual const GroupDef * getGroupDef() const =0
Manages programming language parsers.
Definition parserintf.h:183
static PlantumlManager & instance()
Definition plantuml.cpp:231
void run()
Run plant UML tool for all images.
Definition plantuml.cpp:389
void processFile(const QCString &fileName, const std::string &input, std::string &output)
Definition pre.l:4196
void addSearchDir(const QCString &dir)
Definition pre.l:4178
This is an alternative implementation of QCString.
Definition qcstring.h:101
int find(char c, int index=0, bool cs=TRUE) const
Definition qcstring.cpp:43
QCString & prepend(const char *s)
Definition qcstring.h:422
int toInt(bool *ok=nullptr, int base=10) const
Definition qcstring.cpp:254
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition qcstring.h:166
bool startsWith(const char *s) const
Definition qcstring.h:507
QCString mid(size_t index, size_t len=static_cast< size_t >(-1)) const
Definition qcstring.h:241
QCString lower() const
Definition qcstring.h:249
bool endsWith(const char *s) const
Definition qcstring.h:524
char & at(size_t i)
Returns a reference to the character at index i.
Definition qcstring.h:593
bool isEmpty() const
Returns TRUE iff the string is empty.
Definition qcstring.h:163
QCString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
Definition qcstring.h:260
QCString fill(char c, int len=-1)
Fills a string with a predefined character.
Definition qcstring.h:193
const std::string & str() const
Definition qcstring.h:552
QCString & setNum(short n)
Definition qcstring.h:459
QCString right(size_t len) const
Definition qcstring.h:234
size_t size() const
Returns the length of the string, not counting the 0-terminator.
Definition qcstring.h:169
QCString & sprintf(const char *format,...)
Definition qcstring.cpp:29
@ ExplicitSize
Definition qcstring.h:146
int findRev(char c, int index=-1, bool cs=TRUE) const
Definition qcstring.cpp:96
QCString & replace(size_t index, size_t len, const char *s)
Definition qcstring.cpp:217
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition qcstring.h:172
QCString left(size_t len) const
Definition qcstring.h:229
int contains(char c, bool cs=TRUE) const
Definition qcstring.cpp:148
bool stripPrefix(const QCString &prefix)
Definition qcstring.h:213
void clear()
Definition qcstring.h:182
Definition qhp.h:27
static const QCString qhpFileName
Definition qhp.h:47
static QCString getQchFileName()
Definition qhp.cpp:426
Generator for RTF output.
Definition rtfgen.h:80
static void init()
Definition rtfgen.cpp:461
static bool preProcessFileInplace(const QCString &path, const QCString &name)
This is an API to a VERY brittle RTF preprocessor that combines nested RTF files.
Definition rtfgen.cpp:2461
static void writeStyleSheetFile(TextStream &t)
Definition rtfgen.cpp:394
static void writeExtensionsFile(TextStream &t)
Definition rtfgen.cpp:409
static RefListManager & instance()
Definition reflist.h:121
static RequirementManager & instance()
void writeTagFile(TextStream &tagFile)
void addRequirement(Entry *e)
Abstract proxy interface for non-javascript based search indices.
class that provide information about a section.
Definition section.h:58
QCString ref() const
Definition section.h:72
QCString fileName() const
Definition section.h:74
int lineNr() const
Definition section.h:73
SectionInfo * replace(const QCString &label, const QCString &fileName, int lineNr, const QCString &title, SectionType type, int level, const QCString &ref=QCString())
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:269
void print()
Definition doxygen.cpp:246
void begin(const char *name)
Definition doxygen.cpp:233
void end()
Definition doxygen.cpp:239
std::chrono::steady_clock::time_point startTime
Definition doxygen.cpp:270
const Definition * resolveSymbol(const Definition *scope, const QCString &name, const QCString &args=QCString(), bool checkCV=false, bool insideCode=false, bool onlyLinkable=false)
Find the symbool definition matching name within the scope set.
const ClassDef * resolveClass(const Definition *scope, const QCString &name, bool maybeUnlinkable=false, bool mayBeHidden=false)
Find the class definition matching name within the scope set.
QCString getTemplateSpec() const
In case a call to resolveClass() points to a template specialization, the template part is return via...
ClassDefMutable * resolveClassMutable(const Definition *scope, const QCString &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 MemberDef * getTypedef() const
In case a call to resolveClass() resolves to a type member (e.g.
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:216
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
Wrapper class for a number of boolean properties.
Definition types.h:654
std::string to_string() const
Definition types.h:698
static void correctMemberProperties(MemberDefMutable *md)
static void computeVhdlComponentRelations()
ClassDefMutable * toClassDefMutable(Definition *d)
ClassDef * getClass(const QCString &n)
std::unique_ptr< ClassDef > createClassDefAlias(const Definition *newScope, const ClassDef *cd)
Definition classdef.cpp:792
std::unique_ptr< ClassDef > createClassDef(const QCString &fileName, int startLine, int startColumn, const QCString &name, ClassDef::CompoundType ct, const QCString &ref, const QCString &fName, bool isSymbol, bool isJavaEnum)
Factory method to create a new ClassDef object.
Definition classdef.cpp:563
ClassDef * toClassDef(Definition *d)
std::unordered_set< const ClassDef * > ClassDefSet
Definition classdef.h:95
std::map< std::string, int > TemplateNameMap
Definition classdef.h:93
ClassDefMutable * getClassMutable(const QCString &key)
Definition classdef.h:471
Class representing a regular expression.
Definition regex.h:39
Class to iterate through matches.
Definition regex.h:230
Object representing the matching results.
Definition regex.h:151
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 QCString &fileName, int startLine, int startColumn, const QCString &name, const QCString &tagRef, const QCString &tagFile)
ConceptDefMutable * getConceptMutable(const QCString &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 QCString &decl, const SrcLangExt lang, QCString &clName, QCString &type, QCString &name, QCString &args, QCString &funcTempList, QCString &exceptions)
Definition declinfo.l:325
std::unique_ptr< ArgumentList > stringToArgumentList(SrcLangExt lang, const QCString &argsString, QCString *extraTypeChars=nullptr)
Definition defargs.l:822
void generateDEF()
Definition defgen.cpp:482
std::unordered_map< std::string, DefineList > DefinesPerFileList
Definition define.h:50
Definition * toDefinition(DefinitionMutable *dm)
DefinitionMutable * toDefinitionMutable(Definition *d)
DirIterator begin(DirIterator it) noexcept
Definition dir.cpp:170
DirIterator end(const DirIterator &) noexcept
Definition dir.cpp:175
void buildDirectories()
Definition dirdef.cpp:1119
void computeDirDependencies()
Definition dirdef.cpp:1193
void generateDirDocs(OutputList &ol)
Definition dirdef.cpp:1210
#define AUTO_TRACE_ADD(...)
Definition docnode.cpp:48
#define AUTO_TRACE(...)
Definition docnode.cpp:47
#define AUTO_TRACE_EXIT(...)
Definition docnode.cpp:49
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:1327
static void findInheritedTemplateInstances()
Definition doxygen.cpp:5327
void printNavTree(Entry *root, int indent)
static void addClassToContext(const Entry *root)
Definition doxygen.cpp:938
static void makeTemplateInstanceRelation(const Entry *root, ClassDefMutable *cd)
Definition doxygen.cpp:5342
static StringUnorderedSet g_pathsVisited(1009)
static int findEndOfTemplate(const QCString &s, size_t startPos)
Definition doxygen.cpp:3112
static void buildGroupList(const Entry *root)
Definition doxygen.cpp:431
static void insertMemberAlias(Definition *outerScope, const MemberDef *md)
Definition doxygen.cpp:6711
static void findUsingDeclarations(const Entry *root, bool filterPythonPackages)
Definition doxygen.cpp:2057
static void flushCachedTemplateRelations()
Definition doxygen.cpp:9461
static bool isRecursiveBaseClass(const QCString &scope, const QCString &name)
Definition doxygen.cpp:4885
static void copyLatexStyleSheet()
static void generateDocsForClassList(const std::vector< ClassDefMutable * > &classList)
Definition doxygen.cpp:9121
static std::shared_ptr< Entry > parseFile(OutlineParserInterface &parser, FileDef *fd, const QCString &fn, ClangTUParser *clangParser, bool newTU)
static int findFunctionPtr(const std::string &type, SrcLangExt lang, int *pLength=nullptr)
Definition doxygen.cpp:2908
static bool isSpecialization(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists)
Definition doxygen.cpp:6012
static MemberDef * addVariableToClass(const Entry *root, ClassDefMutable *cd, MemberType mtype, const QCString &type, const QCString &name, const QCString &args, bool fromAnnScope, MemberDef *fromAnnMemb, Protection prot, Relationship related)
Definition doxygen.cpp:2477
static void computeTemplateClassRelations()
Definition doxygen.cpp:5421
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:8175
static void runQHelpGenerator()
static void addConceptToContext(const Entry *root)
Definition doxygen.cpp:1164
static void addRelatedPage(Entry *root)
Definition doxygen.cpp:329
void initDoxygen()
static void addIncludeFile(DefMutable *cd, FileDef *ifd, const Entry *root)
Definition doxygen.cpp:588
static StringVector g_inputFiles
Definition doxygen.cpp:188
void printSectionsTree()
class Statistics g_s
static void createUsingMemberImportForClass(const Entry *root, ClassDefMutable *cd, const MemberDef *md, const QCString &fileName, const QCString &memName)
Definition doxygen.cpp:2161
static void generateXRefPages()
Definition doxygen.cpp:5600
static void findUsingDeclImports(const Entry *root)
Definition doxygen.cpp:2210
static void copyStyleSheet()
FindBaseClassRelation_Mode
Definition doxygen.cpp:286
@ Undocumented
Definition doxygen.cpp:289
@ TemplateInstances
Definition doxygen.cpp:287
@ DocumentedOnly
Definition doxygen.cpp:288
void distributeClassGroupRelations()
Definition doxygen.cpp:1495
static void generateGroupDocs()
static void findDirDocumentation(const Entry *root)
Definition doxygen.cpp:9699
void checkConfiguration()
check and resolve config options
QCString stripTemplateSpecifiers(const QCString &s)
Definition doxygen.cpp:685
static bool findClassRelation(const Entry *root, Definition *context, ClassDefMutable *cd, const BaseInfo *bi, const TemplateNameMap &templateNames, FindBaseClassRelation_Mode mode, bool isArtificial)
Definition doxygen.cpp:4927
static void resolveTemplateInstanceInType(const Entry *root, const Definition *scope, const MemberDef *md)
Definition doxygen.cpp:4855
static void organizeSubGroupsFiltered(const Entry *root, bool additional)
Definition doxygen.cpp:471
static void warnUndocumentedNamespaces()
Definition doxygen.cpp:5376
static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments, const std::string &name)
Definition doxygen.cpp:4534
static NamespaceDef * findUsedNamespace(const LinkedRefMap< NamespaceDef > &unl, const QCString &name)
Definition doxygen.cpp:1888
static void buildConceptList(const Entry *root)
Definition doxygen.cpp:1325
static void resolveClassNestingRelations()
Definition doxygen.cpp:1380
static void generateNamespaceConceptDocs(const ConceptLinkedRefMap &conceptList)
static void findMember(const Entry *root, const QCString &relates, const QCString &type, const QCString &args, QCString funcDecl, bool overloaded, bool isFunc)
Definition doxygen.cpp:6754
static void findClassEntries(const Entry *root)
Definition doxygen.cpp:5290
static void processTagLessClasses(const ClassDef *rootCd, const ClassDef *cd, ClassDefMutable *tagParentCd, const QCString &prefix, int count)
Look through the members of class cd and its public members.
Definition doxygen.cpp:1614
static void addMemberFunction(const Entry *root, MemberName *mn, const QCString &scopeName, const QCString &namespaceName, const QCString &className, const QCString &funcTyp, const QCString &funcName, const QCString &funcArgs, const QCString &funcTempList, const QCString &exceptions, const QCString &type, const QCString &args, bool isFriend, TypeSpecifier spec, const QCString &relates, const QCString &funcDecl, bool overloaded, bool isFunc)
Definition doxygen.cpp:6224
static void vhdlCorrectMemberProperties()
Definition doxygen.cpp:8419
static void generateExampleDocs()
void generateOutput()
static void stopDoxygen(int)
static void findTemplateInstanceRelation(const Entry *root, Definition *context, ClassDefMutable *templateClass, const QCString &templSpec, const TemplateNameMap &templateNames, bool isArtificial)
Definition doxygen.cpp:4804
static void substituteTemplatesInArgList(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists, const ArgumentList &src, ArgumentList &dst)
Definition doxygen.cpp:6126
static void computeMemberReferences()
Definition doxygen.cpp:5490
static void generateConfigFile(const QCString &configFile, bool shortList, bool updateOnly=FALSE)
static void transferRelatedFunctionDocumentation()
Definition doxygen.cpp:4448
static void addMembersToMemberGroup()
Definition doxygen.cpp:9326
static bool tryAddEnumDocsToGroupMember(const Entry *root, const QCString &name)
Definition doxygen.cpp:8056
static void addOverloaded(const Entry *root, MemberName *mn, const QCString &funcType, const QCString &funcName, const QCString &funcArgs, const QCString &funcDecl, const QCString &exceptions, TypeSpecifier spec)
Definition doxygen.cpp:6643
static void findMainPageTagFiles(Entry *root)
Definition doxygen.cpp:9869
static void distributeConceptGroups()
Definition doxygen.cpp:1347
static void transferFunctionDocumentation()
Definition doxygen.cpp:4367
static void setAnonymousEnumType()
Definition doxygen.cpp:9066
static void sortMemberLists()
Definition doxygen.cpp:8971
static void createTemplateInstanceMembers()
Definition doxygen.cpp:8547
void transferStaticInstanceInitializers()
Definition doxygen.cpp:4497
static void findObjCMethodDefinitions(const Entry *root)
Definition doxygen.cpp:7560
static void dumpSymbolMap()
static void buildTypedefList(const Entry *root)
Definition doxygen.cpp:3436
static void findGroupScope(const Entry *root)
Definition doxygen.cpp:446
static void generateFileDocs()
Definition doxygen.cpp:8784
static void findDefineDocumentation(Entry *root)
Definition doxygen.cpp:9612
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:4597
void parseInput()
static void findMemberDocumentation(const Entry *root)
Definition doxygen.cpp:7530
static void copyIcon(const QCString &outputOption)
static void addLocalObjCMethod(const Entry *root, const QCString &scopeName, const QCString &funcType, const QCString &funcName, const QCString &funcArgs, const QCString &exceptions, const QCString &funcDecl, TypeSpecifier spec)
Definition doxygen.cpp:6170
static bool findGlobalMember(const Entry *root, const QCString &namespaceName, const QCString &type, const QCString &name, const QCString &tempArg, const QCString &, const QCString &decl, TypeSpecifier)
Definition doxygen.cpp:5804
static void distributeMemberGroupDocumentation()
Definition doxygen.cpp:9364
static void generateNamespaceClassDocs(const ClassLinkedRefMap &classList)
static void addEnumValuesToEnums(const Entry *root)
Definition doxygen.cpp:7764
static void generatePageDocs()
Definition doxygen.cpp:9999
static void resolveUserReferences()
Definition doxygen.cpp:9931
static void buildRequirementsList(Entry *root)
Definition doxygen.cpp:9760
static void compareDoxyfile(Config::CompareMode diffList)
static void addPageToContext(PageDef *pd, Entry *root)
Definition doxygen.cpp:310
static void buildVarList(const Entry *root)
Definition doxygen.cpp:3573
static void buildSequenceList(const Entry *root)
Definition doxygen.cpp:3536
static void generateFileSources()
Definition doxygen.cpp:8618
static QCString substituteTemplatesInString(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists, const std::string &src)
Definition doxygen.cpp:6042
static void copyLogo(const QCString &outputOption)
static void usage(const QCString &name, const QCString &versionString)
static MemberDef * addVariableToFile(const Entry *root, MemberType mtype, const QCString &scope, const QCString &type, const QCString &name, const QCString &args, bool fromAnnScope, MemberDef *fromAnnMemb)
Definition doxygen.cpp:2652
static void generateClassDocs()
Definition doxygen.cpp:9219
static void buildNamespaceList(const Entry *root)
Definition doxygen.cpp:1719
static int computeIdealCacheParam(size_t v)
static void readTagFile(const std::shared_ptr< Entry > &root, const QCString &tagLine)
static void findIncludedUsingDirectives()
Definition doxygen.cpp:2460
static void addDefineDoc(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:9585
static void addMemberDocs(const Entry *root, MemberDefMutable *md, const QCString &funcDecl, const ArgumentList *al, bool over_load, TypeSpecifier spec)
Definition doxygen.cpp:5614
static void countMembers()
Definition doxygen.cpp:9080
void clearAll()
Definition doxygen.cpp:202
static void devUsage()
static void addInterfaceOrServiceToServiceOrSingleton(const Entry *root, ClassDefMutable *cd, QCString const &rname)
Definition doxygen.cpp:3605
static void organizeSubGroups(const Entry *root)
Definition doxygen.cpp:490
static void applyMemberOverrideOptions(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:2149
void searchInputFiles()
static void findFriends()
Definition doxygen.cpp:4270
static void findEnums(const Entry *root)
Definition doxygen.cpp:7588
static void dumpSymbol(TextStream &t, Definition *d)
static void addClassAndNestedClasses(std::vector< ClassDefMutable * > &list, ClassDefMutable *cd)
Definition doxygen.cpp:9196
static void addEnumDocs(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:8014
static void addListReferences()
Definition doxygen.cpp:5591
static void exitDoxygen() noexcept
static void copyExtraFiles(const StringVector &files, const QCString &filesOption, const QCString &outputOption)
static bool isClassSection(const Entry *root)
Definition doxygen.cpp:5268
static Definition * findScopeFromQualifiedName(NamespaceDefMutable *startScope, const QCString &n, FileDef *fileScope, const TagInfo *tagInfo)
Definition doxygen.cpp:781
static ClassDef * findClassWithinClassContext(Definition *context, ClassDef *cd, const QCString &name)
Definition doxygen.cpp:4562
static void buildGroupListFiltered(const Entry *root, bool additional, bool includeExternal)
Definition doxygen.cpp:360
static void runHtmlHelpCompiler()
static QCString extractClassName(const Entry *root)
Definition doxygen.cpp:5299
static void addMembersToIndex()
Definition doxygen.cpp:8209
static bool g_dumpSymbolMap
Definition doxygen.cpp:192
static void version(const bool extended)
static void addGlobalFunction(const Entry *root, const QCString &rname, const QCString &sc)
Definition doxygen.cpp:3856
static OutputList * g_outputList
Definition doxygen.cpp:189
static void findMainPage(Entry *root)
Definition doxygen.cpp:9799
static ClassDef::CompoundType convertToCompoundType(EntryType section, TypeSpecifier specifier)
Definition doxygen.cpp:896
static void findUsingDirectives(const Entry *root)
Definition doxygen.cpp:1901
std::unique_ptr< ArgumentList > getTemplateArgumentsFromName(const QCString &name, const ArgumentLists &tArgLists)
Definition doxygen.cpp:866
static bool g_successfulRun
Definition doxygen.cpp:191
static void addSourceReferences()
Definition doxygen.cpp:8844
std::function< std::unique_ptr< T >() > make_parser_factory()
static void buildExampleList(Entry *root)
static void inheritDocumentation()
Definition doxygen.cpp:9266
static void flushUnresolvedRelations()
Definition doxygen.cpp:9515
static bool isSymbolHidden(const Definition *d)
Definition doxygen.cpp:9013
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 QCString g_commentFileName
Definition doxygen.cpp:193
static void findDocumentedEnumValues()
Definition doxygen.cpp:8201
static void findTagLessClasses()
Definition doxygen.cpp:1695
static void generateDiskNames()
static void addToIndices()
Definition doxygen.cpp:8251
static void computeClassRelations()
Definition doxygen.cpp:5396
static void buildFunctionList(const Entry *root)
Definition doxygen.cpp:3965
static void checkPageRelations()
Definition doxygen.cpp:9911
static void findModuleDocumentation(const Entry *root)
Definition doxygen.cpp:1315
static void findEnumDocumentation(const Entry *root)
Definition doxygen.cpp:8089
static void computePageRelations(Entry *root)
Definition doxygen.cpp:9881
static void filterMemberDocumentation(const Entry *root, const QCString &relates)
Definition doxygen.cpp:7380
static QCString createOutputDirectory(const QCString &baseDirName, const QCString &formatDirName, const char *defaultDirName)
void initResources()
static bool isVarWithConstructor(const Entry *root)
Definition doxygen.cpp:2968
static StringSet g_usingDeclarations
Definition doxygen.cpp:190
static void buildDictionaryList(const Entry *root)
Definition doxygen.cpp:3554
static bool haveEqualFileNames(const Entry *root, const MemberDef *md)
Definition doxygen.cpp:9574
static void generateNamespaceDocs()
static void buildClassDocList(const Entry *root)
Definition doxygen.cpp:1150
static void buildPageList(Entry *root)
Definition doxygen.cpp:9772
static void writeTagFile()
static Definition * buildScopeFromQualifiedName(const QCString &name_, SrcLangExt lang, const TagInfo *tagInfo)
Definition doxygen.cpp:712
static void addRequirementReferences()
Definition doxygen.cpp:5583
static void computeVerifiedDotPath()
static bool g_singleComment
Definition doxygen.cpp:194
static void findSectionsInDocumentation()
Definition doxygen.cpp:9402
static void mergeCategories()
Definition doxygen.cpp:8566
static const StringUnorderedSet g_compoundKeywords
Definition doxygen.cpp:199
static bool scopeIsTemplate(const Definition *d)
Definition doxygen.cpp:6028
static void buildFileList(const Entry *root)
Definition doxygen.cpp:502
static ClassDefMutable * createTagLessInstance(const ClassDef *rootCd, const ClassDef *templ, const QCString &fieldName)
Definition doxygen.cpp:1528
static void buildClassList(const Entry *root)
Definition doxygen.cpp:1140
static void addMethodToClass(const Entry *root, ClassDefMutable *cd, const QCString &rtype, const QCString &rname, const QCString &rargs, bool isFriend, Protection protection, bool stat, Specifier virt, TypeSpecifier spec, const QCString &relates)
Definition doxygen.cpp:3720
static std::unique_ptr< OutlineParserInterface > getParserForFile(const QCString &fn)
static void findUsedTemplateInstances()
Definition doxygen.cpp:5360
static void computeTooltipTexts()
Definition doxygen.cpp:9020
void readFileOrDirectory(const QCString &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 addVariable(const Entry *root, int isFuncPtr=-1)
Definition doxygen.cpp:3180
static void addMemberSpecialization(const Entry *root, MemberName *mn, ClassDefMutable *cd, const QCString &funcType, const QCString &funcName, const QCString &funcArgs, const QCString &funcDecl, const QCString &exceptions, TypeSpecifier spec)
Definition doxygen.cpp:6580
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:4753
static void parseFilesSingleThreading(const std::shared_ptr< Entry > &root)
parse the list of input files
static void buildCompleteMemberLists()
Definition doxygen.cpp:8588
static void generateConceptDocs()
Definition doxygen.cpp:9245
static void combineUsingRelations()
Definition doxygen.cpp:9301
static const char * getArg(int argc, char **argv, int &optInd)
static const ClassDef * findClassDefinition(FileDef *fd, NamespaceDef *nd, const QCString &scopeName)
Definition doxygen.cpp:5765
static void checkMarkdownMainfile()
static std::unordered_map< std::string, std::vector< ClassDefMutable * > > g_usingClassMap
Definition doxygen.cpp:2208
static void buildConceptDocList(const Entry *root)
Definition doxygen.cpp:1335
static int findTemplateSpecializationPosition(const QCString &name)
Definition doxygen.cpp:4897
static bool isEntryInGroupOfMember(const Entry *root, const MemberDef *md, bool allowNoGroup=false)
Definition doxygen.cpp:5780
static void applyToAllDefinitions(Func func)
Definition doxygen.cpp:5526
static void parseFilesMultiThreading(const std::shared_ptr< Entry > &root)
parse the list of input files
static void transferFunctionReferences()
Definition doxygen.cpp:4400
static void buildDefineList()
Definition doxygen.cpp:8923
static void buildInterfaceAndServiceList(const Entry *root)
Definition doxygen.cpp:3668
static void computeMemberRelations()
Definition doxygen.cpp:8531
static void buildListOfUsingDecls(const Entry *root)
Definition doxygen.cpp:2044
static void computeMemberRelationsForBaseClass(const ClassDef *cd, const BaseClassDef *bcd)
Definition doxygen.cpp:8451
static std::multimap< std::string, const Entry * > g_classEntries
Definition doxygen.cpp:187
std::vector< InputFileEncoding > InputFileEncodingList
Definition doxygen.h:80
std::unordered_map< std::string, BodyInfo > StaticInitMap
Definition doxygen.h:84
std::unordered_map< std::string, NamespaceAliasInfo > NamespaceAliasInfoMap
Definition doxygen.h:86
std::unordered_map< std::string, const Definition * > ClangUsrMap
Definition doxygen.h:82
std::unique_ptr< FileDef > createFileDef(const QCString &p, const QCString &n, const QCString &ref, const QCString &dn)
Definition filedef.cpp:268
FileDef * toFileDef(Definition *d)
Definition filedef.cpp:1966
std::unordered_set< const FileDef * > FileDefSet
Definition filedef.h:44
static void startScope(yyscan_t yyscanner)
start scope
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 QCString &fileName, int line, const QCString &name, const QCString &title, const QCString &refFileName)
Definition groupdef.cpp:178
void addConceptToGroups(const Entry *root, ConceptDef *cd)
void addMemberToGroups(const Entry *root, MemberDef *md)
void startTitle(OutputList &ol, const QCString &fileName, const DefinitionMutable *def)
Definition index.cpp:386
void endFile(OutputList &ol, bool skipNavIndex, bool skipEndContents, const QCString &navPath)
Definition index.cpp:429
void writeGraphInfo(OutputList &ol)
Definition index.cpp:4111
void endTitle(OutputList &ol, const QCString &fileName, const QCString &name)
Definition index.cpp:396
void startFile(OutputList &ol, const QCString &name, bool isSource, const QCString &manName, const QCString &title, HighlightedItem hli, bool additionalIndices, const QCString &altSidebarName, int hierarchyLevel, const QCString &allMembersFile)
Definition index.cpp:403
void writeIndexHierarchy(OutputList &ol)
Definition index.cpp:5825
Translator * theTranslator
Definition language.cpp:71
void setTranslator(OUTPUT_LANGUAGE_t langName)
Definition language.cpp:73
#define LATEX_STYLE_EXTENSION
Definition latexgen.h:22
void writeDefaultLayoutFile(const QCString &fileName)
Definition layout.cpp:1734
void printLayout()
Definition layout.cpp:1822
std::unique_ptr< MemberDef > createMemberDefAlias(const Definition *newScope, const MemberDef *aliasMd)
MemberDefMutable * toMemberDefMutable(Definition *d)
void combineDeclarationAndDefinition(MemberDefMutable *mdec, MemberDefMutable *mdef)
MemberDef * toMemberDef(Definition *d)
std::unique_ptr< MemberDef > createMemberDef(const QCString &defFileName, int defLine, int defColumn, const QCString &type, const QCString &name, const QCString &args, const QCString &excp, Protection prot, Specifier virt, bool stat, Relationship related, MemberType t, const ArgumentList &tal, const ArgumentList &al, const QCString &metaData)
Factory method to create a new instance of a MemberDef.
std::unordered_map< int, std::unique_ptr< MemberGroupInfo > > MemberGroupInfoMap
#define DOX_NOGROUP
Definition membergroup.h:27
QCString warn_line(const QCString &file, int line)
Definition message.cpp:214
void initWarningFormat()
Definition message.cpp:236
void warn_flush()
Definition message.cpp:229
void finishWarnExit()
Definition message.cpp:294
#define warn_undoc(file, line, fmt,...)
Definition message.h:102
#define warn_uncond(fmt,...)
Definition message.h:122
#define warn(file, line, fmt,...)
Definition message.h:97
#define msg(fmt,...)
Definition message.h:94
#define err(fmt,...)
Definition message.h:127
#define term(fmt,...)
Definition message.h:137
void postProcess(bool clearHeaderAndFooter, CompareMode compareMode=CompareMode::Full)
CompareMode
Definition config.h:54
void checkAndCorrect(bool quiet, const bool check)
void compareDoxyfile(TextStream &t, CompareMode compareMode)
void deinit()
bool parse(const QCString &fileName, bool update=FALSE, CompareMode compareMode=CompareMode::Full)
void init()
void writeTemplate(TextStream &t, bool shortList, bool updateOnly=FALSE)
void updateObsolete()
void correctPath(const StringVector &list)
Correct a possible wrong PATH variable.
Definition portable.cpp:516
bool isAbsolutePath(const QCString &fileName)
Definition portable.cpp:497
FILE * popen(const QCString &name, const QCString &type)
Definition portable.cpp:479
std::ofstream openOutputStream(const QCString &name, bool append=false)
Definition portable.cpp:648
double getSysElapsedTime()
Definition portable.cpp:97
QCString pathListSeparator()
Definition portable.cpp:383
uint32_t pid()
Definition portable.cpp:248
int pclose(FILE *stream)
Definition portable.cpp:488
int system(const QCString &command, const QCString &args, bool commandHasConsole=true)
Definition portable.cpp:105
void setenv(const QCString &variable, const QCString &value)
Definition portable.cpp:286
const char * commandExtension()
Definition portable.cpp:461
QCString getenv(const QCString &variable)
Definition portable.cpp:321
void setShortDir()
Definition portable.cpp:553
QCString trunc(const QCString &s, size_t numChars=15)
Definition trace.h:56
void replaceNamespaceAliases(QCString &name)
std::unique_ptr< NamespaceDef > createNamespaceDefAlias(const Definition *newScope, const NamespaceDef *nd)
Factory method to create an alias of an existing namespace.
std::unique_ptr< NamespaceDef > createNamespaceDef(const QCString &defFileName, int defLine, int defColumn, const QCString &name, const QCString &ref, const QCString &refFile, const QCString &type, bool isPublished)
Factory method to create new NamespaceDef instance.
NamespaceDef * getResolvedNamespace(const QCString &name)
NamespaceDef * toNamespaceDef(Definition *d)
NamespaceDefMutable * toNamespaceDefMutable(Definition *d)
NamespaceDefMutable * getResolvedNamespaceMutable(const QCString &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:844
std::unique_ptr< PageDef > createPageDef(const QCString &f, int l, const QCString &n, const QCString &d, const QCString &t)
Definition pagedef.cpp:82
void generatePerlMod()
void setPerlModDoxyfile(const QCString &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)
int qstricmp(const char *s1, const char *s2)
Definition qcstring.cpp:530
QCString substitute(const QCString &s, const QCString &src, const QCString &dst)
substitute all occurrences of src in s by dst
Definition qcstring.cpp:571
int qstricmp_sort(const char *str1, const char *str2)
Definition qcstring.h:86
const char * qPrint(const char *s)
Definition qcstring.h:687
#define TRUE
Definition qcstring.h:37
#define FALSE
Definition qcstring.h:34
uint32_t qstrlen(const char *str)
Returns the length of string str, or 0 if a null pointer is passed.
Definition qcstring.h:58
#define ASSERT(x)
Definition qcstring.h:39
int qstrcmp(const char *str1, const char *str2)
Definition qcstring.h:69
void initSearchIndexer()
void finalizeSearchIndexer()
static void addMemberToSearchIndex(const MemberDef *md)
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:84
This class contains the information about the argument of a function or template.
Definition arguments.h:27
QCString type
Definition arguments.h:42
QCString name
Definition arguments.h:44
QCString defval
Definition arguments.h:46
QCString array
Definition arguments.h:45
Class that contains information about an inheritance relation.
Definition classdef.h:55
ClassDef * classDef
Class definition that this relation inherits from.
Definition classdef.h:60
This class stores information about an inheritance relation.
Definition entry.h:91
Protection prot
inheritance type
Definition entry.h:96
Specifier virt
virtualness
Definition entry.h:97
QCString name
the name of the base class
Definition entry.h:95
Data associated with description found in the body.
Definition definition.h:64
Grouping info.
Definition types.h:227
QCString 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 QCString &htmldir)
Definition htags.cpp:38
static bool loadFilemap(const QCString &htmldir)
Definition htags.cpp:107
static bool useHtags
Definition htags.h:23
const Definition * definition
Definition doxygen.h:59
const MemberDef * typeDef
Definition doxygen.h:60
stat(const char *n, double el)
Definition doxygen.cpp:267
const char * name
Definition doxygen.cpp:264
This struct is used to capture the tag file information for an Entry.
Definition entry.h:104
QCString fileName
Definition entry.h:106
QCString tagName
Definition entry.h:105
void parseTagFile(const std::shared_ptr< Entry > &root, const char *fullName)
void exitTracing()
Definition trace.cpp:52
void initTracing(const QCString &logFile, bool timing)
Definition trace.cpp:22
#define TRACE(...)
Definition trace.h:77
MemberType
Definition types.h:552
@ Enumeration
Definition types.h:557
@ EnumValue
Definition types.h:558
@ Dictionary
Definition types.h:568
@ Interface
Definition types.h:565
@ Sequence
Definition types.h:567
@ Variable
Definition types.h:555
@ Property
Definition types.h:563
@ Typedef
Definition types.h:556
@ Function
Definition types.h:554
@ Service
Definition types.h:566
Protection
Definition types.h:32
SrcLangExt
Definition types.h:207
Relationship
Definition types.h:167
Specifier
Definition types.h:80
bool matchArguments2(const Definition *srcScope, const FileDef *srcFileScope, const QCString &srcReturnType, const ArgumentList *srcAl, const Definition *dstScope, const FileDef *dstFileScope, const QCString &dstReturnType, const ArgumentList *dstAl, bool checkCV, SrcLangExt lang)
Definition util.cpp:2000
QCString removeRedundantWhiteSpace(const QCString &s)
Definition util.cpp:567
QCString mergeScopes(const QCString &leftScope, const QCString &rightScope)
Definition util.cpp:4577
QCString normalizeNonTemplateArgumentsInString(const QCString &name, const Definition *context, const ArgumentList &formalArgs)
Definition util.cpp:4283
SrcLangExt getLanguageFromFileName(const QCString &fileName, SrcLangExt defLang)
Definition util.cpp:5193
bool protectionLevelVisible(Protection prot)
Definition util.cpp:5939
bool matchTemplateArguments(const ArgumentList &srcAl, const ArgumentList &dstAl)
Definition util.cpp:2244
void addCodeOnlyMappings()
Definition util.cpp:5187
QCString substituteTemplateArgumentsInString(const QCString &nm, const ArgumentList &formalArgs, const ArgumentList *actualArgs)
Definition util.cpp:4348
int extractClassNameFromType(const QCString &type, int &pos, QCString &name, QCString &templSpec, SrcLangExt lang)
Definition util.cpp:4198
bool leftScopeMatch(const QCString &scope, const QCString &name)
Definition util.cpp:881
QCString stripAnonymousNamespaceScope(const QCString &s)
Definition util.cpp:230
QCString stripFromIncludePath(const QCString &path)
Definition util.cpp:329
bool checkIfTypedef(const Definition *scope, const FileDef *fileScope, const QCString &n)
Definition util.cpp:5297
bool readInputFile(const QCString &fileName, std::string &contents, bool filter, bool isSourceCode)
read a file name fileName and optionally filter and transcode it
Definition util.cpp:5532
bool patternMatch(const FileInfo &fi, const StringVector &patList)
Definition util.cpp:5686
bool openOutputFile(const QCString &outFile, std::ofstream &f)
Definition util.cpp:6298
QCString tempArgListToString(const ArgumentList &al, SrcLangExt lang, bool includeDefault)
Definition util.cpp:1278
void addRefItem(const RefItemVector &sli, const QCString &key, const QCString &prefix, const QCString &name, const QCString &title, const QCString &args, const Definition *scope)
Definition util.cpp:4807
QCString showFileDefMatches(const FileNameLinkedMap *fnMap, const QCString &n)
Definition util.cpp:3001
QCString fileToString(const QCString &name, bool filter, bool isSourceCode)
Definition util.cpp:1473
QCString filterTitle(const QCString &title)
Definition util.cpp:5612
QCString removeAnonymousScopes(const QCString &str)
Definition util.cpp:161
QCString resolveTypeDef(const Definition *context, const QCString &qualifiedName, const Definition **typedefContext)
Definition util.cpp:373
bool checkExtension(const QCString &fName, const QCString &ext)
Definition util.cpp:4899
int computeQualifiedIndex(const QCString &name)
Return the index of the last :: in the string name that is still before the first <.
Definition util.cpp:6826
void initDefaultExtensionMapping()
Definition util.cpp:5120
bool findAndRemoveWord(QCString &sentence, const char *word)
removes occurrences of whole word from sentence, while keeps internal spaces and reducing multiple se...
Definition util.cpp:4969
QCString convertNameToFile(const QCString &name, bool allowDots, bool allowUnderscore)
Definition util.cpp:3487
QCString langToString(SrcLangExt lang)
Returns a string representation of lang.
Definition util.cpp:5893
EntryType guessSection(const QCString &name)
Definition util.cpp:338
void extractNamespaceName(const QCString &scopeName, QCString &className, QCString &namespaceName, bool allowEmptyClass)
Definition util.cpp:3680
QCString argListToString(const ArgumentList &al, bool useCanonicalType, bool showDefVals)
Definition util.cpp:1233
QCString getLanguageSpecificSeparator(SrcLangExt lang, bool classScope)
Returns the scope separator to use given the programming language lang.
Definition util.cpp:5899
void mergeMemberOverrideOptions(MemberDefMutable *md1, MemberDefMutable *md2)
Definition util.cpp:6863
QCString mangleCSharpGenericName(const QCString &name)
Definition util.cpp:6915
QCString projectLogoFile()
Definition util.cpp:3123
void mergeArguments(ArgumentList &srcAl, ArgumentList &dstAl, bool forceNameOverwrite)
Definition util.cpp:2100
bool copyFile(const QCString &src, const QCString &dest)
Copies the contents of file with name src to the newly created file with name dest.
Definition util.cpp:5859
QCString stripTemplateSpecifiersFromScope(const QCString &fullName, bool parentOnly, QCString *pLastScopeStripped, QCString scopeName, bool allowArtificial)
Definition util.cpp:4510
QCString getOverloadDocs()
Definition util.cpp:4072
void cleanupInlineGraph()
Definition util.cpp:6992
int getPrefixIndex(const QCString &name)
Definition util.cpp:3214
bool rightScopeMatch(const QCString &scope, const QCString &name)
Definition util.cpp:870
bool updateLanguageMapping(const QCString &extension, const QCString &language)
Definition util.cpp:5088
QCString getFileNameExtension(const QCString &fn)
Definition util.cpp:5235
QCString replaceAnonymousScopes(const QCString &s, const QCString &replacement)
Definition util.cpp:218
FileDef * findFileDef(const FileNameLinkedMap *fnMap, const QCString &n, bool &ambig)
Definition util.cpp:2873
int getScopeFragment(const QCString &s, int p, int *l)
Definition util.cpp:4622
void addHtmlExtensionIfMissing(QCString &fName)
Definition util.cpp:4904
A bunch of utility functions.
void generateXML()
Definition xmlgen.cpp:2325