Doxygen
Loading...
Searching...
No Matches
doxygen.cpp
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * Copyright (C) 1997-2015 by Dimitri van Heesch.
4 *
5 * Permission to use, copy, modify, and distribute this software and its
6 * documentation under the terms of the GNU General Public License is hereby
7 * granted. No representations are made about the suitability of this software
8 * for any purpose. It is provided "as is" without express or implied warranty.
9 * See the GNU General Public License for more details.
10 *
11 * Documents produced by Doxygen are derivative works derived from the
12 * input used in their production; they are not affected by this license.
13 *
14 */
15
16#include <cstdio>
17#include <cstdlib>
18#include <cerrno>
19#include <sys/stat.h>
20
21#include <algorithm>
22#include <unordered_map>
23#include <memory>
24#include <cinttypes>
25#include <chrono>
26#include <clocale>
27#include <locale>
28
29#include "aliases.h"
30#include "arguments.h"
31#include "cite.h"
32#include "clangparser.h"
33#include "classlist.h"
34#include "cmdmapper.h"
35#include "code.h"
36#include "commentcnv.h"
37#include "conceptdef.h"
38#include "config.h"
39#include "debug.h"
40#include "declinfo.h"
41#include "defargs.h"
42#include "defgen.h"
43#include "dir.h"
44#include "dirdef.h"
45#include "docbookgen.h"
46#include "docparser.h"
47#include "docsets.h"
48#include "dot.h"
49#include "doxygen.h"
50#include "eclipsehelp.h"
51#include "emoji.h"
52#include "entry.h"
53#include "fileinfo.h"
54#include "filename.h"
55#include "fileparser.h"
56#include "formula.h"
57#include "fortrancode.h"
58#include "fortranscanner.h"
59#include "ftvhelp.h"
60#include "groupdef.h"
61#include "htags.h"
62#include "htmlgen.h"
63#include "htmlhelp.h"
64#include "index.h"
65#include "indexlist.h"
66#include "language.h"
67#include "latexgen.h"
68#include "layout.h"
69#include "lexcode.h"
70#include "lexscanner.h"
71#include "mangen.h"
72#include "markdown.h"
73#include "membergroup.h"
74#include "memberlist.h"
75#include "membername.h"
76#include "message.h"
77#include "moduledef.h"
78#include "msc.h"
79#include "namespacedef.h"
80#include "outputlist.h"
81#include "pagedef.h"
82#include "parserintf.h"
83#include "perlmodgen.h"
84#include "plantuml.h"
85#include "portable.h"
86#include "pre.h"
87#include "pycode.h"
88#include "pyscanner.h"
89#include "qhp.h"
90#include "reflist.h"
91#include "regex.h"
92#include "requirement.h"
93#include "rtfgen.h"
94#include "scanner.h"
95#include "searchindex_js.h"
96#include "settings.h"
97#include "singlecomment.h"
98#include "sitemap.h"
99#include "sqlcode.h"
100#include "sqlite3gen.h"
101#include "stlsupport.h"
102#include "stringutil.h"
103#include "symbolresolver.h"
104#include "tagreader.h"
105#include "threadpool.h"
106#include "trace.h"
107#include "util.h"
108#include "version.h"
109#include "vhdlcode.h"
110#include "vhdldocgen.h"
111#include "vhdljjparser.h"
112#include "xmlcode.h"
113#include "xmlgen.h"
114
115#include <sqlite3.h>
116
117#if USE_LIBCLANG
118#if defined(__GNUC__)
119#pragma GCC diagnostic push
120#pragma GCC diagnostic ignored "-Wshadow"
121#endif
122#include <clang/Basic/Version.h>
123#if defined(__GNUC__)
124#pragma GCC diagnostic pop
125#endif
126#endif
127
128// provided by the generated file resources.cpp
129extern void initResources();
130
131#if !defined(_WIN32) || defined(__CYGWIN__)
132#include <signal.h>
133#define HAS_SIGNALS
134#endif
135
136// globally accessible variables
148FileNameLinkedMap *Doxygen::includeNameLinkedMap = nullptr; // include names
154FileNameLinkedMap *Doxygen::plantUmlFileNameLinkedMap = nullptr;// plantuml files
156StringMap Doxygen::tagDestinationMap; // all tag locations
157StringUnorderedSet Doxygen::tagFileSet; // all tag file names
158StringUnorderedSet Doxygen::expandAsDefinedSet; // all macros that should be expanded
159MemberGroupInfoMap Doxygen::memberGroupInfoMap; // dictionary of the member groups heading
160std::unique_ptr<PageDef> Doxygen::mainPage;
161std::unique_ptr<NamespaceDef> Doxygen::globalNamespaceDef;
181std::mutex Doxygen::addExampleMutex;
183
184// locally accessible globals
185static std::multimap< std::string, const Entry* > g_classEntries;
187static OutputList *g_outputList = nullptr; // list of output generating objects
188static StringSet g_usingDeclarations; // used classes
189static bool g_successfulRun = FALSE;
190static bool g_dumpSymbolMap = FALSE;
192static bool g_singleComment=false;
193
194
195
196// keywords recognized as compounds
198{ "template class", "template struct", "class", "struct", "union", "interface", "exception" };
199
201{
202 g_inputFiles.clear();
203 //g_excludeNameDict.clear();
204 //delete g_outputList; g_outputList=nullptr;
205
210 Doxygen::pageLinkedMap->clear();
223 Doxygen::mainPage.reset();
225}
226
228{
229 public:
231 void begin(const char *name)
232 {
233 msg("{}", name);
234 stats.emplace_back(name,0);
235 startTime = std::chrono::steady_clock::now();
236 }
237 void end()
238 {
239 std::chrono::steady_clock::time_point endTime = std::chrono::steady_clock::now();
240 stats.back().elapsed = static_cast<double>(std::chrono::duration_cast<
241 std::chrono::microseconds>(endTime - startTime).count())/1000000.0;
242 warn_flush();
243 }
244 void print()
245 {
246 bool restore=FALSE;
248 {
250 restore=TRUE;
251 }
252 msg("----------------------\n");
253 for (const auto &s : stats)
254 {
255 msg("Spent {:.6f} seconds in {}",s.elapsed,s.name);
256 }
257 if (restore) Debug::setFlag(Debug::Time);
258 }
259 private:
260 struct stat
261 {
262 const char *name;
263 double elapsed;
264 //stat() : name(nullptr),elapsed(0) {}
265 stat(const char *n, double el) : name(n),elapsed(el) {}
266 };
267 std::vector<stat> stats;
268 std::chrono::steady_clock::time_point startTime;
270
271
272static void addMemberDocs(const Entry *root,MemberDefMutable *md, const QCString &funcDecl,
273 const ArgumentList *al,bool over_load,TypeSpecifier spec);
274static void findMember(const Entry *root,
275 const QCString &relates,
276 const QCString &type,
277 const QCString &args,
278 QCString funcDecl,
279 bool overloaded,
280 bool isFunc
281 );
282
289
290
291static bool findClassRelation(
292 const Entry *root,
293 Definition *context,
294 ClassDefMutable *cd,
295 const BaseInfo *bi,
296 const TemplateNameMap &templateNames,
297 /*bool insertUndocumented*/
299 bool isArtificial
300 );
301
302//----------------------------------------------------------------------------
303
305 FileDef *fileScope,const TagInfo *tagInfo);
306static void resolveTemplateInstanceInType(const Entry *root,const Definition *scope,const MemberDef *md);
307
308static void addPageToContext(PageDef *pd,Entry *root)
309{
310 if (root->parent()) // add the page to it's scope
311 {
312 QCString scope = root->parent()->name;
313 if (root->parent()->section.isPackageDoc())
314 {
315 scope=substitute(scope,".","::");
316 }
317 scope = stripAnonymousNamespaceScope(scope);
318 scope+="::"+pd->name();
320 if (d)
321 {
322 pd->setPageScope(d);
323 }
324 }
325}
326
327static void addRelatedPage(Entry *root)
328{
329 GroupDef *gd=nullptr;
330 for (const Grouping &g : root->groups)
331 {
332 if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname))) break;
333 }
334 //printf("---> addRelatedPage() %s gd=%p\n",qPrint(root->name),gd);
335 QCString doc=root->doc+root->inbodyDocs;
336
337 PageDef *pd = addRelatedPage(root->name, // name
338 root->args, // ptitle
339 doc, // doc
340 root->docFile, // fileName
341 root->docLine, // docLine
342 root->startLine, // startLine
343 root->sli, // sli
344 gd, // gd
345 root->tagInfo(), // tagInfo
346 FALSE, // xref
347 root->lang // lang
348 );
349 if (pd)
350 {
351 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
353 pd->setLocalToc(root->localToc);
354 addPageToContext(pd,root);
355 }
356}
357
358static void buildGroupListFiltered(const Entry *root,bool additional, bool includeExternal)
359{
360 if (root->section.isGroupDoc() && !root->name.isEmpty() &&
361 ((!includeExternal && root->tagInfo()==nullptr) ||
362 ( includeExternal && root->tagInfo()!=nullptr))
363 )
364 {
365 AUTO_TRACE("additional={} includeExternal={}",additional,includeExternal);
366 if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
367 (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
368 {
369 GroupDef *gd = Doxygen::groupLinkedMap->find(root->name);
370 AUTO_TRACE_ADD("Processing group '{}':'{}' gd={}", root->type,root->name,(void*)gd);
371
372 if (gd)
373 {
374 if ( !gd->hasGroupTitle() )
375 {
376 gd->setGroupTitle( root->type );
377 }
378 else if ( root->type.length() > 0 && root->name != root->type && gd->groupTitle() != root->type )
379 {
380 warn( root->fileName,root->startLine,
381 "group {}: ignoring title \"{}\" that does not match old title \"{}\"",
382 root->name, root->type, gd->groupTitle() );
383 }
384 gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
385 gd->setDocumentation( root->doc, root->docFile, root->docLine );
386 gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
388 gd->setRefItems(root->sli);
390 gd->setLanguage(root->lang);
392 {
393 root->commandOverrides.apply_groupGraph([&](bool b) { gd->overrideGroupGraph(b); });
394 }
395 }
396 else
397 {
398 if (root->tagInfo())
399 {
400 gd = Doxygen::groupLinkedMap->add(root->name,
401 std::unique_ptr<GroupDef>(
402 createGroupDef(root->fileName,root->startLine,root->name,root->type,root->tagInfo()->fileName)));
403 gd->setReference(root->tagInfo()->tagName);
404 }
405 else
406 {
407 gd = Doxygen::groupLinkedMap->add(root->name,
408 std::unique_ptr<GroupDef>(
409 createGroupDef(root->fileName,root->startLine,root->name,root->type)));
410 }
411 gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
412 // allow empty docs for group
413 gd->setDocumentation(!root->doc.isEmpty() ? root->doc : QCString(" "),root->docFile,root->docLine,FALSE);
414 gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
416 gd->setRefItems(root->sli);
418 gd->setLanguage(root->lang);
420 {
421 root->commandOverrides.apply_groupGraph([&](bool b) { gd->overrideGroupGraph(b); });
422 }
423 }
424 }
425 }
426 for (const auto &e : root->children()) buildGroupListFiltered(e.get(),additional,includeExternal);
427}
428
429static void buildGroupList(const Entry *root)
430{
431 // --- first process only local groups
432 // first process the @defgroups blocks
434 // then process the @addtogroup, @weakgroup blocks
436
437 // --- then also process external groups
438 // first process the @defgroups blocks
440 // then process the @addtogroup, @weakgroup blocks
442}
443
444static void findGroupScope(const Entry *root)
445{
446 if (root->section.isGroupDoc() && !root->name.isEmpty() &&
447 root->parent() && !root->parent()->name.isEmpty())
448 {
449 GroupDef *gd = Doxygen::groupLinkedMap->find(root->name);
450 if (gd)
451 {
452 QCString scope = root->parent()->name;
453 if (root->parent()->section.isPackageDoc())
454 {
455 scope=substitute(scope,".","::");
456 }
457 scope = stripAnonymousNamespaceScope(scope);
458 scope+="::"+gd->name();
460 if (d)
461 {
462 gd->setGroupScope(d);
463 }
464 }
465 }
466 for (const auto &e : root->children()) findGroupScope(e.get());
467}
468
469static void organizeSubGroupsFiltered(const Entry *root,bool additional)
470{
471 if (root->section.isGroupDoc() && !root->name.isEmpty())
472 {
473 AUTO_TRACE("additional={}",additional);
474 if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
475 (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
476 {
477 GroupDef *gd = Doxygen::groupLinkedMap->find(root->name);
478 if (gd)
479 {
480 AUTO_TRACE_ADD("adding {} to group {}",root->name,gd->name());
481 addGroupToGroups(root,gd);
482 }
483 }
484 }
485 for (const auto &e : root->children()) organizeSubGroupsFiltered(e.get(),additional);
486}
487
488static void organizeSubGroups(const Entry *root)
489{
490 //printf("Defining groups\n");
491 // first process the @defgroups blocks
493 //printf("Additional groups\n");
494 // then process the @addtogroup, @weakgroup blocks
496}
497
498//----------------------------------------------------------------------
499
500static void buildFileList(const Entry *root)
501{
502 if ((root->section.isFileDoc() || (root->section.isFile() && Config_getBool(EXTRACT_ALL))) &&
503 !root->name.isEmpty() && !root->tagInfo() // skip any file coming from tag files
504 )
505 {
506 bool ambig = false;
508 if (!fd || ambig)
509 {
510 bool save_ambig = ambig;
511 // use the directory of the file to see if the described file is in the same
512 // directory as the describing file.
513 QCString fn = root->fileName;
514 int newIndex=fn.findRev('/');
515 if (newIndex<0)
516 {
517 fn = root->name;
518 }
519 else
520 {
521 fn = fn.left(newIndex)+"/"+root->name;
522 }
524 if (!fd) ambig = save_ambig;
525 }
526 //printf("**************** root->name=%s fd=%p\n",qPrint(root->name),(void*)fd);
527 if (fd && !ambig)
528 {
529 //printf("Adding documentation!\n");
530 // using FALSE in setDocumentation is small hack to make sure a file
531 // is documented even if a \file command is used without further
532 // documentation
533 fd->setDocumentation(root->doc,root->docFile,root->docLine,FALSE);
534 fd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
536 fd->setRefItems(root->sli);
538 root->commandOverrides.apply_includeGraph ([&](bool b) { fd->overrideIncludeGraph(b); });
539 root->commandOverrides.apply_includedByGraph([&](bool b) { fd->overrideIncludedByGraph(b); });
540 for (const Grouping &g : root->groups)
541 {
542 GroupDef *gd=nullptr;
543 if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname)))
544 {
545 if (!gd->containsFile(fd))
546 {
547 gd->addFile(fd);
548 fd->makePartOfGroup(gd);
549 //printf("File %s: in group %s\n",qPrint(fd->name()),qPrint(gd->name()));
550 }
551 }
552 else if (!gd && g.pri == Grouping::GROUPING_INGROUP)
553 {
554 warn(root->fileName, root->startLine,
555 "Found non-existing group '{}' for the command '{}', ignoring command",
557 );
558 }
559 }
560 }
561 else
562 {
564 text.sprintf("the name '%s' supplied as "
565 "the argument in the \\file statement ",
566 qPrint(root->name));
567 if (ambig) // name is ambiguous
568 {
569 text+="matches the following input files:\n";
571 text+="\n";
572 text+="Please use a more specific name by "
573 "including a (larger) part of the path!";
574 }
575 else // name is not an input file
576 {
577 text+="is not an input file";
578 }
579 warn(root->fileName,root->startLine,"{}", text);
580 }
581 }
582 for (const auto &e : root->children()) buildFileList(e.get());
583}
584
585template<class DefMutable>
586static void addIncludeFile(DefMutable *cd,FileDef *ifd,const Entry *root)
587{
588 if (
589 (!root->doc.stripWhiteSpace().isEmpty() ||
590 !root->brief.stripWhiteSpace().isEmpty() ||
591 Config_getBool(EXTRACT_ALL)
592 ) && root->protection!=Protection::Private
593 )
594 {
595 //printf(">>>>>> includeFile=%s\n",qPrint(root->includeFile));
596
597 bool local=Config_getBool(FORCE_LOCAL_INCLUDES);
598 QCString includeFile = root->includeFile;
599 if (!includeFile.isEmpty() && includeFile.at(0)=='"')
600 {
601 local = TRUE;
602 includeFile=includeFile.mid(1,includeFile.length()-2);
603 }
604 else if (!includeFile.isEmpty() && includeFile.at(0)=='<')
605 {
606 local = FALSE;
607 includeFile=includeFile.mid(1,includeFile.length()-2);
608 }
609
610 bool ambig = false;
611 FileDef *fd=nullptr;
612 // see if we need to include a verbatim copy of the header file
613 //printf("root->includeFile=%s\n",qPrint(root->includeFile));
614 if (!includeFile.isEmpty() &&
615 (fd=findFileDef(Doxygen::inputNameLinkedMap,includeFile,ambig))==nullptr
616 )
617 { // explicit request
618 QCString text;
619 text.sprintf("the name '%s' supplied as "
620 "the argument of the \\class, \\struct, \\union, or \\include command ",
621 qPrint(includeFile)
622 );
623 if (ambig) // name is ambiguous
624 {
625 text+="matches the following input files:\n";
627 text+="\n";
628 text+="Please use a more specific name by "
629 "including a (larger) part of the path!";
630 }
631 else // name is not an input file
632 {
633 text+="is not an input file";
634 }
635 warn(root->fileName,root->startLine, "{}", text);
636 }
637 else if (includeFile.isEmpty() && ifd &&
638 // see if the file extension makes sense
639 guessSection(ifd->name()).isHeader())
640 { // implicit assumption
641 fd=ifd;
642 }
643
644 // if a file is found, we mark it as a source file.
645 if (fd)
646 {
647 QCString iName = !root->includeName.isEmpty() ?
648 root->includeName : includeFile;
649 if (!iName.isEmpty()) // user specified include file
650 {
651 if (iName.at(0)=='<') local=FALSE; // explicit override
652 else if (iName.at(0)=='"') local=TRUE;
653 if (iName.at(0)=='"' || iName.at(0)=='<')
654 {
655 iName=iName.mid(1,iName.length()-2); // strip quotes or brackets
656 }
657 if (iName.isEmpty())
658 {
659 iName=fd->name();
660 }
661 }
662 else if (!Config_getList(STRIP_FROM_INC_PATH).empty())
663 {
665 }
666 else // use name of the file containing the class definition
667 {
668 iName=fd->name();
669 }
670 if (fd->generateSourceFile()) // generate code for header
671 {
672 cd->setIncludeFile(fd,iName,local,!root->includeName.isEmpty());
673 }
674 else // put #include in the class documentation without link
675 {
676 cd->setIncludeFile(nullptr,iName,local,TRUE);
677 }
678 }
679 }
680}
681
682
684{
685 size_t l = s.length();
686 int count=0;
687 int round=0;
688 QCString result;
689 for (size_t i=0;i<l;i++)
690 {
691 char c=s.at(i);
692 if (c=='(') round++;
693 else if (c==')' && round>0) round--;
694 else if (c=='<' && round==0) count++;
695 if (count==0)
696 {
697 result+=c;
698 }
699 if (c=='>' && round==0 && count>0) count--;
700 }
701 //printf("stripTemplateSpecifiers(%s)=%s\n",qPrint(s),qPrint(result));
702 return result;
703}
704
705/*! returns the Definition object belonging to the first \a level levels of
706 * full qualified name \a name. Creates an artificial scope if the scope is
707 * not found and set the parent/child scope relation if the scope is found.
708 */
709[[maybe_unused]]
710static Definition *buildScopeFromQualifiedName(const QCString &name_,SrcLangExt lang,const TagInfo *tagInfo)
711{
712 QCString name = stripTemplateSpecifiers(name_);
713 name.stripPrefix("::");
714 int level = name.contains("::");
715 //printf("buildScopeFromQualifiedName(%s) level=%d\n",qPrint(name),level);
716 int i=0, p=0, l=0;
718 QCString fullScope;
719 while (i<level)
720 {
721 int idx=getScopeFragment(name,p,&l);
722 if (idx==-1) return prevScope;
723 QCString nsName = name.mid(idx,l);
724 if (nsName.isEmpty()) return prevScope;
725 if (!fullScope.isEmpty()) fullScope+="::";
726 fullScope+=nsName;
727 NamespaceDef *nd=Doxygen::namespaceLinkedMap->find(fullScope);
728 DefinitionMutable *innerScope = toDefinitionMutable(nd);
729 ClassDef *cd=nullptr;
730 if (nd==nullptr) cd = getClass(fullScope);
731 if (nd==nullptr && cd) // scope is a class
732 {
733 innerScope = toDefinitionMutable(cd);
734 }
735 else if (nd==nullptr && cd==nullptr && fullScope.find('<')==-1) // scope is not known and could be a namespace!
736 {
737 // introduce bogus namespace
738 //printf("++ adding dummy namespace %s to %s tagInfo=%p\n",qPrint(nsName),qPrint(prevScope->name()),(void*)tagInfo);
739 NamespaceDefMutable *newNd=
741 Doxygen::namespaceLinkedMap->add(fullScope,
743 "[generated]",1,1,fullScope,
744 tagInfo?tagInfo->tagName:QCString(),
745 tagInfo?tagInfo->fileName:QCString())));
746 if (newNd)
747 {
748 newNd->setLanguage(lang);
749 newNd->setArtificial(TRUE);
750 // add namespace to the list
751 innerScope = newNd;
752 }
753 }
754 else // scope is a namespace
755 {
756 }
757 if (innerScope)
758 {
759 // make the parent/child scope relation
760 DefinitionMutable *prevScopeMutable = toDefinitionMutable(prevScope);
761 if (prevScopeMutable)
762 {
763 prevScopeMutable->addInnerCompound(toDefinition(innerScope));
764 }
765 innerScope->setOuterScope(prevScope);
766 }
767 else // current scope is a class, so return only the namespace part...
768 {
769 return prevScope;
770 }
771 // proceed to the next scope fragment
772 p=idx+l+2;
773 prevScope=toDefinition(innerScope);
774 i++;
775 }
776 return prevScope;
777}
778
780 FileDef *fileScope,const TagInfo *tagInfo)
781{
782 //printf("<findScopeFromQualifiedName(%s,%s)\n",startScope ? qPrint(startScope->name()) : 0, qPrint(n));
783 Definition *resultScope=toDefinition(startScope);
784 if (resultScope==nullptr) resultScope=Doxygen::globalScope;
786 int l1 = 0;
787 int i1 = getScopeFragment(scope,0,&l1);
788 if (i1==-1)
789 {
790 //printf(">no fragments!\n");
791 return resultScope;
792 }
793 int p=i1+l1,l2=0,i2=0;
794 while ((i2=getScopeFragment(scope,p,&l2))!=-1)
795 {
796 QCString nestedNameSpecifier = scope.mid(i1,l1);
797 Definition *orgScope = resultScope;
798 //printf(" nestedNameSpecifier=%s\n",qPrint(nestedNameSpecifier));
799 resultScope = const_cast<Definition*>(resultScope->findInnerCompound(nestedNameSpecifier));
800 //printf(" resultScope=%p\n",resultScope);
801 if (resultScope==nullptr)
802 {
803 if (orgScope==Doxygen::globalScope && fileScope && !fileScope->getUsedNamespaces().empty())
804 // also search for used namespaces
805 {
806 for (const auto &nd : fileScope->getUsedNamespaces())
807 {
809 if (mnd)
810 {
811 resultScope = findScopeFromQualifiedName(toNamespaceDefMutable(nd),n,fileScope,tagInfo);
812 if (resultScope!=nullptr) break;
813 }
814 }
815 if (resultScope)
816 {
817 // for a nested class A::I in used namespace N, we get
818 // N::A::I while looking for A, so we should compare
819 // resultScope->name() against scope.left(i2+l2)
820 //printf(" -> result=%s scope=%s\n",qPrint(resultScope->name()),qPrint(scope));
821 if (rightScopeMatch(resultScope->name(),scope.left(i2+l2)))
822 {
823 break;
824 }
825 goto nextFragment;
826 }
827 }
828
829 // also search for used classes. Complication: we haven't been able
830 // to put them in the right scope yet, because we are still resolving
831 // the scope relations!
832 // Therefore loop through all used classes and see if there is a right
833 // scope match between the used class and nestedNameSpecifier.
834 for (const auto &usedName : g_usingDeclarations)
835 {
836 //printf("Checking using class %s\n",qPrint(usedName));
837 if (rightScopeMatch(usedName,nestedNameSpecifier))
838 {
839 // ui.currentKey() is the fully qualified name of nestedNameSpecifier
840 // so use this instead.
841 QCString fqn = usedName + scope.right(scope.length()-p);
842 resultScope = buildScopeFromQualifiedName(fqn,startScope->getLanguage(),nullptr);
843 //printf("Creating scope from fqn=%s result %p\n",qPrint(fqn),resultScope);
844 if (resultScope)
845 {
846 //printf("> Match! resultScope=%s\n",qPrint(resultScope->name()));
847 return resultScope;
848 }
849 }
850 }
851
852 //printf("> name %s not found in scope %s\n",qPrint(nestedNameSpecifier),qPrint(orgScope->name()));
853 return nullptr;
854 }
855 nextFragment:
856 i1=i2;
857 l1=l2;
858 p=i2+l2;
859 }
860 //printf(">findScopeFromQualifiedName scope %s\n",qPrint(resultScope->name()));
861 return resultScope;
862}
863
864std::unique_ptr<ArgumentList> getTemplateArgumentsFromName(
865 const QCString &name,
866 const ArgumentLists &tArgLists)
867{
868 // for each scope fragment, check if it is a template and advance through
869 // the list if so.
870 int i=0, p=0;
871 auto alIt = tArgLists.begin();
872 while ((i=name.find("::",p))!=-1 && alIt!=tArgLists.end())
873 {
874 NamespaceDef *nd = Doxygen::namespaceLinkedMap->find(name.left(i));
875 if (nd==nullptr)
876 {
877 ClassDef *cd = getClass(name.left(i));
878 if (cd)
879 {
880 if (!cd->templateArguments().empty())
881 {
882 ++alIt;
883 }
884 }
885 }
886 p=i+2;
887 }
888 return alIt!=tArgLists.end() ?
889 std::make_unique<ArgumentList>(*alIt) :
890 std::unique_ptr<ArgumentList>();
891}
892
893static
895{
897
898 if (specifier.isStruct())
900 else if (specifier.isUnion())
901 sec=ClassDef::Union;
902 else if (specifier.isCategory())
904 else if (specifier.isInterface())
906 else if (specifier.isProtocol())
908 else if (specifier.isException())
910 else if (specifier.isService())
912 else if (specifier.isSingleton())
914
915 if (section.isUnionDoc())
916 sec=ClassDef::Union;
917 else if (section.isStructDoc())
919 else if (section.isInterfaceDoc())
921 else if (section.isProtocolDoc())
923 else if (section.isCategoryDoc())
925 else if (section.isExceptionDoc())
927 else if (section.isServiceDoc())
929 else if (section.isSingletonDoc())
931
932 return sec;
933}
934
935
936static void addClassToContext(const Entry *root)
937{
938 AUTO_TRACE("name={}",root->name);
939 FileDef *fd = root->fileDef();
940
941 QCString scName;
942 if (root->parent()->section.isScope())
943 {
944 scName=root->parent()->name;
945 }
946 // name without parent's scope
947 QCString fullName = root->name;
948
949 // strip off any template parameters (but not those for specializations)
950 int idx=fullName.find('>');
951 if (idx!=-1 && root->lang==SrcLangExt::CSharp) // mangle A<S,T>::N as A-2-g::N
952 {
953 fullName = mangleCSharpGenericName(fullName.left(idx+1))+fullName.mid(idx+1);
954 }
955 fullName=stripTemplateSpecifiersFromScope(fullName);
956
957 // name with scope (if not present already)
958 QCString qualifiedName = fullName;
959 if (!scName.isEmpty() && !leftScopeMatch(scName,fullName))
960 {
961 qualifiedName.prepend(scName+"::");
962 }
963
964 // see if we already found the class before
965 ClassDefMutable *cd = getClassMutable(qualifiedName);
966
967 AUTO_TRACE_ADD("Found class with name '{}', qualifiedName '{}'", cd ? cd->name() : root->name, qualifiedName);
968
969 if (cd)
970 {
971 fullName=cd->name();
972 AUTO_TRACE_ADD("Existing class '{}'",cd->name());
973 //if (cd->templateArguments()==0)
974 //{
975 // //printf("existing ClassDef tempArgList=%p specScope=%s\n",root->tArgList,qPrint(root->scopeSpec));
976 // cd->setTemplateArguments(tArgList);
977 //}
978
979 cd->setDocumentation(root->doc,root->docFile,root->docLine);
980 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
981 root->commandOverrides.apply_collaborationGraph([&](bool b ) { cd->overrideCollaborationGraph(b); });
982 root->commandOverrides.apply_inheritanceGraph ([&](CLASS_GRAPH_t gt) { cd->overrideInheritanceGraph(gt); });
983
984 if (!root->spec.isForwardDecl() && cd->isForwardDeclared())
985 {
986 cd->setDefFile(root->fileName,root->startLine,root->startColumn);
987 if (root->bodyLine!=-1)
988 {
989 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
990 cd->setBodyDef(fd);
991 }
992 }
993
994 if (cd->templateArguments().empty() || (cd->isForwardDeclared() && !root->spec.isForwardDecl()))
995 {
996 // this happens if a template class declared with @class is found
997 // before the actual definition or if a forward declaration has different template
998 // parameter names.
999 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(cd->name(),root->tArgLists);
1000 if (tArgList)
1001 {
1002 cd->setTemplateArguments(*tArgList);
1003 }
1004 }
1005 if (cd->requiresClause().isEmpty() && !root->req.isEmpty())
1006 {
1007 cd->setRequiresClause(root->req);
1008 }
1009
1011
1012 cd->setMetaData(root->metaData);
1013 }
1014 else // new class
1015 {
1017
1018 QCString className;
1019 QCString namespaceName;
1020 extractNamespaceName(fullName,className,namespaceName);
1021
1022 AUTO_TRACE_ADD("New class: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1023 fullName, namespaceName, className, Trace::trunc(root->brief), Trace::trunc(root->doc));
1024
1025 QCString tagName;
1026 QCString refFileName;
1027 const TagInfo *tagInfo = root->tagInfo();
1028 if (tagInfo)
1029 {
1030 tagName = tagInfo->tagName;
1031 refFileName = tagInfo->fileName;
1032 if (fullName.find("::")!=-1)
1033 // symbols imported via tag files may come without the parent scope,
1034 // so we artificially create it here
1035 {
1036 buildScopeFromQualifiedName(fullName,root->lang,tagInfo);
1037 }
1038 }
1039 std::unique_ptr<ArgumentList> tArgList;
1040 int i=0;
1041 if ((root->lang==SrcLangExt::CSharp || root->lang==SrcLangExt::Java) &&
1042 (i=fullName.find('<'))!=-1)
1043 {
1044 // a Java/C# generic class looks like a C++ specialization, so we need to split the
1045 // name and template arguments here
1046 tArgList = stringToArgumentList(root->lang,fullName.mid(i));
1047 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
1048 // A -> A
1049 // A<T> -> A-1-g
1050 // A<T,S> -> A-2-g
1051 {
1052 fullName=mangleCSharpGenericName(fullName);
1053 }
1054 else
1055 {
1056 fullName=fullName.left(i);
1057 }
1058 }
1059 else
1060 {
1061 tArgList = getTemplateArgumentsFromName(fullName,root->tArgLists);
1062 }
1063 // add class to the list
1064 cd = toClassDefMutable(
1065 Doxygen::classLinkedMap->add(fullName,
1066 createClassDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1067 fullName,sec,tagName,refFileName,TRUE,root->spec.isEnum()) ));
1068 if (cd)
1069 {
1070 AUTO_TRACE_ADD("New class '{}' type={} #tArgLists={} tagInfo={} hidden={} artificial={}",
1071 fullName,cd->compoundTypeString(),root->tArgLists.size(),
1072 fmt::ptr(tagInfo),root->hidden,root->artificial);
1073 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1074 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1075 cd->setLanguage(root->lang);
1076 cd->setId(root->id);
1077 cd->setHidden(root->hidden);
1078 cd->setArtificial(root->artificial);
1079 cd->setClassSpecifier(root->spec);
1080 if (root->lang==SrcLangExt::CSharp && !root->args.isEmpty())
1081 {
1083 }
1084 cd->addQualifiers(root->qualifiers);
1085 cd->setTypeConstraints(root->typeConstr);
1086 root->commandOverrides.apply_collaborationGraph([&](bool b ) { cd->overrideCollaborationGraph(b); });
1087 root->commandOverrides.apply_inheritanceGraph ([&](CLASS_GRAPH_t gt) { cd->overrideInheritanceGraph(gt); });
1088
1089 if (tArgList)
1090 {
1091 cd->setTemplateArguments(*tArgList);
1092 }
1093 cd->setRequiresClause(root->req);
1094 cd->setProtection(root->protection);
1095 cd->setIsStatic(root->isStatic);
1096
1097 // file definition containing the class cd
1098 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1099 cd->setBodyDef(fd);
1100
1101 cd->setMetaData(root->metaData);
1102
1103 cd->insertUsedFile(fd);
1104 }
1105 else
1106 {
1107 AUTO_TRACE_ADD("Class {} not added, already exists as alias", fullName);
1108 }
1109 }
1110
1111 if (cd)
1112 {
1114 if (!root->subGrouping) cd->setSubGrouping(FALSE);
1115 if (!root->spec.isForwardDecl())
1116 {
1117 if (cd->hasDocumentation())
1118 {
1119 addIncludeFile(cd,fd,root);
1120 }
1121 if (fd && root->section.isCompound())
1122 {
1123 AUTO_TRACE_ADD("Inserting class {} in file {} (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1124 cd->setFileDef(fd);
1125 fd->insertClass(cd);
1126 }
1127 }
1128 addClassToGroups(root,cd);
1130 cd->setRefItems(root->sli);
1131 cd->setRequirementReferences(root->rqli);
1132 }
1133}
1134
1135//----------------------------------------------------------------------
1136// build a list of all classes mentioned in the documentation
1137// and all classes that have a documentation block before their definition.
1138static void buildClassList(const Entry *root)
1139{
1140 if ((root->section.isCompound() || root->section.isObjcImpl()) && !root->name.isEmpty())
1141 {
1142 AUTO_TRACE();
1143 addClassToContext(root);
1144 }
1145 for (const auto &e : root->children()) buildClassList(e.get());
1146}
1147
1148static void buildClassDocList(const Entry *root)
1149{
1150 if ((root->section.isCompoundDoc()) && !root->name.isEmpty())
1151 {
1152 AUTO_TRACE();
1153 addClassToContext(root);
1154 }
1155 for (const auto &e : root->children()) buildClassDocList(e.get());
1156}
1157
1158//----------------------------------------------------------------------
1159// build a list of all classes mentioned in the documentation
1160// and all classes that have a documentation block before their definition.
1161
1162static void addConceptToContext(const Entry *root)
1163{
1164 AUTO_TRACE();
1165 FileDef *fd = root->fileDef();
1166
1167 QCString scName;
1168 if (root->parent()->section.isScope())
1169 {
1170 scName=root->parent()->name;
1171 }
1172
1173 // name with scope (if not present already)
1174 QCString qualifiedName = root->name;
1175 if (!scName.isEmpty() && !leftScopeMatch(qualifiedName,scName))
1176 {
1177 qualifiedName.prepend(scName+"::");
1178 }
1179
1180 // see if we already found the concept before
1181 ConceptDefMutable *cd = getConceptMutable(qualifiedName);
1182
1183 AUTO_TRACE_ADD("Found concept with name '{}' (qualifiedName='{}')", cd ? cd->name() : root->name, qualifiedName);
1184
1185 if (cd)
1186 {
1187 qualifiedName=cd->name();
1188 AUTO_TRACE_ADD("Existing concept '{}'",cd->name());
1189
1190 cd->setDocumentation(root->doc,root->docFile,root->docLine);
1191 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1192
1193 addIncludeFile(cd,fd,root);
1194 }
1195 else // new concept
1196 {
1197 QCString className;
1198 QCString namespaceName;
1199 extractNamespaceName(qualifiedName,className,namespaceName);
1200
1201 AUTO_TRACE_ADD("New concept: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1202 qualifiedName,namespaceName,className,root->brief,root->doc);
1203
1204 QCString tagName;
1205 QCString refFileName;
1206 const TagInfo *tagInfo = root->tagInfo();
1207 if (tagInfo)
1208 {
1209 tagName = tagInfo->tagName;
1210 refFileName = tagInfo->fileName;
1211 if (qualifiedName.find("::")!=-1)
1212 // symbols imported via tag files may come without the parent scope,
1213 // so we artificially create it here
1214 {
1215 buildScopeFromQualifiedName(qualifiedName,root->lang,tagInfo);
1216 }
1217 }
1218 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(qualifiedName,root->tArgLists);
1219 // add concept to the list
1221 Doxygen::conceptLinkedMap->add(qualifiedName,
1222 createConceptDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1223 qualifiedName,tagName,refFileName)));
1224 if (cd)
1225 {
1226 AUTO_TRACE_ADD("New concept '{}' #tArgLists={} tagInfo={}",
1227 qualifiedName,root->tArgLists.size(),fmt::ptr(tagInfo));
1228 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1229 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1230 cd->setLanguage(root->lang);
1231 cd->setId(root->id);
1232 cd->setHidden(root->hidden);
1233 cd->setGroupId(root->mGrpId);
1234 if (tArgList)
1235 {
1236 cd->setTemplateArguments(*tArgList);
1237 }
1238 cd->setInitializer(root->initializer.str());
1239 // file definition containing the class cd
1240 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1241 cd->setBodyDef(fd);
1243 cd->setRefItems(root->sli);
1244 cd->setRequirementReferences(root->rqli);
1245 addIncludeFile(cd,fd,root);
1246
1247 // also add namespace to the correct structural context
1248 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,qualifiedName,nullptr,tagInfo);
1250 {
1252 if (dm)
1253 {
1254 dm->addInnerCompound(cd);
1255 }
1256 cd->setOuterScope(d);
1257 }
1258 for (const auto &ce : root->children())
1259 {
1260 //printf("Concept %s has child %s\n",qPrint(root->name),qPrint(ce->section.to_string()));
1261 if (ce->section.isConceptDocPart())
1262 {
1263 cd->addSectionsToDefinition(ce->anchors);
1264 cd->setRefItems(ce->sli);
1265 cd->setRequirementReferences(ce->rqli);
1266 if (!ce->brief.isEmpty())
1267 {
1268 cd->addDocPart(ce->brief,ce->startLine,ce->startColumn);
1269 //printf(" brief=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->brief),ce->startLine,ce->startColumn);
1270 }
1271 if (!ce->doc.isEmpty())
1272 {
1273 cd->addDocPart(ce->doc,ce->startLine,ce->startColumn);
1274 //printf(" doc=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->doc),ce->startLine,ce->startColumn);
1275 }
1276 }
1277 else if (ce->section.isConceptCodePart())
1278 {
1279 cd->addCodePart(ce->initializer.str(),ce->startLine,ce->startColumn);
1280 //printf(" code=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->initializer.str()),ce->startLine,ce->startColumn);
1281 }
1282 }
1283 }
1284 else
1285 {
1286 AUTO_TRACE_ADD("Concept '{}' not added, already exists (as alias)", qualifiedName);
1287 }
1288 }
1289
1290 if (cd)
1291 {
1293 for (const auto &ce : root->children())
1294 {
1295 if (ce->section.isConceptDocPart())
1296 {
1297 cd->addSectionsToDefinition(ce->anchors);
1298 }
1299 }
1300 if (fd)
1301 {
1302 AUTO_TRACE_ADD("Inserting concept '{}' in file '{}' (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1303 cd->setFileDef(fd);
1304 fd->insertConcept(cd);
1305 }
1306 addConceptToGroups(root,cd);
1308 cd->setRefItems(root->sli);
1309 cd->setRequirementReferences(root->rqli);
1310 }
1311}
1312
1313static void findModuleDocumentation(const Entry *root)
1314{
1315 if (root->section.isModuleDoc())
1316 {
1317 AUTO_TRACE();
1319 }
1320 for (const auto &e : root->children()) findModuleDocumentation(e.get());
1321}
1322
1323static void buildConceptList(const Entry *root)
1324{
1325 if (root->section.isConcept())
1326 {
1327 AUTO_TRACE();
1328 addConceptToContext(root);
1329 }
1330 for (const auto &e : root->children()) buildConceptList(e.get());
1331}
1332
1333static void buildConceptDocList(const Entry *root)
1334{
1335 if (root->section.isConceptDoc())
1336 {
1337 AUTO_TRACE();
1338 addConceptToContext(root);
1339 }
1340 for (const auto &e : root->children()) buildConceptDocList(e.get());
1341}
1342
1343// This routine is to allow @ingroup X @{ concept A; concept B; @} to work
1344// (same also works for variable and functions because of logic in MemberGroup::insertMember)
1346{
1347 AUTO_TRACE();
1348 for (const auto &cd : *Doxygen::conceptLinkedMap)
1349 {
1350 if (cd->groupId()!=DOX_NOGROUP)
1351 {
1352 for (const auto &ocd : *Doxygen::conceptLinkedMap)
1353 {
1354 if (cd!=ocd && cd->groupId()==ocd->groupId() &&
1355 !cd->partOfGroups().empty() && ocd->partOfGroups().empty())
1356 {
1357 ConceptDefMutable *ocdm = toConceptDefMutable(ocd.get());
1358 if (ocdm)
1359 {
1360 for (const auto &gd : cd->partOfGroups())
1361 {
1362 if (gd)
1363 {
1364 AUTO_TRACE_ADD("making concept '{}' part of group '{}'",ocdm->name(),gd->name());
1365 ocdm->makePartOfGroup(gd);
1366 gd->addConcept(ocd.get());
1367 }
1368 }
1369 }
1370 }
1371 }
1372 }
1373 }
1374}
1375
1376//----------------------------------------------------------------------
1377
1379{
1380 ClassDefSet visitedClasses;
1381
1382 bool done=FALSE;
1383 //int iteration=0;
1384 while (!done)
1385 {
1386 done=TRUE;
1387 //++iteration;
1388 struct ClassAlias
1389 {
1390 ClassAlias(const QCString &name,std::unique_ptr<ClassDef> cd,DefinitionMutable *ctx) :
1391 aliasFullName(name),aliasCd(std::move(cd)), aliasContext(ctx) {}
1392 QCString aliasFullName;
1393 std::unique_ptr<ClassDef> aliasCd;
1394 DefinitionMutable *aliasContext;
1395 };
1396 std::vector<ClassAlias> aliases;
1397 for (const auto &icd : *Doxygen::classLinkedMap)
1398 {
1399 ClassDefMutable *cd = toClassDefMutable(icd.get());
1400 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1401 {
1402 QCString name = stripAnonymousNamespaceScope(icd->name());
1403 //printf("processing=%s, iteration=%d\n",qPrint(cd->name()),iteration);
1404 // also add class to the correct structural context
1406 name,icd->getFileDef(),nullptr);
1407 if (d)
1408 {
1409 //printf("****** adding %s to scope %s in iteration %d\n",qPrint(cd->name()),qPrint(d->name()),iteration);
1411 if (dm)
1412 {
1413 dm->addInnerCompound(cd);
1414 }
1415 cd->setOuterScope(d);
1416
1417 // for inline namespace add an alias of the class to the outer scope
1419 {
1421 //printf("nd->isInline()=%d\n",nd->isInline());
1422 if (nd && nd->isInline())
1423 {
1424 d = d->getOuterScope();
1425 if (d)
1426 {
1427 dm = toDefinitionMutable(d);
1428 if (dm)
1429 {
1430 auto aliasCd = createClassDefAlias(d,cd);
1431 QCString aliasFullName = d->qualifiedName()+"::"+aliasCd->localName();
1432 aliases.emplace_back(aliasFullName,std::move(aliasCd),dm);
1433 //printf("adding %s to %s as %s\n",qPrint(aliasCd->name()),qPrint(d->name()),qPrint(aliasFullName));
1434 }
1435 }
1436 }
1437 else
1438 {
1439 break;
1440 }
1441 }
1442
1443 visitedClasses.insert(icd.get());
1444 done=FALSE;
1445 }
1446 //else
1447 //{
1448 // printf("****** ignoring %s: scope not (yet) found in iteration %d\n",qPrint(cd->name()),iteration);
1449 //}
1450 }
1451 }
1452 // add aliases
1453 for (auto &alias : aliases)
1454 {
1455 ClassDef *aliasCd = Doxygen::classLinkedMap->add(alias.aliasFullName,std::move(alias.aliasCd));
1456 if (aliasCd)
1457 {
1458 alias.aliasContext->addInnerCompound(aliasCd);
1459 }
1460 }
1461 }
1462
1463 //give warnings for unresolved compounds
1464 for (const auto &icd : *Doxygen::classLinkedMap)
1465 {
1466 ClassDefMutable *cd = toClassDefMutable(icd.get());
1467 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1468 {
1470 /// create the scope artificially
1471 // anyway, so we can at least relate scopes properly.
1472 Definition *d = buildScopeFromQualifiedName(name,cd->getLanguage(),nullptr);
1473 if (d && d!=cd && !cd->getDefFileName().isEmpty())
1474 // avoid recursion in case of redundant scopes, i.e: namespace N { class N::C {}; }
1475 // for this case doxygen assumes the existence of a namespace N::N in which C is to be found!
1476 // also avoid warning for stuff imported via a tagfile.
1477 {
1479 if (dm)
1480 {
1481 dm->addInnerCompound(cd);
1482 }
1483 cd->setOuterScope(d);
1484 warn(cd->getDefFileName(),cd->getDefLine(),
1485 "Incomplete input: scope for class {} not found!{}",name,
1486 name.startsWith("std::") ? " Try enabling BUILTIN_STL_SUPPORT." : ""
1487 );
1488 }
1489 }
1490 }
1491}
1492
1494{
1495 //bool inlineGroupedClasses = Config_getBool(INLINE_GROUPED_CLASSES);
1496 //if (!inlineGroupedClasses) return;
1497 //printf("** distributeClassGroupRelations()\n");
1498
1499 ClassDefSet visitedClasses;
1500 for (const auto &cd : *Doxygen::classLinkedMap)
1501 {
1502 //printf("Checking %s\n",qPrint(cd->name()));
1503 // distribute the group to nested classes as well
1504 if (visitedClasses.find(cd.get())==visitedClasses.end() && !cd->partOfGroups().empty())
1505 {
1506 //printf(" Candidate for merging\n");
1507 GroupDef *gd = cd->partOfGroups().front();
1508 for (auto &ncd : cd->getClasses())
1509 {
1511 if (ncdm && ncdm->partOfGroups().empty())
1512 {
1513 //printf(" Adding %s to group '%s'\n",qPrint(ncd->name()),
1514 // gd->groupTitle());
1515 ncdm->makePartOfGroup(gd);
1516 gd->addClass(ncdm);
1517 }
1518 }
1519 visitedClasses.insert(cd.get()); // only visit every class once
1520 }
1521 }
1522}
1523
1524//----------------------------
1525
1526static ClassDefMutable *createTagLessInstance(const ClassDef *rootCd,const ClassDef *templ,const QCString &fieldName)
1527{
1528 QCString fullName = removeAnonymousScopes(templ->name());
1529 if (fullName.endsWith("::")) fullName=fullName.left(fullName.length()-2);
1530 fullName+="."+fieldName;
1531
1532 //printf("** adding class %s based on %s\n",qPrint(fullName),qPrint(templ->name()));
1534 Doxygen::classLinkedMap->add(fullName,
1536 templ->getDefLine(),
1537 templ->getDefColumn(),
1538 fullName,
1539 templ->compoundType())));
1540 if (cd)
1541 {
1542 cd->setDocumentation(templ->documentation(),templ->docFile(),templ->docLine()); // copy docs to definition
1543 cd->setBriefDescription(templ->briefDescription(),templ->briefFile(),templ->briefLine());
1544 cd->setLanguage(templ->getLanguage());
1545 cd->setBodySegment(templ->getDefLine(),templ->getStartBodyLine(),templ->getEndBodyLine());
1546 cd->setBodyDef(templ->getBodyDef());
1547
1548 cd->setOuterScope(rootCd->getOuterScope());
1549 if (rootCd->getOuterScope()!=Doxygen::globalScope)
1550 {
1551 DefinitionMutable *outerScope = toDefinitionMutable(rootCd->getOuterScope());
1552 if (outerScope)
1553 {
1554 outerScope->addInnerCompound(cd);
1555 }
1556 }
1557
1558 FileDef *fd = templ->getFileDef();
1559 if (fd)
1560 {
1561 cd->setFileDef(fd);
1562 fd->insertClass(cd);
1563 }
1564 for (auto &gd : rootCd->partOfGroups())
1565 {
1566 cd->makePartOfGroup(gd);
1567 gd->addClass(cd);
1568 }
1569
1570 MemberList *ml = templ->getMemberList(MemberListType::PubAttribs());
1571 if (ml)
1572 {
1573 for (const auto &md : *ml)
1574 {
1575 //printf(" Member %s type=%s\n",qPrint(md->name()),md->typeString());
1576 auto newMd = createMemberDef(md->getDefFileName(),md->getDefLine(),md->getDefColumn(),
1577 md->typeString(),md->name(),md->argsString(),md->excpString(),
1578 md->protection(),md->virtualness(),md->isStatic(),Relationship::Member,
1579 md->memberType(),
1580 ArgumentList(),ArgumentList(),"");
1581 MemberDefMutable *imd = toMemberDefMutable(newMd.get());
1582 imd->setMemberClass(cd);
1583 imd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
1584 imd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
1585 imd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
1586 imd->setMemberSpecifiers(md->getMemberSpecifiers());
1587 imd->setVhdlSpecifiers(md->getVhdlSpecifiers());
1588 imd->setMemberGroupId(md->getMemberGroupId());
1589 imd->setInitializer(md->initializer());
1590 imd->setRequiresClause(md->requiresClause());
1591 imd->setMaxInitLines(md->initializerLines());
1592 imd->setBitfields(md->bitfieldString());
1593 imd->setLanguage(md->getLanguage());
1594 cd->insertMember(imd);
1595 MemberName *mn = Doxygen::memberNameLinkedMap->add(md->name());
1596 mn->push_back(std::move(newMd));
1597 }
1598 }
1599 }
1600 return cd;
1601}
1602
1603/** Look through the members of class \a cd and its public members.
1604 * If there is a member m of a tag less struct/union,
1605 * then we create a duplicate of the struct/union with the name of the
1606 * member to identify it.
1607 * So if cd has name S, then the tag less struct/union will get name S.m
1608 * Since tag less structs can be nested we need to call this function
1609 * recursively. Later on we need to patch the member types so we keep
1610 * track of the hierarchy of classes we create.
1611 */
1612static void processTagLessClasses(const ClassDef *rootCd,
1613 const ClassDef *cd,
1614 ClassDefMutable *tagParentCd,
1615 const QCString &prefix,int count)
1616{
1617 //printf("%d: processTagLessClasses %s\n",count,qPrint(cd->name()));
1618 //printf("checking members for %s\n",qPrint(cd->name()));
1619 if (tagParentCd && !cd->getClasses().empty())
1620 {
1621 MemberList *ml = cd->getMemberList(MemberListType::PubAttribs());
1622 if (ml)
1623 {
1624 int pos=0;
1625 for (const auto &md : *ml)
1626 {
1627 QCString type = md->typeString();
1628 if (type.find("::@")!=-1) // member of tag less struct/union
1629 {
1630 for (const auto &icd : cd->getClasses())
1631 {
1632 //printf(" member %s: type='%s'\n",qPrint(md->name()),qPrint(type));
1633 //printf(" comparing '%s'<->'%s'\n",qPrint(type),qPrint(icd->name()));
1634 if (type.find(icd->name())!=-1) // matching tag less struct/union
1635 {
1636 QCString name = md->name();
1637 if (md->isAnonymous()) name = "__unnamed" + QCString().setNum(pos++)+"__";
1638 if (!prefix.isEmpty()) name.prepend(prefix+".");
1639 //printf(" found %s for class %s\n",qPrint(name),qPrint(cd->name()));
1640 ClassDefMutable *ncd = createTagLessInstance(rootCd,icd,name);
1641 if (ncd)
1642 {
1643 processTagLessClasses(rootCd,icd,ncd,name,count+1);
1644 //printf(" addTagged %s to %s\n",qPrint(ncd->name()),qPrint(tagParentCd->name()));
1645 ncd->setTagLessReference(icd);
1646
1647 // replace tag-less type for generated/original member
1648 // by newly created class name.
1649 // note the difference between changing cd and tagParentCd.
1650 // for the initial call this is the same pointer, but for
1651 // recursive calls cd is the original tag-less struct (of which
1652 // there is only one instance) and tagParentCd is the newly
1653 // generated tagged struct of which there can be multiple instances!
1654 MemberList *pml = tagParentCd->getMemberList(MemberListType::PubAttribs());
1655 if (pml)
1656 {
1657 for (const auto &pmd : *pml)
1658 {
1660 if (pmdm && pmd->name()==md->name())
1661 {
1662 pmdm->setAccessorType(ncd,substitute(pmd->typeString(),icd->name(),ncd->name()));
1663 //pmd->setType(substitute(pmd->typeString(),icd->name(),ncd->name()));
1664 }
1665 }
1666 }
1667 }
1668 }
1669 }
1670 }
1671 }
1672 }
1673 }
1674}
1675
1676static void findTagLessClasses(std::vector<ClassDefMutable*> &candidates,ClassDef *cd)
1677{
1678 for (const auto &icd : cd->getClasses())
1679 {
1680 if (icd->name().find("@")==-1) // process all non-anonymous inner classes
1681 {
1682 findTagLessClasses(candidates,icd);
1683 }
1684 }
1685
1687 if (cdm)
1688 {
1689 candidates.push_back(cdm);
1690 }
1691}
1692
1694{
1695 std::vector<ClassDefMutable *> candidates;
1696 for (auto &cd : *Doxygen::classLinkedMap)
1697 {
1698 Definition *scope = cd->getOuterScope();
1699 if (scope && scope->definitionType()!=Definition::TypeClass) // that is not nested
1700 {
1701 findTagLessClasses(candidates,cd.get());
1702 }
1703 }
1704
1705 // since processTagLessClasses is potentially adding classes to Doxygen::classLinkedMap
1706 // we need to call it outside of the loop above, otherwise the iterator gets invalidated!
1707 for (auto &cd : candidates)
1708 {
1709 processTagLessClasses(cd,cd,cd,"",0); // process tag less inner struct/classes
1710 }
1711}
1712
1713
1714//----------------------------------------------------------------------
1715// build a list of all namespaces mentioned in the documentation
1716// and all namespaces that have a documentation block before their definition.
1717static void buildNamespaceList(const Entry *root)
1718{
1719 if (
1720 (root->section.isNamespace() ||
1721 root->section.isNamespaceDoc() ||
1722 root->section.isPackageDoc()
1723 ) &&
1724 !root->name.isEmpty()
1725 )
1726 {
1727 AUTO_TRACE("name={}",root->name);
1728
1729 QCString fName = root->name;
1730 if (root->section.isPackageDoc())
1731 {
1732 fName=substitute(fName,".","::");
1733 }
1734
1735 QCString fullName = stripAnonymousNamespaceScope(fName);
1736 if (!fullName.isEmpty())
1737 {
1738 AUTO_TRACE_ADD("Found namespace {} in {} at line {}",root->name,root->fileName,root->startLine);
1739 NamespaceDef *ndi = Doxygen::namespaceLinkedMap->find(fullName);
1740 if (ndi) // existing namespace
1741 {
1743 if (nd) // non-inline namespace
1744 {
1745 AUTO_TRACE_ADD("Existing namespace");
1746 nd->setDocumentation(root->doc,root->docFile,root->docLine);
1747 nd->setName(fullName); // change name to match docs
1749 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1750 if (nd->getLanguage()==SrcLangExt::Unknown)
1751 {
1752 nd->setLanguage(root->lang);
1753 }
1754 if (root->tagInfo()==nullptr && nd->isReference() && !(root->doc.isEmpty() && root->brief.isEmpty()))
1755 // if we previously found namespace nd in a tag file and now we find a
1756 // documented namespace with the same name in the project, then remove
1757 // the tag file reference
1758 {
1759 nd->setReference("");
1760 nd->setFileName(fullName);
1761 }
1762 nd->setMetaData(root->metaData);
1763
1764 // file definition containing the namespace nd
1765 FileDef *fd=root->fileDef();
1766 if (nd->isArtificial())
1767 {
1768 nd->setArtificial(FALSE); // found namespace explicitly, so cannot be artificial
1769 nd->setDefFile(root->fileName,root->startLine,root->startColumn);
1770 }
1771 // insert the namespace in the file definition
1772 if (fd) fd->insertNamespace(nd);
1773 addNamespaceToGroups(root,nd);
1774 nd->setRefItems(root->sli);
1775 nd->setRequirementReferences(root->rqli);
1776 }
1777 }
1778 else // fresh namespace
1779 {
1780 QCString tagName;
1781 QCString tagFileName;
1782 const TagInfo *tagInfo = root->tagInfo();
1783 if (tagInfo)
1784 {
1785 tagName = tagInfo->tagName;
1786 tagFileName = tagInfo->fileName;
1787 }
1788 AUTO_TRACE_ADD("new namespace {} lang={} tagName={}",fullName,langToString(root->lang),tagName);
1789 // add namespace to the list
1791 Doxygen::namespaceLinkedMap->add(fullName,
1792 createNamespaceDef(tagInfo?tagName:root->fileName,root->startLine,
1793 root->startColumn,fullName,tagName,tagFileName,
1794 root->type,root->spec.isPublished())));
1795 if (nd)
1796 {
1797 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1798 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1800 nd->setHidden(root->hidden);
1801 nd->setArtificial(root->artificial);
1802 nd->setLanguage(root->lang);
1803 nd->setId(root->id);
1804 nd->setMetaData(root->metaData);
1805 nd->setInline(root->spec.isInline());
1806 nd->setExported(root->exported);
1807
1808 addNamespaceToGroups(root,nd);
1809 nd->setRefItems(root->sli);
1810 nd->setRequirementReferences(root->rqli);
1811
1812 // file definition containing the namespace nd
1813 FileDef *fd=root->fileDef();
1814 // insert the namespace in the file definition
1815 if (fd) fd->insertNamespace(nd);
1816
1817 // the empty string test is needed for extract all case
1818 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1819 nd->insertUsedFile(fd);
1820 nd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1821 nd->setBodyDef(fd);
1822
1823 // also add namespace to the correct structural context
1824 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,fullName,nullptr,tagInfo);
1825 AUTO_TRACE_ADD("adding namespace {} to context {}",nd->name(),d ? d->name() : QCString("<none>"));
1826 if (d==nullptr) // we didn't find anything, create the scope artificially
1827 // anyway, so we can at least relate scopes properly.
1828 {
1829 d = buildScopeFromQualifiedName(fullName,nd->getLanguage(),tagInfo);
1831 if (dm)
1832 {
1833 dm->addInnerCompound(nd);
1834 }
1835 nd->setOuterScope(d);
1836 // TODO: Due to the order in which the tag file is written
1837 // a nested class can be found before its parent!
1838 }
1839 else
1840 {
1842 if (dm)
1843 {
1844 dm->addInnerCompound(nd);
1845 }
1846 nd->setOuterScope(d);
1847 // in case of d is an inline namespace, alias insert nd in the part scope of d.
1849 {
1850 NamespaceDef *pnd = toNamespaceDef(d);
1851 if (pnd && pnd->isInline())
1852 {
1853 d = d->getOuterScope();
1854 if (d)
1855 {
1856 dm = toDefinitionMutable(d);
1857 if (dm)
1858 {
1859 auto aliasNd = createNamespaceDefAlias(d,nd);
1860 dm->addInnerCompound(aliasNd.get());
1861 QCString aliasName = aliasNd->name();
1862 AUTO_TRACE_ADD("adding alias {} to {}",aliasName,d->name());
1863 Doxygen::namespaceLinkedMap->add(aliasName,std::move(aliasNd));
1864 }
1865 }
1866 else
1867 {
1868 break;
1869 }
1870 }
1871 else
1872 {
1873 break;
1874 }
1875 }
1876 }
1877 }
1878 }
1879 }
1880 }
1881 for (const auto &e : root->children()) buildNamespaceList(e.get());
1882}
1883
1884//----------------------------------------------------------------------
1885
1887 const QCString &name)
1888{
1889 NamespaceDef *usingNd =nullptr;
1890 for (auto &und : unl)
1891 {
1892 QCString uScope=und->name()+"::";
1893 usingNd = getResolvedNamespace(uScope+name);
1894 if (usingNd!=nullptr) break;
1895 }
1896 return usingNd;
1897}
1898
1899static void findUsingDirectives(const Entry *root)
1900{
1901 if (root->section.isUsingDir())
1902 {
1903 AUTO_TRACE("Found using directive {} at line {} of {}",root->name,root->startLine,root->fileName);
1904 QCString name=substitute(root->name,".","::");
1905 if (name.endsWith("::"))
1906 {
1907 name=name.left(name.length()-2);
1908 }
1909 if (!name.isEmpty())
1910 {
1911 NamespaceDef *usingNd = nullptr;
1912 NamespaceDefMutable *nd = nullptr;
1913 FileDef *fd = root->fileDef();
1914 QCString nsName;
1915
1916 // see if the using statement was found inside a namespace or inside
1917 // the global file scope.
1918 if (root->parent() && root->parent()->section.isNamespace() &&
1919 (fd==nullptr || fd->getLanguage()!=SrcLangExt::Java) // not a .java file
1920 )
1921 {
1922 nsName=stripAnonymousNamespaceScope(root->parent()->name);
1923 if (!nsName.isEmpty())
1924 {
1925 nd = getResolvedNamespaceMutable(nsName);
1926 }
1927 }
1928
1929 // find the scope in which the 'using' namespace is defined by prepending
1930 // the possible scopes in which the using statement was found, starting
1931 // with the most inner scope and going to the most outer scope (i.e.
1932 // file scope).
1933 int scopeOffset = static_cast<int>(nsName.length());
1934 do
1935 {
1936 QCString scope=scopeOffset>0 ?
1937 nsName.left(scopeOffset)+"::" : QCString();
1938 usingNd = getResolvedNamespace(scope+name);
1939 //printf("Trying with scope='%s' usingNd=%p\n",(scope+qPrint(name)),usingNd);
1940 if (scopeOffset==0)
1941 {
1942 scopeOffset=-1;
1943 }
1944 else if ((scopeOffset=nsName.findRev("::",scopeOffset-1))==-1)
1945 {
1946 scopeOffset=0;
1947 }
1948 } while (scopeOffset>=0 && usingNd==nullptr);
1949
1950 if (usingNd==nullptr && nd) // not found, try used namespaces in this scope
1951 // or in one of the parent namespace scopes
1952 {
1953 const NamespaceDefMutable *pnd = nd;
1954 while (pnd && usingNd==nullptr)
1955 {
1956 // also try with one of the used namespaces found earlier
1958
1959 // goto the parent
1960 Definition *s = pnd->getOuterScope();
1962 {
1964 }
1965 else
1966 {
1967 pnd = nullptr;
1968 }
1969 }
1970 }
1971 if (usingNd==nullptr && fd) // still nothing, also try used namespace in the
1972 // global scope
1973 {
1974 usingNd = findUsedNamespace(fd->getUsedNamespaces(),name);
1975 }
1976
1977 //printf("%s -> %s\n",qPrint(name),usingNd?qPrint(usingNd->name()):"<none>");
1978
1979 // add the namespace the correct scope
1980 if (usingNd)
1981 {
1982 //printf("using fd=%p nd=%p\n",fd,nd);
1983 if (nd)
1984 {
1985 //printf("Inside namespace %s\n",qPrint(nd->name()));
1986 nd->addUsingDirective(usingNd);
1987 }
1988 else if (fd)
1989 {
1990 //printf("Inside file %s\n",qPrint(fd->name()));
1991 fd->addUsingDirective(usingNd);
1992 }
1993 }
1994 else // unknown namespace, but add it anyway.
1995 {
1996 AUTO_TRACE_ADD("new unknown namespace {} lang={} hidden={}",name,langToString(root->lang),root->hidden);
1997 // add namespace to the list
2000 createNamespaceDef(root->fileName,root->startLine,root->startColumn,name)));
2001 if (nd)
2002 {
2003 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
2004 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2006 nd->setHidden(root->hidden);
2007 nd->setArtificial(TRUE);
2008 nd->setLanguage(root->lang);
2009 nd->setId(root->id);
2010 nd->setMetaData(root->metaData);
2011 nd->setInline(root->spec.isInline());
2012 nd->setExported(root->exported);
2013
2014 for (const Grouping &g : root->groups)
2015 {
2016 GroupDef *gd=nullptr;
2017 if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname)))
2018 gd->addNamespace(nd);
2019 }
2020
2021 // insert the namespace in the file definition
2022 if (fd)
2023 {
2024 fd->insertNamespace(nd);
2025 fd->addUsingDirective(nd);
2026 }
2027
2028 // the empty string test is needed for extract all case
2029 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2030 nd->insertUsedFile(fd);
2031 nd->setRefItems(root->sli);
2032 nd->setRequirementReferences(root->rqli);
2033 }
2034 }
2035 }
2036 }
2037 for (const auto &e : root->children()) findUsingDirectives(e.get());
2038}
2039
2040//----------------------------------------------------------------------
2041
2042static void buildListOfUsingDecls(const Entry *root)
2043{
2044 if (root->section.isUsingDecl() &&
2045 !root->parent()->section.isCompound() // not a class/struct member
2046 )
2047 {
2048 QCString name = substitute(root->name,".","::");
2049 g_usingDeclarations.insert(name.str());
2050 }
2051 for (const auto &e : root->children()) buildListOfUsingDecls(e.get());
2052}
2053
2054
2055static void findUsingDeclarations(const Entry *root,bool filterPythonPackages)
2056{
2057 if (root->section.isUsingDecl() &&
2058 !root->parent()->section.isCompound() && // not a class/struct member
2059 (!filterPythonPackages || (root->lang==SrcLangExt::Python && root->fileName.endsWith("__init__.py")))
2060 )
2061 {
2062 AUTO_TRACE("Found using declaration '{}' at line {} of {} inside section {}",
2063 root->name,root->startLine,root->fileName,root->parent()->section);
2064 if (!root->name.isEmpty())
2065 {
2066 const Definition *usingDef = nullptr;
2067 NamespaceDefMutable *nd = nullptr;
2068 FileDef *fd = root->fileDef();
2069 QCString scName;
2070
2071 // see if the using statement was found inside a namespace or inside
2072 // the global file scope.
2073 if (root->parent()->section.isNamespace())
2074 {
2075 scName=root->parent()->name;
2076 if (!scName.isEmpty())
2077 {
2078 nd = getResolvedNamespaceMutable(scName);
2079 }
2080 }
2081
2082 // Assume the using statement was used to import a class.
2083 // Find the scope in which the 'using' namespace is defined by prepending
2084 // the possible scopes in which the using statement was found, starting
2085 // with the most inner scope and going to the most outer scope (i.e.
2086 // file scope).
2087
2088 QCString name = substitute(root->name,".","::"); //Java/C# scope->internal
2089
2090 SymbolResolver resolver;
2091 const Definition *scope = nd;
2092 if (nd==nullptr) scope = fd;
2093 usingDef = resolver.resolveSymbol(scope,name);
2094
2095 //printf("usingDef(scope=%s,name=%s)=%s\n",qPrint(nd?nd->qualifiedName():""),qPrint(name),usingDef?qPrint(usingDef->qualifiedName()):"nullptr");
2096
2097 if (!usingDef)
2098 {
2099 usingDef = getClass(name); // try direct lookup, this is needed to get
2100 // builtin STL classes to properly resolve, e.g.
2101 // vector -> std::vector
2102 }
2103 if (!usingDef)
2104 {
2105 usingDef = Doxygen::hiddenClassLinkedMap->find(name); // check if it is already hidden
2106 }
2107#if 0
2108 if (!usingDef)
2109 {
2110 AUTO_TRACE_ADD("New using class '{}' (sec={})! #tArgLists={}",
2111 name,root->section,root->tArgLists.size());
2114 createClassDef( "<using>",1,1, name, ClassDef::Class)));
2115 if (usingCd)
2116 {
2117 usingCd->setArtificial(TRUE);
2118 usingCd->setLanguage(root->lang);
2119 usingDef = usingCd;
2120 }
2121 }
2122#endif
2123 else
2124 {
2125 AUTO_TRACE_ADD("Found used type '{}' in scope='{}'",
2126 usingDef->name(), nd ? nd->name(): fd ? fd->name() : QCString("<unknown>"));
2127 }
2128
2129 if (usingDef)
2130 {
2131 if (nd)
2132 {
2133 nd->addUsingDeclaration(usingDef);
2134 }
2135 else if (fd)
2136 {
2137 fd->addUsingDeclaration(usingDef);
2138 }
2139 }
2140 }
2141 }
2142 for (const auto &e : root->children()) findUsingDeclarations(e.get(),filterPythonPackages);
2143}
2144
2145//----------------------------------------------------------------------
2146
2148{
2149 root->commandOverrides.apply_callGraph ([&](bool b) { md->overrideCallGraph(b); });
2150 root->commandOverrides.apply_callerGraph ([&](bool b) { md->overrideCallerGraph(b); });
2151 root->commandOverrides.apply_referencedByRelation([&](bool b) { md->overrideReferencedByRelation(b); });
2152 root->commandOverrides.apply_referencesRelation ([&](bool b) { md->overrideReferencesRelation(b); });
2153 root->commandOverrides.apply_inlineSource ([&](bool b) { md->overrideInlineSource(b); });
2154 root->commandOverrides.apply_enumValues ([&](bool b) { md->overrideEnumValues(b); });
2155}
2156
2157//----------------------------------------------------------------------
2158
2160 const QCString &fileName,const QCString &memName)
2161{
2162 AUTO_TRACE("creating new member {} for class {}",memName,cd->name());
2163 const ArgumentList &templAl = md->templateArguments();
2164 const ArgumentList &al = md->argumentList();
2165 auto newMd = createMemberDef(
2166 fileName,root->startLine,root->startColumn,
2167 md->typeString(),memName,md->argsString(),
2168 md->excpString(),root->protection,root->virt,
2169 md->isStatic(),Relationship::Member,md->memberType(),
2170 templAl,al,root->metaData
2171 );
2172 auto newMmd = toMemberDefMutable(newMd.get());
2173 newMmd->setMemberClass(cd);
2174 cd->insertMember(newMd.get());
2175 if (!root->doc.isEmpty() || !root->brief.isEmpty())
2176 {
2177 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2178 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2179 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2180 }
2181 else
2182 {
2183 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2184 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2185 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2186 }
2187 newMmd->setDefinition(md->definition());
2188 applyMemberOverrideOptions(root,newMmd);
2189 newMmd->addQualifiers(root->qualifiers);
2190 newMmd->setBitfields(md->bitfieldString());
2191 newMmd->addSectionsToDefinition(root->anchors);
2192 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2193 newMmd->setBodyDef(md->getBodyDef());
2194 newMmd->setInitializer(md->initializer());
2195 newMmd->setRequiresClause(md->requiresClause());
2196 newMmd->setMaxInitLines(md->initializerLines());
2197 newMmd->setMemberGroupId(root->mGrpId);
2198 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2199 newMmd->setVhdlSpecifiers(md->getVhdlSpecifiers());
2200 newMmd->setLanguage(root->lang);
2201 newMmd->setId(root->id);
2202 MemberName *mn = Doxygen::memberNameLinkedMap->add(memName);
2203 mn->push_back(std::move(newMd));
2204}
2205
2206static std::unordered_map<std::string,std::vector<ClassDefMutable*>> g_usingClassMap;
2207
2208static void findUsingDeclImports(const Entry *root)
2209{
2210 if (root->section.isUsingDecl() &&
2211 root->parent()->section.isCompound() // in a class/struct member
2212 )
2213 {
2214 AUTO_TRACE("Found using declaration '{}' inside section {}", root->name, root->parent()->section);
2215 QCString fullName=removeRedundantWhiteSpace(root->parent()->name);
2216 fullName=stripAnonymousNamespaceScope(fullName);
2217 fullName=stripTemplateSpecifiersFromScope(fullName);
2218 ClassDefMutable *cd = getClassMutable(fullName);
2219 if (cd)
2220 {
2221 AUTO_TRACE_ADD("found class '{}'",cd->name());
2222 int i=root->name.findRev("::");
2223 if (i!=-1)
2224 {
2225 QCString scope=root->name.left(i);
2226 QCString memName=root->name.right(root->name.length()-i-2);
2227 SymbolResolver resolver;
2228 const ClassDef *bcd = resolver.resolveClass(cd,scope); // todo: file in fileScope parameter
2229 AUTO_TRACE_ADD("name={} scope={} bcd={}",scope,cd?cd->name():"<none>",bcd?bcd->name():"<none>");
2230 if (bcd && bcd!=cd)
2231 {
2232 AUTO_TRACE_ADD("found class '{}' memName='{}'",bcd->name(),memName);
2234 const MemberNameInfo *mni = mnlm.find(memName);
2235 if (mni)
2236 {
2237 for (auto &mi : *mni)
2238 {
2239 const MemberDef *md = mi->memberDef();
2240 if (md && md->protection()!=Protection::Private)
2241 {
2242 AUTO_TRACE_ADD("found member '{}'",mni->memberName());
2243 QCString fileName = root->fileName;
2244 if (fileName.isEmpty() && root->tagInfo())
2245 {
2246 fileName = root->tagInfo()->tagName;
2247 }
2248 if (!cd->containsOverload(md))
2249 {
2250 createUsingMemberImportForClass(root,cd,md,fileName,memName);
2251 // also insert the member into copies of the class
2252 auto it = g_usingClassMap.find(cd->qualifiedName().str());
2253 if (it != g_usingClassMap.end())
2254 {
2255 for (const auto &copyCd : it->second)
2256 {
2257 createUsingMemberImportForClass(root,copyCd,md,fileName,memName);
2258 }
2259 }
2260 }
2261 }
2262 }
2263 }
2264 }
2265 }
2266 }
2267 }
2268 else if (root->section.isUsingDecl() &&
2269 (root->parent()->section.isNamespace() || root->parent()->section.isEmpty()) && // namespace or global member
2270 root->lang==SrcLangExt::Cpp // do we also want this for e.g. Fortran? (see test case 095)
2271 )
2272 {
2273 AUTO_TRACE("Found using declaration '{}' inside section {}", root->name, root->parent()->section);
2274 Definition *scope = nullptr;
2275 NamespaceDefMutable *nd = nullptr;
2276 FileDef *fd = root->parent()->fileDef();
2277 if (!root->parent()->name.isEmpty())
2278 {
2279 QCString fullName=removeRedundantWhiteSpace(root->parent()->name);
2280 fullName=stripAnonymousNamespaceScope(fullName);
2282 scope = nd;
2283 }
2284 else
2285 {
2286 scope = fd;
2287 }
2288 if (scope)
2289 {
2290 AUTO_TRACE_ADD("found scope '{}'",scope->name());
2291 SymbolResolver resolver;
2292 const Definition *def = resolver.resolveSymbol(root->name.startsWith("::") ? nullptr : scope,root->name);
2293 if (def && def->definitionType()==Definition::TypeMember)
2294 {
2295 int i=root->name.findRev("::");
2296 QCString memName;
2297 if (i!=-1)
2298 {
2299 memName = root->name.right(root->name.length()-i-2);
2300 }
2301 else
2302 {
2303 memName = root->name;
2304 }
2305 const MemberDef *md = toMemberDef(def);
2306 AUTO_TRACE_ADD("found member '{}' for name '{}'",md->qualifiedName(),root->name);
2307 QCString fileName = root->fileName;
2308 if (fileName.isEmpty() && root->tagInfo())
2309 {
2310 fileName = root->tagInfo()->tagName;
2311 }
2312 const ArgumentList &templAl = md->templateArguments();
2313 const ArgumentList &al = md->argumentList();
2314
2315 auto newMd = createMemberDef(
2316 fileName,root->startLine,root->startColumn,
2317 md->typeString(),memName,md->argsString(),
2318 md->excpString(),root->protection,root->virt,
2319 md->isStatic(),Relationship::Member,md->memberType(),
2320 templAl,al,root->metaData
2321 );
2322 auto newMmd = toMemberDefMutable(newMd.get());
2323 if (nd)
2324 {
2325 newMmd->setNamespace(nd);
2326 nd->insertMember(newMd.get());
2327 }
2328 if (fd)
2329 {
2330 newMmd->setFileDef(fd);
2331 fd->insertMember(newMd.get());
2332 }
2333 if (!root->doc.isEmpty() || !root->brief.isEmpty())
2334 {
2335 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2336 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2337 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2338 }
2339 else
2340 {
2341 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2342 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2343 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2344 }
2345 newMmd->setDefinition(md->definition());
2346 applyMemberOverrideOptions(root,newMmd);
2347 newMmd->addQualifiers(root->qualifiers);
2348 newMmd->setBitfields(md->bitfieldString());
2349 newMmd->addSectionsToDefinition(root->anchors);
2350 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2351 newMmd->setBodyDef(md->getBodyDef());
2352 newMmd->setInitializer(md->initializer());
2353 newMmd->setRequiresClause(md->requiresClause());
2354 newMmd->setMaxInitLines(md->initializerLines());
2355 newMmd->setMemberGroupId(root->mGrpId);
2356 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2357 newMmd->setVhdlSpecifiers(md->getVhdlSpecifiers());
2358 newMmd->setLanguage(root->lang);
2359 newMmd->setId(root->id);
2360 MemberName *mn = Doxygen::functionNameLinkedMap->add(memName);
2361 mn->push_back(std::move(newMd));
2362#if 0 // insert an alias instead of a copy
2363 const MemberDef *md = toMemberDef(def);
2364 AUTO_TRACE_ADD("found member '{}' for name '{}'",md->qualifiedName(),root->name);
2365 auto aliasMd = createMemberDefAlias(nd,md);
2366 QCString aliasFullName = nd->qualifiedName()+"::"+aliasMd->localName();
2367 if (nd && aliasMd.get())
2368 {
2369 nd->insertMember(aliasMd.get());
2370 }
2371 if (fd && aliasMd.get())
2372 {
2373 fd->insertMember(aliasMd.get());
2374 }
2375 MemberName *mn = Doxygen::memberNameLinkedMap->add(aliasFullName);
2376 mn->push_back(std::move(aliasMd));
2377#endif
2378 }
2379 else if (def && def->definitionType()==Definition::TypeClass)
2380 {
2381 const ClassDef *cd = toClassDef(def);
2382 QCString copyFullName;
2383 if (nd==nullptr)
2384 {
2385 copyFullName = cd->localName();
2386 }
2387 else
2388 {
2389 copyFullName = nd->qualifiedName()+"::"+cd->localName();
2390 }
2391 if (Doxygen::classLinkedMap->find(copyFullName)==nullptr)
2392 {
2394 Doxygen::classLinkedMap->add(copyFullName,
2395 cd->deepCopy(copyFullName)));
2396 AUTO_TRACE_ADD("found class '{}' for name '{}' copy '{}' obj={}",cd->qualifiedName(),root->name,copyFullName,(void*)ncdm);
2397 g_usingClassMap[cd->qualifiedName().str()].push_back(ncdm);
2398 if (ncdm)
2399 {
2400 if (nd) ncdm->moveTo(nd);
2401 if ((!root->doc.isEmpty() || !root->brief.isEmpty())) // use docs at using statement
2402 {
2403 ncdm->setDocumentation(root->doc,root->docFile,root->docLine);
2404 ncdm->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2405 }
2406 else // use docs from used class
2407 {
2408 ncdm->setDocumentation(cd->documentation(),cd->docFile(),cd->docLine());
2410 }
2411 if (nd)
2412 {
2413 nd->addInnerCompound(ncdm);
2414 nd->addUsingDeclaration(ncdm);
2415 }
2416 if (fd)
2417 {
2418 if (ncdm) ncdm->setFileDef(fd);
2419 fd->insertClass(ncdm);
2420 fd->addUsingDeclaration(ncdm);
2421 }
2422 }
2423 }
2424#if 0 // insert an alias instead of a copy
2425 auto aliasCd = createClassDefAlias(nd,cd);
2426 QCString aliasFullName;
2427 if (nd==nullptr)
2428 {
2429 aliasFullName = aliasCd->localName();
2430 }
2431 else
2432 {
2433 aliasFullName = nd->qualifiedName()+"::"+aliasCd->localName();
2434 }
2435 AUTO_TRACE_ADD("found class '{}' for name '{}' aliasFullName='{}'",cd->qualifiedName(),root->name,aliasFullName);
2436 auto acd = Doxygen::classLinkedMap->add(aliasFullName,std::move(aliasCd));
2437 if (nd && acd)
2438 {
2439 nd->addInnerCompound(acd);
2440 }
2441 if (fd && acd)
2442 {
2443 fd->insertClass(acd);
2444 }
2445#endif
2446 }
2447 else if (scope)
2448 {
2449 AUTO_TRACE_ADD("no symbol with name '{}' in scope {}",root->name,scope->name());
2450 }
2451 }
2452 }
2453 for (const auto &e : root->children()) findUsingDeclImports(e.get());
2454}
2455
2456//----------------------------------------------------------------------
2457
2459{
2460 FileDefSet visitedFiles;
2461 // then recursively add using directives found in #include files
2462 // to files that have not been visited.
2463 for (const auto &fn : *Doxygen::inputNameLinkedMap)
2464 {
2465 for (const auto &fd : *fn)
2466 {
2467 //printf("----- adding using directives for file %s\n",qPrint(fd->name()));
2468 fd->addIncludedUsingDirectives(visitedFiles);
2469 }
2470 }
2471}
2472
2473//----------------------------------------------------------------------
2474
2476 const Entry *root,
2477 ClassDefMutable *cd,
2478 MemberType mtype,
2479 const QCString &type,
2480 const QCString &name,
2481 const QCString &args,
2482 bool fromAnnScope,
2483 MemberDef *fromAnnMemb,
2484 Protection prot,
2485 Relationship related)
2486{
2488 QCString scopeSeparator="::";
2489 SrcLangExt lang = cd->getLanguage();
2490 if (lang==SrcLangExt::Java || lang==SrcLangExt::CSharp)
2491 {
2492 qualScope = substitute(qualScope,"::",".");
2493 scopeSeparator=".";
2494 }
2495 AUTO_TRACE("class variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} ann={} init='{}'",
2496 root->fileName, type, qualScope, name, args, root->protection, mtype, lang, fromAnnScope, root->initializer.str());
2497
2498 QCString def;
2499 if (!type.isEmpty())
2500 {
2501 if (related!=Relationship::Member || mtype==MemberType::Friend || Config_getBool(HIDE_SCOPE_NAMES))
2502 {
2503 if (root->spec.isAlias()) // turn 'typedef B A' into 'using A'
2504 {
2505 if (lang==SrcLangExt::Python)
2506 {
2507 def="type "+name+args;
2508 }
2509 else
2510 {
2511 def="using "+name;
2512 }
2513 }
2514 else
2515 {
2516 def=type+" "+name+args;
2517 }
2518 }
2519 else
2520 {
2521 if (root->spec.isAlias()) // turn 'typedef B C::A' into 'using C::A'
2522 {
2523 if (lang==SrcLangExt::Python)
2524 {
2525 def="type "+qualScope+scopeSeparator+name+args;
2526 }
2527 else
2528 {
2529 def="using "+qualScope+scopeSeparator+name;
2530 }
2531 }
2532 else
2533 {
2534 def=type+" "+qualScope+scopeSeparator+name+args;
2535 }
2536 }
2537 }
2538 else
2539 {
2540 if (Config_getBool(HIDE_SCOPE_NAMES))
2541 {
2542 def=name+args;
2543 }
2544 else
2545 {
2546 def=qualScope+scopeSeparator+name+args;
2547 }
2548 }
2549 def.stripPrefix("static ");
2550
2551 // see if the member is already found in the same scope
2552 // (this may be the case for a static member that is initialized
2553 // outside the class)
2555 if (mn)
2556 {
2557 for (const auto &imd : *mn)
2558 {
2559 //printf("md->getClassDef()=%p cd=%p type=[%s] md->typeString()=[%s]\n",
2560 // md->getClassDef(),cd,qPrint(type),md->typeString());
2561 MemberDefMutable *md = toMemberDefMutable(imd.get());
2562 if (md &&
2563 md->getClassDef()==cd &&
2564 ((lang==SrcLangExt::Python && type.isEmpty() && !md->typeString().isEmpty()) ||
2566 // member already in the scope
2567 {
2568
2569 if (root->lang==SrcLangExt::ObjC &&
2570 root->mtype==MethodTypes::Property &&
2572 { // Objective-C 2.0 property
2573 // turn variable into a property
2574 md->setProtection(root->protection);
2576 }
2577 addMemberDocs(root,md,def,nullptr,FALSE,root->spec);
2578 AUTO_TRACE_ADD("Member already found!");
2579 return md;
2580 }
2581 }
2582 }
2583
2584 QCString fileName = root->fileName;
2585 if (fileName.isEmpty() && root->tagInfo())
2586 {
2587 fileName = root->tagInfo()->tagName;
2588 }
2589
2590 // new member variable, typedef or enum value
2591 auto md = createMemberDef(
2592 fileName,root->startLine,root->startColumn,
2593 type,name,args,root->exception,
2594 prot,Specifier::Normal,root->isStatic,related,
2595 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2596 ArgumentList(), root->metaData);
2597 auto mmd = toMemberDefMutable(md.get());
2598 mmd->setTagInfo(root->tagInfo());
2599 mmd->setMemberClass(cd); // also sets outer scope (i.e. getOuterScope())
2600 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2601 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2602 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2603 mmd->setDefinition(def);
2604 mmd->setBitfields(root->bitfields);
2605 mmd->addSectionsToDefinition(root->anchors);
2606 mmd->setFromAnonymousScope(fromAnnScope);
2607 mmd->setFromAnonymousMember(fromAnnMemb);
2608 //md->setIndentDepth(indentDepth);
2609 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2610 mmd->setInitializer(root->initializer.str());
2611 mmd->setMaxInitLines(root->initLines);
2612 mmd->setMemberGroupId(root->mGrpId);
2613 mmd->setMemberSpecifiers(root->spec);
2614 mmd->setVhdlSpecifiers(root->vhdlSpec);
2615 mmd->setReadAccessor(root->read);
2616 mmd->setWriteAccessor(root->write);
2618 mmd->setHidden(root->hidden);
2619 mmd->setArtificial(root->artificial);
2620 mmd->setLanguage(root->lang);
2621 mmd->setId(root->id);
2622 addMemberToGroups(root,md.get());
2624 mmd->setBodyDef(root->fileDef());
2625 mmd->addQualifiers(root->qualifiers);
2626
2627 AUTO_TRACE_ADD("Adding new member to class '{}'",cd->name());
2628 cd->insertMember(md.get());
2629 mmd->setRefItems(root->sli);
2630 mmd->setRequirementReferences(root->rqli);
2631
2632 cd->insertUsedFile(root->fileDef());
2633 root->markAsProcessed();
2634
2635 if (mtype==MemberType::Typedef)
2636 {
2637 resolveTemplateInstanceInType(root,cd,md.get());
2638 }
2639
2640 // add the member to the global list
2641 MemberDef *result = md.get();
2642 mn = Doxygen::memberNameLinkedMap->add(name);
2643 mn->push_back(std::move(md));
2644
2645 return result;
2646}
2647
2648//----------------------------------------------------------------------
2649
2651 const Entry *root,
2652 MemberType mtype,
2653 const QCString &scope,
2654 const QCString &type,
2655 const QCString &name,
2656 const QCString &args,
2657 bool fromAnnScope,
2658 MemberDef *fromAnnMemb)
2659{
2660 AUTO_TRACE("global variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} init='{}'",
2661 root->fileName, type, scope, name, args, root->protection, mtype, root->lang, root->initializer.str());
2662
2663 FileDef *fd = root->fileDef();
2664
2665 // see if we have a typedef that should hide a struct or union
2666 if (mtype==MemberType::Typedef && Config_getBool(TYPEDEF_HIDES_STRUCT))
2667 {
2668 QCString ttype = type;
2669 ttype.stripPrefix("typedef ");
2670 if (ttype.stripPrefix("struct ") || ttype.stripPrefix("union "))
2671 {
2672 static const reg::Ex re(R"(\a\w*)");
2673 reg::Match match;
2674 const std::string &typ = ttype.str();
2675 if (reg::search(typ,match,re))
2676 {
2677 QCString typeValue = match.str();
2678 ClassDefMutable *cd = getClassMutable(typeValue);
2679 if (cd)
2680 {
2681 // this typedef should hide compound name cd, so we
2682 // change the name that is displayed from cd.
2683 cd->setClassName(name);
2684 cd->setDocumentation(root->doc,root->docFile,root->docLine);
2685 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2686 return nullptr;
2687 }
2688 }
2689 }
2690 }
2691
2692 // see if the function is inside a namespace
2693 NamespaceDefMutable *nd = nullptr;
2694 if (!scope.isEmpty())
2695 {
2696 if (scope.find('@')!=-1) return nullptr; // anonymous scope!
2697 nd = getResolvedNamespaceMutable(scope);
2698 }
2699 QCString def;
2700
2701 // determine the definition of the global variable
2702 if (nd && !nd->isAnonymous() &&
2703 !Config_getBool(HIDE_SCOPE_NAMES)
2704 )
2705 // variable is inside a namespace, so put the scope before the name
2706 {
2707 SrcLangExt lang = nd->getLanguage();
2709
2710 if (!type.isEmpty())
2711 {
2712 if (root->spec.isAlias()) // turn 'typedef B NS::A' into 'using NS::A'
2713 {
2714 if (lang==SrcLangExt::Python)
2715 {
2716 def="type "+nd->name()+sep+name+args;
2717 }
2718 else
2719 {
2720 def="using "+nd->name()+sep+name;
2721 }
2722 }
2723 else // normal member
2724 {
2725 def=type+" "+nd->name()+sep+name+args;
2726 }
2727 }
2728 else
2729 {
2730 def=nd->name()+sep+name+args;
2731 }
2732 }
2733 else
2734 {
2735 if (!type.isEmpty() && !root->name.isEmpty())
2736 {
2737 if (name.at(0)=='@') // dummy variable representing anonymous union
2738 {
2739 def=type;
2740 }
2741 else
2742 {
2743 if (root->spec.isAlias()) // turn 'typedef B A' into 'using A'
2744 {
2745 if (root->lang==SrcLangExt::Python)
2746 {
2747 def="type "+root->name+args;
2748 }
2749 else
2750 {
2751 def="using "+root->name;
2752 }
2753 }
2754 else // normal member
2755 {
2756 def=type+" "+name+args;
2757 }
2758 }
2759 }
2760 else
2761 {
2762 def=name+args;
2763 }
2764 }
2765 def.stripPrefix("static ");
2766
2768 if (mn)
2769 {
2770 //QCString nscope=removeAnonymousScopes(scope);
2771 //NamespaceDef *nd=nullptr;
2772 //if (!nscope.isEmpty())
2773 if (!scope.isEmpty())
2774 {
2775 nd = getResolvedNamespaceMutable(scope);
2776 }
2777 for (const auto &imd : *mn)
2778 {
2779 MemberDefMutable *md = toMemberDefMutable(imd.get());
2780 if (md &&
2781 ((nd==nullptr && md->getNamespaceDef()==nullptr && md->getFileDef() &&
2782 root->fileName==md->getFileDef()->absFilePath()
2783 ) // both variable names in the same file
2784 || (nd!=nullptr && md->getNamespaceDef()==nd) // both in same namespace
2785 )
2786 && !md->isDefine() // function style #define's can be "overloaded" by typedefs or variables
2787 && !md->isEnumerate() // in C# an enum value and enum can have the same name
2788 )
2789 // variable already in the scope
2790 {
2791 bool isPHPArray = md->getLanguage()==SrcLangExt::PHP &&
2792 md->argsString()!=args &&
2793 args.find('[')!=-1;
2794 bool staticsInDifferentFiles =
2795 root->isStatic && md->isStatic() &&
2796 root->fileName!=md->getDefFileName();
2797
2798 if (md->getFileDef() &&
2799 !isPHPArray && // not a php array
2800 !staticsInDifferentFiles
2801 )
2802 // not a php array variable
2803 {
2804 AUTO_TRACE_ADD("variable already found: scope='{}'",md->getOuterScope()->name());
2805 addMemberDocs(root,md,def,nullptr,FALSE,root->spec);
2806 md->setRefItems(root->sli);
2807 md->setRequirementReferences(root->rqli);
2808 // if md is a variable forward declaration and root is the definition that
2809 // turn md into the definition
2810 if (!root->explicitExternal && md->isExternal())
2811 {
2812 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
2814 }
2815 // if md is the definition and root point at a declaration, then add the
2816 // declaration info
2817 else if (root->explicitExternal && !md->isExternal())
2818 {
2819 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
2820 }
2821 return md;
2822 }
2823 }
2824 }
2825 }
2826
2827 QCString fileName = root->fileName;
2828 if (fileName.isEmpty() && root->tagInfo())
2829 {
2830 fileName = root->tagInfo()->tagName;
2831 }
2832
2833 AUTO_TRACE_ADD("new variable, namespace='{}'",nd?nd->name():QCString("<global>"));
2834 // new global variable, enum value or typedef
2835 auto md = createMemberDef(
2836 fileName,root->startLine,root->startColumn,
2837 type,name,args,QCString(),
2838 root->protection, Specifier::Normal,root->isStatic,Relationship::Member,
2839 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2840 root->argList, root->metaData);
2841 auto mmd = toMemberDefMutable(md.get());
2842 mmd->setTagInfo(root->tagInfo());
2843 mmd->setMemberSpecifiers(root->spec);
2844 mmd->setVhdlSpecifiers(root->vhdlSpec);
2845 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2846 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2847 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2848 mmd->addSectionsToDefinition(root->anchors);
2849 mmd->setFromAnonymousScope(fromAnnScope);
2850 mmd->setFromAnonymousMember(fromAnnMemb);
2851 mmd->setInitializer(root->initializer.str());
2852 mmd->setMaxInitLines(root->initLines);
2853 mmd->setMemberGroupId(root->mGrpId);
2854 mmd->setDefinition(def);
2855 mmd->setLanguage(root->lang);
2856 mmd->setId(root->id);
2858 mmd->setExplicitExternal(root->explicitExternal,fileName,root->startLine,root->startColumn);
2859 mmd->addQualifiers(root->qualifiers);
2860 //md->setOuterScope(fd);
2861 if (!root->explicitExternal)
2862 {
2863 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2864 mmd->setBodyDef(fd);
2865 }
2866 addMemberToGroups(root,md.get());
2868
2869 mmd->setRefItems(root->sli);
2870 mmd->setRequirementReferences(root->rqli);
2871 if (nd && !nd->isAnonymous())
2872 {
2873 mmd->setNamespace(nd);
2874 nd->insertMember(md.get());
2875 }
2876
2877 // add member to the file (we do this even if we have already inserted
2878 // it into the namespace.
2879 if (fd)
2880 {
2881 mmd->setFileDef(fd);
2882 fd->insertMember(md.get());
2883 }
2884
2885 root->markAsProcessed();
2886
2887 if (mtype==MemberType::Typedef)
2888 {
2889 resolveTemplateInstanceInType(root,nd,md.get());
2890 }
2891
2892 // add member definition to the list of globals
2893 MemberDef *result = md.get();
2894 mn = Doxygen::functionNameLinkedMap->add(name);
2895 mn->push_back(std::move(md));
2896
2897
2898
2899 return result;
2900}
2901
2902/*! See if the return type string \a type is that of a function pointer
2903 * \returns -1 if this is not a function pointer variable or
2904 * the index at which the closing brace of (...*name) was found.
2905 */
2906static int findFunctionPtr(const std::string &type,SrcLangExt lang, int *pLength=nullptr)
2907{
2908 AUTO_TRACE("type='{}' lang={}",type,lang);
2909 if (lang == SrcLangExt::Fortran || lang == SrcLangExt::VHDL)
2910 {
2911 return -1; // Fortran and VHDL do not have function pointers
2912 }
2913
2914 static const reg::Ex re(R"(\‍([^)]*[*&^][^)]*\))");
2915 reg::Match match;
2916 size_t i=std::string::npos;
2917 size_t l=0;
2918 if (reg::search(type,match,re)) // contains (...*...) or (...&...) or (...^...)
2919 {
2920 i = match.position();
2921 l = match.length();
2922 }
2923 if (i!=std::string::npos)
2924 {
2925 size_t di = type.find("decltype(");
2926 if (di!=std::string::npos && di<i)
2927 {
2928 i = std::string::npos;
2929 }
2930 }
2931 size_t bb=type.find('<');
2932 size_t be=type.rfind('>');
2933 bool templFp = false;
2934 if (be!=std::string::npos) {
2935 size_t cc_ast = type.find("::*");
2936 size_t cc_amp = type.find("::&");
2937 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>::*)'
2938 }
2939
2940 if (!type.empty() && // return type is non-empty
2941 i!=std::string::npos && // contains (...*...)
2942 type.find("operator")==std::string::npos && // not an operator
2943 (type.find(")(")==std::string::npos || type.find("typedef ")!=std::string::npos) &&
2944 // not a function pointer return type
2945 (!(bb<i && i<be) || templFp) // bug665855: avoid treating "typedef A<void (T*)> type" as a function pointer
2946 )
2947 {
2948 if (pLength) *pLength=static_cast<int>(l);
2949 //printf("findFunctionPtr=%d\n",(int)i);
2950 AUTO_TRACE_EXIT("result={}",i);
2951 return static_cast<int>(i);
2952 }
2953 else
2954 {
2955 //printf("findFunctionPtr=%d\n",-1);
2956 AUTO_TRACE_EXIT("result=-1");
2957 return -1;
2958 }
2959}
2960
2961//--------------------------------------------------------------------------------------
2962
2963/*! Returns TRUE iff \a type is a class within scope \a context.
2964 * Used to detect variable declarations that look like function prototypes.
2965 */
2966static bool isVarWithConstructor(const Entry *root)
2967{
2968 bool result = false;
2969 bool typeIsClass = false;
2970 bool typePtrType = false;
2971 QCString type;
2972 Definition *ctx = nullptr;
2973 FileDef *fd = root->fileDef();
2974 SymbolResolver resolver(fd);
2975
2976 AUTO_TRACE("isVarWithConstructor({})",root->name);
2977 if (root->parent()->section.isCompound())
2978 { // inside a class
2979 result=FALSE;
2980 AUTO_TRACE_EXIT("inside class: result={}",result);
2981 return result;
2982 }
2983 else if ((fd != nullptr) && (fd->name().endsWith(".c") || fd->name().endsWith(".h")))
2984 { // inside a .c file
2985 result=FALSE;
2986 AUTO_TRACE_EXIT("inside C file: result={}",result);
2987 return result;
2988 }
2989 if (root->type.isEmpty())
2990 {
2991 result=FALSE;
2992 AUTO_TRACE_EXIT("no type: result={}",result);
2993 return result;
2994 }
2995 if (!root->parent()->name.isEmpty())
2996 {
2997 ctx=Doxygen::namespaceLinkedMap->find(root->parent()->name);
2998 }
2999 type = root->type;
3000 // remove qualifiers
3001 findAndRemoveWord(type,"const");
3002 findAndRemoveWord(type,"static");
3003 findAndRemoveWord(type,"volatile");
3004 typePtrType = type.find('*')!=-1 || type.find('&')!=-1;
3005 if (!typePtrType)
3006 {
3007 typeIsClass = resolver.resolveClass(ctx,type)!=nullptr;
3008 int ti=0;
3009 if (!typeIsClass && (ti=type.find('<'))!=-1)
3010 {
3011 typeIsClass=resolver.resolveClass(ctx,type.left(ti))!=nullptr;
3012 }
3013 }
3014 if (typeIsClass) // now we still have to check if the arguments are
3015 // types or values. Since we do not have complete type info
3016 // we need to rely on heuristics :-(
3017 {
3018 if (root->argList.empty())
3019 {
3020 result=FALSE; // empty arg list -> function prototype.
3021 AUTO_TRACE_EXIT("empty arg list: result={}",result);
3022 return result;
3023 }
3024 for (const Argument &a : root->argList)
3025 {
3026 static const reg::Ex initChars(R"([\d"'&*!^]+)");
3027 reg::Match match;
3028 if (!a.name.isEmpty() || !a.defval.isEmpty())
3029 {
3030 std::string name = a.name.str();
3031 if (reg::search(name,match,initChars) && match.position()==0)
3032 {
3033 result=TRUE;
3034 }
3035 else
3036 {
3037 result=FALSE; // arg has (type,name) pair -> function prototype
3038 }
3039 AUTO_TRACE_EXIT("function prototype: result={}",result);
3040 return result;
3041 }
3042 if (!a.type.isEmpty() &&
3043 (a.type.at(a.type.length()-1)=='*' ||
3044 a.type.at(a.type.length()-1)=='&'))
3045 // type ends with * or & => pointer or reference
3046 {
3047 result=FALSE;
3048 AUTO_TRACE_EXIT("pointer or reference: result={}",result);
3049 return result;
3050 }
3051 if (a.type.isEmpty() || resolver.resolveClass(ctx,a.type)!=nullptr)
3052 {
3053 result=FALSE; // arg type is a known type
3054 AUTO_TRACE_EXIT("known type: result={}",result);
3055 return result;
3056 }
3057 if (checkIfTypedef(ctx,fd,a.type))
3058 {
3059 result=FALSE; // argument is a typedef
3060 AUTO_TRACE_EXIT("typedef: result={}",result);
3061 return result;
3062 }
3063 std::string atype = a.type.str();
3064 if (reg::search(atype,match,initChars) && match.position()==0)
3065 {
3066 result=TRUE; // argument type starts with typical initializer char
3067 AUTO_TRACE_EXIT("argument with init char: result={}",result);
3068 return result;
3069 }
3070 std::string resType=resolveTypeDef(ctx,a.type).str();
3071 if (resType.empty()) resType=atype;
3072 static const reg::Ex idChars(R"(\a\w*)");
3073 if (reg::search(resType,match,idChars) && match.position()==0) // resType starts with identifier
3074 {
3075 resType=match.str();
3076 if (resType=="int" || resType=="long" ||
3077 resType=="float" || resType=="double" ||
3078 resType=="char" || resType=="void" ||
3079 resType=="signed" || resType=="unsigned" ||
3080 resType=="const" || resType=="volatile" )
3081 {
3082 result=FALSE; // type keyword -> function prototype
3083 AUTO_TRACE_EXIT("type keyword: result={}",result);
3084 return result;
3085 }
3086 }
3087 }
3088 result=TRUE;
3089 }
3090
3091 AUTO_TRACE_EXIT("end: result={}",result);
3092 return result;
3093}
3094
3095//--------------------------------------------------------------------------------------
3096
3097/*! Searches for the end of a template in prototype \a s starting from
3098 * character position \a startPos. If the end was found the position
3099 * of the closing > is returned, otherwise -1 is returned.
3100 *
3101 * Handles exotic cases such as
3102 * \code
3103 * Class<(id<0)>
3104 * Class<bits<<2>
3105 * Class<"<">
3106 * Class<'<'>
3107 * Class<(")<")>
3108 * \endcode
3109 */
3110static int findEndOfTemplate(const QCString &s,size_t startPos)
3111{
3112 // locate end of template
3113 size_t e=startPos;
3114 int brCount=1;
3115 int roundCount=0;
3116 size_t len = s.length();
3117 bool insideString=FALSE;
3118 bool insideChar=FALSE;
3119 char pc = 0;
3120 while (e<len && brCount!=0)
3121 {
3122 char c=s.at(e);
3123 switch(c)
3124 {
3125 case '<':
3126 if (!insideString && !insideChar)
3127 {
3128 if (e<len-1 && s.at(e+1)=='<')
3129 e++;
3130 else if (roundCount==0)
3131 brCount++;
3132 }
3133 break;
3134 case '>':
3135 if (!insideString && !insideChar)
3136 {
3137 if (e<len-1 && s.at(e+1)=='>')
3138 e++;
3139 else if (roundCount==0)
3140 brCount--;
3141 }
3142 break;
3143 case '(':
3144 if (!insideString && !insideChar)
3145 roundCount++;
3146 break;
3147 case ')':
3148 if (!insideString && !insideChar)
3149 roundCount--;
3150 break;
3151 case '"':
3152 if (!insideChar)
3153 {
3154 if (insideString && pc!='\\')
3155 insideString=FALSE;
3156 else
3157 insideString=TRUE;
3158 }
3159 break;
3160 case '\'':
3161 if (!insideString)
3162 {
3163 if (insideChar && pc!='\\')
3164 insideChar=FALSE;
3165 else
3166 insideChar=TRUE;
3167 }
3168 break;
3169 }
3170 pc = c;
3171 e++;
3172 }
3173 return brCount==0 ? static_cast<int>(e) : -1;
3174}
3175
3176//--------------------------------------------------------------------------------------
3177
3178static void addVariable(const Entry *root,int isFuncPtr=-1)
3179{
3180 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
3181
3182 AUTO_TRACE("VARIABLE_SEC: type='{}' name='{}' args='{}' bodyLine={} endBodyLine={} mGrpId={} relates='{}'",
3183 root->type, root->name, root->args, root->bodyLine, root->endBodyLine, root->mGrpId, root->relates);
3184 //printf("root->parent->name=%s\n",qPrint(root->parent->name));
3185
3186 QCString type = root->type;
3187 QCString name = root->name;
3188 QCString args = root->args;
3189 if (type.isEmpty() && name.find("operator")==-1 &&
3190 (name.find('*')!=-1 || name.find('&')!=-1))
3191 {
3192 // recover from parse error caused by redundant braces
3193 // like in "int *(var[10]);", which is parsed as
3194 // type="" name="int *" args="(var[10])"
3195
3196 type=name;
3197 std::string sargs = args.str();
3198 static const reg::Ex reName(R"(\a\w*)");
3199 reg::Match match;
3200 if (reg::search(sargs,match,reName))
3201 {
3202 name = match.str(); // e.g. 'var' in '(var[10])'
3203 sargs = match.suffix().str(); // e.g. '[10]) in '(var[10])'
3204 size_t j = sargs.find(')');
3205 if (j!=std::string::npos) args=sargs.substr(0,j); // extract, e.g '[10]' from '[10])'
3206 }
3207 }
3208 else
3209 {
3210 int i=isFuncPtr;
3211 if (i==-1 && (root->spec.isAlias())==0) i=findFunctionPtr(type.str(),root->lang); // for typedefs isFuncPtr is not yet set
3212 AUTO_TRACE_ADD("functionPtr={}",i!=-1?"yes":"no");
3213 if (i>=0) // function pointer
3214 {
3215 int ai = type.find('[',i);
3216 if (ai>i) // function pointer array
3217 {
3218 args.prepend(type.right(type.length()-ai));
3219 type=type.left(ai);
3220 }
3221 else if (type.find(')',i)!=-1) // function ptr, not variable like "int (*bla)[10]"
3222 {
3223 type=type.left(type.length()-1);
3224 args.prepend(") ");
3225 }
3226 }
3227 }
3228 AUTO_TRACE_ADD("after correction: type='{}' name='{}' args='{}'",type,name,args);
3229
3230 QCString scope;
3231 name=removeRedundantWhiteSpace(name);
3232
3233 // find the scope of this variable
3234 int index = computeQualifiedIndex(name);
3235 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3236 // grouped members are stored with full scope
3237 {
3238 buildScopeFromQualifiedName(name.left(index+2),root->lang,root->tagInfo());
3239 scope=name.left(index);
3240 name=name.mid(index+2);
3241 }
3242 else
3243 {
3244 Entry *p = root->parent();
3245 while (p->section.isScope())
3246 {
3247 QCString scopeName = p->name;
3248 if (!scopeName.isEmpty())
3249 {
3250 scope.prepend(scopeName);
3251 break;
3252 }
3253 p=p->parent();
3254 }
3255 }
3256
3257 QCString type_s = type;
3258 type=type.stripWhiteSpace();
3259 ClassDefMutable *cd=nullptr;
3260 bool isRelated=FALSE;
3261 bool isMemberOf=FALSE;
3262
3263 QCString classScope=stripAnonymousNamespaceScope(scope);
3264 if (root->lang==SrcLangExt::CSharp)
3265 {
3266 classScope=mangleCSharpGenericName(classScope);
3267 }
3268 else
3269 {
3270 classScope=stripTemplateSpecifiersFromScope(classScope,FALSE);
3271 }
3272 QCString annScopePrefix=scope.left(scope.length()-classScope.length());
3273
3274
3275 // Look for last :: not part of template specifier
3276 int p=-1;
3277 for (size_t i=0;i<name.length()-1;i++)
3278 {
3279 if (name[i]==':' && name[i+1]==':')
3280 {
3281 p=static_cast<int>(i);
3282 }
3283 else if (name[i]=='<') // skip over template parts,
3284 // i.e. A::B<C::D> => p=1 and
3285 // A<B::C>::D => p=8
3286 {
3287 int e = findEndOfTemplate(name,i+1);
3288 if (e!=-1) i=static_cast<int>(e);
3289 }
3290 }
3291
3292 if (p!=-1) // found it
3293 {
3294 if (type=="friend class" || type=="friend struct" ||
3295 type=="friend union")
3296 {
3297 cd=getClassMutable(scope);
3298 if (cd)
3299 {
3300 addVariableToClass(root, // entry
3301 cd, // class to add member to
3302 MemberType::Friend, // type of member
3303 type, // type value as string
3304 name, // name of the member
3305 args, // arguments as string
3306 FALSE, // from Anonymous scope
3307 nullptr, // anonymous member
3308 Protection::Public, // protection
3309 Relationship::Member // related to a class
3310 );
3311 }
3312 }
3313 if (root->bodyLine!=-1 && root->endBodyLine!=-1) // store the body location for later use
3314 {
3315 Doxygen::staticInitMap.emplace(name.str(),BodyInfo{root->startLine,root->bodyLine,root->endBodyLine});
3316 }
3317
3318
3319 AUTO_TRACE_ADD("static variable {} body=[{}..{}]",name,root->bodyLine,root->endBodyLine);
3320 return; /* skip this member, because it is a
3321 * static variable definition (always?), which will be
3322 * found in a class scope as well, but then we know the
3323 * correct protection level, so only then it will be
3324 * inserted in the correct list!
3325 */
3326 }
3327
3329 if (type=="@")
3331 else if (type_s.startsWith("typedef "))
3332 mtype=MemberType::Typedef;
3333 else if (type_s.startsWith("friend "))
3334 mtype=MemberType::Friend;
3335 else if (root->mtype==MethodTypes::Property)
3337 else if (root->mtype==MethodTypes::Event)
3338 mtype=MemberType::Event;
3339 else if (type.find("sequence<") != -1)
3340 mtype=sliceOpt ? MemberType::Sequence : MemberType::Typedef;
3341 else if (type.find("dictionary<") != -1)
3343
3344 if (!root->relates.isEmpty()) // related variable
3345 {
3346 isRelated=TRUE;
3347 isMemberOf=(root->relatesType==RelatesType::MemberOf);
3348 if (getClass(root->relates)==nullptr && !scope.isEmpty())
3349 scope=mergeScopes(scope,root->relates);
3350 else
3351 scope=root->relates;
3352 }
3353
3354 cd=getClassMutable(scope);
3355 if (cd==nullptr && classScope!=scope) cd=getClassMutable(classScope);
3356 if (cd)
3357 {
3358 MemberDef *md=nullptr;
3359
3360 // if cd is an anonymous (=tag less) scope we insert the member
3361 // into a non-anonymous parent scope as well. This is needed to
3362 // be able to refer to it using \var or \fn
3363
3364 //int indentDepth=0;
3365 int si=scope.find('@');
3366 //int anonyScopes = 0;
3367 //bool added=FALSE;
3368
3369 bool inlineSimpleStructs = Config_getBool(INLINE_SIMPLE_STRUCTS);
3370 Relationship relationship = isMemberOf ? Relationship::Foreign :
3371 isRelated ? Relationship::Related :
3372 Relationship::Member ;
3373 if (si!=-1 && !inlineSimpleStructs) // anonymous scope or type
3374 {
3375 QCString pScope;
3376 ClassDefMutable *pcd=nullptr;
3377 pScope = scope.left(std::max(si-2,0)); // scope without tag less parts
3378 if (!pScope.isEmpty())
3379 pScope.prepend(annScopePrefix);
3380 else if (annScopePrefix.length()>2)
3381 pScope=annScopePrefix.left(annScopePrefix.length()-2);
3382 if (name.at(0)!='@')
3383 {
3384 if (!pScope.isEmpty() && (pcd=getClassMutable(pScope)))
3385 {
3386 AUTO_TRACE_ADD("Adding anonymous member to scope '{}'",pScope);
3387 md=addVariableToClass(root, // entry
3388 pcd, // class to add member to
3389 mtype, // member type
3390 type, // type value as string
3391 name, // member name
3392 args, // arguments as string
3393 TRUE, // from anonymous scope
3394 nullptr, // from anonymous member
3395 root->protection,
3396 relationship
3397 );
3398 //added=TRUE;
3399 }
3400 else // anonymous scope inside namespace or file => put variable in the global scope
3401 {
3402 if (mtype==MemberType::Variable)
3403 {
3404 AUTO_TRACE_ADD("Adding anonymous member to global scope '{}'");
3405 md=addVariableToFile(root,mtype,pScope,type,name,args,TRUE,nullptr);
3406 }
3407 //added=TRUE;
3408 }
3409 }
3410 }
3411
3412 addVariableToClass(root, // entry
3413 cd, // class to add member to
3414 mtype, // member type
3415 type, // type value as string
3416 name, // name of the member
3417 args, // arguments as string
3418 FALSE, // from anonymous scope
3419 md, // from anonymous member
3420 root->protection,
3421 relationship
3422 );
3423 }
3424 else if (!name.isEmpty()) // global variable
3425 {
3426 addVariableToFile(root,mtype,scope,type,name,args,FALSE,/*nullptr,*/nullptr);
3427 }
3428
3429}
3430
3431//----------------------------------------------------------------------
3432// Searches the Entry tree for typedef documentation sections.
3433// If found they are stored in their class or in the global list.
3434static void buildTypedefList(const Entry *root)
3435{
3436 //printf("buildVarList(%s)\n",qPrint(rootNav->name()));
3437 if (!root->name.isEmpty() &&
3438 root->section.isVariable() &&
3439 root->type.find("typedef ")!=-1 // its a typedef
3440 )
3441 {
3442 AUTO_TRACE();
3444 QCString scope;
3445 int index = computeQualifiedIndex(rname);
3446 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3447 // grouped members are stored with full scope
3448 {
3449 buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
3450 scope=rname.left(index);
3451 rname=rname.mid(index+2);
3452 }
3453 else
3454 {
3455 scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
3456 }
3459 MemberName *mn = Doxygen::functionNameLinkedMap->find(rname);
3460 bool found=false;
3461 if (mn) // symbol with the same name already found
3462 {
3463 for (auto &imd : *mn)
3464 {
3465 if (!imd->isTypedef())
3466 continue;
3467
3468 QCString rtype = root->type;
3469 rtype.stripPrefix("typedef ");
3470
3471 // merge the typedefs only if they're not both grouped, and both are
3472 // either part of the same class, part of the same namespace, or both
3473 // are global (i.e., neither in a class or a namespace)
3474 bool notBothGrouped = root->groups.empty() || imd->getGroupDef()==nullptr; // see example #100
3475 bool bothSameScope = (!cd && !nd) || (cd && imd->getClassDef() == cd) || (nd && imd->getNamespaceDef() == nd);
3476 //printf("imd->isTypedef()=%d imd->typeString()=%s root->type=%s\n",imd->isTypedef(),
3477 // qPrint(imd->typeString()),qPrint(root->type));
3478 if (notBothGrouped && bothSameScope && imd->typeString()==rtype)
3479 {
3480 MemberDefMutable *md = toMemberDefMutable(imd.get());
3481 if (md)
3482 {
3483 md->setDocumentation(root->doc,root->docFile,root->docLine);
3485 md->setDocsForDefinition(!root->proto);
3486 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3488 md->setRefItems(root->sli);
3489 md->setRequirementReferences(root->rqli);
3490 md->addQualifiers(root->qualifiers);
3491
3492 // merge ingroup specifiers
3493 if (md->getGroupDef()==nullptr && !root->groups.empty())
3494 {
3495 addMemberToGroups(root,md);
3496 }
3497 else if (md->getGroupDef()!=nullptr && root->groups.empty())
3498 {
3499 //printf("existing member is grouped, new member not\n");
3500 }
3501 else if (md->getGroupDef()!=nullptr && !root->groups.empty())
3502 {
3503 //printf("both members are grouped\n");
3504 }
3505 found=true;
3506 break;
3507 }
3508 }
3509 }
3510 }
3511 if (found)
3512 {
3513 AUTO_TRACE_ADD("typedef '{}' already found",rname);
3514 // mark the entry as processed, as we copied everything from it elsewhere
3515 // also, otherwise, due to containing `typedef` it may later get treated
3516 // as a function typedef in filterMemberDocumentation, which is incorrect
3517 root->markAsProcessed();
3518 }
3519 else
3520 {
3521 AUTO_TRACE_ADD("new typedef '{}'",rname);
3522 addVariable(root);
3523 }
3524
3525 }
3526 for (const auto &e : root->children())
3527 if (!e->section.isEnum())
3528 buildTypedefList(e.get());
3529}
3530
3531//----------------------------------------------------------------------
3532// Searches the Entry tree for sequence documentation sections.
3533// If found they are stored in the global list.
3534static void buildSequenceList(const Entry *root)
3535{
3536 if (!root->name.isEmpty() &&
3537 root->section.isVariable() &&
3538 root->type.find("sequence<")!=-1 // it's a sequence
3539 )
3540 {
3541 AUTO_TRACE();
3542 addVariable(root);
3543 }
3544 for (const auto &e : root->children())
3545 if (!e->section.isEnum())
3546 buildSequenceList(e.get());
3547}
3548
3549//----------------------------------------------------------------------
3550// Searches the Entry tree for dictionary documentation sections.
3551// If found they are stored in the global list.
3552static void buildDictionaryList(const Entry *root)
3553{
3554 if (!root->name.isEmpty() &&
3555 root->section.isVariable() &&
3556 root->type.find("dictionary<")!=-1 // it's a dictionary
3557 )
3558 {
3559 AUTO_TRACE();
3560 addVariable(root);
3561 }
3562 for (const auto &e : root->children())
3563 if (!e->section.isEnum())
3564 buildDictionaryList(e.get());
3565}
3566
3567//----------------------------------------------------------------------
3568// Searches the Entry tree for Variable documentation sections.
3569// If found they are stored in their class or in the global list.
3570
3571static void buildVarList(const Entry *root)
3572{
3573 //printf("buildVarList(%s) section=%08x\n",qPrint(rootNav->name()),rootNav->section());
3574 int isFuncPtr=-1;
3575 if (!root->name.isEmpty() &&
3576 (root->type.isEmpty() || g_compoundKeywords.find(root->type.str())==g_compoundKeywords.end()) &&
3577 (
3578 (root->section.isVariable() && // it's a variable
3579 root->type.find("typedef ")==-1 // and not a typedef
3580 ) ||
3581 (root->section.isFunction() && // or maybe a function pointer variable
3582 (isFuncPtr=findFunctionPtr(root->type.str(),root->lang))!=-1
3583 ) ||
3584 (root->section.isFunction() && // class variable initialized by constructor
3586 )
3587 )
3588 ) // documented variable
3589 {
3590 AUTO_TRACE();
3591 addVariable(root,isFuncPtr);
3592 }
3593 for (const auto &e : root->children())
3594 if (!e->section.isEnum())
3595 buildVarList(e.get());
3596}
3597
3598//----------------------------------------------------------------------
3599// Searches the Entry tree for Interface sections (UNO IDL only).
3600// If found they are stored in their service or in the global list.
3601//
3602
3604 const Entry *root,
3605 ClassDefMutable *cd,
3606 QCString const& rname)
3607{
3608 FileDef *fd = root->fileDef();
3609 enum MemberType type = root->section.isExportedInterface() ? MemberType::Interface : MemberType::Service;
3610 QCString fileName = root->fileName;
3611 if (fileName.isEmpty() && root->tagInfo())
3612 {
3613 fileName = root->tagInfo()->tagName;
3614 }
3615 auto md = createMemberDef(
3616 fileName, root->startLine, root->startColumn, root->type, rname,
3617 "", "", root->protection, root->virt, root->isStatic, Relationship::Member,
3618 type, ArgumentList(), root->argList, root->metaData);
3619 auto mmd = toMemberDefMutable(md.get());
3620 mmd->setTagInfo(root->tagInfo());
3621 mmd->setMemberClass(cd);
3622 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3623 mmd->setDocsForDefinition(false);
3624 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3625 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3626 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3627 mmd->setMemberSpecifiers(root->spec);
3628 mmd->setVhdlSpecifiers(root->vhdlSpec);
3629 mmd->setMemberGroupId(root->mGrpId);
3630 mmd->setTypeConstraints(root->typeConstr);
3631 mmd->setLanguage(root->lang);
3632 mmd->setBodyDef(fd);
3633 mmd->setFileDef(fd);
3634 mmd->addSectionsToDefinition(root->anchors);
3635 QCString const def = root->type + " " + rname;
3636 mmd->setDefinition(def);
3638 mmd->addQualifiers(root->qualifiers);
3639
3640 AUTO_TRACE("Interface member: fileName='{}' type='{}' name='{}' mtype='{}' prot={} virt={} state={} proto={} def='{}'",
3641 fileName,root->type,rname,type,root->protection,root->virt,root->isStatic,root->proto,def);
3642
3643 // add member to the class cd
3644 cd->insertMember(md.get());
3645 // also add the member as a "base" (to get nicer diagrams)
3646 // "optional" interface/service get Protected which turns into dashed line
3647 BaseInfo base(rname,
3648 root->spec.isOptional() ? Protection::Protected : Protection::Public, Specifier::Normal);
3649 TemplateNameMap templateNames;
3650 findClassRelation(root,cd,cd,&base,templateNames,DocumentedOnly,true) ||
3651 findClassRelation(root,cd,cd,&base,templateNames,Undocumented,true);
3652 // add file to list of used files
3653 cd->insertUsedFile(fd);
3654
3655 addMemberToGroups(root,md.get());
3657 root->markAsProcessed();
3658 mmd->setRefItems(root->sli);
3659 mmd->setRequirementReferences(root->rqli);
3660
3661 // add member to the global list of all members
3662 MemberName *mn = Doxygen::memberNameLinkedMap->add(rname);
3663 mn->push_back(std::move(md));
3664}
3665
3666static void buildInterfaceAndServiceList(const Entry *root)
3667{
3668 if (root->section.isExportedInterface() || root->section.isIncludedService())
3669 {
3670 AUTO_TRACE("Exported interface/included service: type='{}' scope='{}' name='{}' args='{}'"
3671 " relates='{}' relatesType='{}' file='{}' line={} bodyLine={} #tArgLists={}"
3672 " mGrpId={} spec={} proto={} docFile='{}'",
3673 root->type, root->parent()->name, root->name, root->args,
3674 root->relates, root->relatesType, root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(),
3675 root->mGrpId, root->spec, root->proto, root->docFile);
3676
3678
3679 if (!rname.isEmpty())
3680 {
3681 QCString scope = root->parent()->name;
3682 ClassDefMutable *cd = getClassMutable(scope);
3683 assert(cd);
3684 if (cd && ((ClassDef::Interface == cd->compoundType()) ||
3685 (ClassDef::Service == cd->compoundType()) ||
3687 {
3689 }
3690 else
3691 {
3692 assert(false); // was checked by scanner.l
3693 }
3694 }
3695 else if (rname.isEmpty())
3696 {
3697 warn(root->fileName,root->startLine,
3698 "Illegal member name found.");
3699 }
3700 }
3701 // can only have these in IDL anyway
3702 switch (root->lang)
3703 {
3704 case SrcLangExt::Unknown: // fall through (root node always is Unknown)
3705 case SrcLangExt::IDL:
3706 for (const auto &e : root->children()) buildInterfaceAndServiceList(e.get());
3707 break;
3708 default:
3709 return; // nothing to do here
3710 }
3711}
3712
3713
3714//----------------------------------------------------------------------
3715// Searches the Entry tree for Function sections.
3716// If found they are stored in their class or in the global list.
3717
3718static void addMethodToClass(const Entry *root,ClassDefMutable *cd,
3719 const QCString &rtype,const QCString &rname,const QCString &rargs,
3720 bool isFriend,
3721 Protection protection,bool stat,Specifier virt,TypeSpecifier spec,
3722 const QCString &relates
3723 )
3724{
3725 FileDef *fd=root->fileDef();
3726
3727 QCString type = rtype;
3728 QCString args = rargs;
3729
3731 name.stripPrefix("::");
3732
3734 if (isFriend) mtype=MemberType::Friend;
3735 else if (root->mtype==MethodTypes::Signal) mtype=MemberType::Signal;
3736 else if (root->mtype==MethodTypes::Slot) mtype=MemberType::Slot;
3737 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType::DCOP;
3738
3739 // strip redundant template specifier for constructors
3740 int i = -1;
3741 int j = -1;
3742 if ((fd==nullptr || fd->getLanguage()==SrcLangExt::Cpp) &&
3743 !name.startsWith("operator ") && // not operator
3744 (i=name.find('<'))!=-1 && // containing <
3745 (j=name.find('>'))!=-1 && // or >
3746 (j!=i+2 || name.at(i+1)!='=') // but not the C++20 spaceship operator <=>
3747 )
3748 {
3749 name=name.left(i);
3750 }
3751
3752 QCString fileName = root->fileName;
3753 if (fileName.isEmpty() && root->tagInfo())
3754 {
3755 fileName = root->tagInfo()->tagName;
3756 }
3757
3758 //printf("root->name='%s; args='%s' root->argList='%s'\n",
3759 // qPrint(root->name),qPrint(args),qPrint(argListToString(root->argList))
3760 // );
3761
3762 // adding class member
3763 Relationship relationship = relates.isEmpty() ? Relationship::Member :
3764 root->relatesType==RelatesType::MemberOf ? Relationship::Foreign :
3765 Relationship::Related ;
3766 auto md = createMemberDef(
3767 fileName,root->startLine,root->startColumn,
3768 type,name,args,root->exception,
3769 protection,virt,
3770 stat && root->relatesType!=RelatesType::MemberOf,
3771 relationship,
3772 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3773 root->argList, root->metaData);
3774 auto mmd = toMemberDefMutable(md.get());
3775 mmd->setTagInfo(root->tagInfo());
3776 mmd->setMemberClass(cd);
3777 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3778 mmd->setDocsForDefinition(!root->proto);
3779 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3780 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3781 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3782 mmd->setMemberSpecifiers(spec);
3783 mmd->setVhdlSpecifiers(root->vhdlSpec);
3784 mmd->setMemberGroupId(root->mGrpId);
3785 mmd->setTypeConstraints(root->typeConstr);
3786 mmd->setLanguage(root->lang);
3787 mmd->setRequiresClause(root->req);
3788 mmd->setId(root->id);
3789 mmd->setBodyDef(fd);
3790 mmd->setFileDef(fd);
3791 mmd->addSectionsToDefinition(root->anchors);
3792 QCString def;
3794 SrcLangExt lang = cd->getLanguage();
3795 QCString scopeSeparator=getLanguageSpecificSeparator(lang);
3796 if (scopeSeparator!="::")
3797 {
3798 qualScope = substitute(qualScope,"::",scopeSeparator);
3799 }
3800 if (lang==SrcLangExt::PHP)
3801 {
3802 // for PHP we use Class::method and Namespace\method
3803 scopeSeparator="::";
3804 }
3805 if (!relates.isEmpty() || isFriend || Config_getBool(HIDE_SCOPE_NAMES))
3806 {
3807 if (!type.isEmpty())
3808 {
3809 def=type+" "+name; //+optArgs;
3810 }
3811 else
3812 {
3813 def=name; //+optArgs;
3814 }
3815 }
3816 else
3817 {
3818 if (!type.isEmpty())
3819 {
3820 def=type+" "+qualScope+scopeSeparator+name; //+optArgs;
3821 }
3822 else
3823 {
3824 def=qualScope+scopeSeparator+name; //+optArgs;
3825 }
3826 }
3827 def.stripPrefix("friend ");
3828 mmd->setDefinition(def);
3830 mmd->addQualifiers(root->qualifiers);
3831
3832 AUTO_TRACE("function member: type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
3833 type, qualScope, rname, args, root->proto, def);
3834
3835 // add member to the class cd
3836 cd->insertMember(md.get());
3837 // add file to list of used files
3838 cd->insertUsedFile(fd);
3839
3840 addMemberToGroups(root,md.get());
3842 root->markAsProcessed();
3843 mmd->setRefItems(root->sli);
3844 mmd->setRequirementReferences(root->rqli);
3845
3846 // add member to the global list of all members
3847 //printf("Adding member=%s class=%s\n",qPrint(md->name()),qPrint(cd->name()));
3849 mn->push_back(std::move(md));
3850}
3851
3852//------------------------------------------------------------------------------------------
3853
3854static void addGlobalFunction(const Entry *root,const QCString &rname,const QCString &sc)
3855{
3856 QCString scope = sc;
3857
3858 // new global function
3860 auto md = createMemberDef(
3861 root->fileName,root->startLine,root->startColumn,
3862 root->type,name,root->args,root->exception,
3863 root->protection,root->virt,root->isStatic,Relationship::Member,
3865 !root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3866 root->argList,root->metaData);
3867 auto mmd = toMemberDefMutable(md.get());
3868 mmd->setTagInfo(root->tagInfo());
3869 mmd->setLanguage(root->lang);
3870 mmd->setId(root->id);
3871 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3872 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3873 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3874 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
3875 mmd->setDocsForDefinition(!root->proto);
3876 mmd->setTypeConstraints(root->typeConstr);
3877 //md->setBody(root->body);
3878 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3879 FileDef *fd=root->fileDef();
3880 mmd->setBodyDef(fd);
3881 mmd->addSectionsToDefinition(root->anchors);
3882 mmd->setMemberSpecifiers(root->spec);
3883 mmd->setVhdlSpecifiers(root->vhdlSpec);
3884 mmd->setMemberGroupId(root->mGrpId);
3885 mmd->setRequiresClause(root->req);
3886 mmd->setExplicitExternal(root->explicitExternal,root->fileName,root->startLine,root->startColumn);
3887
3888 NamespaceDefMutable *nd = nullptr;
3889 // see if the function is inside a namespace that was not part of
3890 // the name already (in that case nd should be non-zero already)
3891 if (root->parent()->section.isNamespace())
3892 {
3893 //QCString nscope=removeAnonymousScopes(root->parent()->name);
3894 QCString nscope=root->parent()->name;
3895 if (!nscope.isEmpty())
3896 {
3897 nd = getResolvedNamespaceMutable(nscope);
3898 }
3899 }
3900 else if (root->parent()->section.isGroupDoc() && !scope.isEmpty())
3901 {
3903 }
3904
3905 if (!scope.isEmpty())
3906 {
3908 if (sep!="::")
3909 {
3910 scope = substitute(scope,"::",sep);
3911 }
3912 scope+=sep;
3913 }
3914
3915 if (Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python) scope = "";
3916 QCString def;
3917 //QCString optArgs = root->argList.empty() ? QCString() : root->args;
3918 if (!root->type.isEmpty())
3919 {
3920 def=root->type+" "+scope+name; //+optArgs;
3921 }
3922 else
3923 {
3924 def=scope+name; //+optArgs;
3925 }
3926 AUTO_TRACE("new non-member function type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
3927 root->type,scope,rname,root->args,root->proto,def);
3928 mmd->setDefinition(def);
3930 mmd->addQualifiers(root->qualifiers);
3931
3932 mmd->setRefItems(root->sli);
3933 mmd->setRequirementReferences(root->rqli);
3934 if (nd && !nd->name().isEmpty() && nd->name().at(0)!='@')
3935 {
3936 // add member to namespace
3937 mmd->setNamespace(nd);
3938 nd->insertMember(md.get());
3939 }
3940 if (fd)
3941 {
3942 // add member to the file (we do this even if we have already
3943 // inserted it into the namespace)
3944 mmd->setFileDef(fd);
3945 fd->insertMember(md.get());
3946 }
3947
3948 addMemberToGroups(root,md.get());
3950 if (root->relatesType == RelatesType::Simple) // if this is a relatesalso command,
3951 // allow find Member to pick it up
3952 {
3953 root->markAsProcessed(); // Otherwise we have finished with this entry.
3954 }
3955
3956 // add member to the list of file members
3958 mn->push_back(std::move(md));
3959}
3960
3961//------------------------------------------------------------------------------------------
3962
3963static void buildFunctionList(const Entry *root)
3964{
3965 if (root->section.isFunction())
3966 {
3967 AUTO_TRACE("member function: type='{}' scope='{}' name='{}' args='{}' relates='{}' relatesType='{}'"
3968 " file='{}' line={} bodyLine={} #tArgLists={} mGrpId={}"
3969 " spec={} proto={} docFile='{}'",
3970 root->type, root->parent()->name, root->name, root->args, root->relates, root->relatesType,
3971 root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(), root->mGrpId,
3972 root->spec, root->proto, root->docFile);
3973
3974 bool isFriend=root->type=="friend" || root->type.find("friend ")!=-1;
3976 //printf("rname=%s\n",qPrint(rname));
3977
3978 QCString scope;
3979 int index = computeQualifiedIndex(rname);
3980 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3981 // grouped members are stored with full scope
3982 {
3983 buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
3984 scope=rname.left(index);
3985 rname=rname.mid(index+2);
3986 }
3987 else
3988 {
3989 scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
3990 }
3991 if (!rname.isEmpty() && scope.find('@')==-1)
3992 {
3993 // check if this function's parent is a class
3994 if (root->lang==SrcLangExt::CSharp)
3995 {
3996 scope=mangleCSharpGenericName(scope);
3997 }
3998 else
3999 {
4001 }
4002
4003 FileDef *rfd=root->fileDef();
4004
4005 int memIndex=rname.findRev("::");
4006
4008 if (cd && scope+"::"==rname.left(scope.length()+2)) // found A::f inside A
4009 {
4010 // strip scope from name
4011 rname=rname.right(rname.length()-root->parent()->name.length()-2);
4012 }
4013
4014 bool isMember=FALSE;
4015 if (memIndex!=-1)
4016 {
4017 int ts=rname.find('<');
4018 int te=rname.find('>');
4019 if (memIndex>0 && (ts==-1 || te==-1))
4020 {
4021 // note: the following code was replaced by inMember=TRUE to deal with a
4022 // function rname='X::foo' of class X inside a namespace also called X...
4023 // bug id 548175
4024 //nd = Doxygen::namespaceLinkedMap->find(rname.left(memIndex));
4025 //isMember = nd==nullptr;
4026 //if (nd)
4027 //{
4028 // // strip namespace scope from name
4029 // scope=rname.left(memIndex);
4030 // rname=rname.right(rname.length()-memIndex-2);
4031 //}
4032 isMember = TRUE;
4033 }
4034 else
4035 {
4036 isMember=memIndex<ts || memIndex>te;
4037 }
4038 }
4039
4040 if (!root->parent()->name.isEmpty() && root->parent()->section.isCompound() && cd)
4041 {
4042 AUTO_TRACE_ADD("member '{}' of class '{}'", rname,cd->name());
4043 addMethodToClass(root,cd,root->type,rname,root->args,isFriend,
4044 root->protection,root->isStatic,root->virt,root->spec,root->relates);
4045 }
4046 else if (root->parent()->section.isObjcImpl() && cd)
4047 {
4048 const MemberDef *md = cd->getMemberByName(rname);
4049 if (md)
4050 {
4051 MemberDefMutable *mdm = toMemberDefMutable(const_cast<MemberDef*>(md));
4052 if (mdm)
4053 {
4054 mdm->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
4055 mdm->setBodyDef(root->fileDef());
4056 }
4057 }
4058 }
4059 else if (!root->parent()->section.isCompound() && !root->parent()->section.isObjcImpl() &&
4060 !isMember &&
4061 (root->relates.isEmpty() || root->relatesType==RelatesType::Duplicate) &&
4062 !root->type.startsWith("extern ") && !root->type.startsWith("typedef ")
4063 )
4064 // no member => unrelated function
4065 {
4066 /* check the uniqueness of the function name in the file.
4067 * A file could contain a function prototype and a function definition
4068 * or even multiple function prototypes.
4069 */
4070 bool found=FALSE;
4071 MemberDef *md_found=nullptr;
4072 MemberName *mn = Doxygen::functionNameLinkedMap->find(rname);
4073 if (mn)
4074 {
4075 AUTO_TRACE_ADD("function '{}' already found",rname);
4076 for (const auto &imd : *mn)
4077 {
4078 MemberDefMutable *md = toMemberDefMutable(imd.get());
4079 if (md)
4080 {
4081 const NamespaceDef *mnd = md->getNamespaceDef();
4082 NamespaceDef *rnd = nullptr;
4083 //printf("root namespace=%s\n",qPrint(rootNav->parent()->name()));
4084 QCString fullScope = scope;
4085 QCString parentScope = root->parent()->name;
4086 if (!parentScope.isEmpty() && !leftScopeMatch(parentScope,scope))
4087 {
4088 if (!scope.isEmpty()) fullScope.prepend("::");
4089 fullScope.prepend(parentScope);
4090 }
4091 //printf("fullScope=%s\n",qPrint(fullScope));
4092 rnd = getResolvedNamespace(fullScope);
4093 const FileDef *mfd = md->getFileDef();
4094 QCString nsName,rnsName;
4095 if (mnd) nsName = mnd->name();
4096 if (rnd) rnsName = rnd->name();
4097 //printf("matching arguments for %s%s %s%s\n",
4098 // qPrint(md->name()),md->argsString(),qPrint(rname),qPrint(argListToString(root->argList)));
4099 const ArgumentList &mdAl = md->argumentList();
4100 const ArgumentList &mdTempl = md->templateArguments();
4101
4102 // in case of template functions, we need to check if the
4103 // functions have the same number of template parameters
4104 bool sameTemplateArgs = TRUE;
4105 bool matchingReturnTypes = TRUE;
4106 bool sameRequiresClause = TRUE;
4107 if (!mdTempl.empty() && !root->tArgLists.empty())
4108 {
4109 sameTemplateArgs = matchTemplateArguments(mdTempl,root->tArgLists.back());
4110 if (md->typeString()!=removeRedundantWhiteSpace(root->type))
4111 {
4112 matchingReturnTypes = FALSE;
4113 }
4114 if (md->requiresClause()!=root->req)
4115 {
4116 sameRequiresClause = FALSE;
4117 }
4118 }
4119 else if (!mdTempl.empty() || !root->tArgLists.empty())
4120 { // if one has template parameters and the other doesn't then that also counts as a
4121 // difference
4122 sameTemplateArgs = FALSE;
4123 }
4124
4125 bool staticsInDifferentFiles =
4126 root->isStatic && md->isStatic() && root->fileName!=md->getDefFileName();
4127
4128 if (sameTemplateArgs &&
4129 matchingReturnTypes &&
4130 sameRequiresClause &&
4131 !staticsInDifferentFiles &&
4132 matchArguments2(md->getOuterScope(),mfd,md->typeString(),&mdAl,
4133 rnd ? rnd : Doxygen::globalScope,rfd,root->type,&root->argList,
4134 FALSE,root->lang)
4135 )
4136 {
4137 GroupDef *gd=nullptr;
4138 if (!root->groups.empty() && !root->groups.front().groupname.isEmpty())
4139 {
4140 gd = Doxygen::groupLinkedMap->find(root->groups.front().groupname);
4141 }
4142 //printf("match!\n");
4143 //printf("mnd=%p rnd=%p nsName=%s rnsName=%s\n",mnd,rnd,qPrint(nsName),qPrint(rnsName));
4144 // see if we need to create a new member
4145 found=(mnd && rnd && nsName==rnsName) || // members are in the same namespace
4146 ((mnd==nullptr && rnd==nullptr && mfd!=nullptr && // no external reference and
4147 mfd->absFilePath()==root->fileName // prototype in the same file
4148 )
4149 );
4150 // otherwise, allow a duplicate global member with the same argument list
4151 if (!found && gd && gd==md->getGroupDef() && nsName==rnsName)
4152 {
4153 // member is already in the group, so we don't want to add it again.
4154 found=TRUE;
4155 }
4156
4157 AUTO_TRACE_ADD("combining function with prototype found={} in namespace '{}'",found,nsName);
4158
4159 if (found)
4160 {
4161 // merge argument lists
4162 ArgumentList mergedArgList = root->argList;
4163 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.isEmpty());
4164 // merge documentation
4165 if (md->documentation().isEmpty() && !root->doc.isEmpty())
4166 {
4167 if (root->proto)
4168 {
4170 }
4171 else
4172 {
4174 }
4175 }
4176
4177 md->setDocumentation(root->doc,root->docFile,root->docLine);
4179 md->setDocsForDefinition(!root->proto);
4180 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
4181 {
4182 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
4183 md->setBodyDef(rfd);
4184 }
4185
4186 if (md->briefDescription().isEmpty() && !root->brief.isEmpty())
4187 {
4188 md->setArgsString(root->args);
4189 }
4190 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
4191
4193
4195 md->addQualifiers(root->qualifiers);
4196
4197 // merge ingroup specifiers
4198 if (md->getGroupDef()==nullptr && !root->groups.empty())
4199 {
4200 addMemberToGroups(root,md);
4201 }
4202 else if (md->getGroupDef()!=nullptr && root->groups.empty())
4203 {
4204 //printf("existing member is grouped, new member not\n");
4205 }
4206 else if (md->getGroupDef()!=nullptr && !root->groups.empty())
4207 {
4208 //printf("both members are grouped\n");
4209 }
4211
4212 // if md is a declaration and root is the corresponding
4213 // definition, then turn md into a definition.
4214 if (md->isPrototype() && !root->proto)
4215 {
4216 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
4217 md->setPrototype(FALSE,root->fileName,root->startLine,root->startColumn);
4218 }
4219 // if md is already the definition, then add the declaration info
4220 else if (!md->isPrototype() && root->proto)
4221 {
4222 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
4223 }
4224 }
4225 }
4226 }
4227 if (found)
4228 {
4229 md_found = md;
4230 break;
4231 }
4232 }
4233 }
4234 if (!found) /* global function is unique with respect to the file */
4235 {
4236 addGlobalFunction(root,rname,scope);
4237 }
4238 else
4239 {
4240 FileDef *fd=root->fileDef();
4241 if (fd)
4242 {
4243 // add member to the file (we do this even if we have already
4244 // inserted it into the namespace)
4245 fd->insertMember(md_found);
4246 }
4247 }
4248
4249 AUTO_TRACE_ADD("unrelated function type='{}' name='{}' args='{}'",root->type,rname,root->args);
4250 }
4251 else
4252 {
4253 AUTO_TRACE_ADD("function '{}' is not processed",rname);
4254 }
4255 }
4256 else if (rname.isEmpty())
4257 {
4258 warn(root->fileName,root->startLine,
4259 "Illegal member name found."
4260 );
4261 }
4262 }
4263 for (const auto &e : root->children()) buildFunctionList(e.get());
4264}
4265
4266//----------------------------------------------------------------------
4267
4268static void findFriends()
4269{
4270 AUTO_TRACE();
4271 for (const auto &fn : *Doxygen::functionNameLinkedMap) // for each global function name
4272 {
4273 MemberName *mn = Doxygen::memberNameLinkedMap->find(fn->memberName());
4274 if (mn)
4275 { // there are members with the same name
4276 // for each function with that name
4277 for (const auto &ifmd : *fn)
4278 {
4279 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
4280 // for each member with that name
4281 for (const auto &immd : *mn)
4282 {
4283 MemberDefMutable *mmd = toMemberDefMutable(immd.get());
4284 //printf("Checking for matching arguments
4285 // mmd->isRelated()=%d mmd->isFriend()=%d mmd->isFunction()=%d\n",
4286 // mmd->isRelated(),mmd->isFriend(),mmd->isFunction());
4287 if (fmd && mmd &&
4288 (mmd->isFriend() || (mmd->isRelated() && mmd->isFunction())) &&
4289 matchArguments2(mmd->getOuterScope(), mmd->getFileDef(), mmd->typeString(), &mmd->argumentList(),
4290 fmd->getOuterScope(), fmd->getFileDef(), fmd->typeString(), &fmd->argumentList(),
4291 TRUE,mmd->getLanguage()
4292 )
4293
4294 ) // if the member is related and the arguments match then the
4295 // function is actually a friend.
4296 {
4297 AUTO_TRACE_ADD("Merging related global and member '{}' isFriend={} isRelated={} isFunction={}",
4298 mmd->name(),mmd->isFriend(),mmd->isRelated(),mmd->isFunction());
4299 const ArgumentList &mmdAl = mmd->argumentList();
4300 const ArgumentList &fmdAl = fmd->argumentList();
4301 mergeArguments(const_cast<ArgumentList&>(fmdAl),const_cast<ArgumentList&>(mmdAl));
4302
4303 // reset argument lists to add missing default parameters
4304 QCString mmdAlStr = argListToString(mmdAl);
4305 QCString fmdAlStr = argListToString(fmdAl);
4306 mmd->setArgsString(mmdAlStr);
4307 fmd->setArgsString(fmdAlStr);
4308 mmd->moveDeclArgumentList(std::make_unique<ArgumentList>(mmdAl));
4309 fmd->moveDeclArgumentList(std::make_unique<ArgumentList>(fmdAl));
4310 AUTO_TRACE_ADD("friend args='{}' member args='{}'",argListToString(fmd->argumentList()),argListToString(mmd->argumentList()));
4311
4312 if (!fmd->documentation().isEmpty())
4313 {
4314 mmd->setDocumentation(fmd->documentation(),fmd->docFile(),fmd->docLine());
4315 }
4316 else if (!mmd->documentation().isEmpty())
4317 {
4318 fmd->setDocumentation(mmd->documentation(),mmd->docFile(),mmd->docLine());
4319 }
4320 if (mmd->briefDescription().isEmpty() && !fmd->briefDescription().isEmpty())
4321 {
4322 mmd->setBriefDescription(fmd->briefDescription(),fmd->briefFile(),fmd->briefLine());
4323 }
4324 else if (!mmd->briefDescription().isEmpty() && !fmd->briefDescription().isEmpty())
4325 {
4326 fmd->setBriefDescription(mmd->briefDescription(),mmd->briefFile(),mmd->briefLine());
4327 }
4328 if (!fmd->inbodyDocumentation().isEmpty())
4329 {
4331 }
4332 else if (!mmd->inbodyDocumentation().isEmpty())
4333 {
4335 }
4336 //printf("body mmd %d fmd %d\n",mmd->getStartBodyLine(),fmd->getStartBodyLine());
4337 if (mmd->getStartBodyLine()==-1 && fmd->getStartBodyLine()!=-1)
4338 {
4339 mmd->setBodySegment(fmd->getDefLine(),fmd->getStartBodyLine(),fmd->getEndBodyLine());
4340 mmd->setBodyDef(fmd->getBodyDef());
4341 //mmd->setBodyMember(fmd);
4342 }
4343 else if (mmd->getStartBodyLine()!=-1 && fmd->getStartBodyLine()==-1)
4344 {
4345 fmd->setBodySegment(mmd->getDefLine(),mmd->getStartBodyLine(),mmd->getEndBodyLine());
4346 fmd->setBodyDef(mmd->getBodyDef());
4347 //fmd->setBodyMember(mmd);
4348 }
4350
4352
4353 mmd->addQualifiers(fmd->getQualifiers());
4354 fmd->addQualifiers(mmd->getQualifiers());
4355
4356 }
4357 }
4358 }
4359 }
4360 }
4361}
4362
4363//----------------------------------------------------------------------
4364
4366{
4367 AUTO_TRACE();
4368
4369 // find matching function declaration and definitions.
4370 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4371 {
4372 //printf("memberName=%s count=%zu\n",qPrint(mn->memberName()),mn->size());
4373 /* find a matching function declaration and definition for this function */
4374 for (const auto &imdec : *mn)
4375 {
4376 MemberDefMutable *mdec = toMemberDefMutable(imdec.get());
4377 if (mdec &&
4378 (mdec->isPrototype() ||
4379 (mdec->isVariable() && mdec->isExternal())
4380 ))
4381 {
4382 for (const auto &imdef : *mn)
4383 {
4384 MemberDefMutable *mdef = toMemberDefMutable(imdef.get());
4385 if (mdef && mdec!=mdef &&
4386 mdec->getNamespaceDef()==mdef->getNamespaceDef())
4387 {
4389 }
4390 }
4391 }
4392 }
4393 }
4394}
4395
4396//----------------------------------------------------------------------
4397
4399{
4400 AUTO_TRACE();
4401 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4402 {
4403 MemberDefMutable *mdef=nullptr,*mdec=nullptr;
4404 /* find a matching function declaration and definition for this function */
4405 for (const auto &imd : *mn)
4406 {
4407 MemberDefMutable *md = toMemberDefMutable(imd.get());
4408 if (md)
4409 {
4410 if (md->isPrototype())
4411 mdec=md;
4412 else if (md->isVariable() && md->isExternal())
4413 mdec=md;
4414
4415 if (md->isFunction() && !md->isStatic() && !md->isPrototype())
4416 mdef=md;
4417 else if (md->isVariable() && !md->isExternal() && !md->isStatic())
4418 mdef=md;
4419 }
4420
4421 if (mdef && mdec) break;
4422 }
4423 if (mdef && mdec)
4424 {
4425 const ArgumentList &mdefAl = mdef->argumentList();
4426 const ArgumentList &mdecAl = mdec->argumentList();
4427 if (
4428 matchArguments2(mdef->getOuterScope(),mdef->getFileDef(),mdef->typeString(),const_cast<ArgumentList*>(&mdefAl),
4429 mdec->getOuterScope(),mdec->getFileDef(),mdec->typeString(),const_cast<ArgumentList*>(&mdecAl),
4430 TRUE,mdef->getLanguage()
4431 )
4432 ) /* match found */
4433 {
4434 AUTO_TRACE_ADD("merging references for mdec={} mdef={}",mdec->name(),mdef->name());
4435 mdef->mergeReferences(mdec);
4436 mdec->mergeReferences(mdef);
4437 mdef->mergeReferencedBy(mdec);
4438 mdec->mergeReferencedBy(mdef);
4439 }
4440 }
4441 }
4442}
4443
4444//----------------------------------------------------------------------
4445
4447{
4448 AUTO_TRACE();
4449 // find match between function declaration and definition for
4450 // related functions
4451 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4452 {
4453 /* find a matching function declaration and definition for this function */
4454 // for each global function
4455 for (const auto &imd : *mn)
4456 {
4457 MemberDefMutable *md = toMemberDefMutable(imd.get());
4458 if (md)
4459 {
4460 //printf(" Function '%s'\n",qPrint(md->name()));
4461 MemberName *rmn = Doxygen::memberNameLinkedMap->find(md->name());
4462 if (rmn) // check if there is a member with the same name
4463 {
4464 //printf(" Member name found\n");
4465 // for each member with the same name
4466 for (const auto &irmd : *rmn)
4467 {
4468 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
4469 //printf(" Member found: related='%d'\n",rmd->isRelated());
4470 if (rmd &&
4471 (rmd->isRelated() || rmd->isForeign()) && // related function
4472 matchArguments2( md->getOuterScope(), md->getFileDef(), md->typeString(), &md->argumentList(),
4473 rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmd->argumentList(),
4474 TRUE,md->getLanguage()
4475 )
4476 )
4477 {
4478 AUTO_TRACE_ADD("Found related member '{}'",md->name());
4479 if (rmd->relatedAlso())
4480 md->setRelatedAlso(rmd->relatedAlso());
4481 else if (rmd->isForeign())
4482 md->makeForeign();
4483 else
4484 md->makeRelated();
4485 }
4486 }
4487 }
4488 }
4489 }
4490 }
4491}
4492
4493//----------------------------------------------------------------------
4494
4496{
4497 AUTO_TRACE();
4498 for (const auto &[qualifiedName,bodyInfo] : Doxygen::staticInitMap)
4499 {
4500 size_t i=qualifiedName.rfind("::");
4501 if (i!=std::string::npos)
4502 {
4503 QCString scope = qualifiedName.substr(0,i);
4504 QCString name = qualifiedName.substr(i+2);
4505 MemberName *mn = Doxygen::memberNameLinkedMap->find(name);
4506 if (mn)
4507 {
4508 for (const auto &imd : *mn)
4509 {
4510 MemberDefMutable *md = toMemberDefMutable(imd.get());
4511 if (md && md->qualifiedName().str()==qualifiedName && md->isVariable())
4512 {
4513 AUTO_TRACE_ADD("found static member {} body [{}..{}]\n",
4514 md->qualifiedName(),bodyInfo.startLine,bodyInfo.endLine);
4515 md->setBodySegment(bodyInfo.defLine,
4516 bodyInfo.startLine,
4517 bodyInfo.endLine);
4518 }
4519 }
4520 }
4521 }
4522 }
4523}
4524
4525//----------------------------------------------------------------------
4526
4527/*! make a dictionary of all template arguments of class cd
4528 * that are part of the base class name.
4529 * Example: A template class A with template arguments <R,S,T>
4530 * that inherits from B<T,T,S> will have T and S in the dictionary.
4531 */
4532static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments,const std::string &name)
4533{
4534 std::map<std::string,int> templateNames;
4535 int count=0;
4536 for (const Argument &arg : templateArguments)
4537 {
4538 static const reg::Ex re(R"(\a[\w:]*)");
4539 reg::Iterator it(name,re);
4541 for (; it!=end ; ++it)
4542 {
4543 const auto &match = *it;
4544 std::string n = match.str();
4545 if (n==arg.name.str())
4546 {
4547 if (templateNames.find(n)==templateNames.end())
4548 {
4549 templateNames.emplace(n,count);
4550 }
4551 }
4552 }
4553 }
4554 return templateNames;
4555}
4556
4557/*! Searches a class from within \a context and \a cd and returns its
4558 * definition if found (otherwise nullptr is returned).
4559 */
4561{
4562 ClassDef *result=nullptr;
4563 if (cd==nullptr)
4564 {
4565 return result;
4566 }
4567 FileDef *fd=cd->getFileDef();
4568 SymbolResolver resolver(fd);
4569 if (context && cd!=context)
4570 {
4571 result = const_cast<ClassDef*>(resolver.resolveClass(context,name,true,true));
4572 }
4573 //printf("1. result=%p\n",result);
4574 if (result==nullptr)
4575 {
4576 result = const_cast<ClassDef*>(resolver.resolveClass(cd,name,true,true));
4577 }
4578 //printf("2. result=%p\n",result);
4579 if (result==nullptr) // try direct class, needed for namespaced classes imported via tag files (see bug624095)
4580 {
4581 result = getClass(name);
4582 }
4583 //printf("3. result=%p\n",result);
4584 //printf("** Trying to find %s within context %s class %s result=%s lookup=%p\n",
4585 // qPrint(name),
4586 // context ? qPrint(context->name()) : "<none>",
4587 // cd ? qPrint(cd->name()) : "<none>",
4588 // result ? qPrint(result->name()) : "<none>",
4589 // Doxygen::classLinkedMap->find(name)
4590 // );
4591 return result;
4592}
4593
4594
4595static void findUsedClassesForClass(const Entry *root,
4596 Definition *context,
4597 ClassDefMutable *masterCd,
4598 ClassDefMutable *instanceCd,
4599 bool isArtificial,
4600 const ArgumentList *actualArgs = nullptr,
4601 const TemplateNameMap &templateNames = TemplateNameMap()
4602 )
4603{
4604 AUTO_TRACE();
4605 const ArgumentList &formalArgs = masterCd->templateArguments();
4606 for (auto &mni : masterCd->memberNameInfoLinkedMap())
4607 {
4608 for (auto &mi : *mni)
4609 {
4610 const MemberDef *md=mi->memberDef();
4611 if (md->isVariable() || md->isObjCProperty()) // for each member variable in this class
4612 {
4613 AUTO_TRACE_ADD("Found variable '{}' in class '{}'",md->name(),masterCd->name());
4614 QCString type = normalizeNonTemplateArgumentsInString(md->typeString(),masterCd,formalArgs);
4615 QCString typedefValue = md->getLanguage()==SrcLangExt::Java ? type : resolveTypeDef(masterCd,type);
4616 if (!typedefValue.isEmpty())
4617 {
4618 type = typedefValue;
4619 }
4620 int pos=0;
4621 QCString usedClassName;
4622 QCString templSpec;
4623 bool found=FALSE;
4624 // the type can contain template variables, replace them if present
4625 type = substituteTemplateArgumentsInString(type,formalArgs,actualArgs);
4626
4627 //printf(" template substitution gives=%s\n",qPrint(type));
4628 while (!found && extractClassNameFromType(type,pos,usedClassName,templSpec,root->lang)!=-1)
4629 {
4630 // find the type (if any) that matches usedClassName
4631 SymbolResolver resolver(masterCd->getFileDef());
4632 const ClassDefMutable *typeCd = resolver.resolveClassMutable(masterCd,usedClassName,false,true);
4633 //printf("====> usedClassName=%s -> typeCd=%s\n",
4634 // qPrint(usedClassName),typeCd?qPrint(typeCd->name()):"<none>");
4635 if (typeCd)
4636 {
4637 usedClassName = typeCd->name();
4638 }
4639
4640 // replace any namespace aliases
4641 replaceNamespaceAliases(usedClassName);
4642 // add any template arguments to the class
4643 QCString usedName = removeRedundantWhiteSpace(usedClassName+templSpec);
4644 //printf(" usedName=%s usedClassName=%s templSpec=%s\n",qPrint(usedName),qPrint(usedClassName),qPrint(templSpec));
4645
4646 TemplateNameMap formTemplateNames;
4647 if (templateNames.empty())
4648 {
4649 formTemplateNames = getTemplateArgumentsInName(formalArgs,usedName.str());
4650 }
4651 BaseInfo bi(usedName,Protection::Public,Specifier::Normal);
4652 findClassRelation(root,context,instanceCd,&bi,formTemplateNames,TemplateInstances,isArtificial);
4653
4654 for (const Argument &arg : masterCd->templateArguments())
4655 {
4656 if (arg.name==usedName) // type is a template argument
4657 {
4658 ClassDef *usedCd = Doxygen::hiddenClassLinkedMap->find(usedName);
4659 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4660 if (usedCd==nullptr)
4661 {
4662 usedCdm = toClassDefMutable(
4663 Doxygen::hiddenClassLinkedMap->add(usedName,
4665 masterCd->getDefFileName(),masterCd->getDefLine(),
4666 masterCd->getDefColumn(),
4667 usedName,
4668 ClassDef::Class)));
4669 if (usedCdm)
4670 {
4671 //printf("making %s a template argument!!!\n",qPrint(usedCd->name()));
4672 usedCdm->makeTemplateArgument();
4673 usedCdm->setUsedOnly(TRUE);
4674 usedCdm->setLanguage(masterCd->getLanguage());
4675 usedCd = usedCdm;
4676 }
4677 }
4678 if (usedCd)
4679 {
4680 found=TRUE;
4681 AUTO_TRACE_ADD("case 1: adding used class '{}'", usedCd->name());
4682 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4683 if (usedCdm)
4684 {
4685 if (isArtificial) usedCdm->setArtificial(TRUE);
4686 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4687 }
4688 }
4689 }
4690 }
4691
4692 if (!found)
4693 {
4694 ClassDef *usedCd=findClassWithinClassContext(context,masterCd,usedName);
4695 //printf("Looking for used class %s: result=%s master=%s\n",
4696 // qPrint(usedName),usedCd?qPrint(usedCd->name()):"<none>",masterCd?qPrint(masterCd->name()):"<none>");
4697
4698 if (usedCd)
4699 {
4700 found=TRUE;
4701 AUTO_TRACE_ADD("case 2: adding used class '{}'", usedCd->name());
4702 instanceCd->addUsedClass(usedCd,md->name(),md->protection()); // class exists
4703 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4704 if (usedCdm)
4705 {
4706 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4707 }
4708 }
4709 }
4710 }
4711 if (!found && !type.isEmpty()) // used class is not documented in any scope
4712 {
4713 ClassDef *usedCd = Doxygen::hiddenClassLinkedMap->find(type);
4714 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4715 if (usedCd==nullptr && !Config_getBool(HIDE_UNDOC_RELATIONS))
4716 {
4717 if (type.endsWith("(*") || type.endsWith("(^")) // type is a function pointer
4718 {
4719 type+=md->argsString();
4720 }
4721 AUTO_TRACE_ADD("New undocumented used class '{}'", type);
4722 usedCdm = toClassDefMutable(
4725 masterCd->getDefFileName(),masterCd->getDefLine(),
4726 masterCd->getDefColumn(),
4727 type,ClassDef::Class)));
4728 if (usedCdm)
4729 {
4730 usedCdm->setUsedOnly(TRUE);
4731 usedCdm->setLanguage(masterCd->getLanguage());
4732 usedCd = usedCdm;
4733 }
4734 }
4735 if (usedCd)
4736 {
4737 AUTO_TRACE_ADD("case 3: adding used class '{}'", usedCd->name());
4738 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4739 if (usedCdm)
4740 {
4741 if (isArtificial) usedCdm->setArtificial(TRUE);
4742 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4743 }
4744 }
4745 }
4746 }
4747 }
4748 }
4749}
4750
4752 const Entry *root,
4753 Definition *context,
4754 ClassDefMutable *masterCd,
4755 ClassDefMutable *instanceCd,
4757 bool isArtificial,
4758 const ArgumentList *actualArgs = nullptr,
4759 const TemplateNameMap &templateNames=TemplateNameMap()
4760 )
4761{
4762 AUTO_TRACE("name={}",root->name);
4763 // The base class could ofcouse also be a non-nested class
4764 const ArgumentList &formalArgs = masterCd->templateArguments();
4765 for (const BaseInfo &bi : root->extends)
4766 {
4767 //printf("masterCd=%s bi.name='%s' #actualArgs=%d\n",
4768 // qPrint(masterCd->localName()),qPrint(bi.name),actualArgs ? (int)actualArgs->size() : -1);
4769 TemplateNameMap formTemplateNames;
4770 if (templateNames.empty())
4771 {
4772 formTemplateNames = getTemplateArgumentsInName(formalArgs,bi.name.str());
4773 }
4774 BaseInfo tbi = bi;
4775 tbi.name = substituteTemplateArgumentsInString(bi.name,formalArgs,actualArgs);
4776 //printf("masterCd=%p instanceCd=%p bi->name=%s tbi.name=%s\n",(void*)masterCd,(void*)instanceCd,qPrint(bi.name),qPrint(tbi.name));
4777
4778 if (mode==DocumentedOnly)
4779 {
4780 // find a documented base class in the correct scope
4781 if (!findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,DocumentedOnly,isArtificial))
4782 {
4783 // 1.8.2: decided to show inheritance relations even if not documented,
4784 // we do make them artificial, so they do not appear in the index
4785 //if (!Config_getBool(HIDE_UNDOC_RELATIONS))
4786 bool b = Config_getBool(HIDE_UNDOC_RELATIONS) ? TRUE : isArtificial;
4787 //{
4788 // no documented base class -> try to find an undocumented one
4789 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,Undocumented,b);
4790 //}
4791 }
4792 }
4793 else if (mode==TemplateInstances)
4794 {
4795 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,TemplateInstances,isArtificial);
4796 }
4797 }
4798}
4799
4800//----------------------------------------------------------------------
4801
4802static void findTemplateInstanceRelation(const Entry *root,
4803 Definition *context,
4804 ClassDefMutable *templateClass,const QCString &templSpec,
4805 const TemplateNameMap &templateNames,
4806 bool isArtificial)
4807{
4808 AUTO_TRACE("Derived from template '{}' with parameters '{}' isArtificial={}",
4809 templateClass->name(),templSpec,isArtificial);
4810
4811 QCString tempArgsStr = tempArgListToString(templateClass->templateArguments(),root->lang,false);
4812 bool existingClass = templSpec==tempArgsStr;
4813 if (existingClass) return; // avoid recursion
4814
4815 bool freshInstance=FALSE;
4816 ClassDefMutable *instanceClass = toClassDefMutable(
4817 templateClass->insertTemplateInstance(
4818 root->fileName,root->startLine,root->startColumn,templSpec,freshInstance));
4819 if (instanceClass)
4820 {
4821 if (freshInstance)
4822 {
4823 instanceClass->setArtificial(TRUE);
4824 instanceClass->setLanguage(root->lang);
4825
4826 AUTO_TRACE_ADD("found fresh instance '{}'",instanceClass->name());
4827 instanceClass->setTemplateBaseClassNames(templateNames);
4828
4829 // search for new template instances caused by base classes of
4830 // instanceClass
4831 auto it_pair = g_classEntries.equal_range(templateClass->name().str());
4832 for (auto it=it_pair.first ; it!=it_pair.second ; ++it)
4833 {
4834 const Entry *templateRoot = it->second;
4835 AUTO_TRACE_ADD("template root found '{}' templSpec='{}'",templateRoot->name,templSpec);
4836 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(root->lang,templSpec);
4837 findBaseClassesForClass(templateRoot,context,templateClass,instanceClass,
4838 TemplateInstances,isArtificial,templArgs.get(),templateNames);
4839
4840 findUsedClassesForClass(templateRoot,context,templateClass,instanceClass,
4841 isArtificial,templArgs.get(),templateNames);
4842 }
4843 }
4844 else
4845 {
4846 AUTO_TRACE_ADD("instance already exists");
4847 }
4848 }
4849}
4850
4851//----------------------------------------------------------------------
4852
4853static void resolveTemplateInstanceInType(const Entry *root,const Definition *scope,const MemberDef *md)
4854{
4855 // For a statement like 'using X = T<A>', add a template instance 'T<A>' as a symbol, so it can
4856 // be used to match arguments (see issue #11111)
4857 AUTO_TRACE();
4858 QCString ttype = md->typeString();
4859 ttype.stripPrefix("typedef ");
4860 int ti=ttype.find('<');
4861 if (ti!=-1)
4862 {
4863 QCString templateClassName = ttype.left(ti);
4864 SymbolResolver resolver(root->fileDef());
4865 ClassDefMutable *baseClass = resolver.resolveClassMutable(scope ? scope : Doxygen::globalScope,
4866 templateClassName, true, true);
4867 AUTO_TRACE_ADD("templateClassName={} baseClass={}",templateClassName,baseClass?baseClass->name():"<none>");
4868 if (baseClass)
4869 {
4870 const ArgumentList &tl = baseClass->templateArguments();
4871 TemplateNameMap templateNames = getTemplateArgumentsInName(tl,templateClassName.str());
4873 baseClass,
4874 ttype.mid(ti),
4875 templateNames,
4876 baseClass->isArtificial());
4877 }
4878 }
4879}
4880
4881//----------------------------------------------------------------------
4882
4883static bool isRecursiveBaseClass(const QCString &scope,const QCString &name)
4884{
4885 QCString n=name;
4886 int index=n.find('<');
4887 if (index!=-1)
4888 {
4889 n=n.left(index);
4890 }
4891 bool result = rightScopeMatch(scope,n);
4892 return result;
4893}
4894
4896{
4897 if (name.isEmpty()) return 0;
4898 int l = static_cast<int>(name.length());
4899 if (name[l-1]=='>') // search backward to find the matching <, allowing nested <...> and strings.
4900 {
4901 int count=1;
4902 int i=l-2;
4903 char insideQuote=0;
4904 while (count>0 && i>=0)
4905 {
4906 char c = name[i--];
4907 switch (c)
4908 {
4909 case '>': if (!insideQuote) count++; break;
4910 case '<': if (!insideQuote) count--; break;
4911 case '\'': if (!insideQuote) insideQuote=c;
4912 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
4913 break;
4914 case '"': if (!insideQuote) insideQuote=c;
4915 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
4916 break;
4917 default: break;
4918 }
4919 }
4920 if (i>=0) l=i+1;
4921 }
4922 return l;
4923}
4924
4926 const Entry *root,
4927 Definition *context,
4928 ClassDefMutable *cd,
4929 const BaseInfo *bi,
4930 const TemplateNameMap &templateNames,
4932 bool isArtificial
4933 )
4934{
4935 AUTO_TRACE("name={} base={} isArtificial={} mode={}",cd->name(),bi->name,isArtificial,(int)mode);
4936
4937 QCString biName=bi->name;
4938 bool explicitGlobalScope=FALSE;
4939 if (biName.startsWith("::")) // explicit global scope
4940 {
4941 biName=biName.right(biName.length()-2);
4942 explicitGlobalScope=TRUE;
4943 }
4944
4945 Entry *parentNode=root->parent();
4946 bool lastParent=FALSE;
4947 do // for each parent scope, starting with the largest scope
4948 // (in case of nested classes)
4949 {
4950 QCString scopeName= parentNode ? parentNode->name : QCString();
4951 int scopeOffset=explicitGlobalScope ? 0 : static_cast<int>(scopeName.length());
4952 do // try all parent scope prefixes, starting with the largest scope
4953 {
4954 //printf("scopePrefix='%s' biName='%s'\n",
4955 // qPrint(scopeName.left(scopeOffset)),qPrint(biName));
4956
4957 QCString baseClassName=biName;
4958 if (scopeOffset>0)
4959 {
4960 baseClassName.prepend(scopeName.left(scopeOffset)+"::");
4961 }
4962 if (root->lang==SrcLangExt::CSharp)
4963 {
4964 baseClassName = mangleCSharpGenericName(baseClassName);
4965 }
4966 AUTO_TRACE_ADD("cd='{}' baseClassName='{}'",cd->name(),baseClassName);
4967 SymbolResolver resolver(cd->getFileDef());
4968 ClassDefMutable *baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
4969 baseClassName,
4970 mode==Undocumented,
4971 true
4972 );
4973 const MemberDef *baseClassTypeDef = resolver.getTypedef();
4974 QCString templSpec = resolver.getTemplateSpec();
4975 //printf("baseClassName=%s baseClass=%p cd=%p explicitGlobalScope=%d\n",
4976 // qPrint(baseClassName),baseClass,cd,explicitGlobalScope);
4977 //printf(" scope='%s' baseClassName='%s' baseClass=%s templSpec=%s\n",
4978 // cd ? qPrint(cd->name()):"<none>",
4979 // qPrint(baseClassName),
4980 // baseClass?qPrint(baseClass->name()):"<none>",
4981 // qPrint(templSpec)
4982 // );
4983 //if (baseClassName.left(root->name.length())!=root->name ||
4984 // baseClassName.at(root->name.length())!='<'
4985 // ) // Check for base class with the same name.
4986 // // If found then look in the outer scope for a match
4987 // // and prevent recursion.
4988 if (!isRecursiveBaseClass(root->name,baseClassName)
4989 || explicitGlobalScope
4990 // sadly isRecursiveBaseClass always true for UNO IDL ifc/svc members
4991 // (i.e. this is needed for addInterfaceOrServiceToServiceOrSingleton)
4992 || (root->lang==SrcLangExt::IDL &&
4993 (root->section.isExportedInterface() ||
4994 root->section.isIncludedService()))
4995 )
4996 {
4997 AUTO_TRACE_ADD("class relation '{}' inherited/used by '{}' found prot={} virt={} templSpec='{}'",
4998 baseClassName, root->name, bi->prot, bi->virt, templSpec);
4999
5000 int i=findTemplateSpecializationPosition(baseClassName);
5001 int si=baseClassName.findRev("::",i);
5002 if (si==-1) si=0;
5003 if (baseClass==nullptr && static_cast<size_t>(i)!=baseClassName.length())
5004 // base class has template specifiers
5005 {
5006 // TODO: here we should try to find the correct template specialization
5007 // but for now, we only look for the unspecialized base class.
5008 int e=findEndOfTemplate(baseClassName,i+1);
5009 //printf("baseClass==0 i=%d e=%d\n",i,e);
5010 if (e!=-1) // end of template was found at e
5011 {
5012 templSpec = removeRedundantWhiteSpace(baseClassName.mid(i,e-i));
5013 baseClassName = baseClassName.left(i)+baseClassName.right(baseClassName.length()-e);
5014 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
5015 baseClassName,
5016 mode==Undocumented,
5017 true
5018 );
5019 baseClassTypeDef = resolver.getTypedef();
5020 //printf("baseClass=%p -> baseClass=%s templSpec=%s\n",
5021 // baseClass,qPrint(baseClassName),qPrint(templSpec));
5022 }
5023 }
5024 else if (baseClass && !templSpec.isEmpty()) // we have a known class, but also
5025 // know it is a template, so see if
5026 // we can also link to the explicit
5027 // instance (for instance if a class
5028 // derived from a template argument)
5029 {
5030 //printf("baseClass=%s templSpec=%s\n",qPrint(baseClass->name()),qPrint(templSpec));
5031 ClassDefMutable *templClass=getClassMutable(baseClass->name()+templSpec);
5032 if (templClass)
5033 {
5034 // use the template instance instead of the template base.
5035 baseClass = templClass;
5036 templSpec.clear();
5037 }
5038 }
5039
5040 //printf("cd=%p baseClass=%p\n",cd,baseClass);
5041 bool found=baseClass!=nullptr && (baseClass!=cd || mode==TemplateInstances);
5042 AUTO_TRACE_ADD("1. found={}",found);
5043 if (!found && si!=-1)
5044 {
5045 // replace any namespace aliases
5046 replaceNamespaceAliases(baseClassName);
5047 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
5048 baseClassName,
5049 mode==Undocumented,
5050 true
5051 );
5052 baseClassTypeDef = resolver.getTypedef();
5053 found=baseClass!=nullptr && baseClass!=cd;
5054 if (found) templSpec = resolver.getTemplateSpec();
5055 }
5056 AUTO_TRACE_ADD("2. found={}",found);
5057
5058 if (!found)
5059 {
5060 baseClass=toClassDefMutable(findClassWithinClassContext(context,cd,baseClassName));
5061 //printf("findClassWithinClassContext(%s,%s)=%p\n",
5062 // qPrint(cd->name()),qPrint(baseClassName),baseClass);
5063 found = baseClass!=nullptr && baseClass!=cd;
5064
5065 }
5066 AUTO_TRACE_ADD("3. found={}",found);
5067 if (!found)
5068 {
5069 // for PHP the "use A\B as C" construct map class C to A::B, so we lookup
5070 // the class name also in the alias mapping.
5071 auto it = Doxygen::namespaceAliasMap.find(baseClassName.str());
5072 if (it!=Doxygen::namespaceAliasMap.end()) // see if it is indeed a class.
5073 {
5074 baseClass=getClassMutable(it->second.alias);
5075 found = baseClass!=nullptr && baseClass!=cd;
5076 }
5077 }
5078 bool isATemplateArgument = templateNames.find(biName.str())!=templateNames.end();
5079
5080 AUTO_TRACE_ADD("4. found={}",found);
5081 if (found)
5082 {
5083 AUTO_TRACE_ADD("Documented base class '{}' templSpec='{}'",biName,templSpec);
5084 // add base class to this class
5085
5086 // if templSpec is not empty then we should "instantiate"
5087 // the template baseClass. A new ClassDef should be created
5088 // to represent the instance. To be able to add the (instantiated)
5089 // members and documentation of a template class
5090 // (inserted in that template class at a later stage),
5091 // the template should know about its instances.
5092 // the instantiation process, should be done in a recursive way,
5093 // since instantiating a template may introduce new inheritance
5094 // relations.
5095 if (!templSpec.isEmpty() && mode==TemplateInstances)
5096 {
5097 // if baseClass is actually a typedef then we should not
5098 // instantiate it, since typedefs are in a different namespace
5099 // see bug531637 for an example where this would otherwise hang
5100 // Doxygen
5101 if (baseClassTypeDef==nullptr)
5102 {
5103 //printf(" => findTemplateInstanceRelation: %s\n",qPrint(baseClass->name()));
5104 findTemplateInstanceRelation(root,context,baseClass,templSpec,templateNames,baseClass->isArtificial());
5105 }
5106 }
5107 else if (mode==DocumentedOnly || mode==Undocumented)
5108 {
5109 //printf(" => insert base class\n");
5110 QCString usedName;
5111 if (baseClassTypeDef)
5112 {
5113 usedName=biName;
5114 //printf("***** usedName=%s templSpec=%s\n",qPrint(usedName),qPrint(templSpec));
5115 }
5116 Protection prot = bi->prot;
5117 if (Config_getBool(SIP_SUPPORT)) prot=Protection::Public;
5118 if (cd!=baseClass && !cd->isSubClass(baseClass) && baseClass->isBaseClass(cd,true,templSpec)==0) // check for recursion, see bug690787
5119 {
5120 AUTO_TRACE_ADD("insertBaseClass name={} prot={} virt={} templSpec={}",usedName,prot,bi->virt,templSpec);
5121 cd->insertBaseClass(baseClass,usedName,prot,bi->virt,templSpec);
5122 // add this class as super class to the base class
5123 baseClass->insertSubClass(cd,prot,bi->virt,templSpec);
5124 }
5125 else
5126 {
5127 warn(root->fileName,root->startLine,
5128 "Detected potential recursive class relation "
5129 "between class {} and base class {}!",
5130 cd->name(),baseClass->name()
5131 );
5132 }
5133 }
5134 return TRUE;
5135 }
5136 else if (mode==Undocumented && (scopeOffset==0 || isATemplateArgument))
5137 {
5138 AUTO_TRACE_ADD("New undocumented base class '{}' baseClassName='{}' templSpec='{}' isArtificial={}",
5139 biName,baseClassName,templSpec,isArtificial);
5140 baseClass=nullptr;
5141 if (isATemplateArgument)
5142 {
5143 baseClass = toClassDefMutable(Doxygen::hiddenClassLinkedMap->find(baseClassName));
5144 if (baseClass==nullptr) // not found (or alias)
5145 {
5146 baseClass= toClassDefMutable(
5147 Doxygen::hiddenClassLinkedMap->add(baseClassName,
5148 createClassDef(root->fileName,root->startLine,root->startColumn,
5149 baseClassName,
5150 ClassDef::Class)));
5151 if (baseClass) // really added (not alias)
5152 {
5153 if (isArtificial) baseClass->setArtificial(TRUE);
5154 baseClass->setLanguage(root->lang);
5155 }
5156 }
5157 }
5158 else
5159 {
5160 baseClass = toClassDefMutable(Doxygen::classLinkedMap->find(baseClassName));
5161 //printf("*** classDDict->find(%s)=%p biName=%s templSpec=%s\n",
5162 // qPrint(baseClassName),baseClass,qPrint(biName),qPrint(templSpec));
5163 if (baseClass==nullptr) // not found (or alias)
5164 {
5165 baseClass = toClassDefMutable(
5166 Doxygen::classLinkedMap->add(baseClassName,
5167 createClassDef(root->fileName,root->startLine,root->startColumn,
5168 baseClassName,
5169 ClassDef::Class)));
5170 if (baseClass) // really added (not alias)
5171 {
5172 if (isArtificial) baseClass->setArtificial(TRUE);
5173 baseClass->setLanguage(root->lang);
5174 si = baseClassName.findRev("::");
5175 if (si!=-1) // class is nested
5176 {
5177 Definition *sd = findScopeFromQualifiedName(Doxygen::globalScope,baseClassName.left(si),nullptr,root->tagInfo());
5178 if (sd==nullptr || sd==Doxygen::globalScope) // outer scope not found
5179 {
5180 baseClass->setArtificial(TRUE); // see bug678139
5181 }
5182 }
5183 }
5184 }
5185 }
5186 if (baseClass)
5187 {
5188 if (biName.endsWith("-p"))
5189 {
5190 biName="<"+biName.left(biName.length()-2)+">";
5191 }
5192 if (!cd->isSubClass(baseClass) && cd!=baseClass && cd->isBaseClass(baseClass,true,templSpec)==0) // check for recursion
5193 {
5194 AUTO_TRACE_ADD("insertBaseClass name={} prot={} virt={} templSpec={}",biName,bi->prot,bi->virt,templSpec);
5195 // add base class to this class
5196 cd->insertBaseClass(baseClass,biName,bi->prot,bi->virt,templSpec);
5197 // add this class as super class to the base class
5198 baseClass->insertSubClass(cd,bi->prot,bi->virt,templSpec);
5199 }
5200 // the undocumented base was found in this file
5201 baseClass->insertUsedFile(root->fileDef());
5202
5203 Definition *scope = buildScopeFromQualifiedName(baseClass->name(),root->lang,nullptr);
5204 if (scope!=baseClass)
5205 {
5206 baseClass->setOuterScope(scope);
5207 }
5208
5209 if (baseClassName.endsWith("-p"))
5210 {
5212 }
5213 return TRUE;
5214 }
5215 else
5216 {
5217 AUTO_TRACE_ADD("Base class '{}' not created (alias?)",biName);
5218 }
5219 }
5220 else
5221 {
5222 AUTO_TRACE_ADD("Base class '{}' not found",biName);
5223 }
5224 }
5225 else
5226 {
5227 if (mode!=TemplateInstances)
5228 {
5229 warn(root->fileName,root->startLine,
5230 "Detected potential recursive class relation "
5231 "between class {} and base class {}!",
5232 root->name,baseClassName
5233 );
5234 }
5235 // for mode==TemplateInstance this case is quite common and
5236 // indicates a relation between a template class and a template
5237 // instance with the same name.
5238 }
5239 if (scopeOffset==0)
5240 {
5241 scopeOffset=-1;
5242 }
5243 else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1)
5244 {
5245 scopeOffset=0;
5246 }
5247 //printf("new scopeOffset='%d'",scopeOffset);
5248 } while (scopeOffset>=0);
5249
5250 if (parentNode==nullptr)
5251 {
5252 lastParent=TRUE;
5253 }
5254 else
5255 {
5256 parentNode=parentNode->parent();
5257 }
5258 } while (lastParent);
5259
5260 return FALSE;
5261}
5262
5263//----------------------------------------------------------------------
5264// Computes the base and super classes for each class in the tree
5265
5266static bool isClassSection(const Entry *root)
5267{
5268 if ( !root->name.isEmpty() )
5269 {
5270 if (root->section.isCompound())
5271 // is it a compound (class, struct, union, interface ...)
5272 {
5273 return TRUE;
5274 }
5275 else if (root->section.isCompoundDoc())
5276 // is it a documentation block with inheritance info.
5277 {
5278 bool hasExtends = !root->extends.empty();
5279 if (hasExtends) return TRUE;
5280 }
5281 }
5282 return FALSE;
5283}
5284
5285
5286/*! Builds a dictionary of all entry nodes in the tree starting with \a root
5287 */
5288static void findClassEntries(const Entry *root)
5289{
5290 if (isClassSection(root))
5291 {
5292 g_classEntries.emplace(root->name.str(),root);
5293 }
5294 for (const auto &e : root->children()) findClassEntries(e.get());
5295}
5296
5298{
5299 // strip any anonymous scopes first
5302 int i=0;
5303 if ((root->lang==SrcLangExt::CSharp || root->lang==SrcLangExt::Java) &&
5304 (i=bName.find('<'))!=-1)
5305 {
5306 // a Java/C# generic class looks like a C++ specialization, so we need to strip the
5307 // template part before looking for matches
5308 if (root->lang==SrcLangExt::CSharp)
5309 {
5310 bName = mangleCSharpGenericName(root->name);
5311 }
5312 else
5313 {
5314 bName = bName.left(i);
5315 }
5316 }
5317 return bName;
5318}
5319
5320/*! Using the dictionary build by findClassEntries(), this
5321 * function will look for additional template specialization that
5322 * exists as inheritance relations only. These instances will be
5323 * added to the template they are derived from.
5324 */
5326{
5327 AUTO_TRACE();
5328 ClassDefSet visitedClasses;
5329 for (const auto &[name,root] : g_classEntries)
5330 {
5331 QCString bName = extractClassName(root);
5332 ClassDefMutable *cdm = getClassMutable(bName);
5333 if (cdm)
5334 {
5336 }
5337 }
5338}
5339
5341{
5342 AUTO_TRACE("root->name={} cd={}",root->name,cd->name());
5343 int i = root->name.find('<');
5344 int j = root->name.findRev('>');
5345 int k = root->name.find("::",j+1); // A<T::B> => ok, A<T>::B => nok
5346 if (i!=-1 && j!=-1 && k==-1 && root->lang!=SrcLangExt::CSharp && root->lang!=SrcLangExt::Java)
5347 {
5348 ClassDefMutable *master = getClassMutable(root->name.left(i));
5349 if (master && master!=cd && !cd->templateMaster())
5350 {
5351 AUTO_TRACE_ADD("class={} master={}",cd->name(),cd->templateMaster()?cd->templateMaster()->name():"<none>",master->name());
5352 cd->setTemplateMaster(master);
5353 master->insertExplicitTemplateInstance(cd,root->name.mid(i));
5354 }
5355 }
5356}
5357
5359{
5360 AUTO_TRACE();
5361 for (const auto &[name,root] : g_classEntries)
5362 {
5363 QCString bName = extractClassName(root);
5364 ClassDefMutable *cdm = getClassMutable(bName);
5365 if (cdm)
5366 {
5367 findUsedClassesForClass(root,cdm,cdm,cdm,TRUE);
5369 cdm->addTypeConstraints();
5370 }
5371 }
5372}
5373
5375{
5376 AUTO_TRACE();
5377 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5378 {
5379 if (!nd->hasDocumentation())
5380 {
5381 if ((guessSection(nd->getDefFileName()).isHeader() ||
5382 nd->getLanguage() == SrcLangExt::Fortran) && // Fortran doesn't have header files.
5383 !Config_getBool(HIDE_UNDOC_NAMESPACES) // undocumented namespaces are visible
5384 )
5385 {
5386 warn_undoc(nd->getDefFileName(),nd->getDefLine(), "{} {} is not documented.",
5387 nd->getLanguage() == SrcLangExt::Fortran ? "Module" : "Namespace",
5388 nd->name());
5389 }
5390 }
5391 }
5392}
5393
5395{
5396 AUTO_TRACE();
5397 for (const auto &[name,root] : g_classEntries)
5398 {
5399 QCString bName = extractClassName(root);
5400 ClassDefMutable *cd = getClassMutable(bName);
5401 if (cd)
5402 {
5404 }
5405 size_t numMembers = cd ? cd->memberNameInfoLinkedMap().size() : 0;
5406 if ((cd==nullptr || (!cd->hasDocumentation() && !cd->isReference())) && numMembers>0 && !bName.endsWith("::"))
5407 {
5408 if (!root->name.isEmpty() && root->name.find('@')==-1 && // normal name
5409 (guessSection(root->fileName).isHeader() ||
5410 Config_getBool(EXTRACT_LOCAL_CLASSES)) && // not defined in source file
5411 protectionLevelVisible(root->protection) && // hidden by protection
5412 !Config_getBool(HIDE_UNDOC_CLASSES) // undocumented class are visible
5413 )
5414 warn_undoc(root->fileName,root->startLine, "Compound {} is not documented.", root->name);
5415 }
5416 }
5417}
5418
5420{
5421 AUTO_TRACE();
5422 for (const auto &[name,root] : g_classEntries)
5423 {
5427 // strip any anonymous scopes first
5428 if (cd && !cd->getTemplateInstances().empty())
5429 {
5430 AUTO_TRACE_ADD("Template class '{}'",cd->name());
5431 for (const auto &ti : cd->getTemplateInstances()) // for each template instance
5432 {
5433 ClassDefMutable *tcd=toClassDefMutable(ti.classDef);
5434 if (tcd)
5435 {
5436 AUTO_TRACE_ADD("Template instance '{}'",tcd->name());
5437 QCString templSpec = ti.templSpec;
5438 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(tcd->getLanguage(),templSpec);
5439 for (const BaseInfo &bi : root->extends)
5440 {
5441 // check if the base class is a template argument
5442 BaseInfo tbi = bi;
5443 const ArgumentList &tl = cd->templateArguments();
5444 if (!tl.empty())
5445 {
5446 TemplateNameMap baseClassNames = tcd->getTemplateBaseClassNames();
5447 TemplateNameMap templateNames = getTemplateArgumentsInName(tl,bi.name.str());
5448 // for each template name that we inherit from we need to
5449 // substitute the formal with the actual arguments
5450 TemplateNameMap actualTemplateNames;
5451 for (const auto &tn_kv : templateNames)
5452 {
5453 size_t templIndex = tn_kv.second;
5454 Argument actArg;
5455 bool hasActArg=FALSE;
5456 if (templIndex<templArgs->size())
5457 {
5458 actArg=templArgs->at(templIndex);
5459 hasActArg=TRUE;
5460 }
5461 if (hasActArg &&
5462 baseClassNames.find(actArg.type.str())!=baseClassNames.end() &&
5463 actualTemplateNames.find(actArg.type.str())==actualTemplateNames.end()
5464 )
5465 {
5466 actualTemplateNames.emplace(actArg.type.str(),static_cast<int>(templIndex));
5467 }
5468 }
5469
5470 tbi.name = substituteTemplateArgumentsInString(bi.name,tl,templArgs.get());
5471 // find a documented base class in the correct scope
5472 if (!findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,DocumentedOnly,FALSE))
5473 {
5474 // no documented base class -> try to find an undocumented one
5475 findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,Undocumented,TRUE);
5476 }
5477 }
5478 }
5479 }
5480 }
5481 }
5482 }
5483}
5484
5485//-----------------------------------------------------------------------
5486// compute the references (anchors in HTML) for each function in the file
5487
5489{
5490 AUTO_TRACE();
5491 for (const auto &cd : *Doxygen::classLinkedMap)
5492 {
5493 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5494 if (cdm)
5495 {
5496 cdm->computeAnchors();
5497 }
5498 }
5499 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5500 {
5501 for (const auto &fd : *fn)
5502 {
5503 fd->computeAnchors();
5504 }
5505 }
5506 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5507 {
5509 if (ndm)
5510 {
5511 ndm->computeAnchors();
5512 }
5513 }
5514 for (const auto &gd : *Doxygen::groupLinkedMap)
5515 {
5516 gd->computeAnchors();
5517 }
5518}
5519
5520//----------------------------------------------------------------------
5521
5522
5523template<typename Func>
5524static void applyToAllDefinitions(Func func)
5525{
5526 for (const auto &cd : *Doxygen::classLinkedMap)
5527 {
5528 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5529 if (cdm)
5530 {
5531 func(cdm);
5532 }
5533 }
5534
5535 for (const auto &cd : *Doxygen::conceptLinkedMap)
5536 {
5537 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
5538 if (cdm)
5539 {
5540 func(cdm);
5541 }
5542 }
5543
5544 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5545 {
5546 for (const auto &fd : *fn)
5547 {
5548 func(fd.get());
5549 }
5550 }
5551
5552 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5553 {
5555 if (ndm)
5556 {
5557 func(ndm);
5558 }
5559 }
5560
5561 for (const auto &gd : *Doxygen::groupLinkedMap)
5562 {
5563 func(gd.get());
5564 }
5565
5566 for (const auto &pd : *Doxygen::pageLinkedMap)
5567 {
5568 func(pd.get());
5569 }
5570
5571 for (const auto &dd : *Doxygen::dirLinkedMap)
5572 {
5573 func(dd.get());
5574 }
5575
5576 func(&ModuleManager::instance());
5577}
5578
5579//----------------------------------------------------------------------
5580
5582{
5583 AUTO_TRACE();
5584 applyToAllDefinitions([](auto* obj) { obj->addRequirementReferences(); });
5585}
5586
5587//----------------------------------------------------------------------
5588
5590{
5591 AUTO_TRACE();
5592 applyToAllDefinitions([](auto* obj) { obj->addListReferences(); });
5593}
5594
5595
5596//----------------------------------------------------------------------
5597
5599{
5600 AUTO_TRACE();
5602 {
5603 rl->generatePage();
5604 }
5605}
5606
5607//----------------------------------------------------------------------
5608// Copy the documentation in entry 'root' to member definition 'md' and
5609// set the function declaration of the member to 'funcDecl'. If the boolean
5610// over_load is set the standard overload text is added.
5611
5612static void addMemberDocs(const Entry *root,
5613 MemberDefMutable *md, const QCString &funcDecl,
5614 const ArgumentList *al,
5615 bool over_load,
5616 TypeSpecifier spec
5617 )
5618{
5619 if (md==nullptr) return;
5620 AUTO_TRACE("scope='{}' name='{}' args='{}' funcDecl='{}' mSpec={}",
5621 root->parent()->name,md->name(),md->argsString(),funcDecl,spec);
5622 if (!root->section.isDoc()) // @fn or @var does not need to specify the complete definition, so don't overwrite it
5623 {
5624 QCString fDecl=funcDecl;
5625 // strip extern specifier
5626 fDecl.stripPrefix("extern ");
5627 md->setDefinition(fDecl);
5628 }
5630 md->addQualifiers(root->qualifiers);
5632 const NamespaceDef *nd=md->getNamespaceDef();
5633 QCString fullName;
5634 if (cd)
5635 fullName = cd->name();
5636 else if (nd)
5637 fullName = nd->name();
5638
5639 if (!fullName.isEmpty()) fullName+="::";
5640 fullName+=md->name();
5641 FileDef *rfd=root->fileDef();
5642
5643 // TODO determine scope based on root not md
5644 Definition *rscope = md->getOuterScope();
5645
5646 const ArgumentList &mdAl = md->argumentList();
5647 if (al)
5648 {
5649 ArgumentList mergedAl = *al;
5650 //printf("merging arguments (1) docs=%d\n",root->doc.isEmpty());
5651 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedAl,!root->doc.isEmpty());
5652 }
5653 else
5654 {
5655 if (
5656 matchArguments2( md->getOuterScope(), md->getFileDef(),md->typeString(),const_cast<ArgumentList*>(&mdAl),
5657 rscope,rfd,root->type,&root->argList,
5658 TRUE, root->lang
5659 )
5660 )
5661 {
5662 //printf("merging arguments (2)\n");
5663 ArgumentList mergedArgList = root->argList;
5664 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.isEmpty());
5665 }
5666 }
5667 if (over_load) // the \overload keyword was used
5668 {
5670 if (!root->doc.isEmpty())
5671 {
5672 doc+="<p>";
5673 doc+=root->doc;
5674 }
5675 md->setDocumentation(doc,root->docFile,root->docLine);
5677 md->setDocsForDefinition(!root->proto);
5678 }
5679 else
5680 {
5681 //printf("overwrite!\n");
5682 md->setDocumentation(root->doc,root->docFile,root->docLine);
5683 md->setDocsForDefinition(!root->proto);
5684
5685 //printf("overwrite!\n");
5686 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
5687
5688 if (
5689 (md->inbodyDocumentation().isEmpty() ||
5690 !root->parent()->name.isEmpty()
5691 ) && !root->inbodyDocs.isEmpty()
5692 )
5693 {
5695 }
5696 }
5697
5698 //printf("initializer: '%s'(isEmpty=%d) '%s'(isEmpty=%d)\n",
5699 // qPrint(md->initializer()),md->initializer().isEmpty(),
5700 // qPrint(root->initializer),root->initializer.isEmpty()
5701 // );
5702 std::string rootInit = root->initializer.str();
5703 if (md->initializer().isEmpty() && !rootInit.empty())
5704 {
5705 //printf("setInitializer\n");
5706 md->setInitializer(rootInit);
5707 }
5708 if (md->requiresClause().isEmpty() && !root->req.isEmpty())
5709 {
5710 md->setRequiresClause(root->req);
5711 }
5712
5713 md->setMaxInitLines(root->initLines);
5714
5715 if (rfd)
5716 {
5717 if ((md->getStartBodyLine()==-1 && root->bodyLine!=-1)
5718 )
5719 {
5720 //printf("Setting new body segment [%d,%d]\n",root->bodyLine,root->endBodyLine);
5721 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
5722 md->setBodyDef(rfd);
5723 }
5724
5725 md->setRefItems(root->sli);
5726 md->setRequirementReferences(root->rqli);
5727 }
5728
5730 md->addQualifiers(root->qualifiers);
5731
5732 md->mergeMemberSpecifiers(spec);
5734 addMemberToGroups(root,md);
5736 if (cd) cd->insertUsedFile(rfd);
5737 //printf("root->mGrpId=%d\n",root->mGrpId);
5738 if (root->mGrpId!=-1)
5739 {
5740 if (md->getMemberGroupId()!=-1)
5741 {
5742 if (md->getMemberGroupId()!=root->mGrpId)
5743 {
5744 warn(root->fileName,root->startLine,
5745 "member {} belongs to two different groups. The second one found here will be ignored.",
5746 md->name()
5747 );
5748 }
5749 }
5750 else // set group id
5751 {
5752 //printf("setMemberGroupId=%d md=%s\n",root->mGrpId,qPrint(md->name()));
5753 md->setMemberGroupId(root->mGrpId);
5754 }
5755 }
5756 md->addQualifiers(root->qualifiers);
5757}
5758
5759//----------------------------------------------------------------------
5760// find a class definition given the scope name and (optionally) a
5761// template list specifier
5762
5764 const QCString &scopeName)
5765{
5766 SymbolResolver resolver(fd);
5767 const ClassDef *tcd = resolver.resolveClass(nd,scopeName,true,true);
5768 //printf("findClassDefinition(fd=%s,ns=%s,scopeName=%s)='%s'\n",
5769 // qPrint(fd?fd->name():""),qPrint(nd?nd->name():""),
5770 // qPrint(scopeName),qPrint(tcd?tcd->name():""));
5771 return tcd;
5772}
5773
5774//----------------------------------------------------------------------------
5775// Returns TRUE, if the entry belongs to the group of the member definition,
5776// otherwise FALSE.
5777
5778static bool isEntryInGroupOfMember(const Entry *root,const MemberDef *md,bool allowNoGroup=false)
5779{
5780 const GroupDef *gd = md->getGroupDef();
5781 if (!gd)
5782 {
5783 return allowNoGroup;
5784 }
5785
5786 for (const auto &g : root->groups)
5787 {
5788 if (g.groupname == gd->name())
5789 {
5790 return true; // matching group
5791 }
5792 }
5793
5794 return false;
5795}
5796
5797//----------------------------------------------------------------------
5798// Adds the documentation contained in 'root' to a global function
5799// with name 'name' and argument list 'args' (for overloading) and
5800// function declaration 'decl' to the corresponding member definition.
5801
5802static bool findGlobalMember(const Entry *root,
5803 const QCString &namespaceName,
5804 const QCString &type,
5805 const QCString &name,
5806 const QCString &tempArg,
5807 const QCString &,
5808 const QCString &decl,
5809 TypeSpecifier /* spec */)
5810{
5811 AUTO_TRACE("namespace='{}' type='{}' name='{}' tempArg='{}' decl='{}'",namespaceName,type,name,tempArg,decl);
5812 QCString n=name;
5813 if (n.isEmpty()) return FALSE;
5814 if (n.find("::")!=-1) return FALSE; // skip undefined class members
5815 MemberName *mn=Doxygen::functionNameLinkedMap->find(n+tempArg); // look in function dictionary
5816 if (mn==nullptr)
5817 {
5818 mn=Doxygen::functionNameLinkedMap->find(n); // try without template arguments
5819 }
5820 if (mn) // function name defined
5821 {
5822 AUTO_TRACE_ADD("Found symbol name");
5823 //int count=0;
5824 bool found=FALSE;
5825 for (const auto &md : *mn)
5826 {
5827 // If the entry has groups, then restrict the search to members which are
5828 // in one of the groups of the entry. If md is not associated with a group yet,
5829 // allow this documentation entry to add the group info.
5830 if (!root->groups.empty() && !isEntryInGroupOfMember(root, md.get(), true))
5831 {
5832 continue;
5833 }
5834
5835 const NamespaceDef *nd=nullptr;
5836 if (md->isAlias() && md->getOuterScope() &&
5837 md->getOuterScope()->definitionType()==Definition::TypeNamespace)
5838 {
5839 nd = toNamespaceDef(md->getOuterScope());
5840 }
5841 else
5842 {
5843 nd = md->getNamespaceDef();
5844 }
5845
5846 // special case for strong enums
5847 int enumNamePos=0;
5848 if (nd && md->isEnumValue() && (enumNamePos=namespaceName.findRev("::"))!=-1)
5849 { // md part of a strong enum in a namespace?
5850 QCString enumName = namespaceName.mid(enumNamePos+2);
5851 if (namespaceName.left(enumNamePos)==nd->name())
5852 {
5853 MemberName *enumMn=Doxygen::functionNameLinkedMap->find(enumName);
5854 if (enumMn)
5855 {
5856 for (const auto &emd : *enumMn)
5857 {
5858 found = emd->isStrong() && md->getEnumScope()==emd.get();
5859 if (found)
5860 {
5861 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,nullptr,FALSE,root->spec);
5862 break;
5863 }
5864 }
5865 }
5866 }
5867 if (found)
5868 {
5869 break;
5870 }
5871 }
5872 else if (nd==nullptr && md->isEnumValue()) // md part of global strong enum?
5873 {
5874 MemberName *enumMn=Doxygen::functionNameLinkedMap->find(namespaceName);
5875 if (enumMn)
5876 {
5877 for (const auto &emd : *enumMn)
5878 {
5879 found = emd->isStrong() && md->getEnumScope()==emd.get();
5880 if (found)
5881 {
5882 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,nullptr,FALSE,root->spec);
5883 break;
5884 }
5885 }
5886 }
5887 }
5888
5889 const FileDef *fd=root->fileDef();
5890 //printf("File %s\n",fd ? qPrint(fd->name()) : "<none>");
5892 if (fd)
5893 {
5894 nl = fd->getUsedNamespaces();
5895 }
5896 //printf("NamespaceList %p\n",nl);
5897
5898 // search in the list of namespaces that are imported via a
5899 // using declaration
5900 bool viaUsingDirective = nd && nl.find(nd->qualifiedName())!=nullptr;
5901
5902 if ((namespaceName.isEmpty() && nd==nullptr) || // not in a namespace
5903 (nd && nd->name()==namespaceName) || // or in the same namespace
5904 viaUsingDirective // member in 'using' namespace
5905 )
5906 {
5907 AUTO_TRACE_ADD("Try to add member '{}' to scope '{}'",md->name(),namespaceName);
5908
5909 NamespaceDef *rnd = nullptr;
5910 if (!namespaceName.isEmpty()) rnd = Doxygen::namespaceLinkedMap->find(namespaceName);
5911
5912 const ArgumentList &mdAl = md.get()->argumentList();
5913 bool matching=
5914 (mdAl.empty() && root->argList.empty()) ||
5915 md->isVariable() || md->isTypedef() || /* in case of function pointers */
5916 matchArguments2(md->getOuterScope(),md->getFileDef(),md->typeString(),&mdAl,
5917 rnd ? rnd : Doxygen::globalScope,fd,root->type,&root->argList,
5918 FALSE,root->lang);
5919
5920 // for template members we need to check if the number of
5921 // template arguments is the same, otherwise we are dealing with
5922 // different functions.
5923 if (matching && !root->tArgLists.empty())
5924 {
5925 const ArgumentList &mdTempl = md->templateArguments();
5926 if (root->tArgLists.back().size()!=mdTempl.size())
5927 {
5928 matching=FALSE;
5929 }
5930 }
5931
5932 //printf("%s<->%s\n",
5933 // qPrint(argListToString(md->argumentList())),
5934 // qPrint(argListToString(root->argList)));
5935
5936 // For static members we also check if the comment block was found in
5937 // the same file. This is needed because static members with the same
5938 // name can be in different files. Thus it would be wrong to just
5939 // put the comment block at the first syntactically matching member. If
5940 // the comment block belongs to a group of the static member, then add
5941 // the documentation even if it is in a different file.
5942 if (matching && md->isStatic() &&
5943 md->getDefFileName()!=root->fileName &&
5944 mn->size()>1 &&
5945 !isEntryInGroupOfMember(root,md.get()))
5946 {
5947 matching = FALSE;
5948 }
5949
5950 // for template member we also need to check the return type and requires
5951 if (!md->templateArguments().empty() && !root->tArgLists.empty())
5952 {
5953 //printf("Comparing return types '%s'<->'%s'\n",
5954 // md->typeString(),type);
5955 //printf("%s: Comparing '%s'<=>'%s'\n",qPrint(md->name()),qPrint(md->requiresClause()),qPrint(root->req));
5956 if (md->templateArguments().size()!=root->tArgLists.back().size() ||
5957 md->typeString()!=type ||
5958 md->requiresClause()!=root->req)
5959 {
5960 //printf(" ---> no matching\n");
5961 matching = FALSE;
5962 }
5963 }
5964
5965 if (matching) // add docs to the member
5966 {
5967 AUTO_TRACE_ADD("Match found");
5968 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,&root->argList,FALSE,root->spec);
5969 found=TRUE;
5970 break;
5971 }
5972 }
5973 }
5974 if (!found && root->relatesType!=RelatesType::Duplicate && root->section.isFunction()) // no match
5975 {
5976 QCString fullFuncDecl=decl;
5977 if (!root->argList.empty()) fullFuncDecl+=argListToString(root->argList,TRUE);
5978 QCString warnMsg = "no matching file member found for \n"+fullFuncDecl;
5979 if (mn->size()>0)
5980 {
5981 warnMsg+="\nPossible candidates:";
5982 for (const auto &md : *mn)
5983 {
5984 warnMsg+="\n '";
5985 warnMsg+=replaceAnonymousScopes(md->declaration());
5986 warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
5987 }
5988 }
5989 warn(root->fileName,root->startLine, "{}", qPrint(warnMsg));
5990 }
5991 }
5992 else // got docs for an undefined member!
5993 {
5994 if (root->type!="friend class" &&
5995 root->type!="friend struct" &&
5996 root->type!="friend union" &&
5997 root->type!="friend" &&
5998 (!Config_getBool(TYPEDEF_HIDES_STRUCT) ||
5999 root->type.find("typedef ")==-1)
6000 )
6001 {
6002 warn(root->fileName,root->startLine,
6003 "documented symbol '{}' was not declared or defined.",qPrint(decl)
6004 );
6005 }
6006 }
6007 return TRUE;
6008}
6009
6011 const ArgumentLists &srcTempArgLists,
6012 const ArgumentLists &dstTempArgLists
6013 )
6014{
6015 auto srcIt = srcTempArgLists.begin();
6016 auto dstIt = dstTempArgLists.begin();
6017 while (srcIt!=srcTempArgLists.end() && dstIt!=dstTempArgLists.end())
6018 {
6019 if ((*srcIt).size()!=(*dstIt).size()) return TRUE;
6020 ++srcIt;
6021 ++dstIt;
6022 }
6023 return FALSE;
6024}
6025
6026static bool scopeIsTemplate(const Definition *d)
6027{
6028 bool result=FALSE;
6029 //printf("> scopeIsTemplate(%s)\n",qPrint(d?d->name():"null"));
6031 {
6032 auto cd = toClassDef(d);
6033 result = cd->templateArguments().hasParameters() || cd->templateMaster()!=nullptr ||
6035 }
6036 //printf("< scopeIsTemplate=%d\n",result);
6037 return result;
6038}
6039
6041 const ArgumentLists &srcTempArgLists,
6042 const ArgumentLists &dstTempArgLists,
6043 const std::string &src
6044 )
6045{
6046 std::string dst;
6047 static const reg::Ex re(R"(\a\w*)");
6048 reg::Iterator it(src,re);
6050 //printf("type=%s\n",qPrint(sa->type));
6051 size_t p=0;
6052 for (; it!=end ; ++it) // for each word in srcType
6053 {
6054 const auto &match = *it;
6055 size_t i = match.position();
6056 size_t l = match.length();
6057 bool found=FALSE;
6058 dst+=src.substr(p,i-p);
6059 std::string name=match.str();
6060
6061 auto srcIt = srcTempArgLists.begin();
6062 auto dstIt = dstTempArgLists.begin();
6063 while (srcIt!=srcTempArgLists.end() && !found)
6064 {
6065 const ArgumentList *tdAli = nullptr;
6066 std::vector<Argument>::const_iterator tdaIt;
6067 if (dstIt!=dstTempArgLists.end())
6068 {
6069 tdAli = &(*dstIt);
6070 tdaIt = tdAli->begin();
6071 ++dstIt;
6072 }
6073
6074 const ArgumentList &tsaLi = *srcIt;
6075 for (auto tsaIt = tsaLi.begin(); tsaIt!=tsaLi.end() && !found; ++tsaIt)
6076 {
6077 Argument tsa = *tsaIt;
6078 const Argument *tda = nullptr;
6079 if (tdAli && tdaIt!=tdAli->end())
6080 {
6081 tda = &(*tdaIt);
6082 ++tdaIt;
6083 }
6084 //if (tda) printf("tsa=%s|%s tda=%s|%s\n",
6085 // qPrint(tsa.type),qPrint(tsa.name),
6086 // qPrint(tda->type),qPrint(tda->name));
6087 if (name==tsa.name.str())
6088 {
6089 if (tda && tda->name.isEmpty())
6090 {
6091 QCString tdaName = tda->name;
6092 QCString tdaType = tda->type;
6093 int vc=0;
6094 if (tdaType.startsWith("class ")) vc=6;
6095 else if (tdaType.startsWith("typename ")) vc=9;
6096 if (vc>0) // convert type=="class T" to type=="class" name=="T"
6097 {
6098 tdaName = tdaType.mid(vc);
6099 }
6100 if (!tdaName.isEmpty())
6101 {
6102 name=tdaName.str(); // substitute
6103 found=TRUE;
6104 }
6105 }
6106 }
6107 }
6108
6109 //printf(" srcList='%s' dstList='%s faList='%s'\n",
6110 // qPrint(argListToString(srclali.current())),
6111 // qPrint(argListToString(dstlali.current())),
6112 // funcTempArgList ? qPrint(argListToString(funcTempArgList)) : "<none>");
6113 ++srcIt;
6114 }
6115 dst+=name;
6116 p=i+l;
6117 }
6118 dst+=src.substr(p);
6119 //printf(" substituteTemplatesInString(%s)=%s\n",
6120 // qPrint(src),qPrint(dst));
6121 return dst;
6122}
6123
6125 const ArgumentLists &srcTempArgLists,
6126 const ArgumentLists &dstTempArgLists,
6127 const ArgumentList &src,
6128 ArgumentList &dst
6129 )
6130{
6131 auto dstIt = dst.begin();
6132 for (const Argument &sa : src)
6133 {
6134 QCString dstType = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.type.str());
6135 QCString dstArray = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.array.str());
6136 if (dstIt == dst.end())
6137 {
6138 Argument da = sa;
6139 da.type = dstType;
6140 da.array = dstArray;
6141 dst.push_back(da);
6142 dstIt = dst.end();
6143 }
6144 else
6145 {
6146 Argument da = *dstIt;
6147 da.type = dstType;
6148 da.array = dstArray;
6149 ++dstIt;
6150 }
6151 }
6156 srcTempArgLists,dstTempArgLists,
6157 src.trailingReturnType().str()));
6158 dst.setIsDeleted(src.isDeleted());
6159 dst.setRefQualifier(src.refQualifier());
6160 dst.setNoParameters(src.noParameters());
6161 //printf("substituteTemplatesInArgList: replacing %s with %s\n",
6162 // qPrint(argListToString(src)),qPrint(argListToString(dst))
6163 // );
6164}
6165
6166//-------------------------------------------------------------------------------------------
6167
6168static void addLocalObjCMethod(const Entry *root,
6169 const QCString &scopeName,
6170 const QCString &funcType,const QCString &funcName,const QCString &funcArgs,
6171 const QCString &exceptions,const QCString &funcDecl,
6172 TypeSpecifier spec)
6173{
6174 AUTO_TRACE();
6175 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
6176 ClassDefMutable *cd=nullptr;
6177 if (Config_getBool(EXTRACT_LOCAL_METHODS) && (cd=getClassMutable(scopeName)))
6178 {
6179 AUTO_TRACE_ADD("Local objective C method '{}' scopeName='{}'",root->name,scopeName);
6180 auto md = createMemberDef(
6181 root->fileName,root->startLine,root->startColumn,
6182 funcType,funcName,funcArgs,exceptions,
6183 root->protection,root->virt,root->isStatic,Relationship::Member,
6185 auto mmd = toMemberDefMutable(md.get());
6186 mmd->setTagInfo(root->tagInfo());
6187 mmd->setLanguage(root->lang);
6188 mmd->setId(root->id);
6189 mmd->makeImplementationDetail();
6190 mmd->setMemberClass(cd);
6191 mmd->setDefinition(funcDecl);
6193 mmd->addQualifiers(root->qualifiers);
6194 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6195 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6196 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6197 mmd->setDocsForDefinition(!root->proto);
6198 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6199 mmd->addSectionsToDefinition(root->anchors);
6200 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6201 FileDef *fd=root->fileDef();
6202 mmd->setBodyDef(fd);
6203 mmd->setMemberSpecifiers(spec);
6204 mmd->setVhdlSpecifiers(root->vhdlSpec);
6205 mmd->setMemberGroupId(root->mGrpId);
6206 cd->insertMember(md.get());
6207 cd->insertUsedFile(fd);
6208 mmd->setRefItems(root->sli);
6209 mmd->setRequirementReferences(root->rqli);
6210
6212 mn->push_back(std::move(md));
6213 }
6214 else
6215 {
6216 // local objective C method found for class without interface
6217 }
6218}
6219
6220//-------------------------------------------------------------------------------------------
6221
6222static void addMemberFunction(const Entry *root,
6223 MemberName *mn,
6224 const QCString &scopeName,
6225 const QCString &namespaceName,
6226 const QCString &className,
6227 const QCString &funcTyp,
6228 const QCString &funcName,
6229 const QCString &funcArgs,
6230 const QCString &funcTempList,
6231 const QCString &exceptions,
6232 const QCString &type,
6233 const QCString &args,
6234 bool isFriend,
6235 TypeSpecifier spec,
6236 const QCString &relates,
6237 const QCString &funcDecl,
6238 bool overloaded,
6239 bool isFunc)
6240{
6241 AUTO_TRACE();
6242 QCString funcType = funcTyp;
6243 int count=0;
6244 int noMatchCount=0;
6245 bool memFound=FALSE;
6246 for (const auto &imd : *mn)
6247 {
6248 MemberDefMutable *md = toMemberDefMutable(imd.get());
6249 if (md==nullptr) continue;
6251 if (cd==nullptr) continue;
6252 //AUTO_TRACE_ADD("member definition found, scope needed='{}' scope='{}' args='{}' fileName='{}'",
6253 // scopeName, cd->name(), md->argsString(), root->fileName);
6254 FileDef *fd=root->fileDef();
6255 NamespaceDef *nd=nullptr;
6256 if (!namespaceName.isEmpty()) nd=getResolvedNamespace(namespaceName);
6257
6258 //printf("scopeName %s->%s\n",qPrint(scopeName),
6259 // qPrint(stripTemplateSpecifiersFromScope(scopeName,FALSE)));
6260
6261 // if the member we are searching for is an enum value that is part of
6262 // a "strong" enum, we need to look into the fields of the enum for a match
6263 int enumNamePos=0;
6264 if (md->isEnumValue() && (enumNamePos=className.findRev("::"))!=-1)
6265 {
6266 QCString enumName = className.mid(enumNamePos+2);
6267 QCString fullScope = className.left(enumNamePos);
6268 if (!namespaceName.isEmpty()) fullScope.prepend(namespaceName+"::");
6269 if (fullScope==cd->name())
6270 {
6271 MemberName *enumMn=Doxygen::memberNameLinkedMap->find(enumName);
6272 //printf("enumMn(%s)=%p\n",qPrint(className),(void*)enumMn);
6273 if (enumMn)
6274 {
6275 for (const auto &emd : *enumMn)
6276 {
6277 memFound = emd->isStrong() && md->getEnumScope()==emd.get();
6278 if (memFound)
6279 {
6280 addMemberDocs(root,md,funcDecl,nullptr,overloaded,spec);
6281 count++;
6282 }
6283 if (memFound) break;
6284 }
6285 }
6286 }
6287 }
6288 if (memFound) break;
6289
6290 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
6291 if (tcd==nullptr && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
6292 {
6293 // don't be fooled by anonymous scopes
6294 tcd=cd;
6295 }
6296 //printf("Looking for %s inside nd=%s result=%s cd=%s\n",
6297 // qPrint(scopeName),nd?qPrint(nd->name()):"<none>",tcd?qPrint(tcd->name()):"",qPrint(cd->name()));
6298
6299 if (cd && tcd==cd) // member's classes match
6300 {
6301 AUTO_TRACE_ADD("class definition '{}' found",cd->name());
6302
6303 // get the template parameter lists found at the member declaration
6304 ArgumentLists declTemplArgs = cd->getTemplateParameterLists();
6305 const ArgumentList &templAl = md->templateArguments();
6306 if (!templAl.empty())
6307 {
6308 declTemplArgs.push_back(templAl);
6309 }
6310
6311 // get the template parameter lists found at the member definition
6312 const ArgumentLists &defTemplArgs = root->tArgLists;
6313 //printf("defTemplArgs=%p\n",defTemplArgs);
6314
6315 // do we replace the decl argument lists with the def argument lists?
6316 bool substDone=false;
6317 ArgumentList argList;
6318
6319 /* substitute the occurrences of class template names in the
6320 * argument list before matching
6321 */
6322 const ArgumentList &mdAl = md->argumentList();
6323 if (declTemplArgs.size()>0 && declTemplArgs.size()==defTemplArgs.size())
6324 {
6325 /* the function definition has template arguments
6326 * and the class definition also has template arguments, so
6327 * we must substitute the template names of the class by that
6328 * of the function definition before matching.
6329 */
6330 substituteTemplatesInArgList(declTemplArgs,defTemplArgs,mdAl,argList);
6331
6332 substDone=TRUE;
6333 }
6334 else /* no template arguments, compare argument lists directly */
6335 {
6336 argList = mdAl;
6337 }
6338
6339 bool matching=
6340 md->isVariable() || md->isTypedef() || // needed for function pointers
6342 md->getClassDef(),md->getFileDef(),md->typeString(),&argList,
6343 cd,fd,root->type,&root->argList,
6344 TRUE,root->lang);
6345
6346 AUTO_TRACE_ADD("matching '{}'<=>'{}' className='{}' namespaceName='{}' result={}",
6347 argListToString(argList,TRUE),argListToString(root->argList,TRUE),className,namespaceName,matching);
6348
6349 if (md->getLanguage()==SrcLangExt::ObjC && md->isVariable() && root->section.isFunction())
6350 {
6351 matching = FALSE; // don't match methods and attributes with the same name
6352 }
6353
6354 // for template member we also need to check the return type
6355 if (!md->templateArguments().empty() && !root->tArgLists.empty())
6356 {
6357 QCString memType = md->typeString();
6358 memType.stripPrefix("static "); // see bug700696
6360 className+"::",""); // see bug700693 & bug732594
6362 className+"::",""); // see bug758900
6363 if (memType=="auto" && !argList.trailingReturnType().isEmpty())
6364 {
6365 memType = argList.trailingReturnType();
6366 memType.stripPrefix(" -> ");
6367 }
6368 if (funcType=="auto" && !root->argList.trailingReturnType().isEmpty())
6369 {
6370 funcType = root->argList.trailingReturnType();
6371 funcType.stripPrefix(" -> ");
6373 substDone=true;
6374 }
6375 AUTO_TRACE_ADD("Comparing return types '{}'<->'{}' #args {}<->{}",
6376 memType,funcType,md->templateArguments().size(),root->tArgLists.back().size());
6377 if (md->templateArguments().size()!=root->tArgLists.back().size() || memType!=funcType)
6378 {
6379 //printf(" ---> no matching\n");
6380 matching = FALSE;
6381 }
6382 }
6383 else if (defTemplArgs.size()>declTemplArgs.size())
6384 {
6385 AUTO_TRACE_ADD("Different number of template arguments {} vs {}",defTemplArgs.size(),declTemplArgs.size());
6386 // avoid matching a non-template function in a template class against a
6387 // template function with the same name and parameters, see issue #10184
6388 substDone = false;
6389 matching = false;
6390 }
6391 bool rootIsUserDoc = root->section.isMemberDoc();
6392 bool classIsTemplate = scopeIsTemplate(md->getClassDef());
6393 bool mdIsTemplate = md->templateArguments().hasParameters();
6394 bool classOrMdIsTemplate = mdIsTemplate || classIsTemplate;
6395 bool rootIsTemplate = !root->tArgLists.empty();
6396 //printf("classIsTemplate=%d mdIsTemplate=%d rootIsTemplate=%d\n",classIsTemplate,mdIsTemplate,rootIsTemplate);
6397 if (!rootIsUserDoc && // don't check out-of-line @fn references, see bug722457
6398 (mdIsTemplate || rootIsTemplate) && // either md or root is a template
6399 ((classOrMdIsTemplate && !rootIsTemplate) || (!classOrMdIsTemplate && rootIsTemplate))
6400 )
6401 {
6402 // Method with template return type does not match method without return type
6403 // even if the parameters are the same. See also bug709052
6404 AUTO_TRACE_ADD("Comparing return types: template v.s. non-template");
6405 matching = FALSE;
6406 }
6407
6408 AUTO_TRACE_ADD("Match results of matchArguments2='{}' substDone='{}'",matching,substDone);
6409
6410 if (substDone) // found a new argument list
6411 {
6412 if (matching) // replace member's argument list
6413 {
6415 md->moveArgumentList(std::make_unique<ArgumentList>(argList));
6416 }
6417 else // no match
6418 {
6419 if (!funcTempList.isEmpty() &&
6420 isSpecialization(declTemplArgs,defTemplArgs))
6421 {
6422 // check if we are dealing with a partial template
6423 // specialization. In this case we add it to the class
6424 // even though the member arguments do not match.
6425
6426 addMethodToClass(root,cd,type,md->name(),args,isFriend,
6427 md->protection(),md->isStatic(),md->virtualness(),spec,relates);
6428 return;
6429 }
6430 }
6431 }
6432 if (matching)
6433 {
6434 addMemberDocs(root,md,funcDecl,nullptr,overloaded,spec);
6435 count++;
6436 memFound=TRUE;
6437 }
6438 }
6439 else if (cd && cd!=tcd) // we did find a class with the same name as cd
6440 // but in a different namespace
6441 {
6442 noMatchCount++;
6443 }
6444
6445 if (memFound) break;
6446 }
6447 if (count==0 && root->parent() && root->parent()->section.isObjcImpl())
6448 {
6449 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
6450 return;
6451 }
6452 if (count==0 && !(isFriend && funcType=="class"))
6453 {
6454 int candidates=0;
6455 const ClassDef *ecd = nullptr, *ucd = nullptr;
6456 MemberDef *emd = nullptr, *umd = nullptr;
6457 //printf("Assume template class\n");
6458 for (const auto &md : *mn)
6459 {
6460 MemberDef *cmd=md.get();
6462 ClassDefMutable *ccd=cdmdm ? cdmdm->getClassDefMutable() : nullptr;
6463 //printf("ccd->name()==%s className=%s\n",qPrint(ccd->name()),qPrint(className));
6464 if (ccd!=nullptr && rightScopeMatch(ccd->name(),className))
6465 {
6466 const ArgumentList &templAl = md->templateArguments();
6467 if (!root->tArgLists.empty() && !templAl.empty() &&
6468 root->tArgLists.back().size()<=templAl.size())
6469 {
6470 AUTO_TRACE_ADD("add template specialization");
6471 addMethodToClass(root,ccd,type,md->name(),args,isFriend,
6472 root->protection,root->isStatic,root->virt,spec,relates);
6473 return;
6474 }
6475 if (argListToString(md->argumentList(),FALSE,FALSE) ==
6477 { // exact argument list match -> remember
6478 ucd = ecd = ccd;
6479 umd = emd = cmd;
6480 AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': exact match",
6481 className,ccd->name(),md->argsString());
6482 }
6483 else // arguments do not match, but member name and scope do -> remember
6484 {
6485 ucd = ccd;
6486 umd = cmd;
6487 AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': no match",
6488 className,ccd->name(),md->argsString());
6489 }
6490 candidates++;
6491 }
6492 }
6493 bool strictProtoMatching = Config_getBool(STRICT_PROTO_MATCHING);
6494 if (!strictProtoMatching)
6495 {
6496 if (candidates==1 && ucd && umd)
6497 {
6498 // we didn't find an actual match on argument lists, but there is only 1 member with this
6499 // name in the same scope, so that has to be the one.
6500 addMemberDocs(root,toMemberDefMutable(umd),funcDecl,nullptr,overloaded,spec);
6501 return;
6502 }
6503 else if (candidates>1 && ecd && emd)
6504 {
6505 // we didn't find a unique match using type resolution,
6506 // but one of the matches has the exact same signature so
6507 // we take that one.
6508 addMemberDocs(root,toMemberDefMutable(emd),funcDecl,nullptr,overloaded,spec);
6509 return;
6510 }
6511 }
6512
6513 QCString warnMsg = "no ";
6514 if (noMatchCount>1) warnMsg+="uniquely ";
6515 warnMsg+="matching class member found for \n";
6516
6517 for (const ArgumentList &al : root->tArgLists)
6518 {
6519 warnMsg+=" template ";
6520 warnMsg+=tempArgListToString(al,root->lang);
6521 warnMsg+='\n';
6522 }
6523
6524 QCString fullFuncDecl=funcDecl;
6525 if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
6526
6527 warnMsg+=" ";
6528 warnMsg+=fullFuncDecl;
6529
6530 if (candidates>0 || noMatchCount>=1)
6531 {
6532 warnMsg+="\nPossible candidates:";
6533
6534 NamespaceDef *nd=nullptr;
6535 if (!namespaceName.isEmpty()) nd=getResolvedNamespace(namespaceName);
6536 FileDef *fd=root->fileDef();
6537
6538 for (const auto &md : *mn)
6539 {
6540 const ClassDef *cd=md->getClassDef();
6541 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
6542 if (tcd==nullptr && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
6543 {
6544 // don't be fooled by anonymous scopes
6545 tcd=cd;
6546 }
6547 if (cd!=nullptr && (rightScopeMatch(cd->name(),className) || (cd!=tcd)))
6548 {
6549 warnMsg+='\n';
6550 const ArgumentList &templAl = md->templateArguments();
6551 warnMsg+=" '";
6552 if (templAl.hasParameters())
6553 {
6554 warnMsg+="template ";
6555 warnMsg+=tempArgListToString(templAl,root->lang);
6556 warnMsg+='\n';
6557 warnMsg+=" ";
6558 }
6559 if (!md->typeString().isEmpty())
6560 {
6561 warnMsg+=md->typeString();
6562 warnMsg+=' ';
6563 }
6565 if (!qScope.isEmpty())
6566 warnMsg+=qScope+"::"+md->name();
6567 warnMsg+=md->argsString();
6568 warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
6569 }
6570 }
6571 }
6572 warn(root->fileName,root->startLine,"{}",warnMsg);
6573 }
6574}
6575
6576//-------------------------------------------------------------------------------------------
6577
6578static void addMemberSpecialization(const Entry *root,
6579 MemberName *mn,
6580 ClassDefMutable *cd,
6581 const QCString &funcType,
6582 const QCString &funcName,
6583 const QCString &funcArgs,
6584 const QCString &funcDecl,
6585 const QCString &exceptions,
6586 TypeSpecifier spec
6587 )
6588{
6589 AUTO_TRACE("funcType={} funcName={} funcArgs={} funcDecl={} spec={}",funcType,funcName,funcArgs,funcDecl,spec);
6590 MemberDef *declMd=nullptr;
6591 for (const auto &md : *mn)
6592 {
6593 if (md->getClassDef()==cd)
6594 {
6595 // TODO: we should probably also check for matching arguments
6596 declMd = md.get();
6597 break;
6598 }
6599 }
6601 ArgumentList tArgList;
6602 // getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6603 auto md = createMemberDef(
6604 root->fileName,root->startLine,root->startColumn,
6605 funcType,funcName,funcArgs,exceptions,
6606 declMd ? declMd->protection() : root->protection,
6607 root->virt,root->isStatic,Relationship::Member,
6608 mtype,tArgList,root->argList,root->metaData);
6609 auto mmd = toMemberDefMutable(md.get());
6610 //printf("new specialized member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6611 mmd->setTagInfo(root->tagInfo());
6612 mmd->setLanguage(root->lang);
6613 mmd->setId(root->id);
6614 mmd->setMemberClass(cd);
6615 mmd->setTemplateSpecialization(TRUE);
6616 mmd->setTypeConstraints(root->typeConstr);
6617 mmd->setDefinition(funcDecl);
6619 mmd->addQualifiers(root->qualifiers);
6620 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6621 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6622 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6623 mmd->setDocsForDefinition(!root->proto);
6624 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6625 mmd->addSectionsToDefinition(root->anchors);
6626 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6627 FileDef *fd=root->fileDef();
6628 mmd->setBodyDef(fd);
6629 mmd->setMemberSpecifiers(spec);
6630 mmd->setVhdlSpecifiers(root->vhdlSpec);
6631 mmd->setMemberGroupId(root->mGrpId);
6632 cd->insertMember(md.get());
6633 mmd->setRefItems(root->sli);
6634 mmd->setRequirementReferences(root->rqli);
6635
6636 mn->push_back(std::move(md));
6637}
6638
6639//-------------------------------------------------------------------------------------------
6640
6641static void addOverloaded(const Entry *root,MemberName *mn,
6642 const QCString &funcType,const QCString &funcName,const QCString &funcArgs,
6643 const QCString &funcDecl,const QCString &exceptions,TypeSpecifier spec)
6644{
6645 // for unique overloaded member we allow the class to be
6646 // omitted, this is to be Qt compatible. Using this should
6647 // however be avoided, because it is error prone
6648 bool sameClass=false;
6649 if (mn->size()>0)
6650 {
6651 // check if all members with the same name are also in the same class
6652 sameClass = std::equal(mn->begin()+1,mn->end(),mn->begin(),
6653 [](const auto &md1,const auto &md2)
6654 { return md1->getClassDef()->name()==md2->getClassDef()->name(); });
6655 }
6656 if (sameClass)
6657 {
6658 MemberDefMutable *mdm = toMemberDefMutable(mn->front().get());
6659 ClassDefMutable *cd = mdm ? mdm->getClassDefMutable() : nullptr;
6660 if (cd==nullptr) return;
6661
6663 if (root->mtype==MethodTypes::Signal) mtype=MemberType::Signal;
6664 else if (root->mtype==MethodTypes::Slot) mtype=MemberType::Slot;
6665 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType::DCOP;
6666
6667 // new overloaded member function
6668 std::unique_ptr<ArgumentList> tArgList =
6669 getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6670 //printf("new related member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6671 auto md = createMemberDef(
6672 root->fileName,root->startLine,root->startColumn,
6673 funcType,funcName,funcArgs,exceptions,
6674 root->protection,root->virt,root->isStatic,Relationship::Related,
6675 mtype,tArgList ? *tArgList : ArgumentList(),root->argList,root->metaData);
6676 auto mmd = toMemberDefMutable(md.get());
6677 mmd->setTagInfo(root->tagInfo());
6678 mmd->setLanguage(root->lang);
6679 mmd->setId(root->id);
6680 mmd->setTypeConstraints(root->typeConstr);
6681 mmd->setMemberClass(cd);
6682 mmd->setDefinition(funcDecl);
6684 mmd->addQualifiers(root->qualifiers);
6686 doc+="<p>";
6687 doc+=root->doc;
6688 mmd->setDocumentation(doc,root->docFile,root->docLine);
6689 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6690 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6691 mmd->setDocsForDefinition(!root->proto);
6692 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6693 mmd->addSectionsToDefinition(root->anchors);
6694 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6695 FileDef *fd=root->fileDef();
6696 mmd->setBodyDef(fd);
6697 mmd->setMemberSpecifiers(spec);
6698 mmd->setVhdlSpecifiers(root->vhdlSpec);
6699 mmd->setMemberGroupId(root->mGrpId);
6700 cd->insertMember(md.get());
6701 cd->insertUsedFile(fd);
6702 mmd->setRefItems(root->sli);
6703 mmd->setRequirementReferences(root->rqli);
6704
6705 mn->push_back(std::move(md));
6706 }
6707}
6708
6709static void insertMemberAlias(Definition *outerScope,const MemberDef *md)
6710{
6711 if (outerScope && outerScope!=Doxygen::globalScope)
6712 {
6713 auto aliasMd = createMemberDefAlias(outerScope,md);
6714 if (outerScope->definitionType()==Definition::TypeClass)
6715 {
6716 ClassDefMutable *cdm = toClassDefMutable(outerScope);
6717 if (cdm)
6718 {
6719 cdm->insertMember(aliasMd.get());
6720 }
6721 }
6722 else if (outerScope->definitionType()==Definition::TypeNamespace)
6723 {
6724 NamespaceDefMutable *ndm = toNamespaceDefMutable(outerScope);
6725 if (ndm)
6726 {
6727 ndm->insertMember(aliasMd.get());
6728 }
6729 }
6730 else if (outerScope->definitionType()==Definition::TypeFile)
6731 {
6732 toFileDef(outerScope)->insertMember(aliasMd.get());
6733 }
6734 if (aliasMd)
6735 {
6736 Doxygen::functionNameLinkedMap->add(md->name())->push_back(std::move(aliasMd));
6737 }
6738 }
6739}
6740
6741//-------------------------------------------------------------------------------------------
6742
6743/*! This function tries to find a member (in a documented class/file/namespace)
6744 * that corresponds to the function/variable declaration given in \a funcDecl.
6745 *
6746 * The boolean \a overloaded is used to specify whether or not a standard
6747 * overload documentation line should be generated.
6748 *
6749 * The boolean \a isFunc is a hint that indicates that this is a function
6750 * instead of a variable or typedef.
6751 */
6752static void findMember(const Entry *root,
6753 const QCString &relates,
6754 const QCString &type,
6755 const QCString &args,
6756 QCString funcDecl,
6757 bool overloaded,
6758 bool isFunc
6759 )
6760{
6761 AUTO_TRACE("root='{}' funcDecl='{}' related='{}' overload={} isFunc={} mGrpId={} #tArgList={} spec={} lang={}",
6762 root->name, funcDecl, relates, overloaded, isFunc, root->mGrpId, root->tArgLists.size(),
6763 root->spec, root->lang);
6764
6765 QCString scopeName;
6766 QCString className;
6767 QCString namespaceName;
6768 QCString funcType;
6769 QCString funcName;
6770 QCString funcArgs;
6771 QCString funcTempList;
6772 QCString exceptions;
6773 QCString funcSpec;
6774 bool isRelated=false;
6775 bool isMemberOf=false;
6776 bool isFriend=false;
6777 bool done=false;
6778 TypeSpecifier spec = root->spec;
6779 while (!done)
6780 {
6781 done=true;
6782 if (funcDecl.stripPrefix("friend ")) // treat friends as related members
6783 {
6784 isFriend=true;
6785 done=false;
6786 }
6787 if (funcDecl.stripPrefix("inline "))
6788 {
6789 spec.setInline(true);
6790 done=false;
6791 }
6792 if (funcDecl.stripPrefix("explicit "))
6793 {
6794 spec.setExplicit(true);
6795 done=false;
6796 }
6797 if (funcDecl.stripPrefix("mutable "))
6798 {
6799 spec.setMutable(true);
6800 done=false;
6801 }
6802 if (funcDecl.stripPrefix("thread_local "))
6803 {
6804 spec.setThreadLocal(true);
6805 done=false;
6806 }
6807 if (funcDecl.stripPrefix("virtual "))
6808 {
6809 done=false;
6810 }
6811 }
6812
6813 // delete any ; from the function declaration
6814 int sep=0;
6815 while ((sep=funcDecl.find(';'))!=-1)
6816 {
6817 funcDecl=(funcDecl.left(sep)+funcDecl.right(funcDecl.length()-sep-1)).stripWhiteSpace();
6818 }
6819
6820 // make sure the first character is a space to simplify searching.
6821 if (!funcDecl.isEmpty() && funcDecl[0]!=' ') funcDecl.prepend(" ");
6822
6823 // remove some superfluous spaces
6824 funcDecl= substitute(
6825 substitute(
6826 substitute(funcDecl,"~ ","~"),
6827 ":: ","::"
6828 ),
6829 " ::","::"
6830 ).stripWhiteSpace();
6831
6832 //printf("funcDecl='%s'\n",qPrint(funcDecl));
6833 if (isFriend && funcDecl.startsWith("class "))
6834 {
6835 //printf("friend class\n");
6836 funcDecl=funcDecl.right(funcDecl.length()-6);
6837 funcName = funcDecl;
6838 }
6839 else if (isFriend && funcDecl.startsWith("struct "))
6840 {
6841 funcDecl=funcDecl.right(funcDecl.length()-7);
6842 funcName = funcDecl;
6843 }
6844 else
6845 {
6846 // extract information from the declarations
6847 parseFuncDecl(funcDecl,root->lang,scopeName,funcType,funcName,
6848 funcArgs,funcTempList,exceptions
6849 );
6850 }
6851
6852 // the class name can also be a namespace name, we decide this later.
6853 // if a related class name is specified and the class name could
6854 // not be derived from the function declaration, then use the
6855 // related field.
6856 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}' funcType='{}' funcName='{}' funcArgs='{}'",
6857 scopeName,className,namespaceName,funcType,funcName,funcArgs);
6858 if (!relates.isEmpty())
6859 { // related member, prefix user specified scope
6860 isRelated=TRUE;
6861 isMemberOf=(root->relatesType == RelatesType::MemberOf);
6862 if (getClass(relates)==nullptr && !scopeName.isEmpty())
6863 {
6864 scopeName= mergeScopes(scopeName,relates);
6865 }
6866 else
6867 {
6868 scopeName = relates;
6869 }
6870 }
6871
6872 if (relates.isEmpty() && root->parent() &&
6873 (root->parent()->section.isScope() || root->parent()->section.isObjcImpl()) &&
6874 !root->parent()->name.isEmpty()) // see if we can combine scopeName
6875 // with the scope in which it was found
6876 {
6877 QCString joinedName = root->parent()->name+"::"+scopeName;
6878 if (!scopeName.isEmpty() &&
6879 (getClass(joinedName) || Doxygen::namespaceLinkedMap->find(joinedName)))
6880 {
6881 scopeName = joinedName;
6882 }
6883 else
6884 {
6885 scopeName = mergeScopes(root->parent()->name,scopeName);
6886 }
6887 }
6888 else // see if we can prefix a namespace or class that is used from the file
6889 {
6890 FileDef *fd=root->fileDef();
6891 if (fd)
6892 {
6893 for (const auto &fnd : fd->getUsedNamespaces())
6894 {
6895 QCString joinedName = fnd->name()+"::"+scopeName;
6896 if (Doxygen::namespaceLinkedMap->find(joinedName))
6897 {
6898 scopeName=joinedName;
6899 break;
6900 }
6901 }
6902 }
6903 }
6905 removeRedundantWhiteSpace(scopeName),false,&funcSpec,QCString(),false);
6906
6907 // funcSpec contains the last template specifiers of the given scope.
6908 // If this method does not have any template arguments or they are
6909 // empty while funcSpec is not empty we assume this is a
6910 // specialization of a method. If not, we clear the funcSpec and treat
6911 // this as a normal method of a template class.
6912 if (!(root->tArgLists.size()>0 &&
6913 root->tArgLists.front().size()==0
6914 )
6915 )
6916 {
6917 funcSpec.clear();
6918 }
6919
6920 //namespaceName=removeAnonymousScopes(namespaceName);
6921 if (!Config_getBool(EXTRACT_ANON_NSPACES) && scopeName.find('@')!=-1) return; // skip stuff in anonymous namespace...
6922
6923 // split scope into a namespace and a class part
6924 extractNamespaceName(scopeName,className,namespaceName,TRUE);
6925 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}'",scopeName,className,namespaceName);
6926
6927 //printf("namespaceName='%s' className='%s'\n",qPrint(namespaceName),qPrint(className));
6928 // merge class and namespace scopes again
6929 scopeName.clear();
6930 if (!namespaceName.isEmpty())
6931 {
6932 if (className.isEmpty())
6933 {
6934 scopeName=namespaceName;
6935 }
6936 else if (!relates.isEmpty() || // relates command with explicit scope
6937 !getClass(className)) // class name only exists in a namespace
6938 {
6939 scopeName=namespaceName+"::"+className;
6940 }
6941 else
6942 {
6943 scopeName=className;
6944 }
6945 }
6946 else if (!className.isEmpty())
6947 {
6948 scopeName=className;
6949 }
6950 //printf("new scope='%s'\n",qPrint(scopeName));
6951
6952 QCString tempScopeName=scopeName;
6953 ClassDefMutable *cd=getClassMutable(scopeName);
6954 if (cd)
6955 {
6956 if (funcSpec.isEmpty())
6957 {
6958 uint32_t argListIndex=0;
6959 tempScopeName=cd->qualifiedNameWithTemplateParameters(&root->tArgLists,&argListIndex);
6960 }
6961 else
6962 {
6963 tempScopeName=scopeName+funcSpec;
6964 }
6965 }
6966 //printf("scopeName=%s cd=%p root->tArgLists=%p result=%s\n",
6967 // qPrint(scopeName),cd,root->tArgLists,qPrint(tempScopeName));
6968
6969 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
6970 // rebuild the function declaration (needed to get the scope right).
6971 if (!scopeName.isEmpty() && !isRelated && !isFriend && !Config_getBool(HIDE_SCOPE_NAMES) && root->lang!=SrcLangExt::Python)
6972 {
6973 if (!funcType.isEmpty())
6974 {
6975 if (isFunc) // a function -> we use argList for the arguments
6976 {
6977 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcTempList;
6978 }
6979 else
6980 {
6981 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcArgs;
6982 }
6983 }
6984 else
6985 {
6986 if (isFunc) // a function => we use argList for the arguments
6987 {
6988 funcDecl=tempScopeName+"::"+funcName+funcTempList;
6989 }
6990 else // variable => add 'argument' list
6991 {
6992 funcDecl=tempScopeName+"::"+funcName+funcArgs;
6993 }
6994 }
6995 }
6996 else // build declaration without scope
6997 {
6998 if (!funcType.isEmpty()) // but with a type
6999 {
7000 if (isFunc) // function => omit argument list
7001 {
7002 funcDecl=funcType+" "+funcName+funcTempList;
7003 }
7004 else // variable => add 'argument' list
7005 {
7006 funcDecl=funcType+" "+funcName+funcArgs;
7007 }
7008 }
7009 else // no type
7010 {
7011 if (isFunc)
7012 {
7013 funcDecl=funcName+funcTempList;
7014 }
7015 else
7016 {
7017 funcDecl=funcName+funcArgs;
7018 }
7019 }
7020 }
7021
7022 if (funcType=="template class" && !funcTempList.isEmpty())
7023 return; // ignore explicit template instantiations
7024
7025 AUTO_TRACE_ADD("Parse results: namespaceName='{}' className=`{}` funcType='{}' funcSpec='{}' "
7026 " funcName='{}' funcArgs='{}' funcTempList='{}' funcDecl='{}' relates='{}'"
7027 " exceptions='{}' isRelated={} isMemberOf={} isFriend={} isFunc={}",
7028 namespaceName, className, funcType, funcSpec,
7029 funcName, funcArgs, funcTempList, funcDecl, relates,
7030 exceptions, isRelated, isMemberOf, isFriend, isFunc);
7031
7032 if (!funcName.isEmpty()) // function name is valid
7033 {
7034 // check if 'className' is actually a scoped enum, in which case we need to
7035 // process it as a global, see issue #6471
7036 bool strongEnum = false;
7037 MemberName *mn=nullptr;
7038 if (!className.isEmpty() && (mn=Doxygen::functionNameLinkedMap->find(className)))
7039 {
7040 for (const auto &imd : *mn)
7041 {
7042 MemberDefMutable *md = toMemberDefMutable(imd.get());
7043 Definition *mdScope = nullptr;
7044 if (md && md->isEnumerate() && md->isStrong() && (mdScope=md->getOuterScope()) &&
7045 // need filter for the correct scope, see issue #9668
7046 ((namespaceName.isEmpty() && mdScope==Doxygen::globalScope) || (mdScope->name()==namespaceName)))
7047 {
7048 AUTO_TRACE_ADD("'{}' is a strong enum! (namespace={} md->getOuterScope()->name()={})",md->name(),namespaceName,md->getOuterScope()->name());
7049 strongEnum = true;
7050 // pass the scope name name as a 'namespace' to the findGlobalMember function
7051 if (!namespaceName.isEmpty())
7052 {
7053 namespaceName+="::"+className;
7054 }
7055 else
7056 {
7057 namespaceName=className;
7058 }
7059 }
7060 }
7061 }
7062
7063 if (funcName.startsWith("operator ")) // strip class scope from cast operator
7064 {
7065 funcName = substitute(funcName,className+"::","");
7066 }
7067 mn = nullptr;
7068 if (!funcTempList.isEmpty()) // try with member specialization
7069 {
7070 mn=Doxygen::memberNameLinkedMap->find(funcName+funcTempList);
7071 }
7072 if (mn==nullptr) // try without specialization
7073 {
7074 mn=Doxygen::memberNameLinkedMap->find(funcName);
7075 }
7076 if (!isRelated && !strongEnum && mn) // function name already found
7077 {
7078 AUTO_TRACE_ADD("member name exists ({} members with this name)",mn->size());
7079 if (!className.isEmpty()) // class name is valid
7080 {
7081 if (funcSpec.isEmpty()) // not a member specialization
7082 {
7083 addMemberFunction(root,mn,scopeName,namespaceName,className,funcType,funcName,
7084 funcArgs,funcTempList,exceptions,
7085 type,args,isFriend,spec,relates,funcDecl,overloaded,isFunc);
7086 }
7087 else if (cd) // member specialization
7088 {
7089 addMemberSpecialization(root,mn,cd,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
7090 }
7091 else
7092 {
7093 //printf("*** Specialized member %s of unknown scope %s%s found!\n",
7094 // qPrint(scopeName),qPrint(funcName),qPrint(funcArgs));
7095 }
7096 }
7097 else if (overloaded) // check if the function belongs to only one class
7098 {
7099 addOverloaded(root,mn,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
7100 }
7101 else // unrelated function with the same name as a member
7102 {
7103 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
7104 {
7105 QCString fullFuncDecl=funcDecl;
7106 if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
7107 warn(root->fileName,root->startLine,
7108 "Cannot determine class for function\n{}",
7109 fullFuncDecl
7110 );
7111 }
7112 }
7113 }
7114 else if (isRelated && !relates.isEmpty())
7115 {
7116 AUTO_TRACE_ADD("related function scopeName='{}' className='{}'",scopeName,className);
7117 if (className.isEmpty()) className=relates;
7118 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
7119 if ((cd=getClassMutable(scopeName)))
7120 {
7121 bool newMember=TRUE; // assume we have a new member
7122 MemberDefMutable *mdDefine=nullptr;
7123 {
7124 mn = Doxygen::functionNameLinkedMap->find(funcName);
7125 if (mn)
7126 {
7127 for (const auto &imd : *mn)
7128 {
7129 MemberDefMutable *md = toMemberDefMutable(imd.get());
7130 if (md && md->isDefine())
7131 {
7132 mdDefine = md;
7133 break;
7134 }
7135 }
7136 }
7137 }
7138
7139 if (mdDefine) // macro definition is already created by the preprocessor and inserted as a file member
7140 {
7141 //printf("moving #define %s into class %s\n",qPrint(mdDefine->name()),qPrint(cd->name()));
7142
7143 // take mdDefine from the Doxygen::functionNameLinkedMap (without deleting the data)
7144 auto mdDefineTaken = Doxygen::functionNameLinkedMap->take(funcName,mdDefine);
7145 // insert it as a class member
7146 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==nullptr)
7147 {
7148 mn=Doxygen::memberNameLinkedMap->add(funcName);
7149 }
7150
7151 if (mdDefine->getFileDef())
7152 {
7153 mdDefine->getFileDef()->removeMember(mdDefine);
7154 }
7155 mdDefine->makeRelated();
7156 mdDefine->setMemberClass(cd);
7157 mdDefine->moveTo(cd);
7158 cd->insertMember(mdDefine);
7159 // also insert the member as an alias in the parent's scope, so it can be referenced also without cd's scope
7160 insertMemberAlias(cd->getOuterScope(),mdDefine);
7161 mn->push_back(std::move(mdDefineTaken));
7162 }
7163 else // normal member, needs to be created and added to the class
7164 {
7165 FileDef *fd=root->fileDef();
7166
7167 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==nullptr)
7168 {
7169 mn=Doxygen::memberNameLinkedMap->add(funcName);
7170 }
7171 else
7172 {
7173 // see if we got another member with matching arguments
7174 MemberDefMutable *rmd_found = nullptr;
7175 for (const auto &irmd : *mn)
7176 {
7177 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
7178 if (rmd)
7179 {
7180 const ArgumentList &rmdAl = rmd->argumentList();
7181
7182 newMember=
7183 className!=rmd->getOuterScope()->name() ||
7184 !matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmdAl,
7185 cd,fd,root->type,&root->argList,
7186 TRUE,root->lang);
7187 if (!newMember)
7188 {
7189 rmd_found = rmd;
7190 }
7191 }
7192 }
7193 if (rmd_found) // member already exists as rmd -> add docs
7194 {
7195 AUTO_TRACE_ADD("addMemberDocs for related member {}",root->name);
7196 addMemberDocs(root,rmd_found,funcDecl,nullptr,overloaded,spec);
7197 newMember=false;
7198 }
7199 }
7200
7201 if (newMember) // need to create a new member
7202 {
7204 switch (root->mtype)
7205 {
7206 case MethodTypes::Method: mtype = MemberType::Function; break;
7207 case MethodTypes::Signal: mtype = MemberType::Signal; break;
7208 case MethodTypes::Slot: mtype = MemberType::Slot; break;
7209 case MethodTypes::DCOP: mtype = MemberType::DCOP; break;
7210 case MethodTypes::Property: mtype = MemberType::Property; break;
7211 case MethodTypes::Event: mtype = MemberType::Event; break;
7212 }
7213
7214 //printf("New related name '%s' '%d'\n",qPrint(funcName),
7215 // root->argList ? (int)root->argList->count() : -1);
7216
7217 // first note that we pass:
7218 // (root->tArgLists ? root->tArgLists->last() : nullptr)
7219 // for the template arguments for the new "member."
7220 // this accurately reflects the template arguments of
7221 // the related function, which don't have to do with
7222 // those of the related class.
7223 auto md = createMemberDef(
7224 root->fileName,root->startLine,root->startColumn,
7225 funcType,funcName,funcArgs,exceptions,
7226 root->protection,root->virt,
7227 root->isStatic,
7228 isMemberOf ? Relationship::Foreign : Relationship::Related,
7229 mtype,
7230 (!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList()),
7231 funcArgs.isEmpty() ? ArgumentList() : root->argList,
7232 root->metaData);
7233 auto mmd = toMemberDefMutable(md.get());
7234
7235 // also insert the member as an alias in the parent's scope, so it can be referenced also without cd's scope
7236 insertMemberAlias(cd->getOuterScope(),md.get());
7237
7238 // we still have the problem that
7239 // MemberDef::writeDocumentation() in memberdef.cpp
7240 // writes the template argument list for the class,
7241 // as if this member is a member of the class.
7242 // fortunately, MemberDef::writeDocumentation() has
7243 // a special mechanism that allows us to totally
7244 // override the set of template argument lists that
7245 // are printed. We use that and set it to the
7246 // template argument lists of the related function.
7247 //
7248 mmd->setDefinitionTemplateParameterLists(root->tArgLists);
7249
7250 mmd->setTagInfo(root->tagInfo());
7251
7252 //printf("Related member name='%s' decl='%s' bodyLine='%d'\n",
7253 // qPrint(funcName),qPrint(funcDecl),root->bodyLine);
7254
7255 // try to find the matching line number of the body from the
7256 // global function list
7257 bool found=FALSE;
7258 if (root->bodyLine==-1)
7259 {
7260 MemberName *rmn=Doxygen::functionNameLinkedMap->find(funcName);
7261 if (rmn)
7262 {
7263 const MemberDefMutable *rmd_found=nullptr;
7264 for (const auto &irmd : *rmn)
7265 {
7266 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
7267 if (rmd)
7268 {
7269 const ArgumentList &rmdAl = rmd->argumentList();
7270 // check for matching argument lists
7271 if (
7272 matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmdAl,
7273 cd,fd,root->type,&root->argList,
7274 TRUE,root->lang)
7275 )
7276 {
7277 found=TRUE;
7278 rmd_found = rmd;
7279 break;
7280 }
7281 }
7282 }
7283 if (rmd_found) // member found -> copy line number info
7284 {
7285 mmd->setBodySegment(rmd_found->getDefLine(),rmd_found->getStartBodyLine(),rmd_found->getEndBodyLine());
7286 mmd->setBodyDef(rmd_found->getBodyDef());
7287 //md->setBodyMember(rmd);
7288 }
7289 }
7290 }
7291 if (!found) // line number could not be found or is available in this
7292 // entry
7293 {
7294 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7295 mmd->setBodyDef(fd);
7296 }
7297
7298 //if (root->mGrpId!=-1)
7299 //{
7300 // md->setMemberGroup(memberGroupDict[root->mGrpId]);
7301 //}
7302 mmd->setMemberClass(cd);
7303 mmd->setMemberSpecifiers(spec);
7304 mmd->setVhdlSpecifiers(root->vhdlSpec);
7305 mmd->setDefinition(funcDecl);
7307 mmd->addQualifiers(root->qualifiers);
7308 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7309 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7310 mmd->setDocsForDefinition(!root->proto);
7311 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
7312 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7313 mmd->addSectionsToDefinition(root->anchors);
7314 mmd->setMemberGroupId(root->mGrpId);
7315 mmd->setLanguage(root->lang);
7316 mmd->setId(root->id);
7317 //md->setMemberDefTemplateArguments(root->mtArgList);
7318 cd->insertMember(md.get());
7319 cd->insertUsedFile(fd);
7320 mmd->setRefItems(root->sli);
7321 mmd->setRequirementReferences(root->rqli);
7322 if (root->relatesType==RelatesType::Duplicate) mmd->setRelatedAlso(cd);
7323 addMemberToGroups(root,md.get());
7325 //printf("Adding member=%s\n",qPrint(md->name()));
7326 mn->push_back(std::move(md));
7327 }
7328 if (root->relatesType==RelatesType::Duplicate)
7329 {
7330 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
7331 {
7332 QCString fullFuncDecl=funcDecl;
7333 if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
7334 warn(root->fileName,root->startLine,
7335 "Cannot determine file/namespace for relatedalso function\n{}",
7336 fullFuncDecl
7337 );
7338 }
7339 }
7340 }
7341 }
7342 else
7343 {
7344 warn_undoc(root->fileName,root->startLine, "class '{}' for related function '{}' is not documented.", className,funcName);
7345 }
7346 }
7347 else if (root->parent() && root->parent()->section.isObjcImpl())
7348 {
7349 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
7350 }
7351 else // unrelated not overloaded member found
7352 {
7353 bool globMem = findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec);
7354 if (className.isEmpty() && !globMem)
7355 {
7356 warn(root->fileName,root->startLine, "class for member '{}' cannot be found.", funcName);
7357 }
7358 else if (!className.isEmpty() && !globMem)
7359 {
7360 warn(root->fileName,root->startLine,
7361 "member '{}' of class '{}' cannot be found",
7362 funcName,className);
7363 }
7364 }
7365 }
7366 else
7367 {
7368 // this should not be called
7369 warn(root->fileName,root->startLine,"member with no name found.");
7370 }
7371 return;
7372}
7373
7374//----------------------------------------------------------------------
7375// find the members corresponding to the different documentation blocks
7376// that are extracted from the sources.
7377
7378static void filterMemberDocumentation(const Entry *root,const QCString &relates)
7379{
7380 AUTO_TRACE("root->type='{}' root->inside='{}' root->name='{}' root->args='{}' section={} root->spec={} root->mGrpId={}",
7381 root->type,root->inside,root->name,root->args,root->section,root->spec,root->mGrpId);
7382 //printf("root->parent()->name=%s\n",qPrint(root->parent()->name));
7383 bool isFunc=TRUE;
7384
7385 QCString type = root->type;
7386 QCString args = root->args;
7387 int i=-1, l=0;
7388 if ( // detect func variable/typedef to func ptr
7389 (i=findFunctionPtr(type.str(),root->lang,&l))!=-1
7390 )
7391 {
7392 //printf("Fixing function pointer!\n");
7393 // fix type and argument
7394 args.prepend(type.right(type.length()-i-l));
7395 type=type.left(i+l);
7396 //printf("Results type=%s,name=%s,args=%s\n",qPrint(type),qPrint(root->name),qPrint(args));
7397 isFunc=FALSE;
7398 }
7399 else if ((type.startsWith("typedef ") && args.find('(')!=-1))
7400 // detect function types marked as functions
7401 {
7402 isFunc=FALSE;
7403 }
7404
7405 //printf("Member %s isFunc=%d\n",qPrint(root->name),isFunc);
7406 if (root->section.isMemberDoc())
7407 {
7408 //printf("Documentation for inline member '%s' found args='%s'\n",
7409 // qPrint(root->name),qPrint(args));
7410 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
7411 if (type.isEmpty())
7412 {
7413 findMember(root,
7414 relates,
7415 type,
7416 args,
7417 root->name + args + root->exception,
7418 FALSE,
7419 isFunc);
7420 }
7421 else
7422 {
7423 findMember(root,
7424 relates,
7425 type,
7426 args,
7427 type + " " + root->name + args + root->exception,
7428 FALSE,
7429 isFunc);
7430 }
7431 }
7432 else if (root->section.isOverloadDoc())
7433 {
7434 //printf("Overloaded member %s found\n",qPrint(root->name));
7435 findMember(root,
7436 relates,
7437 type,
7438 args,
7439 root->name,
7440 TRUE,
7441 isFunc);
7442 }
7443 else if
7444 ((root->section.isFunction() // function
7445 ||
7446 (root->section.isVariable() && // variable
7447 !type.isEmpty() && // with a type
7448 g_compoundKeywords.find(type.str())==g_compoundKeywords.end() // that is not a keyword
7449 // (to skip forward declaration of class etc.)
7450 )
7451 )
7452 )
7453 {
7454 //printf("Documentation for member '%s' found args='%s' excp='%s'\n",
7455 // qPrint(root->name),qPrint(args),qPrint(root->exception));
7456 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
7457 //printf("Inside=%s\n Relates=%s\n",qPrint(root->inside),qPrint(relates));
7458 if (type=="friend class" || type=="friend struct" ||
7459 type=="friend union")
7460 {
7461 findMember(root,
7462 relates,
7463 type,
7464 args,
7465 type+" "+root->name,
7466 FALSE,FALSE);
7467
7468 }
7469 else if (!type.isEmpty())
7470 {
7471 findMember(root,
7472 relates,
7473 type,
7474 args,
7475 type+" "+ root->inside + root->name + args + root->exception,
7476 FALSE,isFunc);
7477 }
7478 else
7479 {
7480 findMember(root,
7481 relates,
7482 type,
7483 args,
7484 root->inside + root->name + args + root->exception,
7485 FALSE,isFunc);
7486 }
7487 }
7488 else if (root->section.isDefine() && !relates.isEmpty())
7489 {
7490 findMember(root,
7491 relates,
7492 type,
7493 args,
7494 root->name + args,
7495 FALSE,
7496 !args.isEmpty());
7497 }
7498 else if (root->section.isVariableDoc())
7499 {
7500 //printf("Documentation for variable %s found\n",qPrint(root->name));
7501 //if (!relates.isEmpty()) printf(" Relates %s\n",qPrint(relates));
7502 findMember(root,
7503 relates,
7504 type,
7505 args,
7506 root->name,
7507 FALSE,
7508 FALSE);
7509 }
7510 else if (root->section.isExportedInterface() ||
7511 root->section.isIncludedService())
7512 {
7513 findMember(root,
7514 relates,
7515 type,
7516 args,
7517 type + " " + root->name,
7518 FALSE,
7519 FALSE);
7520 }
7521 else
7522 {
7523 // skip section
7524 //printf("skip section\n");
7525 }
7526}
7527
7528static void findMemberDocumentation(const Entry *root)
7529{
7530 if (root->section.isMemberDoc() ||
7531 root->section.isOverloadDoc() ||
7532 root->section.isFunction() ||
7533 root->section.isVariable() ||
7534 root->section.isVariableDoc() ||
7535 root->section.isDefine() ||
7536 root->section.isIncludedService() ||
7537 root->section.isExportedInterface()
7538 )
7539 {
7540 AUTO_TRACE();
7541 if (root->relatesType==RelatesType::Duplicate && !root->relates.isEmpty())
7542 {
7544 }
7546 }
7547 for (const auto &e : root->children())
7548 {
7549 if (!e->section.isEnum())
7550 {
7551 findMemberDocumentation(e.get());
7552 }
7553 }
7554}
7555
7556//----------------------------------------------------------------------
7557
7558static void findObjCMethodDefinitions(const Entry *root)
7559{
7560 AUTO_TRACE();
7561 for (const auto &objCImpl : root->children())
7562 {
7563 if (objCImpl->section.isObjcImpl())
7564 {
7565 for (const auto &objCMethod : objCImpl->children())
7566 {
7567 if (objCMethod->section.isFunction())
7568 {
7569 //printf(" Found ObjC method definition %s\n",qPrint(objCMethod->name));
7570 findMember(objCMethod.get(),
7571 objCMethod->relates,
7572 objCMethod->type,
7573 objCMethod->args,
7574 objCMethod->type+" "+objCImpl->name+"::"+objCMethod->name+" "+objCMethod->args,
7575 FALSE,TRUE);
7576 objCMethod->section=EntryType::makeEmpty();
7577 }
7578 }
7579 }
7580 }
7581}
7582
7583//----------------------------------------------------------------------
7584// find and add the enumeration to their classes, namespaces or files
7585
7586static void findEnums(const Entry *root)
7587{
7588 if (root->section.isEnum())
7589 {
7590 AUTO_TRACE("name={}",root->name);
7591 ClassDefMutable *cd = nullptr;
7592 FileDef *fd = nullptr;
7593 NamespaceDefMutable *nd = nullptr;
7594 MemberNameLinkedMap *mnsd = nullptr;
7595 bool isGlobal = false;
7596 bool isRelated = false;
7597 bool isMemberOf = false;
7598 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7599
7600 QCString name;
7601 QCString scope;
7602
7603 int i = root->name.findRev("::");
7604 if (i!=-1) // scope is specified
7605 {
7606 scope=root->name.left(i); // extract scope
7607 if (root->lang==SrcLangExt::CSharp)
7608 {
7609 scope = mangleCSharpGenericName(scope);
7610 }
7611 name=root->name.right(root->name.length()-i-2); // extract name
7612 if ((cd=getClassMutable(scope))==nullptr)
7613 {
7615 }
7616 }
7617 else // no scope, check the scope in which the docs where found
7618 {
7619 if (root->parent()->section.isScope() && !root->parent()->name.isEmpty()) // found enum docs inside a compound
7620 {
7621 scope=root->parent()->name;
7622 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7623 }
7624 name=root->name;
7625 }
7626
7627 if (!root->relates.isEmpty())
7628 { // related member, prefix user specified scope
7629 isRelated=TRUE;
7630 isMemberOf=(root->relatesType==RelatesType::MemberOf);
7631 if (getClass(root->relates)==nullptr && !scope.isEmpty())
7632 scope=mergeScopes(scope,root->relates);
7633 else
7634 scope=root->relates;
7635 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7636 }
7637
7638 if (cd && !name.isEmpty()) // found a enum inside a compound
7639 {
7640 //printf("Enum '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7641 fd=nullptr;
7643 isGlobal=false;
7644 }
7645 else if (nd) // found enum inside namespace
7646 {
7648 isGlobal=true;
7649 }
7650 else // found a global enum
7651 {
7652 fd=root->fileDef();
7654 isGlobal=true;
7655 }
7656
7657 if (!name.isEmpty())
7658 {
7659 // new enum type
7660 AUTO_TRACE_ADD("new enum {} at line {} of {}",name,root->bodyLine,root->fileName);
7661 auto md = createMemberDef(
7662 root->fileName,root->startLine,root->startColumn,
7663 QCString(),name,QCString(),QCString(),
7664 root->protection,Specifier::Normal,FALSE,
7665 isMemberOf ? Relationship::Foreign : isRelated ? Relationship::Related : Relationship::Member,
7668 auto mmd = toMemberDefMutable(md.get());
7669 mmd->setTagInfo(root->tagInfo());
7670 mmd->setLanguage(root->lang);
7671 mmd->setId(root->id);
7672 if (!isGlobal) mmd->setMemberClass(cd); else mmd->setFileDef(fd);
7673 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7674 mmd->setBodyDef(root->fileDef());
7675 mmd->setMemberSpecifiers(root->spec);
7676 mmd->setVhdlSpecifiers(root->vhdlSpec);
7677 mmd->setEnumBaseType(root->args);
7678 //printf("Enum %s definition at line %d of %s: protection=%d scope=%s\n",
7679 // qPrint(root->name),root->bodyLine,qPrint(root->fileName),root->protection,cd?qPrint(cd->name()):"<none>");
7680 mmd->addSectionsToDefinition(root->anchors);
7681 mmd->setMemberGroupId(root->mGrpId);
7683 mmd->addQualifiers(root->qualifiers);
7684 //printf("%s::setRefItems(%zu)\n",qPrint(md->name()),root->sli.size());
7685 mmd->setRefItems(root->sli);
7686 mmd->setRequirementReferences(root->rqli);
7687 //printf("found enum %s nd=%p\n",qPrint(md->name()),nd);
7688 bool defSet=FALSE;
7689
7690 QCString baseType = root->args;
7691 if (!baseType.isEmpty())
7692 {
7693 baseType.prepend(" : ");
7694 }
7695
7696 if (nd)
7697 {
7698 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python)
7699 {
7700 mmd->setDefinition(name+baseType);
7701 }
7702 else
7703 {
7704 mmd->setDefinition(nd->name()+"::"+name+baseType);
7705 }
7706 //printf("definition=%s\n",md->definition());
7707 defSet=TRUE;
7708 mmd->setNamespace(nd);
7709 nd->insertMember(md.get());
7710 }
7711
7712 // even if we have already added the enum to a namespace, we still
7713 // also want to add it to other appropriate places such as file
7714 // or class.
7715 if (isGlobal && (nd==nullptr || !nd->isAnonymous()))
7716 {
7717 if (!defSet) mmd->setDefinition(name+baseType);
7718 if (fd==nullptr && root->parent())
7719 {
7720 fd=root->parent()->fileDef();
7721 }
7722 if (fd)
7723 {
7724 mmd->setFileDef(fd);
7725 fd->insertMember(md.get());
7726 }
7727 }
7728 else if (cd)
7729 {
7730 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES) || root->lang==SrcLangExt::Python)
7731 {
7732 mmd->setDefinition(name+baseType);
7733 }
7734 else
7735 {
7736 mmd->setDefinition(cd->name()+"::"+name+baseType);
7737 }
7738 cd->insertMember(md.get());
7739 cd->insertUsedFile(fd);
7740 }
7741 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7742 mmd->setDocsForDefinition(!root->proto);
7743 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7744 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7745
7746 //printf("Adding member=%s\n",qPrint(md->name()));
7747 addMemberToGroups(root,md.get());
7749
7750 MemberName *mn = mnsd->add(name);
7751 mn->push_back(std::move(md));
7752 }
7753 }
7754 else
7755 {
7756 for (const auto &e : root->children()) findEnums(e.get());
7757 }
7758}
7759
7760//----------------------------------------------------------------------
7761
7762static void addEnumValuesToEnums(const Entry *root)
7763{
7764 if (root->section.isEnum())
7765 // non anonymous enumeration
7766 {
7767 AUTO_TRACE("name={}",root->name);
7768 ClassDefMutable *cd = nullptr;
7769 FileDef *fd = nullptr;
7770 NamespaceDefMutable *nd = nullptr;
7771 MemberNameLinkedMap *mnsd = nullptr;
7772 bool isGlobal = false;
7773 bool isRelated = false;
7774 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7775
7776 QCString name;
7777 QCString scope;
7778
7779 int i = root->name.findRev("::");
7780 if (i!=-1) // scope is specified
7781 {
7782 scope=root->name.left(i); // extract scope
7783 if (root->lang==SrcLangExt::CSharp)
7784 {
7785 scope = mangleCSharpGenericName(scope);
7786 }
7787 name=root->name.right(root->name.length()-i-2); // extract name
7788 if ((cd=getClassMutable(scope))==nullptr)
7789 {
7791 }
7792 }
7793 else // no scope, check the scope in which the docs where found
7794 {
7795 if (root->parent()->section.isScope() && !root->parent()->name.isEmpty()) // found enum docs inside a compound
7796 {
7797 scope=root->parent()->name;
7798 if (root->lang==SrcLangExt::CSharp)
7799 {
7800 scope = mangleCSharpGenericName(scope);
7801 }
7802 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7803 }
7804 name=root->name;
7805 }
7806
7807 if (!root->relates.isEmpty())
7808 { // related member, prefix user specified scope
7809 isRelated=TRUE;
7810 if (getClassMutable(root->relates)==nullptr && !scope.isEmpty())
7811 scope=mergeScopes(scope,root->relates);
7812 else
7813 scope=root->relates;
7814 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7815 }
7816
7817 if (cd && !name.isEmpty()) // found a enum inside a compound
7818 {
7819 //printf("Enum in class '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7820 fd=nullptr;
7822 isGlobal=false;
7823 }
7824 else if (nd && !nd->isAnonymous()) // found enum inside namespace
7825 {
7826 //printf("Enum in namespace '%s'::'%s'\n",qPrint(nd->name()),qPrint(name));
7828 isGlobal=true;
7829 }
7830 else // found a global enum
7831 {
7832 fd=root->fileDef();
7833 //printf("Enum in file '%s': '%s'\n",qPrint(fd->name()),qPrint(name));
7835 isGlobal=true;
7836 }
7837
7838 if (!name.isEmpty())
7839 {
7840 //printf("** name=%s\n",qPrint(name));
7841 MemberName *mn = mnsd->find(name); // for all members with this name
7842 if (mn)
7843 {
7844 struct EnumValueInfo
7845 {
7846 EnumValueInfo(const QCString &n,std::unique_ptr<MemberDef> &&md) :
7847 name(n), member(std::move(md)) {}
7848 QCString name;
7849 std::unique_ptr<MemberDef> member;
7850 };
7851 std::vector< EnumValueInfo > extraMembers;
7852 // for each enum in this list
7853 for (const auto &imd : *mn)
7854 {
7855 MemberDefMutable *md = toMemberDefMutable(imd.get());
7856 // use raw pointer in this loop, since we modify mn and can then invalidate mdp.
7857 if (md && md->isEnumerate() && !root->children().empty())
7858 {
7859 AUTO_TRACE_ADD("enum {} with {} children",md->name(),root->children().size());
7860 for (const auto &e : root->children())
7861 {
7862 SrcLangExt sle = root->lang;
7863 bool isJavaLike = sle==SrcLangExt::CSharp || sle==SrcLangExt::Java || sle==SrcLangExt::XML;
7864 if ( isJavaLike || root->spec.isStrong())
7865 {
7866 if (sle == SrcLangExt::Cpp && e->section.isDefine()) continue;
7867 // Unlike classic C/C++ enums, for C++11, C# & Java enum
7868 // values are only visible inside the enum scope, so we must create
7869 // them here and only add them to the enum
7870 //printf("md->qualifiedName()=%s e->name=%s tagInfo=%p name=%s\n",
7871 // qPrint(md->qualifiedName()),qPrint(e->name),(void*)e->tagInfo(),qPrint(e->name));
7872 QCString qualifiedName = root->name;
7873 i = qualifiedName.findRev("::");
7874 if (i!=-1 && sle==SrcLangExt::CSharp)
7875 {
7876 qualifiedName = mangleCSharpGenericName(qualifiedName.left(i))+qualifiedName.mid(i);
7877 }
7878 if (isJavaLike)
7879 {
7880 qualifiedName=substitute(qualifiedName,"::",".");
7881 }
7882 if (md->qualifiedName()==qualifiedName) // enum value scope matches that of the enum
7883 {
7884 QCString fileName = e->fileName;
7885 if (fileName.isEmpty() && e->tagInfo())
7886 {
7887 fileName = e->tagInfo()->tagName;
7888 }
7889 AUTO_TRACE_ADD("strong enum value {}",e->name);
7890 auto fmd = createMemberDef(
7891 fileName,e->startLine,e->startColumn,
7892 e->type,e->name,e->args,QCString(),
7893 e->protection, Specifier::Normal,e->isStatic,Relationship::Member,
7895 auto fmmd = toMemberDefMutable(fmd.get());
7896 NamespaceDef *mnd = md->getNamespaceDef();
7897 if (md->getClassDef())
7898 fmmd->setMemberClass(md->getClassDef());
7899 else if (mnd && (mnd->isLinkable() || mnd->isAnonymous()))
7900 fmmd->setNamespace(mnd);
7901 else if (md->getFileDef())
7902 fmmd->setFileDef(md->getFileDef());
7903 fmmd->setOuterScope(md->getOuterScope());
7904 fmmd->setTagInfo(e->tagInfo());
7905 fmmd->setLanguage(e->lang);
7906 fmmd->setBodySegment(e->startLine,e->bodyLine,e->endBodyLine);
7907 fmmd->setBodyDef(e->fileDef());
7908 fmmd->setId(e->id);
7909 fmmd->setDocumentation(e->doc,e->docFile,e->docLine);
7910 fmmd->setBriefDescription(e->brief,e->briefFile,e->briefLine);
7911 fmmd->addSectionsToDefinition(e->anchors);
7912 fmmd->setInitializer(e->initializer.str());
7913 fmmd->setMaxInitLines(e->initLines);
7914 fmmd->setMemberGroupId(e->mGrpId);
7915 fmmd->setExplicitExternal(e->explicitExternal,fileName,e->startLine,e->startColumn);
7916 fmmd->setRefItems(e->sli);
7917 fmmd->setRequirementReferences(e->rqli);
7918 fmmd->setAnchor();
7919 md->insertEnumField(fmd.get());
7920 fmmd->setEnumScope(md,TRUE);
7921 extraMembers.emplace_back(e->name,std::move(fmd));
7922 }
7923 }
7924 else
7925 {
7926 AUTO_TRACE_ADD("enum value {}",e->name);
7927 //printf("e->name=%s isRelated=%d\n",qPrint(e->name),isRelated);
7928 MemberName *fmn=nullptr;
7929 MemberNameLinkedMap *emnsd = isRelated ? Doxygen::functionNameLinkedMap : mnsd;
7930 if (!e->name.isEmpty() && (fmn=emnsd->find(e->name)))
7931 // get list of members with the same name as the field
7932 {
7933 for (const auto &ifmd : *fmn)
7934 {
7935 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
7936 if (fmd && fmd->isEnumValue() && fmd->getOuterScope()==md->getOuterScope()) // in same scope
7937 {
7938 //printf("found enum value with same name %s in scope %s\n",
7939 // qPrint(fmd->name()),qPrint(fmd->getOuterScope()->name()));
7940 if (nd && !nd->isAnonymous())
7941 {
7942 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
7943 {
7944 const NamespaceDef *fnd=fmd->getNamespaceDef();
7945 if (fnd==nd) // enum value is inside a namespace
7946 {
7947 md->insertEnumField(fmd);
7948 fmd->setEnumScope(md);
7949 }
7950 }
7951 }
7952 else if (isGlobal)
7953 {
7954 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
7955 {
7956 const FileDef *ffd=fmd->getFileDef();
7957 if (ffd==fd && ffd==md->getFileDef()) // enum value has file scope
7958 {
7959 md->insertEnumField(fmd);
7960 fmd->setEnumScope(md);
7961 }
7962 }
7963 }
7964 else if (isRelated && cd) // reparent enum value to
7965 // match the enum's scope
7966 {
7967 md->insertEnumField(fmd); // add field def to list
7968 fmd->setEnumScope(md); // cross ref with enum name
7969 fmd->setEnumClassScope(cd); // cross ref with enum name
7970 fmd->setOuterScope(cd);
7971 fmd->makeRelated();
7972 cd->insertMember(fmd);
7973 }
7974 else
7975 {
7976 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
7977 {
7978 const ClassDef *fcd=fmd->getClassDef();
7979 if (fcd==cd) // enum value is inside a class
7980 {
7981 //printf("Inserting enum field %s in enum scope %s\n",
7982 // qPrint(fmd->name()),qPrint(md->name()));
7983 md->insertEnumField(fmd); // add field def to list
7984 fmd->setEnumScope(md); // cross ref with enum name
7985 }
7986 }
7987 }
7988 }
7989 }
7990 }
7991 }
7992 }
7993 }
7994 }
7995 // move the newly added members into mn
7996 for (auto &e : extraMembers)
7997 {
7998 MemberName *emn=mnsd->add(e.name);
7999 emn->push_back(std::move(e.member));
8000 }
8001 }
8002 }
8003 }
8004 else
8005 {
8006 for (const auto &e : root->children()) addEnumValuesToEnums(e.get());
8007 }
8008}
8009
8010//----------------------------------------------------------------------
8011
8012static void addEnumDocs(const Entry *root,MemberDefMutable *md)
8013{
8014 AUTO_TRACE();
8015 // documentation outside a compound overrides the documentation inside it
8016 {
8017 md->setDocumentation(root->doc,root->docFile,root->docLine);
8018 md->setDocsForDefinition(!root->proto);
8019 }
8020
8021 // brief descriptions inside a compound override the documentation
8022 // outside it
8023 {
8024 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
8025 }
8026
8027 if (md->inbodyDocumentation().isEmpty() || !root->parent()->name.isEmpty())
8028 {
8030 }
8031
8032 if (root->mGrpId!=-1 && md->getMemberGroupId()==-1)
8033 {
8034 md->setMemberGroupId(root->mGrpId);
8035 }
8036
8038 md->setRefItems(root->sli);
8039 md->setRequirementReferences(root->rqli);
8040
8041 const GroupDef *gd=md->getGroupDef();
8042 if (gd==nullptr && !root->groups.empty()) // member not grouped but out-of-line documentation is
8043 {
8044 addMemberToGroups(root,md);
8045 }
8047}
8048
8049//----------------------------------------------------------------------
8050// Search for the name in the associated groups. If a matching member
8051// definition exists, then add the documentation to it and return TRUE,
8052// otherwise FALSE.
8053
8054static bool tryAddEnumDocsToGroupMember(const Entry *root,const QCString &name)
8055{
8056 for (const auto &g : root->groups)
8057 {
8058 const GroupDef *gd = Doxygen::groupLinkedMap->find(g.groupname);
8059 if (gd)
8060 {
8061 MemberList *ml = gd->getMemberList(MemberListType::DecEnumMembers());
8062 if (ml)
8063 {
8064 MemberDefMutable *md = toMemberDefMutable(ml->find(name));
8065 if (md)
8066 {
8067 addEnumDocs(root,md);
8068 return TRUE;
8069 }
8070 }
8071 }
8072 else if (!gd && g.pri == Grouping::GROUPING_INGROUP)
8073 {
8074 warn(root->fileName, root->startLine,
8075 "Found non-existing group '{}' for the command '{}', ignoring command",
8076 g.groupname, Grouping::getGroupPriName( g.pri )
8077 );
8078 }
8079 }
8080
8081 return FALSE;
8082}
8083
8084//----------------------------------------------------------------------
8085// find the documentation blocks for the enumerations
8086
8087static void findEnumDocumentation(const Entry *root)
8088{
8089 if (root->section.isEnumDoc() &&
8090 !root->name.isEmpty() &&
8091 root->name.at(0)!='@' // skip anonymous enums
8092 )
8093 {
8094 QCString name;
8095 QCString scope;
8096 int i = root->name.findRev("::");
8097 if (i!=-1) // scope is specified as part of the name
8098 {
8099 name=root->name.right(root->name.length()-i-2); // extract name
8100 scope=root->name.left(i); // extract scope
8101 //printf("Scope='%s' Name='%s'\n",qPrint(scope),qPrint(name));
8102 }
8103 else // just the name
8104 {
8105 name=root->name;
8106 }
8107 if (root->parent()->section.isScope() && !root->parent()->name.isEmpty()) // found enum docs inside a compound
8108 {
8109 if (!scope.isEmpty()) scope.prepend("::");
8110 scope.prepend(root->parent()->name);
8111 }
8112 const ClassDef *cd = getClass(scope);
8113 const NamespaceDef *nd=Doxygen::namespaceLinkedMap->find(scope);
8114 const FileDef *fd = root->fileDef();
8115 AUTO_TRACE("Found docs for enum with name '{}' and scope '{}' in context '{}' cd='{}', nd='{}' fd='{}'",
8116 name,scope,root->parent()->name,
8117 cd ? cd->name() : QCString("<none>"),
8118 nd ? nd->name() : QCString("<none>"),
8119 fd ? fd->name() : QCString("<none>"));
8120
8121 if (!name.isEmpty())
8122 {
8123 bool found = tryAddEnumDocsToGroupMember(root, name);
8124 if (!found)
8125 {
8126 MemberName *mn = cd ? Doxygen::memberNameLinkedMap->find(name) : Doxygen::functionNameLinkedMap->find(name);
8127 if (mn)
8128 {
8129 for (const auto &imd : *mn)
8130 {
8131 MemberDefMutable *md = toMemberDefMutable(imd.get());
8132 if (md && md->isEnumerate())
8133 {
8134 const ClassDef *mcd = md->getClassDef();
8135 const NamespaceDef *mnd = md->getNamespaceDef();
8136 const FileDef *mfd = md->getFileDef();
8137 if (cd && mcd==cd)
8138 {
8139 AUTO_TRACE_ADD("Match found for class scope");
8140 addEnumDocs(root,md);
8141 found = TRUE;
8142 break;
8143 }
8144 else if (cd==nullptr && mcd==nullptr && nd!=nullptr && mnd==nd)
8145 {
8146 AUTO_TRACE_ADD("Match found for namespace scope");
8147 addEnumDocs(root,md);
8148 found = TRUE;
8149 break;
8150 }
8151 else if (cd==nullptr && nd==nullptr && mcd==nullptr && mnd==nullptr && fd==mfd)
8152 {
8153 AUTO_TRACE_ADD("Match found for global scope");
8154 addEnumDocs(root,md);
8155 found = TRUE;
8156 break;
8157 }
8158 }
8159 }
8160 }
8161 }
8162 if (!found)
8163 {
8164 warn(root->fileName,root->startLine, "Documentation for undefined enum '{}' found.", name);
8165 }
8166 }
8167 }
8168 for (const auto &e : root->children()) findEnumDocumentation(e.get());
8169}
8170
8171// search for each enum (member or function) in mnl if it has documented
8172// enum values.
8173static void findDEV(const MemberNameLinkedMap &mnsd)
8174{
8175 // for each member name
8176 for (const auto &mn : mnsd)
8177 {
8178 // for each member definition
8179 for (const auto &imd : *mn)
8180 {
8181 MemberDefMutable *md = toMemberDefMutable(imd.get());
8182 if (md && md->isEnumerate()) // member is an enum
8183 {
8184 int documentedEnumValues=0;
8185 // for each enum value
8186 for (const auto &fmd : md->enumFieldList())
8187 {
8188 if (fmd->isLinkableInProject()) documentedEnumValues++;
8189 }
8190 // at least one enum value is documented
8191 if (documentedEnumValues>0) md->setDocumentedEnumValues(TRUE);
8192 }
8193 }
8194 }
8195}
8196
8197// search for each enum (member or function) if it has documented enum
8198// values.
8204
8205//----------------------------------------------------------------------
8206
8208{
8209 auto &index = Index::instance();
8210 // for each class member name
8211 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8212 {
8213 // for each member definition
8214 for (const auto &md : *mn)
8215 {
8216 index.addClassMemberNameToIndex(md.get());
8217 if (md->getModuleDef())
8218 {
8219 index.addModuleMemberNameToIndex(md.get());
8220 }
8221 }
8222 }
8223 // for each file/namespace function name
8224 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8225 {
8226 // for each member definition
8227 for (const auto &md : *mn)
8228 {
8229 if (md->getNamespaceDef())
8230 {
8231 index.addNamespaceMemberNameToIndex(md.get());
8232 }
8233 else
8234 {
8235 index.addFileMemberNameToIndex(md.get());
8236 }
8237 if (md->getModuleDef())
8238 {
8239 index.addModuleMemberNameToIndex(md.get());
8240 }
8241 }
8242 }
8243
8244 index.sortMemberIndexLists();
8245}
8246
8247//----------------------------------------------------------------------
8248
8249static void addToIndices()
8250{
8251 for (const auto &cd : *Doxygen::classLinkedMap)
8252 {
8253 if (cd->isLinkableInProject())
8254 {
8255 Doxygen::indexList->addIndexItem(cd.get(),nullptr);
8256 if (Doxygen::searchIndex.enabled())
8257 {
8258 Doxygen::searchIndex.setCurrentDoc(cd.get(),cd->anchor(),FALSE);
8259 Doxygen::searchIndex.addWord(cd->localName(),TRUE);
8260 }
8261 }
8262 }
8263
8264 for (const auto &cd : *Doxygen::conceptLinkedMap)
8265 {
8266 if (cd->isLinkableInProject())
8267 {
8268 Doxygen::indexList->addIndexItem(cd.get(),nullptr);
8269 if (Doxygen::searchIndex.enabled())
8270 {
8271 Doxygen::searchIndex.setCurrentDoc(cd.get(),cd->anchor(),FALSE);
8272 Doxygen::searchIndex.addWord(cd->localName(),TRUE);
8273 }
8274 }
8275 }
8276
8277 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8278 {
8279 if (nd->isLinkableInProject())
8280 {
8281 Doxygen::indexList->addIndexItem(nd.get(),nullptr);
8282 if (Doxygen::searchIndex.enabled())
8283 {
8284 Doxygen::searchIndex.setCurrentDoc(nd.get(),nd->anchor(),FALSE);
8285 Doxygen::searchIndex.addWord(nd->localName(),TRUE);
8286 }
8287 }
8288 }
8289
8290 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8291 {
8292 for (const auto &fd : *fn)
8293 {
8294 if (Doxygen::searchIndex.enabled() && fd->isLinkableInProject())
8295 {
8296 Doxygen::searchIndex.setCurrentDoc(fd.get(),fd->anchor(),FALSE);
8297 Doxygen::searchIndex.addWord(fd->localName(),TRUE);
8298 }
8299 }
8300 }
8301
8302 auto addWordsForTitle = [](const Definition *d,const QCString &anchor,const QCString &title)
8303 {
8304 Doxygen::indexList->addIndexItem(d,nullptr,QCString(),filterTitle(title));
8305 if (Doxygen::searchIndex.enabled())
8306 {
8307 Doxygen::searchIndex.setCurrentDoc(d,anchor,false);
8308 std::string s = title.str();
8309 static const reg::Ex re(R"(\a[\w-]*)");
8310 reg::Iterator it(s,re);
8312 for (; it!=end ; ++it)
8313 {
8314 const auto &match = *it;
8315 std::string matchStr = match.str();
8316 Doxygen::searchIndex.addWord(matchStr,true);
8317 }
8318 }
8319 };
8320
8321 for (const auto &gd : *Doxygen::groupLinkedMap)
8322 {
8323 if (gd->isLinkableInProject())
8324 {
8325 addWordsForTitle(gd.get(),gd->anchor(),gd->groupTitle());
8326 }
8327 }
8328
8329 for (const auto &pd : *Doxygen::pageLinkedMap)
8330 {
8331 if (pd->isLinkableInProject())
8332 {
8333 addWordsForTitle(pd.get(),pd->anchor(),pd->title());
8334 }
8335 }
8336
8338 {
8339 addWordsForTitle(Doxygen::mainPage.get(),Doxygen::mainPage->anchor(),Doxygen::mainPage->title());
8340 }
8341
8342 auto addMemberToSearchIndex = [](const MemberDef *md)
8343 {
8344 if (Doxygen::searchIndex.enabled())
8345 {
8346 Doxygen::searchIndex.setCurrentDoc(md,md->anchor(),FALSE);
8347 QCString ln=md->localName();
8348 QCString qn=md->qualifiedName();
8349 Doxygen::searchIndex.addWord(ln,TRUE);
8350 if (ln!=qn)
8351 {
8352 Doxygen::searchIndex.addWord(qn,TRUE);
8353 if (md->getClassDef())
8354 {
8355 Doxygen::searchIndex.addWord(md->getClassDef()->displayName(),TRUE);
8356 }
8357 if (md->getNamespaceDef())
8358 {
8359 Doxygen::searchIndex.addWord(md->getNamespaceDef()->displayName(),TRUE);
8360 }
8361 }
8362 }
8363 };
8364
8365 auto getScope = [](const MemberDef *md)
8366 {
8367 const Definition *scope = nullptr;
8368 if (md->getGroupDef()) scope = md->getGroupDef();
8369 else if (md->getClassDef()) scope = md->getClassDef();
8370 else if (md->getNamespaceDef()) scope = md->getNamespaceDef();
8371 else if (md->getFileDef()) scope = md->getFileDef();
8372 return scope;
8373 };
8374
8375 auto addMemberToIndices = [addMemberToSearchIndex,getScope](const MemberDef *md)
8376 {
8377 if (md->isLinkableInProject())
8378 {
8379 if (!(md->isEnumerate() && md->isAnonymous()))
8380 {
8381 Doxygen::indexList->addIndexItem(getScope(md),md);
8383 }
8384 if (md->isEnumerate())
8385 {
8386 for (const auto &fmd : md->enumFieldList())
8387 {
8388 Doxygen::indexList->addIndexItem(getScope(fmd),fmd);
8390 }
8391 }
8392 }
8393 };
8394
8395 // for each class member name
8396 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8397 {
8398 // for each member definition
8399 for (const auto &md : *mn)
8400 {
8401 addMemberToIndices(md.get());
8402 }
8403 }
8404 // for each file/namespace function name
8405 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8406 {
8407 // for each member definition
8408 for (const auto &md : *mn)
8409 {
8410 addMemberToIndices(md.get());
8411 }
8412 }
8413}
8414
8415//----------------------------------------------------------------------
8416
8418{
8419 // for each member name
8420 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8421 {
8422 // for each member definition
8423 for (const auto &imd : *mn)
8424 {
8425 MemberDefMutable *md = toMemberDefMutable(imd.get());
8426 if (md)
8427 {
8429 }
8430 }
8431 }
8432 // for each member name
8433 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8434 {
8435 // for each member definition
8436 for (const auto &imd : *mn)
8437 {
8438 MemberDefMutable *md = toMemberDefMutable(imd.get());
8439 if (md)
8440 {
8442 }
8443 }
8444 }
8445}
8446
8447// recursive helper function looking for reimplements/implemented
8448// by relations between class cd and direct or indirect base class bcd
8450{
8451 for (const auto &mn : cd->memberNameInfoLinkedMap()) // for each member in class cd with a unique name
8452 {
8453 for (const auto &imd : *mn) // for each member with a given name
8454 {
8455 MemberDefMutable *md = toMemberDefMutable(imd->memberDef());
8456 if (md && (md->isFunction() || md->isCSharpProperty())) // filter on reimplementable members
8457 {
8458 ClassDef *mbcd = bcd->classDef;
8459 if (mbcd && mbcd->isLinkable()) // filter on linkable classes
8460 {
8461 const auto &bmn = mbcd->memberNameInfoLinkedMap();
8462 const auto &bmni = bmn.find(mn->memberName());
8463 if (bmni) // there are base class members with the same name
8464 {
8465 for (const auto &ibmd : *bmni) // for base class member with that name
8466 {
8467 MemberDefMutable *bmd = toMemberDefMutable(ibmd->memberDef());
8468 if (bmd) // not part of an inline namespace
8469 {
8470 auto lang = bmd->getLanguage();
8471 auto compType = mbcd->compoundType();
8472 if (bmd->virtualness()!=Specifier::Normal ||
8473 lang==SrcLangExt::Python ||
8474 lang==SrcLangExt::Java ||
8475 lang==SrcLangExt::PHP ||
8476 compType==ClassDef::Interface ||
8477 compType==ClassDef::Protocol)
8478 {
8479 const ArgumentList &bmdAl = bmd->argumentList();
8480 const ArgumentList &mdAl = md->argumentList();
8481 //printf(" Base argList='%s'\n Super argList='%s'\n",
8482 // qPrint(argListToString(bmdAl)),
8483 // qPrint(argListToString(mdAl))
8484 // );
8485 if (
8486 lang==SrcLangExt::Python ||
8487 matchArguments2(bmd->getOuterScope(),bmd->getFileDef(),bmd->typeString(),&bmdAl,
8488 md->getOuterScope(), md->getFileDef(), md->typeString(),&mdAl,
8489 TRUE,lang
8490 )
8491 )
8492 {
8493 if (lang==SrcLangExt::Python && md->name().startsWith("__")) continue; // private members do not reimplement
8494 //printf("match!\n");
8495 const MemberDef *rmd = md->reimplements();
8496 if (rmd==nullptr) // not already assigned
8497 {
8498 //printf("%s: setting (new) reimplements member %s\n",qPrint(md->qualifiedName()),qPrint(bmd->qualifiedName()));
8499 md->setReimplements(bmd);
8500 }
8501 //printf("%s: add reimplementedBy member %s\n",qPrint(bmd->qualifiedName()),qPrint(md->qualifiedName()));
8502 bmd->insertReimplementedBy(md);
8503 }
8504 else
8505 {
8506 //printf("no match!\n");
8507 }
8508 }
8509 }
8510 }
8511 }
8512 }
8513 }
8514 }
8515 }
8516
8517 // do also for indirect base classes
8518 for (const auto &bbcd : bcd->classDef->baseClasses())
8519 {
8521 }
8522}
8523
8524//----------------------------------------------------------------------
8525// computes the relation between all members. For each member 'm'
8526// the members that override the implementation of 'm' are searched and
8527// the member that 'm' overrides is searched.
8528
8530{
8531 for (const auto &cd : *Doxygen::classLinkedMap)
8532 {
8533 if (cd->isLinkable())
8534 {
8535 for (const auto &bcd : cd->baseClasses())
8536 {
8538 }
8539 }
8540 }
8541}
8542
8543//----------------------------------------------------------------------------
8544
8546{
8547 // for each class
8548 for (const auto &cd : *Doxygen::classLinkedMap)
8549 {
8550 // that is a template
8551 for (const auto &ti : cd->getTemplateInstances())
8552 {
8553 ClassDefMutable *tcdm = toClassDefMutable(ti.classDef);
8554 if (tcdm)
8555 {
8556 tcdm->addMembersToTemplateInstance(cd.get(),cd->templateArguments(),ti.templSpec);
8557 }
8558 }
8559 }
8560}
8561
8562//----------------------------------------------------------------------------
8563
8564static void mergeCategories()
8565{
8566 AUTO_TRACE();
8567 // merge members of categories into the class they extend
8568 for (const auto &cd : *Doxygen::classLinkedMap)
8569 {
8570 int i=cd->name().find('(');
8571 if (i!=-1) // it is an Objective-C category
8572 {
8573 QCString baseName=cd->name().left(i);
8574 ClassDefMutable *baseClass=toClassDefMutable(Doxygen::classLinkedMap->find(baseName));
8575 if (baseClass)
8576 {
8577 AUTO_TRACE_ADD("merging members of category {} into {}",cd->name(),baseClass->name());
8578 baseClass->mergeCategory(cd.get());
8579 }
8580 }
8581 }
8582}
8583
8584// builds the list of all members for each class
8585
8587{
8588 // merge the member list of base classes into the inherited classes.
8589 for (const auto &cd : *Doxygen::classLinkedMap)
8590 {
8591 if (// !cd->isReference() && // not an external class
8592 cd->subClasses().empty() && // is a root of the hierarchy
8593 !cd->baseClasses().empty()) // and has at least one base class
8594 {
8595 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8596 if (cdm)
8597 {
8598 //printf("*** merging members for %s\n",qPrint(cd->name()));
8599 cdm->mergeMembers();
8600 }
8601 }
8602 }
8603 // now sort the member list of all members for all classes.
8604 for (const auto &cd : *Doxygen::classLinkedMap)
8605 {
8606 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8607 if (cdm)
8608 {
8609 cdm->sortAllMembersList();
8610 }
8611 }
8612}
8613
8614//----------------------------------------------------------------------------
8615
8617{
8618 auto processSourceFile = [](FileDef *fd,OutputList &ol,ClangTUParser *parser)
8619 {
8620 bool showSources = fd->generateSourceFile() && !Htags::useHtags; // sources need to be shown in the output
8621 bool parseSources = !fd->isReference() && Doxygen::parseSourcesNeeded; // we needed to parse the sources even if we do not show them
8622 if (showSources)
8623 {
8624 msg("Generating code for file {}...\n",fd->docName());
8625 fd->writeSourceHeader(ol);
8626 fd->writeSourceBody(ol,parser);
8627 fd->writeSourceFooter(ol);
8628 }
8629 else if (parseSources)
8630 {
8631 msg("Parsing code for file {}...\n",fd->docName());
8632 fd->parseSource(parser);
8633 }
8634 };
8635 if (!Doxygen::inputNameLinkedMap->empty())
8636 {
8637#if USE_LIBCLANG
8639 {
8640 StringUnorderedSet processedFiles;
8641
8642 // create a dictionary with files to process
8643 StringUnorderedSet filesToProcess;
8644
8645 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8646 {
8647 for (const auto &fd : *fn)
8648 {
8649 filesToProcess.insert(fd->absFilePath().str());
8650 }
8651 }
8652 // process source files (and their include dependencies)
8653 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8654 {
8655 for (const auto &fd : *fn)
8656 {
8657 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt::Cpp &&
8658 (fd->generateSourceFile() ||
8660 )
8661 )
8662 {
8663 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8664 clangParser->parse();
8665 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8666
8667 for (auto incFile : clangParser->filesInSameTU())
8668 {
8669 if (filesToProcess.find(incFile)!=filesToProcess.end() && // part of input
8670 fd->absFilePath()!=incFile && // not same file
8671 processedFiles.find(incFile)==processedFiles.end()) // not yet marked as processed
8672 {
8673 StringVector moreFiles;
8674 bool ambig = false;
8676 if (ifd && !ifd->isReference())
8677 {
8678 processSourceFile(ifd,*g_outputList,clangParser.get());
8679 processedFiles.insert(incFile);
8680 }
8681 }
8682 }
8683 processedFiles.insert(fd->absFilePath().str());
8684 }
8685 }
8686 }
8687 // process remaining files
8688 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8689 {
8690 for (const auto &fd : *fn)
8691 {
8692 if (processedFiles.find(fd->absFilePath().str())==processedFiles.end()) // not yet processed
8693 {
8694 if (fd->getLanguage()==SrcLangExt::Cpp) // C/C++ file, use clang parser
8695 {
8696 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8697 clangParser->parse();
8698 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8699 }
8700 else // non C/C++ file, use built-in parser
8701 {
8702 processSourceFile(fd.get(),*g_outputList,nullptr);
8703 }
8704 }
8705 }
8706 }
8707 }
8708 else
8709#endif
8710 {
8711 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8712 if (numThreads>1)
8713 {
8714 msg("Generating code files using {} threads.\n",numThreads);
8715 struct SourceContext
8716 {
8717 SourceContext(FileDef *fd_,bool gen_,const OutputList &ol_)
8718 : fd(fd_), generateSourceFile(gen_), ol(ol_) {}
8719 FileDef *fd;
8720 bool generateSourceFile;
8721 OutputList ol;
8722 };
8723 ThreadPool threadPool(numThreads);
8724 std::vector< std::future< std::shared_ptr<SourceContext> > > results;
8725 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8726 {
8727 for (const auto &fd : *fn)
8728 {
8729 bool generateSourceFile = fd->generateSourceFile() && !Htags::useHtags;
8730 auto ctx = std::make_shared<SourceContext>(fd.get(),generateSourceFile,*g_outputList);
8731 auto processFile = [ctx]()
8732 {
8733 if (ctx->generateSourceFile)
8734 {
8735 msg("Generating code for file {}...\n",ctx->fd->docName());
8736 }
8737 else
8738 {
8739 msg("Parsing code for file {}...\n",ctx->fd->docName());
8740 }
8741 StringVector filesInSameTu;
8742 ctx->fd->getAllIncludeFilesRecursively(filesInSameTu);
8743 if (ctx->generateSourceFile) // sources need to be shown in the output
8744 {
8745 ctx->fd->writeSourceHeader(ctx->ol);
8746 ctx->fd->writeSourceBody(ctx->ol,nullptr);
8747 ctx->fd->writeSourceFooter(ctx->ol);
8748 }
8749 else if (!ctx->fd->isReference() && Doxygen::parseSourcesNeeded)
8750 // we needed to parse the sources even if we do not show them
8751 {
8752 ctx->fd->parseSource(nullptr);
8753 }
8754 return ctx;
8755 };
8756 results.emplace_back(threadPool.queue(processFile));
8757 }
8758 }
8759 for (auto &f : results)
8760 {
8761 auto ctx = f.get();
8762 }
8763 }
8764 else // single threaded version
8765 {
8766 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8767 {
8768 for (const auto &fd : *fn)
8769 {
8770 StringVector filesInSameTu;
8771 fd->getAllIncludeFilesRecursively(filesInSameTu);
8772 processSourceFile(fd.get(),*g_outputList,nullptr);
8773 }
8774 }
8775 }
8776 }
8777 }
8778}
8779
8780//----------------------------------------------------------------------------
8781
8782static void generateFileDocs()
8783{
8784 if (Index::instance().numDocumentedFiles()==0) return;
8785
8786 if (!Doxygen::inputNameLinkedMap->empty())
8787 {
8788 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8789 if (numThreads>1) // multi threaded processing
8790 {
8791 struct DocContext
8792 {
8793 DocContext(FileDef *fd_,const OutputList &ol_)
8794 : fd(fd_), ol(ol_) {}
8795 FileDef *fd;
8796 OutputList ol;
8797 };
8798 ThreadPool threadPool(numThreads);
8799 std::vector< std::future< std::shared_ptr<DocContext> > > results;
8800 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8801 {
8802 for (const auto &fd : *fn)
8803 {
8804 bool doc = fd->isLinkableInProject();
8805 if (doc)
8806 {
8807 auto ctx = std::make_shared<DocContext>(fd.get(),*g_outputList);
8808 auto processFile = [ctx]() {
8809 msg("Generating docs for file {}...\n",ctx->fd->docName());
8810 ctx->fd->writeDocumentation(ctx->ol);
8811 return ctx;
8812 };
8813 results.emplace_back(threadPool.queue(processFile));
8814 }
8815 }
8816 }
8817 for (auto &f : results)
8818 {
8819 auto ctx = f.get();
8820 }
8821 }
8822 else // single threaded processing
8823 {
8824 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8825 {
8826 for (const auto &fd : *fn)
8827 {
8828 bool doc = fd->isLinkableInProject();
8829 if (doc)
8830 {
8831 msg("Generating docs for file {}...\n",fd->docName());
8832 fd->writeDocumentation(*g_outputList);
8833 }
8834 }
8835 }
8836 }
8837 }
8838}
8839
8840//----------------------------------------------------------------------------
8841
8843{
8844 // add source references for class definitions
8845 for (const auto &cd : *Doxygen::classLinkedMap)
8846 {
8847 const FileDef *fd=cd->getBodyDef();
8848 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8849 {
8850 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),nullptr);
8851 }
8852 }
8853 // add source references for concept definitions
8854 for (const auto &cd : *Doxygen::conceptLinkedMap)
8855 {
8856 const FileDef *fd=cd->getBodyDef();
8857 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8858 {
8859 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),nullptr);
8860 }
8861 }
8862 // add source references for namespace definitions
8863 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8864 {
8865 const FileDef *fd=nd->getBodyDef();
8866 if (fd && nd->isLinkableInProject() && nd->getStartDefLine()!=-1)
8867 {
8868 const_cast<FileDef*>(fd)->addSourceRef(nd->getStartDefLine(),nd.get(),nullptr);
8869 }
8870 }
8871
8872 // add source references for member names
8873 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8874 {
8875 for (const auto &md : *mn)
8876 {
8877 //printf("class member %s: def=%s body=%d link?=%d\n",
8878 // qPrint(md->name()),
8879 // md->getBodyDef()?qPrint(md->getBodyDef()->name()):"<none>",
8880 // md->getStartBodyLine(),md->isLinkableInProject());
8881 const FileDef *fd=md->getBodyDef();
8882 if (fd &&
8883 md->getStartDefLine()!=-1 &&
8884 md->isLinkableInProject() &&
8886 )
8887 {
8888 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8889 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8890 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8891 }
8892 }
8893 }
8894 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8895 {
8896 for (const auto &md : *mn)
8897 {
8898 const FileDef *fd=md->getBodyDef();
8899 //printf("member %s body=[%d,%d] fd=%p link=%d parseSources=%d\n",
8900 // qPrint(md->name()),
8901 // md->getStartBodyLine(),md->getEndBodyLine(),fd,
8902 // md->isLinkableInProject(),
8903 // Doxygen::parseSourcesNeeded);
8904 if (fd &&
8905 md->getStartDefLine()!=-1 &&
8906 md->isLinkableInProject() &&
8908 )
8909 {
8910 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8911 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8912 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8913 }
8914 }
8915 }
8916}
8917
8918//----------------------------------------------------------------------------
8919
8920// add the macro definitions found during preprocessing as file members
8921static void buildDefineList()
8922{
8923 AUTO_TRACE();
8924 for (const auto &s : g_inputFiles)
8925 {
8926 auto it = Doxygen::macroDefinitions.find(s);
8928 {
8929 for (const auto &def : it->second)
8930 {
8931 auto md = createMemberDef(
8932 def.fileName,def.lineNr,def.columnNr,
8933 "#define",def.name,def.args,QCString(),
8934 Protection::Public,Specifier::Normal,FALSE,Relationship::Member,MemberType::Define,
8935 ArgumentList(),ArgumentList(),"");
8936 auto mmd = toMemberDefMutable(md.get());
8937
8938 if (!def.args.isEmpty())
8939 {
8940 mmd->moveArgumentList(stringToArgumentList(SrcLangExt::Cpp, def.args));
8941 }
8942 mmd->setInitializer(def.definition);
8943 mmd->setFileDef(def.fileDef);
8944 mmd->setDefinition("#define "+def.name);
8945
8946 MemberName *mn=Doxygen::functionNameLinkedMap->add(def.name);
8947 if (def.fileDef)
8948 {
8949 const MemberList *defMl = def.fileDef->getMemberList(MemberListType::DocDefineMembers());
8950 if (defMl)
8951 {
8952 const MemberDef *defMd = defMl->findRev(def.name);
8953 if (defMd) // definition already stored
8954 {
8955 mmd->setRedefineCount(defMd->redefineCount()+1);
8956 }
8957 }
8958 def.fileDef->insertMember(md.get());
8959 }
8960 AUTO_TRACE_ADD("adding macro {} with definition {}",def.name,def.definition);
8961 mn->push_back(std::move(md));
8962 }
8963 }
8964 }
8965}
8966
8967//----------------------------------------------------------------------------
8968
8969static void sortMemberLists()
8970{
8971 // sort class member lists
8972 for (const auto &cd : *Doxygen::classLinkedMap)
8973 {
8974 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8975 if (cdm)
8976 {
8977 cdm->sortMemberLists();
8978 }
8979 }
8980
8981 // sort namespace member lists
8982 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8983 {
8985 if (ndm)
8986 {
8987 ndm->sortMemberLists();
8988 }
8989 }
8990
8991 // sort file member lists
8992 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8993 {
8994 for (const auto &fd : *fn)
8995 {
8996 fd->sortMemberLists();
8997 }
8998 }
8999
9000 // sort group member lists
9001 for (const auto &gd : *Doxygen::groupLinkedMap)
9002 {
9003 gd->sortMemberLists();
9004 }
9005
9007}
9008
9009//----------------------------------------------------------------------------
9010
9011static bool isSymbolHidden(const Definition *d)
9012{
9013 bool hidden = d->isHidden();
9014 const Definition *parent = d->getOuterScope();
9015 return parent ? hidden || isSymbolHidden(parent) : hidden;
9016}
9017
9019{
9020 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9021 if (numThreads>1)
9022 {
9023 ThreadPool threadPool(numThreads);
9024 std::vector < std::future< void > > results;
9025 // queue the work
9026 for (const auto &[name,symList] : *Doxygen::symbolMap)
9027 {
9028 for (const auto &def : symList)
9029 {
9031 if (dm && !isSymbolHidden(def) && !def->isArtificial() && def->isLinkableInProject())
9032 {
9033 auto processTooltip = [dm]() {
9034 dm->computeTooltip();
9035 };
9036 results.emplace_back(threadPool.queue(processTooltip));
9037 }
9038 }
9039 }
9040 // wait for the results
9041 for (auto &f : results)
9042 {
9043 f.get();
9044 }
9045 }
9046 else
9047 {
9048 for (const auto &[name,symList] : *Doxygen::symbolMap)
9049 {
9050 for (const auto &def : symList)
9051 {
9053 if (dm && !isSymbolHidden(def) && !def->isArtificial() && def->isLinkableInProject())
9054 {
9055 dm->computeTooltip();
9056 }
9057 }
9058 }
9059 }
9060}
9061
9062//----------------------------------------------------------------------------
9063
9065{
9066 for (const auto &cd : *Doxygen::classLinkedMap)
9067 {
9068 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9069 if (cdm)
9070 {
9071 cdm->setAnonymousEnumType();
9072 }
9073 }
9074}
9075
9076//----------------------------------------------------------------------------
9077
9078static void countMembers()
9079{
9080 for (const auto &cd : *Doxygen::classLinkedMap)
9081 {
9082 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9083 if (cdm)
9084 {
9085 cdm->countMembers();
9086 }
9087 }
9088
9089 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9090 {
9092 if (ndm)
9093 {
9094 ndm->countMembers();
9095 }
9096 }
9097
9098 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9099 {
9100 for (const auto &fd : *fn)
9101 {
9102 fd->countMembers();
9103 }
9104 }
9105
9106 for (const auto &gd : *Doxygen::groupLinkedMap)
9107 {
9108 gd->countMembers();
9109 }
9110
9111 auto &mm = ModuleManager::instance();
9112 mm.countMembers();
9113}
9114
9115
9116//----------------------------------------------------------------------------
9117// generate the documentation for all classes
9118
9119static void generateDocsForClassList(const std::vector<ClassDefMutable*> &classList)
9120{
9121 AUTO_TRACE();
9122 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9123 if (numThreads>1) // multi threaded processing
9124 {
9125 struct DocContext
9126 {
9127 DocContext(ClassDefMutable *cd_,const OutputList &ol_)
9128 : cd(cd_), ol(ol_) {}
9129 ClassDefMutable *cd;
9130 OutputList ol;
9131 };
9132 ThreadPool threadPool(numThreads);
9133 std::vector< std::future< std::shared_ptr<DocContext> > > results;
9134 for (const auto &cd : classList)
9135 {
9136 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
9137 if (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9138 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
9139 )
9140 {
9141 auto ctx = std::make_shared<DocContext>(cd,*g_outputList);
9142 auto processFile = [ctx]()
9143 {
9144 msg("Generating docs for compound {}...\n",ctx->cd->displayName());
9145
9146 // skip external references, anonymous compounds and
9147 // template instances
9148 if (!ctx->cd->isHidden() && !ctx->cd->isEmbeddedInOuterScope() &&
9149 ctx->cd->isLinkableInProject() && !ctx->cd->isImplicitTemplateInstance())
9150 {
9151 ctx->cd->writeDocumentation(ctx->ol);
9152 ctx->cd->writeMemberList(ctx->ol);
9153 }
9154
9155 // even for undocumented classes, the inner classes can be documented.
9156 ctx->cd->writeDocumentationForInnerClasses(ctx->ol);
9157 return ctx;
9158 };
9159 results.emplace_back(threadPool.queue(processFile));
9160 }
9161 }
9162 for (auto &f : results)
9163 {
9164 auto ctx = f.get();
9165 }
9166 }
9167 else // single threaded processing
9168 {
9169 for (const auto &cd : classList)
9170 {
9171 //printf("cd=%s getOuterScope=%p global=%p hidden=%d embeddedInOuterScope=%d\n",
9172 // qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope,cd->isHidden(),cd->isEmbeddedInOuterScope());
9173 if (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9174 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
9175 )
9176 {
9177 // skip external references, anonymous compounds and
9178 // template instances
9179 if ( !cd->isHidden() && !cd->isEmbeddedInOuterScope() &&
9180 cd->isLinkableInProject() && !cd->isImplicitTemplateInstance())
9181 {
9182 msg("Generating docs for compound {}...\n",cd->displayName());
9183
9184 cd->writeDocumentation(*g_outputList);
9185 cd->writeMemberList(*g_outputList);
9186 }
9187 // even for undocumented classes, the inner classes can be documented.
9188 cd->writeDocumentationForInnerClasses(*g_outputList);
9189 }
9190 }
9191 }
9192}
9193
9194static void addClassAndNestedClasses(std::vector<ClassDefMutable*> &list,ClassDefMutable *cd)
9195{
9196 list.push_back(cd);
9197 for (const auto &innerCdi : cd->getClasses())
9198 {
9199 ClassDefMutable *innerCd = toClassDefMutable(innerCdi);
9200 if (innerCd)
9201 {
9202 AUTO_TRACE("innerCd={} isLinkable={} isImplicitTemplateInstance={} protectLevelVisible={} embeddedInOuterScope={}",
9203 innerCd->name(),innerCd->isLinkableInProject(),innerCd->isImplicitTemplateInstance(),protectionLevelVisible(innerCd->protection()),
9204 innerCd->isEmbeddedInOuterScope());
9205 }
9206 if (innerCd && innerCd->isLinkableInProject() && !innerCd->isImplicitTemplateInstance() &&
9207 protectionLevelVisible(innerCd->protection()) &&
9208 !innerCd->isEmbeddedInOuterScope()
9209 )
9210 {
9211 list.push_back(innerCd);
9212 addClassAndNestedClasses(list,innerCd);
9213 }
9214 }
9215}
9216
9218{
9219 std::vector<ClassDefMutable*> classList;
9220 for (const auto &cdi : *Doxygen::classLinkedMap)
9221 {
9222 ClassDefMutable *cd = toClassDefMutable(cdi.get());
9223 if (cd && (cd->getOuterScope()==nullptr ||
9225 {
9226 addClassAndNestedClasses(classList,cd);
9227 }
9228 }
9229 for (const auto &cdi : *Doxygen::hiddenClassLinkedMap)
9230 {
9231 ClassDefMutable *cd = toClassDefMutable(cdi.get());
9232 if (cd && (cd->getOuterScope()==nullptr ||
9234 {
9235 addClassAndNestedClasses(classList,cd);
9236 }
9237 }
9238 generateDocsForClassList(classList);
9239}
9240
9241//----------------------------------------------------------------------------
9242
9244{
9245 for (const auto &cdi : *Doxygen::conceptLinkedMap)
9246 {
9248
9249 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
9250 if (cd &&
9251 (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9252 cd->getOuterScope()==Doxygen::globalScope // only look at global concepts
9253 ) && !cd->isHidden() && cd->isLinkableInProject()
9254 )
9255 {
9256 msg("Generating docs for concept {}...\n",cd->displayName());
9258 }
9259 }
9260}
9261
9262//----------------------------------------------------------------------------
9263
9265{
9266 for (const auto &mn : *Doxygen::memberNameLinkedMap)
9267 {
9268 for (const auto &imd : *mn)
9269 {
9270 MemberDefMutable *md = toMemberDefMutable(imd.get());
9271 //static int count=0;
9272 //printf("%04d Member '%s'\n",count++,qPrint(md->qualifiedName()));
9273 if (md && md->documentation().isEmpty() && md->briefDescription().isEmpty())
9274 { // no documentation yet
9275 const MemberDef *bmd = md->reimplements();
9276 while (bmd && bmd->documentation().isEmpty() &&
9277 bmd->briefDescription().isEmpty()
9278 )
9279 { // search up the inheritance tree for a documentation member
9280 //printf("bmd=%s class=%s\n",qPrint(bmd->name()),qPrint(bmd->getClassDef()->name()));
9281 bmd = bmd->reimplements();
9282 }
9283 if (bmd) // copy the documentation from the reimplemented member
9284 {
9285 md->setInheritsDocsFrom(bmd);
9286 md->setDocumentation(bmd->documentation(),bmd->docFile(),bmd->docLine());
9288 md->setBriefDescription(bmd->briefDescription(),bmd->briefFile(),bmd->briefLine());
9289 md->copyArgumentNames(bmd);
9291 }
9292 }
9293 }
9294 }
9295}
9296
9297//----------------------------------------------------------------------------
9298
9300{
9301 // for each file
9302 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9303 {
9304 for (const auto &fd : *fn)
9305 {
9306 fd->combineUsingRelations();
9307 }
9308 }
9309
9310 // for each namespace
9311 NamespaceDefSet visitedNamespaces;
9312 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9313 {
9315 if (ndm)
9316 {
9317 ndm->combineUsingRelations(visitedNamespaces);
9318 }
9319 }
9320}
9321
9322//----------------------------------------------------------------------------
9323
9325{
9326 // for each class
9327 for (const auto &cd : *Doxygen::classLinkedMap)
9328 {
9329 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9330 if (cdm)
9331 {
9333 }
9334 }
9335 // for each file
9336 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9337 {
9338 for (const auto &fd : *fn)
9339 {
9340 fd->addMembersToMemberGroup();
9341 }
9342 }
9343 // for each namespace
9344 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9345 {
9347 if (ndm)
9348 {
9350 }
9351 }
9352 // for each group
9353 for (const auto &gd : *Doxygen::groupLinkedMap)
9354 {
9355 gd->addMembersToMemberGroup();
9356 }
9358}
9359
9360//----------------------------------------------------------------------------
9361
9363{
9364 // for each class
9365 for (const auto &cd : *Doxygen::classLinkedMap)
9366 {
9367 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9368 if (cdm)
9369 {
9371 }
9372 }
9373 // for each file
9374 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9375 {
9376 for (const auto &fd : *fn)
9377 {
9378 fd->distributeMemberGroupDocumentation();
9379 }
9380 }
9381 // for each namespace
9382 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9383 {
9385 if (ndm)
9386 {
9388 }
9389 }
9390 // for each group
9391 for (const auto &gd : *Doxygen::groupLinkedMap)
9392 {
9393 gd->distributeMemberGroupDocumentation();
9394 }
9396}
9397
9398//----------------------------------------------------------------------------
9399
9401{
9402 // for each class
9403 for (const auto &cd : *Doxygen::classLinkedMap)
9404 {
9405 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9406 if (cdm)
9407 {
9409 }
9410 }
9411 // for each concept
9412 for (const auto &cd : *Doxygen::conceptLinkedMap)
9413 {
9414 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
9415 if (cdm)
9416 {
9418 }
9419 }
9420 // for each file
9421 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9422 {
9423 for (const auto &fd : *fn)
9424 {
9425 fd->findSectionsInDocumentation();
9426 }
9427 }
9428 // for each namespace
9429 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9430 {
9432 if (ndm)
9433 {
9435 }
9436 }
9437 // for each group
9438 for (const auto &gd : *Doxygen::groupLinkedMap)
9439 {
9440 gd->findSectionsInDocumentation();
9441 }
9442 // for each page
9443 for (const auto &pd : *Doxygen::pageLinkedMap)
9444 {
9445 pd->findSectionsInDocumentation();
9446 }
9447 // for each directory
9448 for (const auto &dd : *Doxygen::dirLinkedMap)
9449 {
9450 dd->findSectionsInDocumentation();
9451 }
9453 if (Doxygen::mainPage) Doxygen::mainPage->findSectionsInDocumentation();
9454}
9455
9456//----------------------------------------------------------------------
9457
9458
9460{
9461 // remove all references to classes from the cache
9462 // as there can be new template instances in the inheritance path
9463 // to this class. Optimization: only remove those classes that
9464 // have inheritance instances as direct or indirect sub classes.
9466
9467 // remove all cached typedef resolutions whose target is a
9468 // template class as this may now be a template instance
9469 // for each global function name
9470 for (const auto &fn : *Doxygen::functionNameLinkedMap)
9471 {
9472 // for each function with that name
9473 for (const auto &ifmd : *fn)
9474 {
9475 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
9476 if (fmd && fmd->isTypedefValCached())
9477 {
9478 const ClassDef *cd = fmd->getCachedTypedefVal();
9479 if (cd->isTemplate()) fmd->invalidateTypedefValCache();
9480 }
9481 }
9482 }
9483 // for each class method name
9484 for (const auto &nm : *Doxygen::memberNameLinkedMap)
9485 {
9486 // for each function with that name
9487 for (const auto &imd : *nm)
9488 {
9489 MemberDefMutable *md = toMemberDefMutable(imd.get());
9490 if (md && md->isTypedefValCached())
9491 {
9492 const ClassDef *cd = md->getCachedTypedefVal();
9493 if (cd->isTemplate()) md->invalidateTypedefValCache();
9494 }
9495 }
9496 }
9497}
9498
9499//----------------------------------------------------------------------------
9500
9502{
9503 // Remove all unresolved references to classes from the cache.
9504 // This is needed before resolving the inheritance relations, since
9505 // it would otherwise not find the inheritance relation
9506 // for C in the example below, as B::I was already found to be unresolvable
9507 // (which is correct if you ignore the inheritance relation between A and B).
9508 //
9509 // class A { class I {} };
9510 // class B : public A {};
9511 // class C : public B::I {};
9513
9514 // for each class method name
9515 for (const auto &nm : *Doxygen::memberNameLinkedMap)
9516 {
9517 // for each function with that name
9518 for (const auto &imd : *nm)
9519 {
9520 MemberDefMutable *md = toMemberDefMutable(imd.get());
9521 if (md)
9522 {
9524 }
9525 }
9526 }
9527
9528}
9529
9530//----------------------------------------------------------------------------
9531// Returns TRUE if the entry and member definition have equal file names,
9532// otherwise FALSE.
9533
9534static bool haveEqualFileNames(const Entry *root, const MemberDef *md)
9535{
9536 if (const FileDef *fd = md->getFileDef())
9537 {
9538 return fd->absFilePath() == root->fileName;
9539 }
9540 return false;
9541}
9542
9543//----------------------------------------------------------------------------
9544
9545static void addDefineDoc(const Entry *root, MemberDefMutable *md)
9546{
9547 md->setDocumentation(root->doc,root->docFile,root->docLine);
9548 md->setDocsForDefinition(!root->proto);
9549 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9550 if (md->inbodyDocumentation().isEmpty())
9551 {
9553 }
9554 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
9555 {
9556 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
9557 md->setBodyDef(root->fileDef());
9558 }
9560 md->setMaxInitLines(root->initLines);
9562 md->setRefItems(root->sli);
9563 md->setRequirementReferences(root->rqli);
9564 md->addQualifiers(root->qualifiers);
9565 if (root->mGrpId!=-1) md->setMemberGroupId(root->mGrpId);
9566 addMemberToGroups(root,md);
9568}
9569
9570//----------------------------------------------------------------------------
9571
9573{
9574 if ((root->section.isDefineDoc() || root->section.isDefine()) && !root->name.isEmpty())
9575 {
9576 //printf("found define '%s' '%s' brief='%s' doc='%s'\n",
9577 // qPrint(root->name),qPrint(root->args),qPrint(root->brief),qPrint(root->doc));
9578
9579 if (root->tagInfo() && !root->name.isEmpty()) // define read from a tag file
9580 {
9581 auto md = createMemberDef(root->tagInfo()->tagName,1,1,
9582 "#define",root->name,root->args,QCString(),
9583 Protection::Public,Specifier::Normal,FALSE,Relationship::Member,MemberType::Define,
9584 ArgumentList(),ArgumentList(),"");
9585 auto mmd = toMemberDefMutable(md.get());
9586 mmd->setTagInfo(root->tagInfo());
9587 mmd->setLanguage(root->lang);
9588 mmd->addQualifiers(root->qualifiers);
9589 //printf("Searching for '%s' fd=%p\n",qPrint(filePathName),fd);
9590 mmd->setFileDef(root->parent()->fileDef());
9591 //printf("Adding member=%s\n",qPrint(md->name()));
9593 mn->push_back(std::move(md));
9594 }
9596 if (mn)
9597 {
9598 int count=0;
9599 for (const auto &md : *mn)
9600 {
9601 if (md->memberType()==MemberType::Define) count++;
9602 }
9603 if (count==1)
9604 {
9605 for (const auto &imd : *mn)
9606 {
9607 MemberDefMutable *md = toMemberDefMutable(imd.get());
9608 if (md && md->memberType()==MemberType::Define)
9609 {
9610 addDefineDoc(root,md);
9611 }
9612 }
9613 }
9614 else if (count>1 &&
9615 (!root->doc.isEmpty() ||
9616 !root->brief.isEmpty() ||
9617 root->bodyLine!=-1
9618 )
9619 )
9620 // multiple defines don't know where to add docs
9621 // but maybe they are in different files together with their documentation
9622 {
9623 for (const auto &imd : *mn)
9624 {
9625 MemberDefMutable *md = toMemberDefMutable(imd.get());
9626 if (md && md->memberType()==MemberType::Define)
9627 {
9628 if (haveEqualFileNames(root, md) || isEntryInGroupOfMember(root, md))
9629 // doc and define in the same file or group assume they belong together.
9630 {
9631 addDefineDoc(root,md);
9632 }
9633 }
9634 }
9635 //warn("define {} found in the following files:\n",root->name);
9636 //warn("Cannot determine where to add the documentation found "
9637 // "at line {} of file {}. \n",
9638 // root->startLine,root->fileName);
9639 }
9640 }
9641 else if (!root->doc.isEmpty() || !root->brief.isEmpty()) // define not found
9642 {
9643 bool preEnabled = Config_getBool(ENABLE_PREPROCESSING);
9644 if (preEnabled)
9645 {
9646 warn(root->fileName,root->startLine,"documentation for unknown define {} found.",root->name);
9647 }
9648 else
9649 {
9650 warn(root->fileName,root->startLine, "found documented #define {} but ignoring it because ENABLE_PREPROCESSING is NO.", root->name);
9651 }
9652 }
9653 }
9654 for (const auto &e : root->children()) findDefineDocumentation(e.get());
9655}
9656
9657//----------------------------------------------------------------------------
9658
9659static void findDirDocumentation(const Entry *root)
9660{
9661 if (root->section.isDirDoc())
9662 {
9663 QCString normalizedName = root->name;
9664 normalizedName = substitute(normalizedName,"\\","/");
9665 //printf("root->docFile=%s normalizedName=%s\n",
9666 // qPrint(root->docFile),qPrint(normalizedName));
9667 if (root->docFile==normalizedName) // current dir?
9668 {
9669 int lastSlashPos=normalizedName.findRev('/');
9670 if (lastSlashPos!=-1) // strip file name
9671 {
9672 normalizedName=normalizedName.left(lastSlashPos);
9673 }
9674 }
9675 if (normalizedName.at(normalizedName.length()-1)!='/')
9676 {
9677 normalizedName+='/';
9678 }
9679 DirDef *matchingDir=nullptr;
9680 for (const auto &dir : *Doxygen::dirLinkedMap)
9681 {
9682 //printf("Dir: %s<->%s\n",qPrint(dir->name()),qPrint(normalizedName));
9683 if (dir->name().right(normalizedName.length())==normalizedName)
9684 {
9685 if (matchingDir)
9686 {
9687 warn(root->fileName,root->startLine,
9688 "\\dir command matches multiple directories.\n"
9689 " Applying the command for directory {}\n"
9690 " Ignoring the command for directory {}",
9691 matchingDir->name(),dir->name()
9692 );
9693 }
9694 else
9695 {
9696 matchingDir=dir.get();
9697 }
9698 }
9699 }
9700 if (matchingDir)
9701 {
9702 //printf("Match for with dir %s #anchor=%zu\n",qPrint(matchingDir->name()),root->anchors.size());
9703 matchingDir->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9704 matchingDir->setDocumentation(root->doc,root->docFile,root->docLine);
9705 matchingDir->setRefItems(root->sli);
9706 matchingDir->setRequirementReferences(root->rqli);
9707 matchingDir->addSectionsToDefinition(root->anchors);
9708 root->commandOverrides.apply_directoryGraph([&](bool b) { matchingDir->overrideDirectoryGraph(b); });
9709 addDirToGroups(root,matchingDir);
9710 }
9711 else
9712 {
9713 warn(root->fileName,root->startLine,"No matching directory found for command \\dir {}",normalizedName);
9714 }
9715 }
9716 for (const auto &e : root->children()) findDirDocumentation(e.get());
9717}
9718
9719//----------------------------------------------------------------------------
9721{
9722 if (root->section.isRequirementDoc())
9723 {
9725 }
9726 for (const auto &e : root->children()) buildRequirementsList(e.get());
9727}
9728
9729//----------------------------------------------------------------------------
9730// create a (sorted) list of separate documentation pages
9731
9732static void buildPageList(Entry *root)
9733{
9734 if (root->section.isPageDoc())
9735 {
9736 if (!root->name.isEmpty())
9737 {
9738 addRelatedPage(root);
9739 }
9740 }
9741 else if (root->section.isMainpageDoc())
9742 {
9743 QCString title=root->args.stripWhiteSpace();
9744 if (title.isEmpty()) title=theTranslator->trMainPage();
9745 //QCString name = Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9746 QCString name = "index";
9747 addRefItem(root->sli,
9748 name,
9749 theTranslator->trPage(TRUE,TRUE),
9750 name,
9751 title,
9752 QCString(),nullptr
9753 );
9754 }
9755 for (const auto &e : root->children()) buildPageList(e.get());
9756}
9757
9758// search for the main page defined in this project
9759static void findMainPage(Entry *root)
9760{
9761 if (root->section.isMainpageDoc())
9762 {
9763 if (Doxygen::mainPage==nullptr && root->tagInfo()==nullptr)
9764 {
9765 //printf("mainpage: docLine=%d startLine=%d\n",root->docLine,root->startLine);
9766 //printf("Found main page! \n======\n%s\n=======\n",qPrint(root->doc));
9767 QCString title=root->args.stripWhiteSpace();
9768 if (title.isEmpty()) title = Config_getString(PROJECT_NAME);
9769 //QCString indexName=Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9770 QCString indexName="index";
9772 indexName, root->brief+root->doc+root->inbodyDocs,title);
9773 //setFileNameForSections(root->anchors,"index",Doxygen::mainPage);
9774 Doxygen::mainPage->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9775 Doxygen::mainPage->setBodySegment(root->startLine,root->startLine,-1);
9776 Doxygen::mainPage->setFileName(indexName);
9777 Doxygen::mainPage->setLocalToc(root->localToc);
9779
9781 if (si)
9782 {
9783 if (!si->ref().isEmpty()) // we are from a tag file
9784 {
9785 // a page name is a label as well! but should no be double either
9787 Doxygen::mainPage->name(),
9788 indexName,
9789 root->startLine,
9790 Doxygen::mainPage->title(),
9792 0); // level 0
9793 }
9794 else if (si->lineNr() != -1)
9795 {
9796 warn(root->fileName,root->startLine,"multiple use of section label '{}' for main page, (first occurrence: {}, line {})",
9797 Doxygen::mainPage->name(),si->fileName(),si->lineNr());
9798 }
9799 else
9800 {
9801 warn(root->fileName,root->startLine,"multiple use of section label '{}' for main page, (first occurrence: {})",
9802 Doxygen::mainPage->name(),si->fileName());
9803 }
9804 }
9805 else
9806 {
9807 // a page name is a label as well! but should no be double either
9809 Doxygen::mainPage->name(),
9810 indexName,
9811 root->startLine,
9812 Doxygen::mainPage->title(),
9814 0); // level 0
9815 }
9816 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9817 }
9818 else if (root->tagInfo()==nullptr)
9819 {
9820 warn(root->fileName,root->startLine,
9821 "found more than one \\mainpage comment block! (first occurrence: {}, line {}), Skipping current block!",
9822 Doxygen::mainPage->docFile(),Doxygen::mainPage->getStartBodyLine());
9823 }
9824 }
9825 for (const auto &e : root->children()) findMainPage(e.get());
9826}
9827
9828// search for the main page imported via tag files and add only the section labels
9829static void findMainPageTagFiles(Entry *root)
9830{
9831 if (root->section.isMainpageDoc())
9832 {
9833 if (Doxygen::mainPage && root->tagInfo())
9834 {
9835 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9836 }
9837 }
9838 for (const auto &e : root->children()) findMainPageTagFiles(e.get());
9839}
9840
9841static void computePageRelations(Entry *root)
9842{
9843 if ((root->section.isPageDoc() || root->section.isMainpageDoc()) && !root->name.isEmpty())
9844 {
9845 PageDef *pd = root->section.isPageDoc() ?
9846 Doxygen::pageLinkedMap->find(root->name) :
9847 Doxygen::mainPage.get();
9848 if (pd)
9849 {
9850 for (const BaseInfo &bi : root->extends)
9851 {
9852 PageDef *subPd = Doxygen::pageLinkedMap->find(bi.name);
9853 if (pd==subPd)
9854 {
9855 term("page defined {} with label {} is a direct "
9856 "subpage of itself! Please remove this cyclic dependency.\n",
9857 warn_line(pd->docFile(),pd->docLine()),pd->name());
9858 }
9859 else if (subPd)
9860 {
9861 pd->addInnerCompound(subPd);
9862 //printf("*** Added subpage relation: %s->%s\n",
9863 // qPrint(pd->name()),qPrint(subPd->name()));
9864 }
9865 }
9866 }
9867 }
9868 for (const auto &e : root->children()) computePageRelations(e.get());
9869}
9870
9872{
9873 for (const auto &pd : *Doxygen::pageLinkedMap)
9874 {
9875 Definition *ppd = pd->getOuterScope();
9876 while (ppd)
9877 {
9878 if (ppd==pd.get())
9879 {
9880 term("page defined {} with label {} is a subpage "
9881 "of itself! Please remove this cyclic dependency.\n",
9882 warn_line(pd->docFile(),pd->docLine()),pd->name());
9883 }
9884 ppd=ppd->getOuterScope();
9885 }
9886 }
9887}
9888
9889//----------------------------------------------------------------------------
9890
9892{
9893 for (const auto &si : SectionManager::instance())
9894 {
9895 //printf("si->label='%s' si->definition=%s si->fileName='%s'\n",
9896 // qPrint(si->label),si->definition?qPrint(si->definition->name()):"<none>",
9897 // qPrint(si->fileName));
9898 PageDef *pd=nullptr;
9899
9900 // hack: the items of a todo/test/bug/deprecated list are all fragments from
9901 // different files, so the resulting section's all have the wrong file
9902 // name (not from the todo/test/bug/deprecated list, but from the file in
9903 // which they are defined). We correct this here by looking at the
9904 // generated section labels!
9906 {
9907 QCString label="_"+rl->listName(); // "_todo", "_test", ...
9908 if (si->label().left(label.length())==label)
9909 {
9910 si->setFileName(rl->listName());
9911 si->setGenerated(TRUE);
9912 break;
9913 }
9914 }
9915
9916 //printf("start: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
9917 if (!si->generated())
9918 {
9919 // if this section is in a page and the page is in a group, then we
9920 // have to adjust the link file name to point to the group.
9921 if (!si->fileName().isEmpty() &&
9922 (pd=Doxygen::pageLinkedMap->find(si->fileName())) &&
9923 pd->getGroupDef())
9924 {
9925 si->setFileName(pd->getGroupDef()->getOutputFileBase());
9926 }
9927
9928 if (si->definition())
9929 {
9930 // TODO: there should be one function in Definition that returns
9931 // the file to link to, so we can avoid the following tests.
9932 const GroupDef *gd=nullptr;
9933 if (si->definition()->definitionType()==Definition::TypeMember)
9934 {
9935 gd = (toMemberDef(si->definition()))->getGroupDef();
9936 }
9937
9938 if (gd)
9939 {
9940 si->setFileName(gd->getOutputFileBase());
9941 }
9942 else
9943 {
9944 //si->fileName=si->definition->getOutputFileBase();
9945 //printf("Setting si->fileName to %s\n",qPrint(si->fileName));
9946 }
9947 }
9948 }
9949 //printf("end: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
9950 }
9951}
9952
9953
9954
9955//----------------------------------------------------------------------------
9956// generate all separate documentation pages
9957
9958
9959static void generatePageDocs()
9960{
9961 //printf("documentedPages=%d real=%d\n",documentedPages,Doxygen::pageLinkedMap->count());
9962 if (Index::instance().numDocumentedPages()==0) return;
9963 for (const auto &pd : *Doxygen::pageLinkedMap)
9964 {
9965 if (!pd->getGroupDef() && !pd->isReference())
9966 {
9967 msg("Generating docs for page {}...\n",pd->name());
9968 pd->writeDocumentation(*g_outputList);
9969 }
9970 }
9971}
9972
9973//----------------------------------------------------------------------------
9974// create a (sorted) list & dictionary of example pages
9975
9976static void buildExampleList(Entry *root)
9977{
9978 if ((root->section.isExample() || root->section.isExampleLineno()) && !root->name.isEmpty())
9979 {
9980 if (Doxygen::exampleLinkedMap->find(root->name))
9981 {
9982 warn(root->fileName,root->startLine,"Example {} was already documented. Ignoring documentation found here.",root->name);
9983 }
9984 else
9985 {
9986 PageDef *pd = Doxygen::exampleLinkedMap->add(root->name,
9987 createPageDef(root->fileName,root->startLine,
9988 root->name,root->brief+root->doc+root->inbodyDocs,root->args));
9989 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9990 pd->setFileName(convertNameToFile(pd->name()+"-example",FALSE,TRUE));
9992 pd->setLanguage(root->lang);
9993 pd->setShowLineNo(root->section.isExampleLineno());
9994
9995 //we don't add example to groups
9996 //addExampleToGroups(root,pd);
9997 }
9998 }
9999 for (const auto &e : root->children()) buildExampleList(e.get());
10000}
10001
10002//----------------------------------------------------------------------------
10003// prints the Entry tree (for debugging)
10004
10005void printNavTree(Entry *root,int indent)
10006{
10008 {
10009 QCString indentStr;
10010 indentStr.fill(' ',indent);
10011 Debug::print(Debug::Entries,0,"{}{} at {}:{} (sec={}, spec={})\n",
10012 indentStr.isEmpty()?"":indentStr,
10013 root->name.isEmpty()?"<empty>":root->name,
10014 root->fileName,root->startLine,
10015 root->section.to_string(),
10016 root->spec.to_string());
10017 for (const auto &e : root->children())
10018 {
10019 printNavTree(e.get(),indent+2);
10020 }
10021 }
10022}
10023
10024
10025//----------------------------------------------------------------------------
10026// prints the Sections tree (for debugging)
10027
10029{
10031 {
10032 for (const auto &si : SectionManager::instance())
10033 {
10034 Debug::print(Debug::Sections,0,"Section = {}, file = {}, title = {}, type = {}, ref = {}\n",
10035 si->label(),si->fileName(),si->title(),si->type().level(),si->ref());
10036 }
10037 }
10038}
10039
10040
10041//----------------------------------------------------------------------------
10042// generate the example documentation
10043
10045{
10046 g_outputList->disable(OutputType::Man);
10047 for (const auto &pd : *Doxygen::exampleLinkedMap)
10048 {
10049 msg("Generating docs for example {}...\n",pd->name());
10050 SrcLangExt lang = getLanguageFromFileName(pd->name(), SrcLangExt::Unknown);
10051 if (lang != SrcLangExt::Unknown)
10052 {
10053 QCString ext = getFileNameExtension(pd->name());
10054 auto intf = Doxygen::parserManager->getCodeParser(ext);
10055 intf->resetCodeParserState();
10056 }
10057 QCString n=pd->getOutputFileBase();
10058 startFile(*g_outputList,n,false,n,pd->name());
10060 g_outputList->docify(pd->name());
10062 g_outputList->startContents();
10063 QCString lineNoOptStr;
10064 if (pd->showLineNo())
10065 {
10066 lineNoOptStr="{lineno}";
10067 }
10068 g_outputList->generateDoc(pd->docFile(), // file
10069 pd->docLine(), // startLine
10070 pd.get(), // context
10071 nullptr, // memberDef
10072 (pd->briefDescription().isEmpty()?"":pd->briefDescription()+"\n\n")+
10073 pd->documentation()+"\n\n\\include"+lineNoOptStr+" "+pd->name(), // docs
10074 DocOptions()
10075 .setIndexWords(true)
10076 .setExample(pd->name()));
10077 endFile(*g_outputList); // contains g_outputList->endContents()
10078 }
10080}
10081
10082//----------------------------------------------------------------------------
10083// generate module pages
10084
10086{
10087 for (const auto &gd : *Doxygen::groupLinkedMap)
10088 {
10089 if (!gd->isReference())
10090 {
10091 gd->writeDocumentation(*g_outputList);
10092 }
10093 }
10094}
10095
10096//----------------------------------------------------------------------------
10097// generate module pages
10098
10100{
10101 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10102 if (numThreads>1) // multi threaded processing
10103 {
10104 struct DocContext
10105 {
10106 DocContext(ClassDefMutable *cdm_,const OutputList &ol_)
10107 : cdm(cdm_), ol(ol_) {}
10108 ClassDefMutable *cdm;
10109 OutputList ol;
10110 };
10111 ThreadPool threadPool(numThreads);
10112 std::vector< std::future< std::shared_ptr<DocContext> > > results;
10113 // for each class in the namespace...
10114 for (const auto &cd : classList)
10115 {
10117 if (cdm)
10118 {
10119 auto ctx = std::make_shared<DocContext>(cdm,*g_outputList);
10120 auto processFile = [ctx]()
10121 {
10122 if ( ( ctx->cdm->isLinkableInProject() &&
10123 !ctx->cdm->isImplicitTemplateInstance()
10124 ) // skip external references, anonymous compounds and
10125 // template instances and nested classes
10126 && !ctx->cdm->isHidden() && !ctx->cdm->isEmbeddedInOuterScope()
10127 )
10128 {
10129 msg("Generating docs for compound {}...\n",ctx->cdm->displayName());
10130 ctx->cdm->writeDocumentation(ctx->ol);
10131 ctx->cdm->writeMemberList(ctx->ol);
10132 }
10133 ctx->cdm->writeDocumentationForInnerClasses(ctx->ol);
10134 return ctx;
10135 };
10136 results.emplace_back(threadPool.queue(processFile));
10137 }
10138 }
10139 // wait for the results
10140 for (auto &f : results)
10141 {
10142 auto ctx = f.get();
10143 }
10144 }
10145 else // single threaded processing
10146 {
10147 // for each class in the namespace...
10148 for (const auto &cd : classList)
10149 {
10151 if (cdm)
10152 {
10153 if ( ( cd->isLinkableInProject() &&
10154 !cd->isImplicitTemplateInstance()
10155 ) // skip external references, anonymous compounds and
10156 // template instances and nested classes
10157 && !cd->isHidden() && !cd->isEmbeddedInOuterScope()
10158 )
10159 {
10160 msg("Generating docs for compound {}...\n",cd->displayName());
10161
10164 }
10166 }
10167 }
10168 }
10169}
10170
10172{
10173 // for each concept in the namespace...
10174 for (const auto &cd : conceptList)
10175 {
10177 if ( cdm && cd->isLinkableInProject() && !cd->isHidden())
10178 {
10179 msg("Generating docs for concept {}...\n",cd->name());
10181 }
10182 }
10183}
10184
10186{
10187 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
10188
10189 //writeNamespaceIndex(*g_outputList);
10190
10191 // for each namespace...
10192 for (const auto &nd : *Doxygen::namespaceLinkedMap)
10193 {
10194 if (nd->isLinkableInProject())
10195 {
10197 if (ndm)
10198 {
10199 msg("Generating docs for namespace {}\n",nd->displayName());
10201 }
10202 }
10203
10204 generateNamespaceClassDocs(nd->getClasses());
10205 if (sliceOpt)
10206 {
10207 generateNamespaceClassDocs(nd->getInterfaces());
10208 generateNamespaceClassDocs(nd->getStructs());
10209 generateNamespaceClassDocs(nd->getExceptions());
10210 }
10211 generateNamespaceConceptDocs(nd->getConcepts());
10212 }
10213}
10214
10216{
10217 std::string oldDir = Dir::currentDirPath();
10218 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
10221 {
10222 err("failed to run html help compiler on {}\n", HtmlHelp::hhpFileName);
10223 }
10224 Dir::setCurrent(oldDir);
10225}
10226
10228{
10229 QCString args = Qhp::qhpFileName + " -o \"" + Qhp::getQchFileName() + "\"";
10230 std::string oldDir = Dir::currentDirPath();
10231 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
10232
10233 QCString qhgLocation=Config_getString(QHG_LOCATION);
10234 if (Debug::isFlagSet(Debug::Qhp)) // produce info for debugging
10235 {
10236 // run qhelpgenerator -v and extract the Qt version used
10237 QCString cmd=qhgLocation+ " -v 2>&1";
10238 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
10239 FILE *f=Portable::popen(cmd,"r");
10240 if (!f)
10241 {
10242 err("could not execute {}\n",qhgLocation);
10243 }
10244 else
10245 {
10246 const size_t bufSize = 1024;
10247 char inBuf[bufSize+1];
10248 size_t numRead=fread(inBuf,1,bufSize,f);
10249 inBuf[numRead] = '\0';
10250 Debug::print(Debug::Qhp,0,"{}",inBuf);
10252
10253 int qtVersion=0;
10254 static const reg::Ex versionReg(R"(Qt (\d+)\.(\d+)\.(\d+))");
10255 reg::Match match;
10256 std::string s = inBuf;
10257 if (reg::search(s,match,versionReg))
10258 {
10259 qtVersion = 10000*QCString(match[1].str()).toInt() +
10260 100*QCString(match[2].str()).toInt() +
10261 QCString(match[3].str()).toInt();
10262 }
10263 if (qtVersion>0 && (qtVersion<60000 || qtVersion >= 60205))
10264 {
10265 // dump the output of qhelpgenerator -c file.qhp
10266 // Qt<6 or Qt>=6.2.5 or higher, see https://bugreports.qt.io/browse/QTBUG-101070
10267 cmd=qhgLocation+ " -c " + Qhp::qhpFileName + " 2>&1";
10268 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
10269 f=Portable::popen(cmd,"r");
10270 if (!f)
10271 {
10272 err("could not execute {}\n",qhgLocation);
10273 }
10274 else
10275 {
10276 std::string output;
10277 while ((numRead=fread(inBuf,1,bufSize,f))>0)
10278 {
10279 inBuf[numRead] = '\0';
10280 output += inBuf;
10281 }
10283 Debug::print(Debug::Qhp,0,"{}",output);
10284 }
10285 }
10286 }
10287 }
10288
10289 if (Portable::system(qhgLocation, args, FALSE))
10290 {
10291 err("failed to run qhelpgenerator on {}\n",Qhp::qhpFileName);
10292 }
10293 Dir::setCurrent(oldDir);
10294}
10295
10296//----------------------------------------------------------------------------
10297
10299{
10300 // check dot path
10301 QCString dotPath = Config_getString(DOT_PATH);
10302 if (!dotPath.isEmpty())
10303 {
10304 FileInfo fi(dotPath.str());
10305 if (!(fi.exists() && fi.isFile()) )// not an existing user specified path + exec
10306 {
10307 dotPath = dotPath+"/dot"+Portable::commandExtension();
10308 FileInfo dp(dotPath.str());
10309 if (!dp.exists() || !dp.isFile())
10310 {
10311 warn_uncond("the dot tool could not be found as '{}'\n",dotPath);
10312 dotPath = "dot";
10313 dotPath += Portable::commandExtension();
10314 }
10315 }
10316#if defined(_WIN32) // convert slashes
10317 size_t l=dotPath.length();
10318 for (size_t i=0;i<l;i++) if (dotPath.at(i)=='/') dotPath.at(i)='\\';
10319#endif
10320 }
10321 else
10322 {
10323 dotPath = "dot";
10324 dotPath += Portable::commandExtension();
10325 }
10326 Doxygen::verifiedDotPath = dotPath;
10328}
10329
10330//----------------------------------------------------------------------------
10331
10332/*! Generate a template version of the configuration file.
10333 * If the \a shortList parameter is TRUE a configuration file without
10334 * comments will be generated.
10335 */
10336static void generateConfigFile(const QCString &configFile,bool shortList,
10337 bool updateOnly=FALSE)
10338{
10339 std::ofstream f;
10340 bool fileOpened=openOutputFile(configFile,f);
10341 bool writeToStdout=configFile=="-";
10342 if (fileOpened)
10343 {
10344 TextStream t(&f);
10345 Config::writeTemplate(t,shortList,updateOnly);
10346 if (!writeToStdout)
10347 {
10348 if (!updateOnly)
10349 {
10350 msg("\n\nConfiguration file '{}' created.\n\n",configFile);
10351 msg("Now edit the configuration file and enter\n\n");
10352 if (configFile!="Doxyfile" && configFile!="doxyfile")
10353 msg(" doxygen {}\n\n",configFile);
10354 else
10355 msg(" doxygen\n\n");
10356 msg("to generate the documentation for your project\n\n");
10357 }
10358 else
10359 {
10360 msg("\n\nConfiguration file '{}' updated.\n\n",configFile);
10361 }
10362 }
10363 }
10364 else
10365 {
10366 term("Cannot open file {} for writing\n",configFile);
10367 }
10368}
10369
10371{
10372 std::ofstream f;
10373 bool fileOpened=openOutputFile("-",f);
10374 if (fileOpened)
10375 {
10376 TextStream t(&f);
10377 Config::compareDoxyfile(t,diffList);
10378 }
10379 else
10380 {
10381 term("Cannot open stdout for writing\n");
10382 }
10383}
10384
10385//----------------------------------------------------------------------------
10386// read and parse a tag file
10387
10388static void readTagFile(const std::shared_ptr<Entry> &root,const QCString &tagLine)
10389{
10390 QCString fileName;
10391 QCString destName;
10392 int eqPos = tagLine.find('=');
10393 if (eqPos!=-1) // tag command contains a destination
10394 {
10395 fileName = tagLine.left(eqPos).stripWhiteSpace();
10396 destName = tagLine.right(tagLine.length()-eqPos-1).stripWhiteSpace();
10397 if (fileName.isEmpty() || destName.isEmpty()) return;
10398 //printf("insert tagDestination %s->%s\n",qPrint(fi.fileName()),qPrint(destName));
10399 }
10400 else
10401 {
10402 fileName = tagLine;
10403 }
10404
10405 FileInfo fi(fileName.str());
10406 if (!fi.exists() || !fi.isFile())
10407 {
10408 err("Tag file '{}' does not exist or is not a file. Skipping it...\n",fileName);
10409 return;
10410 }
10411
10412 if (Doxygen::tagFileSet.find(fi.absFilePath()) != Doxygen::tagFileSet.end()) return;
10413
10414 Doxygen::tagFileSet.emplace(fi.absFilePath());
10415
10416 if (!destName.isEmpty())
10417 {
10418 Doxygen::tagDestinationMap.emplace(fi.absFilePath(), destName.str());
10419 msg("Reading tag file '{}', location '{}'...\n",fileName,destName);
10420 }
10421 else
10422 {
10423 msg("Reading tag file '{}'...\n",fileName);
10424 }
10425
10426 parseTagFile(root,fi.absFilePath().c_str());
10427}
10428
10429//----------------------------------------------------------------------------
10431{
10432 const StringVector &latexExtraStyleSheet = Config_getList(LATEX_EXTRA_STYLESHEET);
10433 for (const auto &sheet : latexExtraStyleSheet)
10434 {
10435 std::string fileName = sheet;
10436 if (!fileName.empty())
10437 {
10438 FileInfo fi(fileName);
10439 if (!fi.exists())
10440 {
10441 err("Style sheet '{}' specified by LATEX_EXTRA_STYLESHEET does not exist!\n",fileName);
10442 }
10443 else if (fi.isDir())
10444 {
10445 err("Style sheet '{}' specified by LATEX_EXTRA_STYLESHEET is a directory, it has to be a file!\n", fileName);
10446 }
10447 else
10448 {
10449 QCString destFileName = Config_getString(LATEX_OUTPUT)+"/"+fi.fileName();
10451 {
10452 destFileName += LATEX_STYLE_EXTENSION;
10453 }
10454 copyFile(fileName, destFileName);
10455 }
10456 }
10457 }
10458}
10459
10460//----------------------------------------------------------------------------
10461static void copyStyleSheet()
10462{
10463 QCString htmlStyleSheet = Config_getString(HTML_STYLESHEET);
10464 if (!htmlStyleSheet.isEmpty())
10465 {
10466 if (!htmlStyleSheet.startsWith("http:") && !htmlStyleSheet.startsWith("https:"))
10467 {
10468 FileInfo fi(htmlStyleSheet.str());
10469 if (!fi.exists())
10470 {
10471 err("Style sheet '{}' specified by HTML_STYLESHEET does not exist!\n",htmlStyleSheet);
10472 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
10473 }
10474 else if (fi.isDir())
10475 {
10476 err("Style sheet '{}' specified by HTML_STYLESHEET is a directory, it has to be a file!\n",htmlStyleSheet);
10477 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
10478 }
10479 else
10480 {
10481 QCString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
10482 copyFile(htmlStyleSheet,destFileName);
10483 }
10484 }
10485 }
10486 const StringVector &htmlExtraStyleSheet = Config_getList(HTML_EXTRA_STYLESHEET);
10487 for (const auto &sheet : htmlExtraStyleSheet)
10488 {
10489 QCString fileName(sheet);
10490 if (!fileName.isEmpty() && !fileName.startsWith("http:") && !fileName.startsWith("https:"))
10491 {
10492 FileInfo fi(fileName.str());
10493 if (!fi.exists())
10494 {
10495 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET does not exist!\n",fileName);
10496 }
10497 else if (fi.fileName()=="doxygen.css" || fi.fileName()=="tabs.css" || fi.fileName()=="navtree.css")
10498 {
10499 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET is already a built-in stylesheet. Please use a different name\n",fi.fileName());
10500 }
10501 else if (fi.isDir())
10502 {
10503 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET is a directory, it has to be a file!\n",fileName);
10504 }
10505 else
10506 {
10507 QCString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
10508 copyFile(fileName, destFileName);
10509 }
10510 }
10511 }
10512}
10513
10514static void copyLogo(const QCString &outputOption)
10515{
10516 QCString projectLogo = projectLogoFile();
10517 if (!projectLogo.isEmpty())
10518 {
10519 FileInfo fi(projectLogo.str());
10520 if (!fi.exists())
10521 {
10522 err("Project logo '{}' specified by PROJECT_LOGO does not exist!\n",projectLogo);
10523 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
10524 }
10525 else if (fi.isDir())
10526 {
10527 err("Project logo '{}' specified by PROJECT_LOGO is a directory, it has to be a file!\n",projectLogo);
10528 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
10529 }
10530 else
10531 {
10532 QCString destFileName = outputOption+"/"+fi.fileName();
10533 copyFile(projectLogo,destFileName);
10534 Doxygen::indexList->addImageFile(fi.fileName());
10535 }
10536 }
10537}
10538
10539static void copyIcon(const QCString &outputOption)
10540{
10541 QCString projectIcon = Config_getString(PROJECT_ICON);
10542 if (!projectIcon.isEmpty())
10543 {
10544 FileInfo fi(projectIcon.str());
10545 if (!fi.exists())
10546 {
10547 err("Project icon '{}' specified by PROJECT_ICON does not exist!\n",projectIcon);
10548 projectIcon = Config_updateString(PROJECT_ICON,""); // revert to the default
10549 }
10550 else if (fi.isDir())
10551 {
10552 err("Project icon '{}' specified by PROJECT_ICON is a directory, it has to be a file!\n",projectIcon);
10553 projectIcon = Config_updateString(PROJECT_ICON,""); // revert to the default
10554 }
10555 else
10556 {
10557 QCString destFileName = outputOption+"/"+fi.fileName();
10558 copyFile(projectIcon,destFileName);
10559 Doxygen::indexList->addImageFile(fi.fileName());
10560 }
10561 }
10562}
10563
10564static void copyExtraFiles(const StringVector &files,const QCString &filesOption,const QCString &outputOption)
10565{
10566 for (const auto &fileName : files)
10567 {
10568 if (!fileName.empty())
10569 {
10570 FileInfo fi(fileName);
10571 if (!fi.exists())
10572 {
10573 err("Extra file '{}' specified in {} does not exist!\n", fileName,filesOption);
10574 }
10575 else if (fi.isDir())
10576 {
10577 err("Extra file '{}' specified in {} is a directory, it has to be a file!\n", fileName,filesOption);
10578 }
10579 else
10580 {
10581 QCString destFileName = outputOption+"/"+fi.fileName();
10582 Doxygen::indexList->addImageFile(fi.fileName());
10583 copyFile(fileName, destFileName);
10584 }
10585 }
10586 }
10587}
10588
10589//----------------------------------------------------------------------------
10590
10592{
10593 for (const auto &fn : *Doxygen::inputNameLinkedMap)
10594 {
10595 struct FileEntry
10596 {
10597 FileEntry(const QCString &p,FileDef *fd) : path(p), fileDef(fd) {}
10598 QCString path;
10599 FileDef *fileDef;
10600 };
10601
10602 // collect the entry for which to compute the longest common prefix (LCP) of the path
10603 std::vector<FileEntry> fileEntries;
10604 for (const auto &fd : *fn)
10605 {
10606 if (!fd->isReference()) // skip external references
10607 {
10608 fileEntries.emplace_back(fd->getPath(),fd.get());
10609 }
10610 }
10611
10612 size_t size = fileEntries.size();
10613
10614 if (size==1) // name if unique, so diskname is simply the name
10615 {
10616 FileDef *fd = fileEntries[0].fileDef;
10617 fd->setDiskName(fn->fileName());
10618 }
10619 else if (size>1) // multiple occurrences of the same file name
10620 {
10621 // sort the array
10622 std::stable_sort(fileEntries.begin(),
10623 fileEntries.end(),
10624 [](const FileEntry &fe1,const FileEntry &fe2)
10625 { return qstricmp_sort(fe1.path,fe2.path)<0; }
10626 );
10627
10628 // since the entries are sorted, the common prefix of the whole array is same
10629 // as the common prefix between the first and last entry
10630 const FileEntry &first = fileEntries[0];
10631 const FileEntry &last = fileEntries[size-1];
10632 int first_path_size = static_cast<int>(first.path.size())-1; // -1 to skip trailing slash
10633 int last_path_size = static_cast<int>(last.path.size())-1; // -1 to skip trailing slash
10634 int j=0;
10635 int i=0;
10636 for (i=0;i<first_path_size && i<last_path_size;i++)
10637 {
10638 if (first.path[i]=='/') j=i;
10639 if (first.path[i]!=last.path[i]) break;
10640 }
10641 if (i==first_path_size && i<last_path_size && last.path[i]=='/')
10642 {
10643 // case first='some/path' and last='some/path/more' => match is 'some/path'
10644 j=first_path_size;
10645 }
10646 else if (i==last_path_size && i<first_path_size && first.path[i]=='/')
10647 {
10648 // case first='some/path/more' and last='some/path' => match is 'some/path'
10649 j=last_path_size;
10650 }
10651
10652 // add non-common part of the path to the name
10653 for (auto &fileEntry : fileEntries)
10654 {
10655 QCString prefix = fileEntry.path.right(fileEntry.path.length()-j-1);
10656 fileEntry.fileDef->setName(prefix+fn->fileName());
10657 //printf("!!!!!!!! non unique disk name=%s:%s\n",qPrint(prefix),fn->fileName());
10658 fileEntry.fileDef->setDiskName(prefix+fn->fileName());
10659 }
10660 }
10661 }
10662}
10663
10664
10665
10666//----------------------------------------------------------------------------
10667
10668static std::unique_ptr<OutlineParserInterface> getParserForFile(const QCString &fn)
10669{
10670 QCString fileName=fn;
10671 QCString extension;
10672 int sep = fileName.findRev('/');
10673 int ei = fileName.findRev('.');
10674 if (ei!=-1 && (sep==-1 || ei>sep)) // matches dir/file.ext but not dir.1/file
10675 {
10676 extension=fileName.right(fileName.length()-ei);
10677 }
10678 else
10679 {
10680 extension = ".no_extension";
10681 }
10682
10683 return Doxygen::parserManager->getOutlineParser(extension);
10684}
10685
10686static std::shared_ptr<Entry> parseFile(OutlineParserInterface &parser,
10687 FileDef *fd,const QCString &fn,
10688 ClangTUParser *clangParser,bool newTU)
10689{
10690 QCString fileName=fn;
10691 AUTO_TRACE("fileName={}",fileName);
10692 QCString extension;
10693 int ei = fileName.findRev('.');
10694 if (ei!=-1)
10695 {
10696 extension=fileName.right(fileName.length()-ei);
10697 }
10698 else
10699 {
10700 extension = ".no_extension";
10701 }
10702
10703 FileInfo fi(fileName.str());
10704 std::string preBuf;
10705
10706 if (Config_getBool(ENABLE_PREPROCESSING) &&
10707 parser.needsPreprocessing(extension))
10708 {
10709 Preprocessor preprocessor;
10710 const StringVector &includePath = Config_getList(INCLUDE_PATH);
10711 for (const auto &s : includePath)
10712 {
10713 std::string absPath = FileInfo(s).absFilePath();
10714 preprocessor.addSearchDir(absPath);
10715 }
10716 std::string inBuf;
10717 msg("Preprocessing {}...\n",fn);
10718 readInputFile(fileName,inBuf);
10719 addTerminalCharIfMissing(inBuf,'\n');
10720 preprocessor.processFile(fileName,inBuf,preBuf);
10721 }
10722 else // no preprocessing
10723 {
10724 msg("Reading {}...\n",fn);
10725 readInputFile(fileName,preBuf);
10726 addTerminalCharIfMissing(preBuf,'\n');
10727 }
10728
10729 std::string convBuf;
10730 convBuf.reserve(preBuf.size()+1024);
10731
10732 // convert multi-line C++ comments to C style comments
10733 convertCppComments(preBuf,convBuf,fileName.str());
10734
10735 std::shared_ptr<Entry> fileRoot = std::make_shared<Entry>();
10736 // use language parse to parse the file
10737 if (clangParser)
10738 {
10739 if (newTU) clangParser->parse();
10740 clangParser->switchToFile(fd);
10741 }
10742 parser.parseInput(fileName,convBuf.data(),fileRoot,clangParser);
10743 fileRoot->setFileDef(fd);
10744 return fileRoot;
10745}
10746
10747//! parse the list of input files
10748static void parseFilesMultiThreading(const std::shared_ptr<Entry> &root)
10749{
10750 AUTO_TRACE();
10751#if USE_LIBCLANG
10753 {
10754 StringUnorderedSet processedFiles;
10755
10756 // create a dictionary with files to process
10757 StringUnorderedSet filesToProcess;
10758 for (const auto &s : g_inputFiles)
10759 {
10760 filesToProcess.insert(s);
10761 }
10762
10763 std::mutex processedFilesLock;
10764 // process source files (and their include dependencies)
10765 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10766 msg("Processing input using {} threads.\n",numThreads);
10767 ThreadPool threadPool(numThreads);
10768 using FutureType = std::vector< std::shared_ptr<Entry> >;
10769 std::vector< std::future< FutureType > > results;
10770 for (const auto &s : g_inputFiles)
10771 {
10772 bool ambig = false;
10773 QCString qs = s;
10775 ASSERT(fd!=nullptr);
10776 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt::Cpp) // this is a source file
10777 {
10778 // lambda representing the work to executed by a thread
10779 auto processFile = [qs,&filesToProcess,&processedFilesLock,&processedFiles]() {
10780 bool ambig_l = false;
10781 std::vector< std::shared_ptr<Entry> > roots;
10783 auto clangParser = ClangParser::instance()->createTUParser(fd_l);
10784 auto parser = getParserForFile(qs);
10785 auto fileRoot { parseFile(*parser.get(),fd_l,qs,clangParser.get(),true) };
10786 roots.push_back(fileRoot);
10787
10788 // Now process any include files in the same translation unit
10789 // first. When libclang is used this is much more efficient.
10790 for (auto incFile : clangParser->filesInSameTU())
10791 {
10792 QCString qincFile = incFile;
10793 if (filesToProcess.find(incFile)!=filesToProcess.end())
10794 {
10795 bool needsToBeProcessed = false;
10796 {
10797 std::lock_guard<std::mutex> lock(processedFilesLock);
10798 needsToBeProcessed = processedFiles.find(incFile)==processedFiles.end();
10799 if (needsToBeProcessed) processedFiles.insert(incFile);
10800 }
10801 if (qincFile!=qs && needsToBeProcessed)
10802 {
10803 FileDef *ifd=findFileDef(Doxygen::inputNameLinkedMap,qincFile,ambig_l);
10804 if (ifd && !ifd->isReference())
10805 {
10806 //printf(" Processing %s in same translation unit as %s\n",incFile,qPrint(s));
10807 fileRoot = parseFile(*parser.get(),ifd,qincFile,clangParser.get(),false);
10808 roots.push_back(fileRoot);
10809 }
10810 }
10811 }
10812 }
10813 return roots;
10814 };
10815 // dispatch the work and collect the future results
10816 results.emplace_back(threadPool.queue(processFile));
10817 }
10818 }
10819 // synchronize with the Entry result lists produced and add them to the root
10820 for (auto &f : results)
10821 {
10822 auto l = f.get();
10823 for (auto &e : l)
10824 {
10825 root->moveToSubEntryAndKeep(e);
10826 }
10827 }
10828 // process remaining files
10829 results.clear();
10830 for (const auto &s : g_inputFiles)
10831 {
10832 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
10833 {
10834 // lambda representing the work to executed by a thread
10835 auto processFile = [s]() {
10836 bool ambig = false;
10837 QCString qs = s;
10838 std::vector< std::shared_ptr<Entry> > roots;
10840 auto parser { getParserForFile(qs) };
10841 bool useClang = getLanguageFromFileName(qs)==SrcLangExt::Cpp;
10842 if (useClang)
10843 {
10844 auto clangParser = ClangParser::instance()->createTUParser(fd);
10845 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
10846 roots.push_back(fileRoot);
10847 }
10848 else
10849 {
10850 auto fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10851 roots.push_back(fileRoot);
10852 }
10853 return roots;
10854 };
10855 results.emplace_back(threadPool.queue(processFile));
10856 }
10857 }
10858 // synchronize with the Entry result lists produced and add them to the root
10859 for (auto &f : results)
10860 {
10861 auto l = f.get();
10862 for (auto &e : l)
10863 {
10864 root->moveToSubEntryAndKeep(e);
10865 }
10866 }
10867 }
10868 else // normal processing
10869#endif
10870 {
10871 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10872 msg("Processing input using {} threads.\n",numThreads);
10873 ThreadPool threadPool(numThreads);
10874 using FutureType = std::shared_ptr<Entry>;
10875 std::vector< std::future< FutureType > > results;
10876 for (const auto &s : g_inputFiles)
10877 {
10878 // lambda representing the work to executed by a thread
10879 auto processFile = [s]() {
10880 bool ambig = false;
10881 QCString qs = s;
10883 auto parser = getParserForFile(qs);
10884 auto fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10885 return fileRoot;
10886 };
10887 // dispatch the work and collect the future results
10888 results.emplace_back(threadPool.queue(processFile));
10889 }
10890 // synchronize with the Entry results produced and add them to the root
10891 for (auto &f : results)
10892 {
10893 root->moveToSubEntryAndKeep(f.get());
10894 }
10895 }
10896}
10897
10898//! parse the list of input files
10899static void parseFilesSingleThreading(const std::shared_ptr<Entry> &root)
10900{
10901 AUTO_TRACE();
10902#if USE_LIBCLANG
10904 {
10905 StringUnorderedSet processedFiles;
10906
10907 // create a dictionary with files to process
10908 StringUnorderedSet filesToProcess;
10909 for (const auto &s : g_inputFiles)
10910 {
10911 filesToProcess.insert(s);
10912 }
10913
10914 // process source files (and their include dependencies)
10915 for (const auto &s : g_inputFiles)
10916 {
10917 bool ambig = false;
10918 QCString qs =s;
10920 ASSERT(fd!=nullptr);
10921 if (fd->isSource() && !fd->isReference() && getLanguageFromFileName(qs)==SrcLangExt::Cpp) // this is a source file
10922 {
10923 auto clangParser = ClangParser::instance()->createTUParser(fd);
10924 auto parser { getParserForFile(qs) };
10925 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
10926 root->moveToSubEntryAndKeep(fileRoot);
10927 processedFiles.insert(s);
10928
10929 // Now process any include files in the same translation unit
10930 // first. When libclang is used this is much more efficient.
10931 for (auto incFile : clangParser->filesInSameTU())
10932 {
10933 //printf(" file %s\n",qPrint(incFile));
10934 if (filesToProcess.find(incFile)!=filesToProcess.end() && // file need to be processed
10935 processedFiles.find(incFile)==processedFiles.end()) // and is not processed already
10936 {
10938 if (ifd && !ifd->isReference())
10939 {
10940 //printf(" Processing %s in same translation unit as %s\n",qPrint(incFile),qPrint(qs));
10941 fileRoot = parseFile(*parser.get(),ifd,incFile,clangParser.get(),false);
10942 root->moveToSubEntryAndKeep(fileRoot);
10943 processedFiles.insert(incFile);
10944 }
10945 }
10946 }
10947 }
10948 }
10949 // process remaining files
10950 for (const auto &s : g_inputFiles)
10951 {
10952 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
10953 {
10954 bool ambig = false;
10955 QCString qs = s;
10957 if (getLanguageFromFileName(qs)==SrcLangExt::Cpp) // not yet processed
10958 {
10959 auto clangParser = ClangParser::instance()->createTUParser(fd);
10960 auto parser { getParserForFile(qs) };
10961 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
10962 root->moveToSubEntryAndKeep(fileRoot);
10963 }
10964 else
10965 {
10966 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(qs) };
10967 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10968 root->moveToSubEntryAndKeep(fileRoot);
10969 }
10970 processedFiles.insert(s);
10971 }
10972 }
10973 }
10974 else // normal processing
10975#endif
10976 {
10977 for (const auto &s : g_inputFiles)
10978 {
10979 bool ambig = false;
10980 QCString qs = s;
10982 ASSERT(fd!=nullptr);
10983 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(qs) };
10984 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10985 root->moveToSubEntryAndKeep(std::move(fileRoot));
10986 }
10987 }
10988}
10989
10990// resolves a path that may include symlinks, if a recursive symlink is
10991// found an empty string is returned.
10992static std::string resolveSymlink(const std::string &path)
10993{
10994 int sepPos=0;
10995 int oldPos=0;
10996 StringUnorderedSet nonSymlinks;
10997 StringUnorderedSet known;
10998 QCString result(path);
10999 QCString oldPrefix = "/";
11000 do
11001 {
11002#if defined(_WIN32)
11003 // UNC path, skip server and share name
11004 if (sepPos==0 && (result.startsWith("//") || result.startsWith("\\\\")))
11005 sepPos = result.find('/',2);
11006 if (sepPos!=-1)
11007 sepPos = result.find('/',sepPos+1);
11008#else
11009 sepPos = result.find('/',sepPos+1);
11010#endif
11011 QCString prefix = sepPos==-1 ? result : result.left(sepPos);
11012 if (nonSymlinks.find(prefix.str())==nonSymlinks.end())
11013 {
11014 FileInfo fi(prefix.str());
11015 if (fi.isSymLink())
11016 {
11017 QCString target = fi.readLink();
11018 bool isRelative = FileInfo(target.str()).isRelative();
11019 if (isRelative)
11020 {
11021 target = Dir::cleanDirPath(oldPrefix.str()+"/"+target.str());
11022 }
11023 if (sepPos!=-1)
11024 {
11025 if (fi.isDir() && target.length()>0 && target.at(target.length()-1)!='/')
11026 {
11027 target+='/';
11028 }
11029 target+=result.mid(sepPos);
11030 }
11031 result = Dir::cleanDirPath(target.str());
11032 if (known.find(result.str())!=known.end()) return std::string(); // recursive symlink!
11033 known.insert(result.str());
11034 if (isRelative)
11035 {
11036 sepPos = oldPos;
11037 }
11038 else // link to absolute path
11039 {
11040 sepPos = 0;
11041 oldPrefix = "/";
11042 }
11043 }
11044 else
11045 {
11046 nonSymlinks.insert(prefix.str());
11047 oldPrefix = prefix;
11048 }
11049 oldPos = sepPos;
11050 }
11051 }
11052 while (sepPos!=-1);
11053 return Dir::cleanDirPath(result.str());
11054}
11055
11057
11058//----------------------------------------------------------------------------
11059// Read all files matching at least one pattern in 'patList' in the
11060// directory represented by 'fi'.
11061// The directory is read iff the recursiveFlag is set.
11062// The contents of all files is append to the input string
11063
11064static void readDir(FileInfo *fi,
11065 FileNameLinkedMap *fnMap,
11066 StringUnorderedSet *exclSet,
11067 const StringVector *patList,
11068 const StringVector *exclPatList,
11069 StringVector *resultList,
11070 StringUnorderedSet *resultSet,
11071 bool errorIfNotExist,
11072 bool recursive,
11073 StringUnorderedSet *killSet,
11074 StringUnorderedSet *paths
11075 )
11076{
11077 std::string dirName = fi->absFilePath();
11078 if (paths && !dirName.empty())
11079 {
11080 paths->insert(dirName);
11081 }
11082 //printf("%s isSymLink()=%d\n",qPrint(dirName),fi->isSymLink());
11083 if (fi->isSymLink())
11084 {
11085 dirName = resolveSymlink(dirName);
11086 if (dirName.empty())
11087 {
11088 //printf("RECURSIVE SYMLINK: %s\n",qPrint(dirName));
11089 return; // recursive symlink
11090 }
11091 }
11092
11093 if (g_pathsVisited.find(dirName)!=g_pathsVisited.end())
11094 {
11095 //printf("PATH ALREADY VISITED: %s\n",qPrint(dirName));
11096 return; // already visited path
11097 }
11098 g_pathsVisited.insert(dirName);
11099
11100 Dir dir(dirName);
11101 msg("Searching for files in directory {}\n", fi->absFilePath());
11102 //printf("killSet=%p count=%d\n",killSet,killSet ? (int)killSet->count() : -1);
11103
11104 StringVector dirResultList;
11105
11106 for (const auto &dirEntry : dir.iterator())
11107 {
11108 FileInfo cfi(dirEntry.path());
11109 auto checkPatterns = [&]() -> bool
11110 {
11111 return (patList==nullptr || patternMatch(cfi,*patList)) &&
11112 (exclPatList==nullptr || !patternMatch(cfi,*exclPatList)) &&
11113 (killSet==nullptr || killSet->find(cfi.absFilePath())==killSet->end());
11114 };
11115
11116 if (exclSet==nullptr || exclSet->find(cfi.absFilePath())==exclSet->end())
11117 { // file should not be excluded
11118 //printf("killSet->find(%s)\n",qPrint(cfi->absFilePath()));
11119 if (Config_getBool(EXCLUDE_SYMLINKS) && cfi.isSymLink())
11120 {
11121 }
11122 else if (!cfi.exists() || !cfi.isReadable())
11123 {
11124 if (errorIfNotExist && checkPatterns())
11125 {
11126 warn_uncond("source '{}' is not a readable file or directory... skipping.\n",cfi.absFilePath());
11127 }
11128 }
11129 else if (cfi.isFile() && checkPatterns())
11130 {
11131 std::string name=cfi.fileName();
11132 std::string path=cfi.dirPath()+"/";
11133 std::string fullName=path+name;
11134 if (fnMap)
11135 {
11136 auto fd = createFileDef(path,name);
11137 FileName *fn=nullptr;
11138 if (!name.empty())
11139 {
11140 fn = fnMap->add(name,fullName);
11141 fn->push_back(std::move(fd));
11142 }
11143 }
11144 dirResultList.push_back(fullName);
11145 if (resultSet) resultSet->insert(fullName);
11146 if (killSet) killSet->insert(fullName);
11147 }
11148 else if (recursive &&
11149 cfi.isDir() &&
11150 (exclPatList==nullptr || !patternMatch(cfi,*exclPatList)) &&
11151 cfi.fileName().at(0)!='.') // skip "." ".." and ".dir"
11152 {
11153 FileInfo acfi(cfi.absFilePath());
11154 readDir(&acfi,fnMap,exclSet,
11155 patList,exclPatList,&dirResultList,resultSet,errorIfNotExist,
11156 recursive,killSet,paths);
11157 }
11158 }
11159 }
11160 if (resultList && !dirResultList.empty())
11161 {
11162 // sort the resulting list to make the order platform independent.
11163 std::stable_sort(dirResultList.begin(),
11164 dirResultList.end(),
11165 [](const auto &f1,const auto &f2) { return qstricmp_sort(f1.c_str(),f2.c_str())<0; });
11166
11167 // append the sorted results to resultList
11168 resultList->insert(resultList->end(), dirResultList.begin(), dirResultList.end());
11169 }
11170}
11171
11172
11173//----------------------------------------------------------------------------
11174// read a file or all files in a directory and append their contents to the
11175// input string. The names of the files are appended to the 'fiList' list.
11176
11178 FileNameLinkedMap *fnMap,
11179 StringUnorderedSet *exclSet,
11180 const StringVector *patList,
11181 const StringVector *exclPatList,
11182 StringVector *resultList,
11183 StringUnorderedSet *resultSet,
11184 bool recursive,
11185 bool errorIfNotExist,
11186 StringUnorderedSet *killSet,
11187 StringUnorderedSet *paths
11188 )
11189{
11190 //printf("killSet count=%d\n",killSet ? (int)killSet->size() : -1);
11191 // strip trailing slashes
11192 if (s.isEmpty()) return;
11193
11194 g_pathsVisited.clear();
11195
11196 FileInfo fi(s.str());
11197 //printf("readFileOrDirectory(%s)\n",s);
11198 {
11199 if (exclSet==nullptr || exclSet->find(fi.absFilePath())==exclSet->end())
11200 {
11201 if (Config_getBool(EXCLUDE_SYMLINKS) && fi.isSymLink())
11202 {
11203 }
11204 else if (!fi.exists() || !fi.isReadable())
11205 {
11206 if (errorIfNotExist)
11207 {
11208 warn_uncond("source '{}' is not a readable file or directory... skipping.\n",s);
11209 }
11210 }
11211 else if (fi.isFile())
11212 {
11213 std::string dirPath = fi.dirPath(true);
11214 std::string filePath = fi.absFilePath();
11215 if (paths && !dirPath.empty())
11216 {
11217 paths->insert(dirPath);
11218 }
11219 //printf("killSet.find(%s)=%d\n",qPrint(fi.absFilePath()),killSet.find(fi.absFilePath())!=killSet.end());
11220 if (killSet==nullptr || killSet->find(filePath)==killSet->end())
11221 {
11222 std::string name=fi.fileName();
11223 if (fnMap)
11224 {
11225 auto fd = createFileDef(dirPath+"/",name);
11226 if (!name.empty())
11227 {
11228 FileName *fn = fnMap->add(name,filePath);
11229 fn->push_back(std::move(fd));
11230 }
11231 }
11232 if (resultList || resultSet)
11233 {
11234 if (resultList) resultList->push_back(filePath);
11235 if (resultSet) resultSet->insert(filePath);
11236 }
11237
11238 if (killSet) killSet->insert(fi.absFilePath());
11239 }
11240 }
11241 else if (fi.isDir()) // readable dir
11242 {
11243 readDir(&fi,fnMap,exclSet,patList,
11244 exclPatList,resultList,resultSet,errorIfNotExist,
11245 recursive,killSet,paths);
11246 }
11247 }
11248 }
11249}
11250
11251//----------------------------------------------------------------------------
11252
11254{
11255 QCString anchor;
11257 {
11258 MemberDef *md = toMemberDef(d);
11259 anchor=":"+md->anchor();
11260 }
11261 QCString scope;
11262 QCString fn = d->getOutputFileBase();
11265 {
11266 scope = fn;
11267 }
11268 t << "REPLACE INTO symbols (symbol_id,scope_id,name,file,line) VALUES('"
11269 << fn+anchor << "','"
11270 << scope << "','"
11271 << d->name() << "','"
11272 << d->getDefFileName() << "','"
11273 << d->getDefLine()
11274 << "');\n";
11275}
11276
11277static void dumpSymbolMap()
11278{
11279 std::ofstream f = Portable::openOutputStream("symbols.sql");
11280 if (f.is_open())
11281 {
11282 TextStream t(&f);
11283 for (const auto &[name,symList] : *Doxygen::symbolMap)
11284 {
11285 for (const auto &def : symList)
11286 {
11287 dumpSymbol(t,def);
11288 }
11289 }
11290 }
11291}
11292
11293// print developer options of Doxygen
11294static void devUsage()
11295{
11297 msg("Developer parameters:\n");
11298 msg(" -m dump symbol map\n");
11299 msg(" -b making messages output unbuffered\n");
11300 msg(" -c <file> process input file as a comment block and produce HTML output\n");
11301#if ENABLE_TRACING
11302 msg(" -t [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout)\n");
11303 msg(" -t_time [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout),\n"
11304 " and include time and thread information\n");
11305#endif
11306 msg(" -d <level> enable a debug level, such as (multiple invocations of -d are possible):\n");
11308}
11309
11310
11311//----------------------------------------------------------------------------
11312// print the version of Doxygen
11313
11314static void version(const bool extended)
11315{
11317 QCString versionString = getFullVersion();
11318 msg("{}\n",versionString);
11319 if (extended)
11320 {
11321 QCString extVers;
11322 if (!extVers.isEmpty()) extVers+= ", ";
11323 extVers += "sqlite3 ";
11324 extVers += sqlite3_libversion();
11325#if USE_LIBCLANG
11326 if (!extVers.isEmpty()) extVers+= ", ";
11327 extVers += "clang support ";
11328 extVers += CLANG_VERSION_STRING;
11329#endif
11330 if (!extVers.isEmpty())
11331 {
11332 int lastComma = extVers.findRev(',');
11333 if (lastComma != -1) extVers = extVers.replace(lastComma,1," and");
11334 msg(" with {}.\n",extVers);
11335 }
11336 }
11337}
11338
11339//----------------------------------------------------------------------------
11340// print the usage of Doxygen
11341
11342static void usage(const QCString &name,const QCString &versionString)
11343{
11345 msg("Doxygen version {0}\nCopyright Dimitri van Heesch 1997-2025\n\n"
11346 "You can use Doxygen in a number of ways:\n\n"
11347 "1) Use Doxygen to generate a template configuration file*:\n"
11348 " {1} [-s] -g [configName]\n\n"
11349 "2) Use Doxygen to update an old configuration file*:\n"
11350 " {1} [-s] -u [configName]\n\n"
11351 "3) Use Doxygen to generate documentation using an existing "
11352 "configuration file*:\n"
11353 " {1} [configName]\n\n"
11354 "4) Use Doxygen to generate a template file controlling the layout of the\n"
11355 " generated documentation:\n"
11356 " {1} -l [layoutFileName]\n\n"
11357 " In case layoutFileName is omitted DoxygenLayout.xml will be used as filename.\n"
11358 " If - is used for layoutFileName Doxygen will write to standard output.\n\n"
11359 "5) Use Doxygen to generate a template style sheet file for RTF, HTML or Latex.\n"
11360 " RTF: {1} -w rtf styleSheetFile\n"
11361 " HTML: {1} -w html headerFile footerFile styleSheetFile [configFile]\n"
11362 " LaTeX: {1} -w latex headerFile footerFile styleSheetFile [configFile]\n\n"
11363 "6) Use Doxygen to generate a rtf extensions file\n"
11364 " {1} -e rtf extensionsFile\n\n"
11365 " If - is used for extensionsFile Doxygen will write to standard output.\n\n"
11366 "7) Use Doxygen to compare the used configuration file with the template configuration file\n"
11367 " {1} -x [configFile]\n\n"
11368 " Use Doxygen to compare the used configuration file with the template configuration file\n"
11369 " without replacing the environment variables or CMake type replacement variables\n"
11370 " {1} -x_noenv [configFile]\n\n"
11371 "8) Use Doxygen to show a list of built-in emojis.\n"
11372 " {1} -f emoji outputFileName\n\n"
11373 " If - is used for outputFileName Doxygen will write to standard output.\n\n"
11374 "*) If -s is specified the comments of the configuration items in the config file will be omitted.\n"
11375 " If configName is omitted 'Doxyfile' will be used as a default.\n"
11376 " If - is used for configFile Doxygen will write / read the configuration to /from standard output / input.\n\n"
11377 "If -q is used for a Doxygen documentation run, Doxygen will see this as if QUIET=YES has been set.\n\n"
11378 "-v print version string, -V print extended version information\n"
11379 "-h,-? prints usage help information\n"
11380 "{1} -d prints additional usage flags for debugging purposes\n",versionString,name);
11381}
11382
11383//----------------------------------------------------------------------------
11384// read the argument of option 'c' from the comment argument list and
11385// update the option index 'optInd'.
11386
11387static const char *getArg(int argc,char **argv,int &optInd)
11388{
11389 char *s=nullptr;
11390 if (qstrlen(&argv[optInd][2])>0)
11391 s=&argv[optInd][2];
11392 else if (optInd+1<argc && argv[optInd+1][0]!='-')
11393 s=argv[++optInd];
11394 return s;
11395}
11396
11397//----------------------------------------------------------------------------
11398
11399/** @brief /dev/null outline parser */
11401{
11402 public:
11403 void parseInput(const QCString &/* file */, const char * /* buf */,const std::shared_ptr<Entry> &, ClangTUParser*) override {}
11404 bool needsPreprocessing(const QCString &) const override { return FALSE; }
11405 void parsePrototype(const QCString &) override {}
11406};
11407
11408
11409template<class T> std::function< std::unique_ptr<T>() > make_parser_factory()
11410{
11411 return []() { return std::make_unique<T>(); };
11412}
11413
11415{
11416 initResources();
11417 QCString lang = Portable::getenv("LC_ALL");
11418 if (!lang.isEmpty()) Portable::setenv("LANG",lang);
11419 std::setlocale(LC_ALL,"");
11420 std::setlocale(LC_CTYPE,"C"); // to get isspace(0xA0)==0, needed for UTF-8
11421 std::setlocale(LC_NUMERIC,"C");
11422
11424
11448
11449 // register any additional parsers here...
11450
11452
11453#if USE_LIBCLANG
11455#endif
11464 Doxygen::pageLinkedMap = new PageLinkedMap; // all doc pages
11465 Doxygen::exampleLinkedMap = new PageLinkedMap; // all examples
11466 //Doxygen::tagDestinationDict.setAutoDelete(TRUE);
11468
11469 // initialization of these globals depends on
11470 // configuration switches so we need to postpone these
11471 Doxygen::globalScope = nullptr;
11480
11481}
11482
11514
11515void readConfiguration(int argc, char **argv)
11516{
11517 QCString versionString = getFullVersion();
11518
11519 // helper that calls \a func to write to file \a fileName via a TextStream
11520 auto writeFile = [](const char *fileName,std::function<void(TextStream&)> func) -> bool
11521 {
11522 std::ofstream f;
11523 if (openOutputFile(fileName,f))
11524 {
11525 TextStream t(&f);
11526 func(t);
11527 return true;
11528 }
11529 return false;
11530 };
11531
11532
11533 /**************************************************************************
11534 * Handle arguments *
11535 **************************************************************************/
11536
11537 int optInd=1;
11538 QCString configName;
11539 QCString traceName;
11540 bool genConfig=false;
11541 bool shortList=false;
11542 bool traceTiming=false;
11544 bool updateConfig=false;
11545 bool quiet = false;
11546 while (optInd<argc && argv[optInd][0]=='-' &&
11547 (isalpha(argv[optInd][1]) || argv[optInd][1]=='?' ||
11548 argv[optInd][1]=='-')
11549 )
11550 {
11551 switch(argv[optInd][1])
11552 {
11553 case 'g':
11554 {
11555 genConfig=TRUE;
11556 }
11557 break;
11558 case 'l':
11559 {
11560 QCString layoutName;
11561 if (optInd+1>=argc)
11562 {
11563 layoutName="DoxygenLayout.xml";
11564 }
11565 else
11566 {
11567 layoutName=argv[optInd+1];
11568 }
11569 writeDefaultLayoutFile(layoutName);
11571 exit(0);
11572 }
11573 break;
11574 case 'c':
11575 if (optInd+1>=argc) // no file name given
11576 {
11577 msg("option \"-c\" is missing the file name to read\n");
11578 devUsage();
11580 exit(1);
11581 }
11582 else
11583 {
11584 g_commentFileName=argv[optInd+1];
11585 optInd++;
11586 }
11587 g_singleComment=true;
11588 quiet=true;
11589 break;
11590 case 'd':
11591 {
11592 QCString debugLabel=getArg(argc,argv,optInd);
11593 if (debugLabel.isEmpty())
11594 {
11595 devUsage();
11597 exit(0);
11598 }
11599 int retVal = Debug::setFlagStr(debugLabel);
11600 if (!retVal)
11601 {
11602 msg("option \"-d\" has unknown debug specifier: \"{}\".\n",debugLabel);
11603 devUsage();
11605 exit(1);
11606 }
11607 }
11608 break;
11609 case 't':
11610 {
11611#if ENABLE_TRACING
11612 if (!strcmp(argv[optInd]+1,"t_time"))
11613 {
11614 traceTiming = true;
11615 }
11616 else if (!strcmp(argv[optInd]+1,"t"))
11617 {
11618 traceTiming = false;
11619 }
11620 else
11621 {
11622 err("option should be \"-t\" or \"-t_time\", found: \"{}\".\n",argv[optInd]);
11624 exit(1);
11625 }
11626 if (optInd+1>=argc || argv[optInd+1][0] == '-') // no file name given
11627 {
11628 traceName="stdout";
11629 }
11630 else
11631 {
11632 traceName=argv[optInd+1];
11633 optInd++;
11634 }
11635#else
11636 err("support for option \"-t\" has not been compiled in (use a debug build or a release build with tracing enabled).\n");
11638 exit(1);
11639#endif
11640 }
11641 break;
11642 case 'x':
11643 if (!strcmp(argv[optInd]+1,"x_noenv")) diffList=Config::CompareMode::CompressedNoEnv;
11644 else if (!strcmp(argv[optInd]+1,"x")) diffList=Config::CompareMode::Compressed;
11645 else
11646 {
11647 err("option should be \"-x\" or \"-x_noenv\", found: \"{}\".\n",argv[optInd]);
11649 exit(1);
11650 }
11651 break;
11652 case 's':
11653 shortList=TRUE;
11654 break;
11655 case 'u':
11656 updateConfig=TRUE;
11657 break;
11658 case 'e':
11659 {
11660 QCString formatName=getArg(argc,argv,optInd);
11661 if (formatName.isEmpty())
11662 {
11663 err("option \"-e\" is missing format specifier rtf.\n");
11665 exit(1);
11666 }
11667 if (qstricmp(formatName.data(),"rtf")==0)
11668 {
11669 if (optInd+1>=argc)
11670 {
11671 err("option \"-e rtf\" is missing an extensions file name\n");
11673 exit(1);
11674 }
11675 writeFile(argv[optInd+1],RTFGenerator::writeExtensionsFile);
11677 exit(0);
11678 }
11679 err("option \"-e\" has invalid format specifier.\n");
11681 exit(1);
11682 }
11683 break;
11684 case 'f':
11685 {
11686 QCString listName=getArg(argc,argv,optInd);
11687 if (listName.isEmpty())
11688 {
11689 err("option \"-f\" is missing list specifier.\n");
11691 exit(1);
11692 }
11693 if (qstricmp(listName.data(),"emoji")==0)
11694 {
11695 if (optInd+1>=argc)
11696 {
11697 err("option \"-f emoji\" is missing an output file name\n");
11699 exit(1);
11700 }
11701 writeFile(argv[optInd+1],[](TextStream &t) { EmojiEntityMapper::instance().writeEmojiFile(t); });
11703 exit(0);
11704 }
11705 err("option \"-f\" has invalid list specifier.\n");
11707 exit(1);
11708 }
11709 break;
11710 case 'w':
11711 {
11712 QCString formatName=getArg(argc,argv,optInd);
11713 if (formatName.isEmpty())
11714 {
11715 err("option \"-w\" is missing format specifier rtf, html or latex\n");
11717 exit(1);
11718 }
11719 if (qstricmp(formatName.data(),"rtf")==0)
11720 {
11721 if (optInd+1>=argc)
11722 {
11723 err("option \"-w rtf\" is missing a style sheet file name\n");
11725 exit(1);
11726 }
11727 if (!writeFile(argv[optInd+1],RTFGenerator::writeStyleSheetFile))
11728 {
11729 err("error opening RTF style sheet file {}!\n",argv[optInd+1]);
11731 exit(1);
11732 }
11734 exit(0);
11735 }
11736 else if (qstricmp(formatName.data(),"html")==0)
11737 {
11738 Config::init();
11739 if (optInd+4<argc || FileInfo("Doxyfile").exists() || FileInfo("doxyfile").exists())
11740 // explicit config file mentioned or default found on disk
11741 {
11742 QCString df = optInd+4<argc ? argv[optInd+4] : (FileInfo("Doxyfile").exists() ? QCString("Doxyfile") : QCString("doxyfile"));
11743 if (!Config::parse(df)) // parse the config file
11744 {
11745 err("error opening or reading configuration file {}!\n",argv[optInd+4]);
11747 exit(1);
11748 }
11749 }
11750 if (optInd+3>=argc)
11751 {
11752 err("option \"-w html\" does not have enough arguments\n");
11754 exit(1);
11755 }
11759 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11760 writeFile(argv[optInd+1],[&](TextStream &t) { HtmlGenerator::writeHeaderFile(t,argv[optInd+3]); });
11761 writeFile(argv[optInd+2],HtmlGenerator::writeFooterFile);
11762 writeFile(argv[optInd+3],HtmlGenerator::writeStyleSheetFile);
11764 exit(0);
11765 }
11766 else if (qstricmp(formatName.data(),"latex")==0)
11767 {
11768 Config::init();
11769 if (optInd+4<argc || FileInfo("Doxyfile").exists() || FileInfo("doxyfile").exists())
11770 {
11771 QCString df = optInd+4<argc ? argv[optInd+4] : (FileInfo("Doxyfile").exists() ? QCString("Doxyfile") : QCString("doxyfile"));
11772 if (!Config::parse(df))
11773 {
11774 err("error opening or reading configuration file {}!\n",argv[optInd+4]);
11776 exit(1);
11777 }
11778 }
11779 if (optInd+3>=argc)
11780 {
11781 err("option \"-w latex\" does not have enough arguments\n");
11783 exit(1);
11784 }
11788 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11789 writeFile(argv[optInd+1],LatexGenerator::writeHeaderFile);
11790 writeFile(argv[optInd+2],LatexGenerator::writeFooterFile);
11791 writeFile(argv[optInd+3],LatexGenerator::writeStyleSheetFile);
11793 exit(0);
11794 }
11795 else
11796 {
11797 err("Illegal format specifier \"{}\": should be one of rtf, html or latex\n",formatName);
11799 exit(1);
11800 }
11801 }
11802 break;
11803 case 'm':
11805 break;
11806 case 'v':
11807 version(false);
11809 exit(0);
11810 break;
11811 case 'V':
11812 version(true);
11814 exit(0);
11815 break;
11816 case '-':
11817 if (qstrcmp(&argv[optInd][2],"help")==0)
11818 {
11819 usage(argv[0],versionString);
11820 exit(0);
11821 }
11822 else if (qstrcmp(&argv[optInd][2],"version")==0)
11823 {
11824 version(false);
11826 exit(0);
11827 }
11828 else if ((qstrcmp(&argv[optInd][2],"Version")==0) ||
11829 (qstrcmp(&argv[optInd][2],"VERSION")==0))
11830 {
11831 version(true);
11833 exit(0);
11834 }
11835 else
11836 {
11837 err("Unknown option \"-{}\"\n",&argv[optInd][1]);
11838 usage(argv[0],versionString);
11839 exit(1);
11840 }
11841 break;
11842 case 'b':
11843 setvbuf(stdout,nullptr,_IONBF,0);
11844 break;
11845 case 'q':
11846 quiet = true;
11847 break;
11848 case 'h':
11849 case '?':
11850 usage(argv[0],versionString);
11851 exit(0);
11852 break;
11853 default:
11854 err("Unknown option \"-{:c}\"\n",argv[optInd][1]);
11855 usage(argv[0],versionString);
11856 exit(1);
11857 }
11858 optInd++;
11859 }
11860
11861 /**************************************************************************
11862 * Parse or generate the config file *
11863 **************************************************************************/
11864
11865 initTracing(traceName.data(),traceTiming);
11866 TRACE("Doxygen version used: {}",getFullVersion());
11867 Config::init();
11868
11869 FileInfo configFileInfo1("Doxyfile"),configFileInfo2("doxyfile");
11870 if (optInd>=argc)
11871 {
11872 if (configFileInfo1.exists())
11873 {
11874 configName="Doxyfile";
11875 }
11876 else if (configFileInfo2.exists())
11877 {
11878 configName="doxyfile";
11879 }
11880 else if (genConfig)
11881 {
11882 configName="Doxyfile";
11883 }
11884 else
11885 {
11886 err("Doxyfile not found and no input file specified!\n");
11887 usage(argv[0],versionString);
11888 exit(1);
11889 }
11890 }
11891 else
11892 {
11893 FileInfo fi(argv[optInd]);
11894 if (fi.exists() || qstrcmp(argv[optInd],"-")==0 || genConfig)
11895 {
11896 configName=argv[optInd];
11897 }
11898 else
11899 {
11900 err("configuration file {} not found!\n",argv[optInd]);
11901 usage(argv[0],versionString);
11902 exit(1);
11903 }
11904 }
11905
11906 if (genConfig)
11907 {
11908 generateConfigFile(configName,shortList);
11910 exit(0);
11911 }
11912
11913 if (!Config::parse(configName,updateConfig,diffList))
11914 {
11915 err("could not open or read configuration file {}!\n",configName);
11917 exit(1);
11918 }
11919
11920 if (diffList!=Config::CompareMode::Full)
11921 {
11923 compareDoxyfile(diffList);
11925 exit(0);
11926 }
11927
11928 if (updateConfig)
11929 {
11931 generateConfigFile(configName,shortList,TRUE);
11933 exit(0);
11934 }
11935
11936 /* Perlmod wants to know the path to the config file.*/
11937 FileInfo configFileInfo(configName.str());
11938 setPerlModDoxyfile(configFileInfo.absFilePath());
11939
11940 /* handle -q option */
11941 if (quiet) Config_updateBool(QUIET,TRUE);
11942}
11943
11944/** check and resolve config options */
11946{
11947 AUTO_TRACE();
11948
11953}
11954
11955/** adjust globals that depend on configuration settings. */
11957{
11958 AUTO_TRACE();
11959 Doxygen::globalNamespaceDef = createNamespaceDef("<globalScope>",1,1,"<globalScope>");
11969
11970 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11971
11972 /* Set the global html file extension. */
11973 Doxygen::htmlFileExtension = Config_getString(HTML_FILE_EXTENSION);
11974
11975
11977 Config_getBool(CALLER_GRAPH) ||
11978 Config_getBool(REFERENCES_RELATION) ||
11979 Config_getBool(REFERENCED_BY_RELATION);
11980
11981 /**************************************************************************
11982 * Add custom extension mappings
11983 **************************************************************************/
11984
11985 const StringVector &extMaps = Config_getList(EXTENSION_MAPPING);
11986 for (const auto &mapping : extMaps)
11987 {
11988 QCString mapStr = mapping;
11989 int i=mapStr.find('=');
11990 if (i==-1)
11991 {
11992 continue;
11993 }
11994 else
11995 {
11996 QCString ext = mapStr.left(i).stripWhiteSpace().lower();
11997 QCString language = mapStr.mid(i+1).stripWhiteSpace().lower();
11998 if (ext.isEmpty() || language.isEmpty())
11999 {
12000 continue;
12001 }
12002
12003 if (!updateLanguageMapping(ext,language))
12004 {
12005 err("Failed to map file extension '{}' to unsupported language '{}'.\n"
12006 "Check the EXTENSION_MAPPING setting in the config file.\n",
12007 ext,language);
12008 }
12009 else
12010 {
12011 msg("Adding custom extension mapping: '{}' will be treated as language '{}'\n",
12012 ext,language);
12013 }
12014 }
12015 }
12016 // create input file exncodings
12017
12018 // check INPUT_ENCODING
12019 void *cd = portable_iconv_open("UTF-8",Config_getString(INPUT_ENCODING).data());
12020 if (cd==reinterpret_cast<void *>(-1))
12021 {
12022 term("unsupported character conversion: '{}'->'UTF-8': {}\n"
12023 "Check the 'INPUT_ENCODING' setting in the config file!\n",
12024 Config_getString(INPUT_ENCODING),strerror(errno));
12025 }
12026 else
12027 {
12029 }
12030
12031 // check and split INPUT_FILE_ENCODING
12032 const StringVector &fileEncod = Config_getList(INPUT_FILE_ENCODING);
12033 for (const auto &mapping : fileEncod)
12034 {
12035 QCString mapStr = mapping;
12036 int i=mapStr.find('=');
12037 if (i==-1)
12038 {
12039 continue;
12040 }
12041 else
12042 {
12043 QCString pattern = mapStr.left(i).stripWhiteSpace().lower();
12044 QCString encoding = mapStr.mid(i+1).stripWhiteSpace().lower();
12045 if (pattern.isEmpty() || encoding.isEmpty())
12046 {
12047 continue;
12048 }
12049 cd = portable_iconv_open("UTF-8",encoding.data());
12050 if (cd==reinterpret_cast<void *>(-1))
12051 {
12052 term("unsupported character conversion: '{}'->'UTF-8': {}\n"
12053 "Check the 'INPUT_FILE_ENCODING' setting in the config file!\n",
12054 encoding,strerror(errno));
12055 }
12056 else
12057 {
12059 }
12060
12061 Doxygen::inputFileEncodingList.emplace_back(pattern, encoding);
12062 }
12063 }
12064
12065 // add predefined macro name to a dictionary
12066 const StringVector &expandAsDefinedList =Config_getList(EXPAND_AS_DEFINED);
12067 for (const auto &s : expandAsDefinedList)
12068 {
12070 }
12071
12072 // read aliases and store them in a dictionary
12073 readAliases();
12074
12075 // store number of spaces in a tab into Doxygen::spaces
12076 int tabSize = Config_getInt(TAB_SIZE);
12077 Doxygen::spaces.resize(tabSize);
12078 for (int sp=0; sp<tabSize; sp++) Doxygen::spaces.at(sp)=' ';
12079 Doxygen::spaces.at(tabSize)='\0';
12080}
12081
12082#ifdef HAS_SIGNALS
12083static void stopDoxygen(int)
12084{
12085 signal(SIGINT,SIG_DFL); // Re-register signal handler for default action
12086 Dir thisDir;
12087 msg("Cleaning up...\n");
12088 if (!Doxygen::filterDBFileName.isEmpty())
12089 {
12090 thisDir.remove(Doxygen::filterDBFileName.str());
12091 }
12092 killpg(0,SIGINT);
12094 exitTracing();
12095 exit(1);
12096}
12097#endif
12098
12099static void writeTagFile()
12100{
12101 QCString generateTagFile = Config_getString(GENERATE_TAGFILE);
12102 if (generateTagFile.isEmpty()) return;
12103
12104 std::ofstream f = Portable::openOutputStream(generateTagFile);
12105 if (!f.is_open())
12106 {
12107 err("cannot open tag file {} for writing\n", generateTagFile);
12108 return;
12109 }
12110 TextStream tagFile(&f);
12111 tagFile << "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>\n";
12112 tagFile << "<tagfile doxygen_version=\"" << getDoxygenVersion() << "\"";
12113 std::string gitVersion = getGitVersion();
12114 if (!gitVersion.empty())
12115 {
12116 tagFile << " doxygen_gitid=\"" << gitVersion << "\"";
12117 }
12118 tagFile << ">\n";
12119
12120 // for each file
12121 for (const auto &fn : *Doxygen::inputNameLinkedMap)
12122 {
12123 for (const auto &fd : *fn)
12124 {
12125 if (fd->isLinkableInProject()) fd->writeTagFile(tagFile);
12126 }
12127 }
12128 // for each class
12129 for (const auto &cd : *Doxygen::classLinkedMap)
12130 {
12131 ClassDefMutable *cdm = toClassDefMutable(cd.get());
12132 if (cdm && cdm->isLinkableInProject())
12133 {
12134 cdm->writeTagFile(tagFile);
12135 }
12136 }
12137 // for each concept
12138 for (const auto &cd : *Doxygen::conceptLinkedMap)
12139 {
12140 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
12141 if (cdm && cdm->isLinkableInProject())
12142 {
12143 cdm->writeTagFile(tagFile);
12144 }
12145 }
12146 // for each namespace
12147 for (const auto &nd : *Doxygen::namespaceLinkedMap)
12148 {
12150 if (ndm && nd->isLinkableInProject())
12151 {
12152 ndm->writeTagFile(tagFile);
12153 }
12154 }
12155 // for each group
12156 for (const auto &gd : *Doxygen::groupLinkedMap)
12157 {
12158 if (gd->isLinkableInProject()) gd->writeTagFile(tagFile);
12159 }
12160 // for each module
12161 for (const auto &mod : ModuleManager::instance().modules())
12162 {
12163 if (mod->isLinkableInProject()) mod->writeTagFile(tagFile);
12164 }
12165 // for each page
12166 for (const auto &pd : *Doxygen::pageLinkedMap)
12167 {
12168 if (pd->isLinkableInProject()) pd->writeTagFile(tagFile);
12169 }
12170 // for requirements
12172 // for each directory
12173 for (const auto &dd : *Doxygen::dirLinkedMap)
12174 {
12175 if (dd->isLinkableInProject()) dd->writeTagFile(tagFile);
12176 }
12177 if (Doxygen::mainPage) Doxygen::mainPage->writeTagFile(tagFile);
12178
12179 tagFile << "</tagfile>\n";
12180}
12181
12182static void exitDoxygen() noexcept
12183{
12184 if (!g_successfulRun) // premature exit
12185 {
12186 Dir thisDir;
12187 msg("Exiting...\n");
12188 if (!Doxygen::filterDBFileName.isEmpty())
12189 {
12190 thisDir.remove(Doxygen::filterDBFileName.str());
12191 }
12192 }
12193}
12194
12195static QCString createOutputDirectory(const QCString &baseDirName,
12196 const QCString &formatDirName,
12197 const char *defaultDirName)
12198{
12199 QCString result = formatDirName;
12200 if (result.isEmpty())
12201 {
12202 result = baseDirName + defaultDirName;
12203 }
12204 else if (formatDirName[0]!='/' && (formatDirName.length()==1 || formatDirName[1]!=':'))
12205 {
12206 result.prepend(baseDirName+"/");
12207 }
12208 Dir formatDir(result.str());
12209 if (!formatDir.exists() && !formatDir.mkdir(result.str()))
12210 {
12211 term("Could not create output directory {}\n", result);
12212 }
12213 return result;
12214}
12215
12217{
12218 StringUnorderedSet killSet;
12219
12220 const StringVector &exclPatterns = Config_getList(EXCLUDE_PATTERNS);
12221 bool alwaysRecursive = Config_getBool(RECURSIVE);
12222 StringUnorderedSet excludeNameSet;
12223
12224 // gather names of all files in the include path
12225 g_s.begin("Searching for include files...\n");
12226 killSet.clear();
12227 const StringVector &includePathList = Config_getList(INCLUDE_PATH);
12228 for (const auto &s : includePathList)
12229 {
12230 size_t plSize = Config_getList(INCLUDE_FILE_PATTERNS).size();
12231 const StringVector &pl = plSize==0 ? Config_getList(FILE_PATTERNS) :
12232 Config_getList(INCLUDE_FILE_PATTERNS);
12233 readFileOrDirectory(s, // s
12235 nullptr, // exclSet
12236 &pl, // patList
12237 &exclPatterns, // exclPatList
12238 nullptr, // resultList
12239 nullptr, // resultSet
12240 false, // INCLUDE_PATH isn't recursive
12241 TRUE, // errorIfNotExist
12242 &killSet); // killSet
12243 }
12244 g_s.end();
12245
12246 g_s.begin("Searching for example files...\n");
12247 killSet.clear();
12248 const StringVector &examplePathList = Config_getList(EXAMPLE_PATH);
12249 for (const auto &s : examplePathList)
12250 {
12251 readFileOrDirectory(s, // s
12253 nullptr, // exclSet
12254 &Config_getList(EXAMPLE_PATTERNS), // patList
12255 nullptr, // exclPatList
12256 nullptr, // resultList
12257 nullptr, // resultSet
12258 (alwaysRecursive || Config_getBool(EXAMPLE_RECURSIVE)), // recursive
12259 TRUE, // errorIfNotExist
12260 &killSet); // killSet
12261 }
12262 g_s.end();
12263
12264 g_s.begin("Searching for images...\n");
12265 killSet.clear();
12266 const StringVector &imagePathList=Config_getList(IMAGE_PATH);
12267 for (const auto &s : imagePathList)
12268 {
12269 readFileOrDirectory(s, // s
12271 nullptr, // exclSet
12272 nullptr, // patList
12273 nullptr, // exclPatList
12274 nullptr, // resultList
12275 nullptr, // resultSet
12276 alwaysRecursive, // recursive
12277 TRUE, // errorIfNotExist
12278 &killSet); // killSet
12279 }
12280 g_s.end();
12281
12282 g_s.begin("Searching for dot files...\n");
12283 killSet.clear();
12284 const StringVector &dotFileList=Config_getList(DOTFILE_DIRS);
12285 for (const auto &s : dotFileList)
12286 {
12287 readFileOrDirectory(s, // s
12289 nullptr, // exclSet
12290 nullptr, // patList
12291 nullptr, // exclPatList
12292 nullptr, // resultList
12293 nullptr, // resultSet
12294 alwaysRecursive, // recursive
12295 TRUE, // errorIfNotExist
12296 &killSet); // killSet
12297 }
12298 g_s.end();
12299
12300 g_s.begin("Searching for msc files...\n");
12301 killSet.clear();
12302 const StringVector &mscFileList=Config_getList(MSCFILE_DIRS);
12303 for (const auto &s : mscFileList)
12304 {
12305 readFileOrDirectory(s, // s
12307 nullptr, // exclSet
12308 nullptr, // patList
12309 nullptr, // exclPatList
12310 nullptr, // resultList
12311 nullptr, // resultSet
12312 alwaysRecursive, // recursive
12313 TRUE, // errorIfNotExist
12314 &killSet); // killSet
12315 }
12316 g_s.end();
12317
12318 g_s.begin("Searching for dia files...\n");
12319 killSet.clear();
12320 const StringVector &diaFileList=Config_getList(DIAFILE_DIRS);
12321 for (const auto &s : diaFileList)
12322 {
12323 readFileOrDirectory(s, // s
12325 nullptr, // exclSet
12326 nullptr, // patList
12327 nullptr, // exclPatList
12328 nullptr, // resultList
12329 nullptr, // resultSet
12330 alwaysRecursive, // recursive
12331 TRUE, // errorIfNotExist
12332 &killSet); // killSet
12333 }
12334 g_s.end();
12335
12336 g_s.begin("Searching for plantuml files...\n");
12337 killSet.clear();
12338 const StringVector &plantUmlFileList=Config_getList(PLANTUMLFILE_DIRS);
12339 for (const auto &s : plantUmlFileList)
12340 {
12341 readFileOrDirectory(s, // s
12343 nullptr, // exclSet
12344 nullptr, // patList
12345 nullptr, // exclPatList
12346 nullptr, // resultList
12347 nullptr, // resultSet
12348 alwaysRecursive, // recursive
12349 TRUE, // errorIfNotExist
12350 &killSet); // killSet
12351 }
12352 g_s.end();
12353
12354 g_s.begin("Searching for files to exclude\n");
12355 const StringVector &excludeList = Config_getList(EXCLUDE);
12356 for (const auto &s : excludeList)
12357 {
12358 readFileOrDirectory(s, // s
12359 nullptr, // fnDict
12360 nullptr, // exclSet
12361 &Config_getList(FILE_PATTERNS), // patList
12362 nullptr, // exclPatList
12363 nullptr, // resultList
12364 &excludeNameSet, // resultSet
12365 alwaysRecursive, // recursive
12366 FALSE); // errorIfNotExist
12367 }
12368 g_s.end();
12369
12370 /**************************************************************************
12371 * Determine Input Files *
12372 **************************************************************************/
12373
12374 g_s.begin("Searching INPUT for files to process...\n");
12375 killSet.clear();
12376 Doxygen::inputPaths.clear();
12377 const StringVector &inputList=Config_getList(INPUT);
12378 for (const auto &s : inputList)
12379 {
12380 QCString path = s;
12381 size_t l = path.length();
12382 if (l>0)
12383 {
12384 // strip trailing slashes
12385 if (path.at(l-1)=='\\' || path.at(l-1)=='/') path=path.left(l-1);
12386
12388 path, // s
12390 &excludeNameSet, // exclSet
12391 &Config_getList(FILE_PATTERNS), // patList
12392 &exclPatterns, // exclPatList
12393 &g_inputFiles, // resultList
12394 nullptr, // resultSet
12395 alwaysRecursive, // recursive
12396 TRUE, // errorIfNotExist
12397 &killSet, // killSet
12398 &Doxygen::inputPaths); // paths
12399 }
12400 }
12401
12402 // Sort the FileDef objects by full path to get a predictable ordering over multiple runs
12403 std::stable_sort(Doxygen::inputNameLinkedMap->begin(),
12405 [](const auto &f1,const auto &f2)
12406 {
12407 return qstricmp_sort(f1->fullName(),f2->fullName())<0;
12408 });
12409 for (auto &fileName : *Doxygen::inputNameLinkedMap)
12410 {
12411 if (fileName->size()>1)
12412 {
12413 std::stable_sort(fileName->begin(),fileName->end(),[](const auto &f1,const auto &f2)
12414 {
12415 return qstricmp_sort(f1->absFilePath(),f2->absFilePath())<0;
12416 });
12417 }
12418 }
12419 if (Doxygen::inputNameLinkedMap->empty())
12420 {
12421 warn_uncond("No files to be processed, please check your settings, in particular INPUT, FILE_PATTERNS, and RECURSIVE\n");
12422 }
12423 g_s.end();
12424}
12425
12426
12428{
12429 if (Config_getBool(MARKDOWN_SUPPORT))
12430 {
12431 QCString mdfileAsMainPage = Config_getString(USE_MDFILE_AS_MAINPAGE);
12432 if (mdfileAsMainPage.isEmpty()) return;
12433 FileInfo fi(mdfileAsMainPage.data());
12434 if (!fi.exists())
12435 {
12436 warn_uncond("Specified markdown mainpage '{}' does not exist\n",mdfileAsMainPage);
12437 return;
12438 }
12439 bool ambig = false;
12440 if (findFileDef(Doxygen::inputNameLinkedMap,fi.absFilePath(),ambig)==nullptr)
12441 {
12442 warn_uncond("Specified markdown mainpage '{}' has not been defined as input file\n",mdfileAsMainPage);
12443 return;
12444 }
12445 }
12446}
12447
12449{
12450 AUTO_TRACE();
12451 std::atexit(exitDoxygen);
12452
12453 Portable::correctPath(Config_getList(EXTERNAL_TOOL_PATH));
12454
12455#if USE_LIBCLANG
12456 Doxygen::clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING);
12457#endif
12458
12459 // we would like to show the versionString earlier, but we first have to handle the configuration file
12460 // to know the value of the QUIET setting.
12461 QCString versionString = getFullVersion();
12462 msg("Doxygen version used: {}\n",versionString);
12463
12465
12466 /**************************************************************************
12467 * Make sure the output directory exists
12468 **************************************************************************/
12469 QCString outputDirectory = Config_getString(OUTPUT_DIRECTORY);
12470 if (!g_singleComment)
12471 {
12472 if (outputDirectory.isEmpty())
12473 {
12474 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,Dir::currentDirPath());
12475 }
12476 else
12477 {
12478 Dir dir(outputDirectory.str());
12479 if (!dir.exists())
12480 {
12482 if (!dir.mkdir(outputDirectory.str()))
12483 {
12484 term("tag OUTPUT_DIRECTORY: Output directory '{}' does not "
12485 "exist and cannot be created\n",outputDirectory);
12486 }
12487 else
12488 {
12489 msg("Notice: Output directory '{}' does not exist. "
12490 "I have created it for you.\n", outputDirectory);
12491 }
12492 dir.setPath(outputDirectory.str());
12493 }
12494 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,dir.absPath());
12495 }
12496 }
12497 AUTO_TRACE_ADD("outputDirectory={}",outputDirectory);
12498
12499 /**************************************************************************
12500 * Initialize global lists and dictionaries
12501 **************************************************************************/
12502
12503#ifdef HAS_SIGNALS
12504 signal(SIGINT, stopDoxygen);
12505#endif
12506
12507 uint32_t pid = Portable::pid();
12508 Doxygen::filterDBFileName.sprintf("doxygen_filterdb_%d.tmp",pid);
12509 Doxygen::filterDBFileName.prepend(outputDirectory+"/");
12510
12511 /**************************************************************************
12512 * Check/create output directories *
12513 **************************************************************************/
12514
12515 bool generateHtml = Config_getBool(GENERATE_HTML);
12516 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
12517 bool generateXml = Config_getBool(GENERATE_XML);
12518 bool generateLatex = Config_getBool(GENERATE_LATEX);
12519 bool generateRtf = Config_getBool(GENERATE_RTF);
12520 bool generateMan = Config_getBool(GENERATE_MAN);
12521 bool generateSql = Config_getBool(GENERATE_SQLITE3);
12522 QCString htmlOutput;
12523 QCString docbookOutput;
12524 QCString xmlOutput;
12525 QCString latexOutput;
12526 QCString rtfOutput;
12527 QCString manOutput;
12528 QCString sqlOutput;
12529
12530 if (!g_singleComment)
12531 {
12532 if (generateHtml)
12533 {
12534 htmlOutput = createOutputDirectory(outputDirectory,Config_getString(HTML_OUTPUT),"/html");
12535 Config_updateString(HTML_OUTPUT,htmlOutput);
12536
12537 QCString sitemapUrl = Config_getString(SITEMAP_URL);
12538 bool generateSitemap = !sitemapUrl.isEmpty();
12539 if (generateSitemap && !sitemapUrl.endsWith("/"))
12540 {
12541 Config_updateString(SITEMAP_URL,sitemapUrl+"/");
12542 }
12543
12544 // add HTML indexers that are enabled
12545 bool generateHtmlHelp = Config_getBool(GENERATE_HTMLHELP);
12546 bool generateEclipseHelp = Config_getBool(GENERATE_ECLIPSEHELP);
12547 bool generateQhp = Config_getBool(GENERATE_QHP);
12548 bool generateTreeView = Config_getBool(GENERATE_TREEVIEW);
12549 bool generateDocSet = Config_getBool(GENERATE_DOCSET);
12550 if (generateEclipseHelp) Doxygen::indexList->addIndex<EclipseHelp>();
12551 if (generateHtmlHelp) Doxygen::indexList->addIndex<HtmlHelp>();
12552 if (generateQhp) Doxygen::indexList->addIndex<Qhp>();
12553 if (generateSitemap) Doxygen::indexList->addIndex<Sitemap>();
12554 if (generateTreeView) Doxygen::indexList->addIndex<FTVHelp>(TRUE);
12555 if (generateDocSet) Doxygen::indexList->addIndex<DocSets>();
12556 Doxygen::indexList->addIndex<Crawlmap>();
12557 Doxygen::indexList->initialize();
12558 }
12559
12560 if (generateDocbook)
12561 {
12562 docbookOutput = createOutputDirectory(outputDirectory,Config_getString(DOCBOOK_OUTPUT),"/docbook");
12563 Config_updateString(DOCBOOK_OUTPUT,docbookOutput);
12564 }
12565
12566 if (generateXml)
12567 {
12568 xmlOutput = createOutputDirectory(outputDirectory,Config_getString(XML_OUTPUT),"/xml");
12569 Config_updateString(XML_OUTPUT,xmlOutput);
12570 }
12571
12572 if (generateLatex)
12573 {
12574 latexOutput = createOutputDirectory(outputDirectory,Config_getString(LATEX_OUTPUT), "/latex");
12575 Config_updateString(LATEX_OUTPUT,latexOutput);
12576 }
12577
12578 if (generateRtf)
12579 {
12580 rtfOutput = createOutputDirectory(outputDirectory,Config_getString(RTF_OUTPUT),"/rtf");
12581 Config_updateString(RTF_OUTPUT,rtfOutput);
12582 }
12583
12584 if (generateMan)
12585 {
12586 manOutput = createOutputDirectory(outputDirectory,Config_getString(MAN_OUTPUT),"/man");
12587 Config_updateString(MAN_OUTPUT,manOutput);
12588 }
12589
12590 if (generateSql)
12591 {
12592 sqlOutput = createOutputDirectory(outputDirectory,Config_getString(SQLITE3_OUTPUT),"/sqlite3");
12593 Config_updateString(SQLITE3_OUTPUT,sqlOutput);
12594 }
12595 }
12596
12597 if (Config_getBool(HAVE_DOT))
12598 {
12599 QCString curFontPath = Config_getString(DOT_FONTPATH);
12600 if (curFontPath.isEmpty())
12601 {
12602 Portable::getenv("DOTFONTPATH");
12603 QCString newFontPath = ".";
12604 if (!curFontPath.isEmpty())
12605 {
12606 newFontPath+=Portable::pathListSeparator();
12607 newFontPath+=curFontPath;
12608 }
12609 Portable::setenv("DOTFONTPATH",qPrint(newFontPath));
12610 }
12611 else
12612 {
12613 Portable::setenv("DOTFONTPATH",qPrint(curFontPath));
12614 }
12615 }
12616
12617 /**************************************************************************
12618 * Handle layout file *
12619 **************************************************************************/
12620
12622 QCString layoutFileName = Config_getString(LAYOUT_FILE);
12623 bool defaultLayoutUsed = FALSE;
12624 if (layoutFileName.isEmpty())
12625 {
12626 layoutFileName = Config_updateString(LAYOUT_FILE,"DoxygenLayout.xml");
12627 defaultLayoutUsed = TRUE;
12628 }
12629 AUTO_TRACE_ADD("defaultLayoutUsed={}, layoutFileName={}",defaultLayoutUsed,layoutFileName);
12630
12631 FileInfo fi(layoutFileName.str());
12632 if (fi.exists())
12633 {
12634 msg("Parsing layout file {}...\n",layoutFileName);
12635 LayoutDocManager::instance().parse(layoutFileName);
12636 }
12637 else if (!defaultLayoutUsed)
12638 {
12639 warn_uncond("failed to open layout file '{}' for reading! Using default settings.\n",layoutFileName);
12640 }
12641 printLayout();
12642
12643 /**************************************************************************
12644 * Read and preprocess input *
12645 **************************************************************************/
12646
12647 // prevent search in the output directories
12648 StringVector exclPatterns = Config_getList(EXCLUDE_PATTERNS);
12649 if (generateHtml) exclPatterns.push_back(htmlOutput.str());
12650 if (generateDocbook) exclPatterns.push_back(docbookOutput.str());
12651 if (generateXml) exclPatterns.push_back(xmlOutput.str());
12652 if (generateLatex) exclPatterns.push_back(latexOutput.str());
12653 if (generateRtf) exclPatterns.push_back(rtfOutput.str());
12654 if (generateMan) exclPatterns.push_back(manOutput.str());
12655 Config_updateList(EXCLUDE_PATTERNS,exclPatterns);
12656
12657 if (!g_singleComment)
12658 {
12660
12662 }
12663
12664 // Notice: the order of the function calls below is very important!
12665
12666 if (generateHtml && !Config_getBool(USE_MATHJAX))
12667 {
12669 }
12670 if (generateRtf)
12671 {
12673 }
12674 if (generateDocbook)
12675 {
12677 }
12678
12680
12681 /**************************************************************************
12682 * Handle Tag Files *
12683 **************************************************************************/
12684
12685 std::shared_ptr<Entry> root = std::make_shared<Entry>();
12686
12687 if (!g_singleComment)
12688 {
12689 msg("Reading and parsing tag files\n");
12690 const StringVector &tagFileList = Config_getList(TAGFILES);
12691 for (const auto &s : tagFileList)
12692 {
12693 readTagFile(root,s.c_str());
12694 }
12695 }
12696
12697 /**************************************************************************
12698 * Parse source files *
12699 **************************************************************************/
12700
12701 addSTLSupport(root);
12702
12703 g_s.begin("Parsing files\n");
12704 if (g_singleComment)
12705 {
12706 //printf("Parsing comment %s\n",qPrint(g_commentFileName));
12707 if (g_commentFileName=="-")
12708 {
12709 std::string text = fileToString(g_commentFileName).str();
12710 addTerminalCharIfMissing(text,'\n');
12711 generateHtmlForComment("stdin.md",text);
12712 }
12713 else if (FileInfo(g_commentFileName.str()).isFile())
12714 {
12715 std::string text;
12717 addTerminalCharIfMissing(text,'\n');
12719 }
12720 else
12721 {
12722 }
12724 exit(0);
12725 }
12726 else
12727 {
12728 if (Config_getInt(NUM_PROC_THREADS)==1)
12729 {
12731 }
12732 else
12733 {
12735 }
12736 }
12737 g_s.end();
12738
12739 /**************************************************************************
12740 * Gather information *
12741 **************************************************************************/
12742
12743 g_s.begin("Building macro definition list...\n");
12745 g_s.end();
12746
12747 g_s.begin("Building group list...\n");
12748 buildGroupList(root.get());
12749 organizeSubGroups(root.get());
12750 g_s.end();
12751
12752 g_s.begin("Building directory list...\n");
12754 findDirDocumentation(root.get());
12755 g_s.end();
12756
12757 g_s.begin("Building namespace list...\n");
12758 buildNamespaceList(root.get());
12759 findUsingDirectives(root.get());
12760 g_s.end();
12761
12762 g_s.begin("Building file list...\n");
12763 buildFileList(root.get());
12764 g_s.end();
12765
12766 g_s.begin("Building class list...\n");
12767 buildClassList(root.get());
12768 g_s.end();
12769
12770 g_s.begin("Building concept list...\n");
12771 buildConceptList(root.get());
12772 g_s.end();
12773
12774 // build list of using declarations here (global list)
12775 buildListOfUsingDecls(root.get());
12776 g_s.end();
12777
12778 g_s.begin("Computing nesting relations for classes...\n");
12780 g_s.end();
12781 // 1.8.2-20121111: no longer add nested classes to the group as well
12782 //distributeClassGroupRelations();
12783
12784 // calling buildClassList may result in cached relations that
12785 // become invalid after resolveClassNestingRelations(), that's why
12786 // we need to clear the cache here
12788 // we don't need the list of using declaration anymore
12789 g_usingDeclarations.clear();
12790
12791 g_s.begin("Associating documentation with classes...\n");
12792 buildClassDocList(root.get());
12793 g_s.end();
12794
12795 g_s.begin("Associating documentation with concepts...\n");
12796 buildConceptDocList(root.get());
12798 g_s.end();
12799
12800 g_s.begin("Associating documentation with modules...\n");
12801 findModuleDocumentation(root.get());
12802 g_s.end();
12803
12804 g_s.begin("Building example list...\n");
12805 buildExampleList(root.get());
12806 g_s.end();
12807
12808 g_s.begin("Searching for enumerations...\n");
12809 findEnums(root.get());
12810 g_s.end();
12811
12812 // Since buildVarList calls isVarWithConstructor
12813 // and this calls getResolvedClass we need to process
12814 // typedefs first so the relations between classes via typedefs
12815 // are properly resolved. See bug 536385 for an example.
12816 g_s.begin("Searching for documented typedefs...\n");
12817 buildTypedefList(root.get());
12818 g_s.end();
12819
12820 if (Config_getBool(OPTIMIZE_OUTPUT_SLICE))
12821 {
12822 g_s.begin("Searching for documented sequences...\n");
12823 buildSequenceList(root.get());
12824 g_s.end();
12825
12826 g_s.begin("Searching for documented dictionaries...\n");
12827 buildDictionaryList(root.get());
12828 g_s.end();
12829 }
12830
12831 g_s.begin("Searching for members imported via using declarations...\n");
12832 // this should be after buildTypedefList in order to properly import
12833 // used typedefs
12834 findUsingDeclarations(root.get(),TRUE); // do for python packages first
12835 findUsingDeclarations(root.get(),FALSE); // then the rest
12836 g_s.end();
12837
12838 g_s.begin("Searching for included using directives...\n");
12840 g_s.end();
12841
12842 g_s.begin("Searching for documented variables...\n");
12843 buildVarList(root.get());
12844 g_s.end();
12845
12846 g_s.begin("Building interface member list...\n");
12847 buildInterfaceAndServiceList(root.get()); // UNO IDL
12848
12849 g_s.begin("Building member list...\n"); // using class info only !
12850 buildFunctionList(root.get());
12851 g_s.end();
12852
12853 g_s.begin("Searching for friends...\n");
12854 findFriends();
12855 g_s.end();
12856
12857 g_s.begin("Searching for documented defines...\n");
12858 findDefineDocumentation(root.get());
12859 g_s.end();
12860
12861 g_s.begin("Computing class inheritance relations...\n");
12862 findClassEntries(root.get());
12864 g_s.end();
12865
12866 g_s.begin("Computing class usage relations...\n");
12868 g_s.end();
12869
12870 if (Config_getBool(INLINE_SIMPLE_STRUCTS))
12871 {
12872 g_s.begin("Searching for tag less structs...\n");
12874 g_s.end();
12875 }
12876
12877 g_s.begin("Flushing cached template relations that have become invalid...\n");
12879 g_s.end();
12880
12881 g_s.begin("Warn for undocumented namespaces...\n");
12883 g_s.end();
12884
12885 g_s.begin("Computing class relations...\n");
12888 if (Config_getBool(OPTIMIZE_OUTPUT_VHDL))
12889 {
12891 }
12893 g_classEntries.clear();
12894 g_s.end();
12895
12896 g_s.begin("Add enum values to enums...\n");
12897 addEnumValuesToEnums(root.get());
12898 findEnumDocumentation(root.get());
12899 g_s.end();
12900
12901 g_s.begin("Searching for member function documentation...\n");
12902 findObjCMethodDefinitions(root.get());
12903 findMemberDocumentation(root.get()); // may introduce new members !
12904 findUsingDeclImports(root.get()); // may introduce new members !
12905 g_usingClassMap.clear();
12909 g_s.end();
12910
12911 // moved to after finding and copying documentation,
12912 // as this introduces new members see bug 722654
12913 g_s.begin("Creating members for template instances...\n");
12915 g_s.end();
12916
12917 g_s.begin("Building page list...\n");
12918 buildPageList(root.get());
12919 g_s.end();
12920
12921 g_s.begin("Building requirements list...\n");
12922 buildRequirementsList(root.get());
12923 g_s.end();
12924
12925 g_s.begin("Search for main page...\n");
12926 findMainPage(root.get());
12927 findMainPageTagFiles(root.get());
12928 g_s.end();
12929
12930 g_s.begin("Computing page relations...\n");
12931 computePageRelations(root.get());
12933 g_s.end();
12934
12935 g_s.begin("Determining the scope of groups...\n");
12936 findGroupScope(root.get());
12937 g_s.end();
12938
12939 g_s.begin("Computing module relations...\n");
12940 auto &mm = ModuleManager::instance();
12941 mm.resolvePartitions();
12942 mm.resolveImports();
12943 mm.collectExportedSymbols();
12944 g_s.end();
12945
12946 auto memberNameComp = [](const MemberNameLinkedMap::Ptr &n1,const MemberNameLinkedMap::Ptr &n2)
12947 {
12948 return qstricmp_sort(n1->memberName().data()+getPrefixIndex(n1->memberName()),
12949 n2->memberName().data()+getPrefixIndex(n2->memberName())
12950 )<0;
12951 };
12952
12953 auto classComp = [](const ClassLinkedMap::Ptr &c1,const ClassLinkedMap::Ptr &c2)
12954 {
12955 if (Config_getBool(SORT_BY_SCOPE_NAME))
12956 {
12957 return qstricmp_sort(c1->name(), c2->name())<0;
12958 }
12959 else
12960 {
12961 int i = qstricmp_sort(c1->className(), c2->className());
12962 return i==0 ? qstricmp_sort(c1->name(), c2->name())<0 : i<0;
12963 }
12964 };
12965
12966 auto namespaceComp = [](const NamespaceLinkedMap::Ptr &n1,const NamespaceLinkedMap::Ptr &n2)
12967 {
12968 return qstricmp_sort(n1->name(),n2->name())<0;
12969 };
12970
12971 auto conceptComp = [](const ConceptLinkedMap::Ptr &c1,const ConceptLinkedMap::Ptr &c2)
12972 {
12973 return qstricmp_sort(c1->name(),c2->name())<0;
12974 };
12975
12976 g_s.begin("Sorting lists...\n");
12977 std::stable_sort(Doxygen::memberNameLinkedMap->begin(),
12979 memberNameComp);
12980 std::stable_sort(Doxygen::functionNameLinkedMap->begin(),
12982 memberNameComp);
12983 std::stable_sort(Doxygen::hiddenClassLinkedMap->begin(),
12985 classComp);
12986 std::stable_sort(Doxygen::classLinkedMap->begin(),
12988 classComp);
12989 std::stable_sort(Doxygen::conceptLinkedMap->begin(),
12991 conceptComp);
12992 std::stable_sort(Doxygen::namespaceLinkedMap->begin(),
12994 namespaceComp);
12995 g_s.end();
12996
12997 g_s.begin("Determining which enums are documented\n");
12999 g_s.end();
13000
13001 g_s.begin("Computing member relations...\n");
13004 g_s.end();
13005
13006 g_s.begin("Building full member lists recursively...\n");
13008 g_s.end();
13009
13010 g_s.begin("Adding members to member groups.\n");
13012 g_s.end();
13013
13014 if (Config_getBool(DISTRIBUTE_GROUP_DOC))
13015 {
13016 g_s.begin("Distributing member group documentation.\n");
13018 g_s.end();
13019 }
13020
13021 g_s.begin("Computing member references...\n");
13023 g_s.end();
13024
13025 if (Config_getBool(INHERIT_DOCS))
13026 {
13027 g_s.begin("Inheriting documentation...\n");
13029 g_s.end();
13030 }
13031
13032
13033 // compute the shortest possible names of all files
13034 // without losing the uniqueness of the file names.
13035 g_s.begin("Generating disk names...\n");
13037 g_s.end();
13038
13039 g_s.begin("Adding source references...\n");
13041 g_s.end();
13042
13043 g_s.begin("Adding xrefitems...\n");
13046 g_s.end();
13047
13048 g_s.begin("Adding requirements...\n");
13051 g_s.end();
13052
13053 g_s.begin("Sorting member lists...\n");
13055 g_s.end();
13056
13057 g_s.begin("Setting anonymous enum type...\n");
13059 g_s.end();
13060
13061 g_s.begin("Computing dependencies between directories...\n");
13063 g_s.end();
13064
13065 g_s.begin("Generating citations page...\n");
13067 g_s.end();
13068
13069 g_s.begin("Counting members...\n");
13070 countMembers();
13071 g_s.end();
13072
13073 g_s.begin("Counting data structures...\n");
13075 g_s.end();
13076
13077 g_s.begin("Resolving user defined references...\n");
13079 g_s.end();
13080
13081 g_s.begin("Finding anchors and sections in the documentation...\n");
13083 g_s.end();
13084
13085 g_s.begin("Transferring function references...\n");
13087 g_s.end();
13088
13089 g_s.begin("Combining using relations...\n");
13091 g_s.end();
13092
13094 g_s.begin("Adding members to index pages...\n");
13096 addToIndices();
13097 g_s.end();
13098
13099 g_s.begin("Correcting members for VHDL...\n");
13101 g_s.end();
13102
13103 g_s.begin("Computing tooltip texts...\n");
13105 g_s.end();
13106
13107 if (Config_getBool(SORT_GROUP_NAMES))
13108 {
13109 std::stable_sort(Doxygen::groupLinkedMap->begin(),
13111 [](const auto &g1,const auto &g2)
13112 { return g1->groupTitle() < g2->groupTitle(); });
13113
13114 for (const auto &gd : *Doxygen::groupLinkedMap)
13115 {
13116 gd->sortSubGroups();
13117 }
13118 }
13119
13120 printNavTree(root.get(),0);
13122}
13123
13125{
13126 AUTO_TRACE();
13127 /**************************************************************************
13128 * Initialize output generators *
13129 **************************************************************************/
13130
13131 /// add extra languages for which we can only produce syntax highlighted code
13133
13134 //// dump all symbols
13135 if (g_dumpSymbolMap)
13136 {
13137 dumpSymbolMap();
13138 exit(0);
13139 }
13140
13141 bool generateHtml = Config_getBool(GENERATE_HTML);
13142 bool generateLatex = Config_getBool(GENERATE_LATEX);
13143 bool generateMan = Config_getBool(GENERATE_MAN);
13144 bool generateRtf = Config_getBool(GENERATE_RTF);
13145 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
13146
13147
13149 if (generateHtml)
13150 {
13154 }
13155 if (generateLatex)
13156 {
13159 }
13160 if (generateDocbook)
13161 {
13164 }
13165 if (generateMan)
13166 {
13167 g_outputList->add<ManGenerator>();
13169 }
13170 if (generateRtf)
13171 {
13172 g_outputList->add<RTFGenerator>();
13174 }
13175 if (Config_getBool(USE_HTAGS))
13176 {
13178 QCString htmldir = Config_getString(HTML_OUTPUT);
13179 if (!Htags::execute(htmldir))
13180 err("USE_HTAGS is YES but htags(1) failed. \n");
13181 else if (!Htags::loadFilemap(htmldir))
13182 err("htags(1) ended normally but failed to load the filemap. \n");
13183 }
13184
13185 /**************************************************************************
13186 * Generate documentation *
13187 **************************************************************************/
13188
13189 g_s.begin("Generating style sheet...\n");
13190 //printf("writing style info\n");
13191 g_outputList->writeStyleInfo(0); // write first part
13192 g_s.end();
13193
13194 bool searchEngine = Config_getBool(SEARCHENGINE);
13195 bool serverBasedSearch = Config_getBool(SERVER_BASED_SEARCH);
13196
13197 g_s.begin("Generating search indices...\n");
13198 if (searchEngine && !serverBasedSearch && generateHtml)
13199 {
13201 }
13202
13203 // generate search indices (need to do this before writing other HTML
13204 // pages as these contain a drop down menu with options depending on
13205 // what categories we find in this function.
13206 if (generateHtml && searchEngine)
13207 {
13208 QCString searchDirName = Config_getString(HTML_OUTPUT)+"/search";
13209 Dir searchDir(searchDirName.str());
13210 if (!searchDir.exists() && !searchDir.mkdir(searchDirName.str()))
13211 {
13212 term("Could not create search results directory '{}' $PWD='{}'\n",
13213 searchDirName,Dir::currentDirPath());
13214 }
13215 HtmlGenerator::writeSearchData(searchDirName);
13216 if (!serverBasedSearch) // client side search index
13217 {
13219 }
13220 }
13221 g_s.end();
13222
13223 // copy static stuff
13224 if (generateHtml)
13225 {
13227 copyLogo(Config_getString(HTML_OUTPUT));
13228 copyIcon(Config_getString(HTML_OUTPUT));
13229 copyExtraFiles(Config_getList(HTML_EXTRA_FILES),"HTML_EXTRA_FILES",Config_getString(HTML_OUTPUT));
13230 }
13231 if (generateLatex)
13232 {
13234 copyLogo(Config_getString(LATEX_OUTPUT));
13235 copyIcon(Config_getString(LATEX_OUTPUT));
13236 copyExtraFiles(Config_getList(LATEX_EXTRA_FILES),"LATEX_EXTRA_FILES",Config_getString(LATEX_OUTPUT));
13237 }
13238 if (generateDocbook)
13239 {
13240 copyLogo(Config_getString(DOCBOOK_OUTPUT));
13241 copyIcon(Config_getString(DOCBOOK_OUTPUT));
13242 }
13243 if (generateRtf)
13244 {
13245 copyLogo(Config_getString(RTF_OUTPUT));
13246 copyIcon(Config_getString(RTF_OUTPUT));
13247 copyExtraFiles(Config_getList(RTF_EXTRA_FILES),"RTF_EXTRA_FILES",Config_getString(RTF_OUTPUT));
13248 }
13249
13251 if (fm.hasFormulas() && generateHtml
13252 && !Config_getBool(USE_MATHJAX))
13253 {
13254 g_s.begin("Generating images for formulas in HTML...\n");
13255 fm.generateImages(Config_getString(HTML_OUTPUT), Config_getEnum(HTML_FORMULA_FORMAT)==HTML_FORMULA_FORMAT_t::svg ?
13257 g_s.end();
13258 }
13259 if (fm.hasFormulas() && generateRtf)
13260 {
13261 g_s.begin("Generating images for formulas in RTF...\n");
13263 g_s.end();
13264 }
13265
13266 if (fm.hasFormulas() && generateDocbook)
13267 {
13268 g_s.begin("Generating images for formulas in Docbook...\n");
13270 g_s.end();
13271 }
13272
13273 g_s.begin("Generating example documentation...\n");
13275 g_s.end();
13276
13277 g_s.begin("Generating file sources...\n");
13279 g_s.end();
13280
13281 g_s.begin("Generating file documentation...\n");
13283 g_s.end();
13284
13285 g_s.begin("Generating page documentation...\n");
13287 g_s.end();
13288
13289 g_s.begin("Generating group documentation...\n");
13291 g_s.end();
13292
13293 g_s.begin("Generating class documentation...\n");
13295 g_s.end();
13296
13297 g_s.begin("Generating concept documentation...\n");
13299 g_s.end();
13300
13301 g_s.begin("Generating module documentation...\n");
13303 g_s.end();
13304
13305 g_s.begin("Generating namespace documentation...\n");
13307 g_s.end();
13308
13309 if (Config_getBool(GENERATE_LEGEND))
13310 {
13311 g_s.begin("Generating graph info page...\n");
13313 g_s.end();
13314 }
13315
13316 g_s.begin("Generating directory documentation...\n");
13318 g_s.end();
13319
13320 if (g_outputList->size()>0)
13321 {
13323 }
13324
13325 g_s.begin("finalizing index lists...\n");
13326 Doxygen::indexList->finalize();
13327 g_s.end();
13328
13329 g_s.begin("writing tag file...\n");
13330 writeTagFile();
13331 g_s.end();
13332
13333 if (Config_getBool(GENERATE_XML))
13334 {
13335 g_s.begin("Generating XML output...\n");
13337 generateXML();
13339 g_s.end();
13340 }
13341 if (Config_getBool(GENERATE_SQLITE3))
13342 {
13343 g_s.begin("Generating SQLITE3 output...\n");
13345 g_s.end();
13346 }
13347
13348 if (Config_getBool(GENERATE_AUTOGEN_DEF))
13349 {
13350 g_s.begin("Generating AutoGen DEF output...\n");
13351 generateDEF();
13352 g_s.end();
13353 }
13354 if (Config_getBool(GENERATE_PERLMOD))
13355 {
13356 g_s.begin("Generating Perl module output...\n");
13358 g_s.end();
13359 }
13360 if (generateHtml && searchEngine && serverBasedSearch)
13361 {
13362 g_s.begin("Generating search index\n");
13363 if (Doxygen::searchIndex.kind()==SearchIndexIntf::Internal) // write own search index
13364 {
13366 Doxygen::searchIndex.write(Config_getString(HTML_OUTPUT)+"/search/search.idx");
13367 }
13368 else // write data for external search index
13369 {
13371 QCString searchDataFile = Config_getString(SEARCHDATA_FILE);
13372 if (searchDataFile.isEmpty())
13373 {
13374 searchDataFile="searchdata.xml";
13375 }
13376 if (!Portable::isAbsolutePath(searchDataFile.data()))
13377 {
13378 searchDataFile.prepend(Config_getString(OUTPUT_DIRECTORY)+"/");
13379 }
13380 Doxygen::searchIndex.write(searchDataFile);
13381 }
13382 g_s.end();
13383 }
13384
13385 if (generateRtf)
13386 {
13387 g_s.begin("Combining RTF output...\n");
13388 if (!RTFGenerator::preProcessFileInplace(Config_getString(RTF_OUTPUT),"refman.rtf"))
13389 {
13390 err("An error occurred during post-processing the RTF files!\n");
13391 }
13392 g_s.end();
13393 }
13394
13395 g_s.begin("Running plantuml with JAVA...\n");
13397 g_s.end();
13398
13399 if (Config_getBool(HAVE_DOT))
13400 {
13401 g_s.begin("Running dot...\n");
13403 g_s.end();
13404 }
13405
13406 if (generateHtml &&
13407 Config_getBool(GENERATE_HTMLHELP) &&
13408 !Config_getString(HHC_LOCATION).isEmpty())
13409 {
13410 g_s.begin("Running html help compiler...\n");
13412 g_s.end();
13413 }
13414
13415 if ( generateHtml &&
13416 Config_getBool(GENERATE_QHP) &&
13417 !Config_getString(QHG_LOCATION).isEmpty())
13418 {
13419 g_s.begin("Running qhelpgenerator...\n");
13421 g_s.end();
13422 }
13423
13424 g_outputList->cleanup();
13426
13428
13430 {
13431
13432 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
13433 if (numThreads<1) numThreads=1;
13434 msg("Total elapsed time: {:.6f} seconds\n(of which an average of {:.6f} seconds per thread waiting for external tools to finish)\n",
13435 (static_cast<double>(Debug::elapsedTime())),
13436 Portable::getSysElapsedTime()/static_cast<double>(numThreads)
13437 );
13438 g_s.print();
13439
13441 msg("finished...\n");
13443 }
13444 else
13445 {
13446 msg("finished...\n");
13447 }
13448
13449
13450 /**************************************************************************
13451 * Start cleaning up *
13452 **************************************************************************/
13453
13455
13457 Dir thisDir;
13458 thisDir.remove(Doxygen::filterDBFileName.str());
13460 exitTracing();
13462 delete Doxygen::clangUsrMap;
13464
13465 //dumpDocNodeSizes();
13466}
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:137
static void clearFlag(const DebugMask mask)
Definition debug.cpp:122
static bool isFlagSet(const DebugMask mask)
Definition debug.cpp:132
static void print(DebugMask mask, int prio, fmt::format_string< Args... > fmt, Args &&... args)
Definition debug.h:76
static double elapsedTime()
Definition debug.cpp:200
static void startTimer()
Definition debug.cpp:195
static bool setFlagStr(const QCString &label)
Definition debug.cpp:103
static void setFlag(const DebugMask mask)
Definition debug.cpp:117
The common base class of all entity definitions found in the sources.
Definition definition.h:77
virtual QCString docFile() const =0
virtual const QCString & localName() const =0
virtual int getEndBodyLine() const =0
virtual SrcLangExt getLanguage() const =0
Returns the programming language this definition was written in.
virtual int docLine() const =0
virtual QCString getDefFileName() const =0
virtual bool isLinkable() const =0
virtual int getDefLine() const =0
virtual DefType definitionType() const =0
virtual QCString anchor() const =0
virtual int inbodyLine() const =0
virtual const FileDef * getBodyDef() const =0
virtual int briefLine() const =0
virtual bool hasDocumentation() const =0
virtual bool isLinkableInProject() const =0
virtual QCString briefDescription(bool abbreviate=FALSE) const =0
virtual bool isAnonymous() const =0
virtual bool isHidden() const =0
virtual const Definition * findInnerCompound(const QCString &name) const =0
virtual int getStartDefLine() const =0
virtual const GroupList & partOfGroups() const =0
virtual QCString documentation() const =0
virtual QCString qualifiedName() const =0
virtual QCString displayName(bool includeScope=TRUE) const =0
virtual bool isArtificial() const =0
virtual QCString briefFile() const =0
virtual QCString getOutputFileBase() const =0
virtual Definition * getOuterScope() const =0
virtual int getStartBodyLine() const =0
virtual int getDefColumn() const =0
virtual bool isReference() const =0
virtual QCString inbodyDocumentation() const =0
virtual QCString inbodyFile() const =0
virtual const QCString & name() const =0
virtual void mergeReferencedBy(const Definition *other)=0
virtual void setExported(bool b)=0
virtual void setBodySegment(int defLine, int bls, int ble)=0
virtual void setName(const QCString &name)=0
virtual void setHidden(bool b)=0
virtual void mergeReferences(const Definition *other)=0
virtual void setDocumentation(const QCString &d, const QCString &docFile, int docLine, bool stripWhiteSpace=TRUE)=0
virtual void setDefFile(const QCString &df, int defLine, int defColumn)=0
virtual void addInnerCompound(Definition *d)=0
virtual void addSectionsToDefinition(const std::vector< const SectionInfo * > &anchorList)=0
virtual void setInbodyDocumentation(const QCString &d, const QCString &docFile, int docLine)=0
virtual void setLanguage(SrcLangExt lang)=0
virtual void setOuterScope(Definition *d)=0
virtual void setArtificial(bool b)=0
virtual void setId(const QCString &name)=0
virtual void makePartOfGroup(GroupDef *gd)=0
virtual void setBodyDef(const FileDef *fd)=0
virtual void setBriefDescription(const QCString &b, const QCString &briefFile, int briefLine)=0
virtual void setReference(const QCString &r)=0
virtual void setRequirementReferences(const RequirementRefs &rqli)=0
virtual void setRefItems(const RefItemVector &sli)=0
virtual void computeTooltip()=0
A model of a directory symbol.
Definition dirdef.h:110
virtual void overrideDirectoryGraph(bool e)=0
Class representing a directory in the file system.
Definition dir.h:75
static std::string currentDirPath()
Definition dir.cpp:342
std::string absPath() const
Definition dir.cpp:364
bool mkdir(const std::string &path, bool acceptsAbsPath=true) const
Definition dir.cpp:295
void setPath(const std::string &path)
Definition dir.cpp:229
bool remove(const std::string &path, bool acceptsAbsPath=true) const
Definition dir.cpp:314
DirIterator iterator() const
Definition dir.cpp:239
static std::string cleanDirPath(const std::string &path)
Definition dir.cpp:357
static bool setCurrent(const std::string &path)
Definition dir.cpp:350
bool exists() const
Definition dir.cpp:257
A linked map of directories.
Definition dirdef.h:175
A class that generates docset files.
Definition docsets.h:36
static void init()
bool run()
Definition dot.cpp:128
static DotManager * instance()
Definition dot.cpp:78
static NamespaceLinkedMap * namespaceLinkedMap
Definition doxygen.h:114
static ConceptLinkedMap * conceptLinkedMap
Definition doxygen.h:97
static bool suppressDocWarnings
Definition doxygen.h:129
static FileNameLinkedMap * plantUmlFileNameLinkedMap
Definition doxygen.h:109
static bool parseSourcesNeeded
Definition doxygen.h:122
static StringUnorderedSet inputPaths
Definition doxygen.h:103
static std::unique_ptr< PageDef > mainPage
Definition doxygen.h:100
static bool clangAssistedParsing
Definition doxygen.h:135
static StringUnorderedSet expandAsDefinedSet
Definition doxygen.h:118
static FileNameLinkedMap * inputNameLinkedMap
Definition doxygen.h:104
static ParserManager * parserManager
Definition doxygen.h:128
static InputFileEncodingList inputFileEncodingList
Definition doxygen.h:137
static ClassLinkedMap * classLinkedMap
Definition doxygen.h:95
static MemberNameLinkedMap * functionNameLinkedMap
Definition doxygen.h:111
static PageLinkedMap * exampleLinkedMap
Definition doxygen.h:98
static FileNameLinkedMap * dotFileNameLinkedMap
Definition doxygen.h:106
static NamespaceDefMutable * globalScope
Definition doxygen.h:120
static FileNameLinkedMap * imageNameLinkedMap
Definition doxygen.h:105
static FileNameLinkedMap * mscFileNameLinkedMap
Definition doxygen.h:107
static QCString verifiedDotPath
Definition doxygen.h:136
static MemberGroupInfoMap memberGroupInfoMap
Definition doxygen.h:117
static QCString spaces
Definition doxygen.h:132
static IndexList * indexList
Definition doxygen.h:131
static StaticInitMap staticInitMap
Definition doxygen.h:140
static StringMap tagDestinationMap
Definition doxygen.h:115
static std::mutex countFlowKeywordsMutex
Definition doxygen.h:138
static ClassLinkedMap * hiddenClassLinkedMap
Definition doxygen.h:96
static FileNameLinkedMap * diaFileNameLinkedMap
Definition doxygen.h:108
static QCString htmlFileExtension
Definition doxygen.h:121
static QCString filterDBFileName
Definition doxygen.h:130
static PageLinkedMap * pageLinkedMap
Definition doxygen.h:99
static bool generatingXmlOutput
Definition doxygen.h:133
static std::unique_ptr< NamespaceDef > globalNamespaceDef
Definition doxygen.h:119
static DefinesPerFileList macroDefinitions
Definition doxygen.h:134
static DirLinkedMap * dirLinkedMap
Definition doxygen.h:126
static NamespaceAliasInfoMap namespaceAliasMap
Definition doxygen.h:112
static MemberNameLinkedMap * memberNameLinkedMap
Definition doxygen.h:110
static SymbolMap< Definition > * symbolMap
Definition doxygen.h:124
static StringUnorderedSet tagFileSet
Definition doxygen.h:116
static FileNameLinkedMap * includeNameLinkedMap
Definition doxygen.h:101
static FileNameLinkedMap * exampleNameLinkedMap
Definition doxygen.h:102
static SearchIndexIntf searchIndex
Definition doxygen.h:123
static DirRelationLinkedMap dirRelations
Definition doxygen.h:127
static std::mutex addExampleMutex
Definition doxygen.h:139
static ClangUsrMap * clangUsrMap
Definition doxygen.h:125
static GroupLinkedMap * groupLinkedMap
Definition doxygen.h:113
Generator for Eclipse help files.
Definition eclipsehelp.h:44
static EmojiEntityMapper & instance()
Returns the one and only instance of the Emoji entity mapper.
Definition emoji.cpp:1978
void writeEmojiFile(TextStream &t)
Writes the list of supported emojis to the given file.
Definition emoji.cpp:1999
Represents an unstructured piece of information, about an entity found in the sources.
Definition entry.h:117
TextStream initializer
initial value (for variables)
Definition entry.h:198
VhdlSpecifier vhdlSpec
VHDL specifiers.
Definition entry.h:184
bool subGrouping
automatically group class members?
Definition entry.h:189
RequirementRefs rqli
references to requirements
Definition entry.h:228
const std::vector< std::shared_ptr< Entry > > & children() const
Definition entry.h:140
bool proto
prototype ?
Definition entry.h:188
GroupDocType groupDocType
Definition entry.h:232
QCString metaData
Slice metadata.
Definition entry.h:235
int docLine
line number at which the documentation was found
Definition entry.h:202
QCString bitfields
member's bit fields
Definition entry.h:194
ArgumentList typeConstr
where clause (C#) for type constraints
Definition entry.h:216
void markAsProcessed() const
Definition entry.h:168
int endBodyLine
line number where the definition ends
Definition entry.h:219
bool exported
is the symbol exported from a C++20 module
Definition entry.h:190
const TagInfo * tagInfo() const
Definition entry.h:178
QCString includeName
include name (3 arg of \class)
Definition entry.h:200
QCString id
libclang id
Definition entry.h:233
ArgumentLists tArgLists
template argument declarations
Definition entry.h:196
LocalToc localToc
Definition entry.h:234
MethodTypes mtype
signal, slot, (dcop) method, or property?
Definition entry.h:182
@ GROUPDOC_NORMAL
defgroup
Definition entry.h:122
SrcLangExt lang
programming language in which this entry was found
Definition entry.h:229
Entry * parent() const
Definition entry.h:135
QCString inbodyDocs
documentation inside the body of a function
Definition entry.h:207
int startColumn
start column of entry in the source
Definition entry.h:226
QCString relates
related class (doc block)
Definition entry.h:210
bool explicitExternal
explicitly defined as external?
Definition entry.h:187
std::vector< const SectionInfo * > anchors
list of anchors defined in this entry
Definition entry.h:223
QCString fileName
file this entry was extracted from
Definition entry.h:224
RelatesType relatesType
how relates is handled
Definition entry.h:211
QCString write
property write accessor
Definition entry.h:213
QCString args
member argument string
Definition entry.h:193
QCString type
member type
Definition entry.h:174
std::vector< Grouping > groups
list of groups this entry belongs to
Definition entry.h:222
CommandOverrides commandOverrides
store info for commands whose default can be overridden
Definition entry.h:191
QCString exception
throw specification
Definition entry.h:215
int startLine
start line of entry in the source
Definition entry.h:225
QCString req
C++20 requires clause.
Definition entry.h:236
ArgumentList argList
member arguments as a list
Definition entry.h:195
QCString name
member name
Definition entry.h:175
QCString includeFile
include file (2 arg of \class, must be unique)
Definition entry.h:199
int inbodyLine
line number at which the body doc was found
Definition entry.h:208
EntryType section
entry type (see Sections);
Definition entry.h:173
QCString briefFile
file in which the brief desc. was found
Definition entry.h:206
int bodyLine
line number of the body in the source
Definition entry.h:217
int mGrpId
member group id
Definition entry.h:220
std::vector< BaseInfo > extends
list of base classes
Definition entry.h:221
Specifier virt
virtualness of the entry
Definition entry.h:192
std::vector< std::string > qualifiers
qualifiers specified with the qualifier command
Definition entry.h:237
QCString doc
documentation block (partly parsed)
Definition entry.h:201
QCString read
property read accessor
Definition entry.h:212
RefItemVector sli
special lists (test/todo/bug/deprecated/..) this entry is in
Definition entry.h:227
QCString docFile
file in which the documentation was found
Definition entry.h:203
Protection protection
class protection
Definition entry.h:181
bool artificial
Artificially introduced item.
Definition entry.h:231
bool hidden
does this represent an entity that is hidden from the output
Definition entry.h:230
QCString brief
brief description (doc block)
Definition entry.h:204
int briefLine
line number at which the brief desc. was found
Definition entry.h:205
FileDef * fileDef() const
Definition entry.h:170
int initLines
define/variable initializer lines to show
Definition entry.h:185
bool isStatic
static ?
Definition entry.h:186
QCString inbodyFile
file in which the body doc was found
Definition entry.h:209
TypeSpecifier spec
class/member specifiers
Definition entry.h:183
QCString inside
name of the class in which documents are found
Definition entry.h:214
Wrapper class for the Entry type.
Definition types.h:816
constexpr bool isCompoundDoc() const noexcept
Definition types.h:826
constexpr bool isFile() const noexcept
Definition types.h:825
constexpr bool isDoc() const noexcept
Definition types.h:827
ENTRY_TYPES constexpr bool isCompound() const noexcept
Definition types.h:823
std::string to_string() const
Definition types.h:828
constexpr bool isScope() const noexcept
Definition types.h:824
A class that generates a dynamic tree view side panel.
Definition ftvhelp.h:41
A model of a file symbol.
Definition filedef.h:99
virtual void addUsingDeclaration(const Definition *d)=0
virtual void removeMember(MemberDef *md)=0
virtual void insertClass(ClassDef *cd)=0
virtual void insertConcept(ConceptDef *cd)=0
virtual void overrideIncludeGraph(bool e)=0
virtual void writeSourceHeader(OutputList &ol)=0
virtual bool generateSourceFile() const =0
virtual const LinkedRefMap< NamespaceDef > & getUsedNamespaces() const =0
virtual QCString absFilePath() const =0
virtual bool isSource() const =0
virtual void parseSource(ClangTUParser *clangParser)=0
virtual void getAllIncludeFilesRecursively(StringVector &incFiles) const =0
virtual void setDiskName(const QCString &name)=0
virtual void writeSourceFooter(OutputList &ol)=0
virtual void writeSourceBody(OutputList &ol, ClangTUParser *clangParser)=0
virtual void addUsingDirective(NamespaceDef *nd)=0
virtual void overrideIncludedByGraph(bool e)=0
virtual const QCString & docName() const =0
virtual void insertMember(MemberDef *md)=0
virtual void insertNamespace(NamespaceDef *nd)=0
Minimal replacement for QFileInfo.
Definition fileinfo.h:23
std::string readLink() const
Definition fileinfo.cpp:84
bool isRelative() const
Definition fileinfo.cpp:58
bool isSymLink() const
Definition fileinfo.cpp:77
bool exists() const
Definition fileinfo.cpp:30
std::string fileName() const
Definition fileinfo.cpp:118
bool isReadable() const
Definition fileinfo.cpp:44
bool isDir() const
Definition fileinfo.cpp:70
bool isFile() const
Definition fileinfo.cpp:63
std::string dirPath(bool absPath=true) const
Definition fileinfo.cpp:137
std::string absFilePath() const
Definition fileinfo.cpp:101
Class representing all files with a certain base name.
Definition filename.h:30
Ordered dictionary of FileName objects.
Definition filename.h:73
void initFromRepository(const QCString &dir)
Definition formula.cpp:59
bool hasFormulas() const
Definition formula.cpp:719
void checkRepositories()
Definition formula.cpp:172
static FormulaManager & instance()
Definition formula.cpp:53
void generateImages(const QCString &outputDir, Format format, HighDPI hd=HighDPI::Off)
Definition formula.cpp:635
A model of a group of symbols.
Definition groupdef.h:52
virtual QCString groupTitle() const =0
virtual void overrideGroupGraph(bool e)=0
virtual bool addClass(ClassDef *def)=0
virtual bool containsFile(const FileDef *def) const =0
virtual bool addNamespace(NamespaceDef *def)=0
virtual void setGroupScope(Definition *d)=0
virtual void addFile(FileDef *def)=0
virtual MemberList * getMemberList(MemberListType lt) const =0
virtual void setGroupTitle(const QCString &newtitle)=0
virtual bool hasGroupTitle() const =0
Generator for HTML output.
Definition htmlgen.h:96
static void init()
Definition htmlgen.cpp:1197
static void writeSearchPage()
Definition htmlgen.cpp:3197
static void writeFooterFile(TextStream &t)
Definition htmlgen.cpp:1540
static void writeTabData()
Additional initialization after indices have been created.
Definition htmlgen.cpp:1357
static void writeSearchData(const QCString &dir)
Definition htmlgen.cpp:1366
static void writeExternalSearchPage()
Definition htmlgen.cpp:3296
static void writeStyleSheetFile(TextStream &t)
Definition htmlgen.cpp:1528
static void writeHeaderFile(TextStream &t, const QCString &cssname)
Definition htmlgen.cpp:1534
A class that generated the HTML Help specific files.
Definition htmlhelp.h:36
static const QCString hhpFileName
Definition htmlhelp.h:89
static Index & instance()
Definition index.cpp:108
void countDataStructures()
Definition index.cpp:264
A list of index interfaces.
Definition indexlist.h:64
Generator for LaTeX output.
Definition latexgen.h:94
static void writeFooterFile(TextStream &t)
Definition latexgen.cpp:696
static void writeStyleSheetFile(TextStream &t)
Definition latexgen.cpp:702
static void writeHeaderFile(TextStream &t)
Definition latexgen.cpp:690
static void init()
Definition latexgen.cpp:632
static LayoutDocManager & instance()
Returns a reference to this singleton.
Definition layout.cpp:1437
void parse(const QCString &fileName, const char *data=nullptr)
Parses a user provided layout.
Definition layout.cpp:1470
void clear()
Definition linkedmap.h:212
std::unique_ptr< RefList > Ptr
Definition linkedmap.h:38
size_t size() const
Definition linkedmap.h:210
T * add(const char *k, Args &&... args)
Definition linkedmap.h:90
const T * find(const std::string &key) const
Definition linkedmap.h:47
Container class representing a vector of objects with keys.
Definition linkedmap.h:232
const T * find(const std::string &key) const
Definition linkedmap.h:243
bool empty() const
Definition linkedmap.h:374
Generator for Man page output.
Definition mangen.h:69
static void init()
Definition mangen.cpp:272
A model of a class/file/namespace member symbol.
Definition memberdef.h:48
virtual QCString typeString() const =0
virtual QCString requiresClause() const =0
virtual bool isFriend() const =0
virtual bool isForeign() const =0
virtual QCString definition() const =0
virtual bool isRelated() const =0
virtual const ClassDef * getCachedTypedefVal() const =0
virtual QCString excpString() const =0
virtual const ClassDef * getClassDef() const =0
virtual const ArgumentList & templateArguments() const =0
virtual GroupDef * getGroupDef()=0
virtual bool isCSharpProperty() const =0
virtual bool isTypedef() const =0
virtual const MemberVector & enumFieldList() const =0
virtual void moveTo(Definition *)=0
virtual const FileDef * getFileDef() const =0
virtual const ArgumentList & argumentList() const =0
virtual bool isStrongEnumValue() const =0
virtual VhdlSpecifier getVhdlSpecifiers() const =0
virtual bool isFunction() const =0
virtual bool isExternal() const =0
virtual int getMemberGroupId() const =0
virtual bool isStatic() const =0
virtual const MemberDef * reimplements() const =0
virtual StringVector getQualifiers() const =0
virtual QCString bitfieldString() const =0
virtual bool isTypedefValCached() const =0
virtual bool isDocsForDefinition() const =0
virtual bool isDefine() const =0
virtual const NamespaceDef * getNamespaceDef() const =0
virtual bool isObjCProperty() const =0
virtual Protection protection() const =0
virtual TypeSpecifier getMemberSpecifiers() const =0
virtual bool isEnumerate() const =0
virtual MemberType memberType() const =0
virtual ClassDef * relatedAlso() const =0
virtual bool isVariable() const =0
virtual bool isStrong() const =0
virtual QCString argsString() const =0
virtual Specifier virtualness(int count=0) const =0
virtual int redefineCount() const =0
virtual int initializerLines() const =0
virtual const MemberDef * getEnumScope() const =0
virtual bool isEnumValue() const =0
virtual bool isPrototype() const =0
virtual const QCString & initializer() const =0
virtual void setMemberClass(ClassDef *cd)=0
virtual void setProtection(Protection p)=0
virtual void setMemberGroupId(int id)=0
virtual void setDocumentedEnumValues(bool value)=0
virtual void setMemberSpecifiers(TypeSpecifier s)=0
virtual void setDefinition(const QCString &d)=0
virtual ClassDefMutable * getClassDefMutable()=0
virtual void setExplicitExternal(bool b, const QCString &df, int line, int column)=0
virtual void setAccessorType(ClassDef *cd, const QCString &t)=0
virtual void setDefinitionTemplateParameterLists(const ArgumentLists &lists)=0
virtual void invalidateTypedefValCache()=0
virtual void setBitfields(const QCString &s)=0
virtual void setVhdlSpecifiers(VhdlSpecifier s)=0
virtual void setEnumScope(MemberDef *md, bool livesInsideEnum=FALSE)=0
virtual void setEnumClassScope(ClassDef *cd)=0
virtual void setMaxInitLines(int lines)=0
virtual void setInheritsDocsFrom(const MemberDef *md)=0
virtual void setRelatedAlso(ClassDef *cd)=0
virtual void setPrototype(bool p, const QCString &df, int line, int column)=0
virtual void overrideReferencesRelation(bool e)=0
virtual void makeForeign()=0
virtual void overrideReferencedByRelation(bool e)=0
virtual void setDocsForDefinition(bool b)=0
virtual void setRequiresClause(const QCString &req)=0
virtual void overrideCallGraph(bool e)=0
virtual void overrideInlineSource(bool e)=0
virtual void setArgsString(const QCString &as)=0
virtual void setInitializer(const QCString &i)=0
virtual void copyArgumentNames(const MemberDef *bmd)=0
virtual void overrideEnumValues(bool e)=0
virtual void mergeMemberSpecifiers(TypeSpecifier s)=0
virtual void addQualifiers(const StringVector &qualifiers)=0
virtual void insertEnumField(MemberDef *md)=0
virtual void moveDeclArgumentList(std::unique_ptr< ArgumentList > al)=0
virtual void overrideCallerGraph(bool e)=0
virtual void setReimplements(MemberDef *md)=0
virtual void setDeclFile(const QCString &df, int line, int column)=0
virtual void invalidateCachedArgumentTypes()=0
virtual void moveArgumentList(std::unique_ptr< ArgumentList > al)=0
virtual void makeRelated()=0
virtual void insertReimplementedBy(MemberDef *md)=0
A list of MemberDef objects as shown in documentation sections.
Definition memberlist.h:125
Ptr & front()
Definition membername.h:51
size_t size() const
Definition membername.h:48
iterator begin()
Definition membername.h:37
iterator end()
Definition membername.h:38
void push_back(Ptr &&p)
Definition membername.h:54
Ordered dictionary of MemberName objects.
Definition membername.h:63
const MemberDef * find(const QCString &name) const
Definition memberlist.h:93
const MemberDef * findRev(const QCString &name) const
Definition memberlist.h:106
void sortMemberLists()
static ModuleManager & instance()
void addDocs(const Entry *root)
void addConceptToModule(const Entry *root, ConceptDef *cd)
void addClassToModule(const Entry *root, ClassDef *cd)
void addMemberToModule(const Entry *root, MemberDef *md)
void writeDocumentation(OutputList &ol)
void addMembersToMemberGroup()
void findSectionsInDocumentation()
void distributeMemberGroupDocumentation()
An abstract interface of a namespace symbol.
virtual const LinkedRefMap< NamespaceDef > & getUsedNamespaces() const =0
virtual bool isInline() const =0
virtual void insertUsedFile(FileDef *fd)=0
virtual void setMetaData(const QCString &m)=0
virtual void findSectionsInDocumentation()=0
virtual void countMembers()=0
virtual void addUsingDirective(NamespaceDef *nd)=0
virtual void insertMember(MemberDef *md)=0
virtual void addUsingDeclaration(const Definition *d)=0
virtual void distributeMemberGroupDocumentation()=0
virtual void writeTagFile(TextStream &)=0
virtual void writeDocumentation(OutputList &ol)=0
virtual void setInline(bool isInline)=0
virtual void computeAnchors()=0
virtual void combineUsingRelations(NamespaceDefSet &visitedNamespace)=0
virtual void setFileName(const QCString &fn)=0
virtual void sortMemberLists()=0
virtual void addMembersToMemberGroup()=0
/dev/null outline parser
bool needsPreprocessing(const QCString &) const override
Returns TRUE if the language identified by extension needs the C preprocessor to be run before feed t...
void parseInput(const QCString &, const char *, const std::shared_ptr< Entry > &, ClangTUParser *) override
Parses a single input file with the goal to build an Entry tree.
void parsePrototype(const QCString &) override
Callback function called by the comment block scanner.
Abstract interface for outline parsers.
Definition parserintf.h:42
virtual bool needsPreprocessing(const QCString &extension) const =0
Returns TRUE if the language identified by extension needs the C preprocessor to be run before feed t...
virtual void parseInput(const QCString &fileName, const char *fileBuf, const std::shared_ptr< Entry > &root, ClangTUParser *clangParser)=0
Parses a single input file with the goal to build an Entry tree.
Class representing a list of output generators that are written to in parallel.
Definition outputlist.h:315
A model of a page symbol.
Definition pagedef.h:26
virtual void setLocalToc(const LocalToc &tl)=0
virtual void setFileName(const QCString &name)=0
virtual void setShowLineNo(bool)=0
virtual void setPageScope(Definition *)=0
virtual const GroupDef * getGroupDef() const =0
Manages programming language parsers.
Definition parserintf.h:183
static PlantumlManager & instance()
Definition plantuml.cpp:231
void run()
Run plant UML tool for all images.
Definition plantuml.cpp:389
void processFile(const QCString &fileName, const std::string &input, std::string &output)
Definition pre.l:4196
void addSearchDir(const QCString &dir)
Definition pre.l:4178
This is an alternative implementation of QCString.
Definition qcstring.h:101
int find(char c, int index=0, bool cs=TRUE) const
Definition qcstring.cpp:43
QCString & prepend(const char *s)
Definition qcstring.h:422
int toInt(bool *ok=nullptr, int base=10) const
Definition qcstring.cpp:254
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition qcstring.h:166
bool startsWith(const char *s) const
Definition qcstring.h:507
QCString mid(size_t index, size_t len=static_cast< size_t >(-1)) const
Definition qcstring.h:241
QCString lower() const
Definition qcstring.h:249
bool endsWith(const char *s) const
Definition qcstring.h:524
char & at(size_t i)
Returns a reference to the character at index i.
Definition qcstring.h:593
bool isEmpty() const
Returns TRUE iff the string is empty.
Definition qcstring.h:163
QCString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
Definition qcstring.h:260
QCString fill(char c, int len=-1)
Fills a string with a predefined character.
Definition qcstring.h:193
const std::string & str() const
Definition qcstring.h:552
QCString & setNum(short n)
Definition qcstring.h:459
QCString right(size_t len) const
Definition qcstring.h:234
size_t size() const
Returns the length of the string, not counting the 0-terminator.
Definition qcstring.h:169
QCString & sprintf(const char *format,...)
Definition qcstring.cpp:29
@ ExplicitSize
Definition qcstring.h:146
int findRev(char c, int index=-1, bool cs=TRUE) const
Definition qcstring.cpp:96
QCString & replace(size_t index, size_t len, const char *s)
Definition qcstring.cpp:217
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition qcstring.h:172
QCString left(size_t len) const
Definition qcstring.h:229
int contains(char c, bool cs=TRUE) const
Definition qcstring.cpp:148
bool stripPrefix(const QCString &prefix)
Definition qcstring.h:213
void clear()
Definition qcstring.h:182
Definition qhp.h:27
static const QCString qhpFileName
Definition qhp.h:47
static QCString getQchFileName()
Definition qhp.cpp:426
Generator for RTF output.
Definition rtfgen.h:80
static void init()
Definition rtfgen.cpp:461
static bool preProcessFileInplace(const QCString &path, const QCString &name)
This is an API to a VERY brittle RTF preprocessor that combines nested RTF files.
Definition rtfgen.cpp:2461
static void writeStyleSheetFile(TextStream &t)
Definition rtfgen.cpp:394
static void writeExtensionsFile(TextStream &t)
Definition rtfgen.cpp:409
static RefListManager & instance()
Definition reflist.h:121
static RequirementManager & instance()
void writeTagFile(TextStream &tagFile)
void addRequirement(Entry *e)
Abstract proxy interface for non-javascript based search indices.
class that provide information about a section.
Definition section.h:58
QCString ref() const
Definition section.h:72
QCString fileName() const
Definition section.h:74
int lineNr() const
Definition section.h:73
SectionInfo * replace(const QCString &label, const QCString &fileName, int lineNr, const QCString &title, SectionType type, int level, const QCString &ref=QCString())
Definition section.h:157
SectionInfo * add(const SectionInfo &si)
Definition section.h:139
static SectionManager & instance()
returns a reference to the singleton
Definition section.h:179
static constexpr int Page
Definition section.h:31
std::vector< stat > stats
Definition doxygen.cpp:267
void print()
Definition doxygen.cpp:244
void begin(const char *name)
Definition doxygen.cpp:231
void end()
Definition doxygen.cpp:237
std::chrono::steady_clock::time_point startTime
Definition doxygen.cpp:268
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:48
#define AUTO_TRACE(...)
Definition docnode.cpp:47
#define AUTO_TRACE_EXIT(...)
Definition docnode.cpp:49
constexpr DocNodeVariant * parent(DocNodeVariant *n)
returns the parent node of a given node n or nullptr if the node has no parent.
Definition docnode.h:1327
static void findInheritedTemplateInstances()
Definition doxygen.cpp:5325
void printNavTree(Entry *root, int indent)
static void addClassToContext(const Entry *root)
Definition doxygen.cpp:936
static void makeTemplateInstanceRelation(const Entry *root, ClassDefMutable *cd)
Definition doxygen.cpp:5340
static StringUnorderedSet g_pathsVisited(1009)
static int findEndOfTemplate(const QCString &s, size_t startPos)
Definition doxygen.cpp:3110
static void buildGroupList(const Entry *root)
Definition doxygen.cpp:429
static void insertMemberAlias(Definition *outerScope, const MemberDef *md)
Definition doxygen.cpp:6709
static void findUsingDeclarations(const Entry *root, bool filterPythonPackages)
Definition doxygen.cpp:2055
static void flushCachedTemplateRelations()
Definition doxygen.cpp:9459
static bool isRecursiveBaseClass(const QCString &scope, const QCString &name)
Definition doxygen.cpp:4883
static void copyLatexStyleSheet()
static void generateDocsForClassList(const std::vector< ClassDefMutable * > &classList)
Definition doxygen.cpp:9119
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:2906
static bool isSpecialization(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists)
Definition doxygen.cpp:6010
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:2475
static void computeTemplateClassRelations()
Definition doxygen.cpp:5419
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:8173
static void runQHelpGenerator()
static void addConceptToContext(const Entry *root)
Definition doxygen.cpp:1162
static void addRelatedPage(Entry *root)
Definition doxygen.cpp:327
void initDoxygen()
static void addIncludeFile(DefMutable *cd, FileDef *ifd, const Entry *root)
Definition doxygen.cpp:586
static StringVector g_inputFiles
Definition doxygen.cpp:186
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:2159
static void generateXRefPages()
Definition doxygen.cpp:5598
static void findUsingDeclImports(const Entry *root)
Definition doxygen.cpp:2208
static void copyStyleSheet()
FindBaseClassRelation_Mode
Definition doxygen.cpp:284
@ Undocumented
Definition doxygen.cpp:287
@ TemplateInstances
Definition doxygen.cpp:285
@ DocumentedOnly
Definition doxygen.cpp:286
void distributeClassGroupRelations()
Definition doxygen.cpp:1493
static void generateGroupDocs()
static void findDirDocumentation(const Entry *root)
Definition doxygen.cpp:9659
void checkConfiguration()
check and resolve config options
QCString stripTemplateSpecifiers(const QCString &s)
Definition doxygen.cpp:683
static bool findClassRelation(const Entry *root, Definition *context, ClassDefMutable *cd, const BaseInfo *bi, const TemplateNameMap &templateNames, FindBaseClassRelation_Mode mode, bool isArtificial)
Definition doxygen.cpp:4925
static void resolveTemplateInstanceInType(const Entry *root, const Definition *scope, const MemberDef *md)
Definition doxygen.cpp:4853
static void organizeSubGroupsFiltered(const Entry *root, bool additional)
Definition doxygen.cpp:469
static void warnUndocumentedNamespaces()
Definition doxygen.cpp:5374
static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments, const std::string &name)
Definition doxygen.cpp:4532
static NamespaceDef * findUsedNamespace(const LinkedRefMap< NamespaceDef > &unl, const QCString &name)
Definition doxygen.cpp:1886
static void buildConceptList(const Entry *root)
Definition doxygen.cpp:1323
static void resolveClassNestingRelations()
Definition doxygen.cpp:1378
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:6752
static void findClassEntries(const Entry *root)
Definition doxygen.cpp:5288
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:1612
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:6222
static void vhdlCorrectMemberProperties()
Definition doxygen.cpp:8417
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:4802
static void substituteTemplatesInArgList(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists, const ArgumentList &src, ArgumentList &dst)
Definition doxygen.cpp:6124
static void computeMemberReferences()
Definition doxygen.cpp:5488
static void generateConfigFile(const QCString &configFile, bool shortList, bool updateOnly=FALSE)
static void transferRelatedFunctionDocumentation()
Definition doxygen.cpp:4446
static void addMembersToMemberGroup()
Definition doxygen.cpp:9324
static bool tryAddEnumDocsToGroupMember(const Entry *root, const QCString &name)
Definition doxygen.cpp:8054
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:6641
static void findMainPageTagFiles(Entry *root)
Definition doxygen.cpp:9829
static void distributeConceptGroups()
Definition doxygen.cpp:1345
static void transferFunctionDocumentation()
Definition doxygen.cpp:4365
static void setAnonymousEnumType()
Definition doxygen.cpp:9064
static void sortMemberLists()
Definition doxygen.cpp:8969
static void createTemplateInstanceMembers()
Definition doxygen.cpp:8545
void transferStaticInstanceInitializers()
Definition doxygen.cpp:4495
static void findObjCMethodDefinitions(const Entry *root)
Definition doxygen.cpp:7558
static void dumpSymbolMap()
static void buildTypedefList(const Entry *root)
Definition doxygen.cpp:3434
static void findGroupScope(const Entry *root)
Definition doxygen.cpp:444
static void generateFileDocs()
Definition doxygen.cpp:8782
static void findDefineDocumentation(Entry *root)
Definition doxygen.cpp:9572
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:4595
void parseInput()
static void findMemberDocumentation(const Entry *root)
Definition doxygen.cpp:7528
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:6168
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:5802
static void distributeMemberGroupDocumentation()
Definition doxygen.cpp:9362
static void generateNamespaceClassDocs(const ClassLinkedRefMap &classList)
static void addEnumValuesToEnums(const Entry *root)
Definition doxygen.cpp:7762
static void generatePageDocs()
Definition doxygen.cpp:9959
static void resolveUserReferences()
Definition doxygen.cpp:9891
static void buildRequirementsList(Entry *root)
Definition doxygen.cpp:9720
static void compareDoxyfile(Config::CompareMode diffList)
static void addPageToContext(PageDef *pd, Entry *root)
Definition doxygen.cpp:308
static void buildVarList(const Entry *root)
Definition doxygen.cpp:3571
static void buildSequenceList(const Entry *root)
Definition doxygen.cpp:3534
static void generateFileSources()
Definition doxygen.cpp:8616
static QCString substituteTemplatesInString(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists, const std::string &src)
Definition doxygen.cpp:6040
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:2650
static void generateClassDocs()
Definition doxygen.cpp:9217
static void buildNamespaceList(const Entry *root)
Definition doxygen.cpp:1717
static void readTagFile(const std::shared_ptr< Entry > &root, const QCString &tagLine)
static void findIncludedUsingDirectives()
Definition doxygen.cpp:2458
static void addDefineDoc(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:9545
static void addMemberDocs(const Entry *root, MemberDefMutable *md, const QCString &funcDecl, const ArgumentList *al, bool over_load, TypeSpecifier spec)
Definition doxygen.cpp:5612
static void countMembers()
Definition doxygen.cpp:9078
void clearAll()
Definition doxygen.cpp:200
static void devUsage()
static void addInterfaceOrServiceToServiceOrSingleton(const Entry *root, ClassDefMutable *cd, QCString const &rname)
Definition doxygen.cpp:3603
static void organizeSubGroups(const Entry *root)
Definition doxygen.cpp:488
static void applyMemberOverrideOptions(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:2147
void searchInputFiles()
static void findFriends()
Definition doxygen.cpp:4268
static void findEnums(const Entry *root)
Definition doxygen.cpp:7586
static void dumpSymbol(TextStream &t, Definition *d)
static void addClassAndNestedClasses(std::vector< ClassDefMutable * > &list, ClassDefMutable *cd)
Definition doxygen.cpp:9194
static void addEnumDocs(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:8012
static void addListReferences()
Definition doxygen.cpp:5589
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:5266
static Definition * findScopeFromQualifiedName(NamespaceDefMutable *startScope, const QCString &n, FileDef *fileScope, const TagInfo *tagInfo)
Definition doxygen.cpp:779
static ClassDef * findClassWithinClassContext(Definition *context, ClassDef *cd, const QCString &name)
Definition doxygen.cpp:4560
static void buildGroupListFiltered(const Entry *root, bool additional, bool includeExternal)
Definition doxygen.cpp:358
static void runHtmlHelpCompiler()
static QCString extractClassName(const Entry *root)
Definition doxygen.cpp:5297
static void addMembersToIndex()
Definition doxygen.cpp:8207
static bool g_dumpSymbolMap
Definition doxygen.cpp:190
static void version(const bool extended)
static void addGlobalFunction(const Entry *root, const QCString &rname, const QCString &sc)
Definition doxygen.cpp:3854
static OutputList * g_outputList
Definition doxygen.cpp:187
static void findMainPage(Entry *root)
Definition doxygen.cpp:9759
static ClassDef::CompoundType convertToCompoundType(EntryType section, TypeSpecifier specifier)
Definition doxygen.cpp:894
static void findUsingDirectives(const Entry *root)
Definition doxygen.cpp:1899
std::unique_ptr< ArgumentList > getTemplateArgumentsFromName(const QCString &name, const ArgumentLists &tArgLists)
Definition doxygen.cpp:864
static bool g_successfulRun
Definition doxygen.cpp:189
static void addSourceReferences()
Definition doxygen.cpp:8842
std::function< std::unique_ptr< T >() > make_parser_factory()
static void buildExampleList(Entry *root)
Definition doxygen.cpp:9976
static void inheritDocumentation()
Definition doxygen.cpp:9264
static void flushUnresolvedRelations()
Definition doxygen.cpp:9501
static bool isSymbolHidden(const Definition *d)
Definition doxygen.cpp:9011
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:191
static void findDocumentedEnumValues()
Definition doxygen.cpp:8199
static void findTagLessClasses()
Definition doxygen.cpp:1693
static void generateDiskNames()
static void addToIndices()
Definition doxygen.cpp:8249
static void computeClassRelations()
Definition doxygen.cpp:5394
static void buildFunctionList(const Entry *root)
Definition doxygen.cpp:3963
static void checkPageRelations()
Definition doxygen.cpp:9871
static void findModuleDocumentation(const Entry *root)
Definition doxygen.cpp:1313
static void findEnumDocumentation(const Entry *root)
Definition doxygen.cpp:8087
static void computePageRelations(Entry *root)
Definition doxygen.cpp:9841
static void filterMemberDocumentation(const Entry *root, const QCString &relates)
Definition doxygen.cpp:7378
static QCString createOutputDirectory(const QCString &baseDirName, const QCString &formatDirName, const char *defaultDirName)
void initResources()
static bool isVarWithConstructor(const Entry *root)
Definition doxygen.cpp:2966
static StringSet g_usingDeclarations
Definition doxygen.cpp:188
static void buildDictionaryList(const Entry *root)
Definition doxygen.cpp:3552
static bool haveEqualFileNames(const Entry *root, const MemberDef *md)
Definition doxygen.cpp:9534
static void generateNamespaceDocs()
static void buildClassDocList(const Entry *root)
Definition doxygen.cpp:1148
static void buildPageList(Entry *root)
Definition doxygen.cpp:9732
static void writeTagFile()
static Definition * buildScopeFromQualifiedName(const QCString &name_, SrcLangExt lang, const TagInfo *tagInfo)
Definition doxygen.cpp:710
static void addRequirementReferences()
Definition doxygen.cpp:5581
static void computeVerifiedDotPath()
static bool g_singleComment
Definition doxygen.cpp:192
static void findSectionsInDocumentation()
Definition doxygen.cpp:9400
static void mergeCategories()
Definition doxygen.cpp:8564
static const StringUnorderedSet g_compoundKeywords
Definition doxygen.cpp:197
static bool scopeIsTemplate(const Definition *d)
Definition doxygen.cpp:6026
static void buildFileList(const Entry *root)
Definition doxygen.cpp:500
static ClassDefMutable * createTagLessInstance(const ClassDef *rootCd, const ClassDef *templ, const QCString &fieldName)
Definition doxygen.cpp:1526
static void buildClassList(const Entry *root)
Definition doxygen.cpp:1138
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:3718
static std::unique_ptr< OutlineParserInterface > getParserForFile(const QCString &fn)
static void findUsedTemplateInstances()
Definition doxygen.cpp:5358
static void computeTooltipTexts()
Definition doxygen.cpp:9018
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:3178
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:6578
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:4751
static void parseFilesSingleThreading(const std::shared_ptr< Entry > &root)
parse the list of input files
static void buildCompleteMemberLists()
Definition doxygen.cpp:8586
static void generateConceptDocs()
Definition doxygen.cpp:9243
static void combineUsingRelations()
Definition doxygen.cpp:9299
static const char * getArg(int argc, char **argv, int &optInd)
static const ClassDef * findClassDefinition(FileDef *fd, NamespaceDef *nd, const QCString &scopeName)
Definition doxygen.cpp:5763
static void checkMarkdownMainfile()
static std::unordered_map< std::string, std::vector< ClassDefMutable * > > g_usingClassMap
Definition doxygen.cpp:2206
static void buildConceptDocList(const Entry *root)
Definition doxygen.cpp:1333
static int findTemplateSpecializationPosition(const QCString &name)
Definition doxygen.cpp:4895
static bool isEntryInGroupOfMember(const Entry *root, const MemberDef *md, bool allowNoGroup=false)
Definition doxygen.cpp:5778
static void applyToAllDefinitions(Func func)
Definition doxygen.cpp:5524
static void parseFilesMultiThreading(const std::shared_ptr< Entry > &root)
parse the list of input files
static void transferFunctionReferences()
Definition doxygen.cpp:4398
static void buildDefineList()
Definition doxygen.cpp:8921
static void buildInterfaceAndServiceList(const Entry *root)
Definition doxygen.cpp:3666
static void computeMemberRelations()
Definition doxygen.cpp:8529
static void buildListOfUsingDecls(const Entry *root)
Definition doxygen.cpp:2042
static void computeMemberRelationsForBaseClass(const ClassDef *cd, const BaseClassDef *bcd)
Definition doxygen.cpp:8449
static std::multimap< std::string, const Entry * > g_classEntries
Definition doxygen.cpp:185
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:265
const char * name
Definition doxygen.cpp:262
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:2016
QCString removeRedundantWhiteSpace(const QCString &s)
Definition util.cpp:567
QCString mergeScopes(const QCString &leftScope, const QCString &rightScope)
Definition util.cpp:4593
QCString normalizeNonTemplateArgumentsInString(const QCString &name, const Definition *context, const ArgumentList &formalArgs)
Definition util.cpp:4299
SrcLangExt getLanguageFromFileName(const QCString &fileName, SrcLangExt defLang)
Definition util.cpp:5209
bool protectionLevelVisible(Protection prot)
Definition util.cpp:5955
bool matchTemplateArguments(const ArgumentList &srcAl, const ArgumentList &dstAl)
Definition util.cpp:2260
void addCodeOnlyMappings()
Definition util.cpp:5203
QCString substituteTemplateArgumentsInString(const QCString &nm, const ArgumentList &formalArgs, const ArgumentList *actualArgs)
Definition util.cpp:4364
int extractClassNameFromType(const QCString &type, int &pos, QCString &name, QCString &templSpec, SrcLangExt lang)
Definition util.cpp:4214
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:5313
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:5548
bool patternMatch(const FileInfo &fi, const StringVector &patList)
Definition util.cpp:5702
bool openOutputFile(const QCString &outFile, std::ofstream &f)
Definition util.cpp:6314
QCString tempArgListToString(const ArgumentList &al, SrcLangExt lang, bool includeDefault)
Definition util.cpp:1294
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:4823
QCString showFileDefMatches(const FileNameLinkedMap *fnMap, const QCString &n)
Definition util.cpp:3017
QCString fileToString(const QCString &name, bool filter, bool isSourceCode)
Definition util.cpp:1489
QCString filterTitle(const QCString &title)
Definition util.cpp:5628
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:4915
int computeQualifiedIndex(const QCString &name)
Return the index of the last :: in the string name that is still before the first <.
Definition util.cpp:6842
void initDefaultExtensionMapping()
Definition util.cpp:5136
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:4985
QCString convertNameToFile(const QCString &name, bool allowDots, bool allowUnderscore)
Definition util.cpp:3503
QCString langToString(SrcLangExt lang)
Returns a string representation of lang.
Definition util.cpp:5909
EntryType guessSection(const QCString &name)
Definition util.cpp:338
void extractNamespaceName(const QCString &scopeName, QCString &className, QCString &namespaceName, bool allowEmptyClass)
Definition util.cpp:3696
QCString argListToString(const ArgumentList &al, bool useCanonicalType, bool showDefVals)
Definition util.cpp:1249
QCString getLanguageSpecificSeparator(SrcLangExt lang, bool classScope)
Returns the scope separator to use given the programming language lang.
Definition util.cpp:5915
void mergeMemberOverrideOptions(MemberDefMutable *md1, MemberDefMutable *md2)
Definition util.cpp:6879
QCString mangleCSharpGenericName(const QCString &name)
Definition util.cpp:6931
QCString projectLogoFile()
Definition util.cpp:3139
void mergeArguments(ArgumentList &srcAl, ArgumentList &dstAl, bool forceNameOverwrite)
Definition util.cpp:2116
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:5875
QCString stripTemplateSpecifiersFromScope(const QCString &fullName, bool parentOnly, QCString *pLastScopeStripped, QCString scopeName, bool allowArtificial)
Definition util.cpp:4526
QCString getOverloadDocs()
Definition util.cpp:4088
void cleanupInlineGraph()
Definition util.cpp:7008
int getPrefixIndex(const QCString &name)
Definition util.cpp:3230
bool rightScopeMatch(const QCString &scope, const QCString &name)
Definition util.cpp:870
bool updateLanguageMapping(const QCString &extension, const QCString &language)
Definition util.cpp:5104
QCString getFileNameExtension(const QCString &fn)
Definition util.cpp:5251
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:2889
int getScopeFragment(const QCString &s, int p, int *l)
Definition util.cpp:4638
void addHtmlExtensionIfMissing(QCString &fName)
Definition util.cpp:4920
A bunch of utility functions.
void generateXML()
Definition xmlgen.cpp:2325