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 "mermaid.h"
77#include "message.h"
78#include "moduledef.h"
79#include "msc.h"
80#include "namespacedef.h"
81#include "outputlist.h"
82#include "pagedef.h"
83#include "parserintf.h"
84#include "perlmodgen.h"
85#include "plantuml.h"
86#include "portable.h"
87#include "pre.h"
88#include "pycode.h"
89#include "pyscanner.h"
90#include "qhp.h"
91#include "reflist.h"
92#include "regex.h"
93#include "requirement.h"
94#include "rtfgen.h"
95#include "scanner.h"
96#include "searchindex_js.h"
97#include "settings.h"
98#include "singlecomment.h"
99#include "sitemap.h"
100#include "sqlcode.h"
101#include "sqlite3gen.h"
102#include "stlsupport.h"
103#include "stringutil.h"
104#include "symbolresolver.h"
105#include "tagreader.h"
106#include "threadpool.h"
107#include "trace.h"
108#include "util.h"
109#include "version.h"
110#include "vhdlcode.h"
111#include "vhdldocgen.h"
112#include "vhdljjparser.h"
113#include "xmlcode.h"
114#include "xmlgen.h"
115
116#include <sqlite3.h>
117
118#if USE_LIBCLANG
119#if defined(__GNUC__)
120#pragma GCC diagnostic push
121#pragma GCC diagnostic ignored "-Wshadow"
122#endif
123#include <clang/Basic/Version.h>
124#if defined(__GNUC__)
125#pragma GCC diagnostic pop
126#endif
127#endif
128
129// provided by the generated file resources.cpp
130extern void initResources();
131
132#if !defined(_WIN32) || defined(__CYGWIN__)
133#include <signal.h>
134#define HAS_SIGNALS
135#endif
136
137// globally accessible variables
149FileNameLinkedMap *Doxygen::includeNameLinkedMap = nullptr; // include names
155FileNameLinkedMap *Doxygen::plantUmlFileNameLinkedMap = nullptr;// plantuml files
156FileNameLinkedMap *Doxygen::mermaidFileNameLinkedMap = nullptr; // mermaid files
158StringMap Doxygen::tagDestinationMap; // all tag locations
159StringUnorderedSet Doxygen::tagFileSet; // all tag file names
160StringUnorderedSet Doxygen::expandAsDefinedSet; // all macros that should be expanded
161MemberGroupInfoMap Doxygen::memberGroupInfoMap; // dictionary of the member groups heading
162std::unique_ptr<PageDef> Doxygen::mainPage;
163std::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();
226 Doxygen::mainPage.reset();
228}
229
231{
232 public:
234 void begin(const char *name)
235 {
236 msg("{}", name);
237 stats.emplace_back(name,0);
238 startTime = std::chrono::steady_clock::now();
239 }
240 void end()
241 {
242 std::chrono::steady_clock::time_point endTime = std::chrono::steady_clock::now();
243 stats.back().elapsed = static_cast<double>(std::chrono::duration_cast<
244 std::chrono::microseconds>(endTime - startTime).count())/1000000.0;
245 warn_flush();
246 }
247 void print()
248 {
249 bool restore=FALSE;
251 {
253 restore=TRUE;
254 }
255 msg("----------------------\n");
256 for (const auto &s : stats)
257 {
258 msg("Spent {:.6f} seconds in {}",s.elapsed,s.name);
259 }
260 if (restore) Debug::setFlag(Debug::Time);
261 }
262 private:
263 struct stat
264 {
265 const char *name;
266 double elapsed;
267 //stat() : name(nullptr),elapsed(0) {}
268 stat(const char *n, double el) : name(n),elapsed(el) {}
269 };
270 std::vector<stat> stats;
271 std::chrono::steady_clock::time_point startTime;
273
274
275static void addMemberDocs(const Entry *root,MemberDefMutable *md, const QCString &funcDecl,
276 const ArgumentList *al,bool over_load,TypeSpecifier spec);
277static void findMember(const Entry *root,
278 const QCString &relates,
279 const QCString &type,
280 const QCString &args,
281 QCString funcDecl,
282 bool overloaded,
283 bool isFunc
284 );
285
292
293
294static bool findClassRelation(
295 const Entry *root,
296 Definition *context,
297 ClassDefMutable *cd,
298 const BaseInfo *bi,
299 const TemplateNameMap &templateNames,
300 /*bool insertUndocumented*/
302 bool isArtificial
303 );
304
305//----------------------------------------------------------------------------
306
308 FileDef *fileScope,const TagInfo *tagInfo);
309static void resolveTemplateInstanceInType(const Entry *root,const Definition *scope,const MemberDef *md);
310
311static void addPageToContext(PageDef *pd,Entry *root)
312{
313 if (root->parent()) // add the page to it's scope
314 {
315 QCString scope = root->parent()->name;
316 if (root->parent()->section.isPackageDoc())
317 {
318 scope=substitute(scope,".","::");
319 }
320 scope = stripAnonymousNamespaceScope(scope);
321 scope+="::"+pd->name();
323 if (d)
324 {
325 pd->setPageScope(d);
326 }
327 }
328}
329
330static void addRelatedPage(Entry *root)
331{
332 GroupDef *gd=nullptr;
333 for (const Grouping &g : root->groups)
334 {
335 if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname))) break;
336 }
337 //printf("---> addRelatedPage() %s gd=%p\n",qPrint(root->name),gd);
338 QCString doc=root->doc+root->inbodyDocs;
339
340 PageDef *pd = addRelatedPage(root->name, // name
341 root->args, // ptitle
342 doc, // doc
343 root->docFile, // fileName
344 root->docLine, // docLine
345 root->startLine, // startLine
346 root->sli, // sli
347 gd, // gd
348 root->tagInfo(), // tagInfo
349 FALSE, // xref
350 root->lang // lang
351 );
352 if (pd)
353 {
354 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
356 pd->setLocalToc(root->localToc);
357 addPageToContext(pd,root);
358 }
359}
360
361static void buildGroupListFiltered(const Entry *root,bool additional, bool includeExternal)
362{
363 if (root->section.isGroupDoc() && !root->name.isEmpty() &&
364 ((!includeExternal && root->tagInfo()==nullptr) ||
365 ( includeExternal && root->tagInfo()!=nullptr))
366 )
367 {
368 AUTO_TRACE("additional={} includeExternal={}",additional,includeExternal);
369 if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
370 (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
371 {
372 GroupDef *gd = Doxygen::groupLinkedMap->find(root->name);
373 AUTO_TRACE_ADD("Processing group '{}':'{}' gd={}", root->type,root->name,(void*)gd);
374
375 if (gd)
376 {
377 if ( !gd->hasGroupTitle() )
378 {
379 gd->setGroupTitle( root->type );
380 }
381 else if ( root->type.length() > 0 && root->name != root->type && gd->groupTitle() != root->type )
382 {
383 warn( root->fileName,root->startLine,
384 "group {}: ignoring title \"{}\" that does not match old title \"{}\"",
385 root->name, root->type, gd->groupTitle() );
386 }
387 gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
388 gd->setDocumentation( root->doc, root->docFile, root->docLine );
389 gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
391 gd->setRefItems(root->sli);
393 gd->setLanguage(root->lang);
395 {
396 root->commandOverrides.apply_groupGraph([&](bool b) { gd->overrideGroupGraph(b); });
397 }
398 }
399 else
400 {
401 if (root->tagInfo())
402 {
403 gd = Doxygen::groupLinkedMap->add(root->name,
404 std::unique_ptr<GroupDef>(
405 createGroupDef(root->fileName,root->startLine,root->name,root->type,root->tagInfo()->fileName)));
406 gd->setReference(root->tagInfo()->tagName);
407 }
408 else
409 {
410 gd = Doxygen::groupLinkedMap->add(root->name,
411 std::unique_ptr<GroupDef>(
412 createGroupDef(root->fileName,root->startLine,root->name,root->type)));
413 }
414 gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
415 // allow empty docs for group
416 gd->setDocumentation(!root->doc.isEmpty() ? root->doc : QCString(" "),root->docFile,root->docLine,FALSE);
417 gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
419 gd->setRefItems(root->sli);
421 gd->setLanguage(root->lang);
423 {
424 root->commandOverrides.apply_groupGraph([&](bool b) { gd->overrideGroupGraph(b); });
425 }
426 }
427 }
428 }
429 for (const auto &e : root->children()) buildGroupListFiltered(e.get(),additional,includeExternal);
430}
431
432static void buildGroupList(const Entry *root)
433{
434 // --- first process only local groups
435 // first process the @defgroups blocks
437 // then process the @addtogroup, @weakgroup blocks
439
440 // --- then also process external groups
441 // first process the @defgroups blocks
443 // then process the @addtogroup, @weakgroup blocks
445}
446
447static void findGroupScope(const Entry *root)
448{
449 if (root->section.isGroupDoc() && !root->name.isEmpty() &&
450 root->parent() && !root->parent()->name.isEmpty())
451 {
452 GroupDef *gd = Doxygen::groupLinkedMap->find(root->name);
453 if (gd)
454 {
455 QCString scope = root->parent()->name;
456 if (root->parent()->section.isPackageDoc())
457 {
458 scope=substitute(scope,".","::");
459 }
460 scope = stripAnonymousNamespaceScope(scope);
461 scope+="::"+gd->name();
463 if (d)
464 {
465 gd->setGroupScope(d);
466 }
467 }
468 }
469 for (const auto &e : root->children()) findGroupScope(e.get());
470}
471
472static void organizeSubGroupsFiltered(const Entry *root,bool additional)
473{
474 if (root->section.isGroupDoc() && !root->name.isEmpty())
475 {
476 AUTO_TRACE("additional={}",additional);
477 if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
478 (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
479 {
480 GroupDef *gd = Doxygen::groupLinkedMap->find(root->name);
481 if (gd)
482 {
483 AUTO_TRACE_ADD("adding {} to group {}",root->name,gd->name());
484 addGroupToGroups(root,gd);
485 }
486 }
487 }
488 for (const auto &e : root->children()) organizeSubGroupsFiltered(e.get(),additional);
489}
490
491static void organizeSubGroups(const Entry *root)
492{
493 //printf("Defining groups\n");
494 // first process the @defgroups blocks
496 //printf("Additional groups\n");
497 // then process the @addtogroup, @weakgroup blocks
499}
500
501//----------------------------------------------------------------------
502
503static void buildFileList(const Entry *root)
504{
505 if ((root->section.isFileDoc() || (root->section.isFile() && Config_getBool(EXTRACT_ALL))) &&
506 !root->name.isEmpty() && !root->tagInfo() // skip any file coming from tag files
507 )
508 {
509 bool ambig = false;
511 if (!fd || ambig)
512 {
513 bool save_ambig = ambig;
514 // use the directory of the file to see if the described file is in the same
515 // directory as the describing file.
516 QCString fn = root->fileName;
517 int newIndex=fn.findRev('/');
518 if (newIndex<0)
519 {
520 fn = root->name;
521 }
522 else
523 {
524 fn = fn.left(newIndex)+"/"+root->name;
525 }
527 if (!fd) ambig = save_ambig;
528 }
529 //printf("**************** root->name=%s fd=%p\n",qPrint(root->name),(void*)fd);
530 if (fd && !ambig)
531 {
532 //printf("Adding documentation!\n");
533 // using FALSE in setDocumentation is small hack to make sure a file
534 // is documented even if a \file command is used without further
535 // documentation
536 fd->setDocumentation(root->doc,root->docFile,root->docLine,FALSE);
537 fd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
539 fd->setRefItems(root->sli);
541 root->commandOverrides.apply_includeGraph ([&](bool b) { fd->overrideIncludeGraph(b); });
542 root->commandOverrides.apply_includedByGraph([&](bool b) { fd->overrideIncludedByGraph(b); });
543 for (const Grouping &g : root->groups)
544 {
545 GroupDef *gd=nullptr;
546 if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname)))
547 {
548 if (!gd->containsFile(fd))
549 {
550 gd->addFile(fd);
551 fd->makePartOfGroup(gd);
552 //printf("File %s: in group %s\n",qPrint(fd->name()),qPrint(gd->name()));
553 }
554 }
555 else if (!gd && g.pri == Grouping::GROUPING_INGROUP)
556 {
557 warn(root->fileName, root->startLine,
558 "Found non-existing group '{}' for the command '{}', ignoring command",
560 );
561 }
562 }
563 }
564 else
565 {
567 text.sprintf("the name '%s' supplied as "
568 "the argument in the \\file statement ",
569 qPrint(root->name));
570 if (ambig) // name is ambiguous
571 {
572 text+="matches the following input files:\n";
574 text+="\n";
575 text+="Please use a more specific name by "
576 "including a (larger) part of the path!";
577 }
578 else // name is not an input file
579 {
580 text+="is not an input file";
581 }
582 warn(root->fileName,root->startLine,"{}", text);
583 }
584 }
585 for (const auto &e : root->children()) buildFileList(e.get());
586}
587
588template<class DefMutable>
589static void addIncludeFile(DefMutable *cd,FileDef *ifd,const Entry *root)
590{
591 if (
592 (!root->doc.stripWhiteSpace().isEmpty() ||
593 !root->brief.stripWhiteSpace().isEmpty() ||
594 Config_getBool(EXTRACT_ALL)
595 ) && root->protection!=Protection::Private
596 )
597 {
598 //printf(">>>>>> includeFile=%s\n",qPrint(root->includeFile));
599
600 bool local=Config_getBool(FORCE_LOCAL_INCLUDES);
601 QCString includeFile = root->includeFile;
602 if (!includeFile.isEmpty() && includeFile.at(0)=='"')
603 {
604 local = TRUE;
605 includeFile=includeFile.mid(1,includeFile.length()-2);
606 }
607 else if (!includeFile.isEmpty() && includeFile.at(0)=='<')
608 {
609 local = FALSE;
610 includeFile=includeFile.mid(1,includeFile.length()-2);
611 }
612
613 bool ambig = false;
614 FileDef *fd=nullptr;
615 // see if we need to include a verbatim copy of the header file
616 //printf("root->includeFile=%s\n",qPrint(root->includeFile));
617 if (!includeFile.isEmpty() &&
618 (fd=findFileDef(Doxygen::inputNameLinkedMap,includeFile,ambig))==nullptr
619 )
620 { // explicit request
621 QCString text;
622 text.sprintf("the name '%s' supplied as "
623 "the argument of the \\class, \\struct, \\union, or \\include command ",
624 qPrint(includeFile)
625 );
626 if (ambig) // name is ambiguous
627 {
628 text+="matches the following input files:\n";
630 text+="\n";
631 text+="Please use a more specific name by "
632 "including a (larger) part of the path!";
633 }
634 else // name is not an input file
635 {
636 text+="is not an input file";
637 }
638 warn(root->fileName,root->startLine, "{}", text);
639 }
640 else if (includeFile.isEmpty() && ifd &&
641 // see if the file extension makes sense
642 guessSection(ifd->name()).isHeader())
643 { // implicit assumption
644 fd=ifd;
645 }
646
647 // if a file is found, we mark it as a source file.
648 if (fd)
649 {
650 QCString iName = !root->includeName.isEmpty() ?
651 root->includeName : includeFile;
652 if (!iName.isEmpty()) // user specified include file
653 {
654 if (iName.at(0)=='<') local=FALSE; // explicit override
655 else if (iName.at(0)=='"') local=TRUE;
656 if (iName.at(0)=='"' || iName.at(0)=='<')
657 {
658 iName=iName.mid(1,iName.length()-2); // strip quotes or brackets
659 }
660 if (iName.isEmpty())
661 {
662 iName=fd->name();
663 }
664 }
665 else if (!Config_getList(STRIP_FROM_INC_PATH).empty())
666 {
668 }
669 else // use name of the file containing the class definition
670 {
671 iName=fd->name();
672 }
673 if (fd->generateSourceFile()) // generate code for header
674 {
675 cd->setIncludeFile(fd,iName,local,!root->includeName.isEmpty());
676 }
677 else // put #include in the class documentation without link
678 {
679 cd->setIncludeFile(nullptr,iName,local,TRUE);
680 }
681 }
682 }
683}
684
685
687{
688 size_t l = s.length();
689 int count=0;
690 int round=0;
691 QCString result;
692 for (size_t i=0;i<l;i++)
693 {
694 char c=s.at(i);
695 if (c=='(') round++;
696 else if (c==')' && round>0) round--;
697 else if (c=='<' && round==0) count++;
698 if (count==0)
699 {
700 result+=c;
701 }
702 if (c=='>' && round==0 && count>0) count--;
703 }
704 //printf("stripTemplateSpecifiers(%s)=%s\n",qPrint(s),qPrint(result));
705 return result;
706}
707
708/*! returns the Definition object belonging to the first \a level levels of
709 * full qualified name \a name. Creates an artificial scope if the scope is
710 * not found and set the parent/child scope relation if the scope is found.
711 */
712[[maybe_unused]]
713static Definition *buildScopeFromQualifiedName(const QCString &name_,SrcLangExt lang,const TagInfo *tagInfo)
714{
715 QCString name = stripTemplateSpecifiers(name_);
716 name.stripPrefix("::");
717 int level = name.contains("::");
718 //printf("buildScopeFromQualifiedName(%s) level=%d\n",qPrint(name),level);
719 int i=0, p=0, l=0;
721 QCString fullScope;
722 while (i<level)
723 {
724 int idx=getScopeFragment(name,p,&l);
725 if (idx==-1) return prevScope;
726 QCString nsName = name.mid(idx,l);
727 if (nsName.isEmpty()) return prevScope;
728 if (!fullScope.isEmpty()) fullScope+="::";
729 fullScope+=nsName;
730 NamespaceDef *nd=Doxygen::namespaceLinkedMap->find(fullScope);
731 DefinitionMutable *innerScope = toDefinitionMutable(nd);
732 ClassDef *cd=nullptr;
733 if (nd==nullptr) cd = getClass(fullScope);
734 if (nd==nullptr && cd) // scope is a class
735 {
736 innerScope = toDefinitionMutable(cd);
737 }
738 else if (nd==nullptr && cd==nullptr && fullScope.find('<')==-1) // scope is not known and could be a namespace!
739 {
740 // introduce bogus namespace
741 //printf("++ adding dummy namespace %s to %s tagInfo=%p\n",qPrint(nsName),qPrint(prevScope->name()),(void*)tagInfo);
742 NamespaceDefMutable *newNd=
744 Doxygen::namespaceLinkedMap->add(fullScope,
746 "[generated]",1,1,fullScope,
747 tagInfo?tagInfo->tagName:QCString(),
748 tagInfo?tagInfo->fileName:QCString())));
749 if (newNd)
750 {
751 newNd->setLanguage(lang);
752 newNd->setArtificial(TRUE);
753 // add namespace to the list
754 innerScope = newNd;
755 }
756 }
757 else // scope is a namespace
758 {
759 }
760 if (innerScope)
761 {
762 // make the parent/child scope relation
763 DefinitionMutable *prevScopeMutable = toDefinitionMutable(prevScope);
764 if (prevScopeMutable)
765 {
766 prevScopeMutable->addInnerCompound(toDefinition(innerScope));
767 }
768 innerScope->setOuterScope(prevScope);
769 }
770 else // current scope is a class, so return only the namespace part...
771 {
772 return prevScope;
773 }
774 // proceed to the next scope fragment
775 p=idx+l+2;
776 prevScope=toDefinition(innerScope);
777 i++;
778 }
779 return prevScope;
780}
781
783 FileDef *fileScope,const TagInfo *tagInfo)
784{
785 //printf("<findScopeFromQualifiedName(%s,%s)\n",startScope ? qPrint(startScope->name()) : 0, qPrint(n));
786 Definition *resultScope=toDefinition(startScope);
787 if (resultScope==nullptr) resultScope=Doxygen::globalScope;
789 int l1 = 0;
790 int i1 = getScopeFragment(scope,0,&l1);
791 if (i1==-1)
792 {
793 //printf(">no fragments!\n");
794 return resultScope;
795 }
796 int p=i1+l1,l2=0,i2=0;
797 while ((i2=getScopeFragment(scope,p,&l2))!=-1)
798 {
799 QCString nestedNameSpecifier = scope.mid(i1,l1);
800 Definition *orgScope = resultScope;
801 //printf(" nestedNameSpecifier=%s\n",qPrint(nestedNameSpecifier));
802 resultScope = const_cast<Definition*>(resultScope->findInnerCompound(nestedNameSpecifier));
803 //printf(" resultScope=%p\n",resultScope);
804 if (resultScope==nullptr)
805 {
806 if (orgScope==Doxygen::globalScope && fileScope && !fileScope->getUsedNamespaces().empty())
807 // also search for used namespaces
808 {
809 for (const auto &nd : fileScope->getUsedNamespaces())
810 {
812 if (mnd)
813 {
814 resultScope = findScopeFromQualifiedName(toNamespaceDefMutable(nd),n,fileScope,tagInfo);
815 if (resultScope!=nullptr) break;
816 }
817 }
818 if (resultScope)
819 {
820 // for a nested class A::I in used namespace N, we get
821 // N::A::I while looking for A, so we should compare
822 // resultScope->name() against scope.left(i2+l2)
823 //printf(" -> result=%s scope=%s\n",qPrint(resultScope->name()),qPrint(scope));
824 if (rightScopeMatch(resultScope->name(),scope.left(i2+l2)))
825 {
826 break;
827 }
828 goto nextFragment;
829 }
830 }
831
832 // also search for used classes. Complication: we haven't been able
833 // to put them in the right scope yet, because we are still resolving
834 // the scope relations!
835 // Therefore loop through all used classes and see if there is a right
836 // scope match between the used class and nestedNameSpecifier.
837 for (const auto &usedName : g_usingDeclarations)
838 {
839 //printf("Checking using class %s\n",qPrint(usedName));
840 if (rightScopeMatch(usedName,nestedNameSpecifier))
841 {
842 // ui.currentKey() is the fully qualified name of nestedNameSpecifier
843 // so use this instead.
844 QCString fqn = usedName + scope.right(scope.length()-p);
845 resultScope = buildScopeFromQualifiedName(fqn,startScope->getLanguage(),nullptr);
846 //printf("Creating scope from fqn=%s result %p\n",qPrint(fqn),resultScope);
847 if (resultScope)
848 {
849 //printf("> Match! resultScope=%s\n",qPrint(resultScope->name()));
850 return resultScope;
851 }
852 }
853 }
854
855 //printf("> name %s not found in scope %s\n",qPrint(nestedNameSpecifier),qPrint(orgScope->name()));
856 return nullptr;
857 }
858 nextFragment:
859 i1=i2;
860 l1=l2;
861 p=i2+l2;
862 }
863 //printf(">findScopeFromQualifiedName scope %s\n",qPrint(resultScope->name()));
864 return resultScope;
865}
866
867std::unique_ptr<ArgumentList> getTemplateArgumentsFromName(
868 const QCString &name,
869 const ArgumentLists &tArgLists)
870{
871 // for each scope fragment, check if it is a template and advance through
872 // the list if so.
873 int i=0, p=0;
874 auto alIt = tArgLists.begin();
875 while ((i=name.find("::",p))!=-1 && alIt!=tArgLists.end())
876 {
877 NamespaceDef *nd = Doxygen::namespaceLinkedMap->find(name.left(i));
878 if (nd==nullptr)
879 {
880 ClassDef *cd = getClass(name.left(i));
881 if (cd)
882 {
883 if (!cd->templateArguments().empty())
884 {
885 ++alIt;
886 }
887 }
888 }
889 p=i+2;
890 }
891 return alIt!=tArgLists.end() ?
892 std::make_unique<ArgumentList>(*alIt) :
893 std::unique_ptr<ArgumentList>();
894}
895
896static
898{
900
901 if (specifier.isStruct())
903 else if (specifier.isUnion())
904 sec=ClassDef::Union;
905 else if (specifier.isCategory())
907 else if (specifier.isInterface())
909 else if (specifier.isProtocol())
911 else if (specifier.isException())
913 else if (specifier.isService())
915 else if (specifier.isSingleton())
917
918 if (section.isUnionDoc())
919 sec=ClassDef::Union;
920 else if (section.isStructDoc())
922 else if (section.isInterfaceDoc())
924 else if (section.isProtocolDoc())
926 else if (section.isCategoryDoc())
928 else if (section.isExceptionDoc())
930 else if (section.isServiceDoc())
932 else if (section.isSingletonDoc())
934
935 return sec;
936}
937
938
939static void addClassToContext(const Entry *root)
940{
941 AUTO_TRACE("name={}",root->name);
942 FileDef *fd = root->fileDef();
943
944 QCString scName;
945 if (root->parent()->section.isScope())
946 {
947 scName=root->parent()->name;
948 }
949 // name without parent's scope
950 QCString fullName = root->name;
951
952 // strip off any template parameters (but not those for specializations)
953 int idx=fullName.find('>');
954 if (idx!=-1 && root->lang==SrcLangExt::CSharp) // mangle A<S,T>::N as A-2-g::N
955 {
956 fullName = mangleCSharpGenericName(fullName.left(idx+1))+fullName.mid(idx+1);
957 }
958 fullName=stripTemplateSpecifiersFromScope(fullName);
959
960 // name with scope (if not present already)
961 QCString qualifiedName = fullName;
962 if (!scName.isEmpty() && !leftScopeMatch(scName,fullName))
963 {
964 qualifiedName.prepend(scName+"::");
965 }
966
967 // see if we already found the class before
968 ClassDefMutable *cd = getClassMutable(qualifiedName);
969
970 AUTO_TRACE_ADD("Found class with name '{}', qualifiedName '{}'", cd ? cd->name() : root->name, qualifiedName);
971
972 if (cd)
973 {
974 fullName=cd->name();
975 AUTO_TRACE_ADD("Existing class '{}'",cd->name());
976 //if (cd->templateArguments()==0)
977 //{
978 // //printf("existing ClassDef tempArgList=%p specScope=%s\n",root->tArgList,qPrint(root->scopeSpec));
979 // cd->setTemplateArguments(tArgList);
980 //}
981
982 cd->setDocumentation(root->doc,root->docFile,root->docLine);
983 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
984 root->commandOverrides.apply_collaborationGraph([&](bool b ) { cd->overrideCollaborationGraph(b); });
985 root->commandOverrides.apply_inheritanceGraph ([&](CLASS_GRAPH_t gt) { cd->overrideInheritanceGraph(gt); });
986
987 if (!root->spec.isForwardDecl() && cd->isForwardDeclared())
988 {
989 cd->setDefFile(root->fileName,root->startLine,root->startColumn);
990 if (root->bodyLine!=-1)
991 {
992 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
993 cd->setBodyDef(fd);
994 }
995 }
996
997 if (cd->templateArguments().empty() || (cd->isForwardDeclared() && !root->spec.isForwardDecl()))
998 {
999 // this happens if a template class declared with @class is found
1000 // before the actual definition or if a forward declaration has different template
1001 // parameter names.
1002 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(cd->name(),root->tArgLists);
1003 if (tArgList)
1004 {
1005 cd->setTemplateArguments(*tArgList);
1006 }
1007 }
1008 if (cd->requiresClause().isEmpty() && !root->req.isEmpty())
1009 {
1010 cd->setRequiresClause(root->req);
1011 }
1012
1014
1015 cd->setMetaData(root->metaData);
1016 }
1017 else // new class
1018 {
1020
1021 QCString className;
1022 QCString namespaceName;
1023 extractNamespaceName(fullName,className,namespaceName);
1024
1025 AUTO_TRACE_ADD("New class: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1026 fullName, namespaceName, className, Trace::trunc(root->brief), Trace::trunc(root->doc));
1027
1028 QCString tagName;
1029 QCString refFileName;
1030 const TagInfo *tagInfo = root->tagInfo();
1031 if (tagInfo)
1032 {
1033 tagName = tagInfo->tagName;
1034 refFileName = tagInfo->fileName;
1035 if (fullName.find("::")!=-1)
1036 // symbols imported via tag files may come without the parent scope,
1037 // so we artificially create it here
1038 {
1039 buildScopeFromQualifiedName(fullName,root->lang,tagInfo);
1040 }
1041 }
1042 std::unique_ptr<ArgumentList> tArgList;
1043 int i=0;
1044 if ((root->lang==SrcLangExt::CSharp || root->lang==SrcLangExt::Java) &&
1045 (i=fullName.find('<'))!=-1)
1046 {
1047 // a Java/C# generic class looks like a C++ specialization, so we need to split the
1048 // name and template arguments here
1049 tArgList = stringToArgumentList(root->lang,fullName.mid(i));
1050 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
1051 // A -> A
1052 // A<T> -> A-1-g
1053 // A<T,S> -> A-2-g
1054 {
1055 fullName=mangleCSharpGenericName(fullName);
1056 }
1057 else
1058 {
1059 fullName=fullName.left(i);
1060 }
1061 }
1062 else
1063 {
1064 tArgList = getTemplateArgumentsFromName(fullName,root->tArgLists);
1065 }
1066 // add class to the list
1067 cd = toClassDefMutable(
1068 Doxygen::classLinkedMap->add(fullName,
1069 createClassDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1070 fullName,sec,tagName,refFileName,TRUE,root->spec.isEnum()) ));
1071 if (cd)
1072 {
1073 AUTO_TRACE_ADD("New class '{}' type={} #tArgLists={} tagInfo={} hidden={} artificial={}",
1074 fullName,cd->compoundTypeString(),root->tArgLists.size(),
1075 fmt::ptr(tagInfo),root->hidden,root->artificial);
1076 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1077 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1078 cd->setLanguage(root->lang);
1079 cd->setId(root->id);
1080 cd->setHidden(root->hidden);
1081 cd->setArtificial(root->artificial);
1082 cd->setClassSpecifier(root->spec);
1083 if (root->lang==SrcLangExt::CSharp && !root->args.isEmpty())
1084 {
1086 }
1087 cd->addQualifiers(root->qualifiers);
1088 cd->setTypeConstraints(root->typeConstr);
1089 root->commandOverrides.apply_collaborationGraph([&](bool b ) { cd->overrideCollaborationGraph(b); });
1090 root->commandOverrides.apply_inheritanceGraph ([&](CLASS_GRAPH_t gt) { cd->overrideInheritanceGraph(gt); });
1091
1092 if (tArgList)
1093 {
1094 cd->setTemplateArguments(*tArgList);
1095 }
1096 cd->setRequiresClause(root->req);
1097 cd->setProtection(root->protection);
1098 cd->setIsStatic(root->isStatic);
1099
1100 // file definition containing the class cd
1101 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1102 cd->setBodyDef(fd);
1103
1104 cd->setMetaData(root->metaData);
1105
1106 cd->insertUsedFile(fd);
1107 }
1108 else
1109 {
1110 AUTO_TRACE_ADD("Class {} not added, already exists as alias", fullName);
1111 }
1112 }
1113
1114 if (cd)
1115 {
1117 if (!root->subGrouping) cd->setSubGrouping(FALSE);
1118 if (!root->spec.isForwardDecl())
1119 {
1120 if (cd->hasDocumentation())
1121 {
1122 addIncludeFile(cd,fd,root);
1123 }
1124 if (fd && root->section.isCompound())
1125 {
1126 AUTO_TRACE_ADD("Inserting class {} in file {} (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1127 cd->setFileDef(fd);
1128 fd->insertClass(cd);
1129 }
1130 }
1131 addClassToGroups(root,cd);
1133 cd->setRefItems(root->sli);
1134 cd->setRequirementReferences(root->rqli);
1135 }
1136}
1137
1138//----------------------------------------------------------------------
1139// build a list of all classes mentioned in the documentation
1140// and all classes that have a documentation block before their definition.
1141static void buildClassList(const Entry *root)
1142{
1143 if ((root->section.isCompound() || root->section.isObjcImpl()) && !root->name.isEmpty())
1144 {
1145 AUTO_TRACE();
1146 addClassToContext(root);
1147 }
1148 for (const auto &e : root->children()) buildClassList(e.get());
1149}
1150
1151static void buildClassDocList(const Entry *root)
1152{
1153 if ((root->section.isCompoundDoc()) && !root->name.isEmpty())
1154 {
1155 AUTO_TRACE();
1156 addClassToContext(root);
1157 }
1158 for (const auto &e : root->children()) buildClassDocList(e.get());
1159}
1160
1161//----------------------------------------------------------------------
1162// build a list of all classes mentioned in the documentation
1163// and all classes that have a documentation block before their definition.
1164
1165static void addConceptToContext(const Entry *root)
1166{
1167 AUTO_TRACE();
1168 FileDef *fd = root->fileDef();
1169
1170 QCString scName;
1171 if (root->parent()->section.isScope())
1172 {
1173 scName=root->parent()->name;
1174 }
1175
1176 // name with scope (if not present already)
1177 QCString qualifiedName = root->name;
1178 if (!scName.isEmpty() && !leftScopeMatch(qualifiedName,scName))
1179 {
1180 qualifiedName.prepend(scName+"::");
1181 }
1182
1183 // see if we already found the concept before
1184 ConceptDefMutable *cd = getConceptMutable(qualifiedName);
1185
1186 AUTO_TRACE_ADD("Found concept with name '{}' (qualifiedName='{}')", cd ? cd->name() : root->name, qualifiedName);
1187
1188 if (cd)
1189 {
1190 qualifiedName=cd->name();
1191 AUTO_TRACE_ADD("Existing concept '{}'",cd->name());
1192
1193 cd->setDocumentation(root->doc,root->docFile,root->docLine);
1194 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1195
1196 addIncludeFile(cd,fd,root);
1197 }
1198 else // new concept
1199 {
1200 QCString className;
1201 QCString namespaceName;
1202 extractNamespaceName(qualifiedName,className,namespaceName);
1203
1204 AUTO_TRACE_ADD("New concept: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1205 qualifiedName,namespaceName,className,root->brief,root->doc);
1206
1207 QCString tagName;
1208 QCString refFileName;
1209 const TagInfo *tagInfo = root->tagInfo();
1210 if (tagInfo)
1211 {
1212 tagName = tagInfo->tagName;
1213 refFileName = tagInfo->fileName;
1214 if (qualifiedName.find("::")!=-1)
1215 // symbols imported via tag files may come without the parent scope,
1216 // so we artificially create it here
1217 {
1218 buildScopeFromQualifiedName(qualifiedName,root->lang,tagInfo);
1219 }
1220 }
1221 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(qualifiedName,root->tArgLists);
1222 // add concept to the list
1224 Doxygen::conceptLinkedMap->add(qualifiedName,
1225 createConceptDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1226 qualifiedName,tagName,refFileName)));
1227 if (cd)
1228 {
1229 AUTO_TRACE_ADD("New concept '{}' #tArgLists={} tagInfo={}",
1230 qualifiedName,root->tArgLists.size(),fmt::ptr(tagInfo));
1231 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1232 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1233 cd->setLanguage(root->lang);
1234 cd->setId(root->id);
1235 cd->setHidden(root->hidden);
1236 cd->setGroupId(root->mGrpId);
1237 if (tArgList)
1238 {
1239 cd->setTemplateArguments(*tArgList);
1240 }
1241 cd->setInitializer(root->initializer.str());
1242 // file definition containing the class cd
1243 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1244 cd->setBodyDef(fd);
1246 cd->setRefItems(root->sli);
1247 cd->setRequirementReferences(root->rqli);
1248 addIncludeFile(cd,fd,root);
1249
1250 // also add namespace to the correct structural context
1251 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,qualifiedName,nullptr,tagInfo);
1253 {
1255 if (dm)
1256 {
1257 dm->addInnerCompound(cd);
1258 }
1259 cd->setOuterScope(d);
1260 }
1261 for (const auto &ce : root->children())
1262 {
1263 //printf("Concept %s has child %s\n",qPrint(root->name),qPrint(ce->section.to_string()));
1264 if (ce->section.isConceptDocPart())
1265 {
1266 cd->addSectionsToDefinition(ce->anchors);
1267 cd->setRefItems(ce->sli);
1268 cd->setRequirementReferences(ce->rqli);
1269 if (!ce->brief.isEmpty())
1270 {
1271 cd->addDocPart(ce->brief,ce->startLine,ce->startColumn);
1272 //printf(" brief=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->brief),ce->startLine,ce->startColumn);
1273 }
1274 if (!ce->doc.isEmpty())
1275 {
1276 cd->addDocPart(ce->doc,ce->startLine,ce->startColumn);
1277 //printf(" doc=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->doc),ce->startLine,ce->startColumn);
1278 }
1279 }
1280 else if (ce->section.isConceptCodePart())
1281 {
1282 cd->addCodePart(ce->initializer.str(),ce->startLine,ce->startColumn);
1283 //printf(" code=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->initializer.str()),ce->startLine,ce->startColumn);
1284 }
1285 }
1286 }
1287 else
1288 {
1289 AUTO_TRACE_ADD("Concept '{}' not added, already exists (as alias)", qualifiedName);
1290 }
1291 }
1292
1293 if (cd)
1294 {
1296 for (const auto &ce : root->children())
1297 {
1298 if (ce->section.isConceptDocPart())
1299 {
1300 cd->addSectionsToDefinition(ce->anchors);
1301 }
1302 }
1303 if (fd)
1304 {
1305 AUTO_TRACE_ADD("Inserting concept '{}' in file '{}' (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1306 cd->setFileDef(fd);
1307 fd->insertConcept(cd);
1308 }
1309 addConceptToGroups(root,cd);
1311 cd->setRefItems(root->sli);
1312 cd->setRequirementReferences(root->rqli);
1313 }
1314}
1315
1316static void findModuleDocumentation(const Entry *root)
1317{
1318 if (root->section.isModuleDoc())
1319 {
1320 AUTO_TRACE();
1322 }
1323 for (const auto &e : root->children()) findModuleDocumentation(e.get());
1324}
1325
1326static void buildConceptList(const Entry *root)
1327{
1328 if (root->section.isConcept())
1329 {
1330 AUTO_TRACE();
1331 addConceptToContext(root);
1332 }
1333 for (const auto &e : root->children()) buildConceptList(e.get());
1334}
1335
1336static void buildConceptDocList(const Entry *root)
1337{
1338 if (root->section.isConceptDoc())
1339 {
1340 AUTO_TRACE();
1341 addConceptToContext(root);
1342 }
1343 for (const auto &e : root->children()) buildConceptDocList(e.get());
1344}
1345
1346// This routine is to allow @ingroup X @{ concept A; concept B; @} to work
1347// (same also works for variable and functions because of logic in MemberGroup::insertMember)
1349{
1350 AUTO_TRACE();
1351 for (const auto &cd : *Doxygen::conceptLinkedMap)
1352 {
1353 if (cd->groupId()!=DOX_NOGROUP)
1354 {
1355 for (const auto &ocd : *Doxygen::conceptLinkedMap)
1356 {
1357 if (cd!=ocd && cd->groupId()==ocd->groupId() &&
1358 !cd->partOfGroups().empty() && ocd->partOfGroups().empty())
1359 {
1360 ConceptDefMutable *ocdm = toConceptDefMutable(ocd.get());
1361 if (ocdm)
1362 {
1363 for (const auto &gd : cd->partOfGroups())
1364 {
1365 if (gd)
1366 {
1367 AUTO_TRACE_ADD("making concept '{}' part of group '{}'",ocdm->name(),gd->name());
1368 ocdm->makePartOfGroup(gd);
1369 gd->addConcept(ocd.get());
1370 }
1371 }
1372 }
1373 }
1374 }
1375 }
1376 }
1377}
1378
1379//----------------------------------------------------------------------
1380
1382{
1383 ClassDefSet visitedClasses;
1384
1385 bool done=FALSE;
1386 //int iteration=0;
1387 while (!done)
1388 {
1389 done=TRUE;
1390 //++iteration;
1391 struct ClassAlias
1392 {
1393 ClassAlias(const QCString &name,std::unique_ptr<ClassDef> cd,DefinitionMutable *ctx) :
1394 aliasFullName(name),aliasCd(std::move(cd)), aliasContext(ctx) {}
1395 QCString aliasFullName;
1396 std::unique_ptr<ClassDef> aliasCd;
1397 DefinitionMutable *aliasContext;
1398 };
1399 std::vector<ClassAlias> aliases;
1400 for (const auto &icd : *Doxygen::classLinkedMap)
1401 {
1402 ClassDefMutable *cd = toClassDefMutable(icd.get());
1403 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1404 {
1405 QCString name = stripAnonymousNamespaceScope(icd->name());
1406 //printf("processing=%s, iteration=%d\n",qPrint(cd->name()),iteration);
1407 // also add class to the correct structural context
1409 name,icd->getFileDef(),nullptr);
1410 if (d)
1411 {
1412 //printf("****** adding %s to scope %s in iteration %d\n",qPrint(cd->name()),qPrint(d->name()),iteration);
1414 if (dm)
1415 {
1416 dm->addInnerCompound(cd);
1417 }
1418 cd->setOuterScope(d);
1419
1420 // for inline namespace add an alias of the class to the outer scope
1422 {
1424 //printf("nd->isInline()=%d\n",nd->isInline());
1425 if (nd && nd->isInline())
1426 {
1427 d = d->getOuterScope();
1428 if (d)
1429 {
1430 dm = toDefinitionMutable(d);
1431 if (dm)
1432 {
1433 auto aliasCd = createClassDefAlias(d,cd);
1434 QCString aliasFullName = d->qualifiedName()+"::"+aliasCd->localName();
1435 aliases.emplace_back(aliasFullName,std::move(aliasCd),dm);
1436 //printf("adding %s to %s as %s\n",qPrint(aliasCd->name()),qPrint(d->name()),qPrint(aliasFullName));
1437 }
1438 }
1439 }
1440 else
1441 {
1442 break;
1443 }
1444 }
1445
1446 visitedClasses.insert(icd.get());
1447 done=FALSE;
1448 }
1449 //else
1450 //{
1451 // printf("****** ignoring %s: scope not (yet) found in iteration %d\n",qPrint(cd->name()),iteration);
1452 //}
1453 }
1454 }
1455 // add aliases
1456 for (auto &alias : aliases)
1457 {
1458 ClassDef *aliasCd = Doxygen::classLinkedMap->add(alias.aliasFullName,std::move(alias.aliasCd));
1459 if (aliasCd)
1460 {
1461 alias.aliasContext->addInnerCompound(aliasCd);
1462 }
1463 }
1464 }
1465
1466 //give warnings for unresolved compounds
1467 for (const auto &icd : *Doxygen::classLinkedMap)
1468 {
1469 ClassDefMutable *cd = toClassDefMutable(icd.get());
1470 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1471 {
1473 /// create the scope artificially
1474 // anyway, so we can at least relate scopes properly.
1475 Definition *d = buildScopeFromQualifiedName(name,cd->getLanguage(),nullptr);
1476 if (d && d!=cd && !cd->getDefFileName().isEmpty())
1477 // avoid recursion in case of redundant scopes, i.e: namespace N { class N::C {}; }
1478 // for this case doxygen assumes the existence of a namespace N::N in which C is to be found!
1479 // also avoid warning for stuff imported via a tagfile.
1480 {
1482 if (dm)
1483 {
1484 dm->addInnerCompound(cd);
1485 }
1486 cd->setOuterScope(d);
1487 warn(cd->getDefFileName(),cd->getDefLine(),
1488 "Incomplete input: scope for class {} not found!{}",name,
1489 name.startsWith("std::") ? " Try enabling BUILTIN_STL_SUPPORT." : ""
1490 );
1491 }
1492 }
1493 }
1494}
1495
1497{
1498 //bool inlineGroupedClasses = Config_getBool(INLINE_GROUPED_CLASSES);
1499 //if (!inlineGroupedClasses) return;
1500 //printf("** distributeClassGroupRelations()\n");
1501
1502 ClassDefSet visitedClasses;
1503 for (const auto &cd : *Doxygen::classLinkedMap)
1504 {
1505 //printf("Checking %s\n",qPrint(cd->name()));
1506 // distribute the group to nested classes as well
1507 if (visitedClasses.find(cd.get())==visitedClasses.end() && !cd->partOfGroups().empty())
1508 {
1509 //printf(" Candidate for merging\n");
1510 GroupDef *gd = cd->partOfGroups().front();
1511 for (auto &ncd : cd->getClasses())
1512 {
1514 if (ncdm && ncdm->partOfGroups().empty())
1515 {
1516 //printf(" Adding %s to group '%s'\n",qPrint(ncd->name()),
1517 // gd->groupTitle());
1518 ncdm->makePartOfGroup(gd);
1519 gd->addClass(ncdm);
1520 }
1521 }
1522 visitedClasses.insert(cd.get()); // only visit every class once
1523 }
1524 }
1525}
1526
1527//----------------------------
1528
1529static ClassDefMutable *createTagLessInstance(const ClassDef *rootCd,const ClassDef *templ,const QCString &fieldName)
1530{
1531 QCString fullName = removeAnonymousScopes(templ->name());
1532 if (fullName.endsWith("::")) fullName=fullName.left(fullName.length()-2);
1533 fullName+="."+fieldName;
1534
1535 //printf("** adding class %s based on %s\n",qPrint(fullName),qPrint(templ->name()));
1537 Doxygen::classLinkedMap->add(fullName,
1539 templ->getDefLine(),
1540 templ->getDefColumn(),
1541 fullName,
1542 templ->compoundType())));
1543 if (cd)
1544 {
1545 cd->setDocumentation(templ->documentation(),templ->docFile(),templ->docLine()); // copy docs to definition
1546 cd->setBriefDescription(templ->briefDescription(),templ->briefFile(),templ->briefLine());
1547 cd->setLanguage(templ->getLanguage());
1548 cd->setBodySegment(templ->getDefLine(),templ->getStartBodyLine(),templ->getEndBodyLine());
1549 cd->setBodyDef(templ->getBodyDef());
1550
1551 cd->setOuterScope(rootCd->getOuterScope());
1552 if (rootCd->getOuterScope()!=Doxygen::globalScope)
1553 {
1554 DefinitionMutable *outerScope = toDefinitionMutable(rootCd->getOuterScope());
1555 if (outerScope)
1556 {
1557 outerScope->addInnerCompound(cd);
1558 }
1559 }
1560
1561 FileDef *fd = templ->getFileDef();
1562 if (fd)
1563 {
1564 cd->setFileDef(fd);
1565 fd->insertClass(cd);
1566 }
1567 for (auto &gd : rootCd->partOfGroups())
1568 {
1569 cd->makePartOfGroup(gd);
1570 gd->addClass(cd);
1571 }
1572
1573 MemberList *ml = templ->getMemberList(MemberListType::PubAttribs());
1574 if (ml)
1575 {
1576 for (const auto &md : *ml)
1577 {
1578 //printf(" Member %s type=%s\n",qPrint(md->name()),md->typeString());
1579 auto newMd = createMemberDef(md->getDefFileName(),md->getDefLine(),md->getDefColumn(),
1580 md->typeString(),md->name(),md->argsString(),md->excpString(),
1581 md->protection(),md->virtualness(),md->isStatic(),Relationship::Member,
1582 md->memberType(),
1583 ArgumentList(),ArgumentList(),"");
1584 MemberDefMutable *imd = toMemberDefMutable(newMd.get());
1585 imd->setMemberClass(cd);
1586 imd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
1587 imd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
1588 imd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
1589 imd->setMemberSpecifiers(md->getMemberSpecifiers());
1590 imd->setVhdlSpecifiers(md->getVhdlSpecifiers());
1591 imd->setMemberGroupId(md->getMemberGroupId());
1592 imd->setInitializer(md->initializer());
1593 imd->setRequiresClause(md->requiresClause());
1594 imd->setMaxInitLines(md->initializerLines());
1595 imd->setBitfields(md->bitfieldString());
1596 imd->setLanguage(md->getLanguage());
1597 cd->insertMember(imd);
1598 MemberName *mn = Doxygen::memberNameLinkedMap->add(md->name());
1599 mn->push_back(std::move(newMd));
1600 }
1601 }
1602 }
1603 return cd;
1604}
1605
1606/** Look through the members of class \a cd and its public members.
1607 * If there is a member m of a tag less struct/union,
1608 * then we create a duplicate of the struct/union with the name of the
1609 * member to identify it.
1610 * So if cd has name S, then the tag less struct/union will get name S.m
1611 * Since tag less structs can be nested we need to call this function
1612 * recursively. Later on we need to patch the member types so we keep
1613 * track of the hierarchy of classes we create.
1614 */
1615static void processTagLessClasses(const ClassDef *rootCd,
1616 const ClassDef *cd,
1617 ClassDefMutable *tagParentCd,
1618 const QCString &prefix,int count)
1619{
1620 //printf("%d: processTagLessClasses %s\n",count,qPrint(cd->name()));
1621 //printf("checking members for %s\n",qPrint(cd->name()));
1622 if (tagParentCd && !cd->getClasses().empty())
1623 {
1624 MemberList *ml = cd->getMemberList(MemberListType::PubAttribs());
1625 if (ml)
1626 {
1627 int pos=0;
1628 for (const auto &md : *ml)
1629 {
1630 QCString type = md->typeString();
1631 if (type.find("::@")!=-1) // member of tag less struct/union
1632 {
1633 for (const auto &icd : cd->getClasses())
1634 {
1635 //printf(" member %s: type='%s'\n",qPrint(md->name()),qPrint(type));
1636 //printf(" comparing '%s'<->'%s'\n",qPrint(type),qPrint(icd->name()));
1637 if (type.find(icd->name())!=-1) // matching tag less struct/union
1638 {
1639 QCString name = md->name();
1640 if (md->isAnonymous()) name = "__unnamed" + QCString().setNum(pos++)+"__";
1641 if (!prefix.isEmpty()) name.prepend(prefix+".");
1642 //printf(" found %s for class %s\n",qPrint(name),qPrint(cd->name()));
1643 ClassDefMutable *ncd = createTagLessInstance(rootCd,icd,name);
1644 if (ncd)
1645 {
1646 processTagLessClasses(rootCd,icd,ncd,name,count+1);
1647 //printf(" addTagged %s to %s\n",qPrint(ncd->name()),qPrint(tagParentCd->name()));
1648 ncd->setTagLessReference(icd);
1649
1650 // replace tag-less type for generated/original member
1651 // by newly created class name.
1652 // note the difference between changing cd and tagParentCd.
1653 // for the initial call this is the same pointer, but for
1654 // recursive calls cd is the original tag-less struct (of which
1655 // there is only one instance) and tagParentCd is the newly
1656 // generated tagged struct of which there can be multiple instances!
1657 MemberList *pml = tagParentCd->getMemberList(MemberListType::PubAttribs());
1658 if (pml)
1659 {
1660 for (const auto &pmd : *pml)
1661 {
1663 if (pmdm && pmd->name()==md->name())
1664 {
1665 pmdm->setAccessorType(ncd,substitute(pmd->typeString(),icd->name(),ncd->name()));
1666 //pmd->setType(substitute(pmd->typeString(),icd->name(),ncd->name()));
1667 }
1668 }
1669 }
1670 }
1671 }
1672 }
1673 }
1674 }
1675 }
1676 }
1677}
1678
1679static void findTagLessClasses(std::vector<ClassDefMutable*> &candidates,ClassDef *cd)
1680{
1681 for (const auto &icd : cd->getClasses())
1682 {
1683 if (icd->name().find("@")==-1) // process all non-anonymous inner classes
1684 {
1685 findTagLessClasses(candidates,icd);
1686 }
1687 }
1688
1690 if (cdm)
1691 {
1692 candidates.push_back(cdm);
1693 }
1694}
1695
1697{
1698 std::vector<ClassDefMutable *> candidates;
1699 for (auto &cd : *Doxygen::classLinkedMap)
1700 {
1701 Definition *scope = cd->getOuterScope();
1702 if (scope && scope->definitionType()!=Definition::TypeClass) // that is not nested
1703 {
1704 findTagLessClasses(candidates,cd.get());
1705 }
1706 }
1707
1708 // since processTagLessClasses is potentially adding classes to Doxygen::classLinkedMap
1709 // we need to call it outside of the loop above, otherwise the iterator gets invalidated!
1710 for (auto &cd : candidates)
1711 {
1712 processTagLessClasses(cd,cd,cd,"",0); // process tag less inner struct/classes
1713 }
1714}
1715
1716
1717//----------------------------------------------------------------------
1718// build a list of all namespaces mentioned in the documentation
1719// and all namespaces that have a documentation block before their definition.
1720static void buildNamespaceList(const Entry *root)
1721{
1722 if (
1723 (root->section.isNamespace() ||
1724 root->section.isNamespaceDoc() ||
1725 root->section.isPackageDoc()
1726 ) &&
1727 !root->name.isEmpty()
1728 )
1729 {
1730 AUTO_TRACE("name={}",root->name);
1731
1732 QCString fName = root->name;
1733 if (root->section.isPackageDoc())
1734 {
1735 fName=substitute(fName,".","::");
1736 }
1737
1738 QCString fullName = stripAnonymousNamespaceScope(fName);
1739 if (!fullName.isEmpty())
1740 {
1741 AUTO_TRACE_ADD("Found namespace {} in {} at line {}",root->name,root->fileName,root->startLine);
1742 NamespaceDef *ndi = Doxygen::namespaceLinkedMap->find(fullName);
1743 if (ndi) // existing namespace
1744 {
1746 if (nd) // non-inline namespace
1747 {
1748 AUTO_TRACE_ADD("Existing namespace");
1749 nd->setDocumentation(root->doc,root->docFile,root->docLine);
1750 nd->setName(fullName); // change name to match docs
1752 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1753 if (nd->getLanguage()==SrcLangExt::Unknown)
1754 {
1755 nd->setLanguage(root->lang);
1756 }
1757 if (root->tagInfo()==nullptr && nd->isReference() && !(root->doc.isEmpty() && root->brief.isEmpty()))
1758 // if we previously found namespace nd in a tag file and now we find a
1759 // documented namespace with the same name in the project, then remove
1760 // the tag file reference
1761 {
1762 nd->setReference("");
1763 nd->setFileName(fullName);
1764 }
1765 nd->setMetaData(root->metaData);
1766
1767 // file definition containing the namespace nd
1768 FileDef *fd=root->fileDef();
1769 if (nd->isArtificial())
1770 {
1771 nd->setArtificial(FALSE); // found namespace explicitly, so cannot be artificial
1772 nd->setDefFile(root->fileName,root->startLine,root->startColumn);
1773 }
1774 // insert the namespace in the file definition
1775 if (fd) fd->insertNamespace(nd);
1776 addNamespaceToGroups(root,nd);
1777 nd->setRefItems(root->sli);
1778 nd->setRequirementReferences(root->rqli);
1779 }
1780 }
1781 else // fresh namespace
1782 {
1783 QCString tagName;
1784 QCString tagFileName;
1785 const TagInfo *tagInfo = root->tagInfo();
1786 if (tagInfo)
1787 {
1788 tagName = tagInfo->tagName;
1789 tagFileName = tagInfo->fileName;
1790 }
1791 AUTO_TRACE_ADD("new namespace {} lang={} tagName={}",fullName,langToString(root->lang),tagName);
1792 // add namespace to the list
1794 Doxygen::namespaceLinkedMap->add(fullName,
1795 createNamespaceDef(tagInfo?tagName:root->fileName,root->startLine,
1796 root->startColumn,fullName,tagName,tagFileName,
1797 root->type,root->spec.isPublished())));
1798 if (nd)
1799 {
1800 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1801 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1803 nd->setHidden(root->hidden);
1804 nd->setArtificial(root->artificial);
1805 nd->setLanguage(root->lang);
1806 nd->setId(root->id);
1807 nd->setMetaData(root->metaData);
1808 nd->setInline(root->spec.isInline());
1809 nd->setExported(root->exported);
1810
1811 addNamespaceToGroups(root,nd);
1812 nd->setRefItems(root->sli);
1813 nd->setRequirementReferences(root->rqli);
1814
1815 // file definition containing the namespace nd
1816 FileDef *fd=root->fileDef();
1817 // insert the namespace in the file definition
1818 if (fd) fd->insertNamespace(nd);
1819
1820 // the empty string test is needed for extract all case
1821 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1822 nd->insertUsedFile(fd);
1823 nd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1824 nd->setBodyDef(fd);
1825
1826 // also add namespace to the correct structural context
1827 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,fullName,nullptr,tagInfo);
1828 AUTO_TRACE_ADD("adding namespace {} to context {}",nd->name(),d ? d->name() : QCString("<none>"));
1829 if (d==nullptr) // we didn't find anything, create the scope artificially
1830 // anyway, so we can at least relate scopes properly.
1831 {
1832 d = buildScopeFromQualifiedName(fullName,nd->getLanguage(),tagInfo);
1834 if (dm)
1835 {
1836 dm->addInnerCompound(nd);
1837 }
1838 nd->setOuterScope(d);
1839 // TODO: Due to the order in which the tag file is written
1840 // a nested class can be found before its parent!
1841 }
1842 else
1843 {
1845 if (dm)
1846 {
1847 dm->addInnerCompound(nd);
1848 }
1849 nd->setOuterScope(d);
1850 // in case of d is an inline namespace, alias insert nd in the part scope of d.
1852 {
1853 NamespaceDef *pnd = toNamespaceDef(d);
1854 if (pnd && pnd->isInline())
1855 {
1856 d = d->getOuterScope();
1857 if (d)
1858 {
1859 dm = toDefinitionMutable(d);
1860 if (dm)
1861 {
1862 auto aliasNd = createNamespaceDefAlias(d,nd);
1863 dm->addInnerCompound(aliasNd.get());
1864 QCString aliasName = aliasNd->name();
1865 AUTO_TRACE_ADD("adding alias {} to {}",aliasName,d->name());
1866 Doxygen::namespaceLinkedMap->add(aliasName,std::move(aliasNd));
1867 }
1868 }
1869 else
1870 {
1871 break;
1872 }
1873 }
1874 else
1875 {
1876 break;
1877 }
1878 }
1879 }
1880 }
1881 }
1882 }
1883 }
1884 for (const auto &e : root->children()) buildNamespaceList(e.get());
1885}
1886
1887//----------------------------------------------------------------------
1888
1890 const QCString &name)
1891{
1892 NamespaceDef *usingNd =nullptr;
1893 for (auto &und : unl)
1894 {
1895 QCString uScope=und->name()+"::";
1896 usingNd = getResolvedNamespace(uScope+name);
1897 if (usingNd!=nullptr) break;
1898 }
1899 return usingNd;
1900}
1901
1902static void findUsingDirectives(const Entry *root)
1903{
1904 if (root->section.isUsingDir())
1905 {
1906 AUTO_TRACE("Found using directive {} at line {} of {}",root->name,root->startLine,root->fileName);
1907 QCString name=substitute(root->name,".","::");
1908 if (name.endsWith("::"))
1909 {
1910 name=name.left(name.length()-2);
1911 }
1912 if (!name.isEmpty())
1913 {
1914 NamespaceDef *usingNd = nullptr;
1915 NamespaceDefMutable *nd = nullptr;
1916 FileDef *fd = root->fileDef();
1917 QCString nsName;
1918
1919 // see if the using statement was found inside a namespace or inside
1920 // the global file scope.
1921 if (root->parent() && root->parent()->section.isNamespace() &&
1922 (fd==nullptr || fd->getLanguage()!=SrcLangExt::Java) // not a .java file
1923 )
1924 {
1925 nsName=stripAnonymousNamespaceScope(root->parent()->name);
1926 if (!nsName.isEmpty())
1927 {
1928 nd = getResolvedNamespaceMutable(nsName);
1929 }
1930 }
1931
1932 // find the scope in which the 'using' namespace is defined by prepending
1933 // the possible scopes in which the using statement was found, starting
1934 // with the most inner scope and going to the most outer scope (i.e.
1935 // file scope).
1936 int scopeOffset = static_cast<int>(nsName.length());
1937 do
1938 {
1939 QCString scope=scopeOffset>0 ?
1940 nsName.left(scopeOffset)+"::" : QCString();
1941 usingNd = getResolvedNamespace(scope+name);
1942 //printf("Trying with scope='%s' usingNd=%p\n",(scope+qPrint(name)),usingNd);
1943 if (scopeOffset==0)
1944 {
1945 scopeOffset=-1;
1946 }
1947 else if ((scopeOffset=nsName.findRev("::",scopeOffset-1))==-1)
1948 {
1949 scopeOffset=0;
1950 }
1951 } while (scopeOffset>=0 && usingNd==nullptr);
1952
1953 if (usingNd==nullptr && nd) // not found, try used namespaces in this scope
1954 // or in one of the parent namespace scopes
1955 {
1956 const NamespaceDefMutable *pnd = nd;
1957 while (pnd && usingNd==nullptr)
1958 {
1959 // also try with one of the used namespaces found earlier
1961
1962 // goto the parent
1963 Definition *s = pnd->getOuterScope();
1965 {
1967 }
1968 else
1969 {
1970 pnd = nullptr;
1971 }
1972 }
1973 }
1974 if (usingNd==nullptr && fd) // still nothing, also try used namespace in the
1975 // global scope
1976 {
1977 usingNd = findUsedNamespace(fd->getUsedNamespaces(),name);
1978 }
1979
1980 //printf("%s -> %s\n",qPrint(name),usingNd?qPrint(usingNd->name()):"<none>");
1981
1982 // add the namespace the correct scope
1983 if (usingNd)
1984 {
1985 //printf("using fd=%p nd=%p\n",fd,nd);
1986 if (nd)
1987 {
1988 //printf("Inside namespace %s\n",qPrint(nd->name()));
1989 nd->addUsingDirective(usingNd);
1990 }
1991 else if (fd)
1992 {
1993 //printf("Inside file %s\n",qPrint(fd->name()));
1994 fd->addUsingDirective(usingNd);
1995 }
1996 }
1997 else // unknown namespace, but add it anyway.
1998 {
1999 AUTO_TRACE_ADD("new unknown namespace {} lang={} hidden={}",name,langToString(root->lang),root->hidden);
2000 // add namespace to the list
2003 createNamespaceDef(root->fileName,root->startLine,root->startColumn,name)));
2004 if (nd)
2005 {
2006 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
2007 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2009 nd->setHidden(root->hidden);
2010 nd->setArtificial(TRUE);
2011 nd->setLanguage(root->lang);
2012 nd->setId(root->id);
2013 nd->setMetaData(root->metaData);
2014 nd->setInline(root->spec.isInline());
2015 nd->setExported(root->exported);
2016
2017 for (const Grouping &g : root->groups)
2018 {
2019 GroupDef *gd=nullptr;
2020 if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname)))
2021 gd->addNamespace(nd);
2022 }
2023
2024 // insert the namespace in the file definition
2025 if (fd)
2026 {
2027 fd->insertNamespace(nd);
2028 fd->addUsingDirective(nd);
2029 }
2030
2031 // the empty string test is needed for extract all case
2032 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2033 nd->insertUsedFile(fd);
2034 nd->setRefItems(root->sli);
2035 nd->setRequirementReferences(root->rqli);
2036 }
2037 }
2038 }
2039 }
2040 for (const auto &e : root->children()) findUsingDirectives(e.get());
2041}
2042
2043//----------------------------------------------------------------------
2044
2045static void buildListOfUsingDecls(const Entry *root)
2046{
2047 if (root->section.isUsingDecl() &&
2048 !root->parent()->section.isCompound() // not a class/struct member
2049 )
2050 {
2051 QCString name = substitute(root->name,".","::");
2052 g_usingDeclarations.insert(name.str());
2053 }
2054 for (const auto &e : root->children()) buildListOfUsingDecls(e.get());
2055}
2056
2057
2058static void findUsingDeclarations(const Entry *root,bool filterPythonPackages)
2059{
2060 if (root->section.isUsingDecl() &&
2061 !root->parent()->section.isCompound() && // not a class/struct member
2062 (!filterPythonPackages || (root->lang==SrcLangExt::Python && root->fileName.endsWith("__init__.py")))
2063 )
2064 {
2065 AUTO_TRACE("Found using declaration '{}' at line {} of {} inside section {}",
2066 root->name,root->startLine,root->fileName,root->parent()->section);
2067 if (!root->name.isEmpty())
2068 {
2069 const Definition *usingDef = nullptr;
2070 NamespaceDefMutable *nd = nullptr;
2071 FileDef *fd = root->fileDef();
2072 QCString scName;
2073
2074 // see if the using statement was found inside a namespace or inside
2075 // the global file scope.
2076 if (root->parent()->section.isNamespace())
2077 {
2078 scName=root->parent()->name;
2079 if (!scName.isEmpty())
2080 {
2081 nd = getResolvedNamespaceMutable(scName);
2082 }
2083 }
2084
2085 // Assume the using statement was used to import a class.
2086 // Find the scope in which the 'using' namespace is defined by prepending
2087 // the possible scopes in which the using statement was found, starting
2088 // with the most inner scope and going to the most outer scope (i.e.
2089 // file scope).
2090
2091 QCString name = substitute(root->name,".","::"); //Java/C# scope->internal
2092
2093 SymbolResolver resolver;
2094 const Definition *scope = nd;
2095 if (nd==nullptr) scope = fd;
2096 usingDef = resolver.resolveSymbol(scope,name);
2097
2098 //printf("usingDef(scope=%s,name=%s)=%s\n",qPrint(nd?nd->qualifiedName():""),qPrint(name),usingDef?qPrint(usingDef->qualifiedName()):"nullptr");
2099
2100 if (!usingDef)
2101 {
2102 usingDef = getClass(name); // try direct lookup, this is needed to get
2103 // builtin STL classes to properly resolve, e.g.
2104 // vector -> std::vector
2105 }
2106 if (!usingDef)
2107 {
2108 usingDef = Doxygen::hiddenClassLinkedMap->find(name); // check if it is already hidden
2109 }
2110#if 0
2111 if (!usingDef)
2112 {
2113 AUTO_TRACE_ADD("New using class '{}' (sec={})! #tArgLists={}",
2114 name,root->section,root->tArgLists.size());
2117 createClassDef( "<using>",1,1, name, ClassDef::Class)));
2118 if (usingCd)
2119 {
2120 usingCd->setArtificial(TRUE);
2121 usingCd->setLanguage(root->lang);
2122 usingDef = usingCd;
2123 }
2124 }
2125#endif
2126 else
2127 {
2128 AUTO_TRACE_ADD("Found used type '{}' in scope='{}'",
2129 usingDef->name(), nd ? nd->name(): fd ? fd->name() : QCString("<unknown>"));
2130 }
2131
2132 if (usingDef)
2133 {
2134 if (nd)
2135 {
2136 nd->addUsingDeclaration(usingDef);
2137 }
2138 else if (fd)
2139 {
2140 fd->addUsingDeclaration(usingDef);
2141 }
2142 }
2143 }
2144 }
2145 for (const auto &e : root->children()) findUsingDeclarations(e.get(),filterPythonPackages);
2146}
2147
2148//----------------------------------------------------------------------
2149
2151{
2152 root->commandOverrides.apply_callGraph ([&](bool b) { md->overrideCallGraph(b); });
2153 root->commandOverrides.apply_callerGraph ([&](bool b) { md->overrideCallerGraph(b); });
2154 root->commandOverrides.apply_referencedByRelation([&](bool b) { md->overrideReferencedByRelation(b); });
2155 root->commandOverrides.apply_referencesRelation ([&](bool b) { md->overrideReferencesRelation(b); });
2156 root->commandOverrides.apply_inlineSource ([&](bool b) { md->overrideInlineSource(b); });
2157 root->commandOverrides.apply_enumValues ([&](bool b) { md->overrideEnumValues(b); });
2158}
2159
2160//----------------------------------------------------------------------
2161
2163 const QCString &fileName,const QCString &memName)
2164{
2165 AUTO_TRACE("creating new member {} for class {}",memName,cd->name());
2166 const ArgumentList &templAl = md->templateArguments();
2167 const ArgumentList &al = md->argumentList();
2168 auto newMd = createMemberDef(
2169 fileName,root->startLine,root->startColumn,
2170 md->typeString(),memName,md->argsString(),
2171 md->excpString(),root->protection,root->virt,
2172 md->isStatic(),Relationship::Member,md->memberType(),
2173 templAl,al,root->metaData
2174 );
2175 auto newMmd = toMemberDefMutable(newMd.get());
2176 newMmd->setMemberClass(cd);
2177 cd->insertMember(newMd.get());
2178 if (!root->doc.isEmpty() || !root->brief.isEmpty())
2179 {
2180 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2181 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2182 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2183 }
2184 else
2185 {
2186 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2187 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2188 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2189 }
2190 newMmd->setDefinition(md->definition());
2191 applyMemberOverrideOptions(root,newMmd);
2192 newMmd->addQualifiers(root->qualifiers);
2193 newMmd->setBitfields(md->bitfieldString());
2194 newMmd->addSectionsToDefinition(root->anchors);
2195 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2196 newMmd->setBodyDef(md->getBodyDef());
2197 newMmd->setInitializer(md->initializer());
2198 newMmd->setRequiresClause(md->requiresClause());
2199 newMmd->setMaxInitLines(md->initializerLines());
2200 newMmd->setMemberGroupId(root->mGrpId);
2201 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2202 newMmd->setVhdlSpecifiers(md->getVhdlSpecifiers());
2203 newMmd->setLanguage(root->lang);
2204 newMmd->setId(root->id);
2205 MemberName *mn = Doxygen::memberNameLinkedMap->add(memName);
2206 mn->push_back(std::move(newMd));
2207}
2208
2209static std::unordered_map<std::string,std::vector<ClassDefMutable*>> g_usingClassMap;
2210
2211static void findUsingDeclImports(const Entry *root)
2212{
2213 if (root->section.isUsingDecl() &&
2214 root->parent()->section.isCompound() // in a class/struct member
2215 )
2216 {
2217 AUTO_TRACE("Found using declaration '{}' inside section {}", root->name, root->parent()->section);
2218 QCString fullName=removeRedundantWhiteSpace(root->parent()->name);
2219 fullName=stripAnonymousNamespaceScope(fullName);
2220 fullName=stripTemplateSpecifiersFromScope(fullName);
2221 ClassDefMutable *cd = getClassMutable(fullName);
2222 if (cd)
2223 {
2224 AUTO_TRACE_ADD("found class '{}'",cd->name());
2225 int i=root->name.findRev("::");
2226 if (i!=-1)
2227 {
2228 QCString scope=root->name.left(i);
2229 QCString memName=root->name.right(root->name.length()-i-2);
2230 SymbolResolver resolver;
2231 const ClassDef *bcd = resolver.resolveClass(cd,scope); // todo: file in fileScope parameter
2232 AUTO_TRACE_ADD("name={} scope={} bcd={}",scope,cd?cd->name():"<none>",bcd?bcd->name():"<none>");
2233 if (bcd && bcd!=cd)
2234 {
2235 AUTO_TRACE_ADD("found class '{}' memName='{}'",bcd->name(),memName);
2237 const MemberNameInfo *mni = mnlm.find(memName);
2238 if (mni)
2239 {
2240 for (auto &mi : *mni)
2241 {
2242 const MemberDef *md = mi->memberDef();
2243 if (md && md->protection()!=Protection::Private)
2244 {
2245 AUTO_TRACE_ADD("found member '{}'",mni->memberName());
2246 QCString fileName = root->fileName;
2247 if (fileName.isEmpty() && root->tagInfo())
2248 {
2249 fileName = root->tagInfo()->tagName;
2250 }
2251 if (!cd->containsOverload(md))
2252 {
2253 createUsingMemberImportForClass(root,cd,md,fileName,memName);
2254 // also insert the member into copies of the class
2255 auto it = g_usingClassMap.find(cd->qualifiedName().str());
2256 if (it != g_usingClassMap.end())
2257 {
2258 for (const auto &copyCd : it->second)
2259 {
2260 createUsingMemberImportForClass(root,copyCd,md,fileName,memName);
2261 }
2262 }
2263 }
2264 }
2265 }
2266 }
2267 }
2268 }
2269 }
2270 }
2271 else if (root->section.isUsingDecl() &&
2272 (root->parent()->section.isNamespace() || root->parent()->section.isEmpty()) && // namespace or global member
2273 root->lang==SrcLangExt::Cpp // do we also want this for e.g. Fortran? (see test case 095)
2274 )
2275 {
2276 AUTO_TRACE("Found using declaration '{}' inside section {}", root->name, root->parent()->section);
2277 Definition *scope = nullptr;
2278 NamespaceDefMutable *nd = nullptr;
2279 FileDef *fd = root->parent()->fileDef();
2280 if (!root->parent()->name.isEmpty())
2281 {
2282 QCString fullName=removeRedundantWhiteSpace(root->parent()->name);
2283 fullName=stripAnonymousNamespaceScope(fullName);
2285 scope = nd;
2286 }
2287 else
2288 {
2289 scope = fd;
2290 }
2291 if (scope)
2292 {
2293 AUTO_TRACE_ADD("found scope '{}'",scope->name());
2294 SymbolResolver resolver;
2295 const Definition *def = resolver.resolveSymbol(root->name.startsWith("::") ? nullptr : scope,root->name);
2296 if (def && def->definitionType()==Definition::TypeMember)
2297 {
2298 int i=root->name.findRev("::");
2299 QCString memName;
2300 if (i!=-1)
2301 {
2302 memName = root->name.right(root->name.length()-i-2);
2303 }
2304 else
2305 {
2306 memName = root->name;
2307 }
2308 const MemberDef *md = toMemberDef(def);
2309 AUTO_TRACE_ADD("found member '{}' for name '{}'",md->qualifiedName(),root->name);
2310 QCString fileName = root->fileName;
2311 if (fileName.isEmpty() && root->tagInfo())
2312 {
2313 fileName = root->tagInfo()->tagName;
2314 }
2315 const ArgumentList &templAl = md->templateArguments();
2316 const ArgumentList &al = md->argumentList();
2317
2318 auto newMd = createMemberDef(
2319 fileName,root->startLine,root->startColumn,
2320 md->typeString(),memName,md->argsString(),
2321 md->excpString(),root->protection,root->virt,
2322 md->isStatic(),Relationship::Member,md->memberType(),
2323 templAl,al,root->metaData
2324 );
2325 auto newMmd = toMemberDefMutable(newMd.get());
2326 if (nd)
2327 {
2328 newMmd->setNamespace(nd);
2329 nd->insertMember(newMd.get());
2330 }
2331 if (fd)
2332 {
2333 newMmd->setFileDef(fd);
2334 fd->insertMember(newMd.get());
2335 }
2336 if (!root->doc.isEmpty() || !root->brief.isEmpty())
2337 {
2338 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2339 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2340 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2341 }
2342 else
2343 {
2344 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2345 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2346 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2347 }
2348 newMmd->setDefinition(md->definition());
2349 applyMemberOverrideOptions(root,newMmd);
2350 newMmd->addQualifiers(root->qualifiers);
2351 newMmd->setBitfields(md->bitfieldString());
2352 newMmd->addSectionsToDefinition(root->anchors);
2353 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2354 newMmd->setBodyDef(md->getBodyDef());
2355 newMmd->setInitializer(md->initializer());
2356 newMmd->setRequiresClause(md->requiresClause());
2357 newMmd->setMaxInitLines(md->initializerLines());
2358 newMmd->setMemberGroupId(root->mGrpId);
2359 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2360 newMmd->setVhdlSpecifiers(md->getVhdlSpecifiers());
2361 newMmd->setLanguage(root->lang);
2362 newMmd->setId(root->id);
2363 MemberName *mn = Doxygen::functionNameLinkedMap->add(memName);
2364 mn->push_back(std::move(newMd));
2365#if 0 // insert an alias instead of a copy
2366 const MemberDef *md = toMemberDef(def);
2367 AUTO_TRACE_ADD("found member '{}' for name '{}'",md->qualifiedName(),root->name);
2368 auto aliasMd = createMemberDefAlias(nd,md);
2369 QCString aliasFullName = nd->qualifiedName()+"::"+aliasMd->localName();
2370 if (nd && aliasMd.get())
2371 {
2372 nd->insertMember(aliasMd.get());
2373 }
2374 if (fd && aliasMd.get())
2375 {
2376 fd->insertMember(aliasMd.get());
2377 }
2378 MemberName *mn = Doxygen::memberNameLinkedMap->add(aliasFullName);
2379 mn->push_back(std::move(aliasMd));
2380#endif
2381 }
2382 else if (def && def->definitionType()==Definition::TypeClass)
2383 {
2384 const ClassDef *cd = toClassDef(def);
2385 QCString copyFullName;
2386 if (nd==nullptr)
2387 {
2388 copyFullName = cd->localName();
2389 }
2390 else
2391 {
2392 copyFullName = nd->qualifiedName()+"::"+cd->localName();
2393 }
2394 if (Doxygen::classLinkedMap->find(copyFullName)==nullptr)
2395 {
2397 Doxygen::classLinkedMap->add(copyFullName,
2398 cd->deepCopy(copyFullName)));
2399 AUTO_TRACE_ADD("found class '{}' for name '{}' copy '{}' obj={}",cd->qualifiedName(),root->name,copyFullName,(void*)ncdm);
2400 g_usingClassMap[cd->qualifiedName().str()].push_back(ncdm);
2401 if (ncdm)
2402 {
2403 if (nd) ncdm->moveTo(nd);
2404 if ((!root->doc.isEmpty() || !root->brief.isEmpty())) // use docs at using statement
2405 {
2406 ncdm->setDocumentation(root->doc,root->docFile,root->docLine);
2407 ncdm->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2408 }
2409 else // use docs from used class
2410 {
2411 ncdm->setDocumentation(cd->documentation(),cd->docFile(),cd->docLine());
2413 }
2414 if (nd)
2415 {
2416 nd->addInnerCompound(ncdm);
2417 nd->addUsingDeclaration(ncdm);
2418 }
2419 if (fd)
2420 {
2421 if (ncdm) ncdm->setFileDef(fd);
2422 fd->insertClass(ncdm);
2423 fd->addUsingDeclaration(ncdm);
2424 }
2425 }
2426 }
2427#if 0 // insert an alias instead of a copy
2428 auto aliasCd = createClassDefAlias(nd,cd);
2429 QCString aliasFullName;
2430 if (nd==nullptr)
2431 {
2432 aliasFullName = aliasCd->localName();
2433 }
2434 else
2435 {
2436 aliasFullName = nd->qualifiedName()+"::"+aliasCd->localName();
2437 }
2438 AUTO_TRACE_ADD("found class '{}' for name '{}' aliasFullName='{}'",cd->qualifiedName(),root->name,aliasFullName);
2439 auto acd = Doxygen::classLinkedMap->add(aliasFullName,std::move(aliasCd));
2440 if (nd && acd)
2441 {
2442 nd->addInnerCompound(acd);
2443 }
2444 if (fd && acd)
2445 {
2446 fd->insertClass(acd);
2447 }
2448#endif
2449 }
2450 else if (scope)
2451 {
2452 AUTO_TRACE_ADD("no symbol with name '{}' in scope {}",root->name,scope->name());
2453 }
2454 }
2455 }
2456 for (const auto &e : root->children()) findUsingDeclImports(e.get());
2457}
2458
2459//----------------------------------------------------------------------
2460
2462{
2463 FileDefSet visitedFiles;
2464 // then recursively add using directives found in #include files
2465 // to files that have not been visited.
2466 for (const auto &fn : *Doxygen::inputNameLinkedMap)
2467 {
2468 for (const auto &fd : *fn)
2469 {
2470 //printf("----- adding using directives for file %s\n",qPrint(fd->name()));
2471 fd->addIncludedUsingDirectives(visitedFiles);
2472 }
2473 }
2474}
2475
2476//----------------------------------------------------------------------
2477
2479 const Entry *root,
2480 ClassDefMutable *cd,
2481 MemberType mtype,
2482 const QCString &type,
2483 const QCString &name,
2484 const QCString &args,
2485 bool fromAnnScope,
2486 MemberDef *fromAnnMemb,
2487 Protection prot,
2488 Relationship related)
2489{
2491 QCString scopeSeparator="::";
2492 SrcLangExt lang = cd->getLanguage();
2493 if (lang==SrcLangExt::Java || lang==SrcLangExt::CSharp)
2494 {
2495 qualScope = substitute(qualScope,"::",".");
2496 scopeSeparator=".";
2497 }
2498 AUTO_TRACE("class variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} ann={} init='{}'",
2499 root->fileName, type, qualScope, name, args, root->protection, mtype, lang, fromAnnScope, root->initializer.str());
2500
2501 QCString def;
2502 if (!type.isEmpty())
2503 {
2504 if (related!=Relationship::Member || mtype==MemberType::Friend || Config_getBool(HIDE_SCOPE_NAMES))
2505 {
2506 if (root->spec.isAlias()) // turn 'typedef B A' into 'using A'
2507 {
2508 if (lang==SrcLangExt::Python)
2509 {
2510 def="type "+name+args;
2511 }
2512 else
2513 {
2514 def="using "+name;
2515 }
2516 }
2517 else
2518 {
2519 def=type+" "+name+args;
2520 }
2521 }
2522 else
2523 {
2524 if (root->spec.isAlias()) // turn 'typedef B C::A' into 'using C::A'
2525 {
2526 if (lang==SrcLangExt::Python)
2527 {
2528 def="type "+qualScope+scopeSeparator+name+args;
2529 }
2530 else
2531 {
2532 def="using "+qualScope+scopeSeparator+name;
2533 }
2534 }
2535 else
2536 {
2537 def=type+" "+qualScope+scopeSeparator+name+args;
2538 }
2539 }
2540 }
2541 else
2542 {
2543 if (Config_getBool(HIDE_SCOPE_NAMES))
2544 {
2545 def=name+args;
2546 }
2547 else
2548 {
2549 def=qualScope+scopeSeparator+name+args;
2550 }
2551 }
2552 def.stripPrefix("static ");
2553
2554 // see if the member is already found in the same scope
2555 // (this may be the case for a static member that is initialized
2556 // outside the class)
2558 if (mn)
2559 {
2560 for (const auto &imd : *mn)
2561 {
2562 //printf("md->getClassDef()=%p cd=%p type=[%s] md->typeString()=[%s]\n",
2563 // md->getClassDef(),cd,qPrint(type),md->typeString());
2564 MemberDefMutable *md = toMemberDefMutable(imd.get());
2565 if (md &&
2566 md->getClassDef()==cd &&
2567 ((lang==SrcLangExt::Python && type.isEmpty() && !md->typeString().isEmpty()) ||
2569 // member already in the scope
2570 {
2571
2572 if (root->lang==SrcLangExt::ObjC &&
2573 root->mtype==MethodTypes::Property &&
2575 { // Objective-C 2.0 property
2576 // turn variable into a property
2577 md->setProtection(root->protection);
2579 }
2580 addMemberDocs(root,md,def,nullptr,FALSE,root->spec);
2581 AUTO_TRACE_ADD("Member already found!");
2582 return md;
2583 }
2584 }
2585 }
2586
2587 QCString fileName = root->fileName;
2588 if (fileName.isEmpty() && root->tagInfo())
2589 {
2590 fileName = root->tagInfo()->tagName;
2591 }
2592
2593 // new member variable, typedef or enum value
2594 auto md = createMemberDef(
2595 fileName,root->startLine,root->startColumn,
2596 type,name,args,root->exception,
2597 prot,Specifier::Normal,root->isStatic,related,
2598 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2599 ArgumentList(), root->metaData);
2600 auto mmd = toMemberDefMutable(md.get());
2601 mmd->setTagInfo(root->tagInfo());
2602 mmd->setMemberClass(cd); // also sets outer scope (i.e. getOuterScope())
2603 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2604 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2605 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2606 mmd->setDefinition(def);
2607 mmd->setBitfields(root->bitfields);
2608 mmd->addSectionsToDefinition(root->anchors);
2609 mmd->setFromAnonymousScope(fromAnnScope);
2610 mmd->setFromAnonymousMember(fromAnnMemb);
2611 //md->setIndentDepth(indentDepth);
2612 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2613 mmd->setInitializer(root->initializer.str());
2614 mmd->setMaxInitLines(root->initLines);
2615 mmd->setMemberGroupId(root->mGrpId);
2616 mmd->setMemberSpecifiers(root->spec);
2617 mmd->setVhdlSpecifiers(root->vhdlSpec);
2618 mmd->setReadAccessor(root->read);
2619 mmd->setWriteAccessor(root->write);
2621 mmd->setHidden(root->hidden);
2622 mmd->setArtificial(root->artificial);
2623 mmd->setLanguage(root->lang);
2624 mmd->setId(root->id);
2625 addMemberToGroups(root,md.get());
2627 mmd->setBodyDef(root->fileDef());
2628 mmd->addQualifiers(root->qualifiers);
2629
2630 AUTO_TRACE_ADD("Adding new member to class '{}'",cd->name());
2631 cd->insertMember(md.get());
2632 mmd->setRefItems(root->sli);
2633 mmd->setRequirementReferences(root->rqli);
2634
2635 cd->insertUsedFile(root->fileDef());
2636 root->markAsProcessed();
2637
2638 if (mtype==MemberType::Typedef)
2639 {
2640 resolveTemplateInstanceInType(root,cd,md.get());
2641 }
2642
2643 // add the member to the global list
2644 MemberDef *result = md.get();
2645 mn = Doxygen::memberNameLinkedMap->add(name);
2646 mn->push_back(std::move(md));
2647
2648 return result;
2649}
2650
2651//----------------------------------------------------------------------
2652
2654 const Entry *root,
2655 MemberType mtype,
2656 const QCString &scope,
2657 const QCString &type,
2658 const QCString &name,
2659 const QCString &args,
2660 bool fromAnnScope,
2661 MemberDef *fromAnnMemb)
2662{
2663 AUTO_TRACE("global variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} init='{}'",
2664 root->fileName, type, scope, name, args, root->protection, mtype, root->lang, root->initializer.str());
2665
2666 FileDef *fd = root->fileDef();
2667
2668 // see if we have a typedef that should hide a struct or union
2669 if (mtype==MemberType::Typedef && Config_getBool(TYPEDEF_HIDES_STRUCT))
2670 {
2671 QCString ttype = type;
2672 ttype.stripPrefix("typedef ");
2673 if (ttype.stripPrefix("struct ") || ttype.stripPrefix("union "))
2674 {
2675 static const reg::Ex re(R"(\a\w*)");
2676 reg::Match match;
2677 const std::string &typ = ttype.str();
2678 if (reg::search(typ,match,re))
2679 {
2680 QCString typeValue = match.str();
2681 ClassDefMutable *cd = getClassMutable(typeValue);
2682 if (cd)
2683 {
2684 // this typedef should hide compound name cd, so we
2685 // change the name that is displayed from cd.
2686 cd->setClassName(name);
2687 cd->setDocumentation(root->doc,root->docFile,root->docLine);
2688 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2689 return nullptr;
2690 }
2691 }
2692 }
2693 }
2694
2695 // see if the function is inside a namespace
2696 NamespaceDefMutable *nd = nullptr;
2697 if (!scope.isEmpty())
2698 {
2699 if (scope.find('@')!=-1) return nullptr; // anonymous scope!
2700 nd = getResolvedNamespaceMutable(scope);
2701 }
2702 QCString def;
2703
2704 // determine the definition of the global variable
2705 if (nd && !nd->isAnonymous() &&
2706 !Config_getBool(HIDE_SCOPE_NAMES)
2707 )
2708 // variable is inside a namespace, so put the scope before the name
2709 {
2710 SrcLangExt lang = nd->getLanguage();
2712
2713 if (!type.isEmpty())
2714 {
2715 if (root->spec.isAlias()) // turn 'typedef B NS::A' into 'using NS::A'
2716 {
2717 if (lang==SrcLangExt::Python)
2718 {
2719 def="type "+nd->name()+sep+name+args;
2720 }
2721 else
2722 {
2723 def="using "+nd->name()+sep+name;
2724 }
2725 }
2726 else // normal member
2727 {
2728 def=type+" "+nd->name()+sep+name+args;
2729 }
2730 }
2731 else
2732 {
2733 def=nd->name()+sep+name+args;
2734 }
2735 }
2736 else
2737 {
2738 if (!type.isEmpty() && !root->name.isEmpty())
2739 {
2740 if (name.at(0)=='@') // dummy variable representing anonymous union
2741 {
2742 def=type;
2743 }
2744 else
2745 {
2746 if (root->spec.isAlias()) // turn 'typedef B A' into 'using A'
2747 {
2748 if (root->lang==SrcLangExt::Python)
2749 {
2750 def="type "+root->name+args;
2751 }
2752 else
2753 {
2754 def="using "+root->name;
2755 }
2756 }
2757 else // normal member
2758 {
2759 def=type+" "+name+args;
2760 }
2761 }
2762 }
2763 else
2764 {
2765 def=name+args;
2766 }
2767 }
2768 def.stripPrefix("static ");
2769
2771 if (mn)
2772 {
2773 //QCString nscope=removeAnonymousScopes(scope);
2774 //NamespaceDef *nd=nullptr;
2775 //if (!nscope.isEmpty())
2776 if (!scope.isEmpty())
2777 {
2778 nd = getResolvedNamespaceMutable(scope);
2779 }
2780 for (const auto &imd : *mn)
2781 {
2782 MemberDefMutable *md = toMemberDefMutable(imd.get());
2783 if (md &&
2784 ((nd==nullptr && md->getNamespaceDef()==nullptr && md->getFileDef() &&
2785 root->fileName==md->getFileDef()->absFilePath()
2786 ) // both variable names in the same file
2787 || (nd!=nullptr && md->getNamespaceDef()==nd) // both in same namespace
2788 )
2789 && !md->isDefine() // function style #define's can be "overloaded" by typedefs or variables
2790 && !md->isEnumerate() // in C# an enum value and enum can have the same name
2791 )
2792 // variable already in the scope
2793 {
2794 bool isPHPArray = md->getLanguage()==SrcLangExt::PHP &&
2795 md->argsString()!=args &&
2796 args.find('[')!=-1;
2797 bool staticsInDifferentFiles =
2798 root->isStatic && md->isStatic() &&
2799 root->fileName!=md->getDefFileName();
2800
2801 if (md->getFileDef() &&
2802 !isPHPArray && // not a php array
2803 !staticsInDifferentFiles
2804 )
2805 // not a php array variable
2806 {
2807 AUTO_TRACE_ADD("variable already found: scope='{}'",md->getOuterScope()->name());
2808 addMemberDocs(root,md,def,nullptr,FALSE,root->spec);
2809 md->setRefItems(root->sli);
2810 md->setRequirementReferences(root->rqli);
2811 // if md is a variable forward declaration and root is the definition that
2812 // turn md into the definition
2813 if (!root->explicitExternal && md->isExternal())
2814 {
2815 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
2817 }
2818 // if md is the definition and root point at a declaration, then add the
2819 // declaration info
2820 else if (root->explicitExternal && !md->isExternal())
2821 {
2822 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
2823 }
2824 return md;
2825 }
2826 }
2827 }
2828 }
2829
2830 QCString fileName = root->fileName;
2831 if (fileName.isEmpty() && root->tagInfo())
2832 {
2833 fileName = root->tagInfo()->tagName;
2834 }
2835
2836 AUTO_TRACE_ADD("new variable, namespace='{}'",nd?nd->name():QCString("<global>"));
2837 // new global variable, enum value or typedef
2838 auto md = createMemberDef(
2839 fileName,root->startLine,root->startColumn,
2840 type,name,args,QCString(),
2841 root->protection, Specifier::Normal,root->isStatic,Relationship::Member,
2842 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2843 root->argList, root->metaData);
2844 auto mmd = toMemberDefMutable(md.get());
2845 mmd->setTagInfo(root->tagInfo());
2846 mmd->setMemberSpecifiers(root->spec);
2847 mmd->setVhdlSpecifiers(root->vhdlSpec);
2848 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2849 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2850 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2851 mmd->addSectionsToDefinition(root->anchors);
2852 mmd->setFromAnonymousScope(fromAnnScope);
2853 mmd->setFromAnonymousMember(fromAnnMemb);
2854 mmd->setInitializer(root->initializer.str());
2855 mmd->setMaxInitLines(root->initLines);
2856 mmd->setMemberGroupId(root->mGrpId);
2857 mmd->setDefinition(def);
2858 mmd->setLanguage(root->lang);
2859 mmd->setId(root->id);
2861 mmd->setExplicitExternal(root->explicitExternal,fileName,root->startLine,root->startColumn);
2862 mmd->addQualifiers(root->qualifiers);
2863 //md->setOuterScope(fd);
2864 if (!root->explicitExternal)
2865 {
2866 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2867 mmd->setBodyDef(fd);
2868 }
2869 addMemberToGroups(root,md.get());
2871
2872 mmd->setRefItems(root->sli);
2873 mmd->setRequirementReferences(root->rqli);
2874 if (nd && !nd->isAnonymous())
2875 {
2876 mmd->setNamespace(nd);
2877 nd->insertMember(md.get());
2878 }
2879
2880 // add member to the file (we do this even if we have already inserted
2881 // it into the namespace.
2882 if (fd)
2883 {
2884 mmd->setFileDef(fd);
2885 fd->insertMember(md.get());
2886 }
2887
2888 root->markAsProcessed();
2889
2890 if (mtype==MemberType::Typedef)
2891 {
2892 resolveTemplateInstanceInType(root,nd,md.get());
2893 }
2894
2895 // add member definition to the list of globals
2896 MemberDef *result = md.get();
2897 mn = Doxygen::functionNameLinkedMap->add(name);
2898 mn->push_back(std::move(md));
2899
2900
2901
2902 return result;
2903}
2904
2905/*! See if the return type string \a type is that of a function pointer
2906 * \returns -1 if this is not a function pointer variable or
2907 * the index at which the closing brace of (...*name) was found.
2908 */
2909static int findFunctionPtr(const std::string &type,SrcLangExt lang, int *pLength=nullptr)
2910{
2911 AUTO_TRACE("type='{}' lang={}",type,lang);
2912 if (lang == SrcLangExt::Fortran || lang == SrcLangExt::VHDL)
2913 {
2914 return -1; // Fortran and VHDL do not have function pointers
2915 }
2916
2917 static const reg::Ex re(R"(\‍([^)]*[*&^][^)]*\))");
2918 reg::Match match;
2919 size_t i=std::string::npos;
2920 size_t l=0;
2921 if (reg::search(type,match,re)) // contains (...*...) or (...&...) or (...^...)
2922 {
2923 i = match.position();
2924 l = match.length();
2925 }
2926 if (i!=std::string::npos)
2927 {
2928 size_t di = type.find("decltype(");
2929 if (di!=std::string::npos && di<i)
2930 {
2931 i = std::string::npos;
2932 }
2933 }
2934 size_t bb=type.find('<');
2935 size_t be=type.rfind('>');
2936 bool templFp = false;
2937 if (be!=std::string::npos) {
2938 size_t cc_ast = type.find("::*");
2939 size_t cc_amp = type.find("::&");
2940 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>::*)'
2941 }
2942
2943 if (!type.empty() && // return type is non-empty
2944 i!=std::string::npos && // contains (...*...)
2945 type.find("operator")==std::string::npos && // not an operator
2946 (type.find(")(")==std::string::npos || type.find("typedef ")!=std::string::npos) &&
2947 // not a function pointer return type
2948 (!(bb<i && i<be) || templFp) // bug665855: avoid treating "typedef A<void (T*)> type" as a function pointer
2949 )
2950 {
2951 if (pLength) *pLength=static_cast<int>(l);
2952 //printf("findFunctionPtr=%d\n",(int)i);
2953 AUTO_TRACE_EXIT("result={}",i);
2954 return static_cast<int>(i);
2955 }
2956 else
2957 {
2958 //printf("findFunctionPtr=%d\n",-1);
2959 AUTO_TRACE_EXIT("result=-1");
2960 return -1;
2961 }
2962}
2963
2964//--------------------------------------------------------------------------------------
2965
2966/*! Returns TRUE iff \a type is a class within scope \a context.
2967 * Used to detect variable declarations that look like function prototypes.
2968 */
2969static bool isVarWithConstructor(const Entry *root)
2970{
2971 bool result = false;
2972 bool typeIsClass = false;
2973 bool typePtrType = false;
2974 QCString type;
2975 Definition *ctx = nullptr;
2976 FileDef *fd = root->fileDef();
2977 SymbolResolver resolver(fd);
2978
2979 AUTO_TRACE("isVarWithConstructor({})",root->name);
2980 if (root->parent()->section.isCompound())
2981 { // inside a class
2982 result=FALSE;
2983 AUTO_TRACE_EXIT("inside class: result={}",result);
2984 return result;
2985 }
2986 else if ((fd != nullptr) && (fd->name().endsWith(".c") || fd->name().endsWith(".h")))
2987 { // inside a .c file
2988 result=FALSE;
2989 AUTO_TRACE_EXIT("inside C file: result={}",result);
2990 return result;
2991 }
2992 if (root->type.isEmpty())
2993 {
2994 result=FALSE;
2995 AUTO_TRACE_EXIT("no type: result={}",result);
2996 return result;
2997 }
2998 if (!root->parent()->name.isEmpty())
2999 {
3000 ctx=Doxygen::namespaceLinkedMap->find(root->parent()->name);
3001 }
3002 type = root->type;
3003 // remove qualifiers
3004 findAndRemoveWord(type,"const");
3005 findAndRemoveWord(type,"static");
3006 findAndRemoveWord(type,"volatile");
3007 typePtrType = type.find('*')!=-1 || type.find('&')!=-1;
3008 if (!typePtrType)
3009 {
3010 typeIsClass = resolver.resolveClass(ctx,type)!=nullptr;
3011 int ti=0;
3012 if (!typeIsClass && (ti=type.find('<'))!=-1)
3013 {
3014 typeIsClass=resolver.resolveClass(ctx,type.left(ti))!=nullptr;
3015 }
3016 }
3017 if (typeIsClass) // now we still have to check if the arguments are
3018 // types or values. Since we do not have complete type info
3019 // we need to rely on heuristics :-(
3020 {
3021 if (root->argList.empty())
3022 {
3023 result=FALSE; // empty arg list -> function prototype.
3024 AUTO_TRACE_EXIT("empty arg list: result={}",result);
3025 return result;
3026 }
3027 for (const Argument &a : root->argList)
3028 {
3029 static const reg::Ex initChars(R"([\d"'&*!^]+)");
3030 reg::Match match;
3031 if (!a.name.isEmpty() || !a.defval.isEmpty())
3032 {
3033 std::string name = a.name.str();
3034 if (reg::search(name,match,initChars) && match.position()==0)
3035 {
3036 result=TRUE;
3037 }
3038 else
3039 {
3040 result=FALSE; // arg has (type,name) pair -> function prototype
3041 }
3042 AUTO_TRACE_EXIT("function prototype: result={}",result);
3043 return result;
3044 }
3045 if (!a.type.isEmpty() &&
3046 (a.type.at(a.type.length()-1)=='*' ||
3047 a.type.at(a.type.length()-1)=='&'))
3048 // type ends with * or & => pointer or reference
3049 {
3050 result=FALSE;
3051 AUTO_TRACE_EXIT("pointer or reference: result={}",result);
3052 return result;
3053 }
3054 if (a.type.isEmpty() || resolver.resolveClass(ctx,a.type)!=nullptr)
3055 {
3056 result=FALSE; // arg type is a known type
3057 AUTO_TRACE_EXIT("known type: result={}",result);
3058 return result;
3059 }
3060 if (checkIfTypedef(ctx,fd,a.type))
3061 {
3062 result=FALSE; // argument is a typedef
3063 AUTO_TRACE_EXIT("typedef: result={}",result);
3064 return result;
3065 }
3066 std::string atype = a.type.str();
3067 if (reg::search(atype,match,initChars) && match.position()==0)
3068 {
3069 result=TRUE; // argument type starts with typical initializer char
3070 AUTO_TRACE_EXIT("argument with init char: result={}",result);
3071 return result;
3072 }
3073 std::string resType=resolveTypeDef(ctx,a.type).str();
3074 if (resType.empty()) resType=atype;
3075 static const reg::Ex idChars(R"(\a\w*)");
3076 if (reg::search(resType,match,idChars) && match.position()==0) // resType starts with identifier
3077 {
3078 resType=match.str();
3079 if (resType=="int" || resType=="long" ||
3080 resType=="float" || resType=="double" ||
3081 resType=="char" || resType=="void" ||
3082 resType=="signed" || resType=="unsigned" ||
3083 resType=="const" || resType=="volatile" )
3084 {
3085 result=FALSE; // type keyword -> function prototype
3086 AUTO_TRACE_EXIT("type keyword: result={}",result);
3087 return result;
3088 }
3089 }
3090 }
3091 result=TRUE;
3092 }
3093
3094 AUTO_TRACE_EXIT("end: result={}",result);
3095 return result;
3096}
3097
3098//--------------------------------------------------------------------------------------
3099
3100/*! Searches for the end of a template in prototype \a s starting from
3101 * character position \a startPos. If the end was found the position
3102 * of the closing > is returned, otherwise -1 is returned.
3103 *
3104 * Handles exotic cases such as
3105 * \code
3106 * Class<(id<0)>
3107 * Class<bits<<2>
3108 * Class<"<">
3109 * Class<'<'>
3110 * Class<(")<")>
3111 * \endcode
3112 */
3113static int findEndOfTemplate(const QCString &s,size_t startPos)
3114{
3115 // locate end of template
3116 size_t e=startPos;
3117 int brCount=1;
3118 int roundCount=0;
3119 size_t len = s.length();
3120 bool insideString=FALSE;
3121 bool insideChar=FALSE;
3122 char pc = 0;
3123 while (e<len && brCount!=0)
3124 {
3125 char c=s.at(e);
3126 switch(c)
3127 {
3128 case '<':
3129 if (!insideString && !insideChar)
3130 {
3131 if (e<len-1 && s.at(e+1)=='<')
3132 e++;
3133 else if (roundCount==0)
3134 brCount++;
3135 }
3136 break;
3137 case '>':
3138 if (!insideString && !insideChar)
3139 {
3140 if (e<len-1 && s.at(e+1)=='>')
3141 e++;
3142 else if (roundCount==0)
3143 brCount--;
3144 }
3145 break;
3146 case '(':
3147 if (!insideString && !insideChar)
3148 roundCount++;
3149 break;
3150 case ')':
3151 if (!insideString && !insideChar)
3152 roundCount--;
3153 break;
3154 case '"':
3155 if (!insideChar)
3156 {
3157 if (insideString && pc!='\\')
3158 insideString=FALSE;
3159 else
3160 insideString=TRUE;
3161 }
3162 break;
3163 case '\'':
3164 if (!insideString)
3165 {
3166 if (insideChar && pc!='\\')
3167 insideChar=FALSE;
3168 else
3169 insideChar=TRUE;
3170 }
3171 break;
3172 }
3173 pc = c;
3174 e++;
3175 }
3176 return brCount==0 ? static_cast<int>(e) : -1;
3177}
3178
3179//--------------------------------------------------------------------------------------
3180
3181static void addVariable(const Entry *root,int isFuncPtr=-1)
3182{
3183 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
3184
3185 AUTO_TRACE("VARIABLE_SEC: type='{}' name='{}' args='{}' bodyLine={} endBodyLine={} mGrpId={} relates='{}'",
3186 root->type, root->name, root->args, root->bodyLine, root->endBodyLine, root->mGrpId, root->relates);
3187 //printf("root->parent->name=%s\n",qPrint(root->parent->name));
3188
3189 QCString type = root->type;
3190 QCString name = root->name;
3191 QCString args = root->args;
3192 if (type.isEmpty() && name.find("operator")==-1 &&
3193 (name.find('*')!=-1 || name.find('&')!=-1))
3194 {
3195 // recover from parse error caused by redundant braces
3196 // like in "int *(var[10]);", which is parsed as
3197 // type="" name="int *" args="(var[10])"
3198
3199 type=name;
3200 std::string sargs = args.str();
3201 static const reg::Ex reName(R"(\a\w*)");
3202 reg::Match match;
3203 if (reg::search(sargs,match,reName))
3204 {
3205 name = match.str(); // e.g. 'var' in '(var[10])'
3206 sargs = match.suffix().str(); // e.g. '[10]) in '(var[10])'
3207 size_t j = sargs.find(')');
3208 if (j!=std::string::npos) args=sargs.substr(0,j); // extract, e.g '[10]' from '[10])'
3209 }
3210 }
3211 else
3212 {
3213 int i=isFuncPtr;
3214 if (i==-1 && (root->spec.isAlias())==0) i=findFunctionPtr(type.str(),root->lang); // for typedefs isFuncPtr is not yet set
3215 AUTO_TRACE_ADD("functionPtr={}",i!=-1?"yes":"no");
3216 if (i>=0) // function pointer
3217 {
3218 int ai = type.find('[',i);
3219 if (ai>i) // function pointer array
3220 {
3221 args.prepend(type.right(type.length()-ai));
3222 type=type.left(ai);
3223 }
3224 else if (type.find(')',i)!=-1) // function ptr, not variable like "int (*bla)[10]"
3225 {
3226 type=type.left(type.length()-1);
3227 args.prepend(") ");
3228 }
3229 }
3230 }
3231 AUTO_TRACE_ADD("after correction: type='{}' name='{}' args='{}'",type,name,args);
3232
3233 QCString scope;
3234 name=removeRedundantWhiteSpace(name);
3235
3236 // find the scope of this variable
3237 int index = computeQualifiedIndex(name);
3238 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3239 // grouped members are stored with full scope
3240 {
3241 buildScopeFromQualifiedName(name.left(index+2),root->lang,root->tagInfo());
3242 scope=name.left(index);
3243 name=name.mid(index+2);
3244 }
3245 else
3246 {
3247 Entry *p = root->parent();
3248 while (p->section.isScope())
3249 {
3250 QCString scopeName = p->name;
3251 if (!scopeName.isEmpty())
3252 {
3253 scope.prepend(scopeName);
3254 break;
3255 }
3256 p=p->parent();
3257 }
3258 }
3259
3260 QCString type_s = type;
3261 type=type.stripWhiteSpace();
3262 ClassDefMutable *cd=nullptr;
3263 bool isRelated=FALSE;
3264 bool isMemberOf=FALSE;
3265
3266 QCString classScope=stripAnonymousNamespaceScope(scope);
3267 if (root->lang==SrcLangExt::CSharp)
3268 {
3269 classScope=mangleCSharpGenericName(classScope);
3270 }
3271 else
3272 {
3273 classScope=stripTemplateSpecifiersFromScope(classScope,FALSE);
3274 }
3275 QCString annScopePrefix=scope.left(scope.length()-classScope.length());
3276
3277
3278 // Look for last :: not part of template specifier
3279 int p=-1;
3280 for (size_t i=0;i<name.length()-1;i++)
3281 {
3282 if (name[i]==':' && name[i+1]==':')
3283 {
3284 p=static_cast<int>(i);
3285 }
3286 else if (name[i]=='<') // skip over template parts,
3287 // i.e. A::B<C::D> => p=1 and
3288 // A<B::C>::D => p=8
3289 {
3290 int e = findEndOfTemplate(name,i+1);
3291 if (e!=-1) i=static_cast<int>(e);
3292 }
3293 }
3294
3295 if (p!=-1) // found it
3296 {
3297 if (type=="friend class" || type=="friend struct" ||
3298 type=="friend union")
3299 {
3300 cd=getClassMutable(scope);
3301 if (cd)
3302 {
3303 addVariableToClass(root, // entry
3304 cd, // class to add member to
3305 MemberType::Friend, // type of member
3306 type, // type value as string
3307 name, // name of the member
3308 args, // arguments as string
3309 FALSE, // from Anonymous scope
3310 nullptr, // anonymous member
3311 Protection::Public, // protection
3312 Relationship::Member // related to a class
3313 );
3314 }
3315 }
3316 if (root->bodyLine!=-1 && root->endBodyLine!=-1) // store the body location for later use
3317 {
3318 Doxygen::staticInitMap.emplace(name.str(),BodyInfo{root->startLine,root->bodyLine,root->endBodyLine});
3319 }
3320
3321
3322 AUTO_TRACE_ADD("static variable {} body=[{}..{}]",name,root->bodyLine,root->endBodyLine);
3323 return; /* skip this member, because it is a
3324 * static variable definition (always?), which will be
3325 * found in a class scope as well, but then we know the
3326 * correct protection level, so only then it will be
3327 * inserted in the correct list!
3328 */
3329 }
3330
3332 if (type=="@")
3334 else if (type_s.startsWith("typedef "))
3335 mtype=MemberType::Typedef;
3336 else if (type_s.startsWith("friend "))
3337 mtype=MemberType::Friend;
3338 else if (root->mtype==MethodTypes::Property)
3340 else if (root->mtype==MethodTypes::Event)
3341 mtype=MemberType::Event;
3342 else if (type.find("sequence<") != -1)
3343 mtype=sliceOpt ? MemberType::Sequence : MemberType::Typedef;
3344 else if (type.find("dictionary<") != -1)
3346
3347 if (!root->relates.isEmpty()) // related variable
3348 {
3349 isRelated=TRUE;
3350 isMemberOf=(root->relatesType==RelatesType::MemberOf);
3351 if (getClass(root->relates)==nullptr && !scope.isEmpty())
3352 scope=mergeScopes(scope,root->relates);
3353 else
3354 scope=root->relates;
3355 }
3356
3357 cd=getClassMutable(scope);
3358 if (cd==nullptr && classScope!=scope) cd=getClassMutable(classScope);
3359 if (cd)
3360 {
3361 MemberDef *md=nullptr;
3362
3363 // if cd is an anonymous (=tag less) scope we insert the member
3364 // into a non-anonymous parent scope as well. This is needed to
3365 // be able to refer to it using \var or \fn
3366
3367 //int indentDepth=0;
3368 int si=scope.find('@');
3369 //int anonyScopes = 0;
3370 //bool added=FALSE;
3371
3372 bool inlineSimpleStructs = Config_getBool(INLINE_SIMPLE_STRUCTS);
3373 Relationship relationship = isMemberOf ? Relationship::Foreign :
3374 isRelated ? Relationship::Related :
3375 Relationship::Member ;
3376 if (si!=-1 && !inlineSimpleStructs) // anonymous scope or type
3377 {
3378 QCString pScope;
3379 ClassDefMutable *pcd=nullptr;
3380 pScope = scope.left(std::max(si-2,0)); // scope without tag less parts
3381 if (!pScope.isEmpty())
3382 pScope.prepend(annScopePrefix);
3383 else if (annScopePrefix.length()>2)
3384 pScope=annScopePrefix.left(annScopePrefix.length()-2);
3385 if (name.at(0)!='@')
3386 {
3387 if (!pScope.isEmpty() && (pcd=getClassMutable(pScope)))
3388 {
3389 AUTO_TRACE_ADD("Adding anonymous member to scope '{}'",pScope);
3390 md=addVariableToClass(root, // entry
3391 pcd, // class to add member to
3392 mtype, // member type
3393 type, // type value as string
3394 name, // member name
3395 args, // arguments as string
3396 TRUE, // from anonymous scope
3397 nullptr, // from anonymous member
3398 root->protection,
3399 relationship
3400 );
3401 //added=TRUE;
3402 }
3403 else // anonymous scope inside namespace or file => put variable in the global scope
3404 {
3405 if (mtype==MemberType::Variable)
3406 {
3407 AUTO_TRACE_ADD("Adding anonymous member to global scope '{}'");
3408 md=addVariableToFile(root,mtype,pScope,type,name,args,TRUE,nullptr);
3409 }
3410 //added=TRUE;
3411 }
3412 }
3413 }
3414
3415 addVariableToClass(root, // entry
3416 cd, // class to add member to
3417 mtype, // member type
3418 type, // type value as string
3419 name, // name of the member
3420 args, // arguments as string
3421 FALSE, // from anonymous scope
3422 md, // from anonymous member
3423 root->protection,
3424 relationship
3425 );
3426 }
3427 else if (!name.isEmpty()) // global variable
3428 {
3429 addVariableToFile(root,mtype,scope,type,name,args,FALSE,/*nullptr,*/nullptr);
3430 }
3431
3432}
3433
3434//----------------------------------------------------------------------
3435// Searches the Entry tree for typedef documentation sections.
3436// If found they are stored in their class or in the global list.
3437static void buildTypedefList(const Entry *root)
3438{
3439 //printf("buildVarList(%s)\n",qPrint(rootNav->name()));
3440 if (!root->name.isEmpty() &&
3441 root->section.isVariable() &&
3442 root->type.find("typedef ")!=-1 // its a typedef
3443 )
3444 {
3445 AUTO_TRACE();
3447 QCString scope;
3448 int index = computeQualifiedIndex(rname);
3449 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3450 // grouped members are stored with full scope
3451 {
3452 buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
3453 scope=rname.left(index);
3454 rname=rname.mid(index+2);
3455 }
3456 else
3457 {
3458 scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
3459 }
3462 MemberName *mn = Doxygen::functionNameLinkedMap->find(rname);
3463 bool found=false;
3464 if (mn) // symbol with the same name already found
3465 {
3466 for (auto &imd : *mn)
3467 {
3468 if (!imd->isTypedef())
3469 continue;
3470
3471 QCString rtype = root->type;
3472 rtype.stripPrefix("typedef ");
3473
3474 // merge the typedefs only if they're not both grouped, and both are
3475 // either part of the same class, part of the same namespace, or both
3476 // are global (i.e., neither in a class or a namespace)
3477 bool notBothGrouped = root->groups.empty() || imd->getGroupDef()==nullptr; // see example #100
3478 bool bothSameScope = (!cd && !nd) || (cd && imd->getClassDef() == cd) || (nd && imd->getNamespaceDef() == nd);
3479 //printf("imd->isTypedef()=%d imd->typeString()=%s root->type=%s\n",imd->isTypedef(),
3480 // qPrint(imd->typeString()),qPrint(root->type));
3481 if (notBothGrouped && bothSameScope && imd->typeString()==rtype)
3482 {
3483 MemberDefMutable *md = toMemberDefMutable(imd.get());
3484 if (md)
3485 {
3486 md->setDocumentation(root->doc,root->docFile,root->docLine);
3488 md->setDocsForDefinition(!root->proto);
3489 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3491 md->setRefItems(root->sli);
3492 md->setRequirementReferences(root->rqli);
3493 md->addQualifiers(root->qualifiers);
3494
3495 // merge ingroup specifiers
3496 if (md->getGroupDef()==nullptr && !root->groups.empty())
3497 {
3498 addMemberToGroups(root,md);
3499 }
3500 else if (md->getGroupDef()!=nullptr && root->groups.empty())
3501 {
3502 //printf("existing member is grouped, new member not\n");
3503 }
3504 else if (md->getGroupDef()!=nullptr && !root->groups.empty())
3505 {
3506 //printf("both members are grouped\n");
3507 }
3508 found=true;
3509 break;
3510 }
3511 }
3512 }
3513 }
3514 if (found)
3515 {
3516 AUTO_TRACE_ADD("typedef '{}' already found",rname);
3517 // mark the entry as processed, as we copied everything from it elsewhere
3518 // also, otherwise, due to containing `typedef` it may later get treated
3519 // as a function typedef in filterMemberDocumentation, which is incorrect
3520 root->markAsProcessed();
3521 }
3522 else
3523 {
3524 AUTO_TRACE_ADD("new typedef '{}'",rname);
3525 addVariable(root);
3526 }
3527
3528 }
3529 for (const auto &e : root->children())
3530 if (!e->section.isEnum())
3531 buildTypedefList(e.get());
3532}
3533
3534//----------------------------------------------------------------------
3535// Searches the Entry tree for sequence documentation sections.
3536// If found they are stored in the global list.
3537static void buildSequenceList(const Entry *root)
3538{
3539 if (!root->name.isEmpty() &&
3540 root->section.isVariable() &&
3541 root->type.find("sequence<")!=-1 // it's a sequence
3542 )
3543 {
3544 AUTO_TRACE();
3545 addVariable(root);
3546 }
3547 for (const auto &e : root->children())
3548 if (!e->section.isEnum())
3549 buildSequenceList(e.get());
3550}
3551
3552//----------------------------------------------------------------------
3553// Searches the Entry tree for dictionary documentation sections.
3554// If found they are stored in the global list.
3555static void buildDictionaryList(const Entry *root)
3556{
3557 if (!root->name.isEmpty() &&
3558 root->section.isVariable() &&
3559 root->type.find("dictionary<")!=-1 // it's a dictionary
3560 )
3561 {
3562 AUTO_TRACE();
3563 addVariable(root);
3564 }
3565 for (const auto &e : root->children())
3566 if (!e->section.isEnum())
3567 buildDictionaryList(e.get());
3568}
3569
3570//----------------------------------------------------------------------
3571// Searches the Entry tree for Variable documentation sections.
3572// If found they are stored in their class or in the global list.
3573
3574static void buildVarList(const Entry *root)
3575{
3576 //printf("buildVarList(%s) section=%08x\n",qPrint(rootNav->name()),rootNav->section());
3577 int isFuncPtr=-1;
3578 if (!root->name.isEmpty() &&
3579 (root->type.isEmpty() || g_compoundKeywords.find(root->type.str())==g_compoundKeywords.end()) &&
3580 (
3581 (root->section.isVariable() && // it's a variable
3582 root->type.find("typedef ")==-1 // and not a typedef
3583 ) ||
3584 (root->section.isFunction() && // or maybe a function pointer variable
3585 (isFuncPtr=findFunctionPtr(root->type.str(),root->lang))!=-1
3586 ) ||
3587 (root->section.isFunction() && // class variable initialized by constructor
3589 )
3590 )
3591 ) // documented variable
3592 {
3593 AUTO_TRACE();
3594 addVariable(root,isFuncPtr);
3595 }
3596 for (const auto &e : root->children())
3597 if (!e->section.isEnum())
3598 buildVarList(e.get());
3599}
3600
3601//----------------------------------------------------------------------
3602// Searches the Entry tree for Interface sections (UNO IDL only).
3603// If found they are stored in their service or in the global list.
3604//
3605
3607 const Entry *root,
3608 ClassDefMutable *cd,
3609 QCString const& rname)
3610{
3611 FileDef *fd = root->fileDef();
3612 enum MemberType type = root->section.isExportedInterface() ? MemberType::Interface : MemberType::Service;
3613 QCString fileName = root->fileName;
3614 if (fileName.isEmpty() && root->tagInfo())
3615 {
3616 fileName = root->tagInfo()->tagName;
3617 }
3618 auto md = createMemberDef(
3619 fileName, root->startLine, root->startColumn, root->type, rname,
3620 "", "", root->protection, root->virt, root->isStatic, Relationship::Member,
3621 type, ArgumentList(), root->argList, root->metaData);
3622 auto mmd = toMemberDefMutable(md.get());
3623 mmd->setTagInfo(root->tagInfo());
3624 mmd->setMemberClass(cd);
3625 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3626 mmd->setDocsForDefinition(false);
3627 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3628 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3629 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3630 mmd->setMemberSpecifiers(root->spec);
3631 mmd->setVhdlSpecifiers(root->vhdlSpec);
3632 mmd->setMemberGroupId(root->mGrpId);
3633 mmd->setTypeConstraints(root->typeConstr);
3634 mmd->setLanguage(root->lang);
3635 mmd->setBodyDef(fd);
3636 mmd->setFileDef(fd);
3637 mmd->addSectionsToDefinition(root->anchors);
3638 QCString const def = root->type + " " + rname;
3639 mmd->setDefinition(def);
3641 mmd->addQualifiers(root->qualifiers);
3642
3643 AUTO_TRACE("Interface member: fileName='{}' type='{}' name='{}' mtype='{}' prot={} virt={} state={} proto={} def='{}'",
3644 fileName,root->type,rname,type,root->protection,root->virt,root->isStatic,root->proto,def);
3645
3646 // add member to the class cd
3647 cd->insertMember(md.get());
3648 // also add the member as a "base" (to get nicer diagrams)
3649 // "optional" interface/service get Protected which turns into dashed line
3650 BaseInfo base(rname,
3651 root->spec.isOptional() ? Protection::Protected : Protection::Public, Specifier::Normal);
3652 TemplateNameMap templateNames;
3653 findClassRelation(root,cd,cd,&base,templateNames,DocumentedOnly,true) ||
3654 findClassRelation(root,cd,cd,&base,templateNames,Undocumented,true);
3655 // add file to list of used files
3656 cd->insertUsedFile(fd);
3657
3658 addMemberToGroups(root,md.get());
3660 root->markAsProcessed();
3661 mmd->setRefItems(root->sli);
3662 mmd->setRequirementReferences(root->rqli);
3663
3664 // add member to the global list of all members
3665 MemberName *mn = Doxygen::memberNameLinkedMap->add(rname);
3666 mn->push_back(std::move(md));
3667}
3668
3669static void buildInterfaceAndServiceList(const Entry *root)
3670{
3671 if (root->section.isExportedInterface() || root->section.isIncludedService())
3672 {
3673 AUTO_TRACE("Exported interface/included service: type='{}' scope='{}' name='{}' args='{}'"
3674 " relates='{}' relatesType='{}' file='{}' line={} bodyLine={} #tArgLists={}"
3675 " mGrpId={} spec={} proto={} docFile='{}'",
3676 root->type, root->parent()->name, root->name, root->args,
3677 root->relates, root->relatesType, root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(),
3678 root->mGrpId, root->spec, root->proto, root->docFile);
3679
3681
3682 if (!rname.isEmpty())
3683 {
3684 QCString scope = root->parent()->name;
3685 ClassDefMutable *cd = getClassMutable(scope);
3686 assert(cd);
3687 if (cd && ((ClassDef::Interface == cd->compoundType()) ||
3688 (ClassDef::Service == cd->compoundType()) ||
3690 {
3692 }
3693 else
3694 {
3695 assert(false); // was checked by scanner.l
3696 }
3697 }
3698 else if (rname.isEmpty())
3699 {
3700 warn(root->fileName,root->startLine,
3701 "Illegal member name found.");
3702 }
3703 }
3704 // can only have these in IDL anyway
3705 switch (root->lang)
3706 {
3707 case SrcLangExt::Unknown: // fall through (root node always is Unknown)
3708 case SrcLangExt::IDL:
3709 for (const auto &e : root->children()) buildInterfaceAndServiceList(e.get());
3710 break;
3711 default:
3712 return; // nothing to do here
3713 }
3714}
3715
3716
3717//----------------------------------------------------------------------
3718// Searches the Entry tree for Function sections.
3719// If found they are stored in their class or in the global list.
3720
3721static void addMethodToClass(const Entry *root,ClassDefMutable *cd,
3722 const QCString &rtype,const QCString &rname,const QCString &rargs,
3723 bool isFriend,
3724 Protection protection,bool stat,Specifier virt,TypeSpecifier spec,
3725 const QCString &relates
3726 )
3727{
3728 FileDef *fd=root->fileDef();
3729
3730 QCString type = rtype;
3731 QCString args = rargs;
3732
3734 name.stripPrefix("::");
3735
3737 if (isFriend) mtype=MemberType::Friend;
3738 else if (root->mtype==MethodTypes::Signal) mtype=MemberType::Signal;
3739 else if (root->mtype==MethodTypes::Slot) mtype=MemberType::Slot;
3740 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType::DCOP;
3741
3742 // strip redundant template specifier for constructors
3743 int i = -1;
3744 int j = -1;
3745 if ((fd==nullptr || fd->getLanguage()==SrcLangExt::Cpp) &&
3746 !name.startsWith("operator ") && // not operator
3747 (i=name.find('<'))!=-1 && // containing <
3748 (j=name.find('>'))!=-1 && // or >
3749 (j!=i+2 || name.at(i+1)!='=') // but not the C++20 spaceship operator <=>
3750 )
3751 {
3752 name=name.left(i);
3753 }
3754
3755 QCString fileName = root->fileName;
3756 if (fileName.isEmpty() && root->tagInfo())
3757 {
3758 fileName = root->tagInfo()->tagName;
3759 }
3760
3761 //printf("root->name='%s; args='%s' root->argList='%s'\n",
3762 // qPrint(root->name),qPrint(args),qPrint(argListToString(root->argList))
3763 // );
3764
3765 // adding class member
3766 Relationship relationship = relates.isEmpty() ? Relationship::Member :
3767 root->relatesType==RelatesType::MemberOf ? Relationship::Foreign :
3768 Relationship::Related ;
3769 auto md = createMemberDef(
3770 fileName,root->startLine,root->startColumn,
3771 type,name,args,root->exception,
3772 protection,virt,
3773 stat && root->relatesType!=RelatesType::MemberOf,
3774 relationship,
3775 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3776 root->argList, root->metaData);
3777 auto mmd = toMemberDefMutable(md.get());
3778 mmd->setTagInfo(root->tagInfo());
3779 mmd->setMemberClass(cd);
3780 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3781 mmd->setDocsForDefinition(!root->proto);
3782 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3783 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3784 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3785 mmd->setMemberSpecifiers(spec);
3786 mmd->setVhdlSpecifiers(root->vhdlSpec);
3787 mmd->setMemberGroupId(root->mGrpId);
3788 mmd->setTypeConstraints(root->typeConstr);
3789 mmd->setLanguage(root->lang);
3790 mmd->setRequiresClause(root->req);
3791 mmd->setId(root->id);
3792 mmd->setBodyDef(fd);
3793 mmd->setFileDef(fd);
3794 mmd->addSectionsToDefinition(root->anchors);
3795 QCString def;
3797 SrcLangExt lang = cd->getLanguage();
3798 QCString scopeSeparator=getLanguageSpecificSeparator(lang);
3799 if (scopeSeparator!="::")
3800 {
3801 qualScope = substitute(qualScope,"::",scopeSeparator);
3802 }
3803 if (lang==SrcLangExt::PHP)
3804 {
3805 // for PHP we use Class::method and Namespace\method
3806 scopeSeparator="::";
3807 }
3808 if (!relates.isEmpty() || isFriend || Config_getBool(HIDE_SCOPE_NAMES))
3809 {
3810 if (!type.isEmpty())
3811 {
3812 def=type+" "+name; //+optArgs;
3813 }
3814 else
3815 {
3816 def=name; //+optArgs;
3817 }
3818 }
3819 else
3820 {
3821 if (!type.isEmpty())
3822 {
3823 def=type+" "+qualScope+scopeSeparator+name; //+optArgs;
3824 }
3825 else
3826 {
3827 def=qualScope+scopeSeparator+name; //+optArgs;
3828 }
3829 }
3830 def.stripPrefix("friend ");
3831 mmd->setDefinition(def);
3833 mmd->addQualifiers(root->qualifiers);
3834
3835 AUTO_TRACE("function member: type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
3836 type, qualScope, rname, args, root->proto, def);
3837
3838 // add member to the class cd
3839 cd->insertMember(md.get());
3840 // add file to list of used files
3841 cd->insertUsedFile(fd);
3842
3843 addMemberToGroups(root,md.get());
3845 root->markAsProcessed();
3846 mmd->setRefItems(root->sli);
3847 mmd->setRequirementReferences(root->rqli);
3848
3849 // add member to the global list of all members
3850 //printf("Adding member=%s class=%s\n",qPrint(md->name()),qPrint(cd->name()));
3852 mn->push_back(std::move(md));
3853}
3854
3855//------------------------------------------------------------------------------------------
3856
3857static void addGlobalFunction(const Entry *root,const QCString &rname,const QCString &sc)
3858{
3859 QCString scope = sc;
3860
3861 // new global function
3863 auto md = createMemberDef(
3864 root->fileName,root->startLine,root->startColumn,
3865 root->type,name,root->args,root->exception,
3866 root->protection,root->virt,root->isStatic,Relationship::Member,
3868 !root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3869 root->argList,root->metaData);
3870 auto mmd = toMemberDefMutable(md.get());
3871 mmd->setTagInfo(root->tagInfo());
3872 mmd->setLanguage(root->lang);
3873 mmd->setId(root->id);
3874 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3875 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3876 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3877 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
3878 mmd->setDocsForDefinition(!root->proto);
3879 mmd->setTypeConstraints(root->typeConstr);
3880 //md->setBody(root->body);
3881 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3882 FileDef *fd=root->fileDef();
3883 mmd->setBodyDef(fd);
3884 mmd->addSectionsToDefinition(root->anchors);
3885 mmd->setMemberSpecifiers(root->spec);
3886 mmd->setVhdlSpecifiers(root->vhdlSpec);
3887 mmd->setMemberGroupId(root->mGrpId);
3888 mmd->setRequiresClause(root->req);
3889 mmd->setExplicitExternal(root->explicitExternal,root->fileName,root->startLine,root->startColumn);
3890
3891 NamespaceDefMutable *nd = nullptr;
3892 // see if the function is inside a namespace that was not part of
3893 // the name already (in that case nd should be non-zero already)
3894 if (root->parent()->section.isNamespace())
3895 {
3896 //QCString nscope=removeAnonymousScopes(root->parent()->name);
3897 QCString nscope=root->parent()->name;
3898 if (!nscope.isEmpty())
3899 {
3900 nd = getResolvedNamespaceMutable(nscope);
3901 }
3902 }
3903 else if (root->parent()->section.isGroupDoc() && !scope.isEmpty())
3904 {
3906 }
3907
3908 if (!scope.isEmpty())
3909 {
3911 if (sep!="::")
3912 {
3913 scope = substitute(scope,"::",sep);
3914 }
3915 scope+=sep;
3916 }
3917
3918 if (Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python) scope = "";
3919 QCString def;
3920 //QCString optArgs = root->argList.empty() ? QCString() : root->args;
3921 if (!root->type.isEmpty())
3922 {
3923 def=root->type+" "+scope+name; //+optArgs;
3924 }
3925 else
3926 {
3927 def=scope+name; //+optArgs;
3928 }
3929 AUTO_TRACE("new non-member function type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
3930 root->type,scope,rname,root->args,root->proto,def);
3931 mmd->setDefinition(def);
3933 mmd->addQualifiers(root->qualifiers);
3934
3935 mmd->setRefItems(root->sli);
3936 mmd->setRequirementReferences(root->rqli);
3937 if (nd && !nd->name().isEmpty() && nd->name().at(0)!='@')
3938 {
3939 // add member to namespace
3940 mmd->setNamespace(nd);
3941 nd->insertMember(md.get());
3942 }
3943 if (fd)
3944 {
3945 // add member to the file (we do this even if we have already
3946 // inserted it into the namespace)
3947 mmd->setFileDef(fd);
3948 fd->insertMember(md.get());
3949 }
3950
3951 addMemberToGroups(root,md.get());
3953 if (root->relatesType == RelatesType::Simple) // if this is a relatesalso command,
3954 // allow find Member to pick it up
3955 {
3956 root->markAsProcessed(); // Otherwise we have finished with this entry.
3957 }
3958
3959 // add member to the list of file members
3961 mn->push_back(std::move(md));
3962}
3963
3964//------------------------------------------------------------------------------------------
3965
3966static void buildFunctionList(const Entry *root)
3967{
3968 if (root->section.isFunction())
3969 {
3970 AUTO_TRACE("member function: type='{}' scope='{}' name='{}' args='{}' relates='{}' relatesType='{}'"
3971 " file='{}' line={} bodyLine={} #tArgLists={} mGrpId={}"
3972 " spec={} proto={} docFile='{}'",
3973 root->type, root->parent()->name, root->name, root->args, root->relates, root->relatesType,
3974 root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(), root->mGrpId,
3975 root->spec, root->proto, root->docFile);
3976
3977 bool isFriend=root->type=="friend" || root->type.find("friend ")!=-1;
3979 //printf("rname=%s\n",qPrint(rname));
3980
3981 QCString scope;
3982 int index = computeQualifiedIndex(rname);
3983 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3984 // grouped members are stored with full scope
3985 {
3986 buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
3987 scope=rname.left(index);
3988 rname=rname.mid(index+2);
3989 }
3990 else
3991 {
3992 scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
3993 }
3994 if (!rname.isEmpty() && scope.find('@')==-1)
3995 {
3996 // check if this function's parent is a class
3997 if (root->lang==SrcLangExt::CSharp)
3998 {
3999 scope=mangleCSharpGenericName(scope);
4000 }
4001 else
4002 {
4004 }
4005
4006 FileDef *rfd=root->fileDef();
4007
4008 int memIndex=rname.findRev("::");
4009
4011 if (cd && scope+"::"==rname.left(scope.length()+2)) // found A::f inside A
4012 {
4013 // strip scope from name
4014 rname=rname.right(rname.length()-root->parent()->name.length()-2);
4015 }
4016
4017 bool isMember=FALSE;
4018 if (memIndex!=-1)
4019 {
4020 int ts=rname.find('<');
4021 int te=rname.find('>');
4022 if (memIndex>0 && (ts==-1 || te==-1))
4023 {
4024 // note: the following code was replaced by inMember=TRUE to deal with a
4025 // function rname='X::foo' of class X inside a namespace also called X...
4026 // bug id 548175
4027 //nd = Doxygen::namespaceLinkedMap->find(rname.left(memIndex));
4028 //isMember = nd==nullptr;
4029 //if (nd)
4030 //{
4031 // // strip namespace scope from name
4032 // scope=rname.left(memIndex);
4033 // rname=rname.right(rname.length()-memIndex-2);
4034 //}
4035 isMember = TRUE;
4036 }
4037 else
4038 {
4039 isMember=memIndex<ts || memIndex>te;
4040 }
4041 }
4042
4043 if (!root->parent()->name.isEmpty() && root->parent()->section.isCompound() && cd)
4044 {
4045 AUTO_TRACE_ADD("member '{}' of class '{}'", rname,cd->name());
4046 addMethodToClass(root,cd,root->type,rname,root->args,isFriend,
4047 root->protection,root->isStatic,root->virt,root->spec,root->relates);
4048 }
4049 else if (root->parent()->section.isObjcImpl() && cd)
4050 {
4051 const MemberDef *md = cd->getMemberByName(rname);
4052 if (md)
4053 {
4054 MemberDefMutable *mdm = toMemberDefMutable(const_cast<MemberDef*>(md));
4055 if (mdm)
4056 {
4057 mdm->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
4058 mdm->setBodyDef(root->fileDef());
4059 }
4060 }
4061 }
4062 else if (!root->parent()->section.isCompound() && !root->parent()->section.isObjcImpl() &&
4063 !isMember &&
4064 (root->relates.isEmpty() || root->relatesType==RelatesType::Duplicate) &&
4065 !root->type.startsWith("extern ") && !root->type.startsWith("typedef ")
4066 )
4067 // no member => unrelated function
4068 {
4069 /* check the uniqueness of the function name in the file.
4070 * A file could contain a function prototype and a function definition
4071 * or even multiple function prototypes.
4072 */
4073 bool found=FALSE;
4074 MemberDef *md_found=nullptr;
4075 MemberName *mn = Doxygen::functionNameLinkedMap->find(rname);
4076 if (mn)
4077 {
4078 AUTO_TRACE_ADD("function '{}' already found",rname);
4079 for (const auto &imd : *mn)
4080 {
4081 MemberDefMutable *md = toMemberDefMutable(imd.get());
4082 if (md)
4083 {
4084 const NamespaceDef *mnd = md->getNamespaceDef();
4085 NamespaceDef *rnd = nullptr;
4086 //printf("root namespace=%s\n",qPrint(rootNav->parent()->name()));
4087 QCString fullScope = scope;
4088 QCString parentScope = root->parent()->name;
4089 if (!parentScope.isEmpty() && !leftScopeMatch(parentScope,scope))
4090 {
4091 if (!scope.isEmpty()) fullScope.prepend("::");
4092 fullScope.prepend(parentScope);
4093 }
4094 //printf("fullScope=%s\n",qPrint(fullScope));
4095 rnd = getResolvedNamespace(fullScope);
4096 const FileDef *mfd = md->getFileDef();
4097 QCString nsName,rnsName;
4098 if (mnd) nsName = mnd->name();
4099 if (rnd) rnsName = rnd->name();
4100 //printf("matching arguments for %s%s %s%s\n",
4101 // qPrint(md->name()),md->argsString(),qPrint(rname),qPrint(argListToString(root->argList)));
4102 const ArgumentList &mdAl = md->argumentList();
4103 const ArgumentList &mdTempl = md->templateArguments();
4104
4105 // in case of template functions, we need to check if the
4106 // functions have the same number of template parameters
4107 bool sameTemplateArgs = TRUE;
4108 bool matchingReturnTypes = TRUE;
4109 bool sameRequiresClause = TRUE;
4110 if (!mdTempl.empty() && !root->tArgLists.empty())
4111 {
4112 sameTemplateArgs = matchTemplateArguments(mdTempl,root->tArgLists.back());
4113 if (md->typeString()!=removeRedundantWhiteSpace(root->type))
4114 {
4115 matchingReturnTypes = FALSE;
4116 }
4117 if (md->requiresClause()!=root->req)
4118 {
4119 sameRequiresClause = FALSE;
4120 }
4121 }
4122 else if (!mdTempl.empty() || !root->tArgLists.empty())
4123 { // if one has template parameters and the other doesn't then that also counts as a
4124 // difference
4125 sameTemplateArgs = FALSE;
4126 }
4127
4128 bool staticsInDifferentFiles =
4129 root->isStatic && md->isStatic() && root->fileName!=md->getDefFileName();
4130
4131 if (sameTemplateArgs &&
4132 matchingReturnTypes &&
4133 sameRequiresClause &&
4134 !staticsInDifferentFiles &&
4135 matchArguments2(md->getOuterScope(),mfd,md->typeString(),&mdAl,
4136 rnd ? rnd : Doxygen::globalScope,rfd,root->type,&root->argList,
4137 FALSE,root->lang)
4138 )
4139 {
4140 GroupDef *gd=nullptr;
4141 if (!root->groups.empty() && !root->groups.front().groupname.isEmpty())
4142 {
4143 gd = Doxygen::groupLinkedMap->find(root->groups.front().groupname);
4144 }
4145 //printf("match!\n");
4146 //printf("mnd=%p rnd=%p nsName=%s rnsName=%s\n",mnd,rnd,qPrint(nsName),qPrint(rnsName));
4147 // see if we need to create a new member
4148 found=(mnd && rnd && nsName==rnsName) || // members are in the same namespace
4149 ((mnd==nullptr && rnd==nullptr && mfd!=nullptr && // no external reference and
4150 mfd->absFilePath()==root->fileName // prototype in the same file
4151 )
4152 );
4153 // otherwise, allow a duplicate global member with the same argument list
4154 if (!found && gd && gd==md->getGroupDef() && nsName==rnsName)
4155 {
4156 // member is already in the group, so we don't want to add it again.
4157 found=TRUE;
4158 }
4159
4160 AUTO_TRACE_ADD("combining function with prototype found={} in namespace '{}'",found,nsName);
4161
4162 if (found)
4163 {
4164 // merge argument lists
4165 ArgumentList mergedArgList = root->argList;
4166 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.isEmpty());
4167 // merge documentation
4168 if (md->documentation().isEmpty() && !root->doc.isEmpty())
4169 {
4170 if (root->proto)
4171 {
4173 }
4174 else
4175 {
4177 }
4178 }
4179
4180 md->setDocumentation(root->doc,root->docFile,root->docLine);
4182 md->setDocsForDefinition(!root->proto);
4183 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
4184 {
4185 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
4186 md->setBodyDef(rfd);
4187 }
4188
4189 if (md->briefDescription().isEmpty() && !root->brief.isEmpty())
4190 {
4191 md->setArgsString(root->args);
4192 }
4193 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
4194
4196
4198 md->addQualifiers(root->qualifiers);
4199
4200 // merge ingroup specifiers
4201 if (md->getGroupDef()==nullptr && !root->groups.empty())
4202 {
4203 addMemberToGroups(root,md);
4204 }
4205 else if (md->getGroupDef()!=nullptr && root->groups.empty())
4206 {
4207 //printf("existing member is grouped, new member not\n");
4208 }
4209 else if (md->getGroupDef()!=nullptr && !root->groups.empty())
4210 {
4211 //printf("both members are grouped\n");
4212 }
4214
4215 // if md is a declaration and root is the corresponding
4216 // definition, then turn md into a definition.
4217 if (md->isPrototype() && !root->proto)
4218 {
4219 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
4220 md->setPrototype(FALSE,root->fileName,root->startLine,root->startColumn);
4221 }
4222 // if md is already the definition, then add the declaration info
4223 else if (!md->isPrototype() && root->proto)
4224 {
4225 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
4226 }
4227 }
4228 }
4229 }
4230 if (found)
4231 {
4232 md_found = md;
4233 break;
4234 }
4235 }
4236 }
4237 if (!found) /* global function is unique with respect to the file */
4238 {
4239 addGlobalFunction(root,rname,scope);
4240 }
4241 else
4242 {
4243 FileDef *fd=root->fileDef();
4244 if (fd)
4245 {
4246 // add member to the file (we do this even if we have already
4247 // inserted it into the namespace)
4248 fd->insertMember(md_found);
4249 }
4250 }
4251
4252 AUTO_TRACE_ADD("unrelated function type='{}' name='{}' args='{}'",root->type,rname,root->args);
4253 }
4254 else
4255 {
4256 AUTO_TRACE_ADD("function '{}' is not processed",rname);
4257 }
4258 }
4259 else if (rname.isEmpty())
4260 {
4261 warn(root->fileName,root->startLine,
4262 "Illegal member name found."
4263 );
4264 }
4265 }
4266 for (const auto &e : root->children()) buildFunctionList(e.get());
4267}
4268
4269//----------------------------------------------------------------------
4270
4271static void findFriends()
4272{
4273 AUTO_TRACE();
4274 for (const auto &fn : *Doxygen::functionNameLinkedMap) // for each global function name
4275 {
4276 MemberName *mn = Doxygen::memberNameLinkedMap->find(fn->memberName());
4277 if (mn)
4278 { // there are members with the same name
4279 // for each function with that name
4280 for (const auto &ifmd : *fn)
4281 {
4282 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
4283 // for each member with that name
4284 for (const auto &immd : *mn)
4285 {
4286 MemberDefMutable *mmd = toMemberDefMutable(immd.get());
4287 //printf("Checking for matching arguments
4288 // mmd->isRelated()=%d mmd->isFriend()=%d mmd->isFunction()=%d\n",
4289 // mmd->isRelated(),mmd->isFriend(),mmd->isFunction());
4290 if (fmd && mmd &&
4291 (mmd->isFriend() || (mmd->isRelated() && mmd->isFunction())) &&
4292 matchArguments2(mmd->getOuterScope(), mmd->getFileDef(), mmd->typeString(), &mmd->argumentList(),
4293 fmd->getOuterScope(), fmd->getFileDef(), fmd->typeString(), &fmd->argumentList(),
4294 TRUE,mmd->getLanguage()
4295 )
4296
4297 ) // if the member is related and the arguments match then the
4298 // function is actually a friend.
4299 {
4300 AUTO_TRACE_ADD("Merging related global and member '{}' isFriend={} isRelated={} isFunction={}",
4301 mmd->name(),mmd->isFriend(),mmd->isRelated(),mmd->isFunction());
4302 const ArgumentList &mmdAl = mmd->argumentList();
4303 const ArgumentList &fmdAl = fmd->argumentList();
4304 mergeArguments(const_cast<ArgumentList&>(fmdAl),const_cast<ArgumentList&>(mmdAl));
4305
4306 // reset argument lists to add missing default parameters
4307 QCString mmdAlStr = argListToString(mmdAl);
4308 QCString fmdAlStr = argListToString(fmdAl);
4309 mmd->setArgsString(mmdAlStr);
4310 fmd->setArgsString(fmdAlStr);
4311 mmd->moveDeclArgumentList(std::make_unique<ArgumentList>(mmdAl));
4312 fmd->moveDeclArgumentList(std::make_unique<ArgumentList>(fmdAl));
4313 AUTO_TRACE_ADD("friend args='{}' member args='{}'",argListToString(fmd->argumentList()),argListToString(mmd->argumentList()));
4314
4315 if (!fmd->documentation().isEmpty())
4316 {
4317 mmd->setDocumentation(fmd->documentation(),fmd->docFile(),fmd->docLine());
4318 }
4319 else if (!mmd->documentation().isEmpty())
4320 {
4321 fmd->setDocumentation(mmd->documentation(),mmd->docFile(),mmd->docLine());
4322 }
4323 if (mmd->briefDescription().isEmpty() && !fmd->briefDescription().isEmpty())
4324 {
4325 mmd->setBriefDescription(fmd->briefDescription(),fmd->briefFile(),fmd->briefLine());
4326 }
4327 else if (!mmd->briefDescription().isEmpty() && !fmd->briefDescription().isEmpty())
4328 {
4329 fmd->setBriefDescription(mmd->briefDescription(),mmd->briefFile(),mmd->briefLine());
4330 }
4331 if (!fmd->inbodyDocumentation().isEmpty())
4332 {
4334 }
4335 else if (!mmd->inbodyDocumentation().isEmpty())
4336 {
4338 }
4339 //printf("body mmd %d fmd %d\n",mmd->getStartBodyLine(),fmd->getStartBodyLine());
4340 if (mmd->getStartBodyLine()==-1 && fmd->getStartBodyLine()!=-1)
4341 {
4342 mmd->setBodySegment(fmd->getDefLine(),fmd->getStartBodyLine(),fmd->getEndBodyLine());
4343 mmd->setBodyDef(fmd->getBodyDef());
4344 //mmd->setBodyMember(fmd);
4345 }
4346 else if (mmd->getStartBodyLine()!=-1 && fmd->getStartBodyLine()==-1)
4347 {
4348 fmd->setBodySegment(mmd->getDefLine(),mmd->getStartBodyLine(),mmd->getEndBodyLine());
4349 fmd->setBodyDef(mmd->getBodyDef());
4350 //fmd->setBodyMember(mmd);
4351 }
4353
4355
4356 mmd->addQualifiers(fmd->getQualifiers());
4357 fmd->addQualifiers(mmd->getQualifiers());
4358
4359 }
4360 }
4361 }
4362 }
4363 }
4364}
4365
4366//----------------------------------------------------------------------
4367
4369{
4370 AUTO_TRACE();
4371
4372 // find matching function declaration and definitions.
4373 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4374 {
4375 //printf("memberName=%s count=%zu\n",qPrint(mn->memberName()),mn->size());
4376 /* find a matching function declaration and definition for this function */
4377 for (const auto &imdec : *mn)
4378 {
4379 MemberDefMutable *mdec = toMemberDefMutable(imdec.get());
4380 if (mdec &&
4381 (mdec->isPrototype() ||
4382 (mdec->isVariable() && mdec->isExternal())
4383 ))
4384 {
4385 for (const auto &imdef : *mn)
4386 {
4387 MemberDefMutable *mdef = toMemberDefMutable(imdef.get());
4388 if (mdef && mdec!=mdef &&
4389 mdec->getNamespaceDef()==mdef->getNamespaceDef())
4390 {
4392 }
4393 }
4394 }
4395 }
4396 }
4397}
4398
4399//----------------------------------------------------------------------
4400
4402{
4403 AUTO_TRACE();
4404 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4405 {
4406 MemberDefMutable *mdef=nullptr,*mdec=nullptr;
4407 /* find a matching function declaration and definition for this function */
4408 for (const auto &imd : *mn)
4409 {
4410 MemberDefMutable *md = toMemberDefMutable(imd.get());
4411 if (md)
4412 {
4413 if (md->isPrototype())
4414 mdec=md;
4415 else if (md->isVariable() && md->isExternal())
4416 mdec=md;
4417
4418 if (md->isFunction() && !md->isStatic() && !md->isPrototype())
4419 mdef=md;
4420 else if (md->isVariable() && !md->isExternal() && !md->isStatic())
4421 mdef=md;
4422 }
4423
4424 if (mdef && mdec) break;
4425 }
4426 if (mdef && mdec)
4427 {
4428 const ArgumentList &mdefAl = mdef->argumentList();
4429 const ArgumentList &mdecAl = mdec->argumentList();
4430 if (
4431 matchArguments2(mdef->getOuterScope(),mdef->getFileDef(),mdef->typeString(),const_cast<ArgumentList*>(&mdefAl),
4432 mdec->getOuterScope(),mdec->getFileDef(),mdec->typeString(),const_cast<ArgumentList*>(&mdecAl),
4433 TRUE,mdef->getLanguage()
4434 )
4435 ) /* match found */
4436 {
4437 AUTO_TRACE_ADD("merging references for mdec={} mdef={}",mdec->name(),mdef->name());
4438 mdef->mergeReferences(mdec);
4439 mdec->mergeReferences(mdef);
4440 mdef->mergeReferencedBy(mdec);
4441 mdec->mergeReferencedBy(mdef);
4442 }
4443 }
4444 }
4445}
4446
4447//----------------------------------------------------------------------
4448
4450{
4451 AUTO_TRACE();
4452 // find match between function declaration and definition for
4453 // related functions
4454 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4455 {
4456 /* find a matching function declaration and definition for this function */
4457 // for each global function
4458 for (const auto &imd : *mn)
4459 {
4460 MemberDefMutable *md = toMemberDefMutable(imd.get());
4461 if (md)
4462 {
4463 //printf(" Function '%s'\n",qPrint(md->name()));
4464 MemberName *rmn = Doxygen::memberNameLinkedMap->find(md->name());
4465 if (rmn) // check if there is a member with the same name
4466 {
4467 //printf(" Member name found\n");
4468 // for each member with the same name
4469 for (const auto &irmd : *rmn)
4470 {
4471 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
4472 //printf(" Member found: related='%d'\n",rmd->isRelated());
4473 if (rmd &&
4474 (rmd->isRelated() || rmd->isForeign()) && // related function
4475 matchArguments2( md->getOuterScope(), md->getFileDef(), md->typeString(), &md->argumentList(),
4476 rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmd->argumentList(),
4477 TRUE,md->getLanguage()
4478 )
4479 )
4480 {
4481 AUTO_TRACE_ADD("Found related member '{}'",md->name());
4482 if (rmd->relatedAlso())
4483 md->setRelatedAlso(rmd->relatedAlso());
4484 else if (rmd->isForeign())
4485 md->makeForeign();
4486 else
4487 md->makeRelated();
4488 }
4489 }
4490 }
4491 }
4492 }
4493 }
4494}
4495
4496//----------------------------------------------------------------------
4497
4499{
4500 AUTO_TRACE();
4501 for (const auto &[qualifiedName,bodyInfo] : Doxygen::staticInitMap)
4502 {
4503 size_t i=qualifiedName.rfind("::");
4504 if (i!=std::string::npos)
4505 {
4506 QCString scope = qualifiedName.substr(0,i);
4507 QCString name = qualifiedName.substr(i+2);
4508 MemberName *mn = Doxygen::memberNameLinkedMap->find(name);
4509 if (mn)
4510 {
4511 for (const auto &imd : *mn)
4512 {
4513 MemberDefMutable *md = toMemberDefMutable(imd.get());
4514 if (md && md->qualifiedName().str()==qualifiedName && md->isVariable())
4515 {
4516 AUTO_TRACE_ADD("found static member {} body [{}..{}]\n",
4517 md->qualifiedName(),bodyInfo.startLine,bodyInfo.endLine);
4518 md->setBodySegment(bodyInfo.defLine,
4519 bodyInfo.startLine,
4520 bodyInfo.endLine);
4521 }
4522 }
4523 }
4524 }
4525 }
4526}
4527
4528//----------------------------------------------------------------------
4529
4530/*! make a dictionary of all template arguments of class cd
4531 * that are part of the base class name.
4532 * Example: A template class A with template arguments <R,S,T>
4533 * that inherits from B<T,T,S> will have T and S in the dictionary.
4534 */
4535static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments,const std::string &name)
4536{
4537 std::map<std::string,int> templateNames;
4538 int count=0;
4539 for (const Argument &arg : templateArguments)
4540 {
4541 static const reg::Ex re(R"(\a[\w:]*)");
4542 reg::Iterator it(name,re);
4544 for (; it!=end ; ++it)
4545 {
4546 const auto &match = *it;
4547 std::string n = match.str();
4548 if (n==arg.name.str())
4549 {
4550 if (templateNames.find(n)==templateNames.end())
4551 {
4552 templateNames.emplace(n,count);
4553 }
4554 }
4555 }
4556 }
4557 return templateNames;
4558}
4559
4560/*! Searches a class from within \a context and \a cd and returns its
4561 * definition if found (otherwise nullptr is returned).
4562 */
4564{
4565 ClassDef *result=nullptr;
4566 if (cd==nullptr)
4567 {
4568 return result;
4569 }
4570 FileDef *fd=cd->getFileDef();
4571 SymbolResolver resolver(fd);
4572 if (context && cd!=context)
4573 {
4574 result = const_cast<ClassDef*>(resolver.resolveClass(context,name,true,true));
4575 }
4576 //printf("1. result=%p\n",result);
4577 if (result==nullptr)
4578 {
4579 result = const_cast<ClassDef*>(resolver.resolveClass(cd,name,true,true));
4580 }
4581 //printf("2. result=%p\n",result);
4582 if (result==nullptr) // try direct class, needed for namespaced classes imported via tag files (see bug624095)
4583 {
4584 result = getClass(name);
4585 }
4586 //printf("3. result=%p\n",result);
4587 //printf("** Trying to find %s within context %s class %s result=%s lookup=%p\n",
4588 // qPrint(name),
4589 // context ? qPrint(context->name()) : "<none>",
4590 // cd ? qPrint(cd->name()) : "<none>",
4591 // result ? qPrint(result->name()) : "<none>",
4592 // Doxygen::classLinkedMap->find(name)
4593 // );
4594 return result;
4595}
4596
4597
4598static void findUsedClassesForClass(const Entry *root,
4599 Definition *context,
4600 ClassDefMutable *masterCd,
4601 ClassDefMutable *instanceCd,
4602 bool isArtificial,
4603 const ArgumentList *actualArgs = nullptr,
4604 const TemplateNameMap &templateNames = TemplateNameMap()
4605 )
4606{
4607 AUTO_TRACE();
4608 const ArgumentList &formalArgs = masterCd->templateArguments();
4609 for (auto &mni : masterCd->memberNameInfoLinkedMap())
4610 {
4611 for (auto &mi : *mni)
4612 {
4613 const MemberDef *md=mi->memberDef();
4614 if (md->isVariable() || md->isObjCProperty()) // for each member variable in this class
4615 {
4616 AUTO_TRACE_ADD("Found variable '{}' in class '{}'",md->name(),masterCd->name());
4617 QCString type = normalizeNonTemplateArgumentsInString(md->typeString(),masterCd,formalArgs);
4618 QCString typedefValue = md->getLanguage()==SrcLangExt::Java ? type : resolveTypeDef(masterCd,type);
4619 if (!typedefValue.isEmpty())
4620 {
4621 type = typedefValue;
4622 }
4623 int pos=0;
4624 QCString usedClassName;
4625 QCString templSpec;
4626 bool found=FALSE;
4627 // the type can contain template variables, replace them if present
4628 type = substituteTemplateArgumentsInString(type,formalArgs,actualArgs);
4629
4630 //printf(" template substitution gives=%s\n",qPrint(type));
4631 while (!found && extractClassNameFromType(type,pos,usedClassName,templSpec,root->lang)!=-1)
4632 {
4633 // find the type (if any) that matches usedClassName
4634 SymbolResolver resolver(masterCd->getFileDef());
4635 const ClassDefMutable *typeCd = resolver.resolveClassMutable(masterCd,usedClassName,false,true);
4636 //printf("====> usedClassName=%s -> typeCd=%s\n",
4637 // qPrint(usedClassName),typeCd?qPrint(typeCd->name()):"<none>");
4638 if (typeCd)
4639 {
4640 usedClassName = typeCd->name();
4641 }
4642
4643 // replace any namespace aliases
4644 replaceNamespaceAliases(usedClassName);
4645 // add any template arguments to the class
4646 QCString usedName = removeRedundantWhiteSpace(usedClassName+templSpec);
4647 //printf(" usedName=%s usedClassName=%s templSpec=%s\n",qPrint(usedName),qPrint(usedClassName),qPrint(templSpec));
4648
4649 TemplateNameMap formTemplateNames;
4650 if (templateNames.empty())
4651 {
4652 formTemplateNames = getTemplateArgumentsInName(formalArgs,usedName.str());
4653 }
4654 BaseInfo bi(usedName,Protection::Public,Specifier::Normal);
4655 findClassRelation(root,context,instanceCd,&bi,formTemplateNames,TemplateInstances,isArtificial);
4656
4657 for (const Argument &arg : masterCd->templateArguments())
4658 {
4659 if (arg.name==usedName) // type is a template argument
4660 {
4661 ClassDef *usedCd = Doxygen::hiddenClassLinkedMap->find(usedName);
4662 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4663 if (usedCd==nullptr)
4664 {
4665 usedCdm = toClassDefMutable(
4666 Doxygen::hiddenClassLinkedMap->add(usedName,
4668 masterCd->getDefFileName(),masterCd->getDefLine(),
4669 masterCd->getDefColumn(),
4670 usedName,
4671 ClassDef::Class)));
4672 if (usedCdm)
4673 {
4674 //printf("making %s a template argument!!!\n",qPrint(usedCd->name()));
4675 usedCdm->makeTemplateArgument();
4676 usedCdm->setUsedOnly(TRUE);
4677 usedCdm->setLanguage(masterCd->getLanguage());
4678 usedCd = usedCdm;
4679 }
4680 }
4681 if (usedCd)
4682 {
4683 found=TRUE;
4684 AUTO_TRACE_ADD("case 1: adding used class '{}'", usedCd->name());
4685 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4686 if (usedCdm)
4687 {
4688 if (isArtificial) usedCdm->setArtificial(TRUE);
4689 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4690 }
4691 }
4692 }
4693 }
4694
4695 if (!found)
4696 {
4697 ClassDef *usedCd=findClassWithinClassContext(context,masterCd,usedName);
4698 //printf("Looking for used class %s: result=%s master=%s\n",
4699 // qPrint(usedName),usedCd?qPrint(usedCd->name()):"<none>",masterCd?qPrint(masterCd->name()):"<none>");
4700
4701 if (usedCd)
4702 {
4703 found=TRUE;
4704 AUTO_TRACE_ADD("case 2: adding used class '{}'", usedCd->name());
4705 instanceCd->addUsedClass(usedCd,md->name(),md->protection()); // class exists
4706 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4707 if (usedCdm)
4708 {
4709 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4710 }
4711 }
4712 }
4713 }
4714 if (!found && !type.isEmpty()) // used class is not documented in any scope
4715 {
4716 ClassDef *usedCd = Doxygen::hiddenClassLinkedMap->find(type);
4717 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4718 if (usedCd==nullptr && !Config_getBool(HIDE_UNDOC_RELATIONS))
4719 {
4720 if (type.endsWith("(*") || type.endsWith("(^")) // type is a function pointer
4721 {
4722 type+=md->argsString();
4723 }
4724 AUTO_TRACE_ADD("New undocumented used class '{}'", type);
4725 usedCdm = toClassDefMutable(
4728 masterCd->getDefFileName(),masterCd->getDefLine(),
4729 masterCd->getDefColumn(),
4730 type,ClassDef::Class)));
4731 if (usedCdm)
4732 {
4733 usedCdm->setUsedOnly(TRUE);
4734 usedCdm->setLanguage(masterCd->getLanguage());
4735 usedCd = usedCdm;
4736 }
4737 }
4738 if (usedCd)
4739 {
4740 AUTO_TRACE_ADD("case 3: adding used class '{}'", usedCd->name());
4741 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4742 if (usedCdm)
4743 {
4744 if (isArtificial) usedCdm->setArtificial(TRUE);
4745 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4746 }
4747 }
4748 }
4749 }
4750 }
4751 }
4752}
4753
4755 const Entry *root,
4756 Definition *context,
4757 ClassDefMutable *masterCd,
4758 ClassDefMutable *instanceCd,
4760 bool isArtificial,
4761 const ArgumentList *actualArgs = nullptr,
4762 const TemplateNameMap &templateNames=TemplateNameMap()
4763 )
4764{
4765 AUTO_TRACE("name={}",root->name);
4766 // The base class could ofcouse also be a non-nested class
4767 const ArgumentList &formalArgs = masterCd->templateArguments();
4768 for (const BaseInfo &bi : root->extends)
4769 {
4770 //printf("masterCd=%s bi.name='%s' #actualArgs=%d\n",
4771 // qPrint(masterCd->localName()),qPrint(bi.name),actualArgs ? (int)actualArgs->size() : -1);
4772 TemplateNameMap formTemplateNames;
4773 if (templateNames.empty())
4774 {
4775 formTemplateNames = getTemplateArgumentsInName(formalArgs,bi.name.str());
4776 }
4777 BaseInfo tbi = bi;
4778 tbi.name = substituteTemplateArgumentsInString(bi.name,formalArgs,actualArgs);
4779 //printf("masterCd=%p instanceCd=%p bi->name=%s tbi.name=%s\n",(void*)masterCd,(void*)instanceCd,qPrint(bi.name),qPrint(tbi.name));
4780
4781 if (mode==DocumentedOnly)
4782 {
4783 // find a documented base class in the correct scope
4784 if (!findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,DocumentedOnly,isArtificial))
4785 {
4786 // 1.8.2: decided to show inheritance relations even if not documented,
4787 // we do make them artificial, so they do not appear in the index
4788 //if (!Config_getBool(HIDE_UNDOC_RELATIONS))
4789 bool b = Config_getBool(HIDE_UNDOC_RELATIONS) ? TRUE : isArtificial;
4790 //{
4791 // no documented base class -> try to find an undocumented one
4792 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,Undocumented,b);
4793 //}
4794 }
4795 }
4796 else if (mode==TemplateInstances)
4797 {
4798 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,TemplateInstances,isArtificial);
4799 }
4800 }
4801}
4802
4803//----------------------------------------------------------------------
4804
4805static void findTemplateInstanceRelation(const Entry *root,
4806 Definition *context,
4807 ClassDefMutable *templateClass,const QCString &templSpec,
4808 const TemplateNameMap &templateNames,
4809 bool isArtificial)
4810{
4811 AUTO_TRACE("Derived from template '{}' with parameters '{}' isArtificial={}",
4812 templateClass->name(),templSpec,isArtificial);
4813
4814 QCString tempArgsStr = tempArgListToString(templateClass->templateArguments(),root->lang,false);
4815 bool existingClass = templSpec==tempArgsStr;
4816 if (existingClass) return; // avoid recursion
4817
4818 bool freshInstance=FALSE;
4819 ClassDefMutable *instanceClass = toClassDefMutable(
4820 templateClass->insertTemplateInstance(
4821 root->fileName,root->startLine,root->startColumn,templSpec,freshInstance));
4822 if (instanceClass)
4823 {
4824 if (freshInstance)
4825 {
4826 instanceClass->setArtificial(TRUE);
4827 instanceClass->setLanguage(root->lang);
4828
4829 AUTO_TRACE_ADD("found fresh instance '{}'",instanceClass->name());
4830 instanceClass->setTemplateBaseClassNames(templateNames);
4831
4832 // search for new template instances caused by base classes of
4833 // instanceClass
4834 auto it_pair = g_classEntries.equal_range(templateClass->name().str());
4835 for (auto it=it_pair.first ; it!=it_pair.second ; ++it)
4836 {
4837 const Entry *templateRoot = it->second;
4838 AUTO_TRACE_ADD("template root found '{}' templSpec='{}'",templateRoot->name,templSpec);
4839 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(root->lang,templSpec);
4840 findBaseClassesForClass(templateRoot,context,templateClass,instanceClass,
4841 TemplateInstances,isArtificial,templArgs.get(),templateNames);
4842
4843 findUsedClassesForClass(templateRoot,context,templateClass,instanceClass,
4844 isArtificial,templArgs.get(),templateNames);
4845 }
4846 }
4847 else
4848 {
4849 AUTO_TRACE_ADD("instance already exists");
4850 }
4851 }
4852}
4853
4854//----------------------------------------------------------------------
4855
4856static void resolveTemplateInstanceInType(const Entry *root,const Definition *scope,const MemberDef *md)
4857{
4858 // For a statement like 'using X = T<A>', add a template instance 'T<A>' as a symbol, so it can
4859 // be used to match arguments (see issue #11111)
4860 AUTO_TRACE();
4861 QCString ttype = md->typeString();
4862 ttype.stripPrefix("typedef ");
4863 int ti=ttype.find('<');
4864 if (ti!=-1)
4865 {
4866 QCString templateClassName = ttype.left(ti);
4867 SymbolResolver resolver(root->fileDef());
4868 ClassDefMutable *baseClass = resolver.resolveClassMutable(scope ? scope : Doxygen::globalScope,
4869 templateClassName, true, true);
4870 AUTO_TRACE_ADD("templateClassName={} baseClass={}",templateClassName,baseClass?baseClass->name():"<none>");
4871 if (baseClass)
4872 {
4873 const ArgumentList &tl = baseClass->templateArguments();
4874 TemplateNameMap templateNames = getTemplateArgumentsInName(tl,templateClassName.str());
4876 baseClass,
4877 ttype.mid(ti),
4878 templateNames,
4879 baseClass->isArtificial());
4880 }
4881 }
4882}
4883
4884//----------------------------------------------------------------------
4885
4886static bool isRecursiveBaseClass(const QCString &scope,const QCString &name)
4887{
4888 QCString n=name;
4889 int index=n.find('<');
4890 if (index!=-1)
4891 {
4892 n=n.left(index);
4893 }
4894 bool result = rightScopeMatch(scope,n);
4895 return result;
4896}
4897
4899{
4900 if (name.isEmpty()) return 0;
4901 int l = static_cast<int>(name.length());
4902 if (name[l-1]=='>') // search backward to find the matching <, allowing nested <...> and strings.
4903 {
4904 int count=1;
4905 int i=l-2;
4906 char insideQuote=0;
4907 while (count>0 && i>=0)
4908 {
4909 char c = name[i--];
4910 switch (c)
4911 {
4912 case '>': if (!insideQuote) count++; break;
4913 case '<': if (!insideQuote) count--; break;
4914 case '\'': if (!insideQuote) insideQuote=c;
4915 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
4916 break;
4917 case '"': if (!insideQuote) insideQuote=c;
4918 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
4919 break;
4920 default: break;
4921 }
4922 }
4923 if (i>=0) l=i+1;
4924 }
4925 return l;
4926}
4927
4929 const Entry *root,
4930 Definition *context,
4931 ClassDefMutable *cd,
4932 const BaseInfo *bi,
4933 const TemplateNameMap &templateNames,
4935 bool isArtificial
4936 )
4937{
4938 AUTO_TRACE("name={} base={} isArtificial={} mode={}",cd->name(),bi->name,isArtificial,(int)mode);
4939
4940 QCString biName=bi->name;
4941 bool explicitGlobalScope=FALSE;
4942 if (biName.startsWith("::")) // explicit global scope
4943 {
4944 biName=biName.right(biName.length()-2);
4945 explicitGlobalScope=TRUE;
4946 }
4947
4948 Entry *parentNode=root->parent();
4949 bool lastParent=FALSE;
4950 do // for each parent scope, starting with the largest scope
4951 // (in case of nested classes)
4952 {
4953 QCString scopeName= parentNode ? parentNode->name : QCString();
4954 int scopeOffset=explicitGlobalScope ? 0 : static_cast<int>(scopeName.length());
4955 do // try all parent scope prefixes, starting with the largest scope
4956 {
4957 //printf("scopePrefix='%s' biName='%s'\n",
4958 // qPrint(scopeName.left(scopeOffset)),qPrint(biName));
4959
4960 QCString baseClassName=biName;
4961 if (scopeOffset>0)
4962 {
4963 baseClassName.prepend(scopeName.left(scopeOffset)+"::");
4964 }
4965 if (root->lang==SrcLangExt::CSharp)
4966 {
4967 baseClassName = mangleCSharpGenericName(baseClassName);
4968 }
4969 AUTO_TRACE_ADD("cd='{}' baseClassName='{}'",cd->name(),baseClassName);
4970 SymbolResolver resolver(cd->getFileDef());
4971 ClassDefMutable *baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
4972 baseClassName,
4973 mode==Undocumented,
4974 true
4975 );
4976 const MemberDef *baseClassTypeDef = resolver.getTypedef();
4977 QCString templSpec = resolver.getTemplateSpec();
4978 //printf("baseClassName=%s baseClass=%p cd=%p explicitGlobalScope=%d\n",
4979 // qPrint(baseClassName),baseClass,cd,explicitGlobalScope);
4980 //printf(" scope='%s' baseClassName='%s' baseClass=%s templSpec=%s\n",
4981 // cd ? qPrint(cd->name()):"<none>",
4982 // qPrint(baseClassName),
4983 // baseClass?qPrint(baseClass->name()):"<none>",
4984 // qPrint(templSpec)
4985 // );
4986 //if (baseClassName.left(root->name.length())!=root->name ||
4987 // baseClassName.at(root->name.length())!='<'
4988 // ) // Check for base class with the same name.
4989 // // If found then look in the outer scope for a match
4990 // // and prevent recursion.
4991 if (!isRecursiveBaseClass(root->name,baseClassName)
4992 || explicitGlobalScope
4993 // sadly isRecursiveBaseClass always true for UNO IDL ifc/svc members
4994 // (i.e. this is needed for addInterfaceOrServiceToServiceOrSingleton)
4995 || (root->lang==SrcLangExt::IDL &&
4996 (root->section.isExportedInterface() ||
4997 root->section.isIncludedService()))
4998 )
4999 {
5000 AUTO_TRACE_ADD("class relation '{}' inherited/used by '{}' found prot={} virt={} templSpec='{}'",
5001 baseClassName, root->name, bi->prot, bi->virt, templSpec);
5002
5003 int i=findTemplateSpecializationPosition(baseClassName);
5004 int si=baseClassName.findRev("::",i);
5005 if (si==-1) si=0;
5006 if (baseClass==nullptr && static_cast<size_t>(i)!=baseClassName.length())
5007 // base class has template specifiers
5008 {
5009 // TODO: here we should try to find the correct template specialization
5010 // but for now, we only look for the unspecialized base class.
5011 int e=findEndOfTemplate(baseClassName,i+1);
5012 //printf("baseClass==0 i=%d e=%d\n",i,e);
5013 if (e!=-1) // end of template was found at e
5014 {
5015 templSpec = removeRedundantWhiteSpace(baseClassName.mid(i,e-i));
5016 baseClassName = baseClassName.left(i)+baseClassName.right(baseClassName.length()-e);
5017 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
5018 baseClassName,
5019 mode==Undocumented,
5020 true
5021 );
5022 baseClassTypeDef = resolver.getTypedef();
5023 //printf("baseClass=%p -> baseClass=%s templSpec=%s\n",
5024 // baseClass,qPrint(baseClassName),qPrint(templSpec));
5025 }
5026 }
5027 else if (baseClass && !templSpec.isEmpty()) // we have a known class, but also
5028 // know it is a template, so see if
5029 // we can also link to the explicit
5030 // instance (for instance if a class
5031 // derived from a template argument)
5032 {
5033 //printf("baseClass=%s templSpec=%s\n",qPrint(baseClass->name()),qPrint(templSpec));
5034 ClassDefMutable *templClass=getClassMutable(baseClass->name()+templSpec);
5035 if (templClass)
5036 {
5037 // use the template instance instead of the template base.
5038 baseClass = templClass;
5039 templSpec.clear();
5040 }
5041 }
5042
5043 //printf("cd=%p baseClass=%p\n",cd,baseClass);
5044 bool found=baseClass!=nullptr && (baseClass!=cd || mode==TemplateInstances);
5045 AUTO_TRACE_ADD("1. found={}",found);
5046 if (!found && si!=-1)
5047 {
5048 // replace any namespace aliases
5049 replaceNamespaceAliases(baseClassName);
5050 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
5051 baseClassName,
5052 mode==Undocumented,
5053 true
5054 );
5055 baseClassTypeDef = resolver.getTypedef();
5056 found=baseClass!=nullptr && baseClass!=cd;
5057 if (found) templSpec = resolver.getTemplateSpec();
5058 }
5059 AUTO_TRACE_ADD("2. found={}",found);
5060
5061 if (!found)
5062 {
5063 baseClass=toClassDefMutable(findClassWithinClassContext(context,cd,baseClassName));
5064 //printf("findClassWithinClassContext(%s,%s)=%p\n",
5065 // qPrint(cd->name()),qPrint(baseClassName),baseClass);
5066 found = baseClass!=nullptr && baseClass!=cd;
5067
5068 }
5069 AUTO_TRACE_ADD("3. found={}",found);
5070 if (!found)
5071 {
5072 // for PHP the "use A\B as C" construct map class C to A::B, so we lookup
5073 // the class name also in the alias mapping.
5074 auto it = Doxygen::namespaceAliasMap.find(baseClassName.str());
5075 if (it!=Doxygen::namespaceAliasMap.end()) // see if it is indeed a class.
5076 {
5077 baseClass=getClassMutable(it->second.alias);
5078 found = baseClass!=nullptr && baseClass!=cd;
5079 }
5080 }
5081 bool isATemplateArgument = templateNames.find(biName.str())!=templateNames.end();
5082
5083 AUTO_TRACE_ADD("4. found={}",found);
5084 if (found)
5085 {
5086 AUTO_TRACE_ADD("Documented base class '{}' templSpec='{}'",biName,templSpec);
5087 // add base class to this class
5088
5089 // if templSpec is not empty then we should "instantiate"
5090 // the template baseClass. A new ClassDef should be created
5091 // to represent the instance. To be able to add the (instantiated)
5092 // members and documentation of a template class
5093 // (inserted in that template class at a later stage),
5094 // the template should know about its instances.
5095 // the instantiation process, should be done in a recursive way,
5096 // since instantiating a template may introduce new inheritance
5097 // relations.
5098 if (!templSpec.isEmpty() && mode==TemplateInstances)
5099 {
5100 // if baseClass is actually a typedef then we should not
5101 // instantiate it, since typedefs are in a different namespace
5102 // see bug531637 for an example where this would otherwise hang
5103 // Doxygen
5104 if (baseClassTypeDef==nullptr)
5105 {
5106 //printf(" => findTemplateInstanceRelation: %s\n",qPrint(baseClass->name()));
5107 findTemplateInstanceRelation(root,context,baseClass,templSpec,templateNames,baseClass->isArtificial());
5108 }
5109 }
5110 else if (mode==DocumentedOnly || mode==Undocumented)
5111 {
5112 //printf(" => insert base class\n");
5113 QCString usedName;
5114 if (baseClassTypeDef)
5115 {
5116 usedName=biName;
5117 //printf("***** usedName=%s templSpec=%s\n",qPrint(usedName),qPrint(templSpec));
5118 }
5119 Protection prot = bi->prot;
5120 if (Config_getBool(SIP_SUPPORT)) prot=Protection::Public;
5121 if (cd!=baseClass && !cd->isSubClass(baseClass) && baseClass->isBaseClass(cd,true,templSpec)==0) // check for recursion, see bug690787
5122 {
5123 AUTO_TRACE_ADD("insertBaseClass name={} prot={} virt={} templSpec={}",usedName,prot,bi->virt,templSpec);
5124 cd->insertBaseClass(baseClass,usedName,prot,bi->virt,templSpec);
5125 // add this class as super class to the base class
5126 baseClass->insertSubClass(cd,prot,bi->virt,templSpec);
5127 }
5128 else
5129 {
5130 warn(root->fileName,root->startLine,
5131 "Detected potential recursive class relation "
5132 "between class {} and base class {}!",
5133 cd->name(),baseClass->name()
5134 );
5135 }
5136 }
5137 return TRUE;
5138 }
5139 else if (mode==Undocumented && (scopeOffset==0 || isATemplateArgument))
5140 {
5141 AUTO_TRACE_ADD("New undocumented base class '{}' baseClassName='{}' templSpec='{}' isArtificial={}",
5142 biName,baseClassName,templSpec,isArtificial);
5143 baseClass=nullptr;
5144 if (isATemplateArgument)
5145 {
5146 baseClass = toClassDefMutable(Doxygen::hiddenClassLinkedMap->find(baseClassName));
5147 if (baseClass==nullptr) // not found (or alias)
5148 {
5149 baseClass= toClassDefMutable(
5150 Doxygen::hiddenClassLinkedMap->add(baseClassName,
5151 createClassDef(root->fileName,root->startLine,root->startColumn,
5152 baseClassName,
5153 ClassDef::Class)));
5154 if (baseClass) // really added (not alias)
5155 {
5156 if (isArtificial) baseClass->setArtificial(TRUE);
5157 baseClass->setLanguage(root->lang);
5158 }
5159 }
5160 }
5161 else
5162 {
5163 baseClass = toClassDefMutable(Doxygen::classLinkedMap->find(baseClassName));
5164 //printf("*** classDDict->find(%s)=%p biName=%s templSpec=%s\n",
5165 // qPrint(baseClassName),baseClass,qPrint(biName),qPrint(templSpec));
5166 if (baseClass==nullptr) // not found (or alias)
5167 {
5168 baseClass = toClassDefMutable(
5169 Doxygen::classLinkedMap->add(baseClassName,
5170 createClassDef(root->fileName,root->startLine,root->startColumn,
5171 baseClassName,
5172 ClassDef::Class)));
5173 if (baseClass) // really added (not alias)
5174 {
5175 if (isArtificial) baseClass->setArtificial(TRUE);
5176 baseClass->setLanguage(root->lang);
5177 si = baseClassName.findRev("::");
5178 if (si!=-1) // class is nested
5179 {
5180 Definition *sd = findScopeFromQualifiedName(Doxygen::globalScope,baseClassName.left(si),nullptr,root->tagInfo());
5181 if (sd==nullptr || sd==Doxygen::globalScope) // outer scope not found
5182 {
5183 baseClass->setArtificial(TRUE); // see bug678139
5184 }
5185 }
5186 }
5187 }
5188 }
5189 if (baseClass)
5190 {
5191 if (biName.endsWith("-p"))
5192 {
5193 biName="<"+biName.left(biName.length()-2)+">";
5194 }
5195 if (!cd->isSubClass(baseClass) && cd!=baseClass && cd->isBaseClass(baseClass,true,templSpec)==0) // check for recursion
5196 {
5197 AUTO_TRACE_ADD("insertBaseClass name={} prot={} virt={} templSpec={}",biName,bi->prot,bi->virt,templSpec);
5198 // add base class to this class
5199 cd->insertBaseClass(baseClass,biName,bi->prot,bi->virt,templSpec);
5200 // add this class as super class to the base class
5201 baseClass->insertSubClass(cd,bi->prot,bi->virt,templSpec);
5202 }
5203 // the undocumented base was found in this file
5204 baseClass->insertUsedFile(root->fileDef());
5205
5206 Definition *scope = buildScopeFromQualifiedName(baseClass->name(),root->lang,nullptr);
5207 if (scope!=baseClass)
5208 {
5209 baseClass->setOuterScope(scope);
5210 }
5211
5212 if (baseClassName.endsWith("-p"))
5213 {
5215 }
5216 return TRUE;
5217 }
5218 else
5219 {
5220 AUTO_TRACE_ADD("Base class '{}' not created (alias?)",biName);
5221 }
5222 }
5223 else
5224 {
5225 AUTO_TRACE_ADD("Base class '{}' not found",biName);
5226 }
5227 }
5228 else
5229 {
5230 if (mode!=TemplateInstances)
5231 {
5232 warn(root->fileName,root->startLine,
5233 "Detected potential recursive class relation "
5234 "between class {} and base class {}!",
5235 root->name,baseClassName
5236 );
5237 }
5238 // for mode==TemplateInstance this case is quite common and
5239 // indicates a relation between a template class and a template
5240 // instance with the same name.
5241 }
5242 if (scopeOffset==0)
5243 {
5244 scopeOffset=-1;
5245 }
5246 else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1)
5247 {
5248 scopeOffset=0;
5249 }
5250 //printf("new scopeOffset='%d'",scopeOffset);
5251 } while (scopeOffset>=0);
5252
5253 if (parentNode==nullptr)
5254 {
5255 lastParent=TRUE;
5256 }
5257 else
5258 {
5259 parentNode=parentNode->parent();
5260 }
5261 } while (lastParent);
5262
5263 return FALSE;
5264}
5265
5266//----------------------------------------------------------------------
5267// Computes the base and super classes for each class in the tree
5268
5269static bool isClassSection(const Entry *root)
5270{
5271 if ( !root->name.isEmpty() )
5272 {
5273 if (root->section.isCompound())
5274 // is it a compound (class, struct, union, interface ...)
5275 {
5276 return TRUE;
5277 }
5278 else if (root->section.isCompoundDoc())
5279 // is it a documentation block with inheritance info.
5280 {
5281 bool hasExtends = !root->extends.empty();
5282 if (hasExtends) return TRUE;
5283 }
5284 }
5285 return FALSE;
5286}
5287
5288
5289/*! Builds a dictionary of all entry nodes in the tree starting with \a root
5290 */
5291static void findClassEntries(const Entry *root)
5292{
5293 if (isClassSection(root))
5294 {
5295 g_classEntries.emplace(root->name.str(),root);
5296 }
5297 for (const auto &e : root->children()) findClassEntries(e.get());
5298}
5299
5301{
5302 // strip any anonymous scopes first
5305 int i=0;
5306 if ((root->lang==SrcLangExt::CSharp || root->lang==SrcLangExt::Java) &&
5307 (i=bName.find('<'))!=-1)
5308 {
5309 // a Java/C# generic class looks like a C++ specialization, so we need to strip the
5310 // template part before looking for matches
5311 if (root->lang==SrcLangExt::CSharp)
5312 {
5313 bName = mangleCSharpGenericName(root->name);
5314 }
5315 else
5316 {
5317 bName = bName.left(i);
5318 }
5319 }
5320 return bName;
5321}
5322
5323/*! Using the dictionary build by findClassEntries(), this
5324 * function will look for additional template specialization that
5325 * exists as inheritance relations only. These instances will be
5326 * added to the template they are derived from.
5327 */
5329{
5330 AUTO_TRACE();
5331 ClassDefSet visitedClasses;
5332 for (const auto &[name,root] : g_classEntries)
5333 {
5334 QCString bName = extractClassName(root);
5335 ClassDefMutable *cdm = getClassMutable(bName);
5336 if (cdm)
5337 {
5339 }
5340 }
5341}
5342
5344{
5345 AUTO_TRACE("root->name={} cd={}",root->name,cd->name());
5346 int i = root->name.find('<');
5347 int j = root->name.findRev('>');
5348 int k = root->name.find("::",j+1); // A<T::B> => ok, A<T>::B => nok
5349 if (i!=-1 && j!=-1 && k==-1 && root->lang!=SrcLangExt::CSharp && root->lang!=SrcLangExt::Java)
5350 {
5351 ClassDefMutable *master = getClassMutable(root->name.left(i));
5352 if (master && master!=cd && !cd->templateMaster())
5353 {
5354 AUTO_TRACE_ADD("class={} master={}",cd->name(),cd->templateMaster()?cd->templateMaster()->name():"<none>",master->name());
5355 cd->setTemplateMaster(master);
5356 master->insertExplicitTemplateInstance(cd,root->name.mid(i));
5357 }
5358 }
5359}
5360
5362{
5363 AUTO_TRACE();
5364 for (const auto &[name,root] : g_classEntries)
5365 {
5366 QCString bName = extractClassName(root);
5367 ClassDefMutable *cdm = getClassMutable(bName);
5368 if (cdm)
5369 {
5370 findUsedClassesForClass(root,cdm,cdm,cdm,TRUE);
5372 cdm->addTypeConstraints();
5373 }
5374 }
5375}
5376
5378{
5379 AUTO_TRACE();
5380 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5381 {
5382 if (!nd->hasDocumentation())
5383 {
5384 if ((guessSection(nd->getDefFileName()).isHeader() ||
5385 nd->getLanguage() == SrcLangExt::Fortran) && // Fortran doesn't have header files.
5386 !Config_getBool(HIDE_UNDOC_NAMESPACES) // undocumented namespaces are visible
5387 )
5388 {
5389 warn_undoc(nd->getDefFileName(),nd->getDefLine(), "{} {} is not documented.",
5390 nd->getLanguage() == SrcLangExt::Fortran ? "Module" : "Namespace",
5391 nd->name());
5392 }
5393 }
5394 }
5395}
5396
5398{
5399 AUTO_TRACE();
5400 for (const auto &[name,root] : g_classEntries)
5401 {
5402 QCString bName = extractClassName(root);
5403 ClassDefMutable *cd = getClassMutable(bName);
5404 if (cd)
5405 {
5407 }
5408 size_t numMembers = cd ? cd->memberNameInfoLinkedMap().size() : 0;
5409 if ((cd==nullptr || (!cd->hasDocumentation() && !cd->isReference())) && numMembers>0 && !bName.endsWith("::"))
5410 {
5411 if (!root->name.isEmpty() && root->name.find('@')==-1 && // normal name
5412 (guessSection(root->fileName).isHeader() ||
5413 Config_getBool(EXTRACT_LOCAL_CLASSES)) && // not defined in source file
5414 protectionLevelVisible(root->protection) && // hidden by protection
5415 !Config_getBool(HIDE_UNDOC_CLASSES) // undocumented class are visible
5416 )
5417 warn_undoc(root->fileName,root->startLine, "Compound {} is not documented.", root->name);
5418 }
5419 }
5420}
5421
5423{
5424 AUTO_TRACE();
5425 for (const auto &[name,root] : g_classEntries)
5426 {
5430 // strip any anonymous scopes first
5431 if (cd && !cd->getTemplateInstances().empty())
5432 {
5433 AUTO_TRACE_ADD("Template class '{}'",cd->name());
5434 for (const auto &ti : cd->getTemplateInstances()) // for each template instance
5435 {
5436 ClassDefMutable *tcd=toClassDefMutable(ti.classDef);
5437 if (tcd)
5438 {
5439 AUTO_TRACE_ADD("Template instance '{}'",tcd->name());
5440 QCString templSpec = ti.templSpec;
5441 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(tcd->getLanguage(),templSpec);
5442 for (const BaseInfo &bi : root->extends)
5443 {
5444 // check if the base class is a template argument
5445 BaseInfo tbi = bi;
5446 const ArgumentList &tl = cd->templateArguments();
5447 if (!tl.empty())
5448 {
5449 TemplateNameMap baseClassNames = tcd->getTemplateBaseClassNames();
5450 TemplateNameMap templateNames = getTemplateArgumentsInName(tl,bi.name.str());
5451 // for each template name that we inherit from we need to
5452 // substitute the formal with the actual arguments
5453 TemplateNameMap actualTemplateNames;
5454 for (const auto &tn_kv : templateNames)
5455 {
5456 size_t templIndex = tn_kv.second;
5457 Argument actArg;
5458 bool hasActArg=FALSE;
5459 if (templIndex<templArgs->size())
5460 {
5461 actArg=templArgs->at(templIndex);
5462 hasActArg=TRUE;
5463 }
5464 if (hasActArg &&
5465 baseClassNames.find(actArg.type.str())!=baseClassNames.end() &&
5466 actualTemplateNames.find(actArg.type.str())==actualTemplateNames.end()
5467 )
5468 {
5469 actualTemplateNames.emplace(actArg.type.str(),static_cast<int>(templIndex));
5470 }
5471 }
5472
5473 tbi.name = substituteTemplateArgumentsInString(bi.name,tl,templArgs.get());
5474 // find a documented base class in the correct scope
5475 if (!findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,DocumentedOnly,FALSE))
5476 {
5477 // no documented base class -> try to find an undocumented one
5478 findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,Undocumented,TRUE);
5479 }
5480 }
5481 }
5482 }
5483 }
5484 }
5485 }
5486}
5487
5488//-----------------------------------------------------------------------
5489// compute the references (anchors in HTML) for each function in the file
5490
5492{
5493 AUTO_TRACE();
5494 for (const auto &cd : *Doxygen::classLinkedMap)
5495 {
5496 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5497 if (cdm)
5498 {
5499 cdm->computeAnchors();
5500 }
5501 }
5502 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5503 {
5504 for (const auto &fd : *fn)
5505 {
5506 fd->computeAnchors();
5507 }
5508 }
5509 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5510 {
5512 if (ndm)
5513 {
5514 ndm->computeAnchors();
5515 }
5516 }
5517 for (const auto &gd : *Doxygen::groupLinkedMap)
5518 {
5519 gd->computeAnchors();
5520 }
5521}
5522
5523//----------------------------------------------------------------------
5524
5525
5526template<typename Func>
5527static void applyToAllDefinitions(Func func)
5528{
5529 for (const auto &cd : *Doxygen::classLinkedMap)
5530 {
5531 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5532 if (cdm)
5533 {
5534 func(cdm);
5535 }
5536 }
5537
5538 for (const auto &cd : *Doxygen::conceptLinkedMap)
5539 {
5540 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
5541 if (cdm)
5542 {
5543 func(cdm);
5544 }
5545 }
5546
5547 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5548 {
5549 for (const auto &fd : *fn)
5550 {
5551 func(fd.get());
5552 }
5553 }
5554
5555 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5556 {
5558 if (ndm)
5559 {
5560 func(ndm);
5561 }
5562 }
5563
5564 for (const auto &gd : *Doxygen::groupLinkedMap)
5565 {
5566 func(gd.get());
5567 }
5568
5569 for (const auto &pd : *Doxygen::pageLinkedMap)
5570 {
5571 func(pd.get());
5572 }
5573
5574 for (const auto &dd : *Doxygen::dirLinkedMap)
5575 {
5576 func(dd.get());
5577 }
5578
5579 func(&ModuleManager::instance());
5580}
5581
5582//----------------------------------------------------------------------
5583
5585{
5586 AUTO_TRACE();
5587 applyToAllDefinitions([](auto* obj) { obj->addRequirementReferences(); });
5588}
5589
5590//----------------------------------------------------------------------
5591
5593{
5594 AUTO_TRACE();
5595 applyToAllDefinitions([](auto* obj) { obj->addListReferences(); });
5596}
5597
5598
5599//----------------------------------------------------------------------
5600
5602{
5603 AUTO_TRACE();
5605 {
5606 rl->generatePage();
5607 }
5608}
5609
5610//----------------------------------------------------------------------
5611// Copy the documentation in entry 'root' to member definition 'md' and
5612// set the function declaration of the member to 'funcDecl'. If the boolean
5613// over_load is set the standard overload text is added.
5614
5615static void addMemberDocs(const Entry *root,
5616 MemberDefMutable *md, const QCString &funcDecl,
5617 const ArgumentList *al,
5618 bool over_load,
5619 TypeSpecifier spec
5620 )
5621{
5622 if (md==nullptr) return;
5623 AUTO_TRACE("scope='{}' name='{}' args='{}' funcDecl='{}' mSpec={}",
5624 root->parent()->name,md->name(),md->argsString(),funcDecl,spec);
5625 if (!root->section.isDoc()) // @fn or @var does not need to specify the complete definition, so don't overwrite it
5626 {
5627 QCString fDecl=funcDecl;
5628 // strip extern specifier
5629 fDecl.stripPrefix("extern ");
5630 md->setDefinition(fDecl);
5631 }
5633 md->addQualifiers(root->qualifiers);
5635 const NamespaceDef *nd=md->getNamespaceDef();
5636 QCString fullName;
5637 if (cd)
5638 fullName = cd->name();
5639 else if (nd)
5640 fullName = nd->name();
5641
5642 if (!fullName.isEmpty()) fullName+="::";
5643 fullName+=md->name();
5644 FileDef *rfd=root->fileDef();
5645
5646 // TODO determine scope based on root not md
5647 Definition *rscope = md->getOuterScope();
5648
5649 const ArgumentList &mdAl = md->argumentList();
5650 if (al)
5651 {
5652 ArgumentList mergedAl = *al;
5653 //printf("merging arguments (1) docs=%d\n",root->doc.isEmpty());
5654 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedAl,!root->doc.isEmpty());
5655 }
5656 else
5657 {
5658 if (
5659 matchArguments2( md->getOuterScope(), md->getFileDef(),md->typeString(),const_cast<ArgumentList*>(&mdAl),
5660 rscope,rfd,root->type,&root->argList,
5661 TRUE, root->lang
5662 )
5663 )
5664 {
5665 //printf("merging arguments (2)\n");
5666 ArgumentList mergedArgList = root->argList;
5667 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.isEmpty());
5668 }
5669 }
5670 if (over_load) // the \overload keyword was used
5671 {
5673 if (!root->doc.isEmpty())
5674 {
5675 doc+="<p>";
5676 doc+=root->doc;
5677 }
5678 md->setDocumentation(doc,root->docFile,root->docLine);
5680 md->setDocsForDefinition(!root->proto);
5681 }
5682 else
5683 {
5684 //printf("overwrite!\n");
5685 md->setDocumentation(root->doc,root->docFile,root->docLine);
5686 md->setDocsForDefinition(!root->proto);
5687
5688 //printf("overwrite!\n");
5689 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
5690
5691 if (
5692 (md->inbodyDocumentation().isEmpty() ||
5693 !root->parent()->name.isEmpty()
5694 ) && !root->inbodyDocs.isEmpty()
5695 )
5696 {
5698 }
5699 }
5700
5701 //printf("initializer: '%s'(isEmpty=%d) '%s'(isEmpty=%d)\n",
5702 // qPrint(md->initializer()),md->initializer().isEmpty(),
5703 // qPrint(root->initializer),root->initializer.isEmpty()
5704 // );
5705 std::string rootInit = root->initializer.str();
5706 if (md->initializer().isEmpty() && !rootInit.empty())
5707 {
5708 //printf("setInitializer\n");
5709 md->setInitializer(rootInit);
5710 }
5711 if (md->requiresClause().isEmpty() && !root->req.isEmpty())
5712 {
5713 md->setRequiresClause(root->req);
5714 }
5715
5716 md->setMaxInitLines(root->initLines);
5717
5718 if (rfd)
5719 {
5720 if ((md->getStartBodyLine()==-1 && root->bodyLine!=-1)
5721 )
5722 {
5723 //printf("Setting new body segment [%d,%d]\n",root->bodyLine,root->endBodyLine);
5724 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
5725 md->setBodyDef(rfd);
5726 }
5727
5728 md->setRefItems(root->sli);
5729 md->setRequirementReferences(root->rqli);
5730 }
5731
5733 md->addQualifiers(root->qualifiers);
5734
5735 md->mergeMemberSpecifiers(spec);
5737 addMemberToGroups(root,md);
5739 if (cd) cd->insertUsedFile(rfd);
5740 //printf("root->mGrpId=%d\n",root->mGrpId);
5741 if (root->mGrpId!=-1)
5742 {
5743 if (md->getMemberGroupId()!=-1)
5744 {
5745 if (md->getMemberGroupId()!=root->mGrpId)
5746 {
5747 warn(root->fileName,root->startLine,
5748 "member {} belongs to two different groups. The second one found here will be ignored.",
5749 md->name()
5750 );
5751 }
5752 }
5753 else // set group id
5754 {
5755 //printf("setMemberGroupId=%d md=%s\n",root->mGrpId,qPrint(md->name()));
5756 md->setMemberGroupId(root->mGrpId);
5757 }
5758 }
5759 md->addQualifiers(root->qualifiers);
5760}
5761
5762//----------------------------------------------------------------------
5763// find a class definition given the scope name and (optionally) a
5764// template list specifier
5765
5767 const QCString &scopeName)
5768{
5769 SymbolResolver resolver(fd);
5770 const ClassDef *tcd = resolver.resolveClass(nd,scopeName,true,true);
5771 //printf("findClassDefinition(fd=%s,ns=%s,scopeName=%s)='%s'\n",
5772 // qPrint(fd?fd->name():""),qPrint(nd?nd->name():""),
5773 // qPrint(scopeName),qPrint(tcd?tcd->name():""));
5774 return tcd;
5775}
5776
5777//----------------------------------------------------------------------------
5778// Returns TRUE, if the entry belongs to the group of the member definition,
5779// otherwise FALSE.
5780
5781static bool isEntryInGroupOfMember(const Entry *root,const MemberDef *md,bool allowNoGroup=false)
5782{
5783 const GroupDef *gd = md->getGroupDef();
5784 if (!gd)
5785 {
5786 return allowNoGroup;
5787 }
5788
5789 for (const auto &g : root->groups)
5790 {
5791 if (g.groupname == gd->name())
5792 {
5793 return true; // matching group
5794 }
5795 }
5796
5797 return false;
5798}
5799
5800//----------------------------------------------------------------------
5801// Adds the documentation contained in 'root' to a global function
5802// with name 'name' and argument list 'args' (for overloading) and
5803// function declaration 'decl' to the corresponding member definition.
5804
5805static bool findGlobalMember(const Entry *root,
5806 const QCString &namespaceName,
5807 const QCString &type,
5808 const QCString &name,
5809 const QCString &tempArg,
5810 const QCString &,
5811 const QCString &decl,
5812 TypeSpecifier /* spec */)
5813{
5814 AUTO_TRACE("namespace='{}' type='{}' name='{}' tempArg='{}' decl='{}'",namespaceName,type,name,tempArg,decl);
5815 QCString n=name;
5816 if (n.isEmpty()) return FALSE;
5817 if (n.find("::")!=-1) return FALSE; // skip undefined class members
5818 MemberName *mn=Doxygen::functionNameLinkedMap->find(n+tempArg); // look in function dictionary
5819 if (mn==nullptr)
5820 {
5821 mn=Doxygen::functionNameLinkedMap->find(n); // try without template arguments
5822 }
5823 if (mn) // function name defined
5824 {
5825 AUTO_TRACE_ADD("Found symbol name");
5826 //int count=0;
5827 bool found=FALSE;
5828 for (const auto &md : *mn)
5829 {
5830 // If the entry has groups, then restrict the search to members which are
5831 // in one of the groups of the entry. If md is not associated with a group yet,
5832 // allow this documentation entry to add the group info.
5833 if (!root->groups.empty() && !isEntryInGroupOfMember(root, md.get(), true))
5834 {
5835 continue;
5836 }
5837
5838 const NamespaceDef *nd=nullptr;
5839 if (md->isAlias() && md->getOuterScope() &&
5840 md->getOuterScope()->definitionType()==Definition::TypeNamespace)
5841 {
5842 nd = toNamespaceDef(md->getOuterScope());
5843 }
5844 else
5845 {
5846 nd = md->getNamespaceDef();
5847 }
5848
5849 // special case for strong enums
5850 int enumNamePos=0;
5851 if (nd && md->isEnumValue() && (enumNamePos=namespaceName.findRev("::"))!=-1)
5852 { // md part of a strong enum in a namespace?
5853 QCString enumName = namespaceName.mid(enumNamePos+2);
5854 if (namespaceName.left(enumNamePos)==nd->name())
5855 {
5856 MemberName *enumMn=Doxygen::functionNameLinkedMap->find(enumName);
5857 if (enumMn)
5858 {
5859 for (const auto &emd : *enumMn)
5860 {
5861 found = emd->isStrong() && md->getEnumScope()==emd.get();
5862 if (found)
5863 {
5864 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,nullptr,FALSE,root->spec);
5865 break;
5866 }
5867 }
5868 }
5869 }
5870 if (found)
5871 {
5872 break;
5873 }
5874 }
5875 else if (nd==nullptr && md->isEnumValue()) // md part of global strong enum?
5876 {
5877 MemberName *enumMn=Doxygen::functionNameLinkedMap->find(namespaceName);
5878 if (enumMn)
5879 {
5880 for (const auto &emd : *enumMn)
5881 {
5882 found = emd->isStrong() && md->getEnumScope()==emd.get();
5883 if (found)
5884 {
5885 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,nullptr,FALSE,root->spec);
5886 break;
5887 }
5888 }
5889 }
5890 }
5891
5892 const FileDef *fd=root->fileDef();
5893 //printf("File %s\n",fd ? qPrint(fd->name()) : "<none>");
5895 if (fd)
5896 {
5897 nl = fd->getUsedNamespaces();
5898 }
5899 //printf("NamespaceList %p\n",nl);
5900
5901 // search in the list of namespaces that are imported via a
5902 // using declaration
5903 bool viaUsingDirective = nd && nl.find(nd->qualifiedName())!=nullptr;
5904
5905 if ((namespaceName.isEmpty() && nd==nullptr) || // not in a namespace
5906 (nd && nd->name()==namespaceName) || // or in the same namespace
5907 viaUsingDirective // member in 'using' namespace
5908 )
5909 {
5910 AUTO_TRACE_ADD("Try to add member '{}' to scope '{}'",md->name(),namespaceName);
5911
5912 NamespaceDef *rnd = nullptr;
5913 if (!namespaceName.isEmpty()) rnd = Doxygen::namespaceLinkedMap->find(namespaceName);
5914
5915 const ArgumentList &mdAl = md.get()->argumentList();
5916 bool matching=
5917 (mdAl.empty() && root->argList.empty()) ||
5918 md->isVariable() || md->isTypedef() || /* in case of function pointers */
5919 matchArguments2(md->getOuterScope(),md->getFileDef(),md->typeString(),&mdAl,
5920 rnd ? rnd : Doxygen::globalScope,fd,root->type,&root->argList,
5921 FALSE,root->lang);
5922
5923 // for template members we need to check if the number of
5924 // template arguments is the same, otherwise we are dealing with
5925 // different functions.
5926 if (matching && !root->tArgLists.empty())
5927 {
5928 const ArgumentList &mdTempl = md->templateArguments();
5929 if (root->tArgLists.back().size()!=mdTempl.size())
5930 {
5931 matching=FALSE;
5932 }
5933 }
5934
5935 //printf("%s<->%s\n",
5936 // qPrint(argListToString(md->argumentList())),
5937 // qPrint(argListToString(root->argList)));
5938
5939 // For static members we also check if the comment block was found in
5940 // the same file. This is needed because static members with the same
5941 // name can be in different files. Thus it would be wrong to just
5942 // put the comment block at the first syntactically matching member. If
5943 // the comment block belongs to a group of the static member, then add
5944 // the documentation even if it is in a different file.
5945 if (matching && md->isStatic() &&
5946 md->getDefFileName()!=root->fileName &&
5947 mn->size()>1 &&
5948 !isEntryInGroupOfMember(root,md.get()))
5949 {
5950 matching = FALSE;
5951 }
5952
5953 // for template member we also need to check the return type and requires
5954 if (!md->templateArguments().empty() && !root->tArgLists.empty())
5955 {
5956 //printf("Comparing return types '%s'<->'%s'\n",
5957 // md->typeString(),type);
5958 //printf("%s: Comparing '%s'<=>'%s'\n",qPrint(md->name()),qPrint(md->requiresClause()),qPrint(root->req));
5959 if (md->templateArguments().size()!=root->tArgLists.back().size() ||
5960 md->typeString()!=type ||
5961 md->requiresClause()!=root->req)
5962 {
5963 //printf(" ---> no matching\n");
5964 matching = FALSE;
5965 }
5966 }
5967
5968 if (matching) // add docs to the member
5969 {
5970 AUTO_TRACE_ADD("Match found");
5971 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,&root->argList,FALSE,root->spec);
5972 found=TRUE;
5973 break;
5974 }
5975 }
5976 }
5977 if (!found && root->relatesType!=RelatesType::Duplicate && root->section.isFunction()) // no match
5978 {
5979 QCString fullFuncDecl=decl;
5980 if (!root->argList.empty()) fullFuncDecl+=argListToString(root->argList,TRUE);
5981 QCString warnMsg = "no matching file member found for \n"+fullFuncDecl;
5982 if (mn->size()>0)
5983 {
5984 warnMsg+="\nPossible candidates:";
5985 for (const auto &md : *mn)
5986 {
5987 warnMsg+="\n '";
5988 warnMsg+=replaceAnonymousScopes(md->declaration());
5989 warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
5990 }
5991 }
5992 warn(root->fileName,root->startLine, "{}", qPrint(warnMsg));
5993 }
5994 }
5995 else // got docs for an undefined member!
5996 {
5997 if (root->type!="friend class" &&
5998 root->type!="friend struct" &&
5999 root->type!="friend union" &&
6000 root->type!="friend" &&
6001 (!Config_getBool(TYPEDEF_HIDES_STRUCT) ||
6002 root->type.find("typedef ")==-1)
6003 )
6004 {
6005 warn(root->fileName,root->startLine,
6006 "documented symbol '{}' was not declared or defined.",qPrint(decl)
6007 );
6008 }
6009 }
6010 return TRUE;
6011}
6012
6014 const ArgumentLists &srcTempArgLists,
6015 const ArgumentLists &dstTempArgLists
6016 )
6017{
6018 auto srcIt = srcTempArgLists.begin();
6019 auto dstIt = dstTempArgLists.begin();
6020 while (srcIt!=srcTempArgLists.end() && dstIt!=dstTempArgLists.end())
6021 {
6022 if ((*srcIt).size()!=(*dstIt).size()) return TRUE;
6023 ++srcIt;
6024 ++dstIt;
6025 }
6026 return FALSE;
6027}
6028
6029static bool scopeIsTemplate(const Definition *d)
6030{
6031 bool result=FALSE;
6032 //printf("> scopeIsTemplate(%s)\n",qPrint(d?d->name():"null"));
6034 {
6035 auto cd = toClassDef(d);
6036 result = cd->templateArguments().hasParameters() || cd->templateMaster()!=nullptr ||
6038 }
6039 //printf("< scopeIsTemplate=%d\n",result);
6040 return result;
6041}
6042
6044 const ArgumentLists &srcTempArgLists,
6045 const ArgumentLists &dstTempArgLists,
6046 const std::string &src
6047 )
6048{
6049 std::string dst;
6050 static const reg::Ex re(R"(\a\w*)");
6051 reg::Iterator it(src,re);
6053 //printf("type=%s\n",qPrint(sa->type));
6054 size_t p=0;
6055 for (; it!=end ; ++it) // for each word in srcType
6056 {
6057 const auto &match = *it;
6058 size_t i = match.position();
6059 size_t l = match.length();
6060 bool found=FALSE;
6061 dst+=src.substr(p,i-p);
6062 std::string name=match.str();
6063
6064 auto srcIt = srcTempArgLists.begin();
6065 auto dstIt = dstTempArgLists.begin();
6066 while (srcIt!=srcTempArgLists.end() && !found)
6067 {
6068 const ArgumentList *tdAli = nullptr;
6069 std::vector<Argument>::const_iterator tdaIt;
6070 if (dstIt!=dstTempArgLists.end())
6071 {
6072 tdAli = &(*dstIt);
6073 tdaIt = tdAli->begin();
6074 ++dstIt;
6075 }
6076
6077 const ArgumentList &tsaLi = *srcIt;
6078 for (auto tsaIt = tsaLi.begin(); tsaIt!=tsaLi.end() && !found; ++tsaIt)
6079 {
6080 Argument tsa = *tsaIt;
6081 const Argument *tda = nullptr;
6082 if (tdAli && tdaIt!=tdAli->end())
6083 {
6084 tda = &(*tdaIt);
6085 ++tdaIt;
6086 }
6087 //if (tda) printf("tsa=%s|%s tda=%s|%s\n",
6088 // qPrint(tsa.type),qPrint(tsa.name),
6089 // qPrint(tda->type),qPrint(tda->name));
6090 if (name==tsa.name.str())
6091 {
6092 if (tda && tda->name.isEmpty())
6093 {
6094 QCString tdaName = tda->name;
6095 QCString tdaType = tda->type;
6096 int vc=0;
6097 if (tdaType.startsWith("class ")) vc=6;
6098 else if (tdaType.startsWith("typename ")) vc=9;
6099 if (vc>0) // convert type=="class T" to type=="class" name=="T"
6100 {
6101 tdaName = tdaType.mid(vc);
6102 }
6103 if (!tdaName.isEmpty())
6104 {
6105 name=tdaName.str(); // substitute
6106 found=TRUE;
6107 }
6108 }
6109 }
6110 }
6111
6112 //printf(" srcList='%s' dstList='%s faList='%s'\n",
6113 // qPrint(argListToString(srclali.current())),
6114 // qPrint(argListToString(dstlali.current())),
6115 // funcTempArgList ? qPrint(argListToString(funcTempArgList)) : "<none>");
6116 ++srcIt;
6117 }
6118 dst+=name;
6119 p=i+l;
6120 }
6121 dst+=src.substr(p);
6122 //printf(" substituteTemplatesInString(%s)=%s\n",
6123 // qPrint(src),qPrint(dst));
6124 return dst;
6125}
6126
6128 const ArgumentLists &srcTempArgLists,
6129 const ArgumentLists &dstTempArgLists,
6130 const ArgumentList &src,
6131 ArgumentList &dst
6132 )
6133{
6134 auto dstIt = dst.begin();
6135 for (const Argument &sa : src)
6136 {
6137 QCString dstType = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.type.str());
6138 QCString dstArray = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.array.str());
6139 if (dstIt == dst.end())
6140 {
6141 Argument da = sa;
6142 da.type = dstType;
6143 da.array = dstArray;
6144 dst.push_back(da);
6145 dstIt = dst.end();
6146 }
6147 else
6148 {
6149 Argument da = *dstIt;
6150 da.type = dstType;
6151 da.array = dstArray;
6152 ++dstIt;
6153 }
6154 }
6159 srcTempArgLists,dstTempArgLists,
6160 src.trailingReturnType().str()));
6161 dst.setIsDeleted(src.isDeleted());
6162 dst.setRefQualifier(src.refQualifier());
6163 dst.setNoParameters(src.noParameters());
6164 //printf("substituteTemplatesInArgList: replacing %s with %s\n",
6165 // qPrint(argListToString(src)),qPrint(argListToString(dst))
6166 // );
6167}
6168
6169//-------------------------------------------------------------------------------------------
6170
6171static void addLocalObjCMethod(const Entry *root,
6172 const QCString &scopeName,
6173 const QCString &funcType,const QCString &funcName,const QCString &funcArgs,
6174 const QCString &exceptions,const QCString &funcDecl,
6175 TypeSpecifier spec)
6176{
6177 AUTO_TRACE();
6178 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
6179 ClassDefMutable *cd=nullptr;
6180 if (Config_getBool(EXTRACT_LOCAL_METHODS) && (cd=getClassMutable(scopeName)))
6181 {
6182 AUTO_TRACE_ADD("Local objective C method '{}' scopeName='{}'",root->name,scopeName);
6183 auto md = createMemberDef(
6184 root->fileName,root->startLine,root->startColumn,
6185 funcType,funcName,funcArgs,exceptions,
6186 root->protection,root->virt,root->isStatic,Relationship::Member,
6188 auto mmd = toMemberDefMutable(md.get());
6189 mmd->setTagInfo(root->tagInfo());
6190 mmd->setLanguage(root->lang);
6191 mmd->setId(root->id);
6192 mmd->makeImplementationDetail();
6193 mmd->setMemberClass(cd);
6194 mmd->setDefinition(funcDecl);
6196 mmd->addQualifiers(root->qualifiers);
6197 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6198 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6199 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6200 mmd->setDocsForDefinition(!root->proto);
6201 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6202 mmd->addSectionsToDefinition(root->anchors);
6203 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6204 FileDef *fd=root->fileDef();
6205 mmd->setBodyDef(fd);
6206 mmd->setMemberSpecifiers(spec);
6207 mmd->setVhdlSpecifiers(root->vhdlSpec);
6208 mmd->setMemberGroupId(root->mGrpId);
6209 cd->insertMember(md.get());
6210 cd->insertUsedFile(fd);
6211 mmd->setRefItems(root->sli);
6212 mmd->setRequirementReferences(root->rqli);
6213
6215 mn->push_back(std::move(md));
6216 }
6217 else
6218 {
6219 // local objective C method found for class without interface
6220 }
6221}
6222
6223//-------------------------------------------------------------------------------------------
6224
6225static void addMemberFunction(const Entry *root,
6226 MemberName *mn,
6227 const QCString &scopeName,
6228 const QCString &namespaceName,
6229 const QCString &className,
6230 const QCString &funcTyp,
6231 const QCString &funcName,
6232 const QCString &funcArgs,
6233 const QCString &funcTempList,
6234 const QCString &exceptions,
6235 const QCString &type,
6236 const QCString &args,
6237 bool isFriend,
6238 TypeSpecifier spec,
6239 const QCString &relates,
6240 const QCString &funcDecl,
6241 bool overloaded,
6242 bool isFunc)
6243{
6244 AUTO_TRACE();
6245 QCString funcType = funcTyp;
6246 int count=0;
6247 int noMatchCount=0;
6248 bool memFound=FALSE;
6249 for (const auto &imd : *mn)
6250 {
6251 MemberDefMutable *md = toMemberDefMutable(imd.get());
6252 if (md==nullptr) continue;
6254 if (cd==nullptr) continue;
6255 //AUTO_TRACE_ADD("member definition found, scope needed='{}' scope='{}' args='{}' fileName='{}'",
6256 // scopeName, cd->name(), md->argsString(), root->fileName);
6257 FileDef *fd=root->fileDef();
6258 NamespaceDef *nd=nullptr;
6259 if (!namespaceName.isEmpty()) nd=getResolvedNamespace(namespaceName);
6260
6261 //printf("scopeName %s->%s\n",qPrint(scopeName),
6262 // qPrint(stripTemplateSpecifiersFromScope(scopeName,FALSE)));
6263
6264 // if the member we are searching for is an enum value that is part of
6265 // a "strong" enum, we need to look into the fields of the enum for a match
6266 int enumNamePos=0;
6267 if (md->isEnumValue() && (enumNamePos=className.findRev("::"))!=-1)
6268 {
6269 QCString enumName = className.mid(enumNamePos+2);
6270 QCString fullScope = className.left(enumNamePos);
6271 if (!namespaceName.isEmpty()) fullScope.prepend(namespaceName+"::");
6272 if (fullScope==cd->name())
6273 {
6274 MemberName *enumMn=Doxygen::memberNameLinkedMap->find(enumName);
6275 //printf("enumMn(%s)=%p\n",qPrint(className),(void*)enumMn);
6276 if (enumMn)
6277 {
6278 for (const auto &emd : *enumMn)
6279 {
6280 memFound = emd->isStrong() && md->getEnumScope()==emd.get();
6281 if (memFound)
6282 {
6283 addMemberDocs(root,md,funcDecl,nullptr,overloaded,spec);
6284 count++;
6285 }
6286 if (memFound) break;
6287 }
6288 }
6289 }
6290 }
6291 if (memFound) break;
6292
6293 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
6294 if (tcd==nullptr && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
6295 {
6296 // don't be fooled by anonymous scopes
6297 tcd=cd;
6298 }
6299 //printf("Looking for %s inside nd=%s result=%s cd=%s\n",
6300 // qPrint(scopeName),nd?qPrint(nd->name()):"<none>",tcd?qPrint(tcd->name()):"",qPrint(cd->name()));
6301
6302 if (cd && tcd==cd) // member's classes match
6303 {
6304 AUTO_TRACE_ADD("class definition '{}' found",cd->name());
6305
6306 // get the template parameter lists found at the member declaration
6307 ArgumentLists declTemplArgs = cd->getTemplateParameterLists();
6308 const ArgumentList &templAl = md->templateArguments();
6309 if (!templAl.empty())
6310 {
6311 declTemplArgs.push_back(templAl);
6312 }
6313
6314 // get the template parameter lists found at the member definition
6315 const ArgumentLists &defTemplArgs = root->tArgLists;
6316 //printf("defTemplArgs=%p\n",defTemplArgs);
6317
6318 // do we replace the decl argument lists with the def argument lists?
6319 bool substDone=false;
6320 ArgumentList argList;
6321
6322 /* substitute the occurrences of class template names in the
6323 * argument list before matching
6324 */
6325 const ArgumentList &mdAl = md->argumentList();
6326 if (declTemplArgs.size()>0 && declTemplArgs.size()==defTemplArgs.size())
6327 {
6328 /* the function definition has template arguments
6329 * and the class definition also has template arguments, so
6330 * we must substitute the template names of the class by that
6331 * of the function definition before matching.
6332 */
6333 substituteTemplatesInArgList(declTemplArgs,defTemplArgs,mdAl,argList);
6334
6335 substDone=TRUE;
6336 }
6337 else /* no template arguments, compare argument lists directly */
6338 {
6339 argList = mdAl;
6340 }
6341
6342 bool matching=
6343 md->isVariable() || md->isTypedef() || // needed for function pointers
6345 md->getClassDef(),md->getFileDef(),md->typeString(),&argList,
6346 cd,fd,root->type,&root->argList,
6347 TRUE,root->lang);
6348
6349 AUTO_TRACE_ADD("matching '{}'<=>'{}' className='{}' namespaceName='{}' result={}",
6350 argListToString(argList,TRUE),argListToString(root->argList,TRUE),className,namespaceName,matching);
6351
6352 if (md->getLanguage()==SrcLangExt::ObjC && md->isVariable() && root->section.isFunction())
6353 {
6354 matching = FALSE; // don't match methods and attributes with the same name
6355 }
6356
6357 // for template member we also need to check the return type
6358 if (!md->templateArguments().empty() && !root->tArgLists.empty())
6359 {
6360 QCString memType = md->typeString();
6361 memType.stripPrefix("static "); // see bug700696
6363 className+"::",""); // see bug700693 & bug732594
6365 className+"::",""); // see bug758900
6366 if (memType=="auto" && !argList.trailingReturnType().isEmpty())
6367 {
6368 memType = argList.trailingReturnType();
6369 memType.stripPrefix(" -> ");
6370 }
6371 if (funcType=="auto" && !root->argList.trailingReturnType().isEmpty())
6372 {
6373 funcType = root->argList.trailingReturnType();
6374 funcType.stripPrefix(" -> ");
6376 substDone=true;
6377 }
6378 AUTO_TRACE_ADD("Comparing return types '{}'<->'{}' #args {}<->{}",
6379 memType,funcType,md->templateArguments().size(),root->tArgLists.back().size());
6380 if (md->templateArguments().size()!=root->tArgLists.back().size() || memType!=funcType)
6381 {
6382 //printf(" ---> no matching\n");
6383 matching = FALSE;
6384 }
6385 }
6386 else if (defTemplArgs.size()>declTemplArgs.size())
6387 {
6388 AUTO_TRACE_ADD("Different number of template arguments {} vs {}",defTemplArgs.size(),declTemplArgs.size());
6389 // avoid matching a non-template function in a template class against a
6390 // template function with the same name and parameters, see issue #10184
6391 substDone = false;
6392 matching = false;
6393 }
6394 bool rootIsUserDoc = root->section.isMemberDoc();
6395 bool classIsTemplate = scopeIsTemplate(md->getClassDef());
6396 bool mdIsTemplate = md->templateArguments().hasParameters();
6397 bool classOrMdIsTemplate = mdIsTemplate || classIsTemplate;
6398 bool rootIsTemplate = !root->tArgLists.empty();
6399 //printf("classIsTemplate=%d mdIsTemplate=%d rootIsTemplate=%d\n",classIsTemplate,mdIsTemplate,rootIsTemplate);
6400 if (!rootIsUserDoc && // don't check out-of-line @fn references, see bug722457
6401 (mdIsTemplate || rootIsTemplate) && // either md or root is a template
6402 ((classOrMdIsTemplate && !rootIsTemplate) || (!classOrMdIsTemplate && rootIsTemplate))
6403 )
6404 {
6405 // Method with template return type does not match method without return type
6406 // even if the parameters are the same. See also bug709052
6407 AUTO_TRACE_ADD("Comparing return types: template v.s. non-template");
6408 matching = FALSE;
6409 }
6410
6411 AUTO_TRACE_ADD("Match results of matchArguments2='{}' substDone='{}'",matching,substDone);
6412
6413 if (substDone) // found a new argument list
6414 {
6415 if (matching) // replace member's argument list
6416 {
6418 md->moveArgumentList(std::make_unique<ArgumentList>(argList));
6419 }
6420 else // no match
6421 {
6422 if (!funcTempList.isEmpty() &&
6423 isSpecialization(declTemplArgs,defTemplArgs))
6424 {
6425 // check if we are dealing with a partial template
6426 // specialization. In this case we add it to the class
6427 // even though the member arguments do not match.
6428
6429 addMethodToClass(root,cd,type,md->name(),args,isFriend,
6430 md->protection(),md->isStatic(),md->virtualness(),spec,relates);
6431 return;
6432 }
6433 }
6434 }
6435 if (matching)
6436 {
6437 addMemberDocs(root,md,funcDecl,nullptr,overloaded,spec);
6438 count++;
6439 memFound=TRUE;
6440 }
6441 }
6442 else if (cd && cd!=tcd) // we did find a class with the same name as cd
6443 // but in a different namespace
6444 {
6445 noMatchCount++;
6446 }
6447
6448 if (memFound) break;
6449 }
6450 if (count==0 && root->parent() && root->parent()->section.isObjcImpl())
6451 {
6452 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
6453 return;
6454 }
6455 if (count==0 && !(isFriend && funcType=="class"))
6456 {
6457 int candidates=0;
6458 const ClassDef *ecd = nullptr, *ucd = nullptr;
6459 MemberDef *emd = nullptr, *umd = nullptr;
6460 //printf("Assume template class\n");
6461 for (const auto &md : *mn)
6462 {
6463 MemberDef *cmd=md.get();
6465 ClassDefMutable *ccd=cdmdm ? cdmdm->getClassDefMutable() : nullptr;
6466 //printf("ccd->name()==%s className=%s\n",qPrint(ccd->name()),qPrint(className));
6467 if (ccd!=nullptr && rightScopeMatch(ccd->name(),className))
6468 {
6469 const ArgumentList &templAl = md->templateArguments();
6470 if (!root->tArgLists.empty() && !templAl.empty() &&
6471 root->tArgLists.back().size()<=templAl.size())
6472 {
6473 AUTO_TRACE_ADD("add template specialization");
6474 addMethodToClass(root,ccd,type,md->name(),args,isFriend,
6475 root->protection,root->isStatic,root->virt,spec,relates);
6476 return;
6477 }
6478 if (argListToString(md->argumentList(),FALSE,FALSE) ==
6480 { // exact argument list match -> remember
6481 ucd = ecd = ccd;
6482 umd = emd = cmd;
6483 AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': exact match",
6484 className,ccd->name(),md->argsString());
6485 }
6486 else // arguments do not match, but member name and scope do -> remember
6487 {
6488 ucd = ccd;
6489 umd = cmd;
6490 AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': no match",
6491 className,ccd->name(),md->argsString());
6492 }
6493 candidates++;
6494 }
6495 }
6496 bool strictProtoMatching = Config_getBool(STRICT_PROTO_MATCHING);
6497 if (!strictProtoMatching)
6498 {
6499 if (candidates==1 && ucd && umd)
6500 {
6501 // we didn't find an actual match on argument lists, but there is only 1 member with this
6502 // name in the same scope, so that has to be the one.
6503 addMemberDocs(root,toMemberDefMutable(umd),funcDecl,nullptr,overloaded,spec);
6504 return;
6505 }
6506 else if (candidates>1 && ecd && emd)
6507 {
6508 // we didn't find a unique match using type resolution,
6509 // but one of the matches has the exact same signature so
6510 // we take that one.
6511 addMemberDocs(root,toMemberDefMutable(emd),funcDecl,nullptr,overloaded,spec);
6512 return;
6513 }
6514 }
6515
6516 QCString warnMsg = "no ";
6517 if (noMatchCount>1) warnMsg+="uniquely ";
6518 warnMsg+="matching class member found for \n";
6519
6520 for (const ArgumentList &al : root->tArgLists)
6521 {
6522 warnMsg+=" template ";
6523 warnMsg+=tempArgListToString(al,root->lang);
6524 warnMsg+='\n';
6525 }
6526
6527 QCString fullFuncDecl=funcDecl;
6528 if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
6529
6530 warnMsg+=" ";
6531 warnMsg+=fullFuncDecl;
6532
6533 if (candidates>0 || noMatchCount>=1)
6534 {
6535 warnMsg+="\nPossible candidates:";
6536
6537 NamespaceDef *nd=nullptr;
6538 if (!namespaceName.isEmpty()) nd=getResolvedNamespace(namespaceName);
6539 FileDef *fd=root->fileDef();
6540
6541 for (const auto &md : *mn)
6542 {
6543 const ClassDef *cd=md->getClassDef();
6544 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
6545 if (tcd==nullptr && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
6546 {
6547 // don't be fooled by anonymous scopes
6548 tcd=cd;
6549 }
6550 if (cd!=nullptr && (rightScopeMatch(cd->name(),className) || (cd!=tcd)))
6551 {
6552 warnMsg+='\n';
6553 const ArgumentList &templAl = md->templateArguments();
6554 warnMsg+=" '";
6555 if (templAl.hasParameters())
6556 {
6557 warnMsg+="template ";
6558 warnMsg+=tempArgListToString(templAl,root->lang);
6559 warnMsg+='\n';
6560 warnMsg+=" ";
6561 }
6562 if (!md->typeString().isEmpty())
6563 {
6564 warnMsg+=md->typeString();
6565 warnMsg+=' ';
6566 }
6568 if (!qScope.isEmpty())
6569 warnMsg+=qScope+"::"+md->name();
6570 warnMsg+=md->argsString();
6571 warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
6572 }
6573 }
6574 }
6575 warn(root->fileName,root->startLine,"{}",warnMsg);
6576 }
6577}
6578
6579//-------------------------------------------------------------------------------------------
6580
6581static void addMemberSpecialization(const Entry *root,
6582 MemberName *mn,
6583 ClassDefMutable *cd,
6584 const QCString &funcType,
6585 const QCString &funcName,
6586 const QCString &funcArgs,
6587 const QCString &funcDecl,
6588 const QCString &exceptions,
6589 TypeSpecifier spec
6590 )
6591{
6592 AUTO_TRACE("funcType={} funcName={} funcArgs={} funcDecl={} spec={}",funcType,funcName,funcArgs,funcDecl,spec);
6593 MemberDef *declMd=nullptr;
6594 for (const auto &md : *mn)
6595 {
6596 if (md->getClassDef()==cd)
6597 {
6598 // TODO: we should probably also check for matching arguments
6599 declMd = md.get();
6600 break;
6601 }
6602 }
6604 ArgumentList tArgList;
6605 // getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6606 auto md = createMemberDef(
6607 root->fileName,root->startLine,root->startColumn,
6608 funcType,funcName,funcArgs,exceptions,
6609 declMd ? declMd->protection() : root->protection,
6610 root->virt,root->isStatic,Relationship::Member,
6611 mtype,tArgList,root->argList,root->metaData);
6612 auto mmd = toMemberDefMutable(md.get());
6613 //printf("new specialized member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6614 mmd->setTagInfo(root->tagInfo());
6615 mmd->setLanguage(root->lang);
6616 mmd->setId(root->id);
6617 mmd->setMemberClass(cd);
6618 mmd->setTemplateSpecialization(TRUE);
6619 mmd->setTypeConstraints(root->typeConstr);
6620 mmd->setDefinition(funcDecl);
6622 mmd->addQualifiers(root->qualifiers);
6623 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6624 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6625 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6626 mmd->setDocsForDefinition(!root->proto);
6627 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6628 mmd->addSectionsToDefinition(root->anchors);
6629 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6630 FileDef *fd=root->fileDef();
6631 mmd->setBodyDef(fd);
6632 mmd->setMemberSpecifiers(spec);
6633 mmd->setVhdlSpecifiers(root->vhdlSpec);
6634 mmd->setMemberGroupId(root->mGrpId);
6635 cd->insertMember(md.get());
6636 mmd->setRefItems(root->sli);
6637 mmd->setRequirementReferences(root->rqli);
6638
6639 mn->push_back(std::move(md));
6640}
6641
6642//-------------------------------------------------------------------------------------------
6643
6644static void addOverloaded(const Entry *root,MemberName *mn,
6645 const QCString &funcType,const QCString &funcName,const QCString &funcArgs,
6646 const QCString &funcDecl,const QCString &exceptions,TypeSpecifier spec)
6647{
6648 // for unique overloaded member we allow the class to be
6649 // omitted, this is to be Qt compatible. Using this should
6650 // however be avoided, because it is error prone
6651 bool sameClass=false;
6652 if (mn->size()>0)
6653 {
6654 // check if all members with the same name are also in the same class
6655 sameClass = std::equal(mn->begin()+1,mn->end(),mn->begin(),
6656 [](const auto &md1,const auto &md2)
6657 { return md1->getClassDef()->name()==md2->getClassDef()->name(); });
6658 }
6659 if (sameClass)
6660 {
6661 MemberDefMutable *mdm = toMemberDefMutable(mn->front().get());
6662 ClassDefMutable *cd = mdm ? mdm->getClassDefMutable() : nullptr;
6663 if (cd==nullptr) return;
6664
6666 if (root->mtype==MethodTypes::Signal) mtype=MemberType::Signal;
6667 else if (root->mtype==MethodTypes::Slot) mtype=MemberType::Slot;
6668 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType::DCOP;
6669
6670 // new overloaded member function
6671 std::unique_ptr<ArgumentList> tArgList =
6672 getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6673 //printf("new related member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6674 auto md = createMemberDef(
6675 root->fileName,root->startLine,root->startColumn,
6676 funcType,funcName,funcArgs,exceptions,
6677 root->protection,root->virt,root->isStatic,Relationship::Related,
6678 mtype,tArgList ? *tArgList : ArgumentList(),root->argList,root->metaData);
6679 auto mmd = toMemberDefMutable(md.get());
6680 mmd->setTagInfo(root->tagInfo());
6681 mmd->setLanguage(root->lang);
6682 mmd->setId(root->id);
6683 mmd->setTypeConstraints(root->typeConstr);
6684 mmd->setMemberClass(cd);
6685 mmd->setDefinition(funcDecl);
6687 mmd->addQualifiers(root->qualifiers);
6689 doc+="<p>";
6690 doc+=root->doc;
6691 mmd->setDocumentation(doc,root->docFile,root->docLine);
6692 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6693 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6694 mmd->setDocsForDefinition(!root->proto);
6695 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6696 mmd->addSectionsToDefinition(root->anchors);
6697 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6698 FileDef *fd=root->fileDef();
6699 mmd->setBodyDef(fd);
6700 mmd->setMemberSpecifiers(spec);
6701 mmd->setVhdlSpecifiers(root->vhdlSpec);
6702 mmd->setMemberGroupId(root->mGrpId);
6703 cd->insertMember(md.get());
6704 cd->insertUsedFile(fd);
6705 mmd->setRefItems(root->sli);
6706 mmd->setRequirementReferences(root->rqli);
6707
6708 mn->push_back(std::move(md));
6709 }
6710}
6711
6712static void insertMemberAlias(Definition *outerScope,const MemberDef *md)
6713{
6714 if (outerScope && outerScope!=Doxygen::globalScope)
6715 {
6716 auto aliasMd = createMemberDefAlias(outerScope,md);
6717 if (outerScope->definitionType()==Definition::TypeClass)
6718 {
6719 ClassDefMutable *cdm = toClassDefMutable(outerScope);
6720 if (cdm)
6721 {
6722 cdm->insertMember(aliasMd.get());
6723 }
6724 }
6725 else if (outerScope->definitionType()==Definition::TypeNamespace)
6726 {
6727 NamespaceDefMutable *ndm = toNamespaceDefMutable(outerScope);
6728 if (ndm)
6729 {
6730 ndm->insertMember(aliasMd.get());
6731 }
6732 }
6733 else if (outerScope->definitionType()==Definition::TypeFile)
6734 {
6735 toFileDef(outerScope)->insertMember(aliasMd.get());
6736 }
6737 if (aliasMd)
6738 {
6739 Doxygen::functionNameLinkedMap->add(md->name())->push_back(std::move(aliasMd));
6740 }
6741 }
6742}
6743
6744//-------------------------------------------------------------------------------------------
6745
6746/*! This function tries to find a member (in a documented class/file/namespace)
6747 * that corresponds to the function/variable declaration given in \a funcDecl.
6748 *
6749 * The boolean \a overloaded is used to specify whether or not a standard
6750 * overload documentation line should be generated.
6751 *
6752 * The boolean \a isFunc is a hint that indicates that this is a function
6753 * instead of a variable or typedef.
6754 */
6755static void findMember(const Entry *root,
6756 const QCString &relates,
6757 const QCString &type,
6758 const QCString &args,
6759 QCString funcDecl,
6760 bool overloaded,
6761 bool isFunc
6762 )
6763{
6764 AUTO_TRACE("root='{}' funcDecl='{}' related='{}' overload={} isFunc={} mGrpId={} #tArgList={} spec={} lang={}",
6765 root->name, funcDecl, relates, overloaded, isFunc, root->mGrpId, root->tArgLists.size(),
6766 root->spec, root->lang);
6767
6768 QCString scopeName;
6769 QCString className;
6770 QCString namespaceName;
6771 QCString funcType;
6772 QCString funcName;
6773 QCString funcArgs;
6774 QCString funcTempList;
6775 QCString exceptions;
6776 QCString funcSpec;
6777 bool isRelated=false;
6778 bool isMemberOf=false;
6779 bool isFriend=false;
6780 bool done=false;
6781 TypeSpecifier spec = root->spec;
6782 while (!done)
6783 {
6784 done=true;
6785 if (funcDecl.stripPrefix("friend ")) // treat friends as related members
6786 {
6787 isFriend=true;
6788 done=false;
6789 }
6790 if (funcDecl.stripPrefix("inline "))
6791 {
6792 spec.setInline(true);
6793 done=false;
6794 }
6795 if (funcDecl.stripPrefix("explicit "))
6796 {
6797 spec.setExplicit(true);
6798 done=false;
6799 }
6800 if (funcDecl.stripPrefix("mutable "))
6801 {
6802 spec.setMutable(true);
6803 done=false;
6804 }
6805 if (funcDecl.stripPrefix("thread_local "))
6806 {
6807 spec.setThreadLocal(true);
6808 done=false;
6809 }
6810 if (funcDecl.stripPrefix("virtual "))
6811 {
6812 done=false;
6813 }
6814 }
6815
6816 // delete any ; from the function declaration
6817 int sep=0;
6818 while ((sep=funcDecl.find(';'))!=-1)
6819 {
6820 funcDecl=(funcDecl.left(sep)+funcDecl.right(funcDecl.length()-sep-1)).stripWhiteSpace();
6821 }
6822
6823 // make sure the first character is a space to simplify searching.
6824 if (!funcDecl.isEmpty() && funcDecl[0]!=' ') funcDecl.prepend(" ");
6825
6826 // remove some superfluous spaces
6827 funcDecl= substitute(
6828 substitute(
6829 substitute(funcDecl,"~ ","~"),
6830 ":: ","::"
6831 ),
6832 " ::","::"
6833 ).stripWhiteSpace();
6834
6835 //printf("funcDecl='%s'\n",qPrint(funcDecl));
6836 if (isFriend && funcDecl.startsWith("class "))
6837 {
6838 //printf("friend class\n");
6839 funcDecl=funcDecl.right(funcDecl.length()-6);
6840 funcName = funcDecl;
6841 }
6842 else if (isFriend && funcDecl.startsWith("struct "))
6843 {
6844 funcDecl=funcDecl.right(funcDecl.length()-7);
6845 funcName = funcDecl;
6846 }
6847 else
6848 {
6849 // extract information from the declarations
6850 parseFuncDecl(funcDecl,root->lang,scopeName,funcType,funcName,
6851 funcArgs,funcTempList,exceptions
6852 );
6853 }
6854
6855 // the class name can also be a namespace name, we decide this later.
6856 // if a related class name is specified and the class name could
6857 // not be derived from the function declaration, then use the
6858 // related field.
6859 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}' funcType='{}' funcName='{}' funcArgs='{}'",
6860 scopeName,className,namespaceName,funcType,funcName,funcArgs);
6861 if (!relates.isEmpty())
6862 { // related member, prefix user specified scope
6863 isRelated=TRUE;
6864 isMemberOf=(root->relatesType == RelatesType::MemberOf);
6865 if (getClass(relates)==nullptr && !scopeName.isEmpty())
6866 {
6867 scopeName= mergeScopes(scopeName,relates);
6868 }
6869 else
6870 {
6871 scopeName = relates;
6872 }
6873 }
6874
6875 if (relates.isEmpty() && root->parent() &&
6876 (root->parent()->section.isScope() || root->parent()->section.isObjcImpl()) &&
6877 !root->parent()->name.isEmpty()) // see if we can combine scopeName
6878 // with the scope in which it was found
6879 {
6880 QCString joinedName = root->parent()->name+"::"+scopeName;
6881 if (!scopeName.isEmpty() &&
6882 (getClass(joinedName) || Doxygen::namespaceLinkedMap->find(joinedName)))
6883 {
6884 scopeName = joinedName;
6885 }
6886 else
6887 {
6888 scopeName = mergeScopes(root->parent()->name,scopeName);
6889 }
6890 }
6891 else // see if we can prefix a namespace or class that is used from the file
6892 {
6893 FileDef *fd=root->fileDef();
6894 if (fd)
6895 {
6896 for (const auto &fnd : fd->getUsedNamespaces())
6897 {
6898 QCString joinedName = fnd->name()+"::"+scopeName;
6899 if (Doxygen::namespaceLinkedMap->find(joinedName))
6900 {
6901 scopeName=joinedName;
6902 break;
6903 }
6904 }
6905 }
6906 }
6908 removeRedundantWhiteSpace(scopeName),false,&funcSpec,QCString(),false);
6909
6910 // funcSpec contains the last template specifiers of the given scope.
6911 // If this method does not have any template arguments or they are
6912 // empty while funcSpec is not empty we assume this is a
6913 // specialization of a method. If not, we clear the funcSpec and treat
6914 // this as a normal method of a template class.
6915 if (!(root->tArgLists.size()>0 &&
6916 root->tArgLists.front().size()==0
6917 )
6918 )
6919 {
6920 funcSpec.clear();
6921 }
6922
6923 //namespaceName=removeAnonymousScopes(namespaceName);
6924 if (!Config_getBool(EXTRACT_ANON_NSPACES) && scopeName.find('@')!=-1) return; // skip stuff in anonymous namespace...
6925
6926 // split scope into a namespace and a class part
6927 extractNamespaceName(scopeName,className,namespaceName,TRUE);
6928 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}'",scopeName,className,namespaceName);
6929
6930 //printf("namespaceName='%s' className='%s'\n",qPrint(namespaceName),qPrint(className));
6931 // merge class and namespace scopes again
6932 scopeName.clear();
6933 if (!namespaceName.isEmpty())
6934 {
6935 if (className.isEmpty())
6936 {
6937 scopeName=namespaceName;
6938 }
6939 else if (!relates.isEmpty() || // relates command with explicit scope
6940 !getClass(className)) // class name only exists in a namespace
6941 {
6942 scopeName=namespaceName+"::"+className;
6943 }
6944 else
6945 {
6946 scopeName=className;
6947 }
6948 }
6949 else if (!className.isEmpty())
6950 {
6951 scopeName=className;
6952 }
6953 //printf("new scope='%s'\n",qPrint(scopeName));
6954
6955 QCString tempScopeName=scopeName;
6956 ClassDefMutable *cd=getClassMutable(scopeName);
6957 if (cd)
6958 {
6959 if (funcSpec.isEmpty())
6960 {
6961 uint32_t argListIndex=0;
6962 tempScopeName=cd->qualifiedNameWithTemplateParameters(&root->tArgLists,&argListIndex);
6963 }
6964 else
6965 {
6966 tempScopeName=scopeName+funcSpec;
6967 }
6968 }
6969 //printf("scopeName=%s cd=%p root->tArgLists=%p result=%s\n",
6970 // qPrint(scopeName),cd,root->tArgLists,qPrint(tempScopeName));
6971
6972 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
6973 // rebuild the function declaration (needed to get the scope right).
6974 if (!scopeName.isEmpty() && !isRelated && !isFriend && !Config_getBool(HIDE_SCOPE_NAMES) && root->lang!=SrcLangExt::Python)
6975 {
6976 if (!funcType.isEmpty())
6977 {
6978 if (isFunc) // a function -> we use argList for the arguments
6979 {
6980 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcTempList;
6981 }
6982 else
6983 {
6984 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcArgs;
6985 }
6986 }
6987 else
6988 {
6989 if (isFunc) // a function => we use argList for the arguments
6990 {
6991 funcDecl=tempScopeName+"::"+funcName+funcTempList;
6992 }
6993 else // variable => add 'argument' list
6994 {
6995 funcDecl=tempScopeName+"::"+funcName+funcArgs;
6996 }
6997 }
6998 }
6999 else // build declaration without scope
7000 {
7001 if (!funcType.isEmpty()) // but with a type
7002 {
7003 if (isFunc) // function => omit argument list
7004 {
7005 funcDecl=funcType+" "+funcName+funcTempList;
7006 }
7007 else // variable => add 'argument' list
7008 {
7009 funcDecl=funcType+" "+funcName+funcArgs;
7010 }
7011 }
7012 else // no type
7013 {
7014 if (isFunc)
7015 {
7016 funcDecl=funcName+funcTempList;
7017 }
7018 else
7019 {
7020 funcDecl=funcName+funcArgs;
7021 }
7022 }
7023 }
7024
7025 if (funcType=="template class" && !funcTempList.isEmpty())
7026 return; // ignore explicit template instantiations
7027
7028 AUTO_TRACE_ADD("Parse results: namespaceName='{}' className=`{}` funcType='{}' funcSpec='{}' "
7029 " funcName='{}' funcArgs='{}' funcTempList='{}' funcDecl='{}' relates='{}'"
7030 " exceptions='{}' isRelated={} isMemberOf={} isFriend={} isFunc={}",
7031 namespaceName, className, funcType, funcSpec,
7032 funcName, funcArgs, funcTempList, funcDecl, relates,
7033 exceptions, isRelated, isMemberOf, isFriend, isFunc);
7034
7035 if (!funcName.isEmpty()) // function name is valid
7036 {
7037 // check if 'className' is actually a scoped enum, in which case we need to
7038 // process it as a global, see issue #6471
7039 bool strongEnum = false;
7040 MemberName *mn=nullptr;
7041 if (!className.isEmpty() && (mn=Doxygen::functionNameLinkedMap->find(className)))
7042 {
7043 for (const auto &imd : *mn)
7044 {
7045 MemberDefMutable *md = toMemberDefMutable(imd.get());
7046 Definition *mdScope = nullptr;
7047 if (md && md->isEnumerate() && md->isStrong() && (mdScope=md->getOuterScope()) &&
7048 // need filter for the correct scope, see issue #9668
7049 ((namespaceName.isEmpty() && mdScope==Doxygen::globalScope) || (mdScope->name()==namespaceName)))
7050 {
7051 AUTO_TRACE_ADD("'{}' is a strong enum! (namespace={} md->getOuterScope()->name()={})",md->name(),namespaceName,md->getOuterScope()->name());
7052 strongEnum = true;
7053 // pass the scope name name as a 'namespace' to the findGlobalMember function
7054 if (!namespaceName.isEmpty())
7055 {
7056 namespaceName+="::"+className;
7057 }
7058 else
7059 {
7060 namespaceName=className;
7061 }
7062 }
7063 }
7064 }
7065
7066 if (funcName.startsWith("operator ")) // strip class scope from cast operator
7067 {
7068 funcName = substitute(funcName,className+"::","");
7069 }
7070 mn = nullptr;
7071 if (!funcTempList.isEmpty()) // try with member specialization
7072 {
7073 mn=Doxygen::memberNameLinkedMap->find(funcName+funcTempList);
7074 }
7075 if (mn==nullptr) // try without specialization
7076 {
7077 mn=Doxygen::memberNameLinkedMap->find(funcName);
7078 }
7079 if (!isRelated && !strongEnum && mn) // function name already found
7080 {
7081 AUTO_TRACE_ADD("member name exists ({} members with this name)",mn->size());
7082 if (!className.isEmpty()) // class name is valid
7083 {
7084 if (funcSpec.isEmpty()) // not a member specialization
7085 {
7086 addMemberFunction(root,mn,scopeName,namespaceName,className,funcType,funcName,
7087 funcArgs,funcTempList,exceptions,
7088 type,args,isFriend,spec,relates,funcDecl,overloaded,isFunc);
7089 }
7090 else if (cd) // member specialization
7091 {
7092 addMemberSpecialization(root,mn,cd,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
7093 }
7094 else
7095 {
7096 //printf("*** Specialized member %s of unknown scope %s%s found!\n",
7097 // qPrint(scopeName),qPrint(funcName),qPrint(funcArgs));
7098 }
7099 }
7100 else if (overloaded) // check if the function belongs to only one class
7101 {
7102 addOverloaded(root,mn,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
7103 }
7104 else // unrelated function with the same name as a member
7105 {
7106 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
7107 {
7108 QCString fullFuncDecl=funcDecl;
7109 if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
7110 warn(root->fileName,root->startLine,
7111 "Cannot determine class for function\n{}",
7112 fullFuncDecl
7113 );
7114 }
7115 }
7116 }
7117 else if (isRelated && !relates.isEmpty())
7118 {
7119 AUTO_TRACE_ADD("related function scopeName='{}' className='{}'",scopeName,className);
7120 if (className.isEmpty()) className=relates;
7121 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
7122 if ((cd=getClassMutable(scopeName)))
7123 {
7124 bool newMember=TRUE; // assume we have a new member
7125 MemberDefMutable *mdDefine=nullptr;
7126 {
7127 mn = Doxygen::functionNameLinkedMap->find(funcName);
7128 if (mn)
7129 {
7130 for (const auto &imd : *mn)
7131 {
7132 MemberDefMutable *md = toMemberDefMutable(imd.get());
7133 if (md && md->isDefine())
7134 {
7135 mdDefine = md;
7136 break;
7137 }
7138 }
7139 }
7140 }
7141
7142 if (mdDefine) // macro definition is already created by the preprocessor and inserted as a file member
7143 {
7144 //printf("moving #define %s into class %s\n",qPrint(mdDefine->name()),qPrint(cd->name()));
7145
7146 // take mdDefine from the Doxygen::functionNameLinkedMap (without deleting the data)
7147 auto mdDefineTaken = Doxygen::functionNameLinkedMap->take(funcName,mdDefine);
7148 // insert it as a class member
7149 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==nullptr)
7150 {
7151 mn=Doxygen::memberNameLinkedMap->add(funcName);
7152 }
7153
7154 if (mdDefine->getFileDef())
7155 {
7156 mdDefine->getFileDef()->removeMember(mdDefine);
7157 }
7158 mdDefine->makeRelated();
7159 mdDefine->setMemberClass(cd);
7160 mdDefine->moveTo(cd);
7161 cd->insertMember(mdDefine);
7162 // also insert the member as an alias in the parent's scope, so it can be referenced also without cd's scope
7163 insertMemberAlias(cd->getOuterScope(),mdDefine);
7164 mn->push_back(std::move(mdDefineTaken));
7165 }
7166 else // normal member, needs to be created and added to the class
7167 {
7168 FileDef *fd=root->fileDef();
7169
7170 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==nullptr)
7171 {
7172 mn=Doxygen::memberNameLinkedMap->add(funcName);
7173 }
7174 else
7175 {
7176 // see if we got another member with matching arguments
7177 MemberDefMutable *rmd_found = nullptr;
7178 for (const auto &irmd : *mn)
7179 {
7180 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
7181 if (rmd)
7182 {
7183 const ArgumentList &rmdAl = rmd->argumentList();
7184
7185 newMember=
7186 className!=rmd->getOuterScope()->name() ||
7187 !matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmdAl,
7188 cd,fd,root->type,&root->argList,
7189 TRUE,root->lang);
7190 if (!newMember)
7191 {
7192 rmd_found = rmd;
7193 }
7194 }
7195 }
7196 if (rmd_found) // member already exists as rmd -> add docs
7197 {
7198 AUTO_TRACE_ADD("addMemberDocs for related member {}",root->name);
7199 addMemberDocs(root,rmd_found,funcDecl,nullptr,overloaded,spec);
7200 newMember=false;
7201 }
7202 }
7203
7204 if (newMember) // need to create a new member
7205 {
7207 switch (root->mtype)
7208 {
7209 case MethodTypes::Method: mtype = MemberType::Function; break;
7210 case MethodTypes::Signal: mtype = MemberType::Signal; break;
7211 case MethodTypes::Slot: mtype = MemberType::Slot; break;
7212 case MethodTypes::DCOP: mtype = MemberType::DCOP; break;
7213 case MethodTypes::Property: mtype = MemberType::Property; break;
7214 case MethodTypes::Event: mtype = MemberType::Event; break;
7215 }
7216
7217 //printf("New related name '%s' '%d'\n",qPrint(funcName),
7218 // root->argList ? (int)root->argList->count() : -1);
7219
7220 // first note that we pass:
7221 // (root->tArgLists ? root->tArgLists->last() : nullptr)
7222 // for the template arguments for the new "member."
7223 // this accurately reflects the template arguments of
7224 // the related function, which don't have to do with
7225 // those of the related class.
7226 auto md = createMemberDef(
7227 root->fileName,root->startLine,root->startColumn,
7228 funcType,funcName,funcArgs,exceptions,
7229 root->protection,root->virt,
7230 root->isStatic,
7231 isMemberOf ? Relationship::Foreign : Relationship::Related,
7232 mtype,
7233 (!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList()),
7234 funcArgs.isEmpty() ? ArgumentList() : root->argList,
7235 root->metaData);
7236 auto mmd = toMemberDefMutable(md.get());
7237
7238 // also insert the member as an alias in the parent's scope, so it can be referenced also without cd's scope
7239 insertMemberAlias(cd->getOuterScope(),md.get());
7240
7241 // we still have the problem that
7242 // MemberDef::writeDocumentation() in memberdef.cpp
7243 // writes the template argument list for the class,
7244 // as if this member is a member of the class.
7245 // fortunately, MemberDef::writeDocumentation() has
7246 // a special mechanism that allows us to totally
7247 // override the set of template argument lists that
7248 // are printed. We use that and set it to the
7249 // template argument lists of the related function.
7250 //
7251 mmd->setDefinitionTemplateParameterLists(root->tArgLists);
7252
7253 mmd->setTagInfo(root->tagInfo());
7254
7255 //printf("Related member name='%s' decl='%s' bodyLine='%d'\n",
7256 // qPrint(funcName),qPrint(funcDecl),root->bodyLine);
7257
7258 // try to find the matching line number of the body from the
7259 // global function list
7260 bool found=FALSE;
7261 if (root->bodyLine==-1)
7262 {
7263 MemberName *rmn=Doxygen::functionNameLinkedMap->find(funcName);
7264 if (rmn)
7265 {
7266 const MemberDefMutable *rmd_found=nullptr;
7267 for (const auto &irmd : *rmn)
7268 {
7269 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
7270 if (rmd)
7271 {
7272 const ArgumentList &rmdAl = rmd->argumentList();
7273 // check for matching argument lists
7274 if (
7275 matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmdAl,
7276 cd,fd,root->type,&root->argList,
7277 TRUE,root->lang)
7278 )
7279 {
7280 found=TRUE;
7281 rmd_found = rmd;
7282 break;
7283 }
7284 }
7285 }
7286 if (rmd_found) // member found -> copy line number info
7287 {
7288 mmd->setBodySegment(rmd_found->getDefLine(),rmd_found->getStartBodyLine(),rmd_found->getEndBodyLine());
7289 mmd->setBodyDef(rmd_found->getBodyDef());
7290 //md->setBodyMember(rmd);
7291 }
7292 }
7293 }
7294 if (!found) // line number could not be found or is available in this
7295 // entry
7296 {
7297 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7298 mmd->setBodyDef(fd);
7299 }
7300
7301 //if (root->mGrpId!=-1)
7302 //{
7303 // md->setMemberGroup(memberGroupDict[root->mGrpId]);
7304 //}
7305 mmd->setMemberClass(cd);
7306 mmd->setMemberSpecifiers(spec);
7307 mmd->setVhdlSpecifiers(root->vhdlSpec);
7308 mmd->setDefinition(funcDecl);
7310 mmd->addQualifiers(root->qualifiers);
7311 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7312 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7313 mmd->setDocsForDefinition(!root->proto);
7314 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
7315 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7316 mmd->addSectionsToDefinition(root->anchors);
7317 mmd->setMemberGroupId(root->mGrpId);
7318 mmd->setLanguage(root->lang);
7319 mmd->setId(root->id);
7320 //md->setMemberDefTemplateArguments(root->mtArgList);
7321 cd->insertMember(md.get());
7322 cd->insertUsedFile(fd);
7323 mmd->setRefItems(root->sli);
7324 mmd->setRequirementReferences(root->rqli);
7325 if (root->relatesType==RelatesType::Duplicate) mmd->setRelatedAlso(cd);
7326 addMemberToGroups(root,md.get());
7328 //printf("Adding member=%s\n",qPrint(md->name()));
7329 mn->push_back(std::move(md));
7330 }
7331 if (root->relatesType==RelatesType::Duplicate)
7332 {
7333 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
7334 {
7335 QCString fullFuncDecl=funcDecl;
7336 if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
7337 warn(root->fileName,root->startLine,
7338 "Cannot determine file/namespace for relatedalso function\n{}",
7339 fullFuncDecl
7340 );
7341 }
7342 }
7343 }
7344 }
7345 else
7346 {
7347 warn_undoc(root->fileName,root->startLine, "class '{}' for related function '{}' is not documented.", className,funcName);
7348 }
7349 }
7350 else if (root->parent() && root->parent()->section.isObjcImpl())
7351 {
7352 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
7353 }
7354 else // unrelated not overloaded member found
7355 {
7356 bool globMem = findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec);
7357 if (className.isEmpty() && !globMem)
7358 {
7359 warn(root->fileName,root->startLine, "class for member '{}' cannot be found.", funcName);
7360 }
7361 else if (!className.isEmpty() && !globMem)
7362 {
7363 warn(root->fileName,root->startLine,
7364 "member '{}' of class '{}' cannot be found",
7365 funcName,className);
7366 }
7367 }
7368 }
7369 else
7370 {
7371 // this should not be called
7372 warn(root->fileName,root->startLine,"member with no name found.");
7373 }
7374 return;
7375}
7376
7377//----------------------------------------------------------------------
7378// find the members corresponding to the different documentation blocks
7379// that are extracted from the sources.
7380
7381static void filterMemberDocumentation(const Entry *root,const QCString &relates)
7382{
7383 AUTO_TRACE("root->type='{}' root->inside='{}' root->name='{}' root->args='{}' section={} root->spec={} root->mGrpId={}",
7384 root->type,root->inside,root->name,root->args,root->section,root->spec,root->mGrpId);
7385 //printf("root->parent()->name=%s\n",qPrint(root->parent()->name));
7386 bool isFunc=TRUE;
7387
7388 QCString type = root->type;
7389 QCString args = root->args;
7390 int i=-1, l=0;
7391 if ( // detect func variable/typedef to func ptr
7392 (i=findFunctionPtr(type.str(),root->lang,&l))!=-1
7393 )
7394 {
7395 //printf("Fixing function pointer!\n");
7396 // fix type and argument
7397 args.prepend(type.right(type.length()-i-l));
7398 type=type.left(i+l);
7399 //printf("Results type=%s,name=%s,args=%s\n",qPrint(type),qPrint(root->name),qPrint(args));
7400 isFunc=FALSE;
7401 }
7402 else if ((type.startsWith("typedef ") && args.find('(')!=-1))
7403 // detect function types marked as functions
7404 {
7405 isFunc=FALSE;
7406 }
7407
7408 //printf("Member %s isFunc=%d\n",qPrint(root->name),isFunc);
7409 if (root->section.isMemberDoc())
7410 {
7411 //printf("Documentation for inline member '%s' found args='%s'\n",
7412 // qPrint(root->name),qPrint(args));
7413 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
7414 if (type.isEmpty())
7415 {
7416 findMember(root,
7417 relates,
7418 type,
7419 args,
7420 root->name + args + root->exception,
7421 FALSE,
7422 isFunc);
7423 }
7424 else
7425 {
7426 findMember(root,
7427 relates,
7428 type,
7429 args,
7430 type + " " + root->name + args + root->exception,
7431 FALSE,
7432 isFunc);
7433 }
7434 }
7435 else if (root->section.isOverloadDoc())
7436 {
7437 //printf("Overloaded member %s found\n",qPrint(root->name));
7438 findMember(root,
7439 relates,
7440 type,
7441 args,
7442 root->name,
7443 TRUE,
7444 isFunc);
7445 }
7446 else if
7447 ((root->section.isFunction() // function
7448 ||
7449 (root->section.isVariable() && // variable
7450 !type.isEmpty() && // with a type
7451 g_compoundKeywords.find(type.str())==g_compoundKeywords.end() // that is not a keyword
7452 // (to skip forward declaration of class etc.)
7453 )
7454 )
7455 )
7456 {
7457 //printf("Documentation for member '%s' found args='%s' excp='%s'\n",
7458 // qPrint(root->name),qPrint(args),qPrint(root->exception));
7459 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
7460 //printf("Inside=%s\n Relates=%s\n",qPrint(root->inside),qPrint(relates));
7461 if (type=="friend class" || type=="friend struct" ||
7462 type=="friend union")
7463 {
7464 findMember(root,
7465 relates,
7466 type,
7467 args,
7468 type+" "+root->name,
7469 FALSE,FALSE);
7470
7471 }
7472 else if (!type.isEmpty())
7473 {
7474 findMember(root,
7475 relates,
7476 type,
7477 args,
7478 type+" "+ root->inside + root->name + args + root->exception,
7479 FALSE,isFunc);
7480 }
7481 else
7482 {
7483 findMember(root,
7484 relates,
7485 type,
7486 args,
7487 root->inside + root->name + args + root->exception,
7488 FALSE,isFunc);
7489 }
7490 }
7491 else if (root->section.isDefine() && !relates.isEmpty())
7492 {
7493 findMember(root,
7494 relates,
7495 type,
7496 args,
7497 root->name + args,
7498 FALSE,
7499 !args.isEmpty());
7500 }
7501 else if (root->section.isVariableDoc())
7502 {
7503 //printf("Documentation for variable %s found\n",qPrint(root->name));
7504 //if (!relates.isEmpty()) printf(" Relates %s\n",qPrint(relates));
7505 findMember(root,
7506 relates,
7507 type,
7508 args,
7509 root->name,
7510 FALSE,
7511 FALSE);
7512 }
7513 else if (root->section.isExportedInterface() ||
7514 root->section.isIncludedService())
7515 {
7516 findMember(root,
7517 relates,
7518 type,
7519 args,
7520 type + " " + root->name,
7521 FALSE,
7522 FALSE);
7523 }
7524 else
7525 {
7526 // skip section
7527 //printf("skip section\n");
7528 }
7529}
7530
7531static void findMemberDocumentation(const Entry *root)
7532{
7533 if (root->section.isMemberDoc() ||
7534 root->section.isOverloadDoc() ||
7535 root->section.isFunction() ||
7536 root->section.isVariable() ||
7537 root->section.isVariableDoc() ||
7538 root->section.isDefine() ||
7539 root->section.isIncludedService() ||
7540 root->section.isExportedInterface()
7541 )
7542 {
7543 AUTO_TRACE();
7544 if (root->relatesType==RelatesType::Duplicate && !root->relates.isEmpty())
7545 {
7547 }
7549 }
7550 for (const auto &e : root->children())
7551 {
7552 if (!e->section.isEnum())
7553 {
7554 findMemberDocumentation(e.get());
7555 }
7556 }
7557}
7558
7559//----------------------------------------------------------------------
7560
7561static void findObjCMethodDefinitions(const Entry *root)
7562{
7563 AUTO_TRACE();
7564 for (const auto &objCImpl : root->children())
7565 {
7566 if (objCImpl->section.isObjcImpl())
7567 {
7568 for (const auto &objCMethod : objCImpl->children())
7569 {
7570 if (objCMethod->section.isFunction())
7571 {
7572 //printf(" Found ObjC method definition %s\n",qPrint(objCMethod->name));
7573 findMember(objCMethod.get(),
7574 objCMethod->relates,
7575 objCMethod->type,
7576 objCMethod->args,
7577 objCMethod->type+" "+objCImpl->name+"::"+objCMethod->name+" "+objCMethod->args,
7578 FALSE,TRUE);
7579 objCMethod->section=EntryType::makeEmpty();
7580 }
7581 }
7582 }
7583 }
7584}
7585
7586//----------------------------------------------------------------------
7587// find and add the enumeration to their classes, namespaces or files
7588
7589static void findEnums(const Entry *root)
7590{
7591 if (root->section.isEnum())
7592 {
7593 AUTO_TRACE("name={}",root->name);
7594 ClassDefMutable *cd = nullptr;
7595 FileDef *fd = nullptr;
7596 NamespaceDefMutable *nd = nullptr;
7597 MemberNameLinkedMap *mnsd = nullptr;
7598 bool isGlobal = false;
7599 bool isRelated = false;
7600 bool isMemberOf = false;
7601 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7602
7603 QCString name;
7604 QCString scope;
7605
7606 int i = root->name.findRev("::");
7607 if (i!=-1) // scope is specified
7608 {
7609 scope=root->name.left(i); // extract scope
7610 if (root->lang==SrcLangExt::CSharp)
7611 {
7612 scope = mangleCSharpGenericName(scope);
7613 }
7614 name=root->name.right(root->name.length()-i-2); // extract name
7615 if ((cd=getClassMutable(scope))==nullptr)
7616 {
7618 }
7619 }
7620 else // no scope, check the scope in which the docs where found
7621 {
7622 if (root->parent()->section.isScope() && !root->parent()->name.isEmpty()) // found enum docs inside a compound
7623 {
7624 scope=root->parent()->name;
7625 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7626 }
7627 name=root->name;
7628 }
7629
7630 if (!root->relates.isEmpty())
7631 { // related member, prefix user specified scope
7632 isRelated=TRUE;
7633 isMemberOf=(root->relatesType==RelatesType::MemberOf);
7634 if (getClass(root->relates)==nullptr && !scope.isEmpty())
7635 scope=mergeScopes(scope,root->relates);
7636 else
7637 scope=root->relates;
7638 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7639 }
7640
7641 if (cd && !name.isEmpty()) // found a enum inside a compound
7642 {
7643 //printf("Enum '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7644 fd=nullptr;
7646 isGlobal=false;
7647 }
7648 else if (nd) // found enum inside namespace
7649 {
7651 isGlobal=true;
7652 }
7653 else // found a global enum
7654 {
7655 fd=root->fileDef();
7657 isGlobal=true;
7658 }
7659
7660 if (!name.isEmpty())
7661 {
7662 // new enum type
7663 AUTO_TRACE_ADD("new enum {} at line {} of {}",name,root->bodyLine,root->fileName);
7664 auto md = createMemberDef(
7665 root->fileName,root->startLine,root->startColumn,
7666 QCString(),name,QCString(),QCString(),
7667 root->protection,Specifier::Normal,FALSE,
7668 isMemberOf ? Relationship::Foreign : isRelated ? Relationship::Related : Relationship::Member,
7671 auto mmd = toMemberDefMutable(md.get());
7672 mmd->setTagInfo(root->tagInfo());
7673 mmd->setLanguage(root->lang);
7674 mmd->setId(root->id);
7675 if (!isGlobal) mmd->setMemberClass(cd); else mmd->setFileDef(fd);
7676 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7677 mmd->setBodyDef(root->fileDef());
7678 mmd->setMemberSpecifiers(root->spec);
7679 mmd->setVhdlSpecifiers(root->vhdlSpec);
7680 mmd->setEnumBaseType(root->args);
7681 //printf("Enum %s definition at line %d of %s: protection=%d scope=%s\n",
7682 // qPrint(root->name),root->bodyLine,qPrint(root->fileName),root->protection,cd?qPrint(cd->name()):"<none>");
7683 mmd->addSectionsToDefinition(root->anchors);
7684 mmd->setMemberGroupId(root->mGrpId);
7686 mmd->addQualifiers(root->qualifiers);
7687 //printf("%s::setRefItems(%zu)\n",qPrint(md->name()),root->sli.size());
7688 mmd->setRefItems(root->sli);
7689 mmd->setRequirementReferences(root->rqli);
7690 //printf("found enum %s nd=%p\n",qPrint(md->name()),nd);
7691 bool defSet=FALSE;
7692
7693 QCString baseType = root->args;
7694 if (!baseType.isEmpty())
7695 {
7696 baseType.prepend(" : ");
7697 }
7698
7699 if (nd)
7700 {
7701 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python)
7702 {
7703 mmd->setDefinition(name+baseType);
7704 }
7705 else
7706 {
7707 mmd->setDefinition(nd->name()+"::"+name+baseType);
7708 }
7709 //printf("definition=%s\n",md->definition());
7710 defSet=TRUE;
7711 mmd->setNamespace(nd);
7712 nd->insertMember(md.get());
7713 }
7714
7715 // even if we have already added the enum to a namespace, we still
7716 // also want to add it to other appropriate places such as file
7717 // or class.
7718 if (isGlobal && (nd==nullptr || !nd->isAnonymous()))
7719 {
7720 if (!defSet) mmd->setDefinition(name+baseType);
7721 if (fd==nullptr && root->parent())
7722 {
7723 fd=root->parent()->fileDef();
7724 }
7725 if (fd)
7726 {
7727 mmd->setFileDef(fd);
7728 fd->insertMember(md.get());
7729 }
7730 }
7731 else if (cd)
7732 {
7733 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python)
7734 {
7735 mmd->setDefinition(name+baseType);
7736 }
7737 else
7738 {
7739 mmd->setDefinition(cd->name()+"::"+name+baseType);
7740 }
7741 cd->insertMember(md.get());
7742 cd->insertUsedFile(fd);
7743 }
7744 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7745 mmd->setDocsForDefinition(!root->proto);
7746 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7747 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7748
7749 //printf("Adding member=%s\n",qPrint(md->name()));
7750 addMemberToGroups(root,md.get());
7752
7753 MemberName *mn = mnsd->add(name);
7754 mn->push_back(std::move(md));
7755 }
7756 }
7757 else
7758 {
7759 for (const auto &e : root->children()) findEnums(e.get());
7760 }
7761}
7762
7763//----------------------------------------------------------------------
7764
7765static void addEnumValuesToEnums(const Entry *root)
7766{
7767 if (root->section.isEnum())
7768 // non anonymous enumeration
7769 {
7770 AUTO_TRACE("name={}",root->name);
7771 ClassDefMutable *cd = nullptr;
7772 FileDef *fd = nullptr;
7773 NamespaceDefMutable *nd = nullptr;
7774 MemberNameLinkedMap *mnsd = nullptr;
7775 bool isGlobal = false;
7776 bool isRelated = false;
7777 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7778
7779 QCString name;
7780 QCString scope;
7781
7782 int i = root->name.findRev("::");
7783 if (i!=-1) // scope is specified
7784 {
7785 scope=root->name.left(i); // extract scope
7786 if (root->lang==SrcLangExt::CSharp)
7787 {
7788 scope = mangleCSharpGenericName(scope);
7789 }
7790 name=root->name.right(root->name.length()-i-2); // extract name
7791 if ((cd=getClassMutable(scope))==nullptr)
7792 {
7794 }
7795 }
7796 else // no scope, check the scope in which the docs where found
7797 {
7798 if (root->parent()->section.isScope() && !root->parent()->name.isEmpty()) // found enum docs inside a compound
7799 {
7800 scope=root->parent()->name;
7801 if (root->lang==SrcLangExt::CSharp)
7802 {
7803 scope = mangleCSharpGenericName(scope);
7804 }
7805 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7806 }
7807 name=root->name;
7808 }
7809
7810 if (!root->relates.isEmpty())
7811 { // related member, prefix user specified scope
7812 isRelated=TRUE;
7813 if (getClassMutable(root->relates)==nullptr && !scope.isEmpty())
7814 scope=mergeScopes(scope,root->relates);
7815 else
7816 scope=root->relates;
7817 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7818 }
7819
7820 if (cd && !name.isEmpty()) // found a enum inside a compound
7821 {
7822 //printf("Enum in class '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7823 fd=nullptr;
7825 isGlobal=false;
7826 }
7827 else if (nd && !nd->isAnonymous()) // found enum inside namespace
7828 {
7829 //printf("Enum in namespace '%s'::'%s'\n",qPrint(nd->name()),qPrint(name));
7831 isGlobal=true;
7832 }
7833 else // found a global enum
7834 {
7835 fd=root->fileDef();
7836 //printf("Enum in file '%s': '%s'\n",qPrint(fd->name()),qPrint(name));
7838 isGlobal=true;
7839 }
7840
7841 if (!name.isEmpty())
7842 {
7843 //printf("** name=%s\n",qPrint(name));
7844 MemberName *mn = mnsd->find(name); // for all members with this name
7845 if (mn)
7846 {
7847 struct EnumValueInfo
7848 {
7849 EnumValueInfo(const QCString &n,std::unique_ptr<MemberDef> &&md) :
7850 name(n), member(std::move(md)) {}
7851 QCString name;
7852 std::unique_ptr<MemberDef> member;
7853 };
7854 std::vector< EnumValueInfo > extraMembers;
7855 // for each enum in this list
7856 for (const auto &imd : *mn)
7857 {
7858 MemberDefMutable *md = toMemberDefMutable(imd.get());
7859 // use raw pointer in this loop, since we modify mn and can then invalidate mdp.
7860 if (md && md->isEnumerate() && !root->children().empty())
7861 {
7862 AUTO_TRACE_ADD("enum {} with {} children",md->name(),root->children().size());
7863 for (const auto &e : root->children())
7864 {
7865 SrcLangExt sle = root->lang;
7866 bool isJavaLike = sle==SrcLangExt::CSharp || sle==SrcLangExt::Java || sle==SrcLangExt::XML;
7867 if ( isJavaLike || root->spec.isStrong())
7868 {
7869 if (sle == SrcLangExt::Cpp && e->section.isDefine()) continue;
7870 // Unlike classic C/C++ enums, for C++11, C# & Java enum
7871 // values are only visible inside the enum scope, so we must create
7872 // them here and only add them to the enum
7873 //printf("md->qualifiedName()=%s e->name=%s tagInfo=%p name=%s\n",
7874 // qPrint(md->qualifiedName()),qPrint(e->name),(void*)e->tagInfo(),qPrint(e->name));
7875 QCString qualifiedName = root->name;
7876 i = qualifiedName.findRev("::");
7877 if (i!=-1 && sle==SrcLangExt::CSharp)
7878 {
7879 qualifiedName = mangleCSharpGenericName(qualifiedName.left(i))+qualifiedName.mid(i);
7880 }
7881 if (isJavaLike)
7882 {
7883 qualifiedName=substitute(qualifiedName,"::",".");
7884 }
7885 if (md->qualifiedName()==qualifiedName) // enum value scope matches that of the enum
7886 {
7887 QCString fileName = e->fileName;
7888 if (fileName.isEmpty() && e->tagInfo())
7889 {
7890 fileName = e->tagInfo()->tagName;
7891 }
7892 AUTO_TRACE_ADD("strong enum value {}",e->name);
7893 auto fmd = createMemberDef(
7894 fileName,e->startLine,e->startColumn,
7895 e->type,e->name,e->args,QCString(),
7896 e->protection, Specifier::Normal,e->isStatic,Relationship::Member,
7898 auto fmmd = toMemberDefMutable(fmd.get());
7899 NamespaceDef *mnd = md->getNamespaceDef();
7900 if (md->getClassDef())
7901 fmmd->setMemberClass(md->getClassDef());
7902 else if (mnd && (mnd->isLinkable() || mnd->isAnonymous()))
7903 fmmd->setNamespace(mnd);
7904 else if (md->getFileDef())
7905 fmmd->setFileDef(md->getFileDef());
7906 fmmd->setOuterScope(md->getOuterScope());
7907 fmmd->setTagInfo(e->tagInfo());
7908 fmmd->setLanguage(e->lang);
7909 fmmd->setBodySegment(e->startLine,e->bodyLine,e->endBodyLine);
7910 fmmd->setBodyDef(e->fileDef());
7911 fmmd->setId(e->id);
7912 fmmd->setDocumentation(e->doc,e->docFile,e->docLine);
7913 fmmd->setBriefDescription(e->brief,e->briefFile,e->briefLine);
7914 fmmd->addSectionsToDefinition(e->anchors);
7915 fmmd->setInitializer(e->initializer.str());
7916 fmmd->setMaxInitLines(e->initLines);
7917 fmmd->setMemberGroupId(e->mGrpId);
7918 fmmd->setExplicitExternal(e->explicitExternal,fileName,e->startLine,e->startColumn);
7919 fmmd->setRefItems(e->sli);
7920 fmmd->setRequirementReferences(e->rqli);
7921 fmmd->setAnchor();
7922 md->insertEnumField(fmd.get());
7923 fmmd->setEnumScope(md,TRUE);
7924 extraMembers.emplace_back(e->name,std::move(fmd));
7925 }
7926 }
7927 else
7928 {
7929 AUTO_TRACE_ADD("enum value {}",e->name);
7930 //printf("e->name=%s isRelated=%d\n",qPrint(e->name),isRelated);
7931 MemberName *fmn=nullptr;
7932 MemberNameLinkedMap *emnsd = isRelated ? Doxygen::functionNameLinkedMap : mnsd;
7933 if (!e->name.isEmpty() && (fmn=emnsd->find(e->name)))
7934 // get list of members with the same name as the field
7935 {
7936 for (const auto &ifmd : *fmn)
7937 {
7938 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
7939 if (fmd && fmd->isEnumValue() && fmd->getOuterScope()==md->getOuterScope()) // in same scope
7940 {
7941 //printf("found enum value with same name %s in scope %s\n",
7942 // qPrint(fmd->name()),qPrint(fmd->getOuterScope()->name()));
7943 if (nd && !nd->isAnonymous())
7944 {
7945 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
7946 {
7947 const NamespaceDef *fnd=fmd->getNamespaceDef();
7948 if (fnd==nd) // enum value is inside a namespace
7949 {
7950 md->insertEnumField(fmd);
7951 fmd->setEnumScope(md);
7952 }
7953 }
7954 }
7955 else if (isGlobal)
7956 {
7957 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
7958 {
7959 const FileDef *ffd=fmd->getFileDef();
7960 if (ffd==fd && ffd==md->getFileDef()) // enum value has file scope
7961 {
7962 md->insertEnumField(fmd);
7963 fmd->setEnumScope(md);
7964 }
7965 }
7966 }
7967 else if (isRelated && cd) // reparent enum value to
7968 // match the enum's scope
7969 {
7970 md->insertEnumField(fmd); // add field def to list
7971 fmd->setEnumScope(md); // cross ref with enum name
7972 fmd->setEnumClassScope(cd); // cross ref with enum name
7973 fmd->setOuterScope(cd);
7974 fmd->makeRelated();
7975 cd->insertMember(fmd);
7976 }
7977 else
7978 {
7979 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
7980 {
7981 const ClassDef *fcd=fmd->getClassDef();
7982 if (fcd==cd) // enum value is inside a class
7983 {
7984 //printf("Inserting enum field %s in enum scope %s\n",
7985 // qPrint(fmd->name()),qPrint(md->name()));
7986 md->insertEnumField(fmd); // add field def to list
7987 fmd->setEnumScope(md); // cross ref with enum name
7988 }
7989 }
7990 }
7991 }
7992 }
7993 }
7994 }
7995 }
7996 }
7997 }
7998 // move the newly added members into mn
7999 for (auto &e : extraMembers)
8000 {
8001 MemberName *emn=mnsd->add(e.name);
8002 emn->push_back(std::move(e.member));
8003 }
8004 }
8005 }
8006 }
8007 else
8008 {
8009 for (const auto &e : root->children()) addEnumValuesToEnums(e.get());
8010 }
8011}
8012
8013//----------------------------------------------------------------------
8014
8015static void addEnumDocs(const Entry *root,MemberDefMutable *md)
8016{
8017 AUTO_TRACE();
8018 // documentation outside a compound overrides the documentation inside it
8019 {
8020 md->setDocumentation(root->doc,root->docFile,root->docLine);
8021 md->setDocsForDefinition(!root->proto);
8022 }
8023
8024 // brief descriptions inside a compound override the documentation
8025 // outside it
8026 {
8027 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
8028 }
8029
8030 if (md->inbodyDocumentation().isEmpty() || !root->parent()->name.isEmpty())
8031 {
8033 }
8034
8035 if (root->mGrpId!=-1 && md->getMemberGroupId()==-1)
8036 {
8037 md->setMemberGroupId(root->mGrpId);
8038 }
8039
8041 md->setRefItems(root->sli);
8042 md->setRequirementReferences(root->rqli);
8043
8044 const GroupDef *gd=md->getGroupDef();
8045 if (gd==nullptr && !root->groups.empty()) // member not grouped but out-of-line documentation is
8046 {
8047 addMemberToGroups(root,md);
8048 }
8050}
8051
8052//----------------------------------------------------------------------
8053// Search for the name in the associated groups. If a matching member
8054// definition exists, then add the documentation to it and return TRUE,
8055// otherwise FALSE.
8056
8057static bool tryAddEnumDocsToGroupMember(const Entry *root,const QCString &name)
8058{
8059 for (const auto &g : root->groups)
8060 {
8061 const GroupDef *gd = Doxygen::groupLinkedMap->find(g.groupname);
8062 if (gd)
8063 {
8064 MemberList *ml = gd->getMemberList(MemberListType::DecEnumMembers());
8065 if (ml)
8066 {
8067 MemberDefMutable *md = toMemberDefMutable(ml->find(name));
8068 if (md)
8069 {
8070 addEnumDocs(root,md);
8071 return TRUE;
8072 }
8073 }
8074 }
8075 else if (!gd && g.pri == Grouping::GROUPING_INGROUP)
8076 {
8077 warn(root->fileName, root->startLine,
8078 "Found non-existing group '{}' for the command '{}', ignoring command",
8079 g.groupname, Grouping::getGroupPriName( g.pri )
8080 );
8081 }
8082 }
8083
8084 return FALSE;
8085}
8086
8087//----------------------------------------------------------------------
8088// find the documentation blocks for the enumerations
8089
8090static void findEnumDocumentation(const Entry *root)
8091{
8092 if (root->section.isEnumDoc() &&
8093 !root->name.isEmpty() &&
8094 root->name.at(0)!='@' // skip anonymous enums
8095 )
8096 {
8097 QCString name;
8098 QCString scope;
8099 int i = root->name.findRev("::");
8100 if (i!=-1) // scope is specified as part of the name
8101 {
8102 name=root->name.right(root->name.length()-i-2); // extract name
8103 scope=root->name.left(i); // extract scope
8104 //printf("Scope='%s' Name='%s'\n",qPrint(scope),qPrint(name));
8105 }
8106 else // just the name
8107 {
8108 name=root->name;
8109 }
8110 if (root->parent()->section.isScope() && !root->parent()->name.isEmpty()) // found enum docs inside a compound
8111 {
8112 if (!scope.isEmpty()) scope.prepend("::");
8113 scope.prepend(root->parent()->name);
8114 }
8115 const ClassDef *cd = getClass(scope);
8116 const NamespaceDef *nd=Doxygen::namespaceLinkedMap->find(scope);
8117 const FileDef *fd = root->fileDef();
8118 AUTO_TRACE("Found docs for enum with name '{}' and scope '{}' in context '{}' cd='{}', nd='{}' fd='{}'",
8119 name,scope,root->parent()->name,
8120 cd ? cd->name() : QCString("<none>"),
8121 nd ? nd->name() : QCString("<none>"),
8122 fd ? fd->name() : QCString("<none>"));
8123
8124 if (!name.isEmpty())
8125 {
8126 bool found = tryAddEnumDocsToGroupMember(root, name);
8127 if (!found)
8128 {
8129 MemberName *mn = cd ? Doxygen::memberNameLinkedMap->find(name) : Doxygen::functionNameLinkedMap->find(name);
8130 if (mn)
8131 {
8132 for (const auto &imd : *mn)
8133 {
8134 MemberDefMutable *md = toMemberDefMutable(imd.get());
8135 if (md && md->isEnumerate())
8136 {
8137 const ClassDef *mcd = md->getClassDef();
8138 const NamespaceDef *mnd = md->getNamespaceDef();
8139 const FileDef *mfd = md->getFileDef();
8140 if (cd && mcd==cd)
8141 {
8142 AUTO_TRACE_ADD("Match found for class scope");
8143 addEnumDocs(root,md);
8144 found = TRUE;
8145 break;
8146 }
8147 else if (cd==nullptr && mcd==nullptr && nd!=nullptr && mnd==nd)
8148 {
8149 AUTO_TRACE_ADD("Match found for namespace scope");
8150 addEnumDocs(root,md);
8151 found = TRUE;
8152 break;
8153 }
8154 else if (cd==nullptr && nd==nullptr && mcd==nullptr && mnd==nullptr && fd==mfd)
8155 {
8156 AUTO_TRACE_ADD("Match found for global scope");
8157 addEnumDocs(root,md);
8158 found = TRUE;
8159 break;
8160 }
8161 }
8162 }
8163 }
8164 }
8165 if (!found)
8166 {
8167 warn(root->fileName,root->startLine, "Documentation for undefined enum '{}' found.", name);
8168 }
8169 }
8170 }
8171 for (const auto &e : root->children()) findEnumDocumentation(e.get());
8172}
8173
8174// search for each enum (member or function) in mnl if it has documented
8175// enum values.
8176static void findDEV(const MemberNameLinkedMap &mnsd)
8177{
8178 // for each member name
8179 for (const auto &mn : mnsd)
8180 {
8181 // for each member definition
8182 for (const auto &imd : *mn)
8183 {
8184 MemberDefMutable *md = toMemberDefMutable(imd.get());
8185 if (md && md->isEnumerate()) // member is an enum
8186 {
8187 int documentedEnumValues=0;
8188 // for each enum value
8189 for (const auto &fmd : md->enumFieldList())
8190 {
8191 if (fmd->isLinkableInProject()) documentedEnumValues++;
8192 }
8193 // at least one enum value is documented
8194 if (documentedEnumValues>0) md->setDocumentedEnumValues(TRUE);
8195 }
8196 }
8197 }
8198}
8199
8200// search for each enum (member or function) if it has documented enum
8201// values.
8207
8208//----------------------------------------------------------------------
8209
8211{
8212 auto &index = Index::instance();
8213 // for each class member name
8214 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8215 {
8216 // for each member definition
8217 for (const auto &md : *mn)
8218 {
8219 index.addClassMemberNameToIndex(md.get());
8220 if (md->getModuleDef())
8221 {
8222 index.addModuleMemberNameToIndex(md.get());
8223 }
8224 }
8225 }
8226 // for each file/namespace function name
8227 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8228 {
8229 // for each member definition
8230 for (const auto &md : *mn)
8231 {
8232 if (md->getNamespaceDef())
8233 {
8234 index.addNamespaceMemberNameToIndex(md.get());
8235 }
8236 else
8237 {
8238 index.addFileMemberNameToIndex(md.get());
8239 }
8240 if (md->getModuleDef())
8241 {
8242 index.addModuleMemberNameToIndex(md.get());
8243 }
8244 }
8245 }
8246
8247 index.sortMemberIndexLists();
8248}
8249
8250//----------------------------------------------------------------------
8251
8252static void addToIndices()
8253{
8254 for (const auto &cd : *Doxygen::classLinkedMap)
8255 {
8256 if (cd->isLinkableInProject())
8257 {
8258 Doxygen::indexList->addIndexItem(cd.get(),nullptr);
8259 if (Doxygen::searchIndex.enabled())
8260 {
8261 Doxygen::searchIndex.setCurrentDoc(cd.get(),cd->anchor(),FALSE);
8262 Doxygen::searchIndex.addWord(cd->localName(),TRUE);
8263 }
8264 }
8265 }
8266
8267 for (const auto &cd : *Doxygen::conceptLinkedMap)
8268 {
8269 if (cd->isLinkableInProject())
8270 {
8271 Doxygen::indexList->addIndexItem(cd.get(),nullptr);
8272 if (Doxygen::searchIndex.enabled())
8273 {
8274 Doxygen::searchIndex.setCurrentDoc(cd.get(),cd->anchor(),FALSE);
8275 Doxygen::searchIndex.addWord(cd->localName(),TRUE);
8276 }
8277 }
8278 }
8279
8280 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8281 {
8282 if (nd->isLinkableInProject())
8283 {
8284 Doxygen::indexList->addIndexItem(nd.get(),nullptr);
8285 if (Doxygen::searchIndex.enabled())
8286 {
8287 Doxygen::searchIndex.setCurrentDoc(nd.get(),nd->anchor(),FALSE);
8288 Doxygen::searchIndex.addWord(nd->localName(),TRUE);
8289 }
8290 }
8291 }
8292
8293 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8294 {
8295 for (const auto &fd : *fn)
8296 {
8297 if (Doxygen::searchIndex.enabled() && fd->isLinkableInProject())
8298 {
8299 Doxygen::searchIndex.setCurrentDoc(fd.get(),fd->anchor(),FALSE);
8300 Doxygen::searchIndex.addWord(fd->localName(),TRUE);
8301 }
8302 }
8303 }
8304
8305 auto addWordsForTitle = [](const Definition *d,const QCString &anchor,const QCString &title)
8306 {
8307 Doxygen::indexList->addIndexItem(d,nullptr,QCString(),filterTitle(title));
8308 if (Doxygen::searchIndex.enabled())
8309 {
8310 Doxygen::searchIndex.setCurrentDoc(d,anchor,false);
8311 std::string s = title.str();
8312 static const reg::Ex re(R"(\a[\w-]*)");
8313 reg::Iterator it(s,re);
8315 for (; it!=end ; ++it)
8316 {
8317 const auto &match = *it;
8318 std::string matchStr = match.str();
8319 Doxygen::searchIndex.addWord(matchStr,true);
8320 }
8321 }
8322 };
8323
8324 for (const auto &gd : *Doxygen::groupLinkedMap)
8325 {
8326 if (gd->isLinkableInProject())
8327 {
8328 addWordsForTitle(gd.get(),gd->anchor(),gd->groupTitle());
8329 }
8330 }
8331
8332 for (const auto &pd : *Doxygen::pageLinkedMap)
8333 {
8334 if (pd->isLinkableInProject())
8335 {
8336 addWordsForTitle(pd.get(),pd->anchor(),pd->title());
8337 }
8338 }
8339
8341 {
8342 addWordsForTitle(Doxygen::mainPage.get(),Doxygen::mainPage->anchor(),Doxygen::mainPage->title());
8343 }
8344
8345 auto addMemberToSearchIndex = [](const MemberDef *md)
8346 {
8347 if (Doxygen::searchIndex.enabled())
8348 {
8349 Doxygen::searchIndex.setCurrentDoc(md,md->anchor(),FALSE);
8350 QCString ln=md->localName();
8351 QCString qn=md->qualifiedName();
8352 Doxygen::searchIndex.addWord(ln,TRUE);
8353 if (ln!=qn)
8354 {
8355 Doxygen::searchIndex.addWord(qn,TRUE);
8356 if (md->getClassDef())
8357 {
8358 Doxygen::searchIndex.addWord(md->getClassDef()->displayName(),TRUE);
8359 }
8360 if (md->getNamespaceDef())
8361 {
8362 Doxygen::searchIndex.addWord(md->getNamespaceDef()->displayName(),TRUE);
8363 }
8364 }
8365 }
8366 };
8367
8368 auto getScope = [](const MemberDef *md)
8369 {
8370 const Definition *scope = nullptr;
8371 if (md->getGroupDef()) scope = md->getGroupDef();
8372 else if (md->getClassDef()) scope = md->getClassDef();
8373 else if (md->getNamespaceDef()) scope = md->getNamespaceDef();
8374 else if (md->getFileDef()) scope = md->getFileDef();
8375 return scope;
8376 };
8377
8378 auto addMemberToIndices = [addMemberToSearchIndex,getScope](const MemberDef *md)
8379 {
8380 if (md->isLinkableInProject())
8381 {
8382 if (!(md->isEnumerate() && md->isAnonymous()))
8383 {
8384 Doxygen::indexList->addIndexItem(getScope(md),md);
8386 }
8387 if (md->isEnumerate())
8388 {
8389 for (const auto &fmd : md->enumFieldList())
8390 {
8391 Doxygen::indexList->addIndexItem(getScope(fmd),fmd);
8393 }
8394 }
8395 }
8396 };
8397
8398 // for each class member name
8399 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8400 {
8401 // for each member definition
8402 for (const auto &md : *mn)
8403 {
8404 addMemberToIndices(md.get());
8405 }
8406 }
8407 // for each file/namespace function name
8408 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8409 {
8410 // for each member definition
8411 for (const auto &md : *mn)
8412 {
8413 addMemberToIndices(md.get());
8414 }
8415 }
8416}
8417
8418//----------------------------------------------------------------------
8419
8421{
8422 // for each member name
8423 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8424 {
8425 // for each member definition
8426 for (const auto &imd : *mn)
8427 {
8428 MemberDefMutable *md = toMemberDefMutable(imd.get());
8429 if (md)
8430 {
8432 }
8433 }
8434 }
8435 // for each member name
8436 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8437 {
8438 // for each member definition
8439 for (const auto &imd : *mn)
8440 {
8441 MemberDefMutable *md = toMemberDefMutable(imd.get());
8442 if (md)
8443 {
8445 }
8446 }
8447 }
8448}
8449
8450// recursive helper function looking for reimplements/implemented
8451// by relations between class cd and direct or indirect base class bcd
8453{
8454 for (const auto &mn : cd->memberNameInfoLinkedMap()) // for each member in class cd with a unique name
8455 {
8456 for (const auto &imd : *mn) // for each member with a given name
8457 {
8458 MemberDefMutable *md = toMemberDefMutable(imd->memberDef());
8459 if (md && (md->isFunction() || md->isCSharpProperty())) // filter on reimplementable members
8460 {
8461 ClassDef *mbcd = bcd->classDef;
8462 if (mbcd && mbcd->isLinkable()) // filter on linkable classes
8463 {
8464 const auto &bmn = mbcd->memberNameInfoLinkedMap();
8465 const auto &bmni = bmn.find(mn->memberName());
8466 if (bmni) // there are base class members with the same name
8467 {
8468 for (const auto &ibmd : *bmni) // for base class member with that name
8469 {
8470 MemberDefMutable *bmd = toMemberDefMutable(ibmd->memberDef());
8471 if (bmd) // not part of an inline namespace
8472 {
8473 auto lang = bmd->getLanguage();
8474 auto compType = mbcd->compoundType();
8475 if (bmd->virtualness()!=Specifier::Normal ||
8476 lang==SrcLangExt::Python ||
8477 lang==SrcLangExt::Java ||
8478 lang==SrcLangExt::PHP ||
8479 compType==ClassDef::Interface ||
8480 compType==ClassDef::Protocol)
8481 {
8482 const ArgumentList &bmdAl = bmd->argumentList();
8483 const ArgumentList &mdAl = md->argumentList();
8484 //printf(" Base argList='%s'\n Super argList='%s'\n",
8485 // qPrint(argListToString(bmdAl)),
8486 // qPrint(argListToString(mdAl))
8487 // );
8488 if (
8489 lang==SrcLangExt::Python ||
8490 matchArguments2(bmd->getOuterScope(),bmd->getFileDef(),bmd->typeString(),&bmdAl,
8491 md->getOuterScope(), md->getFileDef(), md->typeString(),&mdAl,
8492 TRUE,lang
8493 )
8494 )
8495 {
8496 if (lang==SrcLangExt::Python && md->name().startsWith("__")) continue; // private members do not reimplement
8497 //printf("match!\n");
8498 const MemberDef *rmd = md->reimplements();
8499 if (rmd==nullptr) // not already assigned
8500 {
8501 //printf("%s: setting (new) reimplements member %s\n",qPrint(md->qualifiedName()),qPrint(bmd->qualifiedName()));
8502 md->setReimplements(bmd);
8503 }
8504 //printf("%s: add reimplementedBy member %s\n",qPrint(bmd->qualifiedName()),qPrint(md->qualifiedName()));
8505 bmd->insertReimplementedBy(md);
8506 }
8507 else
8508 {
8509 //printf("no match!\n");
8510 }
8511 }
8512 }
8513 }
8514 }
8515 }
8516 }
8517 }
8518 }
8519
8520 // do also for indirect base classes
8521 for (const auto &bbcd : bcd->classDef->baseClasses())
8522 {
8524 }
8525}
8526
8527//----------------------------------------------------------------------
8528// computes the relation between all members. For each member 'm'
8529// the members that override the implementation of 'm' are searched and
8530// the member that 'm' overrides is searched.
8531
8533{
8534 for (const auto &cd : *Doxygen::classLinkedMap)
8535 {
8536 if (cd->isLinkable())
8537 {
8538 for (const auto &bcd : cd->baseClasses())
8539 {
8541 }
8542 }
8543 }
8544}
8545
8546//----------------------------------------------------------------------------
8547
8549{
8550 // for each class
8551 for (const auto &cd : *Doxygen::classLinkedMap)
8552 {
8553 // that is a template
8554 for (const auto &ti : cd->getTemplateInstances())
8555 {
8556 ClassDefMutable *tcdm = toClassDefMutable(ti.classDef);
8557 if (tcdm)
8558 {
8559 tcdm->addMembersToTemplateInstance(cd.get(),cd->templateArguments(),ti.templSpec);
8560 }
8561 }
8562 }
8563}
8564
8565//----------------------------------------------------------------------------
8566
8567static void mergeCategories()
8568{
8569 AUTO_TRACE();
8570 // merge members of categories into the class they extend
8571 for (const auto &cd : *Doxygen::classLinkedMap)
8572 {
8573 int i=cd->name().find('(');
8574 if (i!=-1) // it is an Objective-C category
8575 {
8576 QCString baseName=cd->name().left(i);
8577 ClassDefMutable *baseClass=toClassDefMutable(Doxygen::classLinkedMap->find(baseName));
8578 if (baseClass)
8579 {
8580 AUTO_TRACE_ADD("merging members of category {} into {}",cd->name(),baseClass->name());
8581 baseClass->mergeCategory(cd.get());
8582 }
8583 }
8584 }
8585}
8586
8587// builds the list of all members for each class
8588
8590{
8591 // merge the member list of base classes into the inherited classes.
8592 for (const auto &cd : *Doxygen::classLinkedMap)
8593 {
8594 if (// !cd->isReference() && // not an external class
8595 cd->subClasses().empty() && // is a root of the hierarchy
8596 !cd->baseClasses().empty()) // and has at least one base class
8597 {
8598 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8599 if (cdm)
8600 {
8601 //printf("*** merging members for %s\n",qPrint(cd->name()));
8602 cdm->mergeMembers();
8603 }
8604 }
8605 }
8606 // now sort the member list of all members for all classes.
8607 for (const auto &cd : *Doxygen::classLinkedMap)
8608 {
8609 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8610 if (cdm)
8611 {
8612 cdm->sortAllMembersList();
8613 }
8614 }
8615}
8616
8617//----------------------------------------------------------------------------
8618
8620{
8621 auto processSourceFile = [](FileDef *fd,OutputList &ol,ClangTUParser *parser)
8622 {
8623 bool showSources = fd->generateSourceFile() && !Htags::useHtags; // sources need to be shown in the output
8624 bool parseSources = !fd->isReference() && Doxygen::parseSourcesNeeded; // we needed to parse the sources even if we do not show them
8625 if (showSources)
8626 {
8627 msg("Generating code for file {}...\n",fd->docName());
8628 fd->writeSourceHeader(ol);
8629 fd->writeSourceBody(ol,parser);
8630 fd->writeSourceFooter(ol);
8631 }
8632 else if (parseSources)
8633 {
8634 msg("Parsing code for file {}...\n",fd->docName());
8635 fd->parseSource(parser);
8636 }
8637 };
8638 if (!Doxygen::inputNameLinkedMap->empty())
8639 {
8640#if USE_LIBCLANG
8642 {
8643 StringUnorderedSet processedFiles;
8644
8645 // create a dictionary with files to process
8646 StringUnorderedSet filesToProcess;
8647
8648 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8649 {
8650 for (const auto &fd : *fn)
8651 {
8652 filesToProcess.insert(fd->absFilePath().str());
8653 }
8654 }
8655 // process source files (and their include dependencies)
8656 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8657 {
8658 for (const auto &fd : *fn)
8659 {
8660 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt::Cpp &&
8661 (fd->generateSourceFile() ||
8663 )
8664 )
8665 {
8666 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8667 clangParser->parse();
8668 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8669
8670 for (auto incFile : clangParser->filesInSameTU())
8671 {
8672 if (filesToProcess.find(incFile)!=filesToProcess.end() && // part of input
8673 fd->absFilePath()!=incFile && // not same file
8674 processedFiles.find(incFile)==processedFiles.end()) // not yet marked as processed
8675 {
8676 StringVector moreFiles;
8677 bool ambig = false;
8679 if (ifd && !ifd->isReference())
8680 {
8681 processSourceFile(ifd,*g_outputList,clangParser.get());
8682 processedFiles.insert(incFile);
8683 }
8684 }
8685 }
8686 processedFiles.insert(fd->absFilePath().str());
8687 }
8688 }
8689 }
8690 // process remaining files
8691 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8692 {
8693 for (const auto &fd : *fn)
8694 {
8695 if (processedFiles.find(fd->absFilePath().str())==processedFiles.end()) // not yet processed
8696 {
8697 if (fd->getLanguage()==SrcLangExt::Cpp) // C/C++ file, use clang parser
8698 {
8699 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8700 clangParser->parse();
8701 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8702 }
8703 else // non C/C++ file, use built-in parser
8704 {
8705 processSourceFile(fd.get(),*g_outputList,nullptr);
8706 }
8707 }
8708 }
8709 }
8710 }
8711 else
8712#endif
8713 {
8714 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8715 if (numThreads>1)
8716 {
8717 msg("Generating code files using {} threads.\n",numThreads);
8718 struct SourceContext
8719 {
8720 SourceContext(FileDef *fd_,bool gen_,const OutputList &ol_)
8721 : fd(fd_), generateSourceFile(gen_), ol(ol_) {}
8722 FileDef *fd;
8723 bool generateSourceFile;
8724 OutputList ol;
8725 };
8726 ThreadPool threadPool(numThreads);
8727 std::vector< std::future< std::shared_ptr<SourceContext> > > results;
8728 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8729 {
8730 for (const auto &fd : *fn)
8731 {
8732 bool generateSourceFile = fd->generateSourceFile() && !Htags::useHtags;
8733 auto ctx = std::make_shared<SourceContext>(fd.get(),generateSourceFile,*g_outputList);
8734 auto processFile = [ctx]()
8735 {
8736 if (ctx->generateSourceFile)
8737 {
8738 msg("Generating code for file {}...\n",ctx->fd->docName());
8739 }
8740 else
8741 {
8742 msg("Parsing code for file {}...\n",ctx->fd->docName());
8743 }
8744 StringVector filesInSameTu;
8745 ctx->fd->getAllIncludeFilesRecursively(filesInSameTu);
8746 if (ctx->generateSourceFile) // sources need to be shown in the output
8747 {
8748 ctx->fd->writeSourceHeader(ctx->ol);
8749 ctx->fd->writeSourceBody(ctx->ol,nullptr);
8750 ctx->fd->writeSourceFooter(ctx->ol);
8751 }
8752 else if (!ctx->fd->isReference() && Doxygen::parseSourcesNeeded)
8753 // we needed to parse the sources even if we do not show them
8754 {
8755 ctx->fd->parseSource(nullptr);
8756 }
8757 return ctx;
8758 };
8759 results.emplace_back(threadPool.queue(processFile));
8760 }
8761 }
8762 for (auto &f : results)
8763 {
8764 auto ctx = f.get();
8765 }
8766 }
8767 else // single threaded version
8768 {
8769 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8770 {
8771 for (const auto &fd : *fn)
8772 {
8773 StringVector filesInSameTu;
8774 fd->getAllIncludeFilesRecursively(filesInSameTu);
8775 processSourceFile(fd.get(),*g_outputList,nullptr);
8776 }
8777 }
8778 }
8779 }
8780 }
8781}
8782
8783//----------------------------------------------------------------------------
8784
8785static void generateFileDocs()
8786{
8787 if (Index::instance().numDocumentedFiles()==0) return;
8788
8789 if (!Doxygen::inputNameLinkedMap->empty())
8790 {
8791 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8792 if (numThreads>1) // multi threaded processing
8793 {
8794 struct DocContext
8795 {
8796 DocContext(FileDef *fd_,const OutputList &ol_)
8797 : fd(fd_), ol(ol_) {}
8798 FileDef *fd;
8799 OutputList ol;
8800 };
8801 ThreadPool threadPool(numThreads);
8802 std::vector< std::future< std::shared_ptr<DocContext> > > results;
8803 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8804 {
8805 for (const auto &fd : *fn)
8806 {
8807 bool doc = fd->isLinkableInProject();
8808 if (doc)
8809 {
8810 auto ctx = std::make_shared<DocContext>(fd.get(),*g_outputList);
8811 auto processFile = [ctx]() {
8812 msg("Generating docs for file {}...\n",ctx->fd->docName());
8813 ctx->fd->writeDocumentation(ctx->ol);
8814 return ctx;
8815 };
8816 results.emplace_back(threadPool.queue(processFile));
8817 }
8818 }
8819 }
8820 for (auto &f : results)
8821 {
8822 auto ctx = f.get();
8823 }
8824 }
8825 else // single threaded processing
8826 {
8827 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8828 {
8829 for (const auto &fd : *fn)
8830 {
8831 bool doc = fd->isLinkableInProject();
8832 if (doc)
8833 {
8834 msg("Generating docs for file {}...\n",fd->docName());
8835 fd->writeDocumentation(*g_outputList);
8836 }
8837 }
8838 }
8839 }
8840 }
8841}
8842
8843//----------------------------------------------------------------------------
8844
8846{
8847 // add source references for class definitions
8848 for (const auto &cd : *Doxygen::classLinkedMap)
8849 {
8850 const FileDef *fd=cd->getBodyDef();
8851 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8852 {
8853 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),nullptr);
8854 }
8855 }
8856 // add source references for concept definitions
8857 for (const auto &cd : *Doxygen::conceptLinkedMap)
8858 {
8859 const FileDef *fd=cd->getBodyDef();
8860 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8861 {
8862 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),nullptr);
8863 }
8864 }
8865 // add source references for namespace definitions
8866 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8867 {
8868 const FileDef *fd=nd->getBodyDef();
8869 if (fd && nd->isLinkableInProject() && nd->getStartDefLine()!=-1)
8870 {
8871 const_cast<FileDef*>(fd)->addSourceRef(nd->getStartDefLine(),nd.get(),nullptr);
8872 }
8873 }
8874
8875 // add source references for member names
8876 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8877 {
8878 for (const auto &md : *mn)
8879 {
8880 //printf("class member %s: def=%s body=%d link?=%d\n",
8881 // qPrint(md->name()),
8882 // md->getBodyDef()?qPrint(md->getBodyDef()->name()):"<none>",
8883 // md->getStartBodyLine(),md->isLinkableInProject());
8884 const FileDef *fd=md->getBodyDef();
8885 if (fd &&
8886 md->getStartDefLine()!=-1 &&
8887 md->isLinkableInProject() &&
8889 )
8890 {
8891 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8892 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8893 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8894 }
8895 }
8896 }
8897 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8898 {
8899 for (const auto &md : *mn)
8900 {
8901 const FileDef *fd=md->getBodyDef();
8902 //printf("member %s body=[%d,%d] fd=%p link=%d parseSources=%d\n",
8903 // qPrint(md->name()),
8904 // md->getStartBodyLine(),md->getEndBodyLine(),fd,
8905 // md->isLinkableInProject(),
8906 // Doxygen::parseSourcesNeeded);
8907 if (fd &&
8908 md->getStartDefLine()!=-1 &&
8909 md->isLinkableInProject() &&
8911 )
8912 {
8913 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8914 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8915 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8916 }
8917 }
8918 }
8919}
8920
8921//----------------------------------------------------------------------------
8922
8923// add the macro definitions found during preprocessing as file members
8924static void buildDefineList()
8925{
8926 AUTO_TRACE();
8927 for (const auto &s : g_inputFiles)
8928 {
8929 auto it = Doxygen::macroDefinitions.find(s);
8931 {
8932 for (const auto &def : it->second)
8933 {
8934 auto md = createMemberDef(
8935 def.fileName,def.lineNr,def.columnNr,
8936 "#define",def.name,def.args,QCString(),
8937 Protection::Public,Specifier::Normal,FALSE,Relationship::Member,MemberType::Define,
8938 ArgumentList(),ArgumentList(),"");
8939 auto mmd = toMemberDefMutable(md.get());
8940
8941 if (!def.args.isEmpty())
8942 {
8943 mmd->moveArgumentList(stringToArgumentList(SrcLangExt::Cpp, def.args));
8944 }
8945 mmd->setInitializer(def.definition);
8946 mmd->setFileDef(def.fileDef);
8947 mmd->setDefinition("#define "+def.name);
8948
8949 MemberName *mn=Doxygen::functionNameLinkedMap->add(def.name);
8950 if (def.fileDef)
8951 {
8952 const MemberList *defMl = def.fileDef->getMemberList(MemberListType::DocDefineMembers());
8953 if (defMl)
8954 {
8955 const MemberDef *defMd = defMl->findRev(def.name);
8956 if (defMd) // definition already stored
8957 {
8958 mmd->setRedefineCount(defMd->redefineCount()+1);
8959 }
8960 }
8961 def.fileDef->insertMember(md.get());
8962 }
8963 AUTO_TRACE_ADD("adding macro {} with definition {}",def.name,def.definition);
8964 mn->push_back(std::move(md));
8965 }
8966 }
8967 }
8968}
8969
8970//----------------------------------------------------------------------------
8971
8972static void sortMemberLists()
8973{
8974 // sort class member lists
8975 for (const auto &cd : *Doxygen::classLinkedMap)
8976 {
8977 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8978 if (cdm)
8979 {
8980 cdm->sortMemberLists();
8981 }
8982 }
8983
8984 // sort namespace member lists
8985 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8986 {
8988 if (ndm)
8989 {
8990 ndm->sortMemberLists();
8991 }
8992 }
8993
8994 // sort file member lists
8995 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8996 {
8997 for (const auto &fd : *fn)
8998 {
8999 fd->sortMemberLists();
9000 }
9001 }
9002
9003 // sort group member lists
9004 for (const auto &gd : *Doxygen::groupLinkedMap)
9005 {
9006 gd->sortMemberLists();
9007 }
9008
9010}
9011
9012//----------------------------------------------------------------------------
9013
9014static bool isSymbolHidden(const Definition *d)
9015{
9016 bool hidden = d->isHidden();
9017 const Definition *parent = d->getOuterScope();
9018 return parent ? hidden || isSymbolHidden(parent) : hidden;
9019}
9020
9022{
9023 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9024 if (numThreads>1)
9025 {
9026 ThreadPool threadPool(numThreads);
9027 std::vector < std::future< void > > results;
9028 // queue the work
9029 for (const auto &[name,symList] : *Doxygen::symbolMap)
9030 {
9031 for (const auto &def : symList)
9032 {
9034 if (dm && !isSymbolHidden(def) && !def->isArtificial() && def->isLinkableInProject())
9035 {
9036 auto processTooltip = [dm]() {
9037 dm->computeTooltip();
9038 };
9039 results.emplace_back(threadPool.queue(processTooltip));
9040 }
9041 }
9042 }
9043 // wait for the results
9044 for (auto &f : results)
9045 {
9046 f.get();
9047 }
9048 }
9049 else
9050 {
9051 for (const auto &[name,symList] : *Doxygen::symbolMap)
9052 {
9053 for (const auto &def : symList)
9054 {
9056 if (dm && !isSymbolHidden(def) && !def->isArtificial() && def->isLinkableInProject())
9057 {
9058 dm->computeTooltip();
9059 }
9060 }
9061 }
9062 }
9063}
9064
9065//----------------------------------------------------------------------------
9066
9068{
9069 for (const auto &cd : *Doxygen::classLinkedMap)
9070 {
9071 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9072 if (cdm)
9073 {
9074 cdm->setAnonymousEnumType();
9075 }
9076 }
9077}
9078
9079//----------------------------------------------------------------------------
9080
9081static void countMembers()
9082{
9083 for (const auto &cd : *Doxygen::classLinkedMap)
9084 {
9085 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9086 if (cdm)
9087 {
9088 cdm->countMembers();
9089 }
9090 }
9091
9092 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9093 {
9095 if (ndm)
9096 {
9097 ndm->countMembers();
9098 }
9099 }
9100
9101 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9102 {
9103 for (const auto &fd : *fn)
9104 {
9105 fd->countMembers();
9106 }
9107 }
9108
9109 for (const auto &gd : *Doxygen::groupLinkedMap)
9110 {
9111 gd->countMembers();
9112 }
9113
9114 auto &mm = ModuleManager::instance();
9115 mm.countMembers();
9116}
9117
9118
9119//----------------------------------------------------------------------------
9120// generate the documentation for all classes
9121
9122static void generateDocsForClassList(const std::vector<ClassDefMutable*> &classList)
9123{
9124 AUTO_TRACE();
9125 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9126 if (numThreads>1) // multi threaded processing
9127 {
9128 struct DocContext
9129 {
9130 DocContext(ClassDefMutable *cd_,const OutputList &ol_)
9131 : cd(cd_), ol(ol_) {}
9132 ClassDefMutable *cd;
9133 OutputList ol;
9134 };
9135 ThreadPool threadPool(numThreads);
9136 std::vector< std::future< std::shared_ptr<DocContext> > > results;
9137 for (const auto &cd : classList)
9138 {
9139 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
9140 if (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9141 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
9142 )
9143 {
9144 auto ctx = std::make_shared<DocContext>(cd,*g_outputList);
9145 auto processFile = [ctx]()
9146 {
9147 msg("Generating docs for compound {}...\n",ctx->cd->displayName());
9148
9149 // skip external references, anonymous compounds and
9150 // template instances
9151 if (!ctx->cd->isHidden() && !ctx->cd->isEmbeddedInOuterScope() &&
9152 ctx->cd->isLinkableInProject() && !ctx->cd->isImplicitTemplateInstance())
9153 {
9154 ctx->cd->writeDocumentation(ctx->ol);
9155 ctx->cd->writeMemberList(ctx->ol);
9156 }
9157
9158 // even for undocumented classes, the inner classes can be documented.
9159 ctx->cd->writeDocumentationForInnerClasses(ctx->ol);
9160 return ctx;
9161 };
9162 results.emplace_back(threadPool.queue(processFile));
9163 }
9164 }
9165 for (auto &f : results)
9166 {
9167 auto ctx = f.get();
9168 }
9169 }
9170 else // single threaded processing
9171 {
9172 for (const auto &cd : classList)
9173 {
9174 //printf("cd=%s getOuterScope=%p global=%p hidden=%d embeddedInOuterScope=%d\n",
9175 // qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope,cd->isHidden(),cd->isEmbeddedInOuterScope());
9176 if (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9177 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
9178 )
9179 {
9180 // skip external references, anonymous compounds and
9181 // template instances
9182 if ( !cd->isHidden() && !cd->isEmbeddedInOuterScope() &&
9183 cd->isLinkableInProject() && !cd->isImplicitTemplateInstance())
9184 {
9185 msg("Generating docs for compound {}...\n",cd->displayName());
9186
9187 cd->writeDocumentation(*g_outputList);
9188 cd->writeMemberList(*g_outputList);
9189 }
9190 // even for undocumented classes, the inner classes can be documented.
9191 cd->writeDocumentationForInnerClasses(*g_outputList);
9192 }
9193 }
9194 }
9195}
9196
9197static void addClassAndNestedClasses(std::vector<ClassDefMutable*> &list,ClassDefMutable *cd)
9198{
9199 list.push_back(cd);
9200 for (const auto &innerCdi : cd->getClasses())
9201 {
9202 ClassDefMutable *innerCd = toClassDefMutable(innerCdi);
9203 if (innerCd)
9204 {
9205 AUTO_TRACE("innerCd={} isLinkable={} isImplicitTemplateInstance={} protectLevelVisible={} embeddedInOuterScope={}",
9206 innerCd->name(),innerCd->isLinkableInProject(),innerCd->isImplicitTemplateInstance(),protectionLevelVisible(innerCd->protection()),
9207 innerCd->isEmbeddedInOuterScope());
9208 }
9209 if (innerCd && innerCd->isLinkableInProject() && !innerCd->isImplicitTemplateInstance() &&
9210 protectionLevelVisible(innerCd->protection()) &&
9211 !innerCd->isEmbeddedInOuterScope()
9212 )
9213 {
9214 list.push_back(innerCd);
9215 addClassAndNestedClasses(list,innerCd);
9216 }
9217 }
9218}
9219
9221{
9222 std::vector<ClassDefMutable*> classList;
9223 for (const auto &cdi : *Doxygen::classLinkedMap)
9224 {
9225 ClassDefMutable *cd = toClassDefMutable(cdi.get());
9226 if (cd && (cd->getOuterScope()==nullptr ||
9228 {
9229 addClassAndNestedClasses(classList,cd);
9230 }
9231 }
9232 for (const auto &cdi : *Doxygen::hiddenClassLinkedMap)
9233 {
9234 ClassDefMutable *cd = toClassDefMutable(cdi.get());
9235 if (cd && (cd->getOuterScope()==nullptr ||
9237 {
9238 addClassAndNestedClasses(classList,cd);
9239 }
9240 }
9241 generateDocsForClassList(classList);
9242}
9243
9244//----------------------------------------------------------------------------
9245
9247{
9248 for (const auto &cdi : *Doxygen::conceptLinkedMap)
9249 {
9251
9252 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
9253 if (cd &&
9254 (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9255 cd->getOuterScope()==Doxygen::globalScope // only look at global concepts
9256 ) && !cd->isHidden() && cd->isLinkableInProject()
9257 )
9258 {
9259 msg("Generating docs for concept {}...\n",cd->displayName());
9261 }
9262 }
9263}
9264
9265//----------------------------------------------------------------------------
9266
9268{
9269 for (const auto &mn : *Doxygen::memberNameLinkedMap)
9270 {
9271 for (const auto &imd : *mn)
9272 {
9273 MemberDefMutable *md = toMemberDefMutable(imd.get());
9274 //static int count=0;
9275 //printf("%04d Member '%s'\n",count++,qPrint(md->qualifiedName()));
9276 if (md && md->documentation().isEmpty() && md->briefDescription().isEmpty())
9277 { // no documentation yet
9278 const MemberDef *bmd = md->reimplements();
9279 while (bmd && bmd->documentation().isEmpty() &&
9280 bmd->briefDescription().isEmpty()
9281 )
9282 { // search up the inheritance tree for a documentation member
9283 //printf("bmd=%s class=%s\n",qPrint(bmd->name()),qPrint(bmd->getClassDef()->name()));
9284 bmd = bmd->reimplements();
9285 }
9286 if (bmd) // copy the documentation from the reimplemented member
9287 {
9288 md->setInheritsDocsFrom(bmd);
9289 md->setDocumentation(bmd->documentation(),bmd->docFile(),bmd->docLine());
9291 md->setBriefDescription(bmd->briefDescription(),bmd->briefFile(),bmd->briefLine());
9292 md->copyArgumentNames(bmd);
9294 }
9295 }
9296 }
9297 }
9298}
9299
9300//----------------------------------------------------------------------------
9301
9303{
9304 // for each file
9305 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9306 {
9307 for (const auto &fd : *fn)
9308 {
9309 fd->combineUsingRelations();
9310 }
9311 }
9312
9313 // for each namespace
9314 NamespaceDefSet visitedNamespaces;
9315 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9316 {
9318 if (ndm)
9319 {
9320 ndm->combineUsingRelations(visitedNamespaces);
9321 }
9322 }
9323}
9324
9325//----------------------------------------------------------------------------
9326
9328{
9329 // for each class
9330 for (const auto &cd : *Doxygen::classLinkedMap)
9331 {
9332 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9333 if (cdm)
9334 {
9336 }
9337 }
9338 // for each file
9339 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9340 {
9341 for (const auto &fd : *fn)
9342 {
9343 fd->addMembersToMemberGroup();
9344 }
9345 }
9346 // for each namespace
9347 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9348 {
9350 if (ndm)
9351 {
9353 }
9354 }
9355 // for each group
9356 for (const auto &gd : *Doxygen::groupLinkedMap)
9357 {
9358 gd->addMembersToMemberGroup();
9359 }
9361}
9362
9363//----------------------------------------------------------------------------
9364
9366{
9367 // for each class
9368 for (const auto &cd : *Doxygen::classLinkedMap)
9369 {
9370 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9371 if (cdm)
9372 {
9374 }
9375 }
9376 // for each file
9377 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9378 {
9379 for (const auto &fd : *fn)
9380 {
9381 fd->distributeMemberGroupDocumentation();
9382 }
9383 }
9384 // for each namespace
9385 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9386 {
9388 if (ndm)
9389 {
9391 }
9392 }
9393 // for each group
9394 for (const auto &gd : *Doxygen::groupLinkedMap)
9395 {
9396 gd->distributeMemberGroupDocumentation();
9397 }
9399}
9400
9401//----------------------------------------------------------------------------
9402
9404{
9405 // for each class
9406 for (const auto &cd : *Doxygen::classLinkedMap)
9407 {
9408 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9409 if (cdm)
9410 {
9412 }
9413 }
9414 // for each concept
9415 for (const auto &cd : *Doxygen::conceptLinkedMap)
9416 {
9417 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
9418 if (cdm)
9419 {
9421 }
9422 }
9423 // for each file
9424 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9425 {
9426 for (const auto &fd : *fn)
9427 {
9428 fd->findSectionsInDocumentation();
9429 }
9430 }
9431 // for each namespace
9432 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9433 {
9435 if (ndm)
9436 {
9438 }
9439 }
9440 // for each group
9441 for (const auto &gd : *Doxygen::groupLinkedMap)
9442 {
9443 gd->findSectionsInDocumentation();
9444 }
9445 // for each page
9446 for (const auto &pd : *Doxygen::pageLinkedMap)
9447 {
9448 pd->findSectionsInDocumentation();
9449 }
9450 // for each directory
9451 for (const auto &dd : *Doxygen::dirLinkedMap)
9452 {
9453 dd->findSectionsInDocumentation();
9454 }
9456 if (Doxygen::mainPage) Doxygen::mainPage->findSectionsInDocumentation();
9457}
9458
9459//----------------------------------------------------------------------
9460
9461
9463{
9464 // remove all references to classes from the cache
9465 // as there can be new template instances in the inheritance path
9466 // to this class. Optimization: only remove those classes that
9467 // have inheritance instances as direct or indirect sub classes.
9469
9470 // remove all cached typedef resolutions whose target is a
9471 // template class as this may now be a template instance
9472 // for each global function name
9473 for (const auto &fn : *Doxygen::functionNameLinkedMap)
9474 {
9475 // for each function with that name
9476 for (const auto &ifmd : *fn)
9477 {
9478 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
9479 if (fmd && fmd->isTypedefValCached())
9480 {
9481 const ClassDef *cd = fmd->getCachedTypedefVal();
9482 if (cd->isTemplate()) fmd->invalidateTypedefValCache();
9483 }
9484 }
9485 }
9486 // for each class method name
9487 for (const auto &nm : *Doxygen::memberNameLinkedMap)
9488 {
9489 // for each function with that name
9490 for (const auto &imd : *nm)
9491 {
9492 MemberDefMutable *md = toMemberDefMutable(imd.get());
9493 if (md && md->isTypedefValCached())
9494 {
9495 const ClassDef *cd = md->getCachedTypedefVal();
9496 if (cd->isTemplate()) md->invalidateTypedefValCache();
9497 }
9498 }
9499 }
9500}
9501
9502//----------------------------------------------------------------------------
9503
9505{
9506 // Remove all unresolved references to classes from the cache.
9507 // This is needed before resolving the inheritance relations, since
9508 // it would otherwise not find the inheritance relation
9509 // for C in the example below, as B::I was already found to be unresolvable
9510 // (which is correct if you ignore the inheritance relation between A and B).
9511 //
9512 // class A { class I {} };
9513 // class B : public A {};
9514 // class C : public B::I {};
9516
9517 // for each class method name
9518 for (const auto &nm : *Doxygen::memberNameLinkedMap)
9519 {
9520 // for each function with that name
9521 for (const auto &imd : *nm)
9522 {
9523 MemberDefMutable *md = toMemberDefMutable(imd.get());
9524 if (md)
9525 {
9527 }
9528 }
9529 }
9530
9531}
9532
9533//----------------------------------------------------------------------------
9534// Returns TRUE if the entry and member definition have equal file names,
9535// otherwise FALSE.
9536
9537static bool haveEqualFileNames(const Entry *root, const MemberDef *md)
9538{
9539 if (const FileDef *fd = md->getFileDef())
9540 {
9541 return fd->absFilePath() == root->fileName;
9542 }
9543 return false;
9544}
9545
9546//----------------------------------------------------------------------------
9547
9548static void addDefineDoc(const Entry *root, MemberDefMutable *md)
9549{
9550 md->setDocumentation(root->doc,root->docFile,root->docLine);
9551 md->setDocsForDefinition(!root->proto);
9552 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9553 if (md->inbodyDocumentation().isEmpty())
9554 {
9556 }
9557 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
9558 {
9559 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
9560 md->setBodyDef(root->fileDef());
9561 }
9563 md->setMaxInitLines(root->initLines);
9565 md->setRefItems(root->sli);
9566 md->setRequirementReferences(root->rqli);
9567 md->addQualifiers(root->qualifiers);
9568 if (root->mGrpId!=-1) md->setMemberGroupId(root->mGrpId);
9569 addMemberToGroups(root,md);
9571}
9572
9573//----------------------------------------------------------------------------
9574
9576{
9577 if ((root->section.isDefineDoc() || root->section.isDefine()) && !root->name.isEmpty())
9578 {
9579 //printf("found define '%s' '%s' brief='%s' doc='%s'\n",
9580 // qPrint(root->name),qPrint(root->args),qPrint(root->brief),qPrint(root->doc));
9581
9582 if (root->tagInfo() && !root->name.isEmpty()) // define read from a tag file
9583 {
9584 auto md = createMemberDef(root->tagInfo()->tagName,1,1,
9585 "#define",root->name,root->args,QCString(),
9586 Protection::Public,Specifier::Normal,FALSE,Relationship::Member,MemberType::Define,
9587 ArgumentList(),ArgumentList(),"");
9588 auto mmd = toMemberDefMutable(md.get());
9589 mmd->setTagInfo(root->tagInfo());
9590 mmd->setLanguage(root->lang);
9591 mmd->addQualifiers(root->qualifiers);
9592 //printf("Searching for '%s' fd=%p\n",qPrint(filePathName),fd);
9593 mmd->setFileDef(root->parent()->fileDef());
9594 //printf("Adding member=%s\n",qPrint(md->name()));
9596 mn->push_back(std::move(md));
9597 }
9599 if (mn)
9600 {
9601 int count=0;
9602 for (const auto &md : *mn)
9603 {
9604 if (md->memberType()==MemberType::Define) count++;
9605 }
9606 if (count==1)
9607 {
9608 for (const auto &imd : *mn)
9609 {
9610 MemberDefMutable *md = toMemberDefMutable(imd.get());
9611 if (md && md->memberType()==MemberType::Define)
9612 {
9613 addDefineDoc(root,md);
9614 }
9615 }
9616 }
9617 else if (count>1 &&
9618 (!root->doc.isEmpty() ||
9619 !root->brief.isEmpty() ||
9620 root->bodyLine!=-1
9621 )
9622 )
9623 // multiple defines don't know where to add docs
9624 // but maybe they are in different files together with their documentation
9625 {
9626 for (const auto &imd : *mn)
9627 {
9628 MemberDefMutable *md = toMemberDefMutable(imd.get());
9629 if (md && md->memberType()==MemberType::Define)
9630 {
9631 if (haveEqualFileNames(root, md) || isEntryInGroupOfMember(root, md))
9632 // doc and define in the same file or group assume they belong together.
9633 {
9634 addDefineDoc(root,md);
9635 }
9636 }
9637 }
9638 //warn("define {} found in the following files:\n",root->name);
9639 //warn("Cannot determine where to add the documentation found "
9640 // "at line {} of file {}. \n",
9641 // root->startLine,root->fileName);
9642 }
9643 }
9644 else if (!root->doc.isEmpty() || !root->brief.isEmpty()) // define not found
9645 {
9646 bool preEnabled = Config_getBool(ENABLE_PREPROCESSING);
9647 if (preEnabled)
9648 {
9649 warn(root->fileName,root->startLine,"documentation for unknown define {} found.",root->name);
9650 }
9651 else
9652 {
9653 warn(root->fileName,root->startLine, "found documented #define {} but ignoring it because ENABLE_PREPROCESSING is NO.", root->name);
9654 }
9655 }
9656 }
9657 for (const auto &e : root->children()) findDefineDocumentation(e.get());
9658}
9659
9660//----------------------------------------------------------------------------
9661
9662static void findDirDocumentation(const Entry *root)
9663{
9664 if (root->section.isDirDoc())
9665 {
9666 QCString normalizedName = root->name;
9667 normalizedName = substitute(normalizedName,"\\","/");
9668 //printf("root->docFile=%s normalizedName=%s\n",
9669 // qPrint(root->docFile),qPrint(normalizedName));
9670 if (root->docFile==normalizedName) // current dir?
9671 {
9672 int lastSlashPos=normalizedName.findRev('/');
9673 if (lastSlashPos!=-1) // strip file name
9674 {
9675 normalizedName=normalizedName.left(lastSlashPos);
9676 }
9677 }
9678 if (normalizedName.at(normalizedName.length()-1)!='/')
9679 {
9680 normalizedName+='/';
9681 }
9682 DirDef *matchingDir=nullptr;
9683 for (const auto &dir : *Doxygen::dirLinkedMap)
9684 {
9685 //printf("Dir: %s<->%s\n",qPrint(dir->name()),qPrint(normalizedName));
9686 if (dir->name().right(normalizedName.length())==normalizedName)
9687 {
9688 if (matchingDir)
9689 {
9690 warn(root->fileName,root->startLine,
9691 "\\dir command matches multiple directories.\n"
9692 " Applying the command for directory {}\n"
9693 " Ignoring the command for directory {}",
9694 matchingDir->name(),dir->name()
9695 );
9696 }
9697 else
9698 {
9699 matchingDir=dir.get();
9700 }
9701 }
9702 }
9703 if (matchingDir)
9704 {
9705 //printf("Match for with dir %s #anchor=%zu\n",qPrint(matchingDir->name()),root->anchors.size());
9706 matchingDir->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9707 matchingDir->setDocumentation(root->doc,root->docFile,root->docLine);
9708 matchingDir->setRefItems(root->sli);
9709 matchingDir->setRequirementReferences(root->rqli);
9710 matchingDir->addSectionsToDefinition(root->anchors);
9711 root->commandOverrides.apply_directoryGraph([&](bool b) { matchingDir->overrideDirectoryGraph(b); });
9712 addDirToGroups(root,matchingDir);
9713 }
9714 else
9715 {
9716 warn(root->fileName,root->startLine,"No matching directory found for command \\dir {}",normalizedName);
9717 }
9718 }
9719 for (const auto &e : root->children()) findDirDocumentation(e.get());
9720}
9721
9722//----------------------------------------------------------------------------
9724{
9725 if (root->section.isRequirementDoc())
9726 {
9728 }
9729 for (const auto &e : root->children()) buildRequirementsList(e.get());
9730}
9731
9732//----------------------------------------------------------------------------
9733// create a (sorted) list of separate documentation pages
9734
9735static void buildPageList(Entry *root)
9736{
9737 if (root->section.isPageDoc())
9738 {
9739 if (!root->name.isEmpty())
9740 {
9741 addRelatedPage(root);
9742 }
9743 }
9744 else if (root->section.isMainpageDoc())
9745 {
9746 QCString title=root->args.stripWhiteSpace();
9747 if (title.isEmpty()) title=theTranslator->trMainPage();
9748 //QCString name = Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9749 QCString name = "index";
9750 addRefItem(root->sli,
9751 name,
9752 theTranslator->trPage(TRUE,TRUE),
9753 name,
9754 title,
9755 QCString(),nullptr
9756 );
9757 }
9758 for (const auto &e : root->children()) buildPageList(e.get());
9759}
9760
9761// search for the main page defined in this project
9762static void findMainPage(Entry *root)
9763{
9764 if (root->section.isMainpageDoc())
9765 {
9766 if (Doxygen::mainPage==nullptr && root->tagInfo()==nullptr)
9767 {
9768 //printf("mainpage: docLine=%d startLine=%d\n",root->docLine,root->startLine);
9769 //printf("Found main page! \n======\n%s\n=======\n",qPrint(root->doc));
9770 QCString title=root->args.stripWhiteSpace();
9771 if (title.isEmpty()) title = Config_getString(PROJECT_NAME);
9772 //QCString indexName=Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9773 QCString indexName="index";
9775 indexName, root->brief+root->doc+root->inbodyDocs,title);
9776 //setFileNameForSections(root->anchors,"index",Doxygen::mainPage);
9777 Doxygen::mainPage->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9778 Doxygen::mainPage->setBodySegment(root->startLine,root->startLine,-1);
9779 Doxygen::mainPage->setFileName(indexName);
9780 Doxygen::mainPage->setLocalToc(root->localToc);
9782
9784 if (si)
9785 {
9786 if (!si->ref().isEmpty()) // we are from a tag file
9787 {
9788 // a page name is a label as well! but should no be double either
9790 Doxygen::mainPage->name(),
9791 indexName,
9792 root->startLine,
9793 Doxygen::mainPage->title(),
9795 0); // level 0
9796 }
9797 else if (si->lineNr() != -1)
9798 {
9799 warn(root->fileName,root->startLine,"multiple use of section label '{}' for main page, (first occurrence: {}, line {})",
9800 Doxygen::mainPage->name(),si->fileName(),si->lineNr());
9801 }
9802 else
9803 {
9804 warn(root->fileName,root->startLine,"multiple use of section label '{}' for main page, (first occurrence: {})",
9805 Doxygen::mainPage->name(),si->fileName());
9806 }
9807 }
9808 else
9809 {
9810 // a page name is a label as well! but should no be double either
9812 Doxygen::mainPage->name(),
9813 indexName,
9814 root->startLine,
9815 Doxygen::mainPage->title(),
9817 0); // level 0
9818 }
9819 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9820 }
9821 else if (root->tagInfo()==nullptr)
9822 {
9823 warn(root->fileName,root->startLine,
9824 "found more than one \\mainpage comment block! (first occurrence: {}, line {}), Skipping current block!",
9825 Doxygen::mainPage->docFile(),Doxygen::mainPage->getStartBodyLine());
9826 }
9827 }
9828 for (const auto &e : root->children()) findMainPage(e.get());
9829}
9830
9831// search for the main page imported via tag files and add only the section labels
9832static void findMainPageTagFiles(Entry *root)
9833{
9834 if (root->section.isMainpageDoc())
9835 {
9836 if (Doxygen::mainPage && root->tagInfo())
9837 {
9838 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9839 }
9840 }
9841 for (const auto &e : root->children()) findMainPageTagFiles(e.get());
9842}
9843
9844static void computePageRelations(Entry *root)
9845{
9846 if ((root->section.isPageDoc() || root->section.isMainpageDoc()) && !root->name.isEmpty())
9847 {
9848 PageDef *pd = root->section.isPageDoc() ?
9849 Doxygen::pageLinkedMap->find(root->name) :
9850 Doxygen::mainPage.get();
9851 if (pd)
9852 {
9853 for (const BaseInfo &bi : root->extends)
9854 {
9855 PageDef *subPd = Doxygen::pageLinkedMap->find(bi.name);
9856 if (pd==subPd)
9857 {
9858 term("page defined {} with label {} is a direct "
9859 "subpage of itself! Please remove this cyclic dependency.\n",
9860 warn_line(pd->docFile(),pd->docLine()),pd->name());
9861 }
9862 else if (subPd)
9863 {
9864 pd->addInnerCompound(subPd);
9865 //printf("*** Added subpage relation: %s->%s\n",
9866 // qPrint(pd->name()),qPrint(subPd->name()));
9867 }
9868 }
9869 }
9870 }
9871 for (const auto &e : root->children()) computePageRelations(e.get());
9872}
9873
9875{
9876 for (const auto &pd : *Doxygen::pageLinkedMap)
9877 {
9878 Definition *ppd = pd->getOuterScope();
9879 while (ppd)
9880 {
9881 if (ppd==pd.get())
9882 {
9883 term("page defined {} with label {} is a subpage "
9884 "of itself! Please remove this cyclic dependency.\n",
9885 warn_line(pd->docFile(),pd->docLine()),pd->name());
9886 }
9887 ppd=ppd->getOuterScope();
9888 }
9889 }
9890}
9891
9892//----------------------------------------------------------------------------
9893
9895{
9896 for (const auto &si : SectionManager::instance())
9897 {
9898 //printf("si->label='%s' si->definition=%s si->fileName='%s'\n",
9899 // qPrint(si->label),si->definition?qPrint(si->definition->name()):"<none>",
9900 // qPrint(si->fileName));
9901 PageDef *pd=nullptr;
9902
9903 // hack: the items of a todo/test/bug/deprecated list are all fragments from
9904 // different files, so the resulting section's all have the wrong file
9905 // name (not from the todo/test/bug/deprecated list, but from the file in
9906 // which they are defined). We correct this here by looking at the
9907 // generated section labels!
9909 {
9910 QCString label="_"+rl->listName(); // "_todo", "_test", ...
9911 if (si->label().left(label.length())==label)
9912 {
9913 si->setFileName(rl->listName());
9914 si->setGenerated(TRUE);
9915 break;
9916 }
9917 }
9918
9919 //printf("start: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
9920 if (!si->generated())
9921 {
9922 // if this section is in a page and the page is in a group, then we
9923 // have to adjust the link file name to point to the group.
9924 if (!si->fileName().isEmpty() &&
9925 (pd=Doxygen::pageLinkedMap->find(si->fileName())) &&
9926 pd->getGroupDef())
9927 {
9928 si->setFileName(pd->getGroupDef()->getOutputFileBase());
9929 }
9930
9931 if (si->definition())
9932 {
9933 // TODO: there should be one function in Definition that returns
9934 // the file to link to, so we can avoid the following tests.
9935 const GroupDef *gd=nullptr;
9936 if (si->definition()->definitionType()==Definition::TypeMember)
9937 {
9938 gd = (toMemberDef(si->definition()))->getGroupDef();
9939 }
9940
9941 if (gd)
9942 {
9943 si->setFileName(gd->getOutputFileBase());
9944 }
9945 else
9946 {
9947 //si->fileName=si->definition->getOutputFileBase();
9948 //printf("Setting si->fileName to %s\n",qPrint(si->fileName));
9949 }
9950 }
9951 }
9952 //printf("end: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
9953 }
9954}
9955
9956
9957
9958//----------------------------------------------------------------------------
9959// generate all separate documentation pages
9960
9961
9962static void generatePageDocs()
9963{
9964 //printf("documentedPages=%d real=%d\n",documentedPages,Doxygen::pageLinkedMap->count());
9965 if (Index::instance().numDocumentedPages()==0) return;
9966 for (const auto &pd : *Doxygen::pageLinkedMap)
9967 {
9968 if (!pd->getGroupDef() && !pd->isReference())
9969 {
9970 msg("Generating docs for page {}...\n",pd->name());
9971 pd->writeDocumentation(*g_outputList);
9972 }
9973 }
9974}
9975
9976//----------------------------------------------------------------------------
9977// create a (sorted) list & dictionary of example pages
9978
9979static void buildExampleList(Entry *root)
9980{
9981 if ((root->section.isExample() || root->section.isExampleLineno()) && !root->name.isEmpty())
9982 {
9983 if (Doxygen::exampleLinkedMap->find(root->name))
9984 {
9985 warn(root->fileName,root->startLine,"Example {} was already documented. Ignoring documentation found here.",root->name);
9986 }
9987 else
9988 {
9989 PageDef *pd = Doxygen::exampleLinkedMap->add(root->name,
9990 createPageDef(root->fileName,root->startLine,
9991 root->name,root->brief+root->doc+root->inbodyDocs,root->args));
9992 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9993 pd->setFileName(convertNameToFile(pd->name()+"-example",FALSE,TRUE));
9995 pd->setLanguage(root->lang);
9996 pd->setShowLineNo(root->section.isExampleLineno());
9997
9998 //we don't add example to groups
9999 //addExampleToGroups(root,pd);
10000 }
10001 }
10002 for (const auto &e : root->children()) buildExampleList(e.get());
10003}
10004
10005//----------------------------------------------------------------------------
10006// prints the Entry tree (for debugging)
10007
10008void printNavTree(Entry *root,int indent)
10009{
10011 {
10012 QCString indentStr;
10013 indentStr.fill(' ',indent);
10014 Debug::print(Debug::Entries,0,"{}{} at {}:{} (sec={}, spec={})\n",
10015 indentStr.isEmpty()?"":indentStr,
10016 root->name.isEmpty()?"<empty>":root->name,
10017 root->fileName,root->startLine,
10018 root->section.to_string(),
10019 root->spec.to_string());
10020 for (const auto &e : root->children())
10021 {
10022 printNavTree(e.get(),indent+2);
10023 }
10024 }
10025}
10026
10027
10028//----------------------------------------------------------------------------
10029// prints the Sections tree (for debugging)
10030
10032{
10034 {
10035 for (const auto &si : SectionManager::instance())
10036 {
10037 Debug::print(Debug::Sections,0,"Section = {}, file = {}, title = {}, type = {}, ref = {}\n",
10038 si->label(),si->fileName(),si->title(),si->type().level(),si->ref());
10039 }
10040 }
10041}
10042
10043
10044//----------------------------------------------------------------------------
10045// generate the example documentation
10046
10048{
10049 g_outputList->disable(OutputType::Man);
10050 for (const auto &pd : *Doxygen::exampleLinkedMap)
10051 {
10052 msg("Generating docs for example {}...\n",pd->name());
10053 SrcLangExt lang = getLanguageFromFileName(pd->name(), SrcLangExt::Unknown);
10054 if (lang != SrcLangExt::Unknown)
10055 {
10056 QCString ext = getFileNameExtension(pd->name());
10057 auto intf = Doxygen::parserManager->getCodeParser(ext);
10058 intf->resetCodeParserState();
10059 }
10060 QCString n=pd->getOutputFileBase();
10061 startFile(*g_outputList,n,false,n,pd->name());
10063 g_outputList->docify(pd->name());
10065 g_outputList->startContents();
10066 QCString lineNoOptStr;
10067 if (pd->showLineNo())
10068 {
10069 lineNoOptStr="{lineno}";
10070 }
10071 g_outputList->generateDoc(pd->docFile(), // file
10072 pd->docLine(), // startLine
10073 pd.get(), // context
10074 nullptr, // memberDef
10075 (pd->briefDescription().isEmpty()?"":pd->briefDescription()+"\n\n")+
10076 pd->documentation()+"\n\n\\include"+lineNoOptStr+" "+pd->name(), // docs
10077 DocOptions()
10078 .setIndexWords(true)
10079 .setExample(pd->name()));
10080 endFile(*g_outputList); // contains g_outputList->endContents()
10081 }
10083}
10084
10085//----------------------------------------------------------------------------
10086// generate module pages
10087
10089{
10090 for (const auto &gd : *Doxygen::groupLinkedMap)
10091 {
10092 if (!gd->isReference())
10093 {
10094 gd->writeDocumentation(*g_outputList);
10095 }
10096 }
10097}
10098
10099//----------------------------------------------------------------------------
10100// generate module pages
10101
10103{
10104 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10105 if (numThreads>1) // multi threaded processing
10106 {
10107 struct DocContext
10108 {
10109 DocContext(ClassDefMutable *cdm_,const OutputList &ol_)
10110 : cdm(cdm_), ol(ol_) {}
10111 ClassDefMutable *cdm;
10112 OutputList ol;
10113 };
10114 ThreadPool threadPool(numThreads);
10115 std::vector< std::future< std::shared_ptr<DocContext> > > results;
10116 // for each class in the namespace...
10117 for (const auto &cd : classList)
10118 {
10120 if (cdm)
10121 {
10122 auto ctx = std::make_shared<DocContext>(cdm,*g_outputList);
10123 auto processFile = [ctx]()
10124 {
10125 if ( ( ctx->cdm->isLinkableInProject() &&
10126 !ctx->cdm->isImplicitTemplateInstance()
10127 ) // skip external references, anonymous compounds and
10128 // template instances and nested classes
10129 && !ctx->cdm->isHidden() && !ctx->cdm->isEmbeddedInOuterScope()
10130 )
10131 {
10132 msg("Generating docs for compound {}...\n",ctx->cdm->displayName());
10133 ctx->cdm->writeDocumentation(ctx->ol);
10134 ctx->cdm->writeMemberList(ctx->ol);
10135 }
10136 ctx->cdm->writeDocumentationForInnerClasses(ctx->ol);
10137 return ctx;
10138 };
10139 results.emplace_back(threadPool.queue(processFile));
10140 }
10141 }
10142 // wait for the results
10143 for (auto &f : results)
10144 {
10145 auto ctx = f.get();
10146 }
10147 }
10148 else // single threaded processing
10149 {
10150 // for each class in the namespace...
10151 for (const auto &cd : classList)
10152 {
10154 if (cdm)
10155 {
10156 if ( ( cd->isLinkableInProject() &&
10157 !cd->isImplicitTemplateInstance()
10158 ) // skip external references, anonymous compounds and
10159 // template instances and nested classes
10160 && !cd->isHidden() && !cd->isEmbeddedInOuterScope()
10161 )
10162 {
10163 msg("Generating docs for compound {}...\n",cd->displayName());
10164
10167 }
10169 }
10170 }
10171 }
10172}
10173
10175{
10176 // for each concept in the namespace...
10177 for (const auto &cd : conceptList)
10178 {
10180 if ( cdm && cd->isLinkableInProject() && !cd->isHidden())
10181 {
10182 msg("Generating docs for concept {}...\n",cd->name());
10184 }
10185 }
10186}
10187
10189{
10190 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
10191
10192 //writeNamespaceIndex(*g_outputList);
10193
10194 // for each namespace...
10195 for (const auto &nd : *Doxygen::namespaceLinkedMap)
10196 {
10197 if (nd->isLinkableInProject())
10198 {
10200 if (ndm)
10201 {
10202 msg("Generating docs for namespace {}\n",nd->displayName());
10204 }
10205 }
10206
10207 generateNamespaceClassDocs(nd->getClasses());
10208 if (sliceOpt)
10209 {
10210 generateNamespaceClassDocs(nd->getInterfaces());
10211 generateNamespaceClassDocs(nd->getStructs());
10212 generateNamespaceClassDocs(nd->getExceptions());
10213 }
10214 generateNamespaceConceptDocs(nd->getConcepts());
10215 }
10216}
10217
10219{
10220 std::string oldDir = Dir::currentDirPath();
10221 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
10224 {
10225 err("failed to run html help compiler on {}\n", HtmlHelp::hhpFileName);
10226 }
10227 Dir::setCurrent(oldDir);
10228}
10229
10231{
10232 QCString args = Qhp::qhpFileName + " -o \"" + Qhp::getQchFileName() + "\"";
10233 std::string oldDir = Dir::currentDirPath();
10234 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
10235
10236 QCString qhgLocation=Config_getString(QHG_LOCATION);
10237 if (Debug::isFlagSet(Debug::Qhp)) // produce info for debugging
10238 {
10239 // run qhelpgenerator -v and extract the Qt version used
10240 QCString cmd=qhgLocation+ " -v 2>&1";
10241 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
10242 FILE *f=Portable::popen(cmd,"r");
10243 if (!f)
10244 {
10245 err("could not execute {}\n",qhgLocation);
10246 }
10247 else
10248 {
10249 const size_t bufSize = 1024;
10250 char inBuf[bufSize+1];
10251 size_t numRead=fread(inBuf,1,bufSize,f);
10252 inBuf[numRead] = '\0';
10253 Debug::print(Debug::Qhp,0,"{}",inBuf);
10255
10256 int qtVersion=0;
10257 static const reg::Ex versionReg(R"(Qt (\d+)\.(\d+)\.(\d+))");
10258 reg::Match match;
10259 std::string s = inBuf;
10260 if (reg::search(s,match,versionReg))
10261 {
10262 qtVersion = 10000*QCString(match[1].str()).toInt() +
10263 100*QCString(match[2].str()).toInt() +
10264 QCString(match[3].str()).toInt();
10265 }
10266 if (qtVersion>0 && (qtVersion<60000 || qtVersion >= 60205))
10267 {
10268 // dump the output of qhelpgenerator -c file.qhp
10269 // Qt<6 or Qt>=6.2.5 or higher, see https://bugreports.qt.io/browse/QTBUG-101070
10270 cmd=qhgLocation+ " -c " + Qhp::qhpFileName + " 2>&1";
10271 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
10272 f=Portable::popen(cmd,"r");
10273 if (!f)
10274 {
10275 err("could not execute {}\n",qhgLocation);
10276 }
10277 else
10278 {
10279 std::string output;
10280 while ((numRead=fread(inBuf,1,bufSize,f))>0)
10281 {
10282 inBuf[numRead] = '\0';
10283 output += inBuf;
10284 }
10286 Debug::print(Debug::Qhp,0,"{}",output);
10287 }
10288 }
10289 }
10290 }
10291
10292 if (Portable::system(qhgLocation, args, FALSE))
10293 {
10294 err("failed to run qhelpgenerator on {}\n",Qhp::qhpFileName);
10295 }
10296 Dir::setCurrent(oldDir);
10297}
10298
10299//----------------------------------------------------------------------------
10300
10302{
10303 // check dot path
10304 QCString dotPath = Config_getString(DOT_PATH);
10305 if (!dotPath.isEmpty())
10306 {
10307 FileInfo fi(dotPath.str());
10308 if (!(fi.exists() && fi.isFile()) )// not an existing user specified path + exec
10309 {
10310 dotPath = dotPath+"/dot"+Portable::commandExtension();
10311 FileInfo dp(dotPath.str());
10312 if (!dp.exists() || !dp.isFile())
10313 {
10314 warn_uncond("the dot tool could not be found as '{}'\n",dotPath);
10315 dotPath = "dot";
10316 dotPath += Portable::commandExtension();
10317 }
10318 }
10319#if defined(_WIN32) // convert slashes
10320 size_t l=dotPath.length();
10321 for (size_t i=0;i<l;i++) if (dotPath.at(i)=='/') dotPath.at(i)='\\';
10322#endif
10323 }
10324 else
10325 {
10326 dotPath = "dot";
10327 dotPath += Portable::commandExtension();
10328 }
10329 Doxygen::verifiedDotPath = dotPath;
10331}
10332
10333//----------------------------------------------------------------------------
10334
10335/*! Generate a template version of the configuration file.
10336 * If the \a shortList parameter is TRUE a configuration file without
10337 * comments will be generated.
10338 */
10339static void generateConfigFile(const QCString &configFile,bool shortList,
10340 bool updateOnly=FALSE)
10341{
10342 std::ofstream f;
10343 bool fileOpened=openOutputFile(configFile,f);
10344 bool writeToStdout=configFile=="-";
10345 if (fileOpened)
10346 {
10347 TextStream t(&f);
10348 Config::writeTemplate(t,shortList,updateOnly);
10349 if (!writeToStdout)
10350 {
10351 if (!updateOnly)
10352 {
10353 msg("\n\nConfiguration file '{}' created.\n\n",configFile);
10354 msg("Now edit the configuration file and enter\n\n");
10355 if (configFile!="Doxyfile" && configFile!="doxyfile")
10356 msg(" doxygen {}\n\n",configFile);
10357 else
10358 msg(" doxygen\n\n");
10359 msg("to generate the documentation for your project\n\n");
10360 }
10361 else
10362 {
10363 msg("\n\nConfiguration file '{}' updated.\n\n",configFile);
10364 }
10365 }
10366 }
10367 else
10368 {
10369 term("Cannot open file {} for writing\n",configFile);
10370 }
10371}
10372
10374{
10375 std::ofstream f;
10376 bool fileOpened=openOutputFile("-",f);
10377 if (fileOpened)
10378 {
10379 TextStream t(&f);
10380 Config::compareDoxyfile(t,diffList);
10381 }
10382 else
10383 {
10384 term("Cannot open stdout for writing\n");
10385 }
10386}
10387
10388//----------------------------------------------------------------------------
10389// read and parse a tag file
10390
10391static void readTagFile(const std::shared_ptr<Entry> &root,const QCString &tagLine)
10392{
10393 QCString fileName;
10394 QCString destName;
10395 int eqPos = tagLine.find('=');
10396 if (eqPos!=-1) // tag command contains a destination
10397 {
10398 fileName = tagLine.left(eqPos).stripWhiteSpace();
10399 destName = tagLine.right(tagLine.length()-eqPos-1).stripWhiteSpace();
10400 if (fileName.isEmpty() || destName.isEmpty()) return;
10401 //printf("insert tagDestination %s->%s\n",qPrint(fi.fileName()),qPrint(destName));
10402 }
10403 else
10404 {
10405 fileName = tagLine;
10406 }
10407
10408 FileInfo fi(fileName.str());
10409 if (!fi.exists() || !fi.isFile())
10410 {
10411 err("Tag file '{}' does not exist or is not a file. Skipping it...\n",fileName);
10412 return;
10413 }
10414
10415 if (Doxygen::tagFileSet.find(fi.absFilePath()) != Doxygen::tagFileSet.end()) return;
10416
10417 Doxygen::tagFileSet.emplace(fi.absFilePath());
10418
10419 if (!destName.isEmpty())
10420 {
10421 Doxygen::tagDestinationMap.emplace(fi.absFilePath(), destName.str());
10422 msg("Reading tag file '{}', location '{}'...\n",fileName,destName);
10423 }
10424 else
10425 {
10426 msg("Reading tag file '{}'...\n",fileName);
10427 }
10428
10429 parseTagFile(root,fi.absFilePath().c_str());
10430}
10431
10432//----------------------------------------------------------------------------
10434{
10435 const StringVector &latexExtraStyleSheet = Config_getList(LATEX_EXTRA_STYLESHEET);
10436 for (const auto &sheet : latexExtraStyleSheet)
10437 {
10438 std::string fileName = sheet;
10439 if (!fileName.empty())
10440 {
10441 FileInfo fi(fileName);
10442 if (!fi.exists())
10443 {
10444 err("Style sheet '{}' specified by LATEX_EXTRA_STYLESHEET does not exist!\n",fileName);
10445 }
10446 else if (fi.isDir())
10447 {
10448 err("Style sheet '{}' specified by LATEX_EXTRA_STYLESHEET is a directory, it has to be a file!\n", fileName);
10449 }
10450 else
10451 {
10452 QCString destFileName = Config_getString(LATEX_OUTPUT)+"/"+fi.fileName();
10454 {
10455 destFileName += LATEX_STYLE_EXTENSION;
10456 }
10457 copyFile(fileName, destFileName);
10458 }
10459 }
10460 }
10461}
10462
10463//----------------------------------------------------------------------------
10464static void copyStyleSheet()
10465{
10466 QCString htmlStyleSheet = Config_getString(HTML_STYLESHEET);
10467 if (!htmlStyleSheet.isEmpty())
10468 {
10469 if (!htmlStyleSheet.startsWith("http:") && !htmlStyleSheet.startsWith("https:"))
10470 {
10471 FileInfo fi(htmlStyleSheet.str());
10472 if (!fi.exists())
10473 {
10474 err("Style sheet '{}' specified by HTML_STYLESHEET does not exist!\n",htmlStyleSheet);
10475 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
10476 }
10477 else if (fi.isDir())
10478 {
10479 err("Style sheet '{}' specified by HTML_STYLESHEET is a directory, it has to be a file!\n",htmlStyleSheet);
10480 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
10481 }
10482 else
10483 {
10484 QCString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
10485 copyFile(htmlStyleSheet,destFileName);
10486 }
10487 }
10488 }
10489 const StringVector &htmlExtraStyleSheet = Config_getList(HTML_EXTRA_STYLESHEET);
10490 for (const auto &sheet : htmlExtraStyleSheet)
10491 {
10492 QCString fileName(sheet);
10493 if (!fileName.isEmpty() && !fileName.startsWith("http:") && !fileName.startsWith("https:"))
10494 {
10495 FileInfo fi(fileName.str());
10496 if (!fi.exists())
10497 {
10498 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET does not exist!\n",fileName);
10499 }
10500 else if (fi.fileName()=="doxygen.css" || fi.fileName()=="tabs.css" || fi.fileName()=="navtree.css")
10501 {
10502 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET is already a built-in stylesheet. Please use a different name\n",fi.fileName());
10503 }
10504 else if (fi.isDir())
10505 {
10506 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET is a directory, it has to be a file!\n",fileName);
10507 }
10508 else
10509 {
10510 QCString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
10511 copyFile(fileName, destFileName);
10512 }
10513 }
10514 }
10515}
10516
10517static void copyLogo(const QCString &outputOption)
10518{
10519 QCString projectLogo = projectLogoFile();
10520 if (!projectLogo.isEmpty())
10521 {
10522 FileInfo fi(projectLogo.str());
10523 if (!fi.exists())
10524 {
10525 err("Project logo '{}' specified by PROJECT_LOGO does not exist!\n",projectLogo);
10526 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
10527 }
10528 else if (fi.isDir())
10529 {
10530 err("Project logo '{}' specified by PROJECT_LOGO is a directory, it has to be a file!\n",projectLogo);
10531 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
10532 }
10533 else
10534 {
10535 QCString destFileName = outputOption+"/"+fi.fileName();
10536 copyFile(projectLogo,destFileName);
10537 Doxygen::indexList->addImageFile(fi.fileName());
10538 }
10539 }
10540}
10541
10542static void copyIcon(const QCString &outputOption)
10543{
10544 QCString projectIcon = Config_getString(PROJECT_ICON);
10545 if (!projectIcon.isEmpty())
10546 {
10547 FileInfo fi(projectIcon.str());
10548 if (!fi.exists())
10549 {
10550 err("Project icon '{}' specified by PROJECT_ICON does not exist!\n",projectIcon);
10551 projectIcon = Config_updateString(PROJECT_ICON,""); // revert to the default
10552 }
10553 else if (fi.isDir())
10554 {
10555 err("Project icon '{}' specified by PROJECT_ICON is a directory, it has to be a file!\n",projectIcon);
10556 projectIcon = Config_updateString(PROJECT_ICON,""); // revert to the default
10557 }
10558 else
10559 {
10560 QCString destFileName = outputOption+"/"+fi.fileName();
10561 copyFile(projectIcon,destFileName);
10562 Doxygen::indexList->addImageFile(fi.fileName());
10563 }
10564 }
10565}
10566
10567static void copyExtraFiles(const StringVector &files,const QCString &filesOption,const QCString &outputOption)
10568{
10569 for (const auto &fileName : files)
10570 {
10571 if (!fileName.empty())
10572 {
10573 FileInfo fi(fileName);
10574 if (!fi.exists())
10575 {
10576 err("Extra file '{}' specified in {} does not exist!\n", fileName,filesOption);
10577 }
10578 else if (fi.isDir())
10579 {
10580 err("Extra file '{}' specified in {} is a directory, it has to be a file!\n", fileName,filesOption);
10581 }
10582 else
10583 {
10584 QCString destFileName = outputOption+"/"+fi.fileName();
10585 Doxygen::indexList->addImageFile(fi.fileName());
10586 copyFile(fileName, destFileName);
10587 }
10588 }
10589 }
10590}
10591
10592//----------------------------------------------------------------------------
10593
10595{
10596 for (const auto &fn : *Doxygen::inputNameLinkedMap)
10597 {
10598 struct FileEntry
10599 {
10600 FileEntry(const QCString &p,FileDef *fd) : path(p), fileDef(fd) {}
10601 QCString path;
10602 FileDef *fileDef;
10603 };
10604
10605 // collect the entry for which to compute the longest common prefix (LCP) of the path
10606 std::vector<FileEntry> fileEntries;
10607 for (const auto &fd : *fn)
10608 {
10609 if (!fd->isReference()) // skip external references
10610 {
10611 fileEntries.emplace_back(fd->getPath(),fd.get());
10612 }
10613 }
10614
10615 size_t size = fileEntries.size();
10616
10617 if (size==1) // name if unique, so diskname is simply the name
10618 {
10619 FileDef *fd = fileEntries[0].fileDef;
10620 fd->setDiskName(fn->fileName());
10621 }
10622 else if (size>1) // multiple occurrences of the same file name
10623 {
10624 // sort the array
10625 std::stable_sort(fileEntries.begin(),
10626 fileEntries.end(),
10627 [](const FileEntry &fe1,const FileEntry &fe2)
10628 { return qstricmp_sort(fe1.path,fe2.path)<0; }
10629 );
10630
10631 // since the entries are sorted, the common prefix of the whole array is same
10632 // as the common prefix between the first and last entry
10633 const FileEntry &first = fileEntries[0];
10634 const FileEntry &last = fileEntries[size-1];
10635 int first_path_size = static_cast<int>(first.path.size())-1; // -1 to skip trailing slash
10636 int last_path_size = static_cast<int>(last.path.size())-1; // -1 to skip trailing slash
10637 int j=0;
10638 int i=0;
10639 for (i=0;i<first_path_size && i<last_path_size;i++)
10640 {
10641 if (first.path[i]=='/') j=i;
10642 if (first.path[i]!=last.path[i]) break;
10643 }
10644 if (i==first_path_size && i<last_path_size && last.path[i]=='/')
10645 {
10646 // case first='some/path' and last='some/path/more' => match is 'some/path'
10647 j=first_path_size;
10648 }
10649 else if (i==last_path_size && i<first_path_size && first.path[i]=='/')
10650 {
10651 // case first='some/path/more' and last='some/path' => match is 'some/path'
10652 j=last_path_size;
10653 }
10654
10655 // add non-common part of the path to the name
10656 for (auto &fileEntry : fileEntries)
10657 {
10658 QCString prefix = fileEntry.path.right(fileEntry.path.length()-j-1);
10659 fileEntry.fileDef->setName(prefix+fn->fileName());
10660 //printf("!!!!!!!! non unique disk name=%s:%s\n",qPrint(prefix),fn->fileName());
10661 fileEntry.fileDef->setDiskName(prefix+fn->fileName());
10662 }
10663 }
10664 }
10665}
10666
10667
10668
10669//----------------------------------------------------------------------------
10670
10671static std::unique_ptr<OutlineParserInterface> getParserForFile(const QCString &fn)
10672{
10673 QCString fileName=fn;
10674 QCString extension;
10675 int sep = fileName.findRev('/');
10676 int ei = fileName.findRev('.');
10677 if (ei!=-1 && (sep==-1 || ei>sep)) // matches dir/file.ext but not dir.1/file
10678 {
10679 extension=fileName.right(fileName.length()-ei);
10680 }
10681 else
10682 {
10683 extension = ".no_extension";
10684 }
10685
10686 return Doxygen::parserManager->getOutlineParser(extension);
10687}
10688
10689static std::shared_ptr<Entry> parseFile(OutlineParserInterface &parser,
10690 FileDef *fd,const QCString &fn,
10691 ClangTUParser *clangParser,bool newTU)
10692{
10693 QCString fileName=fn;
10694 AUTO_TRACE("fileName={}",fileName);
10695 QCString extension;
10696 int ei = fileName.findRev('.');
10697 if (ei!=-1)
10698 {
10699 extension=fileName.right(fileName.length()-ei);
10700 }
10701 else
10702 {
10703 extension = ".no_extension";
10704 }
10705
10706 FileInfo fi(fileName.str());
10707 std::string preBuf;
10708
10709 if (Config_getBool(ENABLE_PREPROCESSING) &&
10710 parser.needsPreprocessing(extension))
10711 {
10712 Preprocessor preprocessor;
10713 const StringVector &includePath = Config_getList(INCLUDE_PATH);
10714 for (const auto &s : includePath)
10715 {
10716 std::string absPath = FileInfo(s).absFilePath();
10717 preprocessor.addSearchDir(absPath);
10718 }
10719 std::string inBuf;
10720 msg("Preprocessing {}...\n",fn);
10721 readInputFile(fileName,inBuf);
10722 addTerminalCharIfMissing(inBuf,'\n');
10723 preprocessor.processFile(fileName,inBuf,preBuf);
10724 }
10725 else // no preprocessing
10726 {
10727 msg("Reading {}...\n",fn);
10728 readInputFile(fileName,preBuf);
10729 addTerminalCharIfMissing(preBuf,'\n');
10730 }
10731
10732 std::string convBuf;
10733 convBuf.reserve(preBuf.size()+1024);
10734
10735 // convert multi-line C++ comments to C style comments
10736 convertCppComments(preBuf,convBuf,fileName.str());
10737
10738 std::shared_ptr<Entry> fileRoot = std::make_shared<Entry>();
10739 // use language parse to parse the file
10740 if (clangParser)
10741 {
10742 if (newTU) clangParser->parse();
10743 clangParser->switchToFile(fd);
10744 }
10745 parser.parseInput(fileName,convBuf.data(),fileRoot,clangParser);
10746 fileRoot->setFileDef(fd);
10747 return fileRoot;
10748}
10749
10750//! parse the list of input files
10751static void parseFilesMultiThreading(const std::shared_ptr<Entry> &root)
10752{
10753 AUTO_TRACE();
10754#if USE_LIBCLANG
10756 {
10757 StringUnorderedSet processedFiles;
10758
10759 // create a dictionary with files to process
10760 StringUnorderedSet filesToProcess;
10761 for (const auto &s : g_inputFiles)
10762 {
10763 filesToProcess.insert(s);
10764 }
10765
10766 std::mutex processedFilesLock;
10767 // process source files (and their include dependencies)
10768 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10769 msg("Processing input using {} threads.\n",numThreads);
10770 ThreadPool threadPool(numThreads);
10771 using FutureType = std::vector< std::shared_ptr<Entry> >;
10772 std::vector< std::future< FutureType > > results;
10773 for (const auto &s : g_inputFiles)
10774 {
10775 bool ambig = false;
10776 QCString qs = s;
10778 ASSERT(fd!=nullptr);
10779 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt::Cpp) // this is a source file
10780 {
10781 // lambda representing the work to executed by a thread
10782 auto processFile = [qs,&filesToProcess,&processedFilesLock,&processedFiles]() {
10783 bool ambig_l = false;
10784 std::vector< std::shared_ptr<Entry> > roots;
10786 auto clangParser = ClangParser::instance()->createTUParser(fd_l);
10787 auto parser = getParserForFile(qs);
10788 auto fileRoot { parseFile(*parser.get(),fd_l,qs,clangParser.get(),true) };
10789 roots.push_back(fileRoot);
10790
10791 // Now process any include files in the same translation unit
10792 // first. When libclang is used this is much more efficient.
10793 for (auto incFile : clangParser->filesInSameTU())
10794 {
10795 QCString qincFile = incFile;
10796 if (filesToProcess.find(incFile)!=filesToProcess.end())
10797 {
10798 bool needsToBeProcessed = false;
10799 {
10800 std::lock_guard<std::mutex> lock(processedFilesLock);
10801 needsToBeProcessed = processedFiles.find(incFile)==processedFiles.end();
10802 if (needsToBeProcessed) processedFiles.insert(incFile);
10803 }
10804 if (qincFile!=qs && needsToBeProcessed)
10805 {
10806 FileDef *ifd=findFileDef(Doxygen::inputNameLinkedMap,qincFile,ambig_l);
10807 if (ifd && !ifd->isReference())
10808 {
10809 //printf(" Processing %s in same translation unit as %s\n",incFile,qPrint(s));
10810 fileRoot = parseFile(*parser.get(),ifd,qincFile,clangParser.get(),false);
10811 roots.push_back(fileRoot);
10812 }
10813 }
10814 }
10815 }
10816 return roots;
10817 };
10818 // dispatch the work and collect the future results
10819 results.emplace_back(threadPool.queue(processFile));
10820 }
10821 }
10822 // synchronize with the Entry result lists produced and add them to the root
10823 for (auto &f : results)
10824 {
10825 auto l = f.get();
10826 for (auto &e : l)
10827 {
10828 root->moveToSubEntryAndKeep(e);
10829 }
10830 }
10831 // process remaining files
10832 results.clear();
10833 for (const auto &s : g_inputFiles)
10834 {
10835 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
10836 {
10837 // lambda representing the work to executed by a thread
10838 auto processFile = [s]() {
10839 bool ambig = false;
10840 QCString qs = s;
10841 std::vector< std::shared_ptr<Entry> > roots;
10843 auto parser { getParserForFile(qs) };
10844 bool useClang = getLanguageFromFileName(qs)==SrcLangExt::Cpp;
10845 if (useClang)
10846 {
10847 auto clangParser = ClangParser::instance()->createTUParser(fd);
10848 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
10849 roots.push_back(fileRoot);
10850 }
10851 else
10852 {
10853 auto fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10854 roots.push_back(fileRoot);
10855 }
10856 return roots;
10857 };
10858 results.emplace_back(threadPool.queue(processFile));
10859 }
10860 }
10861 // synchronize with the Entry result lists produced and add them to the root
10862 for (auto &f : results)
10863 {
10864 auto l = f.get();
10865 for (auto &e : l)
10866 {
10867 root->moveToSubEntryAndKeep(e);
10868 }
10869 }
10870 }
10871 else // normal processing
10872#endif
10873 {
10874 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10875 msg("Processing input using {} threads.\n",numThreads);
10876 ThreadPool threadPool(numThreads);
10877 using FutureType = std::shared_ptr<Entry>;
10878 std::vector< std::future< FutureType > > results;
10879 for (const auto &s : g_inputFiles)
10880 {
10881 // lambda representing the work to executed by a thread
10882 auto processFile = [s]() {
10883 bool ambig = false;
10884 QCString qs = s;
10886 auto parser = getParserForFile(qs);
10887 auto fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10888 return fileRoot;
10889 };
10890 // dispatch the work and collect the future results
10891 results.emplace_back(threadPool.queue(processFile));
10892 }
10893 // synchronize with the Entry results produced and add them to the root
10894 for (auto &f : results)
10895 {
10896 root->moveToSubEntryAndKeep(f.get());
10897 }
10898 }
10899}
10900
10901//! parse the list of input files
10902static void parseFilesSingleThreading(const std::shared_ptr<Entry> &root)
10903{
10904 AUTO_TRACE();
10905#if USE_LIBCLANG
10907 {
10908 StringUnorderedSet processedFiles;
10909
10910 // create a dictionary with files to process
10911 StringUnorderedSet filesToProcess;
10912 for (const auto &s : g_inputFiles)
10913 {
10914 filesToProcess.insert(s);
10915 }
10916
10917 // process source files (and their include dependencies)
10918 for (const auto &s : g_inputFiles)
10919 {
10920 bool ambig = false;
10921 QCString qs =s;
10923 ASSERT(fd!=nullptr);
10924 if (fd->isSource() && !fd->isReference() && getLanguageFromFileName(qs)==SrcLangExt::Cpp) // this is a source file
10925 {
10926 auto clangParser = ClangParser::instance()->createTUParser(fd);
10927 auto parser { getParserForFile(qs) };
10928 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
10929 root->moveToSubEntryAndKeep(fileRoot);
10930 processedFiles.insert(s);
10931
10932 // Now process any include files in the same translation unit
10933 // first. When libclang is used this is much more efficient.
10934 for (auto incFile : clangParser->filesInSameTU())
10935 {
10936 //printf(" file %s\n",qPrint(incFile));
10937 if (filesToProcess.find(incFile)!=filesToProcess.end() && // file need to be processed
10938 processedFiles.find(incFile)==processedFiles.end()) // and is not processed already
10939 {
10941 if (ifd && !ifd->isReference())
10942 {
10943 //printf(" Processing %s in same translation unit as %s\n",qPrint(incFile),qPrint(qs));
10944 fileRoot = parseFile(*parser.get(),ifd,incFile,clangParser.get(),false);
10945 root->moveToSubEntryAndKeep(fileRoot);
10946 processedFiles.insert(incFile);
10947 }
10948 }
10949 }
10950 }
10951 }
10952 // process remaining files
10953 for (const auto &s : g_inputFiles)
10954 {
10955 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
10956 {
10957 bool ambig = false;
10958 QCString qs = s;
10960 if (getLanguageFromFileName(qs)==SrcLangExt::Cpp) // not yet processed
10961 {
10962 auto clangParser = ClangParser::instance()->createTUParser(fd);
10963 auto parser { getParserForFile(qs) };
10964 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
10965 root->moveToSubEntryAndKeep(fileRoot);
10966 }
10967 else
10968 {
10969 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(qs) };
10970 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10971 root->moveToSubEntryAndKeep(fileRoot);
10972 }
10973 processedFiles.insert(s);
10974 }
10975 }
10976 }
10977 else // normal processing
10978#endif
10979 {
10980 for (const auto &s : g_inputFiles)
10981 {
10982 bool ambig = false;
10983 QCString qs = s;
10985 ASSERT(fd!=nullptr);
10986 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(qs) };
10987 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10988 root->moveToSubEntryAndKeep(std::move(fileRoot));
10989 }
10990 }
10991}
10992
10993// resolves a path that may include symlinks, if a recursive symlink is
10994// found an empty string is returned.
10995static std::string resolveSymlink(const std::string &path)
10996{
10997 int sepPos=0;
10998 int oldPos=0;
10999 StringUnorderedSet nonSymlinks;
11000 StringUnorderedSet known;
11001 QCString result(path);
11002 QCString oldPrefix = "/";
11003 do
11004 {
11005#if defined(_WIN32)
11006 // UNC path, skip server and share name
11007 if (sepPos==0 && (result.startsWith("//") || result.startsWith("\\\\")))
11008 sepPos = result.find('/',2);
11009 if (sepPos!=-1)
11010 sepPos = result.find('/',sepPos+1);
11011#else
11012 sepPos = result.find('/',sepPos+1);
11013#endif
11014 QCString prefix = sepPos==-1 ? result : result.left(sepPos);
11015 if (nonSymlinks.find(prefix.str())==nonSymlinks.end())
11016 {
11017 FileInfo fi(prefix.str());
11018 if (fi.isSymLink())
11019 {
11020 QCString target = fi.readLink();
11021 bool isRelative = FileInfo(target.str()).isRelative();
11022 if (isRelative)
11023 {
11024 target = Dir::cleanDirPath(oldPrefix.str()+"/"+target.str());
11025 }
11026 if (sepPos!=-1)
11027 {
11028 if (fi.isDir() && target.length()>0 && target.at(target.length()-1)!='/')
11029 {
11030 target+='/';
11031 }
11032 target+=result.mid(sepPos);
11033 }
11034 result = Dir::cleanDirPath(target.str());
11035 if (known.find(result.str())!=known.end()) return std::string(); // recursive symlink!
11036 known.insert(result.str());
11037 if (isRelative)
11038 {
11039 sepPos = oldPos;
11040 }
11041 else // link to absolute path
11042 {
11043 sepPos = 0;
11044 oldPrefix = "/";
11045 }
11046 }
11047 else
11048 {
11049 nonSymlinks.insert(prefix.str());
11050 oldPrefix = prefix;
11051 }
11052 oldPos = sepPos;
11053 }
11054 }
11055 while (sepPos!=-1);
11056 return Dir::cleanDirPath(result.str());
11057}
11058
11060
11061//----------------------------------------------------------------------------
11062// Read all files matching at least one pattern in 'patList' in the
11063// directory represented by 'fi'.
11064// The directory is read iff the recursiveFlag is set.
11065// The contents of all files is append to the input string
11066
11067static void readDir(FileInfo *fi,
11068 FileNameLinkedMap *fnMap,
11069 StringUnorderedSet *exclSet,
11070 const StringVector *patList,
11071 const StringVector *exclPatList,
11072 StringVector *resultList,
11073 StringUnorderedSet *resultSet,
11074 bool errorIfNotExist,
11075 bool recursive,
11076 StringUnorderedSet *killSet,
11077 StringUnorderedSet *paths
11078 )
11079{
11080 std::string dirName = fi->absFilePath();
11081 if (paths && !dirName.empty())
11082 {
11083 paths->insert(dirName);
11084 }
11085 //printf("%s isSymLink()=%d\n",qPrint(dirName),fi->isSymLink());
11086 if (fi->isSymLink())
11087 {
11088 dirName = resolveSymlink(dirName);
11089 if (dirName.empty())
11090 {
11091 //printf("RECURSIVE SYMLINK: %s\n",qPrint(dirName));
11092 return; // recursive symlink
11093 }
11094 }
11095
11096 if (g_pathsVisited.find(dirName)!=g_pathsVisited.end())
11097 {
11098 //printf("PATH ALREADY VISITED: %s\n",qPrint(dirName));
11099 return; // already visited path
11100 }
11101 g_pathsVisited.insert(dirName);
11102
11103 Dir dir(dirName);
11104 msg("Searching for files in directory {}\n", fi->absFilePath());
11105 //printf("killSet=%p count=%d\n",killSet,killSet ? (int)killSet->count() : -1);
11106
11107 StringVector dirResultList;
11108
11109 for (const auto &dirEntry : dir.iterator())
11110 {
11111 FileInfo cfi(dirEntry.path());
11112 auto checkPatterns = [&]() -> bool
11113 {
11114 return (patList==nullptr || patternMatch(cfi,*patList)) &&
11115 (exclPatList==nullptr || !patternMatch(cfi,*exclPatList)) &&
11116 (killSet==nullptr || killSet->find(cfi.absFilePath())==killSet->end());
11117 };
11118
11119 if (exclSet==nullptr || exclSet->find(cfi.absFilePath())==exclSet->end())
11120 { // file should not be excluded
11121 //printf("killSet->find(%s)\n",qPrint(cfi->absFilePath()));
11122 if (Config_getBool(EXCLUDE_SYMLINKS) && cfi.isSymLink())
11123 {
11124 }
11125 else if (!cfi.exists() || !cfi.isReadable())
11126 {
11127 if (errorIfNotExist && checkPatterns())
11128 {
11129 warn_uncond("source '{}' is not a readable file or directory... skipping.\n",cfi.absFilePath());
11130 }
11131 }
11132 else if (cfi.isFile() && checkPatterns())
11133 {
11134 std::string name=cfi.fileName();
11135 std::string path=cfi.dirPath()+"/";
11136 std::string fullName=path+name;
11137 if (fnMap)
11138 {
11139 auto fd = createFileDef(path,name);
11140 FileName *fn=nullptr;
11141 if (!name.empty())
11142 {
11143 fn = fnMap->add(name,fullName);
11144 fn->push_back(std::move(fd));
11145 }
11146 }
11147 dirResultList.push_back(fullName);
11148 if (resultSet) resultSet->insert(fullName);
11149 if (killSet) killSet->insert(fullName);
11150 }
11151 else if (recursive &&
11152 cfi.isDir() &&
11153 (exclPatList==nullptr || !patternMatch(cfi,*exclPatList)) &&
11154 cfi.fileName().at(0)!='.') // skip "." ".." and ".dir"
11155 {
11156 FileInfo acfi(cfi.absFilePath());
11157 readDir(&acfi,fnMap,exclSet,
11158 patList,exclPatList,&dirResultList,resultSet,errorIfNotExist,
11159 recursive,killSet,paths);
11160 }
11161 }
11162 }
11163 if (resultList && !dirResultList.empty())
11164 {
11165 // sort the resulting list to make the order platform independent.
11166 std::stable_sort(dirResultList.begin(),
11167 dirResultList.end(),
11168 [](const auto &f1,const auto &f2) { return qstricmp_sort(f1.c_str(),f2.c_str())<0; });
11169
11170 // append the sorted results to resultList
11171 resultList->insert(resultList->end(), dirResultList.begin(), dirResultList.end());
11172 }
11173}
11174
11175
11176//----------------------------------------------------------------------------
11177// read a file or all files in a directory and append their contents to the
11178// input string. The names of the files are appended to the 'fiList' list.
11179
11181 FileNameLinkedMap *fnMap,
11182 StringUnorderedSet *exclSet,
11183 const StringVector *patList,
11184 const StringVector *exclPatList,
11185 StringVector *resultList,
11186 StringUnorderedSet *resultSet,
11187 bool recursive,
11188 bool errorIfNotExist,
11189 StringUnorderedSet *killSet,
11190 StringUnorderedSet *paths
11191 )
11192{
11193 //printf("killSet count=%d\n",killSet ? (int)killSet->size() : -1);
11194 // strip trailing slashes
11195 if (s.isEmpty()) return;
11196
11197 g_pathsVisited.clear();
11198
11199 FileInfo fi(s.str());
11200 //printf("readFileOrDirectory(%s)\n",s);
11201 {
11202 if (exclSet==nullptr || exclSet->find(fi.absFilePath())==exclSet->end())
11203 {
11204 if (Config_getBool(EXCLUDE_SYMLINKS) && fi.isSymLink())
11205 {
11206 }
11207 else if (!fi.exists() || !fi.isReadable())
11208 {
11209 if (errorIfNotExist)
11210 {
11211 warn_uncond("source '{}' is not a readable file or directory... skipping.\n",s);
11212 }
11213 }
11214 else if (fi.isFile())
11215 {
11216 std::string dirPath = fi.dirPath(true);
11217 std::string filePath = fi.absFilePath();
11218 if (paths && !dirPath.empty())
11219 {
11220 paths->insert(dirPath);
11221 }
11222 //printf("killSet.find(%s)=%d\n",qPrint(fi.absFilePath()),killSet.find(fi.absFilePath())!=killSet.end());
11223 if (killSet==nullptr || killSet->find(filePath)==killSet->end())
11224 {
11225 std::string name=fi.fileName();
11226 if (fnMap)
11227 {
11228 auto fd = createFileDef(dirPath+"/",name);
11229 if (!name.empty())
11230 {
11231 FileName *fn = fnMap->add(name,filePath);
11232 fn->push_back(std::move(fd));
11233 }
11234 }
11235 if (resultList || resultSet)
11236 {
11237 if (resultList) resultList->push_back(filePath);
11238 if (resultSet) resultSet->insert(filePath);
11239 }
11240
11241 if (killSet) killSet->insert(fi.absFilePath());
11242 }
11243 }
11244 else if (fi.isDir()) // readable dir
11245 {
11246 readDir(&fi,fnMap,exclSet,patList,
11247 exclPatList,resultList,resultSet,errorIfNotExist,
11248 recursive,killSet,paths);
11249 }
11250 }
11251 }
11252}
11253
11254//----------------------------------------------------------------------------
11255
11257{
11258 QCString anchor;
11260 {
11261 MemberDef *md = toMemberDef(d);
11262 anchor=":"+md->anchor();
11263 }
11264 QCString scope;
11265 QCString fn = d->getOutputFileBase();
11268 {
11269 scope = fn;
11270 }
11271 t << "REPLACE INTO symbols (symbol_id,scope_id,name,file,line) VALUES('"
11272 << fn+anchor << "','"
11273 << scope << "','"
11274 << d->name() << "','"
11275 << d->getDefFileName() << "','"
11276 << d->getDefLine()
11277 << "');\n";
11278}
11279
11280static void dumpSymbolMap()
11281{
11282 std::ofstream f = Portable::openOutputStream("symbols.sql");
11283 if (f.is_open())
11284 {
11285 TextStream t(&f);
11286 for (const auto &[name,symList] : *Doxygen::symbolMap)
11287 {
11288 for (const auto &def : symList)
11289 {
11290 dumpSymbol(t,def);
11291 }
11292 }
11293 }
11294}
11295
11296// print developer options of Doxygen
11297static void devUsage()
11298{
11300 msg("Developer parameters:\n");
11301 msg(" -m dump symbol map\n");
11302 msg(" -b making messages output unbuffered\n");
11303 msg(" -c <file> process input file as a comment block and produce HTML output\n");
11304#if ENABLE_TRACING
11305 msg(" -t [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout)\n");
11306 msg(" -t_time [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout),\n"
11307 " and include time and thread information\n");
11308#endif
11309 msg(" -d <level> enable a debug level, such as (multiple invocations of -d are possible):\n");
11311}
11312
11313
11314//----------------------------------------------------------------------------
11315// print the version of Doxygen
11316
11317static void version(const bool extended)
11318{
11320 QCString versionString = getFullVersion();
11321 msg("{}\n",versionString);
11322 if (extended)
11323 {
11324 QCString extVers;
11325 if (!extVers.isEmpty()) extVers+= ", ";
11326 extVers += "sqlite3 ";
11327 extVers += sqlite3_libversion();
11328#if USE_LIBCLANG
11329 if (!extVers.isEmpty()) extVers+= ", ";
11330 extVers += "clang support ";
11331 extVers += CLANG_VERSION_STRING;
11332#endif
11333 if (!extVers.isEmpty())
11334 {
11335 int lastComma = extVers.findRev(',');
11336 if (lastComma != -1) extVers = extVers.replace(lastComma,1," and");
11337 msg(" with {}.\n",extVers);
11338 }
11339 }
11340}
11341
11342//----------------------------------------------------------------------------
11343// print the usage of Doxygen
11344
11345static void usage(const QCString &name,const QCString &versionString)
11346{
11348 msg("Doxygen version {0}\nCopyright Dimitri van Heesch 1997-2025\n\n"
11349 "You can use Doxygen in a number of ways:\n\n"
11350 "1) Use Doxygen to generate a template configuration file*:\n"
11351 " {1} [-s] -g [configName]\n\n"
11352 "2) Use Doxygen to update an old configuration file*:\n"
11353 " {1} [-s] -u [configName]\n\n"
11354 "3) Use Doxygen to generate documentation using an existing "
11355 "configuration file*:\n"
11356 " {1} [configName]\n\n"
11357 "4) Use Doxygen to generate a template file controlling the layout of the\n"
11358 " generated documentation:\n"
11359 " {1} -l [layoutFileName]\n\n"
11360 " In case layoutFileName is omitted DoxygenLayout.xml will be used as filename.\n"
11361 " If - is used for layoutFileName Doxygen will write to standard output.\n\n"
11362 "5) Use Doxygen to generate a template style sheet file for RTF, HTML or Latex.\n"
11363 " RTF: {1} -w rtf styleSheetFile\n"
11364 " HTML: {1} -w html headerFile footerFile styleSheetFile [configFile]\n"
11365 " LaTeX: {1} -w latex headerFile footerFile styleSheetFile [configFile]\n\n"
11366 "6) Use Doxygen to generate a rtf extensions file\n"
11367 " {1} -e rtf extensionsFile\n\n"
11368 " If - is used for extensionsFile Doxygen will write to standard output.\n\n"
11369 "7) Use Doxygen to compare the used configuration file with the template configuration file\n"
11370 " {1} -x [configFile]\n\n"
11371 " Use Doxygen to compare the used configuration file with the template configuration file\n"
11372 " without replacing the environment variables or CMake type replacement variables\n"
11373 " {1} -x_noenv [configFile]\n\n"
11374 "8) Use Doxygen to show a list of built-in emojis.\n"
11375 " {1} -f emoji outputFileName\n\n"
11376 " If - is used for outputFileName Doxygen will write to standard output.\n\n"
11377 "*) If -s is specified the comments of the configuration items in the config file will be omitted.\n"
11378 " If configName is omitted 'Doxyfile' will be used as a default.\n"
11379 " If - is used for configFile Doxygen will write / read the configuration to /from standard output / input.\n\n"
11380 "If -q is used for a Doxygen documentation run, Doxygen will see this as if QUIET=YES has been set.\n\n"
11381 "-v print version string, -V print extended version information\n"
11382 "-h,-? prints usage help information\n"
11383 "{1} -d prints additional usage flags for debugging purposes\n",versionString,name);
11384}
11385
11386//----------------------------------------------------------------------------
11387// read the argument of option 'c' from the comment argument list and
11388// update the option index 'optInd'.
11389
11390static const char *getArg(int argc,char **argv,int &optInd)
11391{
11392 char *s=nullptr;
11393 if (qstrlen(&argv[optInd][2])>0)
11394 s=&argv[optInd][2];
11395 else if (optInd+1<argc && argv[optInd+1][0]!='-')
11396 s=argv[++optInd];
11397 return s;
11398}
11399
11400//----------------------------------------------------------------------------
11401
11402/** @brief /dev/null outline parser */
11404{
11405 public:
11406 void parseInput(const QCString &/* file */, const char * /* buf */,const std::shared_ptr<Entry> &, ClangTUParser*) override {}
11407 bool needsPreprocessing(const QCString &) const override { return FALSE; }
11408 void parsePrototype(const QCString &) override {}
11409};
11410
11411
11412template<class T> std::function< std::unique_ptr<T>() > make_parser_factory()
11413{
11414 return []() { return std::make_unique<T>(); };
11415}
11416
11418{
11419 initResources();
11420 QCString lang = Portable::getenv("LC_ALL");
11421 if (!lang.isEmpty()) Portable::setenv("LANG",lang);
11422 std::setlocale(LC_ALL,"");
11423 std::setlocale(LC_CTYPE,"C"); // to get isspace(0xA0)==0, needed for UTF-8
11424 std::setlocale(LC_NUMERIC,"C");
11425
11427
11451
11452 // register any additional parsers here...
11453
11455
11456#if USE_LIBCLANG
11458#endif
11467 Doxygen::pageLinkedMap = new PageLinkedMap; // all doc pages
11468 Doxygen::exampleLinkedMap = new PageLinkedMap; // all examples
11469 //Doxygen::tagDestinationDict.setAutoDelete(TRUE);
11471
11472 // initialization of these globals depends on
11473 // configuration switches so we need to postpone these
11474 Doxygen::globalScope = nullptr;
11484
11485}
11486
11519
11520void readConfiguration(int argc, char **argv)
11521{
11522 QCString versionString = getFullVersion();
11523
11524 // helper that calls \a func to write to file \a fileName via a TextStream
11525 auto writeFile = [](const char *fileName,std::function<void(TextStream&)> func) -> bool
11526 {
11527 std::ofstream f;
11528 if (openOutputFile(fileName,f))
11529 {
11530 TextStream t(&f);
11531 func(t);
11532 return true;
11533 }
11534 return false;
11535 };
11536
11537
11538 /**************************************************************************
11539 * Handle arguments *
11540 **************************************************************************/
11541
11542 int optInd=1;
11543 QCString configName;
11544 QCString traceName;
11545 bool genConfig=false;
11546 bool shortList=false;
11547 bool traceTiming=false;
11549 bool updateConfig=false;
11550 bool quiet = false;
11551 while (optInd<argc && argv[optInd][0]=='-' &&
11552 (isalpha(argv[optInd][1]) || argv[optInd][1]=='?' ||
11553 argv[optInd][1]=='-')
11554 )
11555 {
11556 switch(argv[optInd][1])
11557 {
11558 case 'g':
11559 {
11560 genConfig=TRUE;
11561 }
11562 break;
11563 case 'l':
11564 {
11565 QCString layoutName;
11566 if (optInd+1>=argc)
11567 {
11568 layoutName="DoxygenLayout.xml";
11569 }
11570 else
11571 {
11572 layoutName=argv[optInd+1];
11573 }
11574 writeDefaultLayoutFile(layoutName);
11576 exit(0);
11577 }
11578 break;
11579 case 'c':
11580 if (optInd+1>=argc) // no file name given
11581 {
11582 msg("option \"-c\" is missing the file name to read\n");
11583 devUsage();
11585 exit(1);
11586 }
11587 else
11588 {
11589 g_commentFileName=argv[optInd+1];
11590 optInd++;
11591 }
11592 g_singleComment=true;
11593 quiet=true;
11594 break;
11595 case 'd':
11596 {
11597 QCString debugLabel=getArg(argc,argv,optInd);
11598 if (debugLabel.isEmpty())
11599 {
11600 devUsage();
11602 exit(0);
11603 }
11604 int retVal = Debug::setFlagStr(debugLabel);
11605 if (!retVal)
11606 {
11607 msg("option \"-d\" has unknown debug specifier: \"{}\".\n",debugLabel);
11608 devUsage();
11610 exit(1);
11611 }
11612 }
11613 break;
11614 case 't':
11615 {
11616#if ENABLE_TRACING
11617 if (!strcmp(argv[optInd]+1,"t_time"))
11618 {
11619 traceTiming = true;
11620 }
11621 else if (!strcmp(argv[optInd]+1,"t"))
11622 {
11623 traceTiming = false;
11624 }
11625 else
11626 {
11627 err("option should be \"-t\" or \"-t_time\", found: \"{}\".\n",argv[optInd]);
11629 exit(1);
11630 }
11631 if (optInd+1>=argc || argv[optInd+1][0] == '-') // no file name given
11632 {
11633 traceName="stdout";
11634 }
11635 else
11636 {
11637 traceName=argv[optInd+1];
11638 optInd++;
11639 }
11640#else
11641 err("support for option \"-t\" has not been compiled in (use a debug build or a release build with tracing enabled).\n");
11643 exit(1);
11644#endif
11645 }
11646 break;
11647 case 'x':
11648 if (!strcmp(argv[optInd]+1,"x_noenv")) diffList=Config::CompareMode::CompressedNoEnv;
11649 else if (!strcmp(argv[optInd]+1,"x")) diffList=Config::CompareMode::Compressed;
11650 else
11651 {
11652 err("option should be \"-x\" or \"-x_noenv\", found: \"{}\".\n",argv[optInd]);
11654 exit(1);
11655 }
11656 break;
11657 case 's':
11658 shortList=TRUE;
11659 break;
11660 case 'u':
11661 updateConfig=TRUE;
11662 break;
11663 case 'e':
11664 {
11665 QCString formatName=getArg(argc,argv,optInd);
11666 if (formatName.isEmpty())
11667 {
11668 err("option \"-e\" is missing format specifier rtf.\n");
11670 exit(1);
11671 }
11672 if (qstricmp(formatName.data(),"rtf")==0)
11673 {
11674 if (optInd+1>=argc)
11675 {
11676 err("option \"-e rtf\" is missing an extensions file name\n");
11678 exit(1);
11679 }
11680 writeFile(argv[optInd+1],RTFGenerator::writeExtensionsFile);
11682 exit(0);
11683 }
11684 err("option \"-e\" has invalid format specifier.\n");
11686 exit(1);
11687 }
11688 break;
11689 case 'f':
11690 {
11691 QCString listName=getArg(argc,argv,optInd);
11692 if (listName.isEmpty())
11693 {
11694 err("option \"-f\" is missing list specifier.\n");
11696 exit(1);
11697 }
11698 if (qstricmp(listName.data(),"emoji")==0)
11699 {
11700 if (optInd+1>=argc)
11701 {
11702 err("option \"-f emoji\" is missing an output file name\n");
11704 exit(1);
11705 }
11706 writeFile(argv[optInd+1],[](TextStream &t) { EmojiEntityMapper::instance().writeEmojiFile(t); });
11708 exit(0);
11709 }
11710 err("option \"-f\" has invalid list specifier.\n");
11712 exit(1);
11713 }
11714 break;
11715 case 'w':
11716 {
11717 QCString formatName=getArg(argc,argv,optInd);
11718 if (formatName.isEmpty())
11719 {
11720 err("option \"-w\" is missing format specifier rtf, html or latex\n");
11722 exit(1);
11723 }
11724 if (qstricmp(formatName.data(),"rtf")==0)
11725 {
11726 if (optInd+1>=argc)
11727 {
11728 err("option \"-w rtf\" is missing a style sheet file name\n");
11730 exit(1);
11731 }
11732 if (!writeFile(argv[optInd+1],RTFGenerator::writeStyleSheetFile))
11733 {
11734 err("error opening RTF style sheet file {}!\n",argv[optInd+1]);
11736 exit(1);
11737 }
11739 exit(0);
11740 }
11741 else if (qstricmp(formatName.data(),"html")==0)
11742 {
11743 Config::init();
11744 if (optInd+4<argc || FileInfo("Doxyfile").exists() || FileInfo("doxyfile").exists())
11745 // explicit config file mentioned or default found on disk
11746 {
11747 QCString df = optInd+4<argc ? argv[optInd+4] : (FileInfo("Doxyfile").exists() ? QCString("Doxyfile") : QCString("doxyfile"));
11748 if (!Config::parse(df)) // parse the config file
11749 {
11750 err("error opening or reading configuration file {}!\n",argv[optInd+4]);
11752 exit(1);
11753 }
11754 }
11755 if (optInd+3>=argc)
11756 {
11757 err("option \"-w html\" does not have enough arguments\n");
11759 exit(1);
11760 }
11764 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11765 writeFile(argv[optInd+1],[&](TextStream &t) { HtmlGenerator::writeHeaderFile(t,argv[optInd+3]); });
11766 writeFile(argv[optInd+2],HtmlGenerator::writeFooterFile);
11767 writeFile(argv[optInd+3],HtmlGenerator::writeStyleSheetFile);
11769 exit(0);
11770 }
11771 else if (qstricmp(formatName.data(),"latex")==0)
11772 {
11773 Config::init();
11774 if (optInd+4<argc || FileInfo("Doxyfile").exists() || FileInfo("doxyfile").exists())
11775 {
11776 QCString df = optInd+4<argc ? argv[optInd+4] : (FileInfo("Doxyfile").exists() ? QCString("Doxyfile") : QCString("doxyfile"));
11777 if (!Config::parse(df))
11778 {
11779 err("error opening or reading configuration file {}!\n",argv[optInd+4]);
11781 exit(1);
11782 }
11783 }
11784 if (optInd+3>=argc)
11785 {
11786 err("option \"-w latex\" does not have enough arguments\n");
11788 exit(1);
11789 }
11793 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11794 writeFile(argv[optInd+1],LatexGenerator::writeHeaderFile);
11795 writeFile(argv[optInd+2],LatexGenerator::writeFooterFile);
11796 writeFile(argv[optInd+3],LatexGenerator::writeStyleSheetFile);
11798 exit(0);
11799 }
11800 else
11801 {
11802 err("Illegal format specifier \"{}\": should be one of rtf, html or latex\n",formatName);
11804 exit(1);
11805 }
11806 }
11807 break;
11808 case 'm':
11810 break;
11811 case 'v':
11812 version(false);
11814 exit(0);
11815 break;
11816 case 'V':
11817 version(true);
11819 exit(0);
11820 break;
11821 case '-':
11822 if (qstrcmp(&argv[optInd][2],"help")==0)
11823 {
11824 usage(argv[0],versionString);
11825 exit(0);
11826 }
11827 else if (qstrcmp(&argv[optInd][2],"version")==0)
11828 {
11829 version(false);
11831 exit(0);
11832 }
11833 else if ((qstrcmp(&argv[optInd][2],"Version")==0) ||
11834 (qstrcmp(&argv[optInd][2],"VERSION")==0))
11835 {
11836 version(true);
11838 exit(0);
11839 }
11840 else
11841 {
11842 err("Unknown option \"-{}\"\n",&argv[optInd][1]);
11843 usage(argv[0],versionString);
11844 exit(1);
11845 }
11846 break;
11847 case 'b':
11848 setvbuf(stdout,nullptr,_IONBF,0);
11849 break;
11850 case 'q':
11851 quiet = true;
11852 break;
11853 case 'h':
11854 case '?':
11855 usage(argv[0],versionString);
11856 exit(0);
11857 break;
11858 default:
11859 err("Unknown option \"-{:c}\"\n",argv[optInd][1]);
11860 usage(argv[0],versionString);
11861 exit(1);
11862 }
11863 optInd++;
11864 }
11865
11866 /**************************************************************************
11867 * Parse or generate the config file *
11868 **************************************************************************/
11869
11870 initTracing(traceName.data(),traceTiming);
11871 TRACE("Doxygen version used: {}",getFullVersion());
11872 Config::init();
11873
11874 FileInfo configFileInfo1("Doxyfile"),configFileInfo2("doxyfile");
11875 if (optInd>=argc)
11876 {
11877 if (configFileInfo1.exists())
11878 {
11879 configName="Doxyfile";
11880 }
11881 else if (configFileInfo2.exists())
11882 {
11883 configName="doxyfile";
11884 }
11885 else if (genConfig)
11886 {
11887 configName="Doxyfile";
11888 }
11889 else
11890 {
11891 err("Doxyfile not found and no input file specified!\n");
11892 usage(argv[0],versionString);
11893 exit(1);
11894 }
11895 }
11896 else
11897 {
11898 FileInfo fi(argv[optInd]);
11899 if (fi.exists() || qstrcmp(argv[optInd],"-")==0 || genConfig)
11900 {
11901 configName=argv[optInd];
11902 }
11903 else
11904 {
11905 err("configuration file {} not found!\n",argv[optInd]);
11906 usage(argv[0],versionString);
11907 exit(1);
11908 }
11909 }
11910
11911 if (genConfig)
11912 {
11913 generateConfigFile(configName,shortList);
11915 exit(0);
11916 }
11917
11918 if (!Config::parse(configName,updateConfig,diffList))
11919 {
11920 err("could not open or read configuration file {}!\n",configName);
11922 exit(1);
11923 }
11924
11925 if (diffList!=Config::CompareMode::Full)
11926 {
11928 compareDoxyfile(diffList);
11930 exit(0);
11931 }
11932
11933 if (updateConfig)
11934 {
11936 generateConfigFile(configName,shortList,TRUE);
11938 exit(0);
11939 }
11940
11941 /* Perlmod wants to know the path to the config file.*/
11942 FileInfo configFileInfo(configName.str());
11943 setPerlModDoxyfile(configFileInfo.absFilePath());
11944
11945 /* handle -q option */
11946 if (quiet) Config_updateBool(QUIET,TRUE);
11947}
11948
11949/** check and resolve config options */
11951{
11952 AUTO_TRACE();
11953
11958}
11959
11960/** adjust globals that depend on configuration settings. */
11962{
11963 AUTO_TRACE();
11964 Doxygen::globalNamespaceDef = createNamespaceDef("<globalScope>",1,1,"<globalScope>");
11975
11976 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11977
11978 /* Set the global html file extension. */
11979 Doxygen::htmlFileExtension = Config_getString(HTML_FILE_EXTENSION);
11980
11981
11983 Config_getBool(CALLER_GRAPH) ||
11984 Config_getBool(REFERENCES_RELATION) ||
11985 Config_getBool(REFERENCED_BY_RELATION);
11986
11987 /**************************************************************************
11988 * Add custom extension mappings
11989 **************************************************************************/
11990
11991 const StringVector &extMaps = Config_getList(EXTENSION_MAPPING);
11992 for (const auto &mapping : extMaps)
11993 {
11994 QCString mapStr = mapping;
11995 int i=mapStr.find('=');
11996 if (i==-1)
11997 {
11998 continue;
11999 }
12000 else
12001 {
12002 QCString ext = mapStr.left(i).stripWhiteSpace().lower();
12003 QCString language = mapStr.mid(i+1).stripWhiteSpace().lower();
12004 if (ext.isEmpty() || language.isEmpty())
12005 {
12006 continue;
12007 }
12008
12009 if (!updateLanguageMapping(ext,language))
12010 {
12011 err("Failed to map file extension '{}' to unsupported language '{}'.\n"
12012 "Check the EXTENSION_MAPPING setting in the config file.\n",
12013 ext,language);
12014 }
12015 else
12016 {
12017 msg("Adding custom extension mapping: '{}' will be treated as language '{}'\n",
12018 ext,language);
12019 }
12020 }
12021 }
12022 // create input file exncodings
12023
12024 // check INPUT_ENCODING
12025 void *cd = portable_iconv_open("UTF-8",Config_getString(INPUT_ENCODING).data());
12026 if (cd==reinterpret_cast<void *>(-1))
12027 {
12028 term("unsupported character conversion: '{}'->'UTF-8': {}\n"
12029 "Check the 'INPUT_ENCODING' setting in the config file!\n",
12030 Config_getString(INPUT_ENCODING),strerror(errno));
12031 }
12032 else
12033 {
12035 }
12036
12037 // check and split INPUT_FILE_ENCODING
12038 const StringVector &fileEncod = Config_getList(INPUT_FILE_ENCODING);
12039 for (const auto &mapping : fileEncod)
12040 {
12041 QCString mapStr = mapping;
12042 int i=mapStr.find('=');
12043 if (i==-1)
12044 {
12045 continue;
12046 }
12047 else
12048 {
12049 QCString pattern = mapStr.left(i).stripWhiteSpace().lower();
12050 QCString encoding = mapStr.mid(i+1).stripWhiteSpace().lower();
12051 if (pattern.isEmpty() || encoding.isEmpty())
12052 {
12053 continue;
12054 }
12055 cd = portable_iconv_open("UTF-8",encoding.data());
12056 if (cd==reinterpret_cast<void *>(-1))
12057 {
12058 term("unsupported character conversion: '{}'->'UTF-8': {}\n"
12059 "Check the 'INPUT_FILE_ENCODING' setting in the config file!\n",
12060 encoding,strerror(errno));
12061 }
12062 else
12063 {
12065 }
12066
12067 Doxygen::inputFileEncodingList.emplace_back(pattern, encoding);
12068 }
12069 }
12070
12071 // add predefined macro name to a dictionary
12072 const StringVector &expandAsDefinedList =Config_getList(EXPAND_AS_DEFINED);
12073 for (const auto &s : expandAsDefinedList)
12074 {
12076 }
12077
12078 // read aliases and store them in a dictionary
12079 readAliases();
12080
12081 // store number of spaces in a tab into Doxygen::spaces
12082 int tabSize = Config_getInt(TAB_SIZE);
12083 Doxygen::spaces.resize(tabSize);
12084 for (int sp=0; sp<tabSize; sp++) Doxygen::spaces.at(sp)=' ';
12085 Doxygen::spaces.at(tabSize)='\0';
12086}
12087
12088#ifdef HAS_SIGNALS
12089static void stopDoxygen(int)
12090{
12091 signal(SIGINT,SIG_DFL); // Re-register signal handler for default action
12092 Dir thisDir;
12093 msg("Cleaning up...\n");
12094 if (!Doxygen::filterDBFileName.isEmpty())
12095 {
12096 thisDir.remove(Doxygen::filterDBFileName.str());
12097 }
12098 killpg(0,SIGINT);
12100 exitTracing();
12101 exit(1);
12102}
12103#endif
12104
12105static void writeTagFile()
12106{
12107 QCString generateTagFile = Config_getString(GENERATE_TAGFILE);
12108 if (generateTagFile.isEmpty()) return;
12109
12110 std::ofstream f = Portable::openOutputStream(generateTagFile);
12111 if (!f.is_open())
12112 {
12113 err("cannot open tag file {} for writing\n", generateTagFile);
12114 return;
12115 }
12116 TextStream tagFile(&f);
12117 tagFile << "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>\n";
12118 tagFile << "<tagfile doxygen_version=\"" << getDoxygenVersion() << "\"";
12119 std::string gitVersion = getGitVersion();
12120 if (!gitVersion.empty())
12121 {
12122 tagFile << " doxygen_gitid=\"" << gitVersion << "\"";
12123 }
12124 tagFile << ">\n";
12125
12126 // for each file
12127 for (const auto &fn : *Doxygen::inputNameLinkedMap)
12128 {
12129 for (const auto &fd : *fn)
12130 {
12131 if (fd->isLinkableInProject()) fd->writeTagFile(tagFile);
12132 }
12133 }
12134 // for each class
12135 for (const auto &cd : *Doxygen::classLinkedMap)
12136 {
12137 ClassDefMutable *cdm = toClassDefMutable(cd.get());
12138 if (cdm && cdm->isLinkableInProject())
12139 {
12140 cdm->writeTagFile(tagFile);
12141 }
12142 }
12143 // for each concept
12144 for (const auto &cd : *Doxygen::conceptLinkedMap)
12145 {
12146 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
12147 if (cdm && cdm->isLinkableInProject())
12148 {
12149 cdm->writeTagFile(tagFile);
12150 }
12151 }
12152 // for each namespace
12153 for (const auto &nd : *Doxygen::namespaceLinkedMap)
12154 {
12156 if (ndm && nd->isLinkableInProject())
12157 {
12158 ndm->writeTagFile(tagFile);
12159 }
12160 }
12161 // for each group
12162 for (const auto &gd : *Doxygen::groupLinkedMap)
12163 {
12164 if (gd->isLinkableInProject()) gd->writeTagFile(tagFile);
12165 }
12166 // for each module
12167 for (const auto &mod : ModuleManager::instance().modules())
12168 {
12169 if (mod->isLinkableInProject()) mod->writeTagFile(tagFile);
12170 }
12171 // for each page
12172 for (const auto &pd : *Doxygen::pageLinkedMap)
12173 {
12174 if (pd->isLinkableInProject()) pd->writeTagFile(tagFile);
12175 }
12176 // for requirements
12178 // for each directory
12179 for (const auto &dd : *Doxygen::dirLinkedMap)
12180 {
12181 if (dd->isLinkableInProject()) dd->writeTagFile(tagFile);
12182 }
12183 if (Doxygen::mainPage) Doxygen::mainPage->writeTagFile(tagFile);
12184
12185 tagFile << "</tagfile>\n";
12186}
12187
12188static void exitDoxygen() noexcept
12189{
12190 if (!g_successfulRun) // premature exit
12191 {
12192 Dir thisDir;
12193 msg("Exiting...\n");
12194 if (!Doxygen::filterDBFileName.isEmpty())
12195 {
12196 thisDir.remove(Doxygen::filterDBFileName.str());
12197 }
12198 }
12199}
12200
12201static QCString createOutputDirectory(const QCString &baseDirName,
12202 const QCString &formatDirName,
12203 const char *defaultDirName)
12204{
12205 QCString result = formatDirName;
12206 if (result.isEmpty())
12207 {
12208 result = baseDirName + defaultDirName;
12209 }
12210 else if (formatDirName[0]!='/' && (formatDirName.length()==1 || formatDirName[1]!=':'))
12211 {
12212 result.prepend(baseDirName+"/");
12213 }
12214 Dir formatDir(result.str());
12215 if (!formatDir.exists() && !formatDir.mkdir(result.str()))
12216 {
12217 term("Could not create output directory {}\n", result);
12218 }
12219 return result;
12220}
12221
12223{
12224 StringUnorderedSet killSet;
12225
12226 const StringVector &exclPatterns = Config_getList(EXCLUDE_PATTERNS);
12227 bool alwaysRecursive = Config_getBool(RECURSIVE);
12228 StringUnorderedSet excludeNameSet;
12229
12230 // gather names of all files in the include path
12231 g_s.begin("Searching for include files...\n");
12232 killSet.clear();
12233 const StringVector &includePathList = Config_getList(INCLUDE_PATH);
12234 for (const auto &s : includePathList)
12235 {
12236 size_t plSize = Config_getList(INCLUDE_FILE_PATTERNS).size();
12237 const StringVector &pl = plSize==0 ? Config_getList(FILE_PATTERNS) :
12238 Config_getList(INCLUDE_FILE_PATTERNS);
12239 readFileOrDirectory(s, // s
12241 nullptr, // exclSet
12242 &pl, // patList
12243 &exclPatterns, // exclPatList
12244 nullptr, // resultList
12245 nullptr, // resultSet
12246 false, // INCLUDE_PATH isn't recursive
12247 TRUE, // errorIfNotExist
12248 &killSet); // killSet
12249 }
12250 g_s.end();
12251
12252 g_s.begin("Searching for example files...\n");
12253 killSet.clear();
12254 const StringVector &examplePathList = Config_getList(EXAMPLE_PATH);
12255 for (const auto &s : examplePathList)
12256 {
12257 readFileOrDirectory(s, // s
12259 nullptr, // exclSet
12260 &Config_getList(EXAMPLE_PATTERNS), // patList
12261 nullptr, // exclPatList
12262 nullptr, // resultList
12263 nullptr, // resultSet
12264 (alwaysRecursive || Config_getBool(EXAMPLE_RECURSIVE)), // recursive
12265 TRUE, // errorIfNotExist
12266 &killSet); // killSet
12267 }
12268 g_s.end();
12269
12270 g_s.begin("Searching for images...\n");
12271 killSet.clear();
12272 const StringVector &imagePathList=Config_getList(IMAGE_PATH);
12273 for (const auto &s : imagePathList)
12274 {
12275 readFileOrDirectory(s, // s
12277 nullptr, // exclSet
12278 nullptr, // patList
12279 nullptr, // exclPatList
12280 nullptr, // resultList
12281 nullptr, // resultSet
12282 alwaysRecursive, // recursive
12283 TRUE, // errorIfNotExist
12284 &killSet); // killSet
12285 }
12286 g_s.end();
12287
12288 g_s.begin("Searching for dot files...\n");
12289 killSet.clear();
12290 const StringVector &dotFileList=Config_getList(DOTFILE_DIRS);
12291 for (const auto &s : dotFileList)
12292 {
12293 readFileOrDirectory(s, // s
12295 nullptr, // exclSet
12296 nullptr, // patList
12297 nullptr, // exclPatList
12298 nullptr, // resultList
12299 nullptr, // resultSet
12300 alwaysRecursive, // recursive
12301 TRUE, // errorIfNotExist
12302 &killSet); // killSet
12303 }
12304 g_s.end();
12305
12306 g_s.begin("Searching for msc files...\n");
12307 killSet.clear();
12308 const StringVector &mscFileList=Config_getList(MSCFILE_DIRS);
12309 for (const auto &s : mscFileList)
12310 {
12311 readFileOrDirectory(s, // s
12313 nullptr, // exclSet
12314 nullptr, // patList
12315 nullptr, // exclPatList
12316 nullptr, // resultList
12317 nullptr, // resultSet
12318 alwaysRecursive, // recursive
12319 TRUE, // errorIfNotExist
12320 &killSet); // killSet
12321 }
12322 g_s.end();
12323
12324 g_s.begin("Searching for dia files...\n");
12325 killSet.clear();
12326 const StringVector &diaFileList=Config_getList(DIAFILE_DIRS);
12327 for (const auto &s : diaFileList)
12328 {
12329 readFileOrDirectory(s, // s
12331 nullptr, // exclSet
12332 nullptr, // patList
12333 nullptr, // exclPatList
12334 nullptr, // resultList
12335 nullptr, // resultSet
12336 alwaysRecursive, // recursive
12337 TRUE, // errorIfNotExist
12338 &killSet); // killSet
12339 }
12340 g_s.end();
12341
12342 g_s.begin("Searching for plantuml files...\n");
12343 killSet.clear();
12344 const StringVector &plantUmlFileList=Config_getList(PLANTUMLFILE_DIRS);
12345 for (const auto &s : plantUmlFileList)
12346 {
12347 readFileOrDirectory(s, // s
12349 nullptr, // exclSet
12350 nullptr, // patList
12351 nullptr, // exclPatList
12352 nullptr, // resultList
12353 nullptr, // resultSet
12354 alwaysRecursive, // recursive
12355 TRUE, // errorIfNotExist
12356 &killSet); // killSet
12357 }
12358 g_s.end();
12359
12360 g_s.begin("Searching for mermaid files...\n");
12361 killSet.clear();
12362 const StringVector &mermaidFileList=Config_getList(MERMAIDFILE_DIRS);
12363 for (const auto &s : mermaidFileList)
12364 {
12365 readFileOrDirectory(s, // s
12367 nullptr, // exclSet
12368 nullptr, // patList
12369 nullptr, // exclPatList
12370 nullptr, // resultList
12371 nullptr, // resultSet
12372 alwaysRecursive, // recursive
12373 TRUE, // errorIfNotExist
12374 &killSet); // killSet
12375 }
12376 g_s.end();
12377
12378 g_s.begin("Searching for files to exclude\n");
12379 const StringVector &excludeList = Config_getList(EXCLUDE);
12380 for (const auto &s : excludeList)
12381 {
12382 readFileOrDirectory(s, // s
12383 nullptr, // fnDict
12384 nullptr, // exclSet
12385 &Config_getList(FILE_PATTERNS), // patList
12386 nullptr, // exclPatList
12387 nullptr, // resultList
12388 &excludeNameSet, // resultSet
12389 alwaysRecursive, // recursive
12390 FALSE); // errorIfNotExist
12391 }
12392 g_s.end();
12393
12394 /**************************************************************************
12395 * Determine Input Files *
12396 **************************************************************************/
12397
12398 g_s.begin("Searching INPUT for files to process...\n");
12399 killSet.clear();
12400 Doxygen::inputPaths.clear();
12401 const StringVector &inputList=Config_getList(INPUT);
12402 for (const auto &s : inputList)
12403 {
12404 QCString path = s;
12405 size_t l = path.length();
12406 if (l>0)
12407 {
12408 // strip trailing slashes
12409 if (path.at(l-1)=='\\' || path.at(l-1)=='/') path=path.left(l-1);
12410
12412 path, // s
12414 &excludeNameSet, // exclSet
12415 &Config_getList(FILE_PATTERNS), // patList
12416 &exclPatterns, // exclPatList
12417 &g_inputFiles, // resultList
12418 nullptr, // resultSet
12419 alwaysRecursive, // recursive
12420 TRUE, // errorIfNotExist
12421 &killSet, // killSet
12422 &Doxygen::inputPaths); // paths
12423 }
12424 }
12425
12426 // Sort the FileDef objects by full path to get a predictable ordering over multiple runs
12427 std::stable_sort(Doxygen::inputNameLinkedMap->begin(),
12429 [](const auto &f1,const auto &f2)
12430 {
12431 return qstricmp_sort(f1->fullName(),f2->fullName())<0;
12432 });
12433 for (auto &fileName : *Doxygen::inputNameLinkedMap)
12434 {
12435 if (fileName->size()>1)
12436 {
12437 std::stable_sort(fileName->begin(),fileName->end(),[](const auto &f1,const auto &f2)
12438 {
12439 return qstricmp_sort(f1->absFilePath(),f2->absFilePath())<0;
12440 });
12441 }
12442 }
12443 if (Doxygen::inputNameLinkedMap->empty())
12444 {
12445 warn_uncond("No files to be processed, please check your settings, in particular INPUT, FILE_PATTERNS, and RECURSIVE\n");
12446 }
12447 g_s.end();
12448}
12449
12450
12452{
12453 if (Config_getBool(MARKDOWN_SUPPORT))
12454 {
12455 QCString mdfileAsMainPage = Config_getString(USE_MDFILE_AS_MAINPAGE);
12456 if (mdfileAsMainPage.isEmpty()) return;
12457 FileInfo fi(mdfileAsMainPage.data());
12458 if (!fi.exists())
12459 {
12460 warn_uncond("Specified markdown mainpage '{}' does not exist\n",mdfileAsMainPage);
12461 return;
12462 }
12463 bool ambig = false;
12464 if (findFileDef(Doxygen::inputNameLinkedMap,fi.absFilePath(),ambig)==nullptr)
12465 {
12466 warn_uncond("Specified markdown mainpage '{}' has not been defined as input file\n",mdfileAsMainPage);
12467 return;
12468 }
12469 }
12470}
12471
12473{
12474 AUTO_TRACE();
12475 std::atexit(exitDoxygen);
12476
12477 Portable::correctPath(Config_getList(EXTERNAL_TOOL_PATH));
12478
12479#if USE_LIBCLANG
12480 Doxygen::clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING);
12481#endif
12482
12483 // we would like to show the versionString earlier, but we first have to handle the configuration file
12484 // to know the value of the QUIET setting.
12485 QCString versionString = getFullVersion();
12486 msg("Doxygen version used: {}\n",versionString);
12487
12489
12490 /**************************************************************************
12491 * Make sure the output directory exists
12492 **************************************************************************/
12493 QCString outputDirectory = Config_getString(OUTPUT_DIRECTORY);
12494 if (!g_singleComment)
12495 {
12496 if (outputDirectory.isEmpty())
12497 {
12498 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,Dir::currentDirPath());
12499 }
12500 else
12501 {
12502 Dir dir(outputDirectory.str());
12503 if (!dir.exists())
12504 {
12506 if (!dir.mkdir(outputDirectory.str()))
12507 {
12508 term("tag OUTPUT_DIRECTORY: Output directory '{}' does not "
12509 "exist and cannot be created\n",outputDirectory);
12510 }
12511 else
12512 {
12513 msg("Notice: Output directory '{}' does not exist. "
12514 "I have created it for you.\n", outputDirectory);
12515 }
12516 dir.setPath(outputDirectory.str());
12517 }
12518 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,dir.absPath());
12519 }
12520 }
12521 AUTO_TRACE_ADD("outputDirectory={}",outputDirectory);
12522
12523 /**************************************************************************
12524 * Initialize global lists and dictionaries
12525 **************************************************************************/
12526
12527#ifdef HAS_SIGNALS
12528 signal(SIGINT, stopDoxygen);
12529#endif
12530
12531 uint32_t pid = Portable::pid();
12532 Doxygen::filterDBFileName.sprintf("doxygen_filterdb_%d.tmp",pid);
12533 Doxygen::filterDBFileName.prepend(outputDirectory+"/");
12534
12535 /**************************************************************************
12536 * Check/create output directories *
12537 **************************************************************************/
12538
12539 bool generateHtml = Config_getBool(GENERATE_HTML);
12540 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
12541 bool generateXml = Config_getBool(GENERATE_XML);
12542 bool generateLatex = Config_getBool(GENERATE_LATEX);
12543 bool generateRtf = Config_getBool(GENERATE_RTF);
12544 bool generateMan = Config_getBool(GENERATE_MAN);
12545 bool generateSql = Config_getBool(GENERATE_SQLITE3);
12546 QCString htmlOutput;
12547 QCString docbookOutput;
12548 QCString xmlOutput;
12549 QCString latexOutput;
12550 QCString rtfOutput;
12551 QCString manOutput;
12552 QCString sqlOutput;
12553
12554 if (!g_singleComment)
12555 {
12556 if (generateHtml)
12557 {
12558 htmlOutput = createOutputDirectory(outputDirectory,Config_getString(HTML_OUTPUT),"/html");
12559 Config_updateString(HTML_OUTPUT,htmlOutput);
12560
12561 QCString sitemapUrl = Config_getString(SITEMAP_URL);
12562 bool generateSitemap = !sitemapUrl.isEmpty();
12563 if (generateSitemap && !sitemapUrl.endsWith("/"))
12564 {
12565 Config_updateString(SITEMAP_URL,sitemapUrl+"/");
12566 }
12567
12568 // add HTML indexers that are enabled
12569 bool generateHtmlHelp = Config_getBool(GENERATE_HTMLHELP);
12570 bool generateEclipseHelp = Config_getBool(GENERATE_ECLIPSEHELP);
12571 bool generateQhp = Config_getBool(GENERATE_QHP);
12572 bool generateTreeView = Config_getBool(GENERATE_TREEVIEW);
12573 bool generateDocSet = Config_getBool(GENERATE_DOCSET);
12574 if (generateEclipseHelp) Doxygen::indexList->addIndex<EclipseHelp>();
12575 if (generateHtmlHelp) Doxygen::indexList->addIndex<HtmlHelp>();
12576 if (generateQhp) Doxygen::indexList->addIndex<Qhp>();
12577 if (generateSitemap) Doxygen::indexList->addIndex<Sitemap>();
12578 if (generateTreeView) Doxygen::indexList->addIndex<FTVHelp>(TRUE);
12579 if (generateDocSet) Doxygen::indexList->addIndex<DocSets>();
12580 Doxygen::indexList->addIndex<Crawlmap>();
12581 Doxygen::indexList->initialize();
12582 }
12583
12584 if (generateDocbook)
12585 {
12586 docbookOutput = createOutputDirectory(outputDirectory,Config_getString(DOCBOOK_OUTPUT),"/docbook");
12587 Config_updateString(DOCBOOK_OUTPUT,docbookOutput);
12588 }
12589
12590 if (generateXml)
12591 {
12592 xmlOutput = createOutputDirectory(outputDirectory,Config_getString(XML_OUTPUT),"/xml");
12593 Config_updateString(XML_OUTPUT,xmlOutput);
12594 }
12595
12596 if (generateLatex)
12597 {
12598 latexOutput = createOutputDirectory(outputDirectory,Config_getString(LATEX_OUTPUT), "/latex");
12599 Config_updateString(LATEX_OUTPUT,latexOutput);
12600 }
12601
12602 if (generateRtf)
12603 {
12604 rtfOutput = createOutputDirectory(outputDirectory,Config_getString(RTF_OUTPUT),"/rtf");
12605 Config_updateString(RTF_OUTPUT,rtfOutput);
12606 }
12607
12608 if (generateMan)
12609 {
12610 manOutput = createOutputDirectory(outputDirectory,Config_getString(MAN_OUTPUT),"/man");
12611 Config_updateString(MAN_OUTPUT,manOutput);
12612 }
12613
12614 if (generateSql)
12615 {
12616 sqlOutput = createOutputDirectory(outputDirectory,Config_getString(SQLITE3_OUTPUT),"/sqlite3");
12617 Config_updateString(SQLITE3_OUTPUT,sqlOutput);
12618 }
12619 }
12620
12621 if (Config_getBool(HAVE_DOT))
12622 {
12623 QCString curFontPath = Config_getString(DOT_FONTPATH);
12624 if (curFontPath.isEmpty())
12625 {
12626 Portable::getenv("DOTFONTPATH");
12627 QCString newFontPath = ".";
12628 if (!curFontPath.isEmpty())
12629 {
12630 newFontPath+=Portable::pathListSeparator();
12631 newFontPath+=curFontPath;
12632 }
12633 Portable::setenv("DOTFONTPATH",qPrint(newFontPath));
12634 }
12635 else
12636 {
12637 Portable::setenv("DOTFONTPATH",qPrint(curFontPath));
12638 }
12639 }
12640
12641 /**************************************************************************
12642 * Handle layout file *
12643 **************************************************************************/
12644
12646 QCString layoutFileName = Config_getString(LAYOUT_FILE);
12647 bool defaultLayoutUsed = FALSE;
12648 if (layoutFileName.isEmpty())
12649 {
12650 layoutFileName = Config_updateString(LAYOUT_FILE,"DoxygenLayout.xml");
12651 defaultLayoutUsed = TRUE;
12652 }
12653 AUTO_TRACE_ADD("defaultLayoutUsed={}, layoutFileName={}",defaultLayoutUsed,layoutFileName);
12654
12655 FileInfo fi(layoutFileName.str());
12656 if (fi.exists())
12657 {
12658 msg("Parsing layout file {}...\n",layoutFileName);
12659 LayoutDocManager::instance().parse(layoutFileName);
12660 }
12661 else if (!defaultLayoutUsed)
12662 {
12663 warn_uncond("failed to open layout file '{}' for reading! Using default settings.\n",layoutFileName);
12664 }
12665 printLayout();
12666
12667 /**************************************************************************
12668 * Read and preprocess input *
12669 **************************************************************************/
12670
12671 // prevent search in the output directories
12672 StringVector exclPatterns = Config_getList(EXCLUDE_PATTERNS);
12673 if (generateHtml) exclPatterns.push_back(htmlOutput.str());
12674 if (generateDocbook) exclPatterns.push_back(docbookOutput.str());
12675 if (generateXml) exclPatterns.push_back(xmlOutput.str());
12676 if (generateLatex) exclPatterns.push_back(latexOutput.str());
12677 if (generateRtf) exclPatterns.push_back(rtfOutput.str());
12678 if (generateMan) exclPatterns.push_back(manOutput.str());
12679 Config_updateList(EXCLUDE_PATTERNS,exclPatterns);
12680
12681 if (!g_singleComment)
12682 {
12684
12686 }
12687
12688 // Notice: the order of the function calls below is very important!
12689
12690 if (generateHtml && !Config_getBool(USE_MATHJAX))
12691 {
12693 }
12694 if (generateRtf)
12695 {
12697 }
12698 if (generateDocbook)
12699 {
12701 }
12702
12704
12705 /**************************************************************************
12706 * Handle Tag Files *
12707 **************************************************************************/
12708
12709 std::shared_ptr<Entry> root = std::make_shared<Entry>();
12710
12711 if (!g_singleComment)
12712 {
12713 msg("Reading and parsing tag files\n");
12714 const StringVector &tagFileList = Config_getList(TAGFILES);
12715 for (const auto &s : tagFileList)
12716 {
12717 readTagFile(root,s.c_str());
12718 }
12719 }
12720
12721 /**************************************************************************
12722 * Parse source files *
12723 **************************************************************************/
12724
12725 addSTLSupport(root);
12726
12727 g_s.begin("Parsing files\n");
12728 if (g_singleComment)
12729 {
12730 //printf("Parsing comment %s\n",qPrint(g_commentFileName));
12731 if (g_commentFileName=="-")
12732 {
12733 std::string text = fileToString(g_commentFileName).str();
12734 addTerminalCharIfMissing(text,'\n');
12735 generateHtmlForComment("stdin.md",text);
12736 }
12737 else if (FileInfo(g_commentFileName.str()).isFile())
12738 {
12739 std::string text;
12741 addTerminalCharIfMissing(text,'\n');
12743 }
12744 else
12745 {
12746 }
12748 exit(0);
12749 }
12750 else
12751 {
12752 if (Config_getInt(NUM_PROC_THREADS)==1)
12753 {
12755 }
12756 else
12757 {
12759 }
12760 }
12761 g_s.end();
12762
12763 /**************************************************************************
12764 * Gather information *
12765 **************************************************************************/
12766
12767 g_s.begin("Building macro definition list...\n");
12769 g_s.end();
12770
12771 g_s.begin("Building group list...\n");
12772 buildGroupList(root.get());
12773 organizeSubGroups(root.get());
12774 g_s.end();
12775
12776 g_s.begin("Building directory list...\n");
12778 findDirDocumentation(root.get());
12779 g_s.end();
12780
12781 g_s.begin("Building namespace list...\n");
12782 buildNamespaceList(root.get());
12783 findUsingDirectives(root.get());
12784 g_s.end();
12785
12786 g_s.begin("Building file list...\n");
12787 buildFileList(root.get());
12788 g_s.end();
12789
12790 g_s.begin("Building class list...\n");
12791 buildClassList(root.get());
12792 g_s.end();
12793
12794 g_s.begin("Building concept list...\n");
12795 buildConceptList(root.get());
12796 g_s.end();
12797
12798 // build list of using declarations here (global list)
12799 buildListOfUsingDecls(root.get());
12800 g_s.end();
12801
12802 g_s.begin("Computing nesting relations for classes...\n");
12804 g_s.end();
12805 // 1.8.2-20121111: no longer add nested classes to the group as well
12806 //distributeClassGroupRelations();
12807
12808 // calling buildClassList may result in cached relations that
12809 // become invalid after resolveClassNestingRelations(), that's why
12810 // we need to clear the cache here
12812 // we don't need the list of using declaration anymore
12813 g_usingDeclarations.clear();
12814
12815 g_s.begin("Associating documentation with classes...\n");
12816 buildClassDocList(root.get());
12817 g_s.end();
12818
12819 g_s.begin("Associating documentation with concepts...\n");
12820 buildConceptDocList(root.get());
12822 g_s.end();
12823
12824 g_s.begin("Associating documentation with modules...\n");
12825 findModuleDocumentation(root.get());
12826 g_s.end();
12827
12828 g_s.begin("Building example list...\n");
12829 buildExampleList(root.get());
12830 g_s.end();
12831
12832 g_s.begin("Searching for enumerations...\n");
12833 findEnums(root.get());
12834 g_s.end();
12835
12836 // Since buildVarList calls isVarWithConstructor
12837 // and this calls getResolvedClass we need to process
12838 // typedefs first so the relations between classes via typedefs
12839 // are properly resolved. See bug 536385 for an example.
12840 g_s.begin("Searching for documented typedefs...\n");
12841 buildTypedefList(root.get());
12842 g_s.end();
12843
12844 if (Config_getBool(OPTIMIZE_OUTPUT_SLICE))
12845 {
12846 g_s.begin("Searching for documented sequences...\n");
12847 buildSequenceList(root.get());
12848 g_s.end();
12849
12850 g_s.begin("Searching for documented dictionaries...\n");
12851 buildDictionaryList(root.get());
12852 g_s.end();
12853 }
12854
12855 g_s.begin("Searching for members imported via using declarations...\n");
12856 // this should be after buildTypedefList in order to properly import
12857 // used typedefs
12858 findUsingDeclarations(root.get(),TRUE); // do for python packages first
12859 findUsingDeclarations(root.get(),FALSE); // then the rest
12860 g_s.end();
12861
12862 g_s.begin("Searching for included using directives...\n");
12864 g_s.end();
12865
12866 g_s.begin("Searching for documented variables...\n");
12867 buildVarList(root.get());
12868 g_s.end();
12869
12870 g_s.begin("Building interface member list...\n");
12871 buildInterfaceAndServiceList(root.get()); // UNO IDL
12872
12873 g_s.begin("Building member list...\n"); // using class info only !
12874 buildFunctionList(root.get());
12875 g_s.end();
12876
12877 g_s.begin("Searching for friends...\n");
12878 findFriends();
12879 g_s.end();
12880
12881 g_s.begin("Searching for documented defines...\n");
12882 findDefineDocumentation(root.get());
12883 g_s.end();
12884
12885 g_s.begin("Computing class inheritance relations...\n");
12886 findClassEntries(root.get());
12888 g_s.end();
12889
12890 g_s.begin("Computing class usage relations...\n");
12892 g_s.end();
12893
12894 if (Config_getBool(INLINE_SIMPLE_STRUCTS))
12895 {
12896 g_s.begin("Searching for tag less structs...\n");
12898 g_s.end();
12899 }
12900
12901 g_s.begin("Flushing cached template relations that have become invalid...\n");
12903 g_s.end();
12904
12905 g_s.begin("Warn for undocumented namespaces...\n");
12907 g_s.end();
12908
12909 g_s.begin("Computing class relations...\n");
12912 if (Config_getBool(OPTIMIZE_OUTPUT_VHDL))
12913 {
12915 }
12917 g_classEntries.clear();
12918 g_s.end();
12919
12920 g_s.begin("Add enum values to enums...\n");
12921 addEnumValuesToEnums(root.get());
12922 findEnumDocumentation(root.get());
12923 g_s.end();
12924
12925 g_s.begin("Searching for member function documentation...\n");
12926 findObjCMethodDefinitions(root.get());
12927 findMemberDocumentation(root.get()); // may introduce new members !
12928 findUsingDeclImports(root.get()); // may introduce new members !
12929 g_usingClassMap.clear();
12933 g_s.end();
12934
12935 // moved to after finding and copying documentation,
12936 // as this introduces new members see bug 722654
12937 g_s.begin("Creating members for template instances...\n");
12939 g_s.end();
12940
12941 g_s.begin("Building page list...\n");
12942 buildPageList(root.get());
12943 g_s.end();
12944
12945 g_s.begin("Building requirements list...\n");
12946 buildRequirementsList(root.get());
12947 g_s.end();
12948
12949 g_s.begin("Search for main page...\n");
12950 findMainPage(root.get());
12951 findMainPageTagFiles(root.get());
12952 g_s.end();
12953
12954 g_s.begin("Computing page relations...\n");
12955 computePageRelations(root.get());
12957 g_s.end();
12958
12959 g_s.begin("Determining the scope of groups...\n");
12960 findGroupScope(root.get());
12961 g_s.end();
12962
12963 g_s.begin("Computing module relations...\n");
12964 auto &mm = ModuleManager::instance();
12965 mm.resolvePartitions();
12966 mm.resolveImports();
12967 mm.collectExportedSymbols();
12968 g_s.end();
12969
12970 auto memberNameComp = [](const MemberNameLinkedMap::Ptr &n1,const MemberNameLinkedMap::Ptr &n2)
12971 {
12972 return qstricmp_sort(n1->memberName().data()+getPrefixIndex(n1->memberName()),
12973 n2->memberName().data()+getPrefixIndex(n2->memberName())
12974 )<0;
12975 };
12976
12977 auto classComp = [](const ClassLinkedMap::Ptr &c1,const ClassLinkedMap::Ptr &c2)
12978 {
12979 if (Config_getBool(SORT_BY_SCOPE_NAME))
12980 {
12981 return qstricmp_sort(c1->name(), c2->name())<0;
12982 }
12983 else
12984 {
12985 int i = qstricmp_sort(c1->className(), c2->className());
12986 return i==0 ? qstricmp_sort(c1->name(), c2->name())<0 : i<0;
12987 }
12988 };
12989
12990 auto namespaceComp = [](const NamespaceLinkedMap::Ptr &n1,const NamespaceLinkedMap::Ptr &n2)
12991 {
12992 return qstricmp_sort(n1->name(),n2->name())<0;
12993 };
12994
12995 auto conceptComp = [](const ConceptLinkedMap::Ptr &c1,const ConceptLinkedMap::Ptr &c2)
12996 {
12997 return qstricmp_sort(c1->name(),c2->name())<0;
12998 };
12999
13000 g_s.begin("Sorting lists...\n");
13001 std::stable_sort(Doxygen::memberNameLinkedMap->begin(),
13003 memberNameComp);
13004 std::stable_sort(Doxygen::functionNameLinkedMap->begin(),
13006 memberNameComp);
13007 std::stable_sort(Doxygen::hiddenClassLinkedMap->begin(),
13009 classComp);
13010 std::stable_sort(Doxygen::classLinkedMap->begin(),
13012 classComp);
13013 std::stable_sort(Doxygen::conceptLinkedMap->begin(),
13015 conceptComp);
13016 std::stable_sort(Doxygen::namespaceLinkedMap->begin(),
13018 namespaceComp);
13019 g_s.end();
13020
13021 g_s.begin("Determining which enums are documented\n");
13023 g_s.end();
13024
13025 g_s.begin("Computing member relations...\n");
13028 g_s.end();
13029
13030 g_s.begin("Building full member lists recursively...\n");
13032 g_s.end();
13033
13034 g_s.begin("Adding members to member groups.\n");
13036 g_s.end();
13037
13038 if (Config_getBool(DISTRIBUTE_GROUP_DOC))
13039 {
13040 g_s.begin("Distributing member group documentation.\n");
13042 g_s.end();
13043 }
13044
13045 g_s.begin("Computing member references...\n");
13047 g_s.end();
13048
13049 if (Config_getBool(INHERIT_DOCS))
13050 {
13051 g_s.begin("Inheriting documentation...\n");
13053 g_s.end();
13054 }
13055
13056
13057 // compute the shortest possible names of all files
13058 // without losing the uniqueness of the file names.
13059 g_s.begin("Generating disk names...\n");
13061 g_s.end();
13062
13063 g_s.begin("Adding source references...\n");
13065 g_s.end();
13066
13067 g_s.begin("Adding xrefitems...\n");
13070 g_s.end();
13071
13072 g_s.begin("Adding requirements...\n");
13075 g_s.end();
13076
13077 g_s.begin("Sorting member lists...\n");
13079 g_s.end();
13080
13081 g_s.begin("Setting anonymous enum type...\n");
13083 g_s.end();
13084
13085 g_s.begin("Computing dependencies between directories...\n");
13087 g_s.end();
13088
13089 g_s.begin("Generating citations page...\n");
13091 g_s.end();
13092
13093 g_s.begin("Counting members...\n");
13094 countMembers();
13095 g_s.end();
13096
13097 g_s.begin("Counting data structures...\n");
13099 g_s.end();
13100
13101 g_s.begin("Resolving user defined references...\n");
13103 g_s.end();
13104
13105 g_s.begin("Finding anchors and sections in the documentation...\n");
13107 g_s.end();
13108
13109 g_s.begin("Transferring function references...\n");
13111 g_s.end();
13112
13113 g_s.begin("Combining using relations...\n");
13115 g_s.end();
13116
13118 g_s.begin("Adding members to index pages...\n");
13120 addToIndices();
13121 g_s.end();
13122
13123 g_s.begin("Correcting members for VHDL...\n");
13125 g_s.end();
13126
13127 g_s.begin("Computing tooltip texts...\n");
13129 g_s.end();
13130
13131 if (Config_getBool(SORT_GROUP_NAMES))
13132 {
13133 std::stable_sort(Doxygen::groupLinkedMap->begin(),
13135 [](const auto &g1,const auto &g2)
13136 { return g1->groupTitle() < g2->groupTitle(); });
13137
13138 for (const auto &gd : *Doxygen::groupLinkedMap)
13139 {
13140 gd->sortSubGroups();
13141 }
13142 }
13143
13144 printNavTree(root.get(),0);
13146}
13147
13149{
13150 AUTO_TRACE();
13151 /**************************************************************************
13152 * Initialize output generators *
13153 **************************************************************************/
13154
13155 /// add extra languages for which we can only produce syntax highlighted code
13157
13158 //// dump all symbols
13159 if (g_dumpSymbolMap)
13160 {
13161 dumpSymbolMap();
13162 exit(0);
13163 }
13164
13165 bool generateHtml = Config_getBool(GENERATE_HTML);
13166 bool generateLatex = Config_getBool(GENERATE_LATEX);
13167 bool generateMan = Config_getBool(GENERATE_MAN);
13168 bool generateRtf = Config_getBool(GENERATE_RTF);
13169 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
13170
13171
13173 if (generateHtml)
13174 {
13178 }
13179 if (generateLatex)
13180 {
13183 }
13184 if (generateDocbook)
13185 {
13188 }
13189 if (generateMan)
13190 {
13191 g_outputList->add<ManGenerator>();
13193 }
13194 if (generateRtf)
13195 {
13196 g_outputList->add<RTFGenerator>();
13198 }
13199 if (Config_getBool(USE_HTAGS))
13200 {
13202 QCString htmldir = Config_getString(HTML_OUTPUT);
13203 if (!Htags::execute(htmldir))
13204 err("USE_HTAGS is YES but htags(1) failed. \n");
13205 else if (!Htags::loadFilemap(htmldir))
13206 err("htags(1) ended normally but failed to load the filemap. \n");
13207 }
13208
13209 /**************************************************************************
13210 * Generate documentation *
13211 **************************************************************************/
13212
13213 g_s.begin("Generating style sheet...\n");
13214 //printf("writing style info\n");
13215 g_outputList->writeStyleInfo(0); // write first part
13216 g_s.end();
13217
13218 bool searchEngine = Config_getBool(SEARCHENGINE);
13219 bool serverBasedSearch = Config_getBool(SERVER_BASED_SEARCH);
13220
13221 g_s.begin("Generating search indices...\n");
13222 if (searchEngine && !serverBasedSearch && generateHtml)
13223 {
13225 }
13226
13227 // generate search indices (need to do this before writing other HTML
13228 // pages as these contain a drop down menu with options depending on
13229 // what categories we find in this function.
13230 if (generateHtml && searchEngine)
13231 {
13232 QCString searchDirName = Config_getString(HTML_OUTPUT)+"/search";
13233 Dir searchDir(searchDirName.str());
13234 if (!searchDir.exists() && !searchDir.mkdir(searchDirName.str()))
13235 {
13236 term("Could not create search results directory '{}' $PWD='{}'\n",
13237 searchDirName,Dir::currentDirPath());
13238 }
13239 HtmlGenerator::writeSearchData(searchDirName);
13240 if (!serverBasedSearch) // client side search index
13241 {
13243 }
13244 }
13245 g_s.end();
13246
13247 // copy static stuff
13248 if (generateHtml)
13249 {
13251 copyLogo(Config_getString(HTML_OUTPUT));
13252 copyIcon(Config_getString(HTML_OUTPUT));
13253 copyExtraFiles(Config_getList(HTML_EXTRA_FILES),"HTML_EXTRA_FILES",Config_getString(HTML_OUTPUT));
13254 }
13255 if (generateLatex)
13256 {
13258 copyLogo(Config_getString(LATEX_OUTPUT));
13259 copyIcon(Config_getString(LATEX_OUTPUT));
13260 copyExtraFiles(Config_getList(LATEX_EXTRA_FILES),"LATEX_EXTRA_FILES",Config_getString(LATEX_OUTPUT));
13261 }
13262 if (generateDocbook)
13263 {
13264 copyLogo(Config_getString(DOCBOOK_OUTPUT));
13265 copyIcon(Config_getString(DOCBOOK_OUTPUT));
13266 }
13267 if (generateRtf)
13268 {
13269 copyLogo(Config_getString(RTF_OUTPUT));
13270 copyIcon(Config_getString(RTF_OUTPUT));
13271 copyExtraFiles(Config_getList(RTF_EXTRA_FILES),"RTF_EXTRA_FILES",Config_getString(RTF_OUTPUT));
13272 }
13273
13275 if (fm.hasFormulas() && generateHtml
13276 && !Config_getBool(USE_MATHJAX))
13277 {
13278 g_s.begin("Generating images for formulas in HTML...\n");
13279 fm.generateImages(Config_getString(HTML_OUTPUT), Config_getEnum(HTML_FORMULA_FORMAT)==HTML_FORMULA_FORMAT_t::svg ?
13281 g_s.end();
13282 }
13283 if (fm.hasFormulas() && generateRtf)
13284 {
13285 g_s.begin("Generating images for formulas in RTF...\n");
13287 g_s.end();
13288 }
13289
13290 if (fm.hasFormulas() && generateDocbook)
13291 {
13292 g_s.begin("Generating images for formulas in Docbook...\n");
13294 g_s.end();
13295 }
13296
13297 g_s.begin("Generating example documentation...\n");
13299 g_s.end();
13300
13301 g_s.begin("Generating file sources...\n");
13303 g_s.end();
13304
13305 g_s.begin("Generating file documentation...\n");
13307 g_s.end();
13308
13309 g_s.begin("Generating page documentation...\n");
13311 g_s.end();
13312
13313 g_s.begin("Generating group documentation...\n");
13315 g_s.end();
13316
13317 g_s.begin("Generating class documentation...\n");
13319 g_s.end();
13320
13321 g_s.begin("Generating concept documentation...\n");
13323 g_s.end();
13324
13325 g_s.begin("Generating module documentation...\n");
13327 g_s.end();
13328
13329 g_s.begin("Generating namespace documentation...\n");
13331 g_s.end();
13332
13333 if (Config_getBool(GENERATE_LEGEND))
13334 {
13335 g_s.begin("Generating graph info page...\n");
13337 g_s.end();
13338 }
13339
13340 g_s.begin("Generating directory documentation...\n");
13342 g_s.end();
13343
13344 if (g_outputList->size()>0)
13345 {
13347 }
13348
13349 g_s.begin("finalizing index lists...\n");
13350 Doxygen::indexList->finalize();
13351 g_s.end();
13352
13353 g_s.begin("writing tag file...\n");
13354 writeTagFile();
13355 g_s.end();
13356
13357 if (Config_getBool(GENERATE_XML))
13358 {
13359 g_s.begin("Generating XML output...\n");
13361 generateXML();
13363 g_s.end();
13364 }
13365 if (Config_getBool(GENERATE_SQLITE3))
13366 {
13367 g_s.begin("Generating SQLITE3 output...\n");
13369 g_s.end();
13370 }
13371
13372 if (Config_getBool(GENERATE_AUTOGEN_DEF))
13373 {
13374 g_s.begin("Generating AutoGen DEF output...\n");
13375 generateDEF();
13376 g_s.end();
13377 }
13378 if (Config_getBool(GENERATE_PERLMOD))
13379 {
13380 g_s.begin("Generating Perl module output...\n");
13382 g_s.end();
13383 }
13384 if (generateHtml && searchEngine && serverBasedSearch)
13385 {
13386 g_s.begin("Generating search index\n");
13387 if (Doxygen::searchIndex.kind()==SearchIndexIntf::Internal) // write own search index
13388 {
13390 Doxygen::searchIndex.write(Config_getString(HTML_OUTPUT)+"/search/search.idx");
13391 }
13392 else // write data for external search index
13393 {
13395 QCString searchDataFile = Config_getString(SEARCHDATA_FILE);
13396 if (searchDataFile.isEmpty())
13397 {
13398 searchDataFile="searchdata.xml";
13399 }
13400 if (!Portable::isAbsolutePath(searchDataFile.data()))
13401 {
13402 searchDataFile.prepend(Config_getString(OUTPUT_DIRECTORY)+"/");
13403 }
13404 Doxygen::searchIndex.write(searchDataFile);
13405 }
13406 g_s.end();
13407 }
13408
13409 if (generateRtf)
13410 {
13411 g_s.begin("Combining RTF output...\n");
13412 if (!RTFGenerator::preProcessFileInplace(Config_getString(RTF_OUTPUT),"refman.rtf"))
13413 {
13414 err("An error occurred during post-processing the RTF files!\n");
13415 }
13416 g_s.end();
13417 }
13418
13419 g_s.begin("Running plantuml with JAVA...\n");
13421 g_s.end();
13422
13423 g_s.begin("Running mermaid (mmdc)...\n");
13425 g_s.end();
13426
13427 if (Config_getBool(HAVE_DOT))
13428 {
13429 g_s.begin("Running dot...\n");
13431 g_s.end();
13432 }
13433
13434 if (generateHtml &&
13435 Config_getBool(GENERATE_HTMLHELP) &&
13436 !Config_getString(HHC_LOCATION).isEmpty())
13437 {
13438 g_s.begin("Running html help compiler...\n");
13440 g_s.end();
13441 }
13442
13443 if ( generateHtml &&
13444 Config_getBool(GENERATE_QHP) &&
13445 !Config_getString(QHG_LOCATION).isEmpty())
13446 {
13447 g_s.begin("Running qhelpgenerator...\n");
13449 g_s.end();
13450 }
13451
13452 g_outputList->cleanup();
13454
13456
13458 {
13459
13460 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
13461 if (numThreads<1) numThreads=1;
13462 msg("Total elapsed time: {:.6f} seconds\n(of which an average of {:.6f} seconds per thread waiting for external tools to finish)\n",
13463 (static_cast<double>(Debug::elapsedTime())),
13464 Portable::getSysElapsedTime()/static_cast<double>(numThreads)
13465 );
13466 g_s.print();
13467
13469 msg("finished...\n");
13471 }
13472 else
13473 {
13474 msg("finished...\n");
13475 }
13476
13477
13478 /**************************************************************************
13479 * Start cleaning up *
13480 **************************************************************************/
13481
13483
13485 Dir thisDir;
13486 thisDir.remove(Doxygen::filterDBFileName.str());
13488 exitTracing();
13490 delete Doxygen::clangUsrMap;
13492
13493 //dumpDocNodeSizes();
13494}
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
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:138
static void clearFlag(const DebugMask mask)
Definition debug.cpp:123
static bool isFlagSet(const DebugMask mask)
Definition debug.cpp:133
static void print(DebugMask mask, int prio, fmt::format_string< Args... > fmt, Args &&... args)
Definition debug.h:77
static double elapsedTime()
Definition debug.cpp:201
static void startTimer()
Definition debug.cpp:196
static bool setFlagStr(const QCString &label)
Definition debug.cpp:104
static void setFlag(const DebugMask mask)
Definition debug.cpp:118
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:115
static ConceptLinkedMap * conceptLinkedMap
Definition doxygen.h:97
static bool suppressDocWarnings
Definition doxygen.h:130
static FileNameLinkedMap * plantUmlFileNameLinkedMap
Definition doxygen.h:109
static bool parseSourcesNeeded
Definition doxygen.h:123
static StringUnorderedSet inputPaths
Definition doxygen.h:103
static std::unique_ptr< PageDef > mainPage
Definition doxygen.h:100
static bool clangAssistedParsing
Definition doxygen.h:136
static StringUnorderedSet expandAsDefinedSet
Definition doxygen.h:119
static FileNameLinkedMap * inputNameLinkedMap
Definition doxygen.h:104
static ParserManager * parserManager
Definition doxygen.h:129
static InputFileEncodingList inputFileEncodingList
Definition doxygen.h:138
static ClassLinkedMap * classLinkedMap
Definition doxygen.h:95
static MemberNameLinkedMap * functionNameLinkedMap
Definition doxygen.h:112
static PageLinkedMap * exampleLinkedMap
Definition doxygen.h:98
static FileNameLinkedMap * dotFileNameLinkedMap
Definition doxygen.h:106
static NamespaceDefMutable * globalScope
Definition doxygen.h:121
static FileNameLinkedMap * imageNameLinkedMap
Definition doxygen.h:105
static FileNameLinkedMap * mscFileNameLinkedMap
Definition doxygen.h:107
static FileNameLinkedMap * mermaidFileNameLinkedMap
Definition doxygen.h:110
static QCString verifiedDotPath
Definition doxygen.h:137
static MemberGroupInfoMap memberGroupInfoMap
Definition doxygen.h:118
static QCString spaces
Definition doxygen.h:133
static IndexList * indexList
Definition doxygen.h:132
static StaticInitMap staticInitMap
Definition doxygen.h:141
static StringMap tagDestinationMap
Definition doxygen.h:116
static std::mutex countFlowKeywordsMutex
Definition doxygen.h:139
static ClassLinkedMap * hiddenClassLinkedMap
Definition doxygen.h:96
static FileNameLinkedMap * diaFileNameLinkedMap
Definition doxygen.h:108
static QCString htmlFileExtension
Definition doxygen.h:122
static QCString filterDBFileName
Definition doxygen.h:131
static PageLinkedMap * pageLinkedMap
Definition doxygen.h:99
static bool generatingXmlOutput
Definition doxygen.h:134
static std::unique_ptr< NamespaceDef > globalNamespaceDef
Definition doxygen.h:120
static DefinesPerFileList macroDefinitions
Definition doxygen.h:135
static DirLinkedMap * dirLinkedMap
Definition doxygen.h:127
static NamespaceAliasInfoMap namespaceAliasMap
Definition doxygen.h:113
static MemberNameLinkedMap * memberNameLinkedMap
Definition doxygen.h:111
static SymbolMap< Definition > * symbolMap
Definition doxygen.h:125
static StringUnorderedSet tagFileSet
Definition doxygen.h:117
static FileNameLinkedMap * includeNameLinkedMap
Definition doxygen.h:101
static FileNameLinkedMap * exampleNameLinkedMap
Definition doxygen.h:102
static SearchIndexIntf searchIndex
Definition doxygen.h:124
static DirRelationLinkedMap dirRelations
Definition doxygen.h:128
static std::mutex addExampleMutex
Definition doxygen.h:140
static ClangUsrMap * clangUsrMap
Definition doxygen.h:126
static GroupLinkedMap * groupLinkedMap
Definition doxygen.h:114
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:1210
static void writeSearchPage()
Definition htmlgen.cpp:3210
static void writeFooterFile(TextStream &t)
Definition htmlgen.cpp:1553
static void writeTabData()
Additional initialization after indices have been created.
Definition htmlgen.cpp:1370
static void writeSearchData(const QCString &dir)
Definition htmlgen.cpp:1379
static void writeExternalSearchPage()
Definition htmlgen.cpp:3309
static void writeStyleSheetFile(TextStream &t)
Definition htmlgen.cpp:1541
static void writeHeaderFile(TextStream &t, const QCString &cssname)
Definition htmlgen.cpp:1547
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 run()
Run mmdc tool for all collected diagrams.
Definition mermaid.cpp:179
static MermaidManager & instance()
Definition mermaid.cpp:32
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:270
void print()
Definition doxygen.cpp:247
void begin(const char *name)
Definition doxygen.cpp:234
void end()
Definition doxygen.cpp:240
std::chrono::steady_clock::time_point startTime
Definition doxygen.cpp:271
static void showCacheUsage()
Show usage of the type lookup cache.
static void clearTypeLookupCache(ClearScope scope)
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:49
#define AUTO_TRACE(...)
Definition docnode.cpp:48
#define AUTO_TRACE_EXIT(...)
Definition docnode.cpp:50
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:1336
static void findInheritedTemplateInstances()
Definition doxygen.cpp:5328
void printNavTree(Entry *root, int indent)
static void addClassToContext(const Entry *root)
Definition doxygen.cpp:939
static void makeTemplateInstanceRelation(const Entry *root, ClassDefMutable *cd)
Definition doxygen.cpp:5343
static StringUnorderedSet g_pathsVisited(1009)
static int findEndOfTemplate(const QCString &s, size_t startPos)
Definition doxygen.cpp:3113
static void buildGroupList(const Entry *root)
Definition doxygen.cpp:432
static void insertMemberAlias(Definition *outerScope, const MemberDef *md)
Definition doxygen.cpp:6712
static void findUsingDeclarations(const Entry *root, bool filterPythonPackages)
Definition doxygen.cpp:2058
static void flushCachedTemplateRelations()
Definition doxygen.cpp:9462
static bool isRecursiveBaseClass(const QCString &scope, const QCString &name)
Definition doxygen.cpp:4886
static void copyLatexStyleSheet()
static void generateDocsForClassList(const std::vector< ClassDefMutable * > &classList)
Definition doxygen.cpp:9122
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:2909
static bool isSpecialization(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists)
Definition doxygen.cpp:6013
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:2478
static void computeTemplateClassRelations()
Definition doxygen.cpp:5422
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:8176
static void runQHelpGenerator()
static void addConceptToContext(const Entry *root)
Definition doxygen.cpp:1165
static void addRelatedPage(Entry *root)
Definition doxygen.cpp:330
void initDoxygen()
static void addIncludeFile(DefMutable *cd, FileDef *ifd, const Entry *root)
Definition doxygen.cpp:589
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:2162
static void generateXRefPages()
Definition doxygen.cpp:5601
static void findUsingDeclImports(const Entry *root)
Definition doxygen.cpp:2211
static void copyStyleSheet()
FindBaseClassRelation_Mode
Definition doxygen.cpp:287
@ Undocumented
Definition doxygen.cpp:290
@ TemplateInstances
Definition doxygen.cpp:288
@ DocumentedOnly
Definition doxygen.cpp:289
void distributeClassGroupRelations()
Definition doxygen.cpp:1496
static void generateGroupDocs()
static void findDirDocumentation(const Entry *root)
Definition doxygen.cpp:9662
void checkConfiguration()
check and resolve config options
QCString stripTemplateSpecifiers(const QCString &s)
Definition doxygen.cpp:686
static bool findClassRelation(const Entry *root, Definition *context, ClassDefMutable *cd, const BaseInfo *bi, const TemplateNameMap &templateNames, FindBaseClassRelation_Mode mode, bool isArtificial)
Definition doxygen.cpp:4928
static void resolveTemplateInstanceInType(const Entry *root, const Definition *scope, const MemberDef *md)
Definition doxygen.cpp:4856
static void organizeSubGroupsFiltered(const Entry *root, bool additional)
Definition doxygen.cpp:472
static void warnUndocumentedNamespaces()
Definition doxygen.cpp:5377
static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments, const std::string &name)
Definition doxygen.cpp:4535
static NamespaceDef * findUsedNamespace(const LinkedRefMap< NamespaceDef > &unl, const QCString &name)
Definition doxygen.cpp:1889
static void buildConceptList(const Entry *root)
Definition doxygen.cpp:1326
static void resolveClassNestingRelations()
Definition doxygen.cpp:1381
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:6755
static void findClassEntries(const Entry *root)
Definition doxygen.cpp:5291
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:1615
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:6225
static void vhdlCorrectMemberProperties()
Definition doxygen.cpp:8420
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:4805
static void substituteTemplatesInArgList(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists, const ArgumentList &src, ArgumentList &dst)
Definition doxygen.cpp:6127
static void computeMemberReferences()
Definition doxygen.cpp:5491
static void generateConfigFile(const QCString &configFile, bool shortList, bool updateOnly=FALSE)
static void transferRelatedFunctionDocumentation()
Definition doxygen.cpp:4449
static void addMembersToMemberGroup()
Definition doxygen.cpp:9327
static bool tryAddEnumDocsToGroupMember(const Entry *root, const QCString &name)
Definition doxygen.cpp:8057
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:6644
static void findMainPageTagFiles(Entry *root)
Definition doxygen.cpp:9832
static void distributeConceptGroups()
Definition doxygen.cpp:1348
static void transferFunctionDocumentation()
Definition doxygen.cpp:4368
static void setAnonymousEnumType()
Definition doxygen.cpp:9067
static void sortMemberLists()
Definition doxygen.cpp:8972
static void createTemplateInstanceMembers()
Definition doxygen.cpp:8548
void transferStaticInstanceInitializers()
Definition doxygen.cpp:4498
static void findObjCMethodDefinitions(const Entry *root)
Definition doxygen.cpp:7561
static void dumpSymbolMap()
static void buildTypedefList(const Entry *root)
Definition doxygen.cpp:3437
static void findGroupScope(const Entry *root)
Definition doxygen.cpp:447
static void generateFileDocs()
Definition doxygen.cpp:8785
static void findDefineDocumentation(Entry *root)
Definition doxygen.cpp:9575
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:4598
void parseInput()
static void findMemberDocumentation(const Entry *root)
Definition doxygen.cpp:7531
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:6171
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:5805
static void distributeMemberGroupDocumentation()
Definition doxygen.cpp:9365
static void generateNamespaceClassDocs(const ClassLinkedRefMap &classList)
static void addEnumValuesToEnums(const Entry *root)
Definition doxygen.cpp:7765
static void generatePageDocs()
Definition doxygen.cpp:9962
static void resolveUserReferences()
Definition doxygen.cpp:9894
static void buildRequirementsList(Entry *root)
Definition doxygen.cpp:9723
static void compareDoxyfile(Config::CompareMode diffList)
static void addPageToContext(PageDef *pd, Entry *root)
Definition doxygen.cpp:311
static void buildVarList(const Entry *root)
Definition doxygen.cpp:3574
static void buildSequenceList(const Entry *root)
Definition doxygen.cpp:3537
static void generateFileSources()
Definition doxygen.cpp:8619
static QCString substituteTemplatesInString(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists, const std::string &src)
Definition doxygen.cpp:6043
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:2653
static void generateClassDocs()
Definition doxygen.cpp:9220
static void buildNamespaceList(const Entry *root)
Definition doxygen.cpp:1720
static void readTagFile(const std::shared_ptr< Entry > &root, const QCString &tagLine)
static void findIncludedUsingDirectives()
Definition doxygen.cpp:2461
static void addDefineDoc(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:9548
static void addMemberDocs(const Entry *root, MemberDefMutable *md, const QCString &funcDecl, const ArgumentList *al, bool over_load, TypeSpecifier spec)
Definition doxygen.cpp:5615
static void countMembers()
Definition doxygen.cpp:9081
void clearAll()
Definition doxygen.cpp:202
static void devUsage()
static void addInterfaceOrServiceToServiceOrSingleton(const Entry *root, ClassDefMutable *cd, QCString const &rname)
Definition doxygen.cpp:3606
static void organizeSubGroups(const Entry *root)
Definition doxygen.cpp:491
static void applyMemberOverrideOptions(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:2150
void searchInputFiles()
static void findFriends()
Definition doxygen.cpp:4271
static void findEnums(const Entry *root)
Definition doxygen.cpp:7589
static void dumpSymbol(TextStream &t, Definition *d)
static void addClassAndNestedClasses(std::vector< ClassDefMutable * > &list, ClassDefMutable *cd)
Definition doxygen.cpp:9197
static void addEnumDocs(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:8015
static void addListReferences()
Definition doxygen.cpp:5592
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:5269
static Definition * findScopeFromQualifiedName(NamespaceDefMutable *startScope, const QCString &n, FileDef *fileScope, const TagInfo *tagInfo)
Definition doxygen.cpp:782
static ClassDef * findClassWithinClassContext(Definition *context, ClassDef *cd, const QCString &name)
Definition doxygen.cpp:4563
static void buildGroupListFiltered(const Entry *root, bool additional, bool includeExternal)
Definition doxygen.cpp:361
static void runHtmlHelpCompiler()
static QCString extractClassName(const Entry *root)
Definition doxygen.cpp:5300
static void addMembersToIndex()
Definition doxygen.cpp:8210
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:3857
static OutputList * g_outputList
Definition doxygen.cpp:189
static void findMainPage(Entry *root)
Definition doxygen.cpp:9762
static ClassDef::CompoundType convertToCompoundType(EntryType section, TypeSpecifier specifier)
Definition doxygen.cpp:897
static void findUsingDirectives(const Entry *root)
Definition doxygen.cpp:1902
std::unique_ptr< ArgumentList > getTemplateArgumentsFromName(const QCString &name, const ArgumentLists &tArgLists)
Definition doxygen.cpp:867
static bool g_successfulRun
Definition doxygen.cpp:191
static void addSourceReferences()
Definition doxygen.cpp:8845
std::function< std::unique_ptr< T >() > make_parser_factory()
static void buildExampleList(Entry *root)
Definition doxygen.cpp:9979
static void inheritDocumentation()
Definition doxygen.cpp:9267
static void flushUnresolvedRelations()
Definition doxygen.cpp:9504
static bool isSymbolHidden(const Definition *d)
Definition doxygen.cpp:9014
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:8202
static void findTagLessClasses()
Definition doxygen.cpp:1696
static void generateDiskNames()
static void addToIndices()
Definition doxygen.cpp:8252
static void computeClassRelations()
Definition doxygen.cpp:5397
static void buildFunctionList(const Entry *root)
Definition doxygen.cpp:3966
static void checkPageRelations()
Definition doxygen.cpp:9874
static void findModuleDocumentation(const Entry *root)
Definition doxygen.cpp:1316
static void findEnumDocumentation(const Entry *root)
Definition doxygen.cpp:8090
static void computePageRelations(Entry *root)
Definition doxygen.cpp:9844
static void filterMemberDocumentation(const Entry *root, const QCString &relates)
Definition doxygen.cpp:7381
static QCString createOutputDirectory(const QCString &baseDirName, const QCString &formatDirName, const char *defaultDirName)
void initResources()
static bool isVarWithConstructor(const Entry *root)
Definition doxygen.cpp:2969
static StringSet g_usingDeclarations
Definition doxygen.cpp:190
static void buildDictionaryList(const Entry *root)
Definition doxygen.cpp:3555
static bool haveEqualFileNames(const Entry *root, const MemberDef *md)
Definition doxygen.cpp:9537
static void generateNamespaceDocs()
static void buildClassDocList(const Entry *root)
Definition doxygen.cpp:1151
static void buildPageList(Entry *root)
Definition doxygen.cpp:9735
static void writeTagFile()
static Definition * buildScopeFromQualifiedName(const QCString &name_, SrcLangExt lang, const TagInfo *tagInfo)
Definition doxygen.cpp:713
static void addRequirementReferences()
Definition doxygen.cpp:5584
static void computeVerifiedDotPath()
static bool g_singleComment
Definition doxygen.cpp:194
static void findSectionsInDocumentation()
Definition doxygen.cpp:9403
static void mergeCategories()
Definition doxygen.cpp:8567
static const StringUnorderedSet g_compoundKeywords
Definition doxygen.cpp:199
static bool scopeIsTemplate(const Definition *d)
Definition doxygen.cpp:6029
static void buildFileList(const Entry *root)
Definition doxygen.cpp:503
static ClassDefMutable * createTagLessInstance(const ClassDef *rootCd, const ClassDef *templ, const QCString &fieldName)
Definition doxygen.cpp:1529
static void buildClassList(const Entry *root)
Definition doxygen.cpp:1141
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:3721
static std::unique_ptr< OutlineParserInterface > getParserForFile(const QCString &fn)
static void findUsedTemplateInstances()
Definition doxygen.cpp:5361
static void computeTooltipTexts()
Definition doxygen.cpp:9021
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:3181
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:6581
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:4754
static void parseFilesSingleThreading(const std::shared_ptr< Entry > &root)
parse the list of input files
static void buildCompleteMemberLists()
Definition doxygen.cpp:8589
static void generateConceptDocs()
Definition doxygen.cpp:9246
static void combineUsingRelations()
Definition doxygen.cpp:9302
static const char * getArg(int argc, char **argv, int &optInd)
static const ClassDef * findClassDefinition(FileDef *fd, NamespaceDef *nd, const QCString &scopeName)
Definition doxygen.cpp:5766
static void checkMarkdownMainfile()
static std::unordered_map< std::string, std::vector< ClassDefMutable * > > g_usingClassMap
Definition doxygen.cpp:2209
static void buildConceptDocList(const Entry *root)
Definition doxygen.cpp:1336
static int findTemplateSpecializationPosition(const QCString &name)
Definition doxygen.cpp:4898
static bool isEntryInGroupOfMember(const Entry *root, const MemberDef *md, bool allowNoGroup=false)
Definition doxygen.cpp:5781
static void applyToAllDefinitions(Func func)
Definition doxygen.cpp:5527
static void parseFilesMultiThreading(const std::shared_ptr< Entry > &root)
parse the list of input files
static void transferFunctionReferences()
Definition doxygen.cpp:4401
static void buildDefineList()
Definition doxygen.cpp:8924
static void buildInterfaceAndServiceList(const Entry *root)
Definition doxygen.cpp:3669
static void computeMemberRelations()
Definition doxygen.cpp:8532
static void buildListOfUsingDecls(const Entry *root)
Definition doxygen.cpp:2045
static void computeMemberRelationsForBaseClass(const ClassDef *cd, const BaseClassDef *bcd)
Definition doxygen.cpp:8452
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:4116
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:5830
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
stat(const char *n, double el)
Definition doxygen.cpp:268
const char * name
Definition doxygen.cpp:265
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:2021
QCString removeRedundantWhiteSpace(const QCString &s)
Definition util.cpp:567
QCString mergeScopes(const QCString &leftScope, const QCString &rightScope)
Definition util.cpp:4598
QCString normalizeNonTemplateArgumentsInString(const QCString &name, const Definition *context, const ArgumentList &formalArgs)
Definition util.cpp:4304
SrcLangExt getLanguageFromFileName(const QCString &fileName, SrcLangExt defLang)
Definition util.cpp:5214
bool protectionLevelVisible(Protection prot)
Definition util.cpp:5960
bool matchTemplateArguments(const ArgumentList &srcAl, const ArgumentList &dstAl)
Definition util.cpp:2265
void addCodeOnlyMappings()
Definition util.cpp:5208
QCString substituteTemplateArgumentsInString(const QCString &nm, const ArgumentList &formalArgs, const ArgumentList *actualArgs)
Definition util.cpp:4369
int extractClassNameFromType(const QCString &type, int &pos, QCString &name, QCString &templSpec, SrcLangExt lang)
Definition util.cpp:4219
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:5318
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:5553
bool patternMatch(const FileInfo &fi, const StringVector &patList)
Definition util.cpp:5707
bool openOutputFile(const QCString &outFile, std::ofstream &f)
Definition util.cpp:6319
QCString tempArgListToString(const ArgumentList &al, SrcLangExt lang, bool includeDefault)
Definition util.cpp:1299
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:4828
QCString showFileDefMatches(const FileNameLinkedMap *fnMap, const QCString &n)
Definition util.cpp:3022
QCString fileToString(const QCString &name, bool filter, bool isSourceCode)
Definition util.cpp:1494
QCString filterTitle(const QCString &title)
Definition util.cpp:5633
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:4920
int computeQualifiedIndex(const QCString &name)
Return the index of the last :: in the string name that is still before the first <.
Definition util.cpp:6847
void initDefaultExtensionMapping()
Definition util.cpp:5141
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:4990
QCString convertNameToFile(const QCString &name, bool allowDots, bool allowUnderscore)
Definition util.cpp:3508
QCString langToString(SrcLangExt lang)
Returns a string representation of lang.
Definition util.cpp:5914
EntryType guessSection(const QCString &name)
Definition util.cpp:338
void extractNamespaceName(const QCString &scopeName, QCString &className, QCString &namespaceName, bool allowEmptyClass)
Definition util.cpp:3701
QCString argListToString(const ArgumentList &al, bool useCanonicalType, bool showDefVals)
Definition util.cpp:1254
QCString getLanguageSpecificSeparator(SrcLangExt lang, bool classScope)
Returns the scope separator to use given the programming language lang.
Definition util.cpp:5920
void mergeMemberOverrideOptions(MemberDefMutable *md1, MemberDefMutable *md2)
Definition util.cpp:6884
QCString mangleCSharpGenericName(const QCString &name)
Definition util.cpp:6936
QCString projectLogoFile()
Definition util.cpp:3144
void mergeArguments(ArgumentList &srcAl, ArgumentList &dstAl, bool forceNameOverwrite)
Definition util.cpp:2121
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:5880
QCString stripTemplateSpecifiersFromScope(const QCString &fullName, bool parentOnly, QCString *pLastScopeStripped, QCString scopeName, bool allowArtificial)
Definition util.cpp:4531
QCString getOverloadDocs()
Definition util.cpp:4093
void cleanupInlineGraph()
Definition util.cpp:7013
int getPrefixIndex(const QCString &name)
Definition util.cpp:3235
bool rightScopeMatch(const QCString &scope, const QCString &name)
Definition util.cpp:870
bool updateLanguageMapping(const QCString &extension, const QCString &language)
Definition util.cpp:5109
QCString getFileNameExtension(const QCString &fn)
Definition util.cpp:5256
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:2894
int getScopeFragment(const QCString &s, int p, int *l)
Definition util.cpp:4643
void addHtmlExtensionIfMissing(QCString &fName)
Definition util.cpp:4925
A bunch of utility functions.
void generateXML()
Definition xmlgen.cpp:2335