Doxygen
Loading...
Searching...
No Matches
doxygen.cpp
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * Copyright (C) 1997-2015 by Dimitri van Heesch.
4 *
5 * Permission to use, copy, modify, and distribute this software and its
6 * documentation under the terms of the GNU General Public License is hereby
7 * granted. No representations are made about the suitability of this software
8 * for any purpose. It is provided "as is" without express or implied warranty.
9 * See the GNU General Public License for more details.
10 *
11 * Documents produced by Doxygen are derivative works derived from the
12 * input used in their production; they are not affected by this license.
13 *
14 */
15
16#include <cstdio>
17#include <cstdlib>
18#include <cerrno>
19#include <sys/stat.h>
20
21#include <algorithm>
22#include <unordered_map>
23#include <memory>
24#include <cinttypes>
25#include <chrono>
26#include <clocale>
27#include <locale>
28
29#include "aliases.h"
30#include "arguments.h"
31#include "cite.h"
32#include "clangparser.h"
33#include "classlist.h"
34#include "cmdmapper.h"
35#include "code.h"
36#include "commentcnv.h"
37#include "conceptdef.h"
38#include "config.h"
39#include "debug.h"
40#include "declinfo.h"
41#include "defargs.h"
42#include "defgen.h"
43#include "dir.h"
44#include "dirdef.h"
45#include "docbookgen.h"
46#include "docparser.h"
47#include "docsets.h"
48#include "dot.h"
49#include "doxygen.h"
50#include "eclipsehelp.h"
51#include "emoji.h"
52#include "entry.h"
53#include "fileinfo.h"
54#include "filename.h"
55#include "fileparser.h"
56#include "formula.h"
57#include "fortrancode.h"
58#include "fortranscanner.h"
59#include "ftvhelp.h"
60#include "groupdef.h"
61#include "htags.h"
62#include "htmlgen.h"
63#include "htmlhelp.h"
64#include "index.h"
65#include "indexlist.h"
66#include "language.h"
67#include "latexgen.h"
68#include "layout.h"
69#include "lexcode.h"
70#include "lexscanner.h"
71#include "mangen.h"
72#include "markdown.h"
73#include "membergroup.h"
74#include "memberlist.h"
75#include "membername.h"
76#include "message.h"
77#include "moduledef.h"
78#include "msc.h"
79#include "namespacedef.h"
80#include "outputlist.h"
81#include "pagedef.h"
82#include "parserintf.h"
83#include "perlmodgen.h"
84#include "plantuml.h"
85#include "portable.h"
86#include "pre.h"
87#include "pycode.h"
88#include "pyscanner.h"
89#include "qhp.h"
90#include "reflist.h"
91#include "regex.h"
92#include "requirement.h"
93#include "rtfgen.h"
94#include "scanner.h"
95#include "searchindex_js.h"
96#include "settings.h"
97#include "singlecomment.h"
98#include "sitemap.h"
99#include "sqlcode.h"
100#include "sqlite3gen.h"
101#include "stlsupport.h"
102#include "stringutil.h"
103#include "symbolresolver.h"
104#include "tagreader.h"
105#include "threadpool.h"
106#include "trace.h"
107#include "util.h"
108#include "version.h"
109#include "vhdlcode.h"
110#include "vhdldocgen.h"
111#include "vhdljjparser.h"
112#include "xmlcode.h"
113#include "xmlgen.h"
114
115#include <sqlite3.h>
116
117#if USE_LIBCLANG
118#if defined(__GNUC__)
119#pragma GCC diagnostic push
120#pragma GCC diagnostic ignored "-Wshadow"
121#endif
122#include <clang/Basic/Version.h>
123#if defined(__GNUC__)
124#pragma GCC diagnostic pop
125#endif
126#endif
127
128// provided by the generated file resources.cpp
129extern void initResources();
130
131#if !defined(_WIN32) || defined(__CYGWIN__)
132#include <signal.h>
133#define HAS_SIGNALS
134#endif
135
136// globally accessible variables
148FileNameLinkedMap *Doxygen::includeNameLinkedMap = nullptr; // include names
154FileNameLinkedMap *Doxygen::plantUmlFileNameLinkedMap = nullptr;// plantuml files
156StringMap Doxygen::tagDestinationMap; // all tag locations
157StringUnorderedSet Doxygen::tagFileSet; // all tag file names
158StringUnorderedSet Doxygen::expandAsDefinedSet; // all macros that should be expanded
159MemberGroupInfoMap Doxygen::memberGroupInfoMap; // dictionary of the member groups heading
160std::unique_ptr<PageDef> Doxygen::mainPage;
161std::unique_ptr<NamespaceDef> Doxygen::globalNamespaceDef;
183std::mutex Doxygen::addExampleMutex;
185
186// locally accessible globals
187static std::multimap< std::string, const Entry* > g_classEntries;
189static OutputList *g_outputList = nullptr; // list of output generating objects
190static StringSet g_usingDeclarations; // used classes
191static bool g_successfulRun = FALSE;
192static bool g_dumpSymbolMap = FALSE;
194static bool g_singleComment=false;
195
196
197
198// keywords recognized as compounds
200{ "template class", "template struct", "class", "struct", "union", "interface", "exception" };
201
203{
204 g_inputFiles.clear();
205 //g_excludeNameDict.clear();
206 //delete g_outputList; g_outputList=nullptr;
207
212 Doxygen::pageLinkedMap->clear();
225 Doxygen::mainPage.reset();
227}
228
230{
231 public:
233 void begin(const char *name)
234 {
235 msg("{}", name);
236 stats.emplace_back(name,0);
237 startTime = std::chrono::steady_clock::now();
238 }
239 void end()
240 {
241 std::chrono::steady_clock::time_point endTime = std::chrono::steady_clock::now();
242 stats.back().elapsed = static_cast<double>(std::chrono::duration_cast<
243 std::chrono::microseconds>(endTime - startTime).count())/1000000.0;
244 warn_flush();
245 }
246 void print()
247 {
248 bool restore=FALSE;
250 {
252 restore=TRUE;
253 }
254 msg("----------------------\n");
255 for (const auto &s : stats)
256 {
257 msg("Spent {:.6f} seconds in {}",s.elapsed,s.name);
258 }
259 if (restore) Debug::setFlag(Debug::Time);
260 }
261 private:
262 struct stat
263 {
264 const char *name;
265 double elapsed;
266 //stat() : name(nullptr),elapsed(0) {}
267 stat(const char *n, double el) : name(n),elapsed(el) {}
268 };
269 std::vector<stat> stats;
270 std::chrono::steady_clock::time_point startTime;
272
273
274static void addMemberDocs(const Entry *root,MemberDefMutable *md, const QCString &funcDecl,
275 const ArgumentList *al,bool over_load,TypeSpecifier spec);
276static void findMember(const Entry *root,
277 const QCString &relates,
278 const QCString &type,
279 const QCString &args,
280 QCString funcDecl,
281 bool overloaded,
282 bool isFunc
283 );
284
291
292
293static bool findClassRelation(
294 const Entry *root,
295 Definition *context,
296 ClassDefMutable *cd,
297 const BaseInfo *bi,
298 const TemplateNameMap &templateNames,
299 /*bool insertUndocumented*/
301 bool isArtificial
302 );
303
304//----------------------------------------------------------------------------
305
307 FileDef *fileScope,const TagInfo *tagInfo);
308static void resolveTemplateInstanceInType(const Entry *root,const Definition *scope,const MemberDef *md);
309
310static void addPageToContext(PageDef *pd,Entry *root)
311{
312 if (root->parent()) // add the page to it's scope
313 {
314 QCString scope = root->parent()->name;
315 if (root->parent()->section.isPackageDoc())
316 {
317 scope=substitute(scope,".","::");
318 }
319 scope = stripAnonymousNamespaceScope(scope);
320 scope+="::"+pd->name();
322 if (d)
323 {
324 pd->setPageScope(d);
325 }
326 }
327}
328
329static void addRelatedPage(Entry *root)
330{
331 GroupDef *gd=nullptr;
332 for (const Grouping &g : root->groups)
333 {
334 if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname))) break;
335 }
336 //printf("---> addRelatedPage() %s gd=%p\n",qPrint(root->name),gd);
337 QCString doc=root->doc+root->inbodyDocs;
338
339 PageDef *pd = addRelatedPage(root->name, // name
340 root->args, // ptitle
341 doc, // doc
342 root->docFile, // fileName
343 root->docLine, // docLine
344 root->startLine, // startLine
345 root->sli, // sli
346 gd, // gd
347 root->tagInfo(), // tagInfo
348 FALSE, // xref
349 root->lang // lang
350 );
351 if (pd)
352 {
353 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
355 pd->setLocalToc(root->localToc);
356 addPageToContext(pd,root);
357 }
358}
359
360static void buildGroupListFiltered(const Entry *root,bool additional, bool includeExternal)
361{
362 if (root->section.isGroupDoc() && !root->name.isEmpty() &&
363 ((!includeExternal && root->tagInfo()==nullptr) ||
364 ( includeExternal && root->tagInfo()!=nullptr))
365 )
366 {
367 AUTO_TRACE("additional={} includeExternal={}",additional,includeExternal);
368 if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
369 (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
370 {
371 GroupDef *gd = Doxygen::groupLinkedMap->find(root->name);
372 AUTO_TRACE_ADD("Processing group '{}':'{}' gd={}", root->type,root->name,(void*)gd);
373
374 if (gd)
375 {
376 if ( !gd->hasGroupTitle() )
377 {
378 gd->setGroupTitle( root->type );
379 }
380 else if ( root->type.length() > 0 && root->name != root->type && gd->groupTitle() != root->type )
381 {
382 warn( root->fileName,root->startLine,
383 "group {}: ignoring title \"{}\" that does not match old title \"{}\"",
384 root->name, root->type, gd->groupTitle() );
385 }
386 gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
387 gd->setDocumentation( root->doc, root->docFile, root->docLine );
388 gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
390 gd->setRefItems(root->sli);
392 gd->setLanguage(root->lang);
394 {
395 root->commandOverrides.apply_groupGraph([&](bool b) { gd->overrideGroupGraph(b); });
396 }
397 }
398 else
399 {
400 if (root->tagInfo())
401 {
402 gd = Doxygen::groupLinkedMap->add(root->name,
403 std::unique_ptr<GroupDef>(
404 createGroupDef(root->fileName,root->startLine,root->name,root->type,root->tagInfo()->fileName)));
405 gd->setReference(root->tagInfo()->tagName);
406 }
407 else
408 {
409 gd = Doxygen::groupLinkedMap->add(root->name,
410 std::unique_ptr<GroupDef>(
411 createGroupDef(root->fileName,root->startLine,root->name,root->type)));
412 }
413 gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
414 // allow empty docs for group
415 gd->setDocumentation(!root->doc.isEmpty() ? root->doc : QCString(" "),root->docFile,root->docLine,FALSE);
416 gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
418 gd->setRefItems(root->sli);
420 gd->setLanguage(root->lang);
422 {
423 root->commandOverrides.apply_groupGraph([&](bool b) { gd->overrideGroupGraph(b); });
424 }
425 }
426 }
427 }
428 for (const auto &e : root->children()) buildGroupListFiltered(e.get(),additional,includeExternal);
429}
430
431static void buildGroupList(const Entry *root)
432{
433 // --- first process only local groups
434 // first process the @defgroups blocks
436 // then process the @addtogroup, @weakgroup blocks
438
439 // --- then also process external groups
440 // first process the @defgroups blocks
442 // then process the @addtogroup, @weakgroup blocks
444}
445
446static void findGroupScope(const Entry *root)
447{
448 if (root->section.isGroupDoc() && !root->name.isEmpty() &&
449 root->parent() && !root->parent()->name.isEmpty())
450 {
451 GroupDef *gd = Doxygen::groupLinkedMap->find(root->name);
452 if (gd)
453 {
454 QCString scope = root->parent()->name;
455 if (root->parent()->section.isPackageDoc())
456 {
457 scope=substitute(scope,".","::");
458 }
459 scope = stripAnonymousNamespaceScope(scope);
460 scope+="::"+gd->name();
462 if (d)
463 {
464 gd->setGroupScope(d);
465 }
466 }
467 }
468 for (const auto &e : root->children()) findGroupScope(e.get());
469}
470
471static void organizeSubGroupsFiltered(const Entry *root,bool additional)
472{
473 if (root->section.isGroupDoc() && !root->name.isEmpty())
474 {
475 AUTO_TRACE("additional={}",additional);
476 if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
477 (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
478 {
479 GroupDef *gd = Doxygen::groupLinkedMap->find(root->name);
480 if (gd)
481 {
482 AUTO_TRACE_ADD("adding {} to group {}",root->name,gd->name());
483 addGroupToGroups(root,gd);
484 }
485 }
486 }
487 for (const auto &e : root->children()) organizeSubGroupsFiltered(e.get(),additional);
488}
489
490static void organizeSubGroups(const Entry *root)
491{
492 //printf("Defining groups\n");
493 // first process the @defgroups blocks
495 //printf("Additional groups\n");
496 // then process the @addtogroup, @weakgroup blocks
498}
499
500//----------------------------------------------------------------------
501
502static void buildFileList(const Entry *root)
503{
504 if ((root->section.isFileDoc() || (root->section.isFile() && Config_getBool(EXTRACT_ALL))) &&
505 !root->name.isEmpty() && !root->tagInfo() // skip any file coming from tag files
506 )
507 {
508 bool ambig = false;
510 if (!fd || ambig)
511 {
512 bool save_ambig = ambig;
513 // use the directory of the file to see if the described file is in the same
514 // directory as the describing file.
515 QCString fn = root->fileName;
516 int newIndex=fn.findRev('/');
517 if (newIndex<0)
518 {
519 fn = root->name;
520 }
521 else
522 {
523 fn = fn.left(newIndex)+"/"+root->name;
524 }
526 if (!fd) ambig = save_ambig;
527 }
528 //printf("**************** root->name=%s fd=%p\n",qPrint(root->name),(void*)fd);
529 if (fd && !ambig)
530 {
531 //printf("Adding documentation!\n");
532 // using FALSE in setDocumentation is small hack to make sure a file
533 // is documented even if a \file command is used without further
534 // documentation
535 fd->setDocumentation(root->doc,root->docFile,root->docLine,FALSE);
536 fd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
538 fd->setRefItems(root->sli);
540 root->commandOverrides.apply_includeGraph ([&](bool b) { fd->overrideIncludeGraph(b); });
541 root->commandOverrides.apply_includedByGraph([&](bool b) { fd->overrideIncludedByGraph(b); });
542 for (const Grouping &g : root->groups)
543 {
544 GroupDef *gd=nullptr;
545 if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname)))
546 {
547 if (!gd->containsFile(fd))
548 {
549 gd->addFile(fd);
550 fd->makePartOfGroup(gd);
551 //printf("File %s: in group %s\n",qPrint(fd->name()),qPrint(gd->name()));
552 }
553 }
554 else if (!gd && g.pri == Grouping::GROUPING_INGROUP)
555 {
556 warn(root->fileName, root->startLine,
557 "Found non-existing group '{}' for the command '{}', ignoring command",
559 );
560 }
561 }
562 }
563 else
564 {
566 text.sprintf("the name '%s' supplied as "
567 "the argument in the \\file statement ",
568 qPrint(root->name));
569 if (ambig) // name is ambiguous
570 {
571 text+="matches the following input files:\n";
573 text+="\n";
574 text+="Please use a more specific name by "
575 "including a (larger) part of the path!";
576 }
577 else // name is not an input file
578 {
579 text+="is not an input file";
580 }
581 warn(root->fileName,root->startLine,"{}", text);
582 }
583 }
584 for (const auto &e : root->children()) buildFileList(e.get());
585}
586
587template<class DefMutable>
588static void addIncludeFile(DefMutable *cd,FileDef *ifd,const Entry *root)
589{
590 if (
591 (!root->doc.stripWhiteSpace().isEmpty() ||
592 !root->brief.stripWhiteSpace().isEmpty() ||
593 Config_getBool(EXTRACT_ALL)
594 ) && root->protection!=Protection::Private
595 )
596 {
597 //printf(">>>>>> includeFile=%s\n",qPrint(root->includeFile));
598
599 bool local=Config_getBool(FORCE_LOCAL_INCLUDES);
600 QCString includeFile = root->includeFile;
601 if (!includeFile.isEmpty() && includeFile.at(0)=='"')
602 {
603 local = TRUE;
604 includeFile=includeFile.mid(1,includeFile.length()-2);
605 }
606 else if (!includeFile.isEmpty() && includeFile.at(0)=='<')
607 {
608 local = FALSE;
609 includeFile=includeFile.mid(1,includeFile.length()-2);
610 }
611
612 bool ambig = false;
613 FileDef *fd=nullptr;
614 // see if we need to include a verbatim copy of the header file
615 //printf("root->includeFile=%s\n",qPrint(root->includeFile));
616 if (!includeFile.isEmpty() &&
617 (fd=findFileDef(Doxygen::inputNameLinkedMap,includeFile,ambig))==nullptr
618 )
619 { // explicit request
620 QCString text;
621 text.sprintf("the name '%s' supplied as "
622 "the argument of the \\class, \\struct, \\union, or \\include command ",
623 qPrint(includeFile)
624 );
625 if (ambig) // name is ambiguous
626 {
627 text+="matches the following input files:\n";
629 text+="\n";
630 text+="Please use a more specific name by "
631 "including a (larger) part of the path!";
632 }
633 else // name is not an input file
634 {
635 text+="is not an input file";
636 }
637 warn(root->fileName,root->startLine, "{}", text);
638 }
639 else if (includeFile.isEmpty() && ifd &&
640 // see if the file extension makes sense
641 guessSection(ifd->name()).isHeader())
642 { // implicit assumption
643 fd=ifd;
644 }
645
646 // if a file is found, we mark it as a source file.
647 if (fd)
648 {
649 QCString iName = !root->includeName.isEmpty() ?
650 root->includeName : includeFile;
651 if (!iName.isEmpty()) // user specified include file
652 {
653 if (iName.at(0)=='<') local=FALSE; // explicit override
654 else if (iName.at(0)=='"') local=TRUE;
655 if (iName.at(0)=='"' || iName.at(0)=='<')
656 {
657 iName=iName.mid(1,iName.length()-2); // strip quotes or brackets
658 }
659 if (iName.isEmpty())
660 {
661 iName=fd->name();
662 }
663 }
664 else if (!Config_getList(STRIP_FROM_INC_PATH).empty())
665 {
667 }
668 else // use name of the file containing the class definition
669 {
670 iName=fd->name();
671 }
672 if (fd->generateSourceFile()) // generate code for header
673 {
674 cd->setIncludeFile(fd,iName,local,!root->includeName.isEmpty());
675 }
676 else // put #include in the class documentation without link
677 {
678 cd->setIncludeFile(nullptr,iName,local,TRUE);
679 }
680 }
681 }
682}
683
684
686{
687 size_t l = s.length();
688 int count=0;
689 int round=0;
690 QCString result;
691 for (size_t i=0;i<l;i++)
692 {
693 char c=s.at(i);
694 if (c=='(') round++;
695 else if (c==')' && round>0) round--;
696 else if (c=='<' && round==0) count++;
697 if (count==0)
698 {
699 result+=c;
700 }
701 if (c=='>' && round==0 && count>0) count--;
702 }
703 //printf("stripTemplateSpecifiers(%s)=%s\n",qPrint(s),qPrint(result));
704 return result;
705}
706
707/*! returns the Definition object belonging to the first \a level levels of
708 * full qualified name \a name. Creates an artificial scope if the scope is
709 * not found and set the parent/child scope relation if the scope is found.
710 */
711[[maybe_unused]]
712static Definition *buildScopeFromQualifiedName(const QCString &name_,SrcLangExt lang,const TagInfo *tagInfo)
713{
714 QCString name = stripTemplateSpecifiers(name_);
715 name.stripPrefix("::");
716 int level = name.contains("::");
717 //printf("buildScopeFromQualifiedName(%s) level=%d\n",qPrint(name),level);
718 int i=0, p=0, l=0;
720 QCString fullScope;
721 while (i<level)
722 {
723 int idx=getScopeFragment(name,p,&l);
724 if (idx==-1) return prevScope;
725 QCString nsName = name.mid(idx,l);
726 if (nsName.isEmpty()) return prevScope;
727 if (!fullScope.isEmpty()) fullScope+="::";
728 fullScope+=nsName;
729 NamespaceDef *nd=Doxygen::namespaceLinkedMap->find(fullScope);
730 DefinitionMutable *innerScope = toDefinitionMutable(nd);
731 ClassDef *cd=nullptr;
732 if (nd==nullptr) cd = getClass(fullScope);
733 if (nd==nullptr && cd) // scope is a class
734 {
735 innerScope = toDefinitionMutable(cd);
736 }
737 else if (nd==nullptr && cd==nullptr && fullScope.find('<')==-1) // scope is not known and could be a namespace!
738 {
739 // introduce bogus namespace
740 //printf("++ adding dummy namespace %s to %s tagInfo=%p\n",qPrint(nsName),qPrint(prevScope->name()),(void*)tagInfo);
741 NamespaceDefMutable *newNd=
743 Doxygen::namespaceLinkedMap->add(fullScope,
745 "[generated]",1,1,fullScope,
746 tagInfo?tagInfo->tagName:QCString(),
747 tagInfo?tagInfo->fileName:QCString())));
748 if (newNd)
749 {
750 newNd->setLanguage(lang);
751 newNd->setArtificial(TRUE);
752 // add namespace to the list
753 innerScope = newNd;
754 }
755 }
756 else // scope is a namespace
757 {
758 }
759 if (innerScope)
760 {
761 // make the parent/child scope relation
762 DefinitionMutable *prevScopeMutable = toDefinitionMutable(prevScope);
763 if (prevScopeMutable)
764 {
765 prevScopeMutable->addInnerCompound(toDefinition(innerScope));
766 }
767 innerScope->setOuterScope(prevScope);
768 }
769 else // current scope is a class, so return only the namespace part...
770 {
771 return prevScope;
772 }
773 // proceed to the next scope fragment
774 p=idx+l+2;
775 prevScope=toDefinition(innerScope);
776 i++;
777 }
778 return prevScope;
779}
780
782 FileDef *fileScope,const TagInfo *tagInfo)
783{
784 //printf("<findScopeFromQualifiedName(%s,%s)\n",startScope ? qPrint(startScope->name()) : 0, qPrint(n));
785 Definition *resultScope=toDefinition(startScope);
786 if (resultScope==nullptr) resultScope=Doxygen::globalScope;
788 int l1 = 0;
789 int i1 = getScopeFragment(scope,0,&l1);
790 if (i1==-1)
791 {
792 //printf(">no fragments!\n");
793 return resultScope;
794 }
795 int p=i1+l1,l2=0,i2=0;
796 while ((i2=getScopeFragment(scope,p,&l2))!=-1)
797 {
798 QCString nestedNameSpecifier = scope.mid(i1,l1);
799 Definition *orgScope = resultScope;
800 //printf(" nestedNameSpecifier=%s\n",qPrint(nestedNameSpecifier));
801 resultScope = const_cast<Definition*>(resultScope->findInnerCompound(nestedNameSpecifier));
802 //printf(" resultScope=%p\n",resultScope);
803 if (resultScope==nullptr)
804 {
805 if (orgScope==Doxygen::globalScope && fileScope && !fileScope->getUsedNamespaces().empty())
806 // also search for used namespaces
807 {
808 for (const auto &nd : fileScope->getUsedNamespaces())
809 {
811 if (mnd)
812 {
813 resultScope = findScopeFromQualifiedName(toNamespaceDefMutable(nd),n,fileScope,tagInfo);
814 if (resultScope!=nullptr) break;
815 }
816 }
817 if (resultScope)
818 {
819 // for a nested class A::I in used namespace N, we get
820 // N::A::I while looking for A, so we should compare
821 // resultScope->name() against scope.left(i2+l2)
822 //printf(" -> result=%s scope=%s\n",qPrint(resultScope->name()),qPrint(scope));
823 if (rightScopeMatch(resultScope->name(),scope.left(i2+l2)))
824 {
825 break;
826 }
827 goto nextFragment;
828 }
829 }
830
831 // also search for used classes. Complication: we haven't been able
832 // to put them in the right scope yet, because we are still resolving
833 // the scope relations!
834 // Therefore loop through all used classes and see if there is a right
835 // scope match between the used class and nestedNameSpecifier.
836 for (const auto &usedName : g_usingDeclarations)
837 {
838 //printf("Checking using class %s\n",qPrint(usedName));
839 if (rightScopeMatch(usedName,nestedNameSpecifier))
840 {
841 // ui.currentKey() is the fully qualified name of nestedNameSpecifier
842 // so use this instead.
843 QCString fqn = usedName + scope.right(scope.length()-p);
844 resultScope = buildScopeFromQualifiedName(fqn,startScope->getLanguage(),nullptr);
845 //printf("Creating scope from fqn=%s result %p\n",qPrint(fqn),resultScope);
846 if (resultScope)
847 {
848 //printf("> Match! resultScope=%s\n",qPrint(resultScope->name()));
849 return resultScope;
850 }
851 }
852 }
853
854 //printf("> name %s not found in scope %s\n",qPrint(nestedNameSpecifier),qPrint(orgScope->name()));
855 return nullptr;
856 }
857 nextFragment:
858 i1=i2;
859 l1=l2;
860 p=i2+l2;
861 }
862 //printf(">findScopeFromQualifiedName scope %s\n",qPrint(resultScope->name()));
863 return resultScope;
864}
865
866std::unique_ptr<ArgumentList> getTemplateArgumentsFromName(
867 const QCString &name,
868 const ArgumentLists &tArgLists)
869{
870 // for each scope fragment, check if it is a template and advance through
871 // the list if so.
872 int i=0, p=0;
873 auto alIt = tArgLists.begin();
874 while ((i=name.find("::",p))!=-1 && alIt!=tArgLists.end())
875 {
876 NamespaceDef *nd = Doxygen::namespaceLinkedMap->find(name.left(i));
877 if (nd==nullptr)
878 {
879 ClassDef *cd = getClass(name.left(i));
880 if (cd)
881 {
882 if (!cd->templateArguments().empty())
883 {
884 ++alIt;
885 }
886 }
887 }
888 p=i+2;
889 }
890 return alIt!=tArgLists.end() ?
891 std::make_unique<ArgumentList>(*alIt) :
892 std::unique_ptr<ArgumentList>();
893}
894
895static
897{
899
900 if (specifier.isStruct())
902 else if (specifier.isUnion())
903 sec=ClassDef::Union;
904 else if (specifier.isCategory())
906 else if (specifier.isInterface())
908 else if (specifier.isProtocol())
910 else if (specifier.isException())
912 else if (specifier.isService())
914 else if (specifier.isSingleton())
916
917 if (section.isUnionDoc())
918 sec=ClassDef::Union;
919 else if (section.isStructDoc())
921 else if (section.isInterfaceDoc())
923 else if (section.isProtocolDoc())
925 else if (section.isCategoryDoc())
927 else if (section.isExceptionDoc())
929 else if (section.isServiceDoc())
931 else if (section.isSingletonDoc())
933
934 return sec;
935}
936
937
938static void addClassToContext(const Entry *root)
939{
940 AUTO_TRACE("name={}",root->name);
941 FileDef *fd = root->fileDef();
942
943 QCString scName;
944 if (root->parent()->section.isScope())
945 {
946 scName=root->parent()->name;
947 }
948 // name without parent's scope
949 QCString fullName = root->name;
950
951 // strip off any template parameters (but not those for specializations)
952 int idx=fullName.find('>');
953 if (idx!=-1 && root->lang==SrcLangExt::CSharp) // mangle A<S,T>::N as A-2-g::N
954 {
955 fullName = mangleCSharpGenericName(fullName.left(idx+1))+fullName.mid(idx+1);
956 }
957 fullName=stripTemplateSpecifiersFromScope(fullName);
958
959 // name with scope (if not present already)
960 QCString qualifiedName = fullName;
961 if (!scName.isEmpty() && !leftScopeMatch(scName,fullName))
962 {
963 qualifiedName.prepend(scName+"::");
964 }
965
966 // see if we already found the class before
967 ClassDefMutable *cd = getClassMutable(qualifiedName);
968
969 AUTO_TRACE_ADD("Found class with name '{}', qualifiedName '{}'", cd ? cd->name() : root->name, qualifiedName);
970
971 if (cd)
972 {
973 fullName=cd->name();
974 AUTO_TRACE_ADD("Existing class '{}'",cd->name());
975 //if (cd->templateArguments()==0)
976 //{
977 // //printf("existing ClassDef tempArgList=%p specScope=%s\n",root->tArgList,qPrint(root->scopeSpec));
978 // cd->setTemplateArguments(tArgList);
979 //}
980
981 cd->setDocumentation(root->doc,root->docFile,root->docLine);
982 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
983 root->commandOverrides.apply_collaborationGraph([&](bool b ) { cd->overrideCollaborationGraph(b); });
984 root->commandOverrides.apply_inheritanceGraph ([&](CLASS_GRAPH_t gt) { cd->overrideInheritanceGraph(gt); });
985
986 if (!root->spec.isForwardDecl() && cd->isForwardDeclared())
987 {
988 cd->setDefFile(root->fileName,root->startLine,root->startColumn);
989 if (root->bodyLine!=-1)
990 {
991 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
992 cd->setBodyDef(fd);
993 }
994 }
995
996 if (cd->templateArguments().empty() || (cd->isForwardDeclared() && !root->spec.isForwardDecl()))
997 {
998 // this happens if a template class declared with @class is found
999 // before the actual definition or if a forward declaration has different template
1000 // parameter names.
1001 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(cd->name(),root->tArgLists);
1002 if (tArgList)
1003 {
1004 cd->setTemplateArguments(*tArgList);
1005 }
1006 }
1007 if (cd->requiresClause().isEmpty() && !root->req.isEmpty())
1008 {
1009 cd->setRequiresClause(root->req);
1010 }
1011
1013
1014 cd->setMetaData(root->metaData);
1015 }
1016 else // new class
1017 {
1019
1020 QCString className;
1021 QCString namespaceName;
1022 extractNamespaceName(fullName,className,namespaceName);
1023
1024 AUTO_TRACE_ADD("New class: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1025 fullName, namespaceName, className, Trace::trunc(root->brief), Trace::trunc(root->doc));
1026
1027 QCString tagName;
1028 QCString refFileName;
1029 const TagInfo *tagInfo = root->tagInfo();
1030 if (tagInfo)
1031 {
1032 tagName = tagInfo->tagName;
1033 refFileName = tagInfo->fileName;
1034 if (fullName.find("::")!=-1)
1035 // symbols imported via tag files may come without the parent scope,
1036 // so we artificially create it here
1037 {
1038 buildScopeFromQualifiedName(fullName,root->lang,tagInfo);
1039 }
1040 }
1041 std::unique_ptr<ArgumentList> tArgList;
1042 int i=0;
1043 if ((root->lang==SrcLangExt::CSharp || root->lang==SrcLangExt::Java) &&
1044 (i=fullName.find('<'))!=-1)
1045 {
1046 // a Java/C# generic class looks like a C++ specialization, so we need to split the
1047 // name and template arguments here
1048 tArgList = stringToArgumentList(root->lang,fullName.mid(i));
1049 if (i!=-1 && root->lang==SrcLangExt::CSharp) // in C# A, A<T>, and A<T,S> are different classes, so we need some way to disguish them using this name mangling
1050 // A -> A
1051 // A<T> -> A-1-g
1052 // A<T,S> -> A-2-g
1053 {
1054 fullName=mangleCSharpGenericName(fullName);
1055 }
1056 else
1057 {
1058 fullName=fullName.left(i);
1059 }
1060 }
1061 else
1062 {
1063 tArgList = getTemplateArgumentsFromName(fullName,root->tArgLists);
1064 }
1065 // add class to the list
1066 cd = toClassDefMutable(
1067 Doxygen::classLinkedMap->add(fullName,
1068 createClassDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1069 fullName,sec,tagName,refFileName,TRUE,root->spec.isEnum()) ));
1070 if (cd)
1071 {
1072 AUTO_TRACE_ADD("New class '{}' type={} #tArgLists={} tagInfo={} hidden={} artificial={}",
1073 fullName,cd->compoundTypeString(),root->tArgLists.size(),
1074 fmt::ptr(tagInfo),root->hidden,root->artificial);
1075 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1076 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1077 cd->setLanguage(root->lang);
1078 cd->setId(root->id);
1079 cd->setHidden(root->hidden);
1080 cd->setArtificial(root->artificial);
1081 cd->setClassSpecifier(root->spec);
1082 cd->addQualifiers(root->qualifiers);
1083 cd->setTypeConstraints(root->typeConstr);
1084 root->commandOverrides.apply_collaborationGraph([&](bool b ) { cd->overrideCollaborationGraph(b); });
1085 root->commandOverrides.apply_inheritanceGraph ([&](CLASS_GRAPH_t gt) { cd->overrideInheritanceGraph(gt); });
1086
1087 if (tArgList)
1088 {
1089 cd->setTemplateArguments(*tArgList);
1090 }
1091 cd->setRequiresClause(root->req);
1092 cd->setProtection(root->protection);
1093 cd->setIsStatic(root->isStatic);
1094
1095 // file definition containing the class cd
1096 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1097 cd->setBodyDef(fd);
1098
1099 cd->setMetaData(root->metaData);
1100
1101 cd->insertUsedFile(fd);
1102 }
1103 else
1104 {
1105 AUTO_TRACE_ADD("Class {} not added, already exists as alias", fullName);
1106 }
1107 }
1108
1109 if (cd)
1110 {
1112 if (!root->subGrouping) cd->setSubGrouping(FALSE);
1113 if (!root->spec.isForwardDecl())
1114 {
1115 if (cd->hasDocumentation())
1116 {
1117 addIncludeFile(cd,fd,root);
1118 }
1119 if (fd && root->section.isCompound())
1120 {
1121 AUTO_TRACE_ADD("Inserting class {} in file {} (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1122 cd->setFileDef(fd);
1123 fd->insertClass(cd);
1124 }
1125 }
1126 addClassToGroups(root,cd);
1128 cd->setRefItems(root->sli);
1129 cd->setRequirementReferences(root->rqli);
1130 }
1131}
1132
1133//----------------------------------------------------------------------
1134// build a list of all classes mentioned in the documentation
1135// and all classes that have a documentation block before their definition.
1136static void buildClassList(const Entry *root)
1137{
1138 if ((root->section.isCompound() || root->section.isObjcImpl()) && !root->name.isEmpty())
1139 {
1140 AUTO_TRACE();
1141 addClassToContext(root);
1142 }
1143 for (const auto &e : root->children()) buildClassList(e.get());
1144}
1145
1146static void buildClassDocList(const Entry *root)
1147{
1148 if ((root->section.isCompoundDoc()) && !root->name.isEmpty())
1149 {
1150 AUTO_TRACE();
1151 addClassToContext(root);
1152 }
1153 for (const auto &e : root->children()) buildClassDocList(e.get());
1154}
1155
1156//----------------------------------------------------------------------
1157// build a list of all classes mentioned in the documentation
1158// and all classes that have a documentation block before their definition.
1159
1160static void addConceptToContext(const Entry *root)
1161{
1162 AUTO_TRACE();
1163 FileDef *fd = root->fileDef();
1164
1165 QCString scName;
1166 if (root->parent()->section.isScope())
1167 {
1168 scName=root->parent()->name;
1169 }
1170
1171 // name with scope (if not present already)
1172 QCString qualifiedName = root->name;
1173 if (!scName.isEmpty() && !leftScopeMatch(qualifiedName,scName))
1174 {
1175 qualifiedName.prepend(scName+"::");
1176 }
1177
1178 // see if we already found the concept before
1179 ConceptDefMutable *cd = getConceptMutable(qualifiedName);
1180
1181 AUTO_TRACE_ADD("Found concept with name '{}' (qualifiedName='{}')", cd ? cd->name() : root->name, qualifiedName);
1182
1183 if (cd)
1184 {
1185 qualifiedName=cd->name();
1186 AUTO_TRACE_ADD("Existing concept '{}'",cd->name());
1187
1188 cd->setDocumentation(root->doc,root->docFile,root->docLine);
1189 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1190
1191 addIncludeFile(cd,fd,root);
1192 }
1193 else // new concept
1194 {
1195 QCString className;
1196 QCString namespaceName;
1197 extractNamespaceName(qualifiedName,className,namespaceName);
1198
1199 AUTO_TRACE_ADD("New concept: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1200 qualifiedName,namespaceName,className,root->brief,root->doc);
1201
1202 QCString tagName;
1203 QCString refFileName;
1204 const TagInfo *tagInfo = root->tagInfo();
1205 if (tagInfo)
1206 {
1207 tagName = tagInfo->tagName;
1208 refFileName = tagInfo->fileName;
1209 if (qualifiedName.find("::")!=-1)
1210 // symbols imported via tag files may come without the parent scope,
1211 // so we artificially create it here
1212 {
1213 buildScopeFromQualifiedName(qualifiedName,root->lang,tagInfo);
1214 }
1215 }
1216 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(qualifiedName,root->tArgLists);
1217 // add concept to the list
1219 Doxygen::conceptLinkedMap->add(qualifiedName,
1220 createConceptDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1221 qualifiedName,tagName,refFileName)));
1222 if (cd)
1223 {
1224 AUTO_TRACE_ADD("New concept '{}' #tArgLists={} tagInfo={}",
1225 qualifiedName,root->tArgLists.size(),fmt::ptr(tagInfo));
1226 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1227 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1228 cd->setLanguage(root->lang);
1229 cd->setId(root->id);
1230 cd->setHidden(root->hidden);
1231 cd->setGroupId(root->mGrpId);
1232 if (tArgList)
1233 {
1234 cd->setTemplateArguments(*tArgList);
1235 }
1236 cd->setInitializer(root->initializer.str());
1237 // file definition containing the class cd
1238 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1239 cd->setBodyDef(fd);
1241 cd->setRefItems(root->sli);
1242 cd->setRequirementReferences(root->rqli);
1243 addIncludeFile(cd,fd,root);
1244
1245 // also add namespace to the correct structural context
1246 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,qualifiedName,nullptr,tagInfo);
1248 {
1250 if (dm)
1251 {
1252 dm->addInnerCompound(cd);
1253 }
1254 cd->setOuterScope(d);
1255 }
1256 for (const auto &ce : root->children())
1257 {
1258 //printf("Concept %s has child %s\n",qPrint(root->name),qPrint(ce->section.to_string()));
1259 if (ce->section.isConceptDocPart())
1260 {
1261 cd->addSectionsToDefinition(ce->anchors);
1262 cd->setRefItems(ce->sli);
1263 cd->setRequirementReferences(ce->rqli);
1264 if (!ce->brief.isEmpty())
1265 {
1266 cd->addDocPart(ce->brief,ce->startLine,ce->startColumn);
1267 //printf(" brief=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->brief),ce->startLine,ce->startColumn);
1268 }
1269 if (!ce->doc.isEmpty())
1270 {
1271 cd->addDocPart(ce->doc,ce->startLine,ce->startColumn);
1272 //printf(" doc=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->doc),ce->startLine,ce->startColumn);
1273 }
1274 }
1275 else if (ce->section.isConceptCodePart())
1276 {
1277 cd->addCodePart(ce->initializer.str(),ce->startLine,ce->startColumn);
1278 //printf(" code=[[\n%s\n]] line=%d,col=%d\n",qPrint(ce->initializer.str()),ce->startLine,ce->startColumn);
1279 }
1280 }
1281 }
1282 else
1283 {
1284 AUTO_TRACE_ADD("Concept '{}' not added, already exists (as alias)", qualifiedName);
1285 }
1286 }
1287
1288 if (cd)
1289 {
1291 for (const auto &ce : root->children())
1292 {
1293 if (ce->section.isConceptDocPart())
1294 {
1295 cd->addSectionsToDefinition(ce->anchors);
1296 }
1297 }
1298 if (fd)
1299 {
1300 AUTO_TRACE_ADD("Inserting concept '{}' in file '{}' (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1301 cd->setFileDef(fd);
1302 fd->insertConcept(cd);
1303 }
1304 addConceptToGroups(root,cd);
1306 cd->setRefItems(root->sli);
1307 cd->setRequirementReferences(root->rqli);
1308 }
1309}
1310
1311static void findModuleDocumentation(const Entry *root)
1312{
1313 if (root->section.isModuleDoc())
1314 {
1315 AUTO_TRACE();
1317 }
1318 for (const auto &e : root->children()) findModuleDocumentation(e.get());
1319}
1320
1321static void buildConceptList(const Entry *root)
1322{
1323 if (root->section.isConcept())
1324 {
1325 AUTO_TRACE();
1326 addConceptToContext(root);
1327 }
1328 for (const auto &e : root->children()) buildConceptList(e.get());
1329}
1330
1331static void buildConceptDocList(const Entry *root)
1332{
1333 if (root->section.isConceptDoc())
1334 {
1335 AUTO_TRACE();
1336 addConceptToContext(root);
1337 }
1338 for (const auto &e : root->children()) buildConceptDocList(e.get());
1339}
1340
1341// This routine is to allow @ingroup X @{ concept A; concept B; @} to work
1342// (same also works for variable and functions because of logic in MemberGroup::insertMember)
1344{
1345 AUTO_TRACE();
1346 for (const auto &cd : *Doxygen::conceptLinkedMap)
1347 {
1348 if (cd->groupId()!=DOX_NOGROUP)
1349 {
1350 for (const auto &ocd : *Doxygen::conceptLinkedMap)
1351 {
1352 if (cd!=ocd && cd->groupId()==ocd->groupId() &&
1353 !cd->partOfGroups().empty() && ocd->partOfGroups().empty())
1354 {
1355 ConceptDefMutable *ocdm = toConceptDefMutable(ocd.get());
1356 if (ocdm)
1357 {
1358 for (const auto &gd : cd->partOfGroups())
1359 {
1360 if (gd)
1361 {
1362 AUTO_TRACE_ADD("making concept '{}' part of group '{}'",ocdm->name(),gd->name());
1363 ocdm->makePartOfGroup(gd);
1364 gd->addConcept(ocd.get());
1365 }
1366 }
1367 }
1368 }
1369 }
1370 }
1371 }
1372}
1373
1374//----------------------------------------------------------------------
1375
1377{
1378 ClassDefSet visitedClasses;
1379
1380 bool done=FALSE;
1381 //int iteration=0;
1382 while (!done)
1383 {
1384 done=TRUE;
1385 //++iteration;
1386 struct ClassAlias
1387 {
1388 ClassAlias(const QCString &name,std::unique_ptr<ClassDef> cd,DefinitionMutable *ctx) :
1389 aliasFullName(name),aliasCd(std::move(cd)), aliasContext(ctx) {}
1390 QCString aliasFullName;
1391 std::unique_ptr<ClassDef> aliasCd;
1392 DefinitionMutable *aliasContext;
1393 };
1394 std::vector<ClassAlias> aliases;
1395 for (const auto &icd : *Doxygen::classLinkedMap)
1396 {
1397 ClassDefMutable *cd = toClassDefMutable(icd.get());
1398 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1399 {
1400 QCString name = stripAnonymousNamespaceScope(icd->name());
1401 //printf("processing=%s, iteration=%d\n",qPrint(cd->name()),iteration);
1402 // also add class to the correct structural context
1404 name,icd->getFileDef(),nullptr);
1405 if (d)
1406 {
1407 //printf("****** adding %s to scope %s in iteration %d\n",qPrint(cd->name()),qPrint(d->name()),iteration);
1409 if (dm)
1410 {
1411 dm->addInnerCompound(cd);
1412 }
1413 cd->setOuterScope(d);
1414
1415 // for inline namespace add an alias of the class to the outer scope
1417 {
1419 //printf("nd->isInline()=%d\n",nd->isInline());
1420 if (nd && nd->isInline())
1421 {
1422 d = d->getOuterScope();
1423 if (d)
1424 {
1425 dm = toDefinitionMutable(d);
1426 if (dm)
1427 {
1428 auto aliasCd = createClassDefAlias(d,cd);
1429 QCString aliasFullName = d->qualifiedName()+"::"+aliasCd->localName();
1430 aliases.emplace_back(aliasFullName,std::move(aliasCd),dm);
1431 //printf("adding %s to %s as %s\n",qPrint(aliasCd->name()),qPrint(d->name()),qPrint(aliasFullName));
1432 }
1433 }
1434 }
1435 else
1436 {
1437 break;
1438 }
1439 }
1440
1441 visitedClasses.insert(icd.get());
1442 done=FALSE;
1443 }
1444 //else
1445 //{
1446 // printf("****** ignoring %s: scope not (yet) found in iteration %d\n",qPrint(cd->name()),iteration);
1447 //}
1448 }
1449 }
1450 // add aliases
1451 for (auto &alias : aliases)
1452 {
1453 ClassDef *aliasCd = Doxygen::classLinkedMap->add(alias.aliasFullName,std::move(alias.aliasCd));
1454 if (aliasCd)
1455 {
1456 alias.aliasContext->addInnerCompound(aliasCd);
1457 }
1458 }
1459 }
1460
1461 //give warnings for unresolved compounds
1462 for (const auto &icd : *Doxygen::classLinkedMap)
1463 {
1464 ClassDefMutable *cd = toClassDefMutable(icd.get());
1465 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1466 {
1468 /// create the scope artificially
1469 // anyway, so we can at least relate scopes properly.
1470 Definition *d = buildScopeFromQualifiedName(name,cd->getLanguage(),nullptr);
1471 if (d && d!=cd && !cd->getDefFileName().isEmpty())
1472 // avoid recursion in case of redundant scopes, i.e: namespace N { class N::C {}; }
1473 // for this case doxygen assumes the existence of a namespace N::N in which C is to be found!
1474 // also avoid warning for stuff imported via a tagfile.
1475 {
1477 if (dm)
1478 {
1479 dm->addInnerCompound(cd);
1480 }
1481 cd->setOuterScope(d);
1482 warn(cd->getDefFileName(),cd->getDefLine(),
1483 "Incomplete input: scope for class {} not found!{}",name,
1484 name.startsWith("std::") ? " Try enabling BUILTIN_STL_SUPPORT." : ""
1485 );
1486 }
1487 }
1488 }
1489}
1490
1492{
1493 //bool inlineGroupedClasses = Config_getBool(INLINE_GROUPED_CLASSES);
1494 //if (!inlineGroupedClasses) return;
1495 //printf("** distributeClassGroupRelations()\n");
1496
1497 ClassDefSet visitedClasses;
1498 for (const auto &cd : *Doxygen::classLinkedMap)
1499 {
1500 //printf("Checking %s\n",qPrint(cd->name()));
1501 // distribute the group to nested classes as well
1502 if (visitedClasses.find(cd.get())==visitedClasses.end() && !cd->partOfGroups().empty())
1503 {
1504 //printf(" Candidate for merging\n");
1505 GroupDef *gd = cd->partOfGroups().front();
1506 for (auto &ncd : cd->getClasses())
1507 {
1509 if (ncdm && ncdm->partOfGroups().empty())
1510 {
1511 //printf(" Adding %s to group '%s'\n",qPrint(ncd->name()),
1512 // gd->groupTitle());
1513 ncdm->makePartOfGroup(gd);
1514 gd->addClass(ncdm);
1515 }
1516 }
1517 visitedClasses.insert(cd.get()); // only visit every class once
1518 }
1519 }
1520}
1521
1522//----------------------------
1523
1524static ClassDefMutable *createTagLessInstance(const ClassDef *rootCd,const ClassDef *templ,const QCString &fieldName)
1525{
1526 QCString fullName = removeAnonymousScopes(templ->name());
1527 if (fullName.endsWith("::")) fullName=fullName.left(fullName.length()-2);
1528 fullName+="."+fieldName;
1529
1530 //printf("** adding class %s based on %s\n",qPrint(fullName),qPrint(templ->name()));
1532 Doxygen::classLinkedMap->add(fullName,
1534 templ->getDefLine(),
1535 templ->getDefColumn(),
1536 fullName,
1537 templ->compoundType())));
1538 if (cd)
1539 {
1540 cd->setDocumentation(templ->documentation(),templ->docFile(),templ->docLine()); // copy docs to definition
1541 cd->setBriefDescription(templ->briefDescription(),templ->briefFile(),templ->briefLine());
1542 cd->setLanguage(templ->getLanguage());
1543 cd->setBodySegment(templ->getDefLine(),templ->getStartBodyLine(),templ->getEndBodyLine());
1544 cd->setBodyDef(templ->getBodyDef());
1545
1546 cd->setOuterScope(rootCd->getOuterScope());
1547 if (rootCd->getOuterScope()!=Doxygen::globalScope)
1548 {
1549 DefinitionMutable *outerScope = toDefinitionMutable(rootCd->getOuterScope());
1550 if (outerScope)
1551 {
1552 outerScope->addInnerCompound(cd);
1553 }
1554 }
1555
1556 FileDef *fd = templ->getFileDef();
1557 if (fd)
1558 {
1559 cd->setFileDef(fd);
1560 fd->insertClass(cd);
1561 }
1562 for (auto &gd : rootCd->partOfGroups())
1563 {
1564 cd->makePartOfGroup(gd);
1565 gd->addClass(cd);
1566 }
1567
1568 MemberList *ml = templ->getMemberList(MemberListType::PubAttribs());
1569 if (ml)
1570 {
1571 for (const auto &md : *ml)
1572 {
1573 //printf(" Member %s type=%s\n",qPrint(md->name()),md->typeString());
1574 auto newMd = createMemberDef(md->getDefFileName(),md->getDefLine(),md->getDefColumn(),
1575 md->typeString(),md->name(),md->argsString(),md->excpString(),
1576 md->protection(),md->virtualness(),md->isStatic(),Relationship::Member,
1577 md->memberType(),
1578 ArgumentList(),ArgumentList(),"");
1579 MemberDefMutable *imd = toMemberDefMutable(newMd.get());
1580 imd->setMemberClass(cd);
1581 imd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
1582 imd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
1583 imd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
1584 imd->setMemberSpecifiers(md->getMemberSpecifiers());
1585 imd->setVhdlSpecifiers(md->getVhdlSpecifiers());
1586 imd->setMemberGroupId(md->getMemberGroupId());
1587 imd->setInitializer(md->initializer());
1588 imd->setRequiresClause(md->requiresClause());
1589 imd->setMaxInitLines(md->initializerLines());
1590 imd->setBitfields(md->bitfieldString());
1591 imd->setLanguage(md->getLanguage());
1592 cd->insertMember(imd);
1593 MemberName *mn = Doxygen::memberNameLinkedMap->add(md->name());
1594 mn->push_back(std::move(newMd));
1595 }
1596 }
1597 }
1598 return cd;
1599}
1600
1601/** Look through the members of class \a cd and its public members.
1602 * If there is a member m of a tag less struct/union,
1603 * then we create a duplicate of the struct/union with the name of the
1604 * member to identify it.
1605 * So if cd has name S, then the tag less struct/union will get name S.m
1606 * Since tag less structs can be nested we need to call this function
1607 * recursively. Later on we need to patch the member types so we keep
1608 * track of the hierarchy of classes we create.
1609 */
1610static void processTagLessClasses(const ClassDef *rootCd,
1611 const ClassDef *cd,
1612 ClassDefMutable *tagParentCd,
1613 const QCString &prefix,int count)
1614{
1615 //printf("%d: processTagLessClasses %s\n",count,qPrint(cd->name()));
1616 //printf("checking members for %s\n",qPrint(cd->name()));
1617 if (tagParentCd && !cd->getClasses().empty())
1618 {
1619 MemberList *ml = cd->getMemberList(MemberListType::PubAttribs());
1620 if (ml)
1621 {
1622 int pos=0;
1623 for (const auto &md : *ml)
1624 {
1625 QCString type = md->typeString();
1626 if (type.find("::@")!=-1) // member of tag less struct/union
1627 {
1628 for (const auto &icd : cd->getClasses())
1629 {
1630 //printf(" member %s: type='%s'\n",qPrint(md->name()),qPrint(type));
1631 //printf(" comparing '%s'<->'%s'\n",qPrint(type),qPrint(icd->name()));
1632 if (type.find(icd->name())!=-1) // matching tag less struct/union
1633 {
1634 QCString name = md->name();
1635 if (md->isAnonymous()) name = "__unnamed" + QCString().setNum(pos++)+"__";
1636 if (!prefix.isEmpty()) name.prepend(prefix+".");
1637 //printf(" found %s for class %s\n",qPrint(name),qPrint(cd->name()));
1638 ClassDefMutable *ncd = createTagLessInstance(rootCd,icd,name);
1639 if (ncd)
1640 {
1641 processTagLessClasses(rootCd,icd,ncd,name,count+1);
1642 //printf(" addTagged %s to %s\n",qPrint(ncd->name()),qPrint(tagParentCd->name()));
1643 ncd->setTagLessReference(icd);
1644
1645 // replace tag-less type for generated/original member
1646 // by newly created class name.
1647 // note the difference between changing cd and tagParentCd.
1648 // for the initial call this is the same pointer, but for
1649 // recursive calls cd is the original tag-less struct (of which
1650 // there is only one instance) and tagParentCd is the newly
1651 // generated tagged struct of which there can be multiple instances!
1652 MemberList *pml = tagParentCd->getMemberList(MemberListType::PubAttribs());
1653 if (pml)
1654 {
1655 for (const auto &pmd : *pml)
1656 {
1658 if (pmdm && pmd->name()==md->name())
1659 {
1660 pmdm->setAccessorType(ncd,substitute(pmd->typeString(),icd->name(),ncd->name()));
1661 //pmd->setType(substitute(pmd->typeString(),icd->name(),ncd->name()));
1662 }
1663 }
1664 }
1665 }
1666 }
1667 }
1668 }
1669 }
1670 }
1671 }
1672}
1673
1674static void findTagLessClasses(std::vector<ClassDefMutable*> &candidates,ClassDef *cd)
1675{
1676 for (const auto &icd : cd->getClasses())
1677 {
1678 if (icd->name().find("@")==-1) // process all non-anonymous inner classes
1679 {
1680 findTagLessClasses(candidates,icd);
1681 }
1682 }
1683
1685 if (cdm)
1686 {
1687 candidates.push_back(cdm);
1688 }
1689}
1690
1692{
1693 std::vector<ClassDefMutable *> candidates;
1694 for (auto &cd : *Doxygen::classLinkedMap)
1695 {
1696 Definition *scope = cd->getOuterScope();
1697 if (scope && scope->definitionType()!=Definition::TypeClass) // that is not nested
1698 {
1699 findTagLessClasses(candidates,cd.get());
1700 }
1701 }
1702
1703 // since processTagLessClasses is potentially adding classes to Doxygen::classLinkedMap
1704 // we need to call it outside of the loop above, otherwise the iterator gets invalidated!
1705 for (auto &cd : candidates)
1706 {
1707 processTagLessClasses(cd,cd,cd,"",0); // process tag less inner struct/classes
1708 }
1709}
1710
1711
1712//----------------------------------------------------------------------
1713// build a list of all namespaces mentioned in the documentation
1714// and all namespaces that have a documentation block before their definition.
1715static void buildNamespaceList(const Entry *root)
1716{
1717 if (
1718 (root->section.isNamespace() ||
1719 root->section.isNamespaceDoc() ||
1720 root->section.isPackageDoc()
1721 ) &&
1722 !root->name.isEmpty()
1723 )
1724 {
1725 AUTO_TRACE("name={}",root->name);
1726
1727 QCString fName = root->name;
1728 if (root->section.isPackageDoc())
1729 {
1730 fName=substitute(fName,".","::");
1731 }
1732
1733 QCString fullName = stripAnonymousNamespaceScope(fName);
1734 if (!fullName.isEmpty())
1735 {
1736 AUTO_TRACE_ADD("Found namespace {} in {} at line {}",root->name,root->fileName,root->startLine);
1737 NamespaceDef *ndi = Doxygen::namespaceLinkedMap->find(fullName);
1738 if (ndi) // existing namespace
1739 {
1741 if (nd) // non-inline namespace
1742 {
1743 AUTO_TRACE_ADD("Existing namespace");
1744 nd->setDocumentation(root->doc,root->docFile,root->docLine);
1745 nd->setName(fullName); // change name to match docs
1747 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1748 if (nd->getLanguage()==SrcLangExt::Unknown)
1749 {
1750 nd->setLanguage(root->lang);
1751 }
1752 if (root->tagInfo()==nullptr && nd->isReference() && !(root->doc.isEmpty() && root->brief.isEmpty()))
1753 // if we previously found namespace nd in a tag file and now we find a
1754 // documented namespace with the same name in the project, then remove
1755 // the tag file reference
1756 {
1757 nd->setReference("");
1758 nd->setFileName(fullName);
1759 }
1760 nd->setMetaData(root->metaData);
1761
1762 // file definition containing the namespace nd
1763 FileDef *fd=root->fileDef();
1764 if (nd->isArtificial())
1765 {
1766 nd->setArtificial(FALSE); // found namespace explicitly, so cannot be artificial
1767 nd->setDefFile(root->fileName,root->startLine,root->startColumn);
1768 }
1769 // insert the namespace in the file definition
1770 if (fd) fd->insertNamespace(nd);
1771 addNamespaceToGroups(root,nd);
1772 nd->setRefItems(root->sli);
1773 nd->setRequirementReferences(root->rqli);
1774 }
1775 }
1776 else // fresh namespace
1777 {
1778 QCString tagName;
1779 QCString tagFileName;
1780 const TagInfo *tagInfo = root->tagInfo();
1781 if (tagInfo)
1782 {
1783 tagName = tagInfo->tagName;
1784 tagFileName = tagInfo->fileName;
1785 }
1786 AUTO_TRACE_ADD("new namespace {} lang={} tagName={}",fullName,langToString(root->lang),tagName);
1787 // add namespace to the list
1789 Doxygen::namespaceLinkedMap->add(fullName,
1790 createNamespaceDef(tagInfo?tagName:root->fileName,root->startLine,
1791 root->startColumn,fullName,tagName,tagFileName,
1792 root->type,root->spec.isPublished())));
1793 if (nd)
1794 {
1795 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1796 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1798 nd->setHidden(root->hidden);
1799 nd->setArtificial(root->artificial);
1800 nd->setLanguage(root->lang);
1801 nd->setId(root->id);
1802 nd->setMetaData(root->metaData);
1803 nd->setInline(root->spec.isInline());
1804 nd->setExported(root->exported);
1805
1806 addNamespaceToGroups(root,nd);
1807 nd->setRefItems(root->sli);
1808 nd->setRequirementReferences(root->rqli);
1809
1810 // file definition containing the namespace nd
1811 FileDef *fd=root->fileDef();
1812 // insert the namespace in the file definition
1813 if (fd) fd->insertNamespace(nd);
1814
1815 // the empty string test is needed for extract all case
1816 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1817 nd->insertUsedFile(fd);
1818 nd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1819 nd->setBodyDef(fd);
1820
1821 // also add namespace to the correct structural context
1822 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,fullName,nullptr,tagInfo);
1823 AUTO_TRACE_ADD("adding namespace {} to context {}",nd->name(),d ? d->name() : QCString("<none>"));
1824 if (d==nullptr) // we didn't find anything, create the scope artificially
1825 // anyway, so we can at least relate scopes properly.
1826 {
1827 d = buildScopeFromQualifiedName(fullName,nd->getLanguage(),tagInfo);
1829 if (dm)
1830 {
1831 dm->addInnerCompound(nd);
1832 }
1833 nd->setOuterScope(d);
1834 // TODO: Due to the order in which the tag file is written
1835 // a nested class can be found before its parent!
1836 }
1837 else
1838 {
1840 if (dm)
1841 {
1842 dm->addInnerCompound(nd);
1843 }
1844 nd->setOuterScope(d);
1845 // in case of d is an inline namespace, alias insert nd in the part scope of d.
1847 {
1848 NamespaceDef *pnd = toNamespaceDef(d);
1849 if (pnd && pnd->isInline())
1850 {
1851 d = d->getOuterScope();
1852 if (d)
1853 {
1854 dm = toDefinitionMutable(d);
1855 if (dm)
1856 {
1857 auto aliasNd = createNamespaceDefAlias(d,nd);
1858 dm->addInnerCompound(aliasNd.get());
1859 QCString aliasName = aliasNd->name();
1860 AUTO_TRACE_ADD("adding alias {} to {}",aliasName,d->name());
1861 Doxygen::namespaceLinkedMap->add(aliasName,std::move(aliasNd));
1862 }
1863 }
1864 else
1865 {
1866 break;
1867 }
1868 }
1869 else
1870 {
1871 break;
1872 }
1873 }
1874 }
1875 }
1876 }
1877 }
1878 }
1879 for (const auto &e : root->children()) buildNamespaceList(e.get());
1880}
1881
1882//----------------------------------------------------------------------
1883
1885 const QCString &name)
1886{
1887 NamespaceDef *usingNd =nullptr;
1888 for (auto &und : unl)
1889 {
1890 QCString uScope=und->name()+"::";
1891 usingNd = getResolvedNamespace(uScope+name);
1892 if (usingNd!=nullptr) break;
1893 }
1894 return usingNd;
1895}
1896
1897static void findUsingDirectives(const Entry *root)
1898{
1899 if (root->section.isUsingDir())
1900 {
1901 AUTO_TRACE("Found using directive {} at line {} of {}",root->name,root->startLine,root->fileName);
1902 QCString name=substitute(root->name,".","::");
1903 if (name.endsWith("::"))
1904 {
1905 name=name.left(name.length()-2);
1906 }
1907 if (!name.isEmpty())
1908 {
1909 NamespaceDef *usingNd = nullptr;
1910 NamespaceDefMutable *nd = nullptr;
1911 FileDef *fd = root->fileDef();
1912 QCString nsName;
1913
1914 // see if the using statement was found inside a namespace or inside
1915 // the global file scope.
1916 if (root->parent() && root->parent()->section.isNamespace() &&
1917 (fd==nullptr || fd->getLanguage()!=SrcLangExt::Java) // not a .java file
1918 )
1919 {
1920 nsName=stripAnonymousNamespaceScope(root->parent()->name);
1921 if (!nsName.isEmpty())
1922 {
1923 nd = getResolvedNamespaceMutable(nsName);
1924 }
1925 }
1926
1927 // find the scope in which the 'using' namespace is defined by prepending
1928 // the possible scopes in which the using statement was found, starting
1929 // with the most inner scope and going to the most outer scope (i.e.
1930 // file scope).
1931 int scopeOffset = static_cast<int>(nsName.length());
1932 do
1933 {
1934 QCString scope=scopeOffset>0 ?
1935 nsName.left(scopeOffset)+"::" : QCString();
1936 usingNd = getResolvedNamespace(scope+name);
1937 //printf("Trying with scope='%s' usingNd=%p\n",(scope+qPrint(name)),usingNd);
1938 if (scopeOffset==0)
1939 {
1940 scopeOffset=-1;
1941 }
1942 else if ((scopeOffset=nsName.findRev("::",scopeOffset-1))==-1)
1943 {
1944 scopeOffset=0;
1945 }
1946 } while (scopeOffset>=0 && usingNd==nullptr);
1947
1948 if (usingNd==nullptr && nd) // not found, try used namespaces in this scope
1949 // or in one of the parent namespace scopes
1950 {
1951 const NamespaceDefMutable *pnd = nd;
1952 while (pnd && usingNd==nullptr)
1953 {
1954 // also try with one of the used namespaces found earlier
1956
1957 // goto the parent
1958 Definition *s = pnd->getOuterScope();
1960 {
1962 }
1963 else
1964 {
1965 pnd = nullptr;
1966 }
1967 }
1968 }
1969 if (usingNd==nullptr && fd) // still nothing, also try used namespace in the
1970 // global scope
1971 {
1972 usingNd = findUsedNamespace(fd->getUsedNamespaces(),name);
1973 }
1974
1975 //printf("%s -> %s\n",qPrint(name),usingNd?qPrint(usingNd->name()):"<none>");
1976
1977 // add the namespace the correct scope
1978 if (usingNd)
1979 {
1980 //printf("using fd=%p nd=%p\n",fd,nd);
1981 if (nd)
1982 {
1983 //printf("Inside namespace %s\n",qPrint(nd->name()));
1984 nd->addUsingDirective(usingNd);
1985 }
1986 else if (fd)
1987 {
1988 //printf("Inside file %s\n",qPrint(fd->name()));
1989 fd->addUsingDirective(usingNd);
1990 }
1991 }
1992 else // unknown namespace, but add it anyway.
1993 {
1994 AUTO_TRACE_ADD("new unknown namespace {} lang={} hidden={}",name,langToString(root->lang),root->hidden);
1995 // add namespace to the list
1998 createNamespaceDef(root->fileName,root->startLine,root->startColumn,name)));
1999 if (nd)
2000 {
2001 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
2002 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2004 nd->setHidden(root->hidden);
2005 nd->setArtificial(TRUE);
2006 nd->setLanguage(root->lang);
2007 nd->setId(root->id);
2008 nd->setMetaData(root->metaData);
2009 nd->setInline(root->spec.isInline());
2010 nd->setExported(root->exported);
2011
2012 for (const Grouping &g : root->groups)
2013 {
2014 GroupDef *gd=nullptr;
2015 if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname)))
2016 gd->addNamespace(nd);
2017 }
2018
2019 // insert the namespace in the file definition
2020 if (fd)
2021 {
2022 fd->insertNamespace(nd);
2023 fd->addUsingDirective(nd);
2024 }
2025
2026 // the empty string test is needed for extract all case
2027 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2028 nd->insertUsedFile(fd);
2029 nd->setRefItems(root->sli);
2030 nd->setRequirementReferences(root->rqli);
2031 }
2032 }
2033 }
2034 }
2035 for (const auto &e : root->children()) findUsingDirectives(e.get());
2036}
2037
2038//----------------------------------------------------------------------
2039
2040static void buildListOfUsingDecls(const Entry *root)
2041{
2042 if (root->section.isUsingDecl() &&
2043 !root->parent()->section.isCompound() // not a class/struct member
2044 )
2045 {
2046 QCString name = substitute(root->name,".","::");
2047 g_usingDeclarations.insert(name.str());
2048 }
2049 for (const auto &e : root->children()) buildListOfUsingDecls(e.get());
2050}
2051
2052
2053static void findUsingDeclarations(const Entry *root,bool filterPythonPackages)
2054{
2055 if (root->section.isUsingDecl() &&
2056 !root->parent()->section.isCompound() && // not a class/struct member
2057 (!filterPythonPackages || (root->lang==SrcLangExt::Python && root->fileName.endsWith("__init__.py")))
2058 )
2059 {
2060 AUTO_TRACE("Found using declaration '{}' at line {} of {} inside section {}",
2061 root->name,root->startLine,root->fileName,root->parent()->section);
2062 if (!root->name.isEmpty())
2063 {
2064 const Definition *usingDef = nullptr;
2065 NamespaceDefMutable *nd = nullptr;
2066 FileDef *fd = root->fileDef();
2067 QCString scName;
2068
2069 // see if the using statement was found inside a namespace or inside
2070 // the global file scope.
2071 if (root->parent()->section.isNamespace())
2072 {
2073 scName=root->parent()->name;
2074 if (!scName.isEmpty())
2075 {
2076 nd = getResolvedNamespaceMutable(scName);
2077 }
2078 }
2079
2080 // Assume the using statement was used to import a class.
2081 // Find the scope in which the 'using' namespace is defined by prepending
2082 // the possible scopes in which the using statement was found, starting
2083 // with the most inner scope and going to the most outer scope (i.e.
2084 // file scope).
2085
2086 QCString name = substitute(root->name,".","::"); //Java/C# scope->internal
2087
2088 SymbolResolver resolver;
2089 const Definition *scope = nd;
2090 if (nd==nullptr) scope = fd;
2091 usingDef = resolver.resolveSymbol(scope,name);
2092
2093 //printf("usingDef(scope=%s,name=%s)=%s\n",qPrint(nd?nd->qualifiedName():""),qPrint(name),usingDef?qPrint(usingDef->qualifiedName()):"nullptr");
2094
2095 if (!usingDef)
2096 {
2097 usingDef = getClass(name); // try direct lookup, this is needed to get
2098 // builtin STL classes to properly resolve, e.g.
2099 // vector -> std::vector
2100 }
2101 if (!usingDef)
2102 {
2103 usingDef = Doxygen::hiddenClassLinkedMap->find(name); // check if it is already hidden
2104 }
2105#if 0
2106 if (!usingDef)
2107 {
2108 AUTO_TRACE_ADD("New using class '{}' (sec={})! #tArgLists={}",
2109 name,root->section,root->tArgLists.size());
2112 createClassDef( "<using>",1,1, name, ClassDef::Class)));
2113 if (usingCd)
2114 {
2115 usingCd->setArtificial(TRUE);
2116 usingCd->setLanguage(root->lang);
2117 usingDef = usingCd;
2118 }
2119 }
2120#endif
2121 else
2122 {
2123 AUTO_TRACE_ADD("Found used type '{}' in scope='{}'",
2124 usingDef->name(), nd ? nd->name(): fd ? fd->name() : QCString("<unknown>"));
2125 }
2126
2127 if (usingDef)
2128 {
2129 if (nd)
2130 {
2131 nd->addUsingDeclaration(usingDef);
2132 }
2133 else if (fd)
2134 {
2135 fd->addUsingDeclaration(usingDef);
2136 }
2137 }
2138 }
2139 }
2140 for (const auto &e : root->children()) findUsingDeclarations(e.get(),filterPythonPackages);
2141}
2142
2143//----------------------------------------------------------------------
2144
2146{
2147 root->commandOverrides.apply_callGraph ([&](bool b) { md->overrideCallGraph(b); });
2148 root->commandOverrides.apply_callerGraph ([&](bool b) { md->overrideCallerGraph(b); });
2149 root->commandOverrides.apply_referencedByRelation([&](bool b) { md->overrideReferencedByRelation(b); });
2150 root->commandOverrides.apply_referencesRelation ([&](bool b) { md->overrideReferencesRelation(b); });
2151 root->commandOverrides.apply_inlineSource ([&](bool b) { md->overrideInlineSource(b); });
2152 root->commandOverrides.apply_enumValues ([&](bool b) { md->overrideEnumValues(b); });
2153}
2154
2155//----------------------------------------------------------------------
2156
2158 const QCString &fileName,const QCString &memName)
2159{
2160 AUTO_TRACE("creating new member {} for class {}",memName,cd->name());
2161 const ArgumentList &templAl = md->templateArguments();
2162 const ArgumentList &al = md->argumentList();
2163 auto newMd = createMemberDef(
2164 fileName,root->startLine,root->startColumn,
2165 md->typeString(),memName,md->argsString(),
2166 md->excpString(),root->protection,root->virt,
2167 md->isStatic(),Relationship::Member,md->memberType(),
2168 templAl,al,root->metaData
2169 );
2170 auto newMmd = toMemberDefMutable(newMd.get());
2171 newMmd->setMemberClass(cd);
2172 cd->insertMember(newMd.get());
2173 if (!root->doc.isEmpty() || !root->brief.isEmpty())
2174 {
2175 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2176 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2177 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2178 }
2179 else
2180 {
2181 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2182 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2183 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2184 }
2185 newMmd->setDefinition(md->definition());
2186 applyMemberOverrideOptions(root,newMmd);
2187 newMmd->addQualifiers(root->qualifiers);
2188 newMmd->setBitfields(md->bitfieldString());
2189 newMmd->addSectionsToDefinition(root->anchors);
2190 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2191 newMmd->setBodyDef(md->getBodyDef());
2192 newMmd->setInitializer(md->initializer());
2193 newMmd->setRequiresClause(md->requiresClause());
2194 newMmd->setMaxInitLines(md->initializerLines());
2195 newMmd->setMemberGroupId(root->mGrpId);
2196 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2197 newMmd->setVhdlSpecifiers(md->getVhdlSpecifiers());
2198 newMmd->setLanguage(root->lang);
2199 newMmd->setId(root->id);
2200 MemberName *mn = Doxygen::memberNameLinkedMap->add(memName);
2201 mn->push_back(std::move(newMd));
2202}
2203
2204static std::unordered_map<std::string,std::vector<ClassDefMutable*>> g_usingClassMap;
2205
2206static void findUsingDeclImports(const Entry *root)
2207{
2208 if (root->section.isUsingDecl() &&
2209 root->parent()->section.isCompound() // in a class/struct member
2210 )
2211 {
2212 AUTO_TRACE("Found using declaration '{}' inside section {}", root->name, root->parent()->section);
2213 QCString fullName=removeRedundantWhiteSpace(root->parent()->name);
2214 fullName=stripAnonymousNamespaceScope(fullName);
2215 fullName=stripTemplateSpecifiersFromScope(fullName);
2216 ClassDefMutable *cd = getClassMutable(fullName);
2217 if (cd)
2218 {
2219 AUTO_TRACE_ADD("found class '{}'",cd->name());
2220 int i=root->name.findRev("::");
2221 if (i!=-1)
2222 {
2223 QCString scope=root->name.left(i);
2224 QCString memName=root->name.right(root->name.length()-i-2);
2225 SymbolResolver resolver;
2226 const ClassDef *bcd = resolver.resolveClass(cd,scope); // todo: file in fileScope parameter
2227 AUTO_TRACE_ADD("name={} scope={} bcd={}",scope,cd?cd->name():"<none>",bcd?bcd->name():"<none>");
2228 if (bcd && bcd!=cd)
2229 {
2230 AUTO_TRACE_ADD("found class '{}' memName='{}'",bcd->name(),memName);
2232 const MemberNameInfo *mni = mnlm.find(memName);
2233 if (mni)
2234 {
2235 for (auto &mi : *mni)
2236 {
2237 const MemberDef *md = mi->memberDef();
2238 if (md && md->protection()!=Protection::Private)
2239 {
2240 AUTO_TRACE_ADD("found member '{}'",mni->memberName());
2241 QCString fileName = root->fileName;
2242 if (fileName.isEmpty() && root->tagInfo())
2243 {
2244 fileName = root->tagInfo()->tagName;
2245 }
2246 if (!cd->containsOverload(md))
2247 {
2248 createUsingMemberImportForClass(root,cd,md,fileName,memName);
2249 // also insert the member into copies of the class
2250 auto it = g_usingClassMap.find(cd->qualifiedName().str());
2251 if (it != g_usingClassMap.end())
2252 {
2253 for (const auto &copyCd : it->second)
2254 {
2255 createUsingMemberImportForClass(root,copyCd,md,fileName,memName);
2256 }
2257 }
2258 }
2259 }
2260 }
2261 }
2262 }
2263 }
2264 }
2265 }
2266 else if (root->section.isUsingDecl() &&
2267 (root->parent()->section.isNamespace() || root->parent()->section.isEmpty()) && // namespace or global member
2268 root->lang==SrcLangExt::Cpp // do we also want this for e.g. Fortran? (see test case 095)
2269 )
2270 {
2271 AUTO_TRACE("Found using declaration '{}' inside section {}", root->name, root->parent()->section);
2272 Definition *scope = nullptr;
2273 NamespaceDefMutable *nd = nullptr;
2274 FileDef *fd = root->parent()->fileDef();
2275 if (!root->parent()->name.isEmpty())
2276 {
2277 QCString fullName=removeRedundantWhiteSpace(root->parent()->name);
2278 fullName=stripAnonymousNamespaceScope(fullName);
2280 scope = nd;
2281 }
2282 else
2283 {
2284 scope = fd;
2285 }
2286 if (scope)
2287 {
2288 AUTO_TRACE_ADD("found scope '{}'",scope->name());
2289 SymbolResolver resolver;
2290 const Definition *def = resolver.resolveSymbol(root->name.startsWith("::") ? nullptr : scope,root->name);
2291 if (def && def->definitionType()==Definition::TypeMember)
2292 {
2293 int i=root->name.findRev("::");
2294 QCString memName;
2295 if (i!=-1)
2296 {
2297 memName = root->name.right(root->name.length()-i-2);
2298 }
2299 else
2300 {
2301 memName = root->name;
2302 }
2303 const MemberDef *md = toMemberDef(def);
2304 AUTO_TRACE_ADD("found member '{}' for name '{}'",md->qualifiedName(),root->name);
2305 QCString fileName = root->fileName;
2306 if (fileName.isEmpty() && root->tagInfo())
2307 {
2308 fileName = root->tagInfo()->tagName;
2309 }
2310 const ArgumentList &templAl = md->templateArguments();
2311 const ArgumentList &al = md->argumentList();
2312
2313 auto newMd = createMemberDef(
2314 fileName,root->startLine,root->startColumn,
2315 md->typeString(),memName,md->argsString(),
2316 md->excpString(),root->protection,root->virt,
2317 md->isStatic(),Relationship::Member,md->memberType(),
2318 templAl,al,root->metaData
2319 );
2320 auto newMmd = toMemberDefMutable(newMd.get());
2321 if (nd)
2322 {
2323 newMmd->setNamespace(nd);
2324 nd->insertMember(newMd.get());
2325 }
2326 if (fd)
2327 {
2328 newMmd->setFileDef(fd);
2329 fd->insertMember(newMd.get());
2330 }
2331 if (!root->doc.isEmpty() || !root->brief.isEmpty())
2332 {
2333 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2334 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2335 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2336 }
2337 else
2338 {
2339 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2340 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2341 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2342 }
2343 newMmd->setDefinition(md->definition());
2344 applyMemberOverrideOptions(root,newMmd);
2345 newMmd->addQualifiers(root->qualifiers);
2346 newMmd->setBitfields(md->bitfieldString());
2347 newMmd->addSectionsToDefinition(root->anchors);
2348 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2349 newMmd->setBodyDef(md->getBodyDef());
2350 newMmd->setInitializer(md->initializer());
2351 newMmd->setRequiresClause(md->requiresClause());
2352 newMmd->setMaxInitLines(md->initializerLines());
2353 newMmd->setMemberGroupId(root->mGrpId);
2354 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2355 newMmd->setVhdlSpecifiers(md->getVhdlSpecifiers());
2356 newMmd->setLanguage(root->lang);
2357 newMmd->setId(root->id);
2358 MemberName *mn = Doxygen::functionNameLinkedMap->add(memName);
2359 mn->push_back(std::move(newMd));
2360#if 0 // insert an alias instead of a copy
2361 const MemberDef *md = toMemberDef(def);
2362 AUTO_TRACE_ADD("found member '{}' for name '{}'",md->qualifiedName(),root->name);
2363 auto aliasMd = createMemberDefAlias(nd,md);
2364 QCString aliasFullName = nd->qualifiedName()+"::"+aliasMd->localName();
2365 if (nd && aliasMd.get())
2366 {
2367 nd->insertMember(aliasMd.get());
2368 }
2369 if (fd && aliasMd.get())
2370 {
2371 fd->insertMember(aliasMd.get());
2372 }
2373 MemberName *mn = Doxygen::memberNameLinkedMap->add(aliasFullName);
2374 mn->push_back(std::move(aliasMd));
2375#endif
2376 }
2377 else if (def && def->definitionType()==Definition::TypeClass)
2378 {
2379 const ClassDef *cd = toClassDef(def);
2380 QCString copyFullName;
2381 if (nd==nullptr)
2382 {
2383 copyFullName = cd->localName();
2384 }
2385 else
2386 {
2387 copyFullName = nd->qualifiedName()+"::"+cd->localName();
2388 }
2389 if (Doxygen::classLinkedMap->find(copyFullName)==nullptr)
2390 {
2392 Doxygen::classLinkedMap->add(copyFullName,
2393 cd->deepCopy(copyFullName)));
2394 AUTO_TRACE_ADD("found class '{}' for name '{}' copy '{}' obj={}",cd->qualifiedName(),root->name,copyFullName,(void*)ncdm);
2395 g_usingClassMap[cd->qualifiedName().str()].push_back(ncdm);
2396 if (ncdm)
2397 {
2398 if (nd) ncdm->moveTo(nd);
2399 if ((!root->doc.isEmpty() || !root->brief.isEmpty())) // use docs at using statement
2400 {
2401 ncdm->setDocumentation(root->doc,root->docFile,root->docLine);
2402 ncdm->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2403 }
2404 else // use docs from used class
2405 {
2406 ncdm->setDocumentation(cd->documentation(),cd->docFile(),cd->docLine());
2408 }
2409 if (nd)
2410 {
2411 nd->addInnerCompound(ncdm);
2412 nd->addUsingDeclaration(ncdm);
2413 }
2414 if (fd)
2415 {
2416 if (ncdm) ncdm->setFileDef(fd);
2417 fd->insertClass(ncdm);
2418 fd->addUsingDeclaration(ncdm);
2419 }
2420 }
2421 }
2422#if 0 // insert an alias instead of a copy
2423 auto aliasCd = createClassDefAlias(nd,cd);
2424 QCString aliasFullName;
2425 if (nd==nullptr)
2426 {
2427 aliasFullName = aliasCd->localName();
2428 }
2429 else
2430 {
2431 aliasFullName = nd->qualifiedName()+"::"+aliasCd->localName();
2432 }
2433 AUTO_TRACE_ADD("found class '{}' for name '{}' aliasFullName='{}'",cd->qualifiedName(),root->name,aliasFullName);
2434 auto acd = Doxygen::classLinkedMap->add(aliasFullName,std::move(aliasCd));
2435 if (nd && acd)
2436 {
2437 nd->addInnerCompound(acd);
2438 }
2439 if (fd && acd)
2440 {
2441 fd->insertClass(acd);
2442 }
2443#endif
2444 }
2445 else if (scope)
2446 {
2447 AUTO_TRACE_ADD("no symbol with name '{}' in scope {}",root->name,scope->name());
2448 }
2449 }
2450 }
2451 for (const auto &e : root->children()) findUsingDeclImports(e.get());
2452}
2453
2454//----------------------------------------------------------------------
2455
2457{
2458 FileDefSet visitedFiles;
2459 // then recursively add using directives found in #include files
2460 // to files that have not been visited.
2461 for (const auto &fn : *Doxygen::inputNameLinkedMap)
2462 {
2463 for (const auto &fd : *fn)
2464 {
2465 //printf("----- adding using directives for file %s\n",qPrint(fd->name()));
2466 fd->addIncludedUsingDirectives(visitedFiles);
2467 }
2468 }
2469}
2470
2471//----------------------------------------------------------------------
2472
2474 const Entry *root,
2475 ClassDefMutable *cd,
2476 MemberType mtype,
2477 const QCString &type,
2478 const QCString &name,
2479 const QCString &args,
2480 bool fromAnnScope,
2481 MemberDef *fromAnnMemb,
2482 Protection prot,
2483 Relationship related)
2484{
2486 QCString scopeSeparator="::";
2487 SrcLangExt lang = cd->getLanguage();
2488 if (lang==SrcLangExt::Java || lang==SrcLangExt::CSharp)
2489 {
2490 qualScope = substitute(qualScope,"::",".");
2491 scopeSeparator=".";
2492 }
2493 AUTO_TRACE("class variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} ann={} init='{}'",
2494 root->fileName, type, qualScope, name, args, root->protection, mtype, lang, fromAnnScope, root->initializer.str());
2495
2496 QCString def;
2497 if (!type.isEmpty())
2498 {
2499 if (related!=Relationship::Member || mtype==MemberType::Friend || Config_getBool(HIDE_SCOPE_NAMES))
2500 {
2501 if (root->spec.isAlias()) // turn 'typedef B A' into 'using A'
2502 {
2503 def="using "+name;
2504 }
2505 else
2506 {
2507 def=type+" "+name+args;
2508 }
2509 }
2510 else
2511 {
2512 if (root->spec.isAlias()) // turn 'typedef B C::A' into 'using C::A'
2513 {
2514 def="using "+qualScope+scopeSeparator+name;
2515 }
2516 else
2517 {
2518 def=type+" "+qualScope+scopeSeparator+name+args;
2519 }
2520 }
2521 }
2522 else
2523 {
2524 if (Config_getBool(HIDE_SCOPE_NAMES))
2525 {
2526 def=name+args;
2527 }
2528 else
2529 {
2530 def=qualScope+scopeSeparator+name+args;
2531 }
2532 }
2533 def.stripPrefix("static ");
2534
2535 // see if the member is already found in the same scope
2536 // (this may be the case for a static member that is initialized
2537 // outside the class)
2539 if (mn)
2540 {
2541 for (const auto &imd : *mn)
2542 {
2543 //printf("md->getClassDef()=%p cd=%p type=[%s] md->typeString()=[%s]\n",
2544 // md->getClassDef(),cd,qPrint(type),md->typeString());
2545 MemberDefMutable *md = toMemberDefMutable(imd.get());
2546 if (md &&
2547 md->getClassDef()==cd &&
2548 ((lang==SrcLangExt::Python && type.isEmpty() && !md->typeString().isEmpty()) ||
2550 // member already in the scope
2551 {
2552
2553 if (root->lang==SrcLangExt::ObjC &&
2554 root->mtype==MethodTypes::Property &&
2556 { // Objective-C 2.0 property
2557 // turn variable into a property
2558 md->setProtection(root->protection);
2560 }
2561 addMemberDocs(root,md,def,nullptr,FALSE,root->spec);
2562 AUTO_TRACE_ADD("Member already found!");
2563 return md;
2564 }
2565 }
2566 }
2567
2568 QCString fileName = root->fileName;
2569 if (fileName.isEmpty() && root->tagInfo())
2570 {
2571 fileName = root->tagInfo()->tagName;
2572 }
2573
2574 // new member variable, typedef or enum value
2575 auto md = createMemberDef(
2576 fileName,root->startLine,root->startColumn,
2577 type,name,args,root->exception,
2578 prot,Specifier::Normal,root->isStatic,related,
2579 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2580 ArgumentList(), root->metaData);
2581 auto mmd = toMemberDefMutable(md.get());
2582 mmd->setTagInfo(root->tagInfo());
2583 mmd->setMemberClass(cd); // also sets outer scope (i.e. getOuterScope())
2584 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2585 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2586 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2587 mmd->setDefinition(def);
2588 mmd->setBitfields(root->bitfields);
2589 mmd->addSectionsToDefinition(root->anchors);
2590 mmd->setFromAnonymousScope(fromAnnScope);
2591 mmd->setFromAnonymousMember(fromAnnMemb);
2592 //md->setIndentDepth(indentDepth);
2593 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2594 mmd->setInitializer(root->initializer.str());
2595 mmd->setMaxInitLines(root->initLines);
2596 mmd->setMemberGroupId(root->mGrpId);
2597 mmd->setMemberSpecifiers(root->spec);
2598 mmd->setVhdlSpecifiers(root->vhdlSpec);
2599 mmd->setReadAccessor(root->read);
2600 mmd->setWriteAccessor(root->write);
2602 mmd->setHidden(root->hidden);
2603 mmd->setArtificial(root->artificial);
2604 mmd->setLanguage(root->lang);
2605 mmd->setId(root->id);
2606 addMemberToGroups(root,md.get());
2608 mmd->setBodyDef(root->fileDef());
2609 mmd->addQualifiers(root->qualifiers);
2610
2611 AUTO_TRACE_ADD("Adding new member to class '{}'",cd->name());
2612 cd->insertMember(md.get());
2613 mmd->setRefItems(root->sli);
2614 mmd->setRequirementReferences(root->rqli);
2615
2616 cd->insertUsedFile(root->fileDef());
2617 root->markAsProcessed();
2618
2619 if (mtype==MemberType::Typedef)
2620 {
2621 resolveTemplateInstanceInType(root,cd,md.get());
2622 }
2623
2624 // add the member to the global list
2625 MemberDef *result = md.get();
2626 mn = Doxygen::memberNameLinkedMap->add(name);
2627 mn->push_back(std::move(md));
2628
2629 return result;
2630}
2631
2632//----------------------------------------------------------------------
2633
2635 const Entry *root,
2636 MemberType mtype,
2637 const QCString &scope,
2638 const QCString &type,
2639 const QCString &name,
2640 const QCString &args,
2641 bool fromAnnScope,
2642 MemberDef *fromAnnMemb)
2643{
2644 AUTO_TRACE("global variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} init='{}'",
2645 root->fileName, type, scope, name, args, root->protection, mtype, root->lang, root->initializer.str());
2646
2647 FileDef *fd = root->fileDef();
2648
2649 // see if we have a typedef that should hide a struct or union
2650 if (mtype==MemberType::Typedef && Config_getBool(TYPEDEF_HIDES_STRUCT))
2651 {
2652 QCString ttype = type;
2653 ttype.stripPrefix("typedef ");
2654 if (ttype.stripPrefix("struct ") || ttype.stripPrefix("union "))
2655 {
2656 static const reg::Ex re(R"(\a\w*)");
2657 reg::Match match;
2658 const std::string &typ = ttype.str();
2659 if (reg::search(typ,match,re))
2660 {
2661 QCString typeValue = match.str();
2662 ClassDefMutable *cd = getClassMutable(typeValue);
2663 if (cd)
2664 {
2665 // this typedef should hide compound name cd, so we
2666 // change the name that is displayed from cd.
2667 cd->setClassName(name);
2668 cd->setDocumentation(root->doc,root->docFile,root->docLine);
2669 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2670 return nullptr;
2671 }
2672 }
2673 }
2674 }
2675
2676 // see if the function is inside a namespace
2677 NamespaceDefMutable *nd = nullptr;
2678 if (!scope.isEmpty())
2679 {
2680 if (scope.find('@')!=-1) return nullptr; // anonymous scope!
2681 nd = getResolvedNamespaceMutable(scope);
2682 }
2683 QCString def;
2684
2685 // determine the definition of the global variable
2686 if (nd && !nd->isAnonymous() &&
2687 !Config_getBool(HIDE_SCOPE_NAMES)
2688 )
2689 // variable is inside a namespace, so put the scope before the name
2690 {
2691 SrcLangExt lang = nd->getLanguage();
2693
2694 if (!type.isEmpty())
2695 {
2696 if (root->spec.isAlias()) // turn 'typedef B NS::A' into 'using NS::A'
2697 {
2698 def="using "+nd->name()+sep+name;
2699 }
2700 else // normal member
2701 {
2702 def=type+" "+nd->name()+sep+name+args;
2703 }
2704 }
2705 else
2706 {
2707 def=nd->name()+sep+name+args;
2708 }
2709 }
2710 else
2711 {
2712 if (!type.isEmpty() && !root->name.isEmpty())
2713 {
2714 if (name.at(0)=='@') // dummy variable representing anonymous union
2715 {
2716 def=type;
2717 }
2718 else
2719 {
2720 if (root->spec.isAlias()) // turn 'typedef B A' into 'using A'
2721 {
2722 def="using "+root->name;
2723 }
2724 else // normal member
2725 {
2726 def=type+" "+name+args;
2727 }
2728 }
2729 }
2730 else
2731 {
2732 def=name+args;
2733 }
2734 }
2735 def.stripPrefix("static ");
2736
2738 if (mn)
2739 {
2740 //QCString nscope=removeAnonymousScopes(scope);
2741 //NamespaceDef *nd=nullptr;
2742 //if (!nscope.isEmpty())
2743 if (!scope.isEmpty())
2744 {
2745 nd = getResolvedNamespaceMutable(scope);
2746 }
2747 for (const auto &imd : *mn)
2748 {
2749 MemberDefMutable *md = toMemberDefMutable(imd.get());
2750 if (md &&
2751 ((nd==nullptr && md->getNamespaceDef()==nullptr && md->getFileDef() &&
2752 root->fileName==md->getFileDef()->absFilePath()
2753 ) // both variable names in the same file
2754 || (nd!=nullptr && md->getNamespaceDef()==nd) // both in same namespace
2755 )
2756 && !md->isDefine() // function style #define's can be "overloaded" by typedefs or variables
2757 && !md->isEnumerate() // in C# an enum value and enum can have the same name
2758 )
2759 // variable already in the scope
2760 {
2761 bool isPHPArray = md->getLanguage()==SrcLangExt::PHP &&
2762 md->argsString()!=args &&
2763 args.find('[')!=-1;
2764 bool staticsInDifferentFiles =
2765 root->isStatic && md->isStatic() &&
2766 root->fileName!=md->getDefFileName();
2767
2768 if (md->getFileDef() &&
2769 !isPHPArray && // not a php array
2770 !staticsInDifferentFiles
2771 )
2772 // not a php array variable
2773 {
2774 AUTO_TRACE_ADD("variable already found: scope='{}'",md->getOuterScope()->name());
2775 addMemberDocs(root,md,def,nullptr,FALSE,root->spec);
2776 md->setRefItems(root->sli);
2777 md->setRequirementReferences(root->rqli);
2778 // if md is a variable forward declaration and root is the definition that
2779 // turn md into the definition
2780 if (!root->explicitExternal && md->isExternal())
2781 {
2782 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
2784 }
2785 // if md is the definition and root point at a declaration, then add the
2786 // declaration info
2787 else if (root->explicitExternal && !md->isExternal())
2788 {
2789 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
2790 }
2791 return md;
2792 }
2793 }
2794 }
2795 }
2796
2797 QCString fileName = root->fileName;
2798 if (fileName.isEmpty() && root->tagInfo())
2799 {
2800 fileName = root->tagInfo()->tagName;
2801 }
2802
2803 AUTO_TRACE_ADD("new variable, namespace='{}'",nd?nd->name():QCString("<global>"));
2804 // new global variable, enum value or typedef
2805 auto md = createMemberDef(
2806 fileName,root->startLine,root->startColumn,
2807 type,name,args,QCString(),
2808 root->protection, Specifier::Normal,root->isStatic,Relationship::Member,
2809 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2810 root->argList, root->metaData);
2811 auto mmd = toMemberDefMutable(md.get());
2812 mmd->setTagInfo(root->tagInfo());
2813 mmd->setMemberSpecifiers(root->spec);
2814 mmd->setVhdlSpecifiers(root->vhdlSpec);
2815 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2816 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2817 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2818 mmd->addSectionsToDefinition(root->anchors);
2819 mmd->setFromAnonymousScope(fromAnnScope);
2820 mmd->setFromAnonymousMember(fromAnnMemb);
2821 mmd->setInitializer(root->initializer.str());
2822 mmd->setMaxInitLines(root->initLines);
2823 mmd->setMemberGroupId(root->mGrpId);
2824 mmd->setDefinition(def);
2825 mmd->setLanguage(root->lang);
2826 mmd->setId(root->id);
2828 mmd->setExplicitExternal(root->explicitExternal,fileName,root->startLine,root->startColumn);
2829 mmd->addQualifiers(root->qualifiers);
2830 //md->setOuterScope(fd);
2831 if (!root->explicitExternal)
2832 {
2833 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2834 mmd->setBodyDef(fd);
2835 }
2836 addMemberToGroups(root,md.get());
2838
2839 mmd->setRefItems(root->sli);
2840 mmd->setRequirementReferences(root->rqli);
2841 if (nd && !nd->isAnonymous())
2842 {
2843 mmd->setNamespace(nd);
2844 nd->insertMember(md.get());
2845 }
2846
2847 // add member to the file (we do this even if we have already inserted
2848 // it into the namespace.
2849 if (fd)
2850 {
2851 mmd->setFileDef(fd);
2852 fd->insertMember(md.get());
2853 }
2854
2855 root->markAsProcessed();
2856
2857 if (mtype==MemberType::Typedef)
2858 {
2859 resolveTemplateInstanceInType(root,nd,md.get());
2860 }
2861
2862 // add member definition to the list of globals
2863 MemberDef *result = md.get();
2864 mn = Doxygen::functionNameLinkedMap->add(name);
2865 mn->push_back(std::move(md));
2866
2867
2868
2869 return result;
2870}
2871
2872/*! See if the return type string \a type is that of a function pointer
2873 * \returns -1 if this is not a function pointer variable or
2874 * the index at which the closing brace of (...*name) was found.
2875 */
2876static int findFunctionPtr(const std::string &type,SrcLangExt lang, int *pLength=nullptr)
2877{
2878 AUTO_TRACE("type='{}' lang={}",type,lang);
2879 if (lang == SrcLangExt::Fortran || lang == SrcLangExt::VHDL)
2880 {
2881 return -1; // Fortran and VHDL do not have function pointers
2882 }
2883
2884 static const reg::Ex re(R"(\‍([^)]*[*&^][^)]*\))");
2885 reg::Match match;
2886 size_t i=std::string::npos;
2887 size_t l=0;
2888 if (reg::search(type,match,re)) // contains (...*...) or (...&...) or (...^...)
2889 {
2890 i = match.position();
2891 l = match.length();
2892 }
2893 if (i!=std::string::npos)
2894 {
2895 size_t di = type.find("decltype(");
2896 if (di!=std::string::npos && di<i)
2897 {
2898 i = std::string::npos;
2899 }
2900 }
2901 size_t bb=type.find('<');
2902 size_t be=type.rfind('>');
2903 bool templFp = false;
2904 if (be!=std::string::npos) {
2905 size_t cc_ast = type.find("::*");
2906 size_t cc_amp = type.find("::&");
2907 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>::*)'
2908 }
2909
2910 if (!type.empty() && // return type is non-empty
2911 i!=std::string::npos && // contains (...*...)
2912 type.find("operator")==std::string::npos && // not an operator
2913 (type.find(")(")==std::string::npos || type.find("typedef ")!=std::string::npos) &&
2914 // not a function pointer return type
2915 (!(bb<i && i<be) || templFp) // bug665855: avoid treating "typedef A<void (T*)> type" as a function pointer
2916 )
2917 {
2918 if (pLength) *pLength=static_cast<int>(l);
2919 //printf("findFunctionPtr=%d\n",(int)i);
2920 AUTO_TRACE_EXIT("result={}",i);
2921 return static_cast<int>(i);
2922 }
2923 else
2924 {
2925 //printf("findFunctionPtr=%d\n",-1);
2926 AUTO_TRACE_EXIT("result=-1");
2927 return -1;
2928 }
2929}
2930
2931//--------------------------------------------------------------------------------------
2932
2933/*! Returns TRUE iff \a type is a class within scope \a context.
2934 * Used to detect variable declarations that look like function prototypes.
2935 */
2936static bool isVarWithConstructor(const Entry *root)
2937{
2938 bool result = false;
2939 bool typeIsClass = false;
2940 bool typePtrType = false;
2941 QCString type;
2942 Definition *ctx = nullptr;
2943 FileDef *fd = root->fileDef();
2944 SymbolResolver resolver(fd);
2945
2946 AUTO_TRACE("isVarWithConstructor({})",root->name);
2947 if (root->parent()->section.isCompound())
2948 { // inside a class
2949 result=FALSE;
2950 AUTO_TRACE_EXIT("inside class: result={}",result);
2951 return result;
2952 }
2953 else if ((fd != nullptr) && (fd->name().endsWith(".c") || fd->name().endsWith(".h")))
2954 { // inside a .c file
2955 result=FALSE;
2956 AUTO_TRACE_EXIT("inside C file: result={}",result);
2957 return result;
2958 }
2959 if (root->type.isEmpty())
2960 {
2961 result=FALSE;
2962 AUTO_TRACE_EXIT("no type: result={}",result);
2963 return result;
2964 }
2965 if (!root->parent()->name.isEmpty())
2966 {
2967 ctx=Doxygen::namespaceLinkedMap->find(root->parent()->name);
2968 }
2969 type = root->type;
2970 // remove qualifiers
2971 findAndRemoveWord(type,"const");
2972 findAndRemoveWord(type,"static");
2973 findAndRemoveWord(type,"volatile");
2974 typePtrType = type.find('*')!=-1 || type.find('&')!=-1;
2975 if (!typePtrType)
2976 {
2977 typeIsClass = resolver.resolveClass(ctx,type)!=nullptr;
2978 int ti=0;
2979 if (!typeIsClass && (ti=type.find('<'))!=-1)
2980 {
2981 typeIsClass=resolver.resolveClass(ctx,type.left(ti))!=nullptr;
2982 }
2983 }
2984 if (typeIsClass) // now we still have to check if the arguments are
2985 // types or values. Since we do not have complete type info
2986 // we need to rely on heuristics :-(
2987 {
2988 if (root->argList.empty())
2989 {
2990 result=FALSE; // empty arg list -> function prototype.
2991 AUTO_TRACE_EXIT("empty arg list: result={}",result);
2992 return result;
2993 }
2994 for (const Argument &a : root->argList)
2995 {
2996 static const reg::Ex initChars(R"([\d"'&*!^]+)");
2997 reg::Match match;
2998 if (!a.name.isEmpty() || !a.defval.isEmpty())
2999 {
3000 std::string name = a.name.str();
3001 if (reg::search(name,match,initChars) && match.position()==0)
3002 {
3003 result=TRUE;
3004 }
3005 else
3006 {
3007 result=FALSE; // arg has (type,name) pair -> function prototype
3008 }
3009 AUTO_TRACE_EXIT("function prototype: result={}",result);
3010 return result;
3011 }
3012 if (!a.type.isEmpty() &&
3013 (a.type.at(a.type.length()-1)=='*' ||
3014 a.type.at(a.type.length()-1)=='&'))
3015 // type ends with * or & => pointer or reference
3016 {
3017 result=FALSE;
3018 AUTO_TRACE_EXIT("pointer or reference: result={}",result);
3019 return result;
3020 }
3021 if (a.type.isEmpty() || resolver.resolveClass(ctx,a.type)!=nullptr)
3022 {
3023 result=FALSE; // arg type is a known type
3024 AUTO_TRACE_EXIT("known type: result={}",result);
3025 return result;
3026 }
3027 if (checkIfTypedef(ctx,fd,a.type))
3028 {
3029 result=FALSE; // argument is a typedef
3030 AUTO_TRACE_EXIT("typedef: result={}",result);
3031 return result;
3032 }
3033 std::string atype = a.type.str();
3034 if (reg::search(atype,match,initChars) && match.position()==0)
3035 {
3036 result=TRUE; // argument type starts with typical initializer char
3037 AUTO_TRACE_EXIT("argument with init char: result={}",result);
3038 return result;
3039 }
3040 std::string resType=resolveTypeDef(ctx,a.type).str();
3041 if (resType.empty()) resType=atype;
3042 static const reg::Ex idChars(R"(\a\w*)");
3043 if (reg::search(resType,match,idChars) && match.position()==0) // resType starts with identifier
3044 {
3045 resType=match.str();
3046 if (resType=="int" || resType=="long" ||
3047 resType=="float" || resType=="double" ||
3048 resType=="char" || resType=="void" ||
3049 resType=="signed" || resType=="unsigned" ||
3050 resType=="const" || resType=="volatile" )
3051 {
3052 result=FALSE; // type keyword -> function prototype
3053 AUTO_TRACE_EXIT("type keyword: result={}",result);
3054 return result;
3055 }
3056 }
3057 }
3058 result=TRUE;
3059 }
3060
3061 AUTO_TRACE_EXIT("end: result={}",result);
3062 return result;
3063}
3064
3065//--------------------------------------------------------------------------------------
3066
3067/*! Searches for the end of a template in prototype \a s starting from
3068 * character position \a startPos. If the end was found the position
3069 * of the closing > is returned, otherwise -1 is returned.
3070 *
3071 * Handles exotic cases such as
3072 * \code
3073 * Class<(id<0)>
3074 * Class<bits<<2>
3075 * Class<"<">
3076 * Class<'<'>
3077 * Class<(")<")>
3078 * \endcode
3079 */
3080static int findEndOfTemplate(const QCString &s,size_t startPos)
3081{
3082 // locate end of template
3083 size_t e=startPos;
3084 int brCount=1;
3085 int roundCount=0;
3086 size_t len = s.length();
3087 bool insideString=FALSE;
3088 bool insideChar=FALSE;
3089 char pc = 0;
3090 while (e<len && brCount!=0)
3091 {
3092 char c=s.at(e);
3093 switch(c)
3094 {
3095 case '<':
3096 if (!insideString && !insideChar)
3097 {
3098 if (e<len-1 && s.at(e+1)=='<')
3099 e++;
3100 else if (roundCount==0)
3101 brCount++;
3102 }
3103 break;
3104 case '>':
3105 if (!insideString && !insideChar)
3106 {
3107 if (e<len-1 && s.at(e+1)=='>')
3108 e++;
3109 else if (roundCount==0)
3110 brCount--;
3111 }
3112 break;
3113 case '(':
3114 if (!insideString && !insideChar)
3115 roundCount++;
3116 break;
3117 case ')':
3118 if (!insideString && !insideChar)
3119 roundCount--;
3120 break;
3121 case '"':
3122 if (!insideChar)
3123 {
3124 if (insideString && pc!='\\')
3125 insideString=FALSE;
3126 else
3127 insideString=TRUE;
3128 }
3129 break;
3130 case '\'':
3131 if (!insideString)
3132 {
3133 if (insideChar && pc!='\\')
3134 insideChar=FALSE;
3135 else
3136 insideChar=TRUE;
3137 }
3138 break;
3139 }
3140 pc = c;
3141 e++;
3142 }
3143 return brCount==0 ? static_cast<int>(e) : -1;
3144}
3145
3146//--------------------------------------------------------------------------------------
3147
3148static void addVariable(const Entry *root,int isFuncPtr=-1)
3149{
3150 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
3151
3152 AUTO_TRACE("VARIABLE_SEC: type='{}' name='{}' args='{}' bodyLine={} endBodyLine={} mGrpId={} relates='{}'",
3153 root->type, root->name, root->args, root->bodyLine, root->endBodyLine, root->mGrpId, root->relates);
3154 //printf("root->parent->name=%s\n",qPrint(root->parent->name));
3155
3156 QCString type = root->type;
3157 QCString name = root->name;
3158 QCString args = root->args;
3159 if (type.isEmpty() && name.find("operator")==-1 &&
3160 (name.find('*')!=-1 || name.find('&')!=-1))
3161 {
3162 // recover from parse error caused by redundant braces
3163 // like in "int *(var[10]);", which is parsed as
3164 // type="" name="int *" args="(var[10])"
3165
3166 type=name;
3167 std::string sargs = args.str();
3168 static const reg::Ex reName(R"(\a\w*)");
3169 reg::Match match;
3170 if (reg::search(sargs,match,reName))
3171 {
3172 name = match.str(); // e.g. 'var' in '(var[10])'
3173 sargs = match.suffix().str(); // e.g. '[10]) in '(var[10])'
3174 size_t j = sargs.find(')');
3175 if (j!=std::string::npos) args=sargs.substr(0,j); // extract, e.g '[10]' from '[10])'
3176 }
3177 }
3178 else
3179 {
3180 int i=isFuncPtr;
3181 if (i==-1 && (root->spec.isAlias())==0) i=findFunctionPtr(type.str(),root->lang); // for typedefs isFuncPtr is not yet set
3182 AUTO_TRACE_ADD("functionPtr={}",i!=-1?"yes":"no");
3183 if (i>=0) // function pointer
3184 {
3185 int ai = type.find('[',i);
3186 if (ai>i) // function pointer array
3187 {
3188 args.prepend(type.right(type.length()-ai));
3189 type=type.left(ai);
3190 }
3191 else if (type.find(')',i)!=-1) // function ptr, not variable like "int (*bla)[10]"
3192 {
3193 type=type.left(type.length()-1);
3194 args.prepend(") ");
3195 }
3196 }
3197 }
3198 AUTO_TRACE_ADD("after correction: type='{}' name='{}' args='{}'",type,name,args);
3199
3200 QCString scope;
3201 name=removeRedundantWhiteSpace(name);
3202
3203 // find the scope of this variable
3204 int index = computeQualifiedIndex(name);
3205 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3206 // grouped members are stored with full scope
3207 {
3208 buildScopeFromQualifiedName(name.left(index+2),root->lang,root->tagInfo());
3209 scope=name.left(index);
3210 name=name.mid(index+2);
3211 }
3212 else
3213 {
3214 Entry *p = root->parent();
3215 while (p->section.isScope())
3216 {
3217 QCString scopeName = p->name;
3218 if (!scopeName.isEmpty())
3219 {
3220 scope.prepend(scopeName);
3221 break;
3222 }
3223 p=p->parent();
3224 }
3225 }
3226
3227 type=type.stripWhiteSpace();
3228 ClassDefMutable *cd=nullptr;
3229 bool isRelated=FALSE;
3230 bool isMemberOf=FALSE;
3231
3232 QCString classScope=stripAnonymousNamespaceScope(scope);
3233 if (root->lang==SrcLangExt::CSharp)
3234 {
3235 classScope=mangleCSharpGenericName(classScope);
3236 }
3237 else
3238 {
3239 classScope=stripTemplateSpecifiersFromScope(classScope,FALSE);
3240 }
3241 QCString annScopePrefix=scope.left(scope.length()-classScope.length());
3242
3243
3244 // Look for last :: not part of template specifier
3245 int p=-1;
3246 for (size_t i=0;i<name.length()-1;i++)
3247 {
3248 if (name[i]==':' && name[i+1]==':')
3249 {
3250 p=static_cast<int>(i);
3251 }
3252 else if (name[i]=='<') // skip over template parts,
3253 // i.e. A::B<C::D> => p=1 and
3254 // A<B::C>::D => p=8
3255 {
3256 int e = findEndOfTemplate(name,i+1);
3257 if (e!=-1) i=static_cast<int>(e);
3258 }
3259 }
3260
3261 if (p!=-1) // found it
3262 {
3263 if (type=="friend class" || type=="friend struct" ||
3264 type=="friend union")
3265 {
3266 cd=getClassMutable(scope);
3267 if (cd)
3268 {
3269 addVariableToClass(root, // entry
3270 cd, // class to add member to
3271 MemberType::Friend, // type of member
3272 type, // type value as string
3273 name, // name of the member
3274 args, // arguments as string
3275 FALSE, // from Anonymous scope
3276 nullptr, // anonymous member
3277 Protection::Public, // protection
3278 Relationship::Member // related to a class
3279 );
3280 }
3281 }
3282 if (root->bodyLine!=-1 && root->endBodyLine!=-1) // store the body location for later use
3283 {
3284 Doxygen::staticInitMap.emplace(name.str(),BodyInfo{root->startLine,root->bodyLine,root->endBodyLine});
3285 }
3286
3287
3288 AUTO_TRACE_ADD("static variable {} body=[{}..{}]",name,root->bodyLine,root->endBodyLine);
3289 return; /* skip this member, because it is a
3290 * static variable definition (always?), which will be
3291 * found in a class scope as well, but then we know the
3292 * correct protection level, so only then it will be
3293 * inserted in the correct list!
3294 */
3295 }
3296
3298 if (type=="@")
3300 else if (type.startsWith("typedef "))
3301 mtype=MemberType::Typedef;
3302 else if (type.startsWith("friend "))
3303 mtype=MemberType::Friend;
3304 else if (root->mtype==MethodTypes::Property)
3306 else if (root->mtype==MethodTypes::Event)
3307 mtype=MemberType::Event;
3308 else if (type.find("sequence<") != -1)
3309 mtype=sliceOpt ? MemberType::Sequence : MemberType::Typedef;
3310 else if (type.find("dictionary<") != -1)
3312
3313 if (!root->relates.isEmpty()) // related variable
3314 {
3315 isRelated=TRUE;
3316 isMemberOf=(root->relatesType==RelatesType::MemberOf);
3317 if (getClass(root->relates)==nullptr && !scope.isEmpty())
3318 scope=mergeScopes(scope,root->relates);
3319 else
3320 scope=root->relates;
3321 }
3322
3323 cd=getClassMutable(scope);
3324 if (cd==nullptr && classScope!=scope) cd=getClassMutable(classScope);
3325 if (cd)
3326 {
3327 MemberDef *md=nullptr;
3328
3329 // if cd is an anonymous (=tag less) scope we insert the member
3330 // into a non-anonymous parent scope as well. This is needed to
3331 // be able to refer to it using \var or \fn
3332
3333 //int indentDepth=0;
3334 int si=scope.find('@');
3335 //int anonyScopes = 0;
3336 //bool added=FALSE;
3337
3338 bool inlineSimpleStructs = Config_getBool(INLINE_SIMPLE_STRUCTS);
3339 Relationship relationship = isMemberOf ? Relationship::Foreign :
3340 isRelated ? Relationship::Related :
3341 Relationship::Member ;
3342 if (si!=-1 && !inlineSimpleStructs) // anonymous scope or type
3343 {
3344 QCString pScope;
3345 ClassDefMutable *pcd=nullptr;
3346 pScope = scope.left(std::max(si-2,0)); // scope without tag less parts
3347 if (!pScope.isEmpty())
3348 pScope.prepend(annScopePrefix);
3349 else if (annScopePrefix.length()>2)
3350 pScope=annScopePrefix.left(annScopePrefix.length()-2);
3351 if (name.at(0)!='@')
3352 {
3353 if (!pScope.isEmpty() && (pcd=getClassMutable(pScope)))
3354 {
3355 AUTO_TRACE_ADD("Adding anonymous member to scope '{}'",pScope);
3356 md=addVariableToClass(root, // entry
3357 pcd, // class to add member to
3358 mtype, // member type
3359 type, // type value as string
3360 name, // member name
3361 args, // arguments as string
3362 TRUE, // from anonymous scope
3363 nullptr, // from anonymous member
3364 root->protection,
3365 relationship
3366 );
3367 //added=TRUE;
3368 }
3369 else // anonymous scope inside namespace or file => put variable in the global scope
3370 {
3371 if (mtype==MemberType::Variable)
3372 {
3373 AUTO_TRACE_ADD("Adding anonymous member to global scope '{}'");
3374 md=addVariableToFile(root,mtype,pScope,type,name,args,TRUE,nullptr);
3375 }
3376 //added=TRUE;
3377 }
3378 }
3379 }
3380
3381 addVariableToClass(root, // entry
3382 cd, // class to add member to
3383 mtype, // member type
3384 type, // type value as string
3385 name, // name of the member
3386 args, // arguments as string
3387 FALSE, // from anonymous scope
3388 md, // from anonymous member
3389 root->protection,
3390 relationship
3391 );
3392 }
3393 else if (!name.isEmpty()) // global variable
3394 {
3395 addVariableToFile(root,mtype,scope,type,name,args,FALSE,/*nullptr,*/nullptr);
3396 }
3397
3398}
3399
3400//----------------------------------------------------------------------
3401// Searches the Entry tree for typedef documentation sections.
3402// If found they are stored in their class or in the global list.
3403static void buildTypedefList(const Entry *root)
3404{
3405 //printf("buildVarList(%s)\n",qPrint(rootNav->name()));
3406 if (!root->name.isEmpty() &&
3407 root->section.isVariable() &&
3408 root->type.find("typedef ")!=-1 // its a typedef
3409 )
3410 {
3411 AUTO_TRACE();
3413 QCString scope;
3414 int index = computeQualifiedIndex(rname);
3415 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3416 // grouped members are stored with full scope
3417 {
3418 buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
3419 scope=rname.left(index);
3420 rname=rname.mid(index+2);
3421 }
3422 else
3423 {
3424 scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
3425 }
3428 MemberName *mn = Doxygen::functionNameLinkedMap->find(rname);
3429 bool found=false;
3430 if (mn) // symbol with the same name already found
3431 {
3432 for (auto &imd : *mn)
3433 {
3434 if (!imd->isTypedef())
3435 continue;
3436
3437 QCString rtype = root->type;
3438 rtype.stripPrefix("typedef ");
3439
3440 // merge the typedefs only if they're not both grouped, and both are
3441 // either part of the same class, part of the same namespace, or both
3442 // are global (i.e., neither in a class or a namespace)
3443 bool notBothGrouped = root->groups.empty() || imd->getGroupDef()==nullptr; // see example #100
3444 bool bothSameScope = (!cd && !nd) || (cd && imd->getClassDef() == cd) || (nd && imd->getNamespaceDef() == nd);
3445 //printf("imd->isTypedef()=%d imd->typeString()=%s root->type=%s\n",imd->isTypedef(),
3446 // qPrint(imd->typeString()),qPrint(root->type));
3447 if (notBothGrouped && bothSameScope && imd->typeString()==rtype)
3448 {
3449 MemberDefMutable *md = toMemberDefMutable(imd.get());
3450 if (md)
3451 {
3452 md->setDocumentation(root->doc,root->docFile,root->docLine);
3454 md->setDocsForDefinition(!root->proto);
3455 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3457 md->setRefItems(root->sli);
3458 md->setRequirementReferences(root->rqli);
3459 md->addQualifiers(root->qualifiers);
3460
3461 // merge ingroup specifiers
3462 if (md->getGroupDef()==nullptr && !root->groups.empty())
3463 {
3464 addMemberToGroups(root,md);
3465 }
3466 else if (md->getGroupDef()!=nullptr && root->groups.empty())
3467 {
3468 //printf("existing member is grouped, new member not\n");
3469 }
3470 else if (md->getGroupDef()!=nullptr && !root->groups.empty())
3471 {
3472 //printf("both members are grouped\n");
3473 }
3474 found=true;
3475 break;
3476 }
3477 }
3478 }
3479 }
3480 if (found)
3481 {
3482 AUTO_TRACE_ADD("typedef '{}' already found",rname);
3483 // mark the entry as processed, as we copied everything from it elsewhere
3484 // also, otherwise, due to containing `typedef` it may later get treated
3485 // as a function typedef in filterMemberDocumentation, which is incorrect
3486 root->markAsProcessed();
3487 }
3488 else
3489 {
3490 AUTO_TRACE_ADD("new typedef '{}'",rname);
3491 addVariable(root);
3492 }
3493
3494 }
3495 for (const auto &e : root->children())
3496 if (!e->section.isEnum())
3497 buildTypedefList(e.get());
3498}
3499
3500//----------------------------------------------------------------------
3501// Searches the Entry tree for sequence documentation sections.
3502// If found they are stored in the global list.
3503static void buildSequenceList(const Entry *root)
3504{
3505 if (!root->name.isEmpty() &&
3506 root->section.isVariable() &&
3507 root->type.find("sequence<")!=-1 // it's a sequence
3508 )
3509 {
3510 AUTO_TRACE();
3511 addVariable(root);
3512 }
3513 for (const auto &e : root->children())
3514 if (!e->section.isEnum())
3515 buildSequenceList(e.get());
3516}
3517
3518//----------------------------------------------------------------------
3519// Searches the Entry tree for dictionary documentation sections.
3520// If found they are stored in the global list.
3521static void buildDictionaryList(const Entry *root)
3522{
3523 if (!root->name.isEmpty() &&
3524 root->section.isVariable() &&
3525 root->type.find("dictionary<")!=-1 // it's a dictionary
3526 )
3527 {
3528 AUTO_TRACE();
3529 addVariable(root);
3530 }
3531 for (const auto &e : root->children())
3532 if (!e->section.isEnum())
3533 buildDictionaryList(e.get());
3534}
3535
3536//----------------------------------------------------------------------
3537// Searches the Entry tree for Variable documentation sections.
3538// If found they are stored in their class or in the global list.
3539
3540static void buildVarList(const Entry *root)
3541{
3542 //printf("buildVarList(%s) section=%08x\n",qPrint(rootNav->name()),rootNav->section());
3543 int isFuncPtr=-1;
3544 if (!root->name.isEmpty() &&
3545 (root->type.isEmpty() || g_compoundKeywords.find(root->type.str())==g_compoundKeywords.end()) &&
3546 (
3547 (root->section.isVariable() && // it's a variable
3548 root->type.find("typedef ")==-1 // and not a typedef
3549 ) ||
3550 (root->section.isFunction() && // or maybe a function pointer variable
3551 (isFuncPtr=findFunctionPtr(root->type.str(),root->lang))!=-1
3552 ) ||
3553 (root->section.isFunction() && // class variable initialized by constructor
3555 )
3556 )
3557 ) // documented variable
3558 {
3559 AUTO_TRACE();
3560 addVariable(root,isFuncPtr);
3561 }
3562 for (const auto &e : root->children())
3563 if (!e->section.isEnum())
3564 buildVarList(e.get());
3565}
3566
3567//----------------------------------------------------------------------
3568// Searches the Entry tree for Interface sections (UNO IDL only).
3569// If found they are stored in their service or in the global list.
3570//
3571
3573 const Entry *root,
3574 ClassDefMutable *cd,
3575 QCString const& rname)
3576{
3577 FileDef *fd = root->fileDef();
3578 enum MemberType type = root->section.isExportedInterface() ? MemberType::Interface : MemberType::Service;
3579 QCString fileName = root->fileName;
3580 if (fileName.isEmpty() && root->tagInfo())
3581 {
3582 fileName = root->tagInfo()->tagName;
3583 }
3584 auto md = createMemberDef(
3585 fileName, root->startLine, root->startColumn, root->type, rname,
3586 "", "", root->protection, root->virt, root->isStatic, Relationship::Member,
3587 type, ArgumentList(), root->argList, root->metaData);
3588 auto mmd = toMemberDefMutable(md.get());
3589 mmd->setTagInfo(root->tagInfo());
3590 mmd->setMemberClass(cd);
3591 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3592 mmd->setDocsForDefinition(false);
3593 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3594 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3595 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3596 mmd->setMemberSpecifiers(root->spec);
3597 mmd->setVhdlSpecifiers(root->vhdlSpec);
3598 mmd->setMemberGroupId(root->mGrpId);
3599 mmd->setTypeConstraints(root->typeConstr);
3600 mmd->setLanguage(root->lang);
3601 mmd->setBodyDef(fd);
3602 mmd->setFileDef(fd);
3603 mmd->addSectionsToDefinition(root->anchors);
3604 QCString const def = root->type + " " + rname;
3605 mmd->setDefinition(def);
3607 mmd->addQualifiers(root->qualifiers);
3608
3609 AUTO_TRACE("Interface member: fileName='{}' type='{}' name='{}' mtype='{}' prot={} virt={} state={} proto={} def='{}'",
3610 fileName,root->type,rname,type,root->protection,root->virt,root->isStatic,root->proto,def);
3611
3612 // add member to the class cd
3613 cd->insertMember(md.get());
3614 // also add the member as a "base" (to get nicer diagrams)
3615 // "optional" interface/service get Protected which turns into dashed line
3616 BaseInfo base(rname,
3617 root->spec.isOptional() ? Protection::Protected : Protection::Public, Specifier::Normal);
3618 TemplateNameMap templateNames;
3619 findClassRelation(root,cd,cd,&base,templateNames,DocumentedOnly,true) ||
3620 findClassRelation(root,cd,cd,&base,templateNames,Undocumented,true);
3621 // add file to list of used files
3622 cd->insertUsedFile(fd);
3623
3624 addMemberToGroups(root,md.get());
3626 root->markAsProcessed();
3627 mmd->setRefItems(root->sli);
3628 mmd->setRequirementReferences(root->rqli);
3629
3630 // add member to the global list of all members
3631 MemberName *mn = Doxygen::memberNameLinkedMap->add(rname);
3632 mn->push_back(std::move(md));
3633}
3634
3635static void buildInterfaceAndServiceList(const Entry *root)
3636{
3637 if (root->section.isExportedInterface() || root->section.isIncludedService())
3638 {
3639 AUTO_TRACE("Exported interface/included service: type='{}' scope='{}' name='{}' args='{}'"
3640 " relates='{}' relatesType='{}' file='{}' line={} bodyLine={} #tArgLists={}"
3641 " mGrpId={} spec={} proto={} docFile='{}'",
3642 root->type, root->parent()->name, root->name, root->args,
3643 root->relates, root->relatesType, root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(),
3644 root->mGrpId, root->spec, root->proto, root->docFile);
3645
3647
3648 if (!rname.isEmpty())
3649 {
3650 QCString scope = root->parent()->name;
3651 ClassDefMutable *cd = getClassMutable(scope);
3652 assert(cd);
3653 if (cd && ((ClassDef::Interface == cd->compoundType()) ||
3654 (ClassDef::Service == cd->compoundType()) ||
3656 {
3658 }
3659 else
3660 {
3661 assert(false); // was checked by scanner.l
3662 }
3663 }
3664 else if (rname.isEmpty())
3665 {
3666 warn(root->fileName,root->startLine,
3667 "Illegal member name found.");
3668 }
3669 }
3670 // can only have these in IDL anyway
3671 switch (root->lang)
3672 {
3673 case SrcLangExt::Unknown: // fall through (root node always is Unknown)
3674 case SrcLangExt::IDL:
3675 for (const auto &e : root->children()) buildInterfaceAndServiceList(e.get());
3676 break;
3677 default:
3678 return; // nothing to do here
3679 }
3680}
3681
3682
3683//----------------------------------------------------------------------
3684// Searches the Entry tree for Function sections.
3685// If found they are stored in their class or in the global list.
3686
3687static void addMethodToClass(const Entry *root,ClassDefMutable *cd,
3688 const QCString &rtype,const QCString &rname,const QCString &rargs,
3689 bool isFriend,
3690 Protection protection,bool stat,Specifier virt,TypeSpecifier spec,
3691 const QCString &relates
3692 )
3693{
3694 FileDef *fd=root->fileDef();
3695
3696 QCString type = rtype;
3697 QCString args = rargs;
3698
3700 name.stripPrefix("::");
3701
3703 if (isFriend) mtype=MemberType::Friend;
3704 else if (root->mtype==MethodTypes::Signal) mtype=MemberType::Signal;
3705 else if (root->mtype==MethodTypes::Slot) mtype=MemberType::Slot;
3706 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType::DCOP;
3707
3708 // strip redundant template specifier for constructors
3709 int i = -1;
3710 int j = -1;
3711 if ((fd==nullptr || fd->getLanguage()==SrcLangExt::Cpp) &&
3712 !name.startsWith("operator ") && // not operator
3713 (i=name.find('<'))!=-1 && // containing <
3714 (j=name.find('>'))!=-1 && // or >
3715 (j!=i+2 || name.at(i+1)!='=') // but not the C++20 spaceship operator <=>
3716 )
3717 {
3718 name=name.left(i);
3719 }
3720
3721 QCString fileName = root->fileName;
3722 if (fileName.isEmpty() && root->tagInfo())
3723 {
3724 fileName = root->tagInfo()->tagName;
3725 }
3726
3727 //printf("root->name='%s; args='%s' root->argList='%s'\n",
3728 // qPrint(root->name),qPrint(args),qPrint(argListToString(root->argList))
3729 // );
3730
3731 // adding class member
3732 Relationship relationship = relates.isEmpty() ? Relationship::Member :
3733 root->relatesType==RelatesType::MemberOf ? Relationship::Foreign :
3734 Relationship::Related ;
3735 auto md = createMemberDef(
3736 fileName,root->startLine,root->startColumn,
3737 type,name,args,root->exception,
3738 protection,virt,
3739 stat && root->relatesType!=RelatesType::MemberOf,
3740 relationship,
3741 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3742 root->argList, root->metaData);
3743 auto mmd = toMemberDefMutable(md.get());
3744 mmd->setTagInfo(root->tagInfo());
3745 mmd->setMemberClass(cd);
3746 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3747 mmd->setDocsForDefinition(!root->proto);
3748 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3749 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3750 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3751 mmd->setMemberSpecifiers(spec);
3752 mmd->setVhdlSpecifiers(root->vhdlSpec);
3753 mmd->setMemberGroupId(root->mGrpId);
3754 mmd->setTypeConstraints(root->typeConstr);
3755 mmd->setLanguage(root->lang);
3756 mmd->setRequiresClause(root->req);
3757 mmd->setId(root->id);
3758 mmd->setBodyDef(fd);
3759 mmd->setFileDef(fd);
3760 mmd->addSectionsToDefinition(root->anchors);
3761 QCString def;
3763 SrcLangExt lang = cd->getLanguage();
3764 QCString scopeSeparator=getLanguageSpecificSeparator(lang);
3765 if (scopeSeparator!="::")
3766 {
3767 qualScope = substitute(qualScope,"::",scopeSeparator);
3768 }
3769 if (lang==SrcLangExt::PHP)
3770 {
3771 // for PHP we use Class::method and Namespace\method
3772 scopeSeparator="::";
3773 }
3774 if (!relates.isEmpty() || isFriend || Config_getBool(HIDE_SCOPE_NAMES))
3775 {
3776 if (!type.isEmpty())
3777 {
3778 def=type+" "+name; //+optArgs;
3779 }
3780 else
3781 {
3782 def=name; //+optArgs;
3783 }
3784 }
3785 else
3786 {
3787 if (!type.isEmpty())
3788 {
3789 def=type+" "+qualScope+scopeSeparator+name; //+optArgs;
3790 }
3791 else
3792 {
3793 def=qualScope+scopeSeparator+name; //+optArgs;
3794 }
3795 }
3796 def.stripPrefix("friend ");
3797 mmd->setDefinition(def);
3799 mmd->addQualifiers(root->qualifiers);
3800
3801 AUTO_TRACE("function member: type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
3802 type, qualScope, rname, args, root->proto, def);
3803
3804 // add member to the class cd
3805 cd->insertMember(md.get());
3806 // add file to list of used files
3807 cd->insertUsedFile(fd);
3808
3809 addMemberToGroups(root,md.get());
3811 root->markAsProcessed();
3812 mmd->setRefItems(root->sli);
3813 mmd->setRequirementReferences(root->rqli);
3814
3815 // add member to the global list of all members
3816 //printf("Adding member=%s class=%s\n",qPrint(md->name()),qPrint(cd->name()));
3818 mn->push_back(std::move(md));
3819}
3820
3821//------------------------------------------------------------------------------------------
3822
3823static void addGlobalFunction(const Entry *root,const QCString &rname,const QCString &sc)
3824{
3825 QCString scope = sc;
3826
3827 // new global function
3829 auto md = createMemberDef(
3830 root->fileName,root->startLine,root->startColumn,
3831 root->type,name,root->args,root->exception,
3832 root->protection,root->virt,root->isStatic,Relationship::Member,
3834 !root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3835 root->argList,root->metaData);
3836 auto mmd = toMemberDefMutable(md.get());
3837 mmd->setTagInfo(root->tagInfo());
3838 mmd->setLanguage(root->lang);
3839 mmd->setId(root->id);
3840 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3841 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3842 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3843 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
3844 mmd->setDocsForDefinition(!root->proto);
3845 mmd->setTypeConstraints(root->typeConstr);
3846 //md->setBody(root->body);
3847 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3848 FileDef *fd=root->fileDef();
3849 mmd->setBodyDef(fd);
3850 mmd->addSectionsToDefinition(root->anchors);
3851 mmd->setMemberSpecifiers(root->spec);
3852 mmd->setVhdlSpecifiers(root->vhdlSpec);
3853 mmd->setMemberGroupId(root->mGrpId);
3854 mmd->setRequiresClause(root->req);
3855 mmd->setExplicitExternal(root->explicitExternal,root->fileName,root->startLine,root->startColumn);
3856
3857 NamespaceDefMutable *nd = nullptr;
3858 // see if the function is inside a namespace that was not part of
3859 // the name already (in that case nd should be non-zero already)
3860 if (root->parent()->section.isNamespace())
3861 {
3862 //QCString nscope=removeAnonymousScopes(root->parent()->name);
3863 QCString nscope=root->parent()->name;
3864 if (!nscope.isEmpty())
3865 {
3866 nd = getResolvedNamespaceMutable(nscope);
3867 }
3868 }
3869 else if (root->parent()->section.isGroupDoc() && !scope.isEmpty())
3870 {
3872 }
3873
3874 if (!scope.isEmpty())
3875 {
3877 if (sep!="::")
3878 {
3879 scope = substitute(scope,"::",sep);
3880 }
3881 scope+=sep;
3882 }
3883
3884 if (Config_getBool(HIDE_SCOPE_NAMES)) scope = "";
3885 QCString def;
3886 //QCString optArgs = root->argList.empty() ? QCString() : root->args;
3887 if (!root->type.isEmpty())
3888 {
3889 def=root->type+" "+scope+name; //+optArgs;
3890 }
3891 else
3892 {
3893 def=scope+name; //+optArgs;
3894 }
3895 AUTO_TRACE("new non-member function type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
3896 root->type,scope,rname,root->args,root->proto,def);
3897 mmd->setDefinition(def);
3899 mmd->addQualifiers(root->qualifiers);
3900
3901 mmd->setRefItems(root->sli);
3902 mmd->setRequirementReferences(root->rqli);
3903 if (nd && !nd->name().isEmpty() && nd->name().at(0)!='@')
3904 {
3905 // add member to namespace
3906 mmd->setNamespace(nd);
3907 nd->insertMember(md.get());
3908 }
3909 if (fd)
3910 {
3911 // add member to the file (we do this even if we have already
3912 // inserted it into the namespace)
3913 mmd->setFileDef(fd);
3914 fd->insertMember(md.get());
3915 }
3916
3917 addMemberToGroups(root,md.get());
3919 if (root->relatesType == RelatesType::Simple) // if this is a relatesalso command,
3920 // allow find Member to pick it up
3921 {
3922 root->markAsProcessed(); // Otherwise we have finished with this entry.
3923 }
3924
3925 // add member to the list of file members
3927 mn->push_back(std::move(md));
3928}
3929
3930//------------------------------------------------------------------------------------------
3931
3932static void buildFunctionList(const Entry *root)
3933{
3934 if (root->section.isFunction())
3935 {
3936 AUTO_TRACE("member function: type='{}' scope='{}' name='{}' args='{}' relates='{}' relatesType='{}'"
3937 " file='{}' line={} bodyLine={} #tArgLists={} mGrpId={}"
3938 " spec={} proto={} docFile='{}'",
3939 root->type, root->parent()->name, root->name, root->args, root->relates, root->relatesType,
3940 root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(), root->mGrpId,
3941 root->spec, root->proto, root->docFile);
3942
3943 bool isFriend=root->type=="friend" || root->type.find("friend ")!=-1;
3945 //printf("rname=%s\n",qPrint(rname));
3946
3947 QCString scope;
3948 int index = computeQualifiedIndex(rname);
3949 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3950 // grouped members are stored with full scope
3951 {
3952 buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
3953 scope=rname.left(index);
3954 rname=rname.mid(index+2);
3955 }
3956 else
3957 {
3958 scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
3959 }
3960 if (!rname.isEmpty() && scope.find('@')==-1)
3961 {
3962 // check if this function's parent is a class
3963 if (root->lang==SrcLangExt::CSharp)
3964 {
3965 scope=mangleCSharpGenericName(scope);
3966 }
3967 else
3968 {
3970 }
3971
3972 FileDef *rfd=root->fileDef();
3973
3974 int memIndex=rname.findRev("::");
3975
3977 if (cd && scope+"::"==rname.left(scope.length()+2)) // found A::f inside A
3978 {
3979 // strip scope from name
3980 rname=rname.right(rname.length()-root->parent()->name.length()-2);
3981 }
3982
3983 bool isMember=FALSE;
3984 if (memIndex!=-1)
3985 {
3986 int ts=rname.find('<');
3987 int te=rname.find('>');
3988 if (memIndex>0 && (ts==-1 || te==-1))
3989 {
3990 // note: the following code was replaced by inMember=TRUE to deal with a
3991 // function rname='X::foo' of class X inside a namespace also called X...
3992 // bug id 548175
3993 //nd = Doxygen::namespaceLinkedMap->find(rname.left(memIndex));
3994 //isMember = nd==nullptr;
3995 //if (nd)
3996 //{
3997 // // strip namespace scope from name
3998 // scope=rname.left(memIndex);
3999 // rname=rname.right(rname.length()-memIndex-2);
4000 //}
4001 isMember = TRUE;
4002 }
4003 else
4004 {
4005 isMember=memIndex<ts || memIndex>te;
4006 }
4007 }
4008
4009 if (!root->parent()->name.isEmpty() && root->parent()->section.isCompound() && cd)
4010 {
4011 AUTO_TRACE_ADD("member '{}' of class '{}'", rname,cd->name());
4012 addMethodToClass(root,cd,root->type,rname,root->args,isFriend,
4013 root->protection,root->isStatic,root->virt,root->spec,root->relates);
4014 }
4015 else if (root->parent()->section.isObjcImpl() && cd)
4016 {
4017 const MemberDef *md = cd->getMemberByName(rname);
4018 if (md)
4019 {
4020 MemberDefMutable *mdm = toMemberDefMutable(const_cast<MemberDef*>(md));
4021 if (mdm)
4022 {
4023 mdm->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
4024 mdm->setBodyDef(root->fileDef());
4025 }
4026 }
4027 }
4028 else if (!root->parent()->section.isCompound() && !root->parent()->section.isObjcImpl() &&
4029 !isMember &&
4030 (root->relates.isEmpty() || root->relatesType==RelatesType::Duplicate) &&
4031 !root->type.startsWith("extern ") && !root->type.startsWith("typedef ")
4032 )
4033 // no member => unrelated function
4034 {
4035 /* check the uniqueness of the function name in the file.
4036 * A file could contain a function prototype and a function definition
4037 * or even multiple function prototypes.
4038 */
4039 bool found=FALSE;
4040 MemberDef *md_found=nullptr;
4041 MemberName *mn = Doxygen::functionNameLinkedMap->find(rname);
4042 if (mn)
4043 {
4044 AUTO_TRACE_ADD("function '{}' already found",rname);
4045 for (const auto &imd : *mn)
4046 {
4047 MemberDefMutable *md = toMemberDefMutable(imd.get());
4048 if (md)
4049 {
4050 const NamespaceDef *mnd = md->getNamespaceDef();
4051 NamespaceDef *rnd = nullptr;
4052 //printf("root namespace=%s\n",qPrint(rootNav->parent()->name()));
4053 QCString fullScope = scope;
4054 QCString parentScope = root->parent()->name;
4055 if (!parentScope.isEmpty() && !leftScopeMatch(parentScope,scope))
4056 {
4057 if (!scope.isEmpty()) fullScope.prepend("::");
4058 fullScope.prepend(parentScope);
4059 }
4060 //printf("fullScope=%s\n",qPrint(fullScope));
4061 rnd = getResolvedNamespace(fullScope);
4062 const FileDef *mfd = md->getFileDef();
4063 QCString nsName,rnsName;
4064 if (mnd) nsName = mnd->name();
4065 if (rnd) rnsName = rnd->name();
4066 //printf("matching arguments for %s%s %s%s\n",
4067 // qPrint(md->name()),md->argsString(),qPrint(rname),qPrint(argListToString(root->argList)));
4068 const ArgumentList &mdAl = md->argumentList();
4069 const ArgumentList &mdTempl = md->templateArguments();
4070
4071 // in case of template functions, we need to check if the
4072 // functions have the same number of template parameters
4073 bool sameTemplateArgs = TRUE;
4074 bool matchingReturnTypes = TRUE;
4075 bool sameRequiresClause = TRUE;
4076 if (!mdTempl.empty() && !root->tArgLists.empty())
4077 {
4078 sameTemplateArgs = matchTemplateArguments(mdTempl,root->tArgLists.back());
4079 if (md->typeString()!=removeRedundantWhiteSpace(root->type))
4080 {
4081 matchingReturnTypes = FALSE;
4082 }
4083 if (md->requiresClause()!=root->req)
4084 {
4085 sameRequiresClause = FALSE;
4086 }
4087 }
4088 else if (!mdTempl.empty() || !root->tArgLists.empty())
4089 { // if one has template parameters and the other doesn't then that also counts as a
4090 // difference
4091 sameTemplateArgs = FALSE;
4092 }
4093
4094 bool staticsInDifferentFiles =
4095 root->isStatic && md->isStatic() && root->fileName!=md->getDefFileName();
4096
4097 if (sameTemplateArgs &&
4098 matchingReturnTypes &&
4099 sameRequiresClause &&
4100 !staticsInDifferentFiles &&
4101 matchArguments2(md->getOuterScope(),mfd,md->typeString(),&mdAl,
4102 rnd ? rnd : Doxygen::globalScope,rfd,root->type,&root->argList,
4103 FALSE,root->lang)
4104 )
4105 {
4106 GroupDef *gd=nullptr;
4107 if (!root->groups.empty() && !root->groups.front().groupname.isEmpty())
4108 {
4109 gd = Doxygen::groupLinkedMap->find(root->groups.front().groupname);
4110 }
4111 //printf("match!\n");
4112 //printf("mnd=%p rnd=%p nsName=%s rnsName=%s\n",mnd,rnd,qPrint(nsName),qPrint(rnsName));
4113 // see if we need to create a new member
4114 found=(mnd && rnd && nsName==rnsName) || // members are in the same namespace
4115 ((mnd==nullptr && rnd==nullptr && mfd!=nullptr && // no external reference and
4116 mfd->absFilePath()==root->fileName // prototype in the same file
4117 )
4118 );
4119 // otherwise, allow a duplicate global member with the same argument list
4120 if (!found && gd && gd==md->getGroupDef() && nsName==rnsName)
4121 {
4122 // member is already in the group, so we don't want to add it again.
4123 found=TRUE;
4124 }
4125
4126 AUTO_TRACE_ADD("combining function with prototype found={} in namespace '{}'",found,nsName);
4127
4128 if (found)
4129 {
4130 // merge argument lists
4131 ArgumentList mergedArgList = root->argList;
4132 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.isEmpty());
4133 // merge documentation
4134 if (md->documentation().isEmpty() && !root->doc.isEmpty())
4135 {
4136 if (root->proto)
4137 {
4139 }
4140 else
4141 {
4143 }
4144 }
4145
4146 md->setDocumentation(root->doc,root->docFile,root->docLine);
4148 md->setDocsForDefinition(!root->proto);
4149 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
4150 {
4151 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
4152 md->setBodyDef(rfd);
4153 }
4154
4155 if (md->briefDescription().isEmpty() && !root->brief.isEmpty())
4156 {
4157 md->setArgsString(root->args);
4158 }
4159 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
4160
4162
4164 md->addQualifiers(root->qualifiers);
4165
4166 // merge ingroup specifiers
4167 if (md->getGroupDef()==nullptr && !root->groups.empty())
4168 {
4169 addMemberToGroups(root,md);
4170 }
4171 else if (md->getGroupDef()!=nullptr && root->groups.empty())
4172 {
4173 //printf("existing member is grouped, new member not\n");
4174 }
4175 else if (md->getGroupDef()!=nullptr && !root->groups.empty())
4176 {
4177 //printf("both members are grouped\n");
4178 }
4180
4181 // if md is a declaration and root is the corresponding
4182 // definition, then turn md into a definition.
4183 if (md->isPrototype() && !root->proto)
4184 {
4185 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
4186 md->setPrototype(FALSE,root->fileName,root->startLine,root->startColumn);
4187 }
4188 // if md is already the definition, then add the declaration info
4189 else if (!md->isPrototype() && root->proto)
4190 {
4191 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
4192 }
4193 }
4194 }
4195 }
4196 if (found)
4197 {
4198 md_found = md;
4199 break;
4200 }
4201 }
4202 }
4203 if (!found) /* global function is unique with respect to the file */
4204 {
4205 addGlobalFunction(root,rname,scope);
4206 }
4207 else
4208 {
4209 FileDef *fd=root->fileDef();
4210 if (fd)
4211 {
4212 // add member to the file (we do this even if we have already
4213 // inserted it into the namespace)
4214 fd->insertMember(md_found);
4215 }
4216 }
4217
4218 AUTO_TRACE_ADD("unrelated function type='{}' name='{}' args='{}'",root->type,rname,root->args);
4219 }
4220 else
4221 {
4222 AUTO_TRACE_ADD("function '{}' is not processed",rname);
4223 }
4224 }
4225 else if (rname.isEmpty())
4226 {
4227 warn(root->fileName,root->startLine,
4228 "Illegal member name found."
4229 );
4230 }
4231 }
4232 for (const auto &e : root->children()) buildFunctionList(e.get());
4233}
4234
4235//----------------------------------------------------------------------
4236
4237static void findFriends()
4238{
4239 AUTO_TRACE();
4240 for (const auto &fn : *Doxygen::functionNameLinkedMap) // for each global function name
4241 {
4242 MemberName *mn = Doxygen::memberNameLinkedMap->find(fn->memberName());
4243 if (mn)
4244 { // there are members with the same name
4245 // for each function with that name
4246 for (const auto &ifmd : *fn)
4247 {
4248 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
4249 // for each member with that name
4250 for (const auto &immd : *mn)
4251 {
4252 MemberDefMutable *mmd = toMemberDefMutable(immd.get());
4253 //printf("Checking for matching arguments
4254 // mmd->isRelated()=%d mmd->isFriend()=%d mmd->isFunction()=%d\n",
4255 // mmd->isRelated(),mmd->isFriend(),mmd->isFunction());
4256 if (fmd && mmd &&
4257 (mmd->isFriend() || (mmd->isRelated() && mmd->isFunction())) &&
4258 matchArguments2(mmd->getOuterScope(), mmd->getFileDef(), mmd->typeString(), &mmd->argumentList(),
4259 fmd->getOuterScope(), fmd->getFileDef(), fmd->typeString(), &fmd->argumentList(),
4260 TRUE,mmd->getLanguage()
4261 )
4262
4263 ) // if the member is related and the arguments match then the
4264 // function is actually a friend.
4265 {
4266 AUTO_TRACE_ADD("Merging related global and member '{}' isFriend={} isRelated={} isFunction={}",
4267 mmd->name(),mmd->isFriend(),mmd->isRelated(),mmd->isFunction());
4268 const ArgumentList &mmdAl = mmd->argumentList();
4269 const ArgumentList &fmdAl = fmd->argumentList();
4270 mergeArguments(const_cast<ArgumentList&>(fmdAl),const_cast<ArgumentList&>(mmdAl));
4271
4272 // reset argument lists to add missing default parameters
4273 QCString mmdAlStr = argListToString(mmdAl);
4274 QCString fmdAlStr = argListToString(fmdAl);
4275 mmd->setArgsString(mmdAlStr);
4276 fmd->setArgsString(fmdAlStr);
4277 mmd->moveDeclArgumentList(std::make_unique<ArgumentList>(mmdAl));
4278 fmd->moveDeclArgumentList(std::make_unique<ArgumentList>(fmdAl));
4279 AUTO_TRACE_ADD("friend args='{}' member args='{}'",argListToString(fmd->argumentList()),argListToString(mmd->argumentList()));
4280
4281 if (!fmd->documentation().isEmpty())
4282 {
4283 mmd->setDocumentation(fmd->documentation(),fmd->docFile(),fmd->docLine());
4284 }
4285 else if (!mmd->documentation().isEmpty())
4286 {
4287 fmd->setDocumentation(mmd->documentation(),mmd->docFile(),mmd->docLine());
4288 }
4289 if (mmd->briefDescription().isEmpty() && !fmd->briefDescription().isEmpty())
4290 {
4291 mmd->setBriefDescription(fmd->briefDescription(),fmd->briefFile(),fmd->briefLine());
4292 }
4293 else if (!mmd->briefDescription().isEmpty() && !fmd->briefDescription().isEmpty())
4294 {
4295 fmd->setBriefDescription(mmd->briefDescription(),mmd->briefFile(),mmd->briefLine());
4296 }
4297 if (!fmd->inbodyDocumentation().isEmpty())
4298 {
4300 }
4301 else if (!mmd->inbodyDocumentation().isEmpty())
4302 {
4304 }
4305 //printf("body mmd %d fmd %d\n",mmd->getStartBodyLine(),fmd->getStartBodyLine());
4306 if (mmd->getStartBodyLine()==-1 && fmd->getStartBodyLine()!=-1)
4307 {
4308 mmd->setBodySegment(fmd->getDefLine(),fmd->getStartBodyLine(),fmd->getEndBodyLine());
4309 mmd->setBodyDef(fmd->getBodyDef());
4310 //mmd->setBodyMember(fmd);
4311 }
4312 else if (mmd->getStartBodyLine()!=-1 && fmd->getStartBodyLine()==-1)
4313 {
4314 fmd->setBodySegment(mmd->getDefLine(),mmd->getStartBodyLine(),mmd->getEndBodyLine());
4315 fmd->setBodyDef(mmd->getBodyDef());
4316 //fmd->setBodyMember(mmd);
4317 }
4319
4321
4322 mmd->addQualifiers(fmd->getQualifiers());
4323 fmd->addQualifiers(mmd->getQualifiers());
4324
4325 }
4326 }
4327 }
4328 }
4329 }
4330}
4331
4332//----------------------------------------------------------------------
4333
4335{
4336 AUTO_TRACE();
4337
4338 // find matching function declaration and definitions.
4339 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4340 {
4341 //printf("memberName=%s count=%zu\n",qPrint(mn->memberName()),mn->size());
4342 /* find a matching function declaration and definition for this function */
4343 for (const auto &imdec : *mn)
4344 {
4345 MemberDefMutable *mdec = toMemberDefMutable(imdec.get());
4346 if (mdec &&
4347 (mdec->isPrototype() ||
4348 (mdec->isVariable() && mdec->isExternal())
4349 ))
4350 {
4351 for (const auto &imdef : *mn)
4352 {
4353 MemberDefMutable *mdef = toMemberDefMutable(imdef.get());
4354 if (mdef && mdec!=mdef &&
4355 mdec->getNamespaceDef()==mdef->getNamespaceDef())
4356 {
4358 }
4359 }
4360 }
4361 }
4362 }
4363}
4364
4365//----------------------------------------------------------------------
4366
4368{
4369 AUTO_TRACE();
4370 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4371 {
4372 MemberDefMutable *mdef=nullptr,*mdec=nullptr;
4373 /* find a matching function declaration and definition for this function */
4374 for (const auto &imd : *mn)
4375 {
4376 MemberDefMutable *md = toMemberDefMutable(imd.get());
4377 if (md)
4378 {
4379 if (md->isPrototype())
4380 mdec=md;
4381 else if (md->isVariable() && md->isExternal())
4382 mdec=md;
4383
4384 if (md->isFunction() && !md->isStatic() && !md->isPrototype())
4385 mdef=md;
4386 else if (md->isVariable() && !md->isExternal() && !md->isStatic())
4387 mdef=md;
4388 }
4389
4390 if (mdef && mdec) break;
4391 }
4392 if (mdef && mdec)
4393 {
4394 const ArgumentList &mdefAl = mdef->argumentList();
4395 const ArgumentList &mdecAl = mdec->argumentList();
4396 if (
4397 matchArguments2(mdef->getOuterScope(),mdef->getFileDef(),mdef->typeString(),const_cast<ArgumentList*>(&mdefAl),
4398 mdec->getOuterScope(),mdec->getFileDef(),mdec->typeString(),const_cast<ArgumentList*>(&mdecAl),
4399 TRUE,mdef->getLanguage()
4400 )
4401 ) /* match found */
4402 {
4403 AUTO_TRACE_ADD("merging references for mdec={} mdef={}",mdec->name(),mdef->name());
4404 mdef->mergeReferences(mdec);
4405 mdec->mergeReferences(mdef);
4406 mdef->mergeReferencedBy(mdec);
4407 mdec->mergeReferencedBy(mdef);
4408 }
4409 }
4410 }
4411}
4412
4413//----------------------------------------------------------------------
4414
4416{
4417 AUTO_TRACE();
4418 // find match between function declaration and definition for
4419 // related functions
4420 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4421 {
4422 /* find a matching function declaration and definition for this function */
4423 // for each global function
4424 for (const auto &imd : *mn)
4425 {
4426 MemberDefMutable *md = toMemberDefMutable(imd.get());
4427 if (md)
4428 {
4429 //printf(" Function '%s'\n",qPrint(md->name()));
4430 MemberName *rmn = Doxygen::memberNameLinkedMap->find(md->name());
4431 if (rmn) // check if there is a member with the same name
4432 {
4433 //printf(" Member name found\n");
4434 // for each member with the same name
4435 for (const auto &irmd : *rmn)
4436 {
4437 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
4438 //printf(" Member found: related='%d'\n",rmd->isRelated());
4439 if (rmd &&
4440 (rmd->isRelated() || rmd->isForeign()) && // related function
4441 matchArguments2( md->getOuterScope(), md->getFileDef(), md->typeString(), &md->argumentList(),
4442 rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmd->argumentList(),
4443 TRUE,md->getLanguage()
4444 )
4445 )
4446 {
4447 AUTO_TRACE_ADD("Found related member '{}'",md->name());
4448 if (rmd->relatedAlso())
4449 md->setRelatedAlso(rmd->relatedAlso());
4450 else if (rmd->isForeign())
4451 md->makeForeign();
4452 else
4453 md->makeRelated();
4454 }
4455 }
4456 }
4457 }
4458 }
4459 }
4460}
4461
4462//----------------------------------------------------------------------
4463
4465{
4466 AUTO_TRACE();
4467 for (const auto &[qualifiedName,bodyInfo] : Doxygen::staticInitMap)
4468 {
4469 size_t i=qualifiedName.rfind("::");
4470 if (i!=std::string::npos)
4471 {
4472 QCString scope = qualifiedName.substr(0,i);
4473 QCString name = qualifiedName.substr(i+2);
4474 MemberName *mn = Doxygen::memberNameLinkedMap->find(name);
4475 if (mn)
4476 {
4477 for (const auto &imd : *mn)
4478 {
4479 MemberDefMutable *md = toMemberDefMutable(imd.get());
4480 if (md && md->qualifiedName().str()==qualifiedName && md->isVariable())
4481 {
4482 AUTO_TRACE_ADD("found static member {} body [{}..{}]\n",
4483 md->qualifiedName(),bodyInfo.startLine,bodyInfo.endLine);
4484 md->setBodySegment(bodyInfo.defLine,
4485 bodyInfo.startLine,
4486 bodyInfo.endLine);
4487 }
4488 }
4489 }
4490 }
4491 }
4492}
4493
4494//----------------------------------------------------------------------
4495
4496/*! make a dictionary of all template arguments of class cd
4497 * that are part of the base class name.
4498 * Example: A template class A with template arguments <R,S,T>
4499 * that inherits from B<T,T,S> will have T and S in the dictionary.
4500 */
4501static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments,const std::string &name)
4502{
4503 std::map<std::string,int> templateNames;
4504 int count=0;
4505 for (const Argument &arg : templateArguments)
4506 {
4507 static const reg::Ex re(R"(\a[\w:]*)");
4508 reg::Iterator it(name,re);
4510 for (; it!=end ; ++it)
4511 {
4512 const auto &match = *it;
4513 std::string n = match.str();
4514 if (n==arg.name.str())
4515 {
4516 if (templateNames.find(n)==templateNames.end())
4517 {
4518 templateNames.emplace(n,count);
4519 }
4520 }
4521 }
4522 }
4523 return templateNames;
4524}
4525
4526/*! Searches a class from within \a context and \a cd and returns its
4527 * definition if found (otherwise nullptr is returned).
4528 */
4530{
4531 ClassDef *result=nullptr;
4532 if (cd==nullptr)
4533 {
4534 return result;
4535 }
4536 FileDef *fd=cd->getFileDef();
4537 SymbolResolver resolver(fd);
4538 if (context && cd!=context)
4539 {
4540 result = const_cast<ClassDef*>(resolver.resolveClass(context,name,true,true));
4541 }
4542 //printf("1. result=%p\n",result);
4543 if (result==nullptr)
4544 {
4545 result = const_cast<ClassDef*>(resolver.resolveClass(cd,name,true,true));
4546 }
4547 //printf("2. result=%p\n",result);
4548 if (result==nullptr) // try direct class, needed for namespaced classes imported via tag files (see bug624095)
4549 {
4550 result = getClass(name);
4551 }
4552 //printf("3. result=%p\n",result);
4553 //printf("** Trying to find %s within context %s class %s result=%s lookup=%p\n",
4554 // qPrint(name),
4555 // context ? qPrint(context->name()) : "<none>",
4556 // cd ? qPrint(cd->name()) : "<none>",
4557 // result ? qPrint(result->name()) : "<none>",
4558 // Doxygen::classLinkedMap->find(name)
4559 // );
4560 return result;
4561}
4562
4563
4564static void findUsedClassesForClass(const Entry *root,
4565 Definition *context,
4566 ClassDefMutable *masterCd,
4567 ClassDefMutable *instanceCd,
4568 bool isArtificial,
4569 const ArgumentList *actualArgs = nullptr,
4570 const TemplateNameMap &templateNames = TemplateNameMap()
4571 )
4572{
4573 AUTO_TRACE();
4574 const ArgumentList &formalArgs = masterCd->templateArguments();
4575 for (auto &mni : masterCd->memberNameInfoLinkedMap())
4576 {
4577 for (auto &mi : *mni)
4578 {
4579 const MemberDef *md=mi->memberDef();
4580 if (md->isVariable() || md->isObjCProperty()) // for each member variable in this class
4581 {
4582 AUTO_TRACE_ADD("Found variable '{}' in class '{}'",md->name(),masterCd->name());
4583 QCString type = normalizeNonTemplateArgumentsInString(md->typeString(),masterCd,formalArgs);
4584 QCString typedefValue = md->getLanguage()==SrcLangExt::Java ? type : resolveTypeDef(masterCd,type);
4585 if (!typedefValue.isEmpty())
4586 {
4587 type = typedefValue;
4588 }
4589 int pos=0;
4590 QCString usedClassName;
4591 QCString templSpec;
4592 bool found=FALSE;
4593 // the type can contain template variables, replace them if present
4594 type = substituteTemplateArgumentsInString(type,formalArgs,actualArgs);
4595
4596 //printf(" template substitution gives=%s\n",qPrint(type));
4597 while (!found && extractClassNameFromType(type,pos,usedClassName,templSpec,root->lang)!=-1)
4598 {
4599 // find the type (if any) that matches usedClassName
4600 SymbolResolver resolver(masterCd->getFileDef());
4601 const ClassDefMutable *typeCd = resolver.resolveClassMutable(masterCd,usedClassName,false,true);
4602 //printf("====> usedClassName=%s -> typeCd=%s\n",
4603 // qPrint(usedClassName),typeCd?qPrint(typeCd->name()):"<none>");
4604 if (typeCd)
4605 {
4606 usedClassName = typeCd->name();
4607 }
4608
4609 // replace any namespace aliases
4610 replaceNamespaceAliases(usedClassName);
4611 // add any template arguments to the class
4612 QCString usedName = removeRedundantWhiteSpace(usedClassName+templSpec);
4613 //printf(" usedName=%s usedClassName=%s templSpec=%s\n",qPrint(usedName),qPrint(usedClassName),qPrint(templSpec));
4614
4615 TemplateNameMap formTemplateNames;
4616 if (templateNames.empty())
4617 {
4618 formTemplateNames = getTemplateArgumentsInName(formalArgs,usedName.str());
4619 }
4620 BaseInfo bi(usedName,Protection::Public,Specifier::Normal);
4621 findClassRelation(root,context,instanceCd,&bi,formTemplateNames,TemplateInstances,isArtificial);
4622
4623 for (const Argument &arg : masterCd->templateArguments())
4624 {
4625 if (arg.name==usedName) // type is a template argument
4626 {
4627 ClassDef *usedCd = Doxygen::hiddenClassLinkedMap->find(usedName);
4628 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4629 if (usedCd==nullptr)
4630 {
4631 usedCdm = toClassDefMutable(
4632 Doxygen::hiddenClassLinkedMap->add(usedName,
4634 masterCd->getDefFileName(),masterCd->getDefLine(),
4635 masterCd->getDefColumn(),
4636 usedName,
4637 ClassDef::Class)));
4638 if (usedCdm)
4639 {
4640 //printf("making %s a template argument!!!\n",qPrint(usedCd->name()));
4641 usedCdm->makeTemplateArgument();
4642 usedCdm->setUsedOnly(TRUE);
4643 usedCdm->setLanguage(masterCd->getLanguage());
4644 usedCd = usedCdm;
4645 }
4646 }
4647 if (usedCd)
4648 {
4649 found=TRUE;
4650 AUTO_TRACE_ADD("case 1: adding used class '{}'", usedCd->name());
4651 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4652 if (usedCdm)
4653 {
4654 if (isArtificial) usedCdm->setArtificial(TRUE);
4655 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4656 }
4657 }
4658 }
4659 }
4660
4661 if (!found)
4662 {
4663 ClassDef *usedCd=findClassWithinClassContext(context,masterCd,usedName);
4664 //printf("Looking for used class %s: result=%s master=%s\n",
4665 // qPrint(usedName),usedCd?qPrint(usedCd->name()):"<none>",masterCd?qPrint(masterCd->name()):"<none>");
4666
4667 if (usedCd)
4668 {
4669 found=TRUE;
4670 AUTO_TRACE_ADD("case 2: adding used class '{}'", usedCd->name());
4671 instanceCd->addUsedClass(usedCd,md->name(),md->protection()); // class exists
4672 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4673 if (usedCdm)
4674 {
4675 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4676 }
4677 }
4678 }
4679 }
4680 if (!found && !type.isEmpty()) // used class is not documented in any scope
4681 {
4682 ClassDef *usedCd = Doxygen::hiddenClassLinkedMap->find(type);
4683 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4684 if (usedCd==nullptr && !Config_getBool(HIDE_UNDOC_RELATIONS))
4685 {
4686 if (type.endsWith("(*") || type.endsWith("(^")) // type is a function pointer
4687 {
4688 type+=md->argsString();
4689 }
4690 AUTO_TRACE_ADD("New undocumented used class '{}'", type);
4691 usedCdm = toClassDefMutable(
4694 masterCd->getDefFileName(),masterCd->getDefLine(),
4695 masterCd->getDefColumn(),
4696 type,ClassDef::Class)));
4697 if (usedCdm)
4698 {
4699 usedCdm->setUsedOnly(TRUE);
4700 usedCdm->setLanguage(masterCd->getLanguage());
4701 usedCd = usedCdm;
4702 }
4703 }
4704 if (usedCd)
4705 {
4706 AUTO_TRACE_ADD("case 3: adding used class '{}'", usedCd->name());
4707 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4708 if (usedCdm)
4709 {
4710 if (isArtificial) usedCdm->setArtificial(TRUE);
4711 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4712 }
4713 }
4714 }
4715 }
4716 }
4717 }
4718}
4719
4721 const Entry *root,
4722 Definition *context,
4723 ClassDefMutable *masterCd,
4724 ClassDefMutable *instanceCd,
4726 bool isArtificial,
4727 const ArgumentList *actualArgs = nullptr,
4728 const TemplateNameMap &templateNames=TemplateNameMap()
4729 )
4730{
4731 AUTO_TRACE("name={}",root->name);
4732 // The base class could ofcouse also be a non-nested class
4733 const ArgumentList &formalArgs = masterCd->templateArguments();
4734 for (const BaseInfo &bi : root->extends)
4735 {
4736 //printf("masterCd=%s bi.name='%s' #actualArgs=%d\n",
4737 // qPrint(masterCd->localName()),qPrint(bi.name),actualArgs ? (int)actualArgs->size() : -1);
4738 TemplateNameMap formTemplateNames;
4739 if (templateNames.empty())
4740 {
4741 formTemplateNames = getTemplateArgumentsInName(formalArgs,bi.name.str());
4742 }
4743 BaseInfo tbi = bi;
4744 tbi.name = substituteTemplateArgumentsInString(bi.name,formalArgs,actualArgs);
4745 //printf("masterCd=%p instanceCd=%p bi->name=%s tbi.name=%s\n",(void*)masterCd,(void*)instanceCd,qPrint(bi.name),qPrint(tbi.name));
4746
4747 if (mode==DocumentedOnly)
4748 {
4749 // find a documented base class in the correct scope
4750 if (!findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,DocumentedOnly,isArtificial))
4751 {
4752 // 1.8.2: decided to show inheritance relations even if not documented,
4753 // we do make them artificial, so they do not appear in the index
4754 //if (!Config_getBool(HIDE_UNDOC_RELATIONS))
4755 bool b = Config_getBool(HIDE_UNDOC_RELATIONS) ? TRUE : isArtificial;
4756 //{
4757 // no documented base class -> try to find an undocumented one
4758 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,Undocumented,b);
4759 //}
4760 }
4761 }
4762 else if (mode==TemplateInstances)
4763 {
4764 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,TemplateInstances,isArtificial);
4765 }
4766 }
4767}
4768
4769//----------------------------------------------------------------------
4770
4771static void findTemplateInstanceRelation(const Entry *root,
4772 Definition *context,
4773 ClassDefMutable *templateClass,const QCString &templSpec,
4774 const TemplateNameMap &templateNames,
4775 bool isArtificial)
4776{
4777 AUTO_TRACE("Derived from template '{}' with parameters '{}' isArtificial={}",
4778 templateClass->name(),templSpec,isArtificial);
4779
4780 QCString tempArgsStr = tempArgListToString(templateClass->templateArguments(),root->lang,false);
4781 bool existingClass = templSpec==tempArgsStr;
4782 if (existingClass) return; // avoid recursion
4783
4784 bool freshInstance=FALSE;
4785 ClassDefMutable *instanceClass = toClassDefMutable(
4786 templateClass->insertTemplateInstance(
4787 root->fileName,root->startLine,root->startColumn,templSpec,freshInstance));
4788 if (instanceClass)
4789 {
4790 if (freshInstance)
4791 {
4792 instanceClass->setArtificial(TRUE);
4793 instanceClass->setLanguage(root->lang);
4794
4795 AUTO_TRACE_ADD("found fresh instance '{}'",instanceClass->name());
4796 instanceClass->setTemplateBaseClassNames(templateNames);
4797
4798 // search for new template instances caused by base classes of
4799 // instanceClass
4800 auto it_pair = g_classEntries.equal_range(templateClass->name().str());
4801 for (auto it=it_pair.first ; it!=it_pair.second ; ++it)
4802 {
4803 const Entry *templateRoot = it->second;
4804 AUTO_TRACE_ADD("template root found '{}' templSpec='{}'",templateRoot->name,templSpec);
4805 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(root->lang,templSpec);
4806 findBaseClassesForClass(templateRoot,context,templateClass,instanceClass,
4807 TemplateInstances,isArtificial,templArgs.get(),templateNames);
4808
4809 findUsedClassesForClass(templateRoot,context,templateClass,instanceClass,
4810 isArtificial,templArgs.get(),templateNames);
4811 }
4812 }
4813 else
4814 {
4815 AUTO_TRACE_ADD("instance already exists");
4816 }
4817 }
4818}
4819
4820//----------------------------------------------------------------------
4821
4822static void resolveTemplateInstanceInType(const Entry *root,const Definition *scope,const MemberDef *md)
4823{
4824 // For a statement like 'using X = T<A>', add a template instance 'T<A>' as a symbol, so it can
4825 // be used to match arguments (see issue #11111)
4826 AUTO_TRACE();
4827 QCString ttype = md->typeString();
4828 ttype.stripPrefix("typedef ");
4829 int ti=ttype.find('<');
4830 if (ti!=-1)
4831 {
4832 QCString templateClassName = ttype.left(ti);
4833 SymbolResolver resolver(root->fileDef());
4834 ClassDefMutable *baseClass = resolver.resolveClassMutable(scope ? scope : Doxygen::globalScope,
4835 templateClassName, true, true);
4836 AUTO_TRACE_ADD("templateClassName={} baseClass={}",templateClassName,baseClass?baseClass->name():"<none>");
4837 if (baseClass)
4838 {
4839 const ArgumentList &tl = baseClass->templateArguments();
4840 TemplateNameMap templateNames = getTemplateArgumentsInName(tl,templateClassName.str());
4842 baseClass,
4843 ttype.mid(ti),
4844 templateNames,
4845 baseClass->isArtificial());
4846 }
4847 }
4848}
4849
4850//----------------------------------------------------------------------
4851
4852static bool isRecursiveBaseClass(const QCString &scope,const QCString &name)
4853{
4854 QCString n=name;
4855 int index=n.find('<');
4856 if (index!=-1)
4857 {
4858 n=n.left(index);
4859 }
4860 bool result = rightScopeMatch(scope,n);
4861 return result;
4862}
4863
4865{
4866 if (name.isEmpty()) return 0;
4867 int l = static_cast<int>(name.length());
4868 if (name[l-1]=='>') // search backward to find the matching <, allowing nested <...> and strings.
4869 {
4870 int count=1;
4871 int i=l-2;
4872 char insideQuote=0;
4873 while (count>0 && i>=0)
4874 {
4875 char c = name[i--];
4876 switch (c)
4877 {
4878 case '>': if (!insideQuote) count++; break;
4879 case '<': if (!insideQuote) count--; break;
4880 case '\'': if (!insideQuote) insideQuote=c;
4881 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
4882 break;
4883 case '"': if (!insideQuote) insideQuote=c;
4884 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
4885 break;
4886 default: break;
4887 }
4888 }
4889 if (i>=0) l=i+1;
4890 }
4891 return l;
4892}
4893
4895 const Entry *root,
4896 Definition *context,
4897 ClassDefMutable *cd,
4898 const BaseInfo *bi,
4899 const TemplateNameMap &templateNames,
4901 bool isArtificial
4902 )
4903{
4904 AUTO_TRACE("name={} base={} isArtificial={} mode={}",cd->name(),bi->name,isArtificial,(int)mode);
4905
4906 QCString biName=bi->name;
4907 bool explicitGlobalScope=FALSE;
4908 if (biName.startsWith("::")) // explicit global scope
4909 {
4910 biName=biName.right(biName.length()-2);
4911 explicitGlobalScope=TRUE;
4912 }
4913
4914 Entry *parentNode=root->parent();
4915 bool lastParent=FALSE;
4916 do // for each parent scope, starting with the largest scope
4917 // (in case of nested classes)
4918 {
4919 QCString scopeName= parentNode ? parentNode->name : QCString();
4920 int scopeOffset=explicitGlobalScope ? 0 : static_cast<int>(scopeName.length());
4921 do // try all parent scope prefixes, starting with the largest scope
4922 {
4923 //printf("scopePrefix='%s' biName='%s'\n",
4924 // qPrint(scopeName.left(scopeOffset)),qPrint(biName));
4925
4926 QCString baseClassName=biName;
4927 if (scopeOffset>0)
4928 {
4929 baseClassName.prepend(scopeName.left(scopeOffset)+"::");
4930 }
4931 if (root->lang==SrcLangExt::CSharp)
4932 {
4933 baseClassName = mangleCSharpGenericName(baseClassName);
4934 }
4935 AUTO_TRACE_ADD("cd='{}' baseClassName='{}'",cd->name(),baseClassName);
4936 SymbolResolver resolver(cd->getFileDef());
4937 ClassDefMutable *baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
4938 baseClassName,
4939 mode==Undocumented,
4940 true
4941 );
4942 const MemberDef *baseClassTypeDef = resolver.getTypedef();
4943 QCString templSpec = resolver.getTemplateSpec();
4944 //printf("baseClassName=%s baseClass=%p cd=%p explicitGlobalScope=%d\n",
4945 // qPrint(baseClassName),baseClass,cd,explicitGlobalScope);
4946 //printf(" scope='%s' baseClassName='%s' baseClass=%s templSpec=%s\n",
4947 // cd ? qPrint(cd->name()):"<none>",
4948 // qPrint(baseClassName),
4949 // baseClass?qPrint(baseClass->name()):"<none>",
4950 // qPrint(templSpec)
4951 // );
4952 //if (baseClassName.left(root->name.length())!=root->name ||
4953 // baseClassName.at(root->name.length())!='<'
4954 // ) // Check for base class with the same name.
4955 // // If found then look in the outer scope for a match
4956 // // and prevent recursion.
4957 if (!isRecursiveBaseClass(root->name,baseClassName)
4958 || explicitGlobalScope
4959 // sadly isRecursiveBaseClass always true for UNO IDL ifc/svc members
4960 // (i.e. this is needed for addInterfaceOrServiceToServiceOrSingleton)
4961 || (root->lang==SrcLangExt::IDL &&
4962 (root->section.isExportedInterface() ||
4963 root->section.isIncludedService()))
4964 )
4965 {
4966 AUTO_TRACE_ADD("class relation '{}' inherited/used by '{}' found prot={} virt={} templSpec='{}'",
4967 baseClassName, root->name, bi->prot, bi->virt, templSpec);
4968
4969 int i=findTemplateSpecializationPosition(baseClassName);
4970 int si=baseClassName.findRev("::",i);
4971 if (si==-1) si=0;
4972 if (baseClass==nullptr && static_cast<size_t>(i)!=baseClassName.length())
4973 // base class has template specifiers
4974 {
4975 // TODO: here we should try to find the correct template specialization
4976 // but for now, we only look for the unspecialized base class.
4977 int e=findEndOfTemplate(baseClassName,i+1);
4978 //printf("baseClass==0 i=%d e=%d\n",i,e);
4979 if (e!=-1) // end of template was found at e
4980 {
4981 templSpec = removeRedundantWhiteSpace(baseClassName.mid(i,e-i));
4982 baseClassName = baseClassName.left(i)+baseClassName.right(baseClassName.length()-e);
4983 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
4984 baseClassName,
4985 mode==Undocumented,
4986 true
4987 );
4988 baseClassTypeDef = resolver.getTypedef();
4989 //printf("baseClass=%p -> baseClass=%s templSpec=%s\n",
4990 // baseClass,qPrint(baseClassName),qPrint(templSpec));
4991 }
4992 }
4993 else if (baseClass && !templSpec.isEmpty()) // we have a known class, but also
4994 // know it is a template, so see if
4995 // we can also link to the explicit
4996 // instance (for instance if a class
4997 // derived from a template argument)
4998 {
4999 //printf("baseClass=%s templSpec=%s\n",qPrint(baseClass->name()),qPrint(templSpec));
5000 ClassDefMutable *templClass=getClassMutable(baseClass->name()+templSpec);
5001 if (templClass)
5002 {
5003 // use the template instance instead of the template base.
5004 baseClass = templClass;
5005 templSpec.clear();
5006 }
5007 }
5008
5009 //printf("cd=%p baseClass=%p\n",cd,baseClass);
5010 bool found=baseClass!=nullptr && (baseClass!=cd || mode==TemplateInstances);
5011 AUTO_TRACE_ADD("1. found={}",found);
5012 if (!found && si!=-1)
5013 {
5014 // replace any namespace aliases
5015 replaceNamespaceAliases(baseClassName);
5016 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
5017 baseClassName,
5018 mode==Undocumented,
5019 true
5020 );
5021 baseClassTypeDef = resolver.getTypedef();
5022 found=baseClass!=nullptr && baseClass!=cd;
5023 if (found) templSpec = resolver.getTemplateSpec();
5024 }
5025 AUTO_TRACE_ADD("2. found={}",found);
5026
5027 if (!found)
5028 {
5029 baseClass=toClassDefMutable(findClassWithinClassContext(context,cd,baseClassName));
5030 //printf("findClassWithinClassContext(%s,%s)=%p\n",
5031 // qPrint(cd->name()),qPrint(baseClassName),baseClass);
5032 found = baseClass!=nullptr && baseClass!=cd;
5033
5034 }
5035 AUTO_TRACE_ADD("3. found={}",found);
5036 if (!found)
5037 {
5038 // for PHP the "use A\B as C" construct map class C to A::B, so we lookup
5039 // the class name also in the alias mapping.
5040 auto it = Doxygen::namespaceAliasMap.find(baseClassName.str());
5041 if (it!=Doxygen::namespaceAliasMap.end()) // see if it is indeed a class.
5042 {
5043 baseClass=getClassMutable(it->second.alias);
5044 found = baseClass!=nullptr && baseClass!=cd;
5045 }
5046 }
5047 bool isATemplateArgument = templateNames.find(biName.str())!=templateNames.end();
5048
5049 AUTO_TRACE_ADD("4. found={}",found);
5050 if (found)
5051 {
5052 AUTO_TRACE_ADD("Documented base class '{}' templSpec='{}'",biName,templSpec);
5053 // add base class to this class
5054
5055 // if templSpec is not empty then we should "instantiate"
5056 // the template baseClass. A new ClassDef should be created
5057 // to represent the instance. To be able to add the (instantiated)
5058 // members and documentation of a template class
5059 // (inserted in that template class at a later stage),
5060 // the template should know about its instances.
5061 // the instantiation process, should be done in a recursive way,
5062 // since instantiating a template may introduce new inheritance
5063 // relations.
5064 if (!templSpec.isEmpty() && mode==TemplateInstances)
5065 {
5066 // if baseClass is actually a typedef then we should not
5067 // instantiate it, since typedefs are in a different namespace
5068 // see bug531637 for an example where this would otherwise hang
5069 // Doxygen
5070 if (baseClassTypeDef==nullptr)
5071 {
5072 //printf(" => findTemplateInstanceRelation: %s\n",qPrint(baseClass->name()));
5073 findTemplateInstanceRelation(root,context,baseClass,templSpec,templateNames,baseClass->isArtificial());
5074 }
5075 }
5076 else if (mode==DocumentedOnly || mode==Undocumented)
5077 {
5078 //printf(" => insert base class\n");
5079 QCString usedName;
5080 if (baseClassTypeDef)
5081 {
5082 usedName=biName;
5083 //printf("***** usedName=%s templSpec=%s\n",qPrint(usedName),qPrint(templSpec));
5084 }
5085 Protection prot = bi->prot;
5086 if (Config_getBool(SIP_SUPPORT)) prot=Protection::Public;
5087 if (cd!=baseClass && !cd->isSubClass(baseClass) && baseClass->isBaseClass(cd,true,templSpec)==0) // check for recursion, see bug690787
5088 {
5089 AUTO_TRACE_ADD("insertBaseClass name={} prot={} virt={} templSpec={}",usedName,prot,bi->virt,templSpec);
5090 cd->insertBaseClass(baseClass,usedName,prot,bi->virt,templSpec);
5091 // add this class as super class to the base class
5092 baseClass->insertSubClass(cd,prot,bi->virt,templSpec);
5093 }
5094 else
5095 {
5096 warn(root->fileName,root->startLine,
5097 "Detected potential recursive class relation "
5098 "between class {} and base class {}!",
5099 cd->name(),baseClass->name()
5100 );
5101 }
5102 }
5103 return TRUE;
5104 }
5105 else if (mode==Undocumented && (scopeOffset==0 || isATemplateArgument))
5106 {
5107 AUTO_TRACE_ADD("New undocumented base class '{}' baseClassName='{}' templSpec='{}' isArtificial={}",
5108 biName,baseClassName,templSpec,isArtificial);
5109 baseClass=nullptr;
5110 if (isATemplateArgument)
5111 {
5112 baseClass = toClassDefMutable(Doxygen::hiddenClassLinkedMap->find(baseClassName));
5113 if (baseClass==nullptr) // not found (or alias)
5114 {
5115 baseClass= toClassDefMutable(
5116 Doxygen::hiddenClassLinkedMap->add(baseClassName,
5117 createClassDef(root->fileName,root->startLine,root->startColumn,
5118 baseClassName,
5119 ClassDef::Class)));
5120 if (baseClass) // really added (not alias)
5121 {
5122 if (isArtificial) baseClass->setArtificial(TRUE);
5123 baseClass->setLanguage(root->lang);
5124 }
5125 }
5126 }
5127 else
5128 {
5129 baseClass = toClassDefMutable(Doxygen::classLinkedMap->find(baseClassName));
5130 //printf("*** classDDict->find(%s)=%p biName=%s templSpec=%s\n",
5131 // qPrint(baseClassName),baseClass,qPrint(biName),qPrint(templSpec));
5132 if (baseClass==nullptr) // not found (or alias)
5133 {
5134 baseClass = toClassDefMutable(
5135 Doxygen::classLinkedMap->add(baseClassName,
5136 createClassDef(root->fileName,root->startLine,root->startColumn,
5137 baseClassName,
5138 ClassDef::Class)));
5139 if (baseClass) // really added (not alias)
5140 {
5141 if (isArtificial) baseClass->setArtificial(TRUE);
5142 baseClass->setLanguage(root->lang);
5143 si = baseClassName.findRev("::");
5144 if (si!=-1) // class is nested
5145 {
5146 Definition *sd = findScopeFromQualifiedName(Doxygen::globalScope,baseClassName.left(si),nullptr,root->tagInfo());
5147 if (sd==nullptr || sd==Doxygen::globalScope) // outer scope not found
5148 {
5149 baseClass->setArtificial(TRUE); // see bug678139
5150 }
5151 }
5152 }
5153 }
5154 }
5155 if (baseClass)
5156 {
5157 if (biName.endsWith("-p"))
5158 {
5159 biName="<"+biName.left(biName.length()-2)+">";
5160 }
5161 if (!cd->isSubClass(baseClass) && cd!=baseClass && cd->isBaseClass(baseClass,true,templSpec)==0) // check for recursion
5162 {
5163 AUTO_TRACE_ADD("insertBaseClass name={} prot={} virt={} templSpec={}",biName,bi->prot,bi->virt,templSpec);
5164 // add base class to this class
5165 cd->insertBaseClass(baseClass,biName,bi->prot,bi->virt,templSpec);
5166 // add this class as super class to the base class
5167 baseClass->insertSubClass(cd,bi->prot,bi->virt,templSpec);
5168 }
5169 // the undocumented base was found in this file
5170 baseClass->insertUsedFile(root->fileDef());
5171
5172 Definition *scope = buildScopeFromQualifiedName(baseClass->name(),root->lang,nullptr);
5173 if (scope!=baseClass)
5174 {
5175 baseClass->setOuterScope(scope);
5176 }
5177
5178 if (baseClassName.endsWith("-p"))
5179 {
5181 }
5182 return TRUE;
5183 }
5184 else
5185 {
5186 AUTO_TRACE_ADD("Base class '{}' not created (alias?)",biName);
5187 }
5188 }
5189 else
5190 {
5191 AUTO_TRACE_ADD("Base class '{}' not found",biName);
5192 }
5193 }
5194 else
5195 {
5196 if (mode!=TemplateInstances)
5197 {
5198 warn(root->fileName,root->startLine,
5199 "Detected potential recursive class relation "
5200 "between class {} and base class {}!",
5201 root->name,baseClassName
5202 );
5203 }
5204 // for mode==TemplateInstance this case is quite common and
5205 // indicates a relation between a template class and a template
5206 // instance with the same name.
5207 }
5208 if (scopeOffset==0)
5209 {
5210 scopeOffset=-1;
5211 }
5212 else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1)
5213 {
5214 scopeOffset=0;
5215 }
5216 //printf("new scopeOffset='%d'",scopeOffset);
5217 } while (scopeOffset>=0);
5218
5219 if (parentNode==nullptr)
5220 {
5221 lastParent=TRUE;
5222 }
5223 else
5224 {
5225 parentNode=parentNode->parent();
5226 }
5227 } while (lastParent);
5228
5229 return FALSE;
5230}
5231
5232//----------------------------------------------------------------------
5233// Computes the base and super classes for each class in the tree
5234
5235static bool isClassSection(const Entry *root)
5236{
5237 if ( !root->name.isEmpty() )
5238 {
5239 if (root->section.isCompound())
5240 // is it a compound (class, struct, union, interface ...)
5241 {
5242 return TRUE;
5243 }
5244 else if (root->section.isCompoundDoc())
5245 // is it a documentation block with inheritance info.
5246 {
5247 bool hasExtends = !root->extends.empty();
5248 if (hasExtends) return TRUE;
5249 }
5250 }
5251 return FALSE;
5252}
5253
5254
5255/*! Builds a dictionary of all entry nodes in the tree starting with \a root
5256 */
5257static void findClassEntries(const Entry *root)
5258{
5259 if (isClassSection(root))
5260 {
5261 g_classEntries.emplace(root->name.str(),root);
5262 }
5263 for (const auto &e : root->children()) findClassEntries(e.get());
5264}
5265
5267{
5268 // strip any anonymous scopes first
5271 int i=0;
5272 if ((root->lang==SrcLangExt::CSharp || root->lang==SrcLangExt::Java) &&
5273 (i=bName.find('<'))!=-1)
5274 {
5275 // a Java/C# generic class looks like a C++ specialization, so we need to strip the
5276 // template part before looking for matches
5277 if (root->lang==SrcLangExt::CSharp)
5278 {
5279 bName = mangleCSharpGenericName(root->name);
5280 }
5281 else
5282 {
5283 bName = bName.left(i);
5284 }
5285 }
5286 return bName;
5287}
5288
5289/*! Using the dictionary build by findClassEntries(), this
5290 * function will look for additional template specialization that
5291 * exists as inheritance relations only. These instances will be
5292 * added to the template they are derived from.
5293 */
5295{
5296 AUTO_TRACE();
5297 ClassDefSet visitedClasses;
5298 for (const auto &[name,root] : g_classEntries)
5299 {
5300 QCString bName = extractClassName(root);
5301 ClassDefMutable *cdm = getClassMutable(bName);
5302 if (cdm)
5303 {
5305 }
5306 }
5307}
5308
5310{
5311 AUTO_TRACE("root->name={} cd={}",root->name,cd->name());
5312 int i = root->name.find('<');
5313 int j = root->name.findRev('>');
5314 int k = root->name.find("::",j+1); // A<T::B> => ok, A<T>::B => nok
5315 if (i!=-1 && j!=-1 && k==-1 && root->lang!=SrcLangExt::CSharp && root->lang!=SrcLangExt::Java)
5316 {
5317 ClassDefMutable *master = getClassMutable(root->name.left(i));
5318 if (master && master!=cd && !cd->templateMaster())
5319 {
5320 AUTO_TRACE_ADD("class={} master={}",cd->name(),cd->templateMaster()?cd->templateMaster()->name():"<none>",master->name());
5321 cd->setTemplateMaster(master);
5322 master->insertExplicitTemplateInstance(cd,root->name.mid(i));
5323 }
5324 }
5325}
5326
5328{
5329 AUTO_TRACE();
5330 for (const auto &[name,root] : g_classEntries)
5331 {
5332 QCString bName = extractClassName(root);
5333 ClassDefMutable *cdm = getClassMutable(bName);
5334 if (cdm)
5335 {
5336 findUsedClassesForClass(root,cdm,cdm,cdm,TRUE);
5338 cdm->addTypeConstraints();
5339 }
5340 }
5341}
5342
5344{
5345 AUTO_TRACE();
5346 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5347 {
5348 if (!nd->hasDocumentation())
5349 {
5350 if ((guessSection(nd->getDefFileName()).isHeader() ||
5351 nd->getLanguage() == SrcLangExt::Fortran) && // Fortran doesn't have header files.
5352 !Config_getBool(HIDE_UNDOC_NAMESPACES) // undocumented namespaces are visible
5353 )
5354 {
5355 warn_undoc(nd->getDefFileName(),nd->getDefLine(), "{} {} is not documented.",
5356 nd->getLanguage() == SrcLangExt::Fortran ? "Module" : "Namespace",
5357 nd->name());
5358 }
5359 }
5360 }
5361}
5362
5364{
5365 AUTO_TRACE();
5366 for (const auto &[name,root] : g_classEntries)
5367 {
5368 QCString bName = extractClassName(root);
5369 ClassDefMutable *cd = getClassMutable(bName);
5370 if (cd)
5371 {
5373 }
5374 size_t numMembers = cd ? cd->memberNameInfoLinkedMap().size() : 0;
5375 if ((cd==nullptr || (!cd->hasDocumentation() && !cd->isReference())) && numMembers>0 && !bName.endsWith("::"))
5376 {
5377 if (!root->name.isEmpty() && root->name.find('@')==-1 && // normal name
5378 (guessSection(root->fileName).isHeader() ||
5379 Config_getBool(EXTRACT_LOCAL_CLASSES)) && // not defined in source file
5380 protectionLevelVisible(root->protection) && // hidden by protection
5381 !Config_getBool(HIDE_UNDOC_CLASSES) // undocumented class are visible
5382 )
5383 warn_undoc(root->fileName,root->startLine, "Compound {} is not documented.", root->name);
5384 }
5385 }
5386}
5387
5389{
5390 AUTO_TRACE();
5391 for (const auto &[name,root] : g_classEntries)
5392 {
5396 // strip any anonymous scopes first
5397 if (cd && !cd->getTemplateInstances().empty())
5398 {
5399 AUTO_TRACE_ADD("Template class '{}'",cd->name());
5400 for (const auto &ti : cd->getTemplateInstances()) // for each template instance
5401 {
5402 ClassDefMutable *tcd=toClassDefMutable(ti.classDef);
5403 if (tcd)
5404 {
5405 AUTO_TRACE_ADD("Template instance '{}'",tcd->name());
5406 QCString templSpec = ti.templSpec;
5407 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(tcd->getLanguage(),templSpec);
5408 for (const BaseInfo &bi : root->extends)
5409 {
5410 // check if the base class is a template argument
5411 BaseInfo tbi = bi;
5412 const ArgumentList &tl = cd->templateArguments();
5413 if (!tl.empty())
5414 {
5415 TemplateNameMap baseClassNames = tcd->getTemplateBaseClassNames();
5416 TemplateNameMap templateNames = getTemplateArgumentsInName(tl,bi.name.str());
5417 // for each template name that we inherit from we need to
5418 // substitute the formal with the actual arguments
5419 TemplateNameMap actualTemplateNames;
5420 for (const auto &tn_kv : templateNames)
5421 {
5422 size_t templIndex = tn_kv.second;
5423 Argument actArg;
5424 bool hasActArg=FALSE;
5425 if (templIndex<templArgs->size())
5426 {
5427 actArg=templArgs->at(templIndex);
5428 hasActArg=TRUE;
5429 }
5430 if (hasActArg &&
5431 baseClassNames.find(actArg.type.str())!=baseClassNames.end() &&
5432 actualTemplateNames.find(actArg.type.str())==actualTemplateNames.end()
5433 )
5434 {
5435 actualTemplateNames.emplace(actArg.type.str(),static_cast<int>(templIndex));
5436 }
5437 }
5438
5439 tbi.name = substituteTemplateArgumentsInString(bi.name,tl,templArgs.get());
5440 // find a documented base class in the correct scope
5441 if (!findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,DocumentedOnly,FALSE))
5442 {
5443 // no documented base class -> try to find an undocumented one
5444 findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,Undocumented,TRUE);
5445 }
5446 }
5447 }
5448 }
5449 }
5450 }
5451 }
5452}
5453
5454//-----------------------------------------------------------------------
5455// compute the references (anchors in HTML) for each function in the file
5456
5458{
5459 AUTO_TRACE();
5460 for (const auto &cd : *Doxygen::classLinkedMap)
5461 {
5462 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5463 if (cdm)
5464 {
5465 cdm->computeAnchors();
5466 }
5467 }
5468 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5469 {
5470 for (const auto &fd : *fn)
5471 {
5472 fd->computeAnchors();
5473 }
5474 }
5475 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5476 {
5478 if (ndm)
5479 {
5480 ndm->computeAnchors();
5481 }
5482 }
5483 for (const auto &gd : *Doxygen::groupLinkedMap)
5484 {
5485 gd->computeAnchors();
5486 }
5487}
5488
5489//----------------------------------------------------------------------
5490
5491
5492template<typename Func>
5493static void applyToAllDefinitions(Func func)
5494{
5495 for (const auto &cd : *Doxygen::classLinkedMap)
5496 {
5497 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5498 if (cdm)
5499 {
5500 func(cdm);
5501 }
5502 }
5503
5504 for (const auto &cd : *Doxygen::conceptLinkedMap)
5505 {
5506 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
5507 if (cdm)
5508 {
5509 func(cdm);
5510 }
5511 }
5512
5513 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5514 {
5515 for (const auto &fd : *fn)
5516 {
5517 func(fd.get());
5518 }
5519 }
5520
5521 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5522 {
5524 if (ndm)
5525 {
5526 func(ndm);
5527 }
5528 }
5529
5530 for (const auto &gd : *Doxygen::groupLinkedMap)
5531 {
5532 func(gd.get());
5533 }
5534
5535 for (const auto &pd : *Doxygen::pageLinkedMap)
5536 {
5537 func(pd.get());
5538 }
5539
5540 for (const auto &dd : *Doxygen::dirLinkedMap)
5541 {
5542 func(dd.get());
5543 }
5544
5545 func(&ModuleManager::instance());
5546}
5547
5548//----------------------------------------------------------------------
5549
5551{
5552 AUTO_TRACE();
5553 applyToAllDefinitions([](auto* obj) { obj->addRequirementReferences(); });
5554}
5555
5556//----------------------------------------------------------------------
5557
5559{
5560 AUTO_TRACE();
5561 applyToAllDefinitions([](auto* obj) { obj->addListReferences(); });
5562}
5563
5564
5565//----------------------------------------------------------------------
5566
5568{
5569 AUTO_TRACE();
5571 {
5572 rl->generatePage();
5573 }
5574}
5575
5576//----------------------------------------------------------------------
5577// Copy the documentation in entry 'root' to member definition 'md' and
5578// set the function declaration of the member to 'funcDecl'. If the boolean
5579// over_load is set the standard overload text is added.
5580
5581static void addMemberDocs(const Entry *root,
5582 MemberDefMutable *md, const QCString &funcDecl,
5583 const ArgumentList *al,
5584 bool over_load,
5585 TypeSpecifier spec
5586 )
5587{
5588 if (md==nullptr) return;
5589 AUTO_TRACE("scope='{}' name='{}' args='{}' funcDecl='{}' mSpec={}",
5590 root->parent()->name,md->name(),md->argsString(),funcDecl,spec);
5591 if (!root->section.isDoc()) // @fn or @var does not need to specify the complete definition, so don't overwrite it
5592 {
5593 QCString fDecl=funcDecl;
5594 // strip extern specifier
5595 fDecl.stripPrefix("extern ");
5596 md->setDefinition(fDecl);
5597 }
5599 md->addQualifiers(root->qualifiers);
5601 const NamespaceDef *nd=md->getNamespaceDef();
5602 QCString fullName;
5603 if (cd)
5604 fullName = cd->name();
5605 else if (nd)
5606 fullName = nd->name();
5607
5608 if (!fullName.isEmpty()) fullName+="::";
5609 fullName+=md->name();
5610 FileDef *rfd=root->fileDef();
5611
5612 // TODO determine scope based on root not md
5613 Definition *rscope = md->getOuterScope();
5614
5615 const ArgumentList &mdAl = md->argumentList();
5616 if (al)
5617 {
5618 ArgumentList mergedAl = *al;
5619 //printf("merging arguments (1) docs=%d\n",root->doc.isEmpty());
5620 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedAl,!root->doc.isEmpty());
5621 }
5622 else
5623 {
5624 if (
5625 matchArguments2( md->getOuterScope(), md->getFileDef(),md->typeString(),const_cast<ArgumentList*>(&mdAl),
5626 rscope,rfd,root->type,&root->argList,
5627 TRUE, root->lang
5628 )
5629 )
5630 {
5631 //printf("merging arguments (2)\n");
5632 ArgumentList mergedArgList = root->argList;
5633 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.isEmpty());
5634 }
5635 }
5636 if (over_load) // the \overload keyword was used
5637 {
5639 if (!root->doc.isEmpty())
5640 {
5641 doc+="<p>";
5642 doc+=root->doc;
5643 }
5644 md->setDocumentation(doc,root->docFile,root->docLine);
5646 md->setDocsForDefinition(!root->proto);
5647 }
5648 else
5649 {
5650 //printf("overwrite!\n");
5651 md->setDocumentation(root->doc,root->docFile,root->docLine);
5652 md->setDocsForDefinition(!root->proto);
5653
5654 //printf("overwrite!\n");
5655 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
5656
5657 if (
5658 (md->inbodyDocumentation().isEmpty() ||
5659 !root->parent()->name.isEmpty()
5660 ) && !root->inbodyDocs.isEmpty()
5661 )
5662 {
5664 }
5665 }
5666
5667 //printf("initializer: '%s'(isEmpty=%d) '%s'(isEmpty=%d)\n",
5668 // qPrint(md->initializer()),md->initializer().isEmpty(),
5669 // qPrint(root->initializer),root->initializer.isEmpty()
5670 // );
5671 std::string rootInit = root->initializer.str();
5672 if (md->initializer().isEmpty() && !rootInit.empty())
5673 {
5674 //printf("setInitializer\n");
5675 md->setInitializer(rootInit);
5676 }
5677 if (md->requiresClause().isEmpty() && !root->req.isEmpty())
5678 {
5679 md->setRequiresClause(root->req);
5680 }
5681
5682 md->setMaxInitLines(root->initLines);
5683
5684 if (rfd)
5685 {
5686 if ((md->getStartBodyLine()==-1 && root->bodyLine!=-1)
5687 )
5688 {
5689 //printf("Setting new body segment [%d,%d]\n",root->bodyLine,root->endBodyLine);
5690 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
5691 md->setBodyDef(rfd);
5692 }
5693
5694 md->setRefItems(root->sli);
5695 md->setRequirementReferences(root->rqli);
5696 }
5697
5699 md->addQualifiers(root->qualifiers);
5700
5701 md->mergeMemberSpecifiers(spec);
5703 addMemberToGroups(root,md);
5705 if (cd) cd->insertUsedFile(rfd);
5706 //printf("root->mGrpId=%d\n",root->mGrpId);
5707 if (root->mGrpId!=-1)
5708 {
5709 if (md->getMemberGroupId()!=-1)
5710 {
5711 if (md->getMemberGroupId()!=root->mGrpId)
5712 {
5713 warn(root->fileName,root->startLine,
5714 "member {} belongs to two different groups. The second one found here will be ignored.",
5715 md->name()
5716 );
5717 }
5718 }
5719 else // set group id
5720 {
5721 //printf("setMemberGroupId=%d md=%s\n",root->mGrpId,qPrint(md->name()));
5722 md->setMemberGroupId(root->mGrpId);
5723 }
5724 }
5725 md->addQualifiers(root->qualifiers);
5726}
5727
5728//----------------------------------------------------------------------
5729// find a class definition given the scope name and (optionally) a
5730// template list specifier
5731
5733 const QCString &scopeName)
5734{
5735 SymbolResolver resolver(fd);
5736 const ClassDef *tcd = resolver.resolveClass(nd,scopeName,true,true);
5737 //printf("findClassDefinition(fd=%s,ns=%s,scopeName=%s)='%s'\n",
5738 // qPrint(fd?fd->name():""),qPrint(nd?nd->name():""),
5739 // qPrint(scopeName),qPrint(tcd?tcd->name():""));
5740 return tcd;
5741}
5742
5743//----------------------------------------------------------------------------
5744// Returns TRUE, if the entry belongs to the group of the member definition,
5745// otherwise FALSE.
5746
5747static bool isEntryInGroupOfMember(const Entry *root,const MemberDef *md,bool allowNoGroup=false)
5748{
5749 const GroupDef *gd = md->getGroupDef();
5750 if (!gd)
5751 {
5752 return allowNoGroup;
5753 }
5754
5755 for (const auto &g : root->groups)
5756 {
5757 if (g.groupname == gd->name())
5758 {
5759 return true; // matching group
5760 }
5761 }
5762
5763 return false;
5764}
5765
5766//----------------------------------------------------------------------
5767// Adds the documentation contained in 'root' to a global function
5768// with name 'name' and argument list 'args' (for overloading) and
5769// function declaration 'decl' to the corresponding member definition.
5770
5771static bool findGlobalMember(const Entry *root,
5772 const QCString &namespaceName,
5773 const QCString &type,
5774 const QCString &name,
5775 const QCString &tempArg,
5776 const QCString &,
5777 const QCString &decl,
5778 TypeSpecifier /* spec */)
5779{
5780 AUTO_TRACE("namespace='{}' type='{}' name='{}' tempArg='{}' decl='{}'",namespaceName,type,name,tempArg,decl);
5781 QCString n=name;
5782 if (n.isEmpty()) return FALSE;
5783 if (n.find("::")!=-1) return FALSE; // skip undefined class members
5784 MemberName *mn=Doxygen::functionNameLinkedMap->find(n+tempArg); // look in function dictionary
5785 if (mn==nullptr)
5786 {
5787 mn=Doxygen::functionNameLinkedMap->find(n); // try without template arguments
5788 }
5789 if (mn) // function name defined
5790 {
5791 AUTO_TRACE_ADD("Found symbol name");
5792 //int count=0;
5793 bool found=FALSE;
5794 for (const auto &md : *mn)
5795 {
5796 // If the entry has groups, then restrict the search to members which are
5797 // in one of the groups of the entry. If md is not associated with a group yet,
5798 // allow this documentation entry to add the group info.
5799 if (!root->groups.empty() && !isEntryInGroupOfMember(root, md.get(), true))
5800 {
5801 continue;
5802 }
5803
5804 const NamespaceDef *nd=nullptr;
5805 if (md->isAlias() && md->getOuterScope() &&
5806 md->getOuterScope()->definitionType()==Definition::TypeNamespace)
5807 {
5808 nd = toNamespaceDef(md->getOuterScope());
5809 }
5810 else
5811 {
5812 nd = md->getNamespaceDef();
5813 }
5814
5815 // special case for strong enums
5816 int enumNamePos=0;
5817 if (nd && md->isEnumValue() && (enumNamePos=namespaceName.findRev("::"))!=-1)
5818 { // md part of a strong enum in a namespace?
5819 QCString enumName = namespaceName.mid(enumNamePos+2);
5820 if (namespaceName.left(enumNamePos)==nd->name())
5821 {
5822 MemberName *enumMn=Doxygen::functionNameLinkedMap->find(enumName);
5823 if (enumMn)
5824 {
5825 for (const auto &emd : *enumMn)
5826 {
5827 found = emd->isStrong() && md->getEnumScope()==emd.get();
5828 if (found)
5829 {
5830 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,nullptr,FALSE,root->spec);
5831 break;
5832 }
5833 }
5834 }
5835 }
5836 if (found)
5837 {
5838 break;
5839 }
5840 }
5841 else if (nd==nullptr && md->isEnumValue()) // md part of global strong enum?
5842 {
5843 MemberName *enumMn=Doxygen::functionNameLinkedMap->find(namespaceName);
5844 if (enumMn)
5845 {
5846 for (const auto &emd : *enumMn)
5847 {
5848 found = emd->isStrong() && md->getEnumScope()==emd.get();
5849 if (found)
5850 {
5851 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,nullptr,FALSE,root->spec);
5852 break;
5853 }
5854 }
5855 }
5856 }
5857
5858 const FileDef *fd=root->fileDef();
5859 //printf("File %s\n",fd ? qPrint(fd->name()) : "<none>");
5861 if (fd)
5862 {
5863 nl = fd->getUsedNamespaces();
5864 }
5865 //printf("NamespaceList %p\n",nl);
5866
5867 // search in the list of namespaces that are imported via a
5868 // using declaration
5869 bool viaUsingDirective = nd && nl.find(nd->qualifiedName())!=nullptr;
5870
5871 if ((namespaceName.isEmpty() && nd==nullptr) || // not in a namespace
5872 (nd && nd->name()==namespaceName) || // or in the same namespace
5873 viaUsingDirective // member in 'using' namespace
5874 )
5875 {
5876 AUTO_TRACE_ADD("Try to add member '{}' to scope '{}'",md->name(),namespaceName);
5877
5878 NamespaceDef *rnd = nullptr;
5879 if (!namespaceName.isEmpty()) rnd = Doxygen::namespaceLinkedMap->find(namespaceName);
5880
5881 const ArgumentList &mdAl = md.get()->argumentList();
5882 bool matching=
5883 (mdAl.empty() && root->argList.empty()) ||
5884 md->isVariable() || md->isTypedef() || /* in case of function pointers */
5885 matchArguments2(md->getOuterScope(),md->getFileDef(),md->typeString(),&mdAl,
5886 rnd ? rnd : Doxygen::globalScope,fd,root->type,&root->argList,
5887 FALSE,root->lang);
5888
5889 // for template members we need to check if the number of
5890 // template arguments is the same, otherwise we are dealing with
5891 // different functions.
5892 if (matching && !root->tArgLists.empty())
5893 {
5894 const ArgumentList &mdTempl = md->templateArguments();
5895 if (root->tArgLists.back().size()!=mdTempl.size())
5896 {
5897 matching=FALSE;
5898 }
5899 }
5900
5901 //printf("%s<->%s\n",
5902 // qPrint(argListToString(md->argumentList())),
5903 // qPrint(argListToString(root->argList)));
5904
5905 // For static members we also check if the comment block was found in
5906 // the same file. This is needed because static members with the same
5907 // name can be in different files. Thus it would be wrong to just
5908 // put the comment block at the first syntactically matching member. If
5909 // the comment block belongs to a group of the static member, then add
5910 // the documentation even if it is in a different file.
5911 if (matching && md->isStatic() &&
5912 md->getDefFileName()!=root->fileName &&
5913 mn->size()>1 &&
5914 !isEntryInGroupOfMember(root,md.get()))
5915 {
5916 matching = FALSE;
5917 }
5918
5919 // for template member we also need to check the return type and requires
5920 if (!md->templateArguments().empty() && !root->tArgLists.empty())
5921 {
5922 //printf("Comparing return types '%s'<->'%s'\n",
5923 // md->typeString(),type);
5924 if (md->templateArguments().size()!=root->tArgLists.back().size() ||
5925 md->typeString()!=type ||
5926 md->requiresClause()!=root->req)
5927 {
5928 //printf(" ---> no matching\n");
5929 matching = FALSE;
5930 }
5931 }
5932
5933 if (matching) // add docs to the member
5934 {
5935 AUTO_TRACE_ADD("Match found");
5936 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,&root->argList,FALSE,root->spec);
5937 found=TRUE;
5938 break;
5939 }
5940 }
5941 }
5942 if (!found && root->relatesType!=RelatesType::Duplicate && root->section.isFunction()) // no match
5943 {
5944 QCString fullFuncDecl=decl;
5945 if (!root->argList.empty()) fullFuncDecl+=argListToString(root->argList,TRUE);
5946 QCString warnMsg = "no matching file member found for \n"+fullFuncDecl;
5947 if (mn->size()>0)
5948 {
5949 warnMsg+="\nPossible candidates:";
5950 for (const auto &md : *mn)
5951 {
5952 warnMsg+="\n '";
5953 warnMsg+=replaceAnonymousScopes(md->declaration());
5954 warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
5955 }
5956 }
5957 warn(root->fileName,root->startLine, "{}", qPrint(warnMsg));
5958 }
5959 }
5960 else // got docs for an undefined member!
5961 {
5962 if (root->type!="friend class" &&
5963 root->type!="friend struct" &&
5964 root->type!="friend union" &&
5965 root->type!="friend" &&
5966 (!Config_getBool(TYPEDEF_HIDES_STRUCT) ||
5967 root->type.find("typedef ")==-1)
5968 )
5969 {
5970 warn(root->fileName,root->startLine,
5971 "documented symbol '{}' was not declared or defined.",qPrint(decl)
5972 );
5973 }
5974 }
5975 return TRUE;
5976}
5977
5979 const ArgumentLists &srcTempArgLists,
5980 const ArgumentLists &dstTempArgLists
5981 )
5982{
5983 auto srcIt = srcTempArgLists.begin();
5984 auto dstIt = dstTempArgLists.begin();
5985 while (srcIt!=srcTempArgLists.end() && dstIt!=dstTempArgLists.end())
5986 {
5987 if ((*srcIt).size()!=(*dstIt).size()) return TRUE;
5988 ++srcIt;
5989 ++dstIt;
5990 }
5991 return FALSE;
5992}
5993
5994static bool scopeIsTemplate(const Definition *d)
5995{
5996 bool result=FALSE;
5997 //printf("> scopeIsTemplate(%s)\n",qPrint(d?d->name():"null"));
5999 {
6000 auto cd = toClassDef(d);
6001 result = cd->templateArguments().hasParameters() || cd->templateMaster()!=nullptr ||
6003 }
6004 //printf("< scopeIsTemplate=%d\n",result);
6005 return result;
6006}
6007
6009 const ArgumentLists &srcTempArgLists,
6010 const ArgumentLists &dstTempArgLists,
6011 const std::string &src
6012 )
6013{
6014 std::string dst;
6015 static const reg::Ex re(R"(\a\w*)");
6016 reg::Iterator it(src,re);
6018 //printf("type=%s\n",qPrint(sa->type));
6019 size_t p=0;
6020 for (; it!=end ; ++it) // for each word in srcType
6021 {
6022 const auto &match = *it;
6023 size_t i = match.position();
6024 size_t l = match.length();
6025 bool found=FALSE;
6026 dst+=src.substr(p,i-p);
6027 std::string name=match.str();
6028
6029 auto srcIt = srcTempArgLists.begin();
6030 auto dstIt = dstTempArgLists.begin();
6031 while (srcIt!=srcTempArgLists.end() && !found)
6032 {
6033 const ArgumentList *tdAli = nullptr;
6034 std::vector<Argument>::const_iterator tdaIt;
6035 if (dstIt!=dstTempArgLists.end())
6036 {
6037 tdAli = &(*dstIt);
6038 tdaIt = tdAli->begin();
6039 ++dstIt;
6040 }
6041
6042 const ArgumentList &tsaLi = *srcIt;
6043 for (auto tsaIt = tsaLi.begin(); tsaIt!=tsaLi.end() && !found; ++tsaIt)
6044 {
6045 Argument tsa = *tsaIt;
6046 const Argument *tda = nullptr;
6047 if (tdAli && tdaIt!=tdAli->end())
6048 {
6049 tda = &(*tdaIt);
6050 ++tdaIt;
6051 }
6052 //if (tda) printf("tsa=%s|%s tda=%s|%s\n",
6053 // qPrint(tsa.type),qPrint(tsa.name),
6054 // qPrint(tda->type),qPrint(tda->name));
6055 if (name==tsa.name.str())
6056 {
6057 if (tda && tda->name.isEmpty())
6058 {
6059 QCString tdaName = tda->name;
6060 QCString tdaType = tda->type;
6061 int vc=0;
6062 if (tdaType.startsWith("class ")) vc=6;
6063 else if (tdaType.startsWith("typename ")) vc=9;
6064 if (vc>0) // convert type=="class T" to type=="class" name=="T"
6065 {
6066 tdaName = tdaType.mid(vc);
6067 }
6068 if (!tdaName.isEmpty())
6069 {
6070 name=tdaName.str(); // substitute
6071 found=TRUE;
6072 }
6073 }
6074 }
6075 }
6076
6077 //printf(" srcList='%s' dstList='%s faList='%s'\n",
6078 // qPrint(argListToString(srclali.current())),
6079 // qPrint(argListToString(dstlali.current())),
6080 // funcTempArgList ? qPrint(argListToString(funcTempArgList)) : "<none>");
6081 ++srcIt;
6082 }
6083 dst+=name;
6084 p=i+l;
6085 }
6086 dst+=src.substr(p);
6087 //printf(" substituteTemplatesInString(%s)=%s\n",
6088 // qPrint(src),qPrint(dst));
6089 return dst;
6090}
6091
6093 const ArgumentLists &srcTempArgLists,
6094 const ArgumentLists &dstTempArgLists,
6095 const ArgumentList &src,
6096 ArgumentList &dst
6097 )
6098{
6099 auto dstIt = dst.begin();
6100 for (const Argument &sa : src)
6101 {
6102 QCString dstType = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.type.str());
6103 QCString dstArray = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.array.str());
6104 if (dstIt == dst.end())
6105 {
6106 Argument da = sa;
6107 da.type = dstType;
6108 da.array = dstArray;
6109 dst.push_back(da);
6110 dstIt = dst.end();
6111 }
6112 else
6113 {
6114 Argument da = *dstIt;
6115 da.type = dstType;
6116 da.array = dstArray;
6117 ++dstIt;
6118 }
6119 }
6124 srcTempArgLists,dstTempArgLists,
6125 src.trailingReturnType().str()));
6126 dst.setIsDeleted(src.isDeleted());
6127 dst.setRefQualifier(src.refQualifier());
6128 dst.setNoParameters(src.noParameters());
6129 //printf("substituteTemplatesInArgList: replacing %s with %s\n",
6130 // qPrint(argListToString(src)),qPrint(argListToString(dst))
6131 // );
6132}
6133
6134//-------------------------------------------------------------------------------------------
6135
6136static void addLocalObjCMethod(const Entry *root,
6137 const QCString &scopeName,
6138 const QCString &funcType,const QCString &funcName,const QCString &funcArgs,
6139 const QCString &exceptions,const QCString &funcDecl,
6140 TypeSpecifier spec)
6141{
6142 AUTO_TRACE();
6143 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
6144 ClassDefMutable *cd=nullptr;
6145 if (Config_getBool(EXTRACT_LOCAL_METHODS) && (cd=getClassMutable(scopeName)))
6146 {
6147 AUTO_TRACE_ADD("Local objective C method '{}' scopeName='{}'",root->name,scopeName);
6148 auto md = createMemberDef(
6149 root->fileName,root->startLine,root->startColumn,
6150 funcType,funcName,funcArgs,exceptions,
6151 root->protection,root->virt,root->isStatic,Relationship::Member,
6153 auto mmd = toMemberDefMutable(md.get());
6154 mmd->setTagInfo(root->tagInfo());
6155 mmd->setLanguage(root->lang);
6156 mmd->setId(root->id);
6157 mmd->makeImplementationDetail();
6158 mmd->setMemberClass(cd);
6159 mmd->setDefinition(funcDecl);
6161 mmd->addQualifiers(root->qualifiers);
6162 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6163 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6164 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6165 mmd->setDocsForDefinition(!root->proto);
6166 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6167 mmd->addSectionsToDefinition(root->anchors);
6168 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6169 FileDef *fd=root->fileDef();
6170 mmd->setBodyDef(fd);
6171 mmd->setMemberSpecifiers(spec);
6172 mmd->setVhdlSpecifiers(root->vhdlSpec);
6173 mmd->setMemberGroupId(root->mGrpId);
6174 cd->insertMember(md.get());
6175 cd->insertUsedFile(fd);
6176 mmd->setRefItems(root->sli);
6177 mmd->setRequirementReferences(root->rqli);
6178
6180 mn->push_back(std::move(md));
6181 }
6182 else
6183 {
6184 // local objective C method found for class without interface
6185 }
6186}
6187
6188//-------------------------------------------------------------------------------------------
6189
6190static void addMemberFunction(const Entry *root,
6191 MemberName *mn,
6192 const QCString &scopeName,
6193 const QCString &namespaceName,
6194 const QCString &className,
6195 const QCString &funcTyp,
6196 const QCString &funcName,
6197 const QCString &funcArgs,
6198 const QCString &funcTempList,
6199 const QCString &exceptions,
6200 const QCString &type,
6201 const QCString &args,
6202 bool isFriend,
6203 TypeSpecifier spec,
6204 const QCString &relates,
6205 const QCString &funcDecl,
6206 bool overloaded,
6207 bool isFunc)
6208{
6209 AUTO_TRACE();
6210 QCString funcType = funcTyp;
6211 int count=0;
6212 int noMatchCount=0;
6213 bool memFound=FALSE;
6214 for (const auto &imd : *mn)
6215 {
6216 MemberDefMutable *md = toMemberDefMutable(imd.get());
6217 if (md==nullptr) continue;
6219 if (cd==nullptr) continue;
6220 //AUTO_TRACE_ADD("member definition found, scope needed='{}' scope='{}' args='{}' fileName='{}'",
6221 // scopeName, cd->name(), md->argsString(), root->fileName);
6222 FileDef *fd=root->fileDef();
6223 NamespaceDef *nd=nullptr;
6224 if (!namespaceName.isEmpty()) nd=getResolvedNamespace(namespaceName);
6225
6226 //printf("scopeName %s->%s\n",qPrint(scopeName),
6227 // qPrint(stripTemplateSpecifiersFromScope(scopeName,FALSE)));
6228
6229 // if the member we are searching for is an enum value that is part of
6230 // a "strong" enum, we need to look into the fields of the enum for a match
6231 int enumNamePos=0;
6232 if (md->isEnumValue() && (enumNamePos=className.findRev("::"))!=-1)
6233 {
6234 QCString enumName = className.mid(enumNamePos+2);
6235 QCString fullScope = className.left(enumNamePos);
6236 if (!namespaceName.isEmpty()) fullScope.prepend(namespaceName+"::");
6237 if (fullScope==cd->name())
6238 {
6239 MemberName *enumMn=Doxygen::memberNameLinkedMap->find(enumName);
6240 //printf("enumMn(%s)=%p\n",qPrint(className),(void*)enumMn);
6241 if (enumMn)
6242 {
6243 for (const auto &emd : *enumMn)
6244 {
6245 memFound = emd->isStrong() && md->getEnumScope()==emd.get();
6246 if (memFound)
6247 {
6248 addMemberDocs(root,md,funcDecl,nullptr,overloaded,spec);
6249 count++;
6250 }
6251 if (memFound) break;
6252 }
6253 }
6254 }
6255 }
6256 if (memFound) break;
6257
6258 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
6259 if (tcd==nullptr && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
6260 {
6261 // don't be fooled by anonymous scopes
6262 tcd=cd;
6263 }
6264 //printf("Looking for %s inside nd=%s result=%s cd=%s\n",
6265 // qPrint(scopeName),nd?qPrint(nd->name()):"<none>",tcd?qPrint(tcd->name()):"",qPrint(cd->name()));
6266
6267 if (cd && tcd==cd) // member's classes match
6268 {
6269 AUTO_TRACE_ADD("class definition '{}' found",cd->name());
6270
6271 // get the template parameter lists found at the member declaration
6272 ArgumentLists declTemplArgs = cd->getTemplateParameterLists();
6273 const ArgumentList &templAl = md->templateArguments();
6274 if (!templAl.empty())
6275 {
6276 declTemplArgs.push_back(templAl);
6277 }
6278
6279 // get the template parameter lists found at the member definition
6280 const ArgumentLists &defTemplArgs = root->tArgLists;
6281 //printf("defTemplArgs=%p\n",defTemplArgs);
6282
6283 // do we replace the decl argument lists with the def argument lists?
6284 bool substDone=false;
6285 ArgumentList argList;
6286
6287 /* substitute the occurrences of class template names in the
6288 * argument list before matching
6289 */
6290 const ArgumentList &mdAl = md->argumentList();
6291 if (declTemplArgs.size()>0 && declTemplArgs.size()==defTemplArgs.size())
6292 {
6293 /* the function definition has template arguments
6294 * and the class definition also has template arguments, so
6295 * we must substitute the template names of the class by that
6296 * of the function definition before matching.
6297 */
6298 substituteTemplatesInArgList(declTemplArgs,defTemplArgs,mdAl,argList);
6299
6300 substDone=TRUE;
6301 }
6302 else /* no template arguments, compare argument lists directly */
6303 {
6304 argList = mdAl;
6305 }
6306
6307 bool matching=
6308 md->isVariable() || md->isTypedef() || // needed for function pointers
6310 md->getClassDef(),md->getFileDef(),md->typeString(),&argList,
6311 cd,fd,root->type,&root->argList,
6312 TRUE,root->lang);
6313
6314 AUTO_TRACE_ADD("matching '{}'<=>'{}' className='{}' namespaceName='{}' result={}",
6315 argListToString(argList,TRUE),argListToString(root->argList,TRUE),className,namespaceName,matching);
6316
6317 if (md->getLanguage()==SrcLangExt::ObjC && md->isVariable() && root->section.isFunction())
6318 {
6319 matching = FALSE; // don't match methods and attributes with the same name
6320 }
6321
6322 // for template member we also need to check the return type
6323 if (!md->templateArguments().empty() && !root->tArgLists.empty())
6324 {
6325 QCString memType = md->typeString();
6326 memType.stripPrefix("static "); // see bug700696
6328 className+"::",""); // see bug700693 & bug732594
6330 className+"::",""); // see bug758900
6331 if (memType=="auto" && !argList.trailingReturnType().isEmpty())
6332 {
6333 memType = argList.trailingReturnType();
6334 memType.stripPrefix(" -> ");
6335 }
6336 if (funcType=="auto" && !root->argList.trailingReturnType().isEmpty())
6337 {
6338 funcType = root->argList.trailingReturnType();
6339 funcType.stripPrefix(" -> ");
6341 substDone=true;
6342 }
6343 AUTO_TRACE_ADD("Comparing return types '{}'<->'{}' #args {}<->{}",
6344 memType,funcType,md->templateArguments().size(),root->tArgLists.back().size());
6345 if (md->templateArguments().size()!=root->tArgLists.back().size() || memType!=funcType)
6346 {
6347 //printf(" ---> no matching\n");
6348 matching = FALSE;
6349 }
6350 }
6351 else if (defTemplArgs.size()>declTemplArgs.size())
6352 {
6353 AUTO_TRACE_ADD("Different number of template arguments {} vs {}",defTemplArgs.size(),declTemplArgs.size());
6354 // avoid matching a non-template function in a template class against a
6355 // template function with the same name and parameters, see issue #10184
6356 substDone = false;
6357 matching = false;
6358 }
6359 bool rootIsUserDoc = root->section.isMemberDoc();
6360 bool classIsTemplate = scopeIsTemplate(md->getClassDef());
6361 bool mdIsTemplate = md->templateArguments().hasParameters();
6362 bool classOrMdIsTemplate = mdIsTemplate || classIsTemplate;
6363 bool rootIsTemplate = !root->tArgLists.empty();
6364 //printf("classIsTemplate=%d mdIsTemplate=%d rootIsTemplate=%d\n",classIsTemplate,mdIsTemplate,rootIsTemplate);
6365 if (!rootIsUserDoc && // don't check out-of-line @fn references, see bug722457
6366 (mdIsTemplate || rootIsTemplate) && // either md or root is a template
6367 ((classOrMdIsTemplate && !rootIsTemplate) || (!classOrMdIsTemplate && rootIsTemplate))
6368 )
6369 {
6370 // Method with template return type does not match method without return type
6371 // even if the parameters are the same. See also bug709052
6372 AUTO_TRACE_ADD("Comparing return types: template v.s. non-template");
6373 matching = FALSE;
6374 }
6375
6376 AUTO_TRACE_ADD("Match results of matchArguments2='{}' substDone='{}'",matching,substDone);
6377
6378 if (substDone) // found a new argument list
6379 {
6380 if (matching) // replace member's argument list
6381 {
6383 md->moveArgumentList(std::make_unique<ArgumentList>(argList));
6384 }
6385 else // no match
6386 {
6387 if (!funcTempList.isEmpty() &&
6388 isSpecialization(declTemplArgs,defTemplArgs))
6389 {
6390 // check if we are dealing with a partial template
6391 // specialization. In this case we add it to the class
6392 // even though the member arguments do not match.
6393
6394 addMethodToClass(root,cd,type,md->name(),args,isFriend,
6395 md->protection(),md->isStatic(),md->virtualness(),spec,relates);
6396 return;
6397 }
6398 }
6399 }
6400 if (matching)
6401 {
6402 addMemberDocs(root,md,funcDecl,nullptr,overloaded,spec);
6403 count++;
6404 memFound=TRUE;
6405 }
6406 }
6407 else if (cd && cd!=tcd) // we did find a class with the same name as cd
6408 // but in a different namespace
6409 {
6410 noMatchCount++;
6411 }
6412
6413 if (memFound) break;
6414 }
6415 if (count==0 && root->parent() && root->parent()->section.isObjcImpl())
6416 {
6417 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
6418 return;
6419 }
6420 if (count==0 && !(isFriend && funcType=="class"))
6421 {
6422 int candidates=0;
6423 const ClassDef *ecd = nullptr, *ucd = nullptr;
6424 MemberDef *emd = nullptr, *umd = nullptr;
6425 //printf("Assume template class\n");
6426 for (const auto &md : *mn)
6427 {
6428 MemberDef *cmd=md.get();
6430 ClassDefMutable *ccd=cdmdm ? cdmdm->getClassDefMutable() : nullptr;
6431 //printf("ccd->name()==%s className=%s\n",qPrint(ccd->name()),qPrint(className));
6432 if (ccd!=nullptr && rightScopeMatch(ccd->name(),className))
6433 {
6434 const ArgumentList &templAl = md->templateArguments();
6435 if (!root->tArgLists.empty() && !templAl.empty() &&
6436 root->tArgLists.back().size()<=templAl.size())
6437 {
6438 AUTO_TRACE_ADD("add template specialization");
6439 addMethodToClass(root,ccd,type,md->name(),args,isFriend,
6440 root->protection,root->isStatic,root->virt,spec,relates);
6441 return;
6442 }
6443 if (argListToString(md->argumentList(),FALSE,FALSE) ==
6445 { // exact argument list match -> remember
6446 ucd = ecd = ccd;
6447 umd = emd = cmd;
6448 AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': exact match",
6449 className,ccd->name(),md->argsString());
6450 }
6451 else // arguments do not match, but member name and scope do -> remember
6452 {
6453 ucd = ccd;
6454 umd = cmd;
6455 AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': no match",
6456 className,ccd->name(),md->argsString());
6457 }
6458 candidates++;
6459 }
6460 }
6461 bool strictProtoMatching = Config_getBool(STRICT_PROTO_MATCHING);
6462 if (!strictProtoMatching)
6463 {
6464 if (candidates==1 && ucd && umd)
6465 {
6466 // we didn't find an actual match on argument lists, but there is only 1 member with this
6467 // name in the same scope, so that has to be the one.
6468 addMemberDocs(root,toMemberDefMutable(umd),funcDecl,nullptr,overloaded,spec);
6469 return;
6470 }
6471 else if (candidates>1 && ecd && emd)
6472 {
6473 // we didn't find a unique match using type resolution,
6474 // but one of the matches has the exact same signature so
6475 // we take that one.
6476 addMemberDocs(root,toMemberDefMutable(emd),funcDecl,nullptr,overloaded,spec);
6477 return;
6478 }
6479 }
6480
6481 QCString warnMsg = "no ";
6482 if (noMatchCount>1) warnMsg+="uniquely ";
6483 warnMsg+="matching class member found for \n";
6484
6485 for (const ArgumentList &al : root->tArgLists)
6486 {
6487 warnMsg+=" template ";
6488 warnMsg+=tempArgListToString(al,root->lang);
6489 warnMsg+='\n';
6490 }
6491
6492 QCString fullFuncDecl=funcDecl;
6493 if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
6494
6495 warnMsg+=" ";
6496 warnMsg+=fullFuncDecl;
6497
6498 if (candidates>0 || noMatchCount>=1)
6499 {
6500 warnMsg+="\nPossible candidates:";
6501
6502 NamespaceDef *nd=nullptr;
6503 if (!namespaceName.isEmpty()) nd=getResolvedNamespace(namespaceName);
6504 FileDef *fd=root->fileDef();
6505
6506 for (const auto &md : *mn)
6507 {
6508 const ClassDef *cd=md->getClassDef();
6509 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
6510 if (tcd==nullptr && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
6511 {
6512 // don't be fooled by anonymous scopes
6513 tcd=cd;
6514 }
6515 if (cd!=nullptr && (rightScopeMatch(cd->name(),className) || (cd!=tcd)))
6516 {
6517 warnMsg+='\n';
6518 const ArgumentList &templAl = md->templateArguments();
6519 warnMsg+=" '";
6520 if (templAl.hasParameters())
6521 {
6522 warnMsg+="template ";
6523 warnMsg+=tempArgListToString(templAl,root->lang);
6524 warnMsg+='\n';
6525 warnMsg+=" ";
6526 }
6527 if (!md->typeString().isEmpty())
6528 {
6529 warnMsg+=md->typeString();
6530 warnMsg+=' ';
6531 }
6533 if (!qScope.isEmpty())
6534 warnMsg+=qScope+"::"+md->name();
6535 warnMsg+=md->argsString();
6536 warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
6537 }
6538 }
6539 }
6540 warn(root->fileName,root->startLine,"{}",warnMsg);
6541 }
6542}
6543
6544//-------------------------------------------------------------------------------------------
6545
6546static void addMemberSpecialization(const Entry *root,
6547 MemberName *mn,
6548 ClassDefMutable *cd,
6549 const QCString &funcType,
6550 const QCString &funcName,
6551 const QCString &funcArgs,
6552 const QCString &funcDecl,
6553 const QCString &exceptions,
6554 TypeSpecifier spec
6555 )
6556{
6557 AUTO_TRACE("funcType={} funcName={} funcArgs={} funcDecl={} spec={}",funcType,funcName,funcArgs,funcDecl,spec);
6558 MemberDef *declMd=nullptr;
6559 for (const auto &md : *mn)
6560 {
6561 if (md->getClassDef()==cd)
6562 {
6563 // TODO: we should probably also check for matching arguments
6564 declMd = md.get();
6565 break;
6566 }
6567 }
6569 ArgumentList tArgList;
6570 // getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6571 auto md = createMemberDef(
6572 root->fileName,root->startLine,root->startColumn,
6573 funcType,funcName,funcArgs,exceptions,
6574 declMd ? declMd->protection() : root->protection,
6575 root->virt,root->isStatic,Relationship::Member,
6576 mtype,tArgList,root->argList,root->metaData);
6577 auto mmd = toMemberDefMutable(md.get());
6578 //printf("new specialized member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6579 mmd->setTagInfo(root->tagInfo());
6580 mmd->setLanguage(root->lang);
6581 mmd->setId(root->id);
6582 mmd->setMemberClass(cd);
6583 mmd->setTemplateSpecialization(TRUE);
6584 mmd->setTypeConstraints(root->typeConstr);
6585 mmd->setDefinition(funcDecl);
6587 mmd->addQualifiers(root->qualifiers);
6588 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6589 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6590 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6591 mmd->setDocsForDefinition(!root->proto);
6592 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6593 mmd->addSectionsToDefinition(root->anchors);
6594 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6595 FileDef *fd=root->fileDef();
6596 mmd->setBodyDef(fd);
6597 mmd->setMemberSpecifiers(spec);
6598 mmd->setVhdlSpecifiers(root->vhdlSpec);
6599 mmd->setMemberGroupId(root->mGrpId);
6600 cd->insertMember(md.get());
6601 mmd->setRefItems(root->sli);
6602 mmd->setRequirementReferences(root->rqli);
6603
6604 mn->push_back(std::move(md));
6605}
6606
6607//-------------------------------------------------------------------------------------------
6608
6609static void addOverloaded(const Entry *root,MemberName *mn,
6610 const QCString &funcType,const QCString &funcName,const QCString &funcArgs,
6611 const QCString &funcDecl,const QCString &exceptions,TypeSpecifier spec)
6612{
6613 // for unique overloaded member we allow the class to be
6614 // omitted, this is to be Qt compatible. Using this should
6615 // however be avoided, because it is error prone
6616 bool sameClass=false;
6617 if (mn->size()>0)
6618 {
6619 // check if all members with the same name are also in the same class
6620 sameClass = std::equal(mn->begin()+1,mn->end(),mn->begin(),
6621 [](const auto &md1,const auto &md2)
6622 { return md1->getClassDef()->name()==md2->getClassDef()->name(); });
6623 }
6624 if (sameClass)
6625 {
6626 MemberDefMutable *mdm = toMemberDefMutable(mn->front().get());
6627 ClassDefMutable *cd = mdm ? mdm->getClassDefMutable() : nullptr;
6628 if (cd==nullptr) return;
6629
6631 if (root->mtype==MethodTypes::Signal) mtype=MemberType::Signal;
6632 else if (root->mtype==MethodTypes::Slot) mtype=MemberType::Slot;
6633 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType::DCOP;
6634
6635 // new overloaded member function
6636 std::unique_ptr<ArgumentList> tArgList =
6637 getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6638 //printf("new related member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6639 auto md = createMemberDef(
6640 root->fileName,root->startLine,root->startColumn,
6641 funcType,funcName,funcArgs,exceptions,
6642 root->protection,root->virt,root->isStatic,Relationship::Related,
6643 mtype,tArgList ? *tArgList : ArgumentList(),root->argList,root->metaData);
6644 auto mmd = toMemberDefMutable(md.get());
6645 mmd->setTagInfo(root->tagInfo());
6646 mmd->setLanguage(root->lang);
6647 mmd->setId(root->id);
6648 mmd->setTypeConstraints(root->typeConstr);
6649 mmd->setMemberClass(cd);
6650 mmd->setDefinition(funcDecl);
6652 mmd->addQualifiers(root->qualifiers);
6654 doc+="<p>";
6655 doc+=root->doc;
6656 mmd->setDocumentation(doc,root->docFile,root->docLine);
6657 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6658 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6659 mmd->setDocsForDefinition(!root->proto);
6660 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6661 mmd->addSectionsToDefinition(root->anchors);
6662 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6663 FileDef *fd=root->fileDef();
6664 mmd->setBodyDef(fd);
6665 mmd->setMemberSpecifiers(spec);
6666 mmd->setVhdlSpecifiers(root->vhdlSpec);
6667 mmd->setMemberGroupId(root->mGrpId);
6668 cd->insertMember(md.get());
6669 cd->insertUsedFile(fd);
6670 mmd->setRefItems(root->sli);
6671 mmd->setRequirementReferences(root->rqli);
6672
6673 mn->push_back(std::move(md));
6674 }
6675}
6676
6677static void insertMemberAlias(Definition *outerScope,const MemberDef *md)
6678{
6679 if (outerScope && outerScope!=Doxygen::globalScope)
6680 {
6681 auto aliasMd = createMemberDefAlias(outerScope,md);
6682 if (outerScope->definitionType()==Definition::TypeClass)
6683 {
6684 ClassDefMutable *cdm = toClassDefMutable(outerScope);
6685 if (cdm)
6686 {
6687 cdm->insertMember(aliasMd.get());
6688 }
6689 }
6690 else if (outerScope->definitionType()==Definition::TypeNamespace)
6691 {
6692 NamespaceDefMutable *ndm = toNamespaceDefMutable(outerScope);
6693 if (ndm)
6694 {
6695 ndm->insertMember(aliasMd.get());
6696 }
6697 }
6698 else if (outerScope->definitionType()==Definition::TypeFile)
6699 {
6700 toFileDef(outerScope)->insertMember(aliasMd.get());
6701 }
6702 if (aliasMd)
6703 {
6704 Doxygen::functionNameLinkedMap->add(md->name())->push_back(std::move(aliasMd));
6705 }
6706 }
6707}
6708
6709//-------------------------------------------------------------------------------------------
6710
6711/*! This function tries to find a member (in a documented class/file/namespace)
6712 * that corresponds to the function/variable declaration given in \a funcDecl.
6713 *
6714 * The boolean \a overloaded is used to specify whether or not a standard
6715 * overload documentation line should be generated.
6716 *
6717 * The boolean \a isFunc is a hint that indicates that this is a function
6718 * instead of a variable or typedef.
6719 */
6720static void findMember(const Entry *root,
6721 const QCString &relates,
6722 const QCString &type,
6723 const QCString &args,
6724 QCString funcDecl,
6725 bool overloaded,
6726 bool isFunc
6727 )
6728{
6729 AUTO_TRACE("root='{}' funcDecl='{}' related='{}' overload={} isFunc={} mGrpId={} #tArgList={} spec={} lang={}",
6730 root->name, funcDecl, relates, overloaded, isFunc, root->mGrpId, root->tArgLists.size(),
6731 root->spec, root->lang);
6732
6733 QCString scopeName;
6734 QCString className;
6735 QCString namespaceName;
6736 QCString funcType;
6737 QCString funcName;
6738 QCString funcArgs;
6739 QCString funcTempList;
6740 QCString exceptions;
6741 QCString funcSpec;
6742 bool isRelated=false;
6743 bool isMemberOf=false;
6744 bool isFriend=false;
6745 bool done=false;
6746 TypeSpecifier spec = root->spec;
6747 while (!done)
6748 {
6749 done=true;
6750 if (funcDecl.stripPrefix("friend ")) // treat friends as related members
6751 {
6752 isFriend=true;
6753 done=false;
6754 }
6755 if (funcDecl.stripPrefix("inline "))
6756 {
6757 spec.setInline(true);
6758 done=false;
6759 }
6760 if (funcDecl.stripPrefix("explicit "))
6761 {
6762 spec.setExplicit(true);
6763 done=false;
6764 }
6765 if (funcDecl.stripPrefix("mutable "))
6766 {
6767 spec.setMutable(true);
6768 done=false;
6769 }
6770 if (funcDecl.stripPrefix("thread_local "))
6771 {
6772 spec.setThreadLocal(true);
6773 done=false;
6774 }
6775 if (funcDecl.stripPrefix("virtual "))
6776 {
6777 done=false;
6778 }
6779 }
6780
6781 // delete any ; from the function declaration
6782 int sep=0;
6783 while ((sep=funcDecl.find(';'))!=-1)
6784 {
6785 funcDecl=(funcDecl.left(sep)+funcDecl.right(funcDecl.length()-sep-1)).stripWhiteSpace();
6786 }
6787
6788 // make sure the first character is a space to simplify searching.
6789 if (!funcDecl.isEmpty() && funcDecl[0]!=' ') funcDecl.prepend(" ");
6790
6791 // remove some superfluous spaces
6792 funcDecl= substitute(
6793 substitute(
6794 substitute(funcDecl,"~ ","~"),
6795 ":: ","::"
6796 ),
6797 " ::","::"
6798 ).stripWhiteSpace();
6799
6800 //printf("funcDecl='%s'\n",qPrint(funcDecl));
6801 if (isFriend && funcDecl.startsWith("class "))
6802 {
6803 //printf("friend class\n");
6804 funcDecl=funcDecl.right(funcDecl.length()-6);
6805 funcName = funcDecl;
6806 }
6807 else if (isFriend && funcDecl.startsWith("struct "))
6808 {
6809 funcDecl=funcDecl.right(funcDecl.length()-7);
6810 funcName = funcDecl;
6811 }
6812 else
6813 {
6814 // extract information from the declarations
6815 parseFuncDecl(funcDecl,root->lang,scopeName,funcType,funcName,
6816 funcArgs,funcTempList,exceptions
6817 );
6818 }
6819
6820 // the class name can also be a namespace name, we decide this later.
6821 // if a related class name is specified and the class name could
6822 // not be derived from the function declaration, then use the
6823 // related field.
6824 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}' funcType='{}' funcName='{}' funcArgs='{}'",
6825 scopeName,className,namespaceName,funcType,funcName,funcArgs);
6826 if (!relates.isEmpty())
6827 { // related member, prefix user specified scope
6828 isRelated=TRUE;
6829 isMemberOf=(root->relatesType == RelatesType::MemberOf);
6830 if (getClass(relates)==nullptr && !scopeName.isEmpty())
6831 {
6832 scopeName= mergeScopes(scopeName,relates);
6833 }
6834 else
6835 {
6836 scopeName = relates;
6837 }
6838 }
6839
6840 if (relates.isEmpty() && root->parent() &&
6841 (root->parent()->section.isScope() || root->parent()->section.isObjcImpl()) &&
6842 !root->parent()->name.isEmpty()) // see if we can combine scopeName
6843 // with the scope in which it was found
6844 {
6845 QCString joinedName = root->parent()->name+"::"+scopeName;
6846 if (!scopeName.isEmpty() &&
6847 (getClass(joinedName) || Doxygen::namespaceLinkedMap->find(joinedName)))
6848 {
6849 scopeName = joinedName;
6850 }
6851 else
6852 {
6853 scopeName = mergeScopes(root->parent()->name,scopeName);
6854 }
6855 }
6856 else // see if we can prefix a namespace or class that is used from the file
6857 {
6858 FileDef *fd=root->fileDef();
6859 if (fd)
6860 {
6861 for (const auto &fnd : fd->getUsedNamespaces())
6862 {
6863 QCString joinedName = fnd->name()+"::"+scopeName;
6864 if (Doxygen::namespaceLinkedMap->find(joinedName))
6865 {
6866 scopeName=joinedName;
6867 break;
6868 }
6869 }
6870 }
6871 }
6873 removeRedundantWhiteSpace(scopeName),false,&funcSpec,QCString(),false);
6874
6875 // funcSpec contains the last template specifiers of the given scope.
6876 // If this method does not have any template arguments or they are
6877 // empty while funcSpec is not empty we assume this is a
6878 // specialization of a method. If not, we clear the funcSpec and treat
6879 // this as a normal method of a template class.
6880 if (!(root->tArgLists.size()>0 &&
6881 root->tArgLists.front().size()==0
6882 )
6883 )
6884 {
6885 funcSpec.clear();
6886 }
6887
6888 //namespaceName=removeAnonymousScopes(namespaceName);
6889 if (!Config_getBool(EXTRACT_ANON_NSPACES) && scopeName.find('@')!=-1) return; // skip stuff in anonymous namespace...
6890
6891 // split scope into a namespace and a class part
6892 extractNamespaceName(scopeName,className,namespaceName,TRUE);
6893 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}'",scopeName,className,namespaceName);
6894
6895 //printf("namespaceName='%s' className='%s'\n",qPrint(namespaceName),qPrint(className));
6896 // merge class and namespace scopes again
6897 scopeName.clear();
6898 if (!namespaceName.isEmpty())
6899 {
6900 if (className.isEmpty())
6901 {
6902 scopeName=namespaceName;
6903 }
6904 else if (!relates.isEmpty() || // relates command with explicit scope
6905 !getClass(className)) // class name only exists in a namespace
6906 {
6907 scopeName=namespaceName+"::"+className;
6908 }
6909 else
6910 {
6911 scopeName=className;
6912 }
6913 }
6914 else if (!className.isEmpty())
6915 {
6916 scopeName=className;
6917 }
6918 //printf("new scope='%s'\n",qPrint(scopeName));
6919
6920 QCString tempScopeName=scopeName;
6921 ClassDefMutable *cd=getClassMutable(scopeName);
6922 if (cd)
6923 {
6924 if (funcSpec.isEmpty())
6925 {
6926 uint32_t argListIndex=0;
6927 tempScopeName=cd->qualifiedNameWithTemplateParameters(&root->tArgLists,&argListIndex);
6928 }
6929 else
6930 {
6931 tempScopeName=scopeName+funcSpec;
6932 }
6933 }
6934 //printf("scopeName=%s cd=%p root->tArgLists=%p result=%s\n",
6935 // qPrint(scopeName),cd,root->tArgLists,qPrint(tempScopeName));
6936
6937 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
6938 // rebuild the function declaration (needed to get the scope right).
6939 if (!scopeName.isEmpty() && !isRelated && !isFriend && !Config_getBool(HIDE_SCOPE_NAMES))
6940 {
6941 if (!funcType.isEmpty())
6942 {
6943 if (isFunc) // a function -> we use argList for the arguments
6944 {
6945 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcTempList;
6946 }
6947 else
6948 {
6949 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcArgs;
6950 }
6951 }
6952 else
6953 {
6954 if (isFunc) // a function => we use argList for the arguments
6955 {
6956 funcDecl=tempScopeName+"::"+funcName+funcTempList;
6957 }
6958 else // variable => add 'argument' list
6959 {
6960 funcDecl=tempScopeName+"::"+funcName+funcArgs;
6961 }
6962 }
6963 }
6964 else // build declaration without scope
6965 {
6966 if (!funcType.isEmpty()) // but with a type
6967 {
6968 if (isFunc) // function => omit argument list
6969 {
6970 funcDecl=funcType+" "+funcName+funcTempList;
6971 }
6972 else // variable => add 'argument' list
6973 {
6974 funcDecl=funcType+" "+funcName+funcArgs;
6975 }
6976 }
6977 else // no type
6978 {
6979 if (isFunc)
6980 {
6981 funcDecl=funcName+funcTempList;
6982 }
6983 else
6984 {
6985 funcDecl=funcName+funcArgs;
6986 }
6987 }
6988 }
6989
6990 if (funcType=="template class" && !funcTempList.isEmpty())
6991 return; // ignore explicit template instantiations
6992
6993 AUTO_TRACE_ADD("Parse results: namespaceName='{}' className=`{}` funcType='{}' funcSpec='{}' "
6994 " funcName='{}' funcArgs='{}' funcTempList='{}' funcDecl='{}' relates='{}'"
6995 " exceptions='{}' isRelated={} isMemberOf={} isFriend={} isFunc={}",
6996 namespaceName, className, funcType, funcSpec,
6997 funcName, funcArgs, funcTempList, funcDecl, relates,
6998 exceptions, isRelated, isMemberOf, isFriend, isFunc);
6999
7000 if (!funcName.isEmpty()) // function name is valid
7001 {
7002 // check if 'className' is actually a scoped enum, in which case we need to
7003 // process it as a global, see issue #6471
7004 bool strongEnum = false;
7005 MemberName *mn=nullptr;
7006 if (!className.isEmpty() && (mn=Doxygen::functionNameLinkedMap->find(className)))
7007 {
7008 for (const auto &imd : *mn)
7009 {
7010 MemberDefMutable *md = toMemberDefMutable(imd.get());
7011 Definition *mdScope = nullptr;
7012 if (md && md->isEnumerate() && md->isStrong() && (mdScope=md->getOuterScope()) &&
7013 // need filter for the correct scope, see issue #9668
7014 ((namespaceName.isEmpty() && mdScope==Doxygen::globalScope) || (mdScope->name()==namespaceName)))
7015 {
7016 AUTO_TRACE_ADD("'{}' is a strong enum! (namespace={} md->getOuterScope()->name()={})",md->name(),namespaceName,md->getOuterScope()->name());
7017 strongEnum = true;
7018 // pass the scope name name as a 'namespace' to the findGlobalMember function
7019 if (!namespaceName.isEmpty())
7020 {
7021 namespaceName+="::"+className;
7022 }
7023 else
7024 {
7025 namespaceName=className;
7026 }
7027 }
7028 }
7029 }
7030
7031 if (funcName.startsWith("operator ")) // strip class scope from cast operator
7032 {
7033 funcName = substitute(funcName,className+"::","");
7034 }
7035 mn = nullptr;
7036 if (!funcTempList.isEmpty()) // try with member specialization
7037 {
7038 mn=Doxygen::memberNameLinkedMap->find(funcName+funcTempList);
7039 }
7040 if (mn==nullptr) // try without specialization
7041 {
7042 mn=Doxygen::memberNameLinkedMap->find(funcName);
7043 }
7044 if (!isRelated && !strongEnum && mn) // function name already found
7045 {
7046 AUTO_TRACE_ADD("member name exists ({} members with this name)",mn->size());
7047 if (!className.isEmpty()) // class name is valid
7048 {
7049 if (funcSpec.isEmpty()) // not a member specialization
7050 {
7051 addMemberFunction(root,mn,scopeName,namespaceName,className,funcType,funcName,
7052 funcArgs,funcTempList,exceptions,
7053 type,args,isFriend,spec,relates,funcDecl,overloaded,isFunc);
7054 }
7055 else if (cd) // member specialization
7056 {
7057 addMemberSpecialization(root,mn,cd,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
7058 }
7059 else
7060 {
7061 //printf("*** Specialized member %s of unknown scope %s%s found!\n",
7062 // qPrint(scopeName),qPrint(funcName),qPrint(funcArgs));
7063 }
7064 }
7065 else if (overloaded) // check if the function belongs to only one class
7066 {
7067 addOverloaded(root,mn,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
7068 }
7069 else // unrelated function with the same name as a member
7070 {
7071 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
7072 {
7073 QCString fullFuncDecl=funcDecl;
7074 if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
7075 warn(root->fileName,root->startLine,
7076 "Cannot determine class for function\n{}",
7077 fullFuncDecl
7078 );
7079 }
7080 }
7081 }
7082 else if (isRelated && !relates.isEmpty())
7083 {
7084 AUTO_TRACE_ADD("related function scopeName='{}' className='{}'",scopeName,className);
7085 if (className.isEmpty()) className=relates;
7086 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
7087 if ((cd=getClassMutable(scopeName)))
7088 {
7089 bool newMember=TRUE; // assume we have a new member
7090 MemberDefMutable *mdDefine=nullptr;
7091 {
7092 mn = Doxygen::functionNameLinkedMap->find(funcName);
7093 if (mn)
7094 {
7095 for (const auto &imd : *mn)
7096 {
7097 MemberDefMutable *md = toMemberDefMutable(imd.get());
7098 if (md && md->isDefine())
7099 {
7100 mdDefine = md;
7101 break;
7102 }
7103 }
7104 }
7105 }
7106
7107 if (mdDefine) // macro definition is already created by the preprocessor and inserted as a file member
7108 {
7109 //printf("moving #define %s into class %s\n",qPrint(mdDefine->name()),qPrint(cd->name()));
7110
7111 // take mdDefine from the Doxygen::functionNameLinkedMap (without deleting the data)
7112 auto mdDefineTaken = Doxygen::functionNameLinkedMap->take(funcName,mdDefine);
7113 // insert it as a class member
7114 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==nullptr)
7115 {
7116 mn=Doxygen::memberNameLinkedMap->add(funcName);
7117 }
7118
7119 if (mdDefine->getFileDef())
7120 {
7121 mdDefine->getFileDef()->removeMember(mdDefine);
7122 }
7123 mdDefine->makeRelated();
7124 mdDefine->setMemberClass(cd);
7125 mdDefine->moveTo(cd);
7126 cd->insertMember(mdDefine);
7127 // also insert the member as an alias in the parent's scope, so it can be referenced also without cd's scope
7128 insertMemberAlias(cd->getOuterScope(),mdDefine);
7129 mn->push_back(std::move(mdDefineTaken));
7130 }
7131 else // normal member, needs to be created and added to the class
7132 {
7133 FileDef *fd=root->fileDef();
7134
7135 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==nullptr)
7136 {
7137 mn=Doxygen::memberNameLinkedMap->add(funcName);
7138 }
7139 else
7140 {
7141 // see if we got another member with matching arguments
7142 MemberDefMutable *rmd_found = nullptr;
7143 for (const auto &irmd : *mn)
7144 {
7145 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
7146 if (rmd)
7147 {
7148 const ArgumentList &rmdAl = rmd->argumentList();
7149
7150 newMember=
7151 className!=rmd->getOuterScope()->name() ||
7152 !matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmdAl,
7153 cd,fd,root->type,&root->argList,
7154 TRUE,root->lang);
7155 if (!newMember)
7156 {
7157 rmd_found = rmd;
7158 }
7159 }
7160 }
7161 if (rmd_found) // member already exists as rmd -> add docs
7162 {
7163 AUTO_TRACE_ADD("addMemberDocs for related member {}",root->name);
7164 addMemberDocs(root,rmd_found,funcDecl,nullptr,overloaded,spec);
7165 newMember=false;
7166 }
7167 }
7168
7169 if (newMember) // need to create a new member
7170 {
7172 switch (root->mtype)
7173 {
7174 case MethodTypes::Method: mtype = MemberType::Function; break;
7175 case MethodTypes::Signal: mtype = MemberType::Signal; break;
7176 case MethodTypes::Slot: mtype = MemberType::Slot; break;
7177 case MethodTypes::DCOP: mtype = MemberType::DCOP; break;
7178 case MethodTypes::Property: mtype = MemberType::Property; break;
7179 case MethodTypes::Event: mtype = MemberType::Event; break;
7180 }
7181
7182 //printf("New related name '%s' '%d'\n",qPrint(funcName),
7183 // root->argList ? (int)root->argList->count() : -1);
7184
7185 // first note that we pass:
7186 // (root->tArgLists ? root->tArgLists->last() : nullptr)
7187 // for the template arguments for the new "member."
7188 // this accurately reflects the template arguments of
7189 // the related function, which don't have to do with
7190 // those of the related class.
7191 auto md = createMemberDef(
7192 root->fileName,root->startLine,root->startColumn,
7193 funcType,funcName,funcArgs,exceptions,
7194 root->protection,root->virt,
7195 root->isStatic,
7196 isMemberOf ? Relationship::Foreign : Relationship::Related,
7197 mtype,
7198 (!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList()),
7199 funcArgs.isEmpty() ? ArgumentList() : root->argList,
7200 root->metaData);
7201 auto mmd = toMemberDefMutable(md.get());
7202
7203 // also insert the member as an alias in the parent's scope, so it can be referenced also without cd's scope
7204 insertMemberAlias(cd->getOuterScope(),md.get());
7205
7206 // we still have the problem that
7207 // MemberDef::writeDocumentation() in memberdef.cpp
7208 // writes the template argument list for the class,
7209 // as if this member is a member of the class.
7210 // fortunately, MemberDef::writeDocumentation() has
7211 // a special mechanism that allows us to totally
7212 // override the set of template argument lists that
7213 // are printed. We use that and set it to the
7214 // template argument lists of the related function.
7215 //
7216 mmd->setDefinitionTemplateParameterLists(root->tArgLists);
7217
7218 mmd->setTagInfo(root->tagInfo());
7219
7220 //printf("Related member name='%s' decl='%s' bodyLine='%d'\n",
7221 // qPrint(funcName),qPrint(funcDecl),root->bodyLine);
7222
7223 // try to find the matching line number of the body from the
7224 // global function list
7225 bool found=FALSE;
7226 if (root->bodyLine==-1)
7227 {
7228 MemberName *rmn=Doxygen::functionNameLinkedMap->find(funcName);
7229 if (rmn)
7230 {
7231 const MemberDefMutable *rmd_found=nullptr;
7232 for (const auto &irmd : *rmn)
7233 {
7234 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
7235 if (rmd)
7236 {
7237 const ArgumentList &rmdAl = rmd->argumentList();
7238 // check for matching argument lists
7239 if (
7240 matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),rmd->typeString(),&rmdAl,
7241 cd,fd,root->type,&root->argList,
7242 TRUE,root->lang)
7243 )
7244 {
7245 found=TRUE;
7246 rmd_found = rmd;
7247 break;
7248 }
7249 }
7250 }
7251 if (rmd_found) // member found -> copy line number info
7252 {
7253 mmd->setBodySegment(rmd_found->getDefLine(),rmd_found->getStartBodyLine(),rmd_found->getEndBodyLine());
7254 mmd->setBodyDef(rmd_found->getBodyDef());
7255 //md->setBodyMember(rmd);
7256 }
7257 }
7258 }
7259 if (!found) // line number could not be found or is available in this
7260 // entry
7261 {
7262 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7263 mmd->setBodyDef(fd);
7264 }
7265
7266 //if (root->mGrpId!=-1)
7267 //{
7268 // md->setMemberGroup(memberGroupDict[root->mGrpId]);
7269 //}
7270 mmd->setMemberClass(cd);
7271 mmd->setMemberSpecifiers(spec);
7272 mmd->setVhdlSpecifiers(root->vhdlSpec);
7273 mmd->setDefinition(funcDecl);
7275 mmd->addQualifiers(root->qualifiers);
7276 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7277 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7278 mmd->setDocsForDefinition(!root->proto);
7279 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
7280 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7281 mmd->addSectionsToDefinition(root->anchors);
7282 mmd->setMemberGroupId(root->mGrpId);
7283 mmd->setLanguage(root->lang);
7284 mmd->setId(root->id);
7285 //md->setMemberDefTemplateArguments(root->mtArgList);
7286 cd->insertMember(md.get());
7287 cd->insertUsedFile(fd);
7288 mmd->setRefItems(root->sli);
7289 mmd->setRequirementReferences(root->rqli);
7290 if (root->relatesType==RelatesType::Duplicate) mmd->setRelatedAlso(cd);
7291 addMemberToGroups(root,md.get());
7293 //printf("Adding member=%s\n",qPrint(md->name()));
7294 mn->push_back(std::move(md));
7295 }
7296 if (root->relatesType==RelatesType::Duplicate)
7297 {
7298 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
7299 {
7300 QCString fullFuncDecl=funcDecl;
7301 if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
7302 warn(root->fileName,root->startLine,
7303 "Cannot determine file/namespace for relatedalso function\n{}",
7304 fullFuncDecl
7305 );
7306 }
7307 }
7308 }
7309 }
7310 else
7311 {
7312 warn_undoc(root->fileName,root->startLine, "class '{}' for related function '{}' is not documented.", className,funcName);
7313 }
7314 }
7315 else if (root->parent() && root->parent()->section.isObjcImpl())
7316 {
7317 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
7318 }
7319 else // unrelated not overloaded member found
7320 {
7321 bool globMem = findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec);
7322 if (className.isEmpty() && !globMem)
7323 {
7324 warn(root->fileName,root->startLine, "class for member '{}' cannot be found.", funcName);
7325 }
7326 else if (!className.isEmpty() && !globMem)
7327 {
7328 warn(root->fileName,root->startLine,
7329 "member '{}' of class '{}' cannot be found",
7330 funcName,className);
7331 }
7332 }
7333 }
7334 else
7335 {
7336 // this should not be called
7337 warn(root->fileName,root->startLine,"member with no name found.");
7338 }
7339 return;
7340}
7341
7342//----------------------------------------------------------------------
7343// find the members corresponding to the different documentation blocks
7344// that are extracted from the sources.
7345
7346static void filterMemberDocumentation(const Entry *root,const QCString &relates)
7347{
7348 AUTO_TRACE("root->type='{}' root->inside='{}' root->name='{}' root->args='{}' section={} root->spec={} root->mGrpId={}",
7349 root->type,root->inside,root->name,root->args,root->section,root->spec,root->mGrpId);
7350 //printf("root->parent()->name=%s\n",qPrint(root->parent()->name));
7351 bool isFunc=TRUE;
7352
7353 QCString type = root->type;
7354 QCString args = root->args;
7355 int i=-1, l=0;
7356 if ( // detect func variable/typedef to func ptr
7357 (i=findFunctionPtr(type.str(),root->lang,&l))!=-1
7358 )
7359 {
7360 //printf("Fixing function pointer!\n");
7361 // fix type and argument
7362 args.prepend(type.right(type.length()-i-l));
7363 type=type.left(i+l);
7364 //printf("Results type=%s,name=%s,args=%s\n",qPrint(type),qPrint(root->name),qPrint(args));
7365 isFunc=FALSE;
7366 }
7367 else if ((type.startsWith("typedef ") && args.find('(')!=-1))
7368 // detect function types marked as functions
7369 {
7370 isFunc=FALSE;
7371 }
7372
7373 //printf("Member %s isFunc=%d\n",qPrint(root->name),isFunc);
7374 if (root->section.isMemberDoc())
7375 {
7376 //printf("Documentation for inline member '%s' found args='%s'\n",
7377 // qPrint(root->name),qPrint(args));
7378 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
7379 if (type.isEmpty())
7380 {
7381 findMember(root,
7382 relates,
7383 type,
7384 args,
7385 root->name + args + root->exception,
7386 FALSE,
7387 isFunc);
7388 }
7389 else
7390 {
7391 findMember(root,
7392 relates,
7393 type,
7394 args,
7395 type + " " + root->name + args + root->exception,
7396 FALSE,
7397 isFunc);
7398 }
7399 }
7400 else if (root->section.isOverloadDoc())
7401 {
7402 //printf("Overloaded member %s found\n",qPrint(root->name));
7403 findMember(root,
7404 relates,
7405 type,
7406 args,
7407 root->name,
7408 TRUE,
7409 isFunc);
7410 }
7411 else if
7412 ((root->section.isFunction() // function
7413 ||
7414 (root->section.isVariable() && // variable
7415 !type.isEmpty() && // with a type
7416 g_compoundKeywords.find(type.str())==g_compoundKeywords.end() // that is not a keyword
7417 // (to skip forward declaration of class etc.)
7418 )
7419 )
7420 )
7421 {
7422 //printf("Documentation for member '%s' found args='%s' excp='%s'\n",
7423 // qPrint(root->name),qPrint(args),qPrint(root->exception));
7424 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
7425 //printf("Inside=%s\n Relates=%s\n",qPrint(root->inside),qPrint(relates));
7426 if (type=="friend class" || type=="friend struct" ||
7427 type=="friend union")
7428 {
7429 findMember(root,
7430 relates,
7431 type,
7432 args,
7433 type+" "+root->name,
7434 FALSE,FALSE);
7435
7436 }
7437 else if (!type.isEmpty())
7438 {
7439 findMember(root,
7440 relates,
7441 type,
7442 args,
7443 type+" "+ root->inside + root->name + args + root->exception,
7444 FALSE,isFunc);
7445 }
7446 else
7447 {
7448 findMember(root,
7449 relates,
7450 type,
7451 args,
7452 root->inside + root->name + args + root->exception,
7453 FALSE,isFunc);
7454 }
7455 }
7456 else if (root->section.isDefine() && !relates.isEmpty())
7457 {
7458 findMember(root,
7459 relates,
7460 type,
7461 args,
7462 root->name + args,
7463 FALSE,
7464 !args.isEmpty());
7465 }
7466 else if (root->section.isVariableDoc())
7467 {
7468 //printf("Documentation for variable %s found\n",qPrint(root->name));
7469 //if (!relates.isEmpty()) printf(" Relates %s\n",qPrint(relates));
7470 findMember(root,
7471 relates,
7472 type,
7473 args,
7474 root->name,
7475 FALSE,
7476 FALSE);
7477 }
7478 else if (root->section.isExportedInterface() ||
7479 root->section.isIncludedService())
7480 {
7481 findMember(root,
7482 relates,
7483 type,
7484 args,
7485 type + " " + root->name,
7486 FALSE,
7487 FALSE);
7488 }
7489 else
7490 {
7491 // skip section
7492 //printf("skip section\n");
7493 }
7494}
7495
7496static void findMemberDocumentation(const Entry *root)
7497{
7498 if (root->section.isMemberDoc() ||
7499 root->section.isOverloadDoc() ||
7500 root->section.isFunction() ||
7501 root->section.isVariable() ||
7502 root->section.isVariableDoc() ||
7503 root->section.isDefine() ||
7504 root->section.isIncludedService() ||
7505 root->section.isExportedInterface()
7506 )
7507 {
7508 AUTO_TRACE();
7509 if (root->relatesType==RelatesType::Duplicate && !root->relates.isEmpty())
7510 {
7512 }
7514 }
7515 for (const auto &e : root->children())
7516 {
7517 if (!e->section.isEnum())
7518 {
7519 findMemberDocumentation(e.get());
7520 }
7521 }
7522}
7523
7524//----------------------------------------------------------------------
7525
7526static void findObjCMethodDefinitions(const Entry *root)
7527{
7528 AUTO_TRACE();
7529 for (const auto &objCImpl : root->children())
7530 {
7531 if (objCImpl->section.isObjcImpl())
7532 {
7533 for (const auto &objCMethod : objCImpl->children())
7534 {
7535 if (objCMethod->section.isFunction())
7536 {
7537 //printf(" Found ObjC method definition %s\n",qPrint(objCMethod->name));
7538 findMember(objCMethod.get(),
7539 objCMethod->relates,
7540 objCMethod->type,
7541 objCMethod->args,
7542 objCMethod->type+" "+objCImpl->name+"::"+objCMethod->name+" "+objCMethod->args,
7543 FALSE,TRUE);
7544 objCMethod->section=EntryType::makeEmpty();
7545 }
7546 }
7547 }
7548 }
7549}
7550
7551//----------------------------------------------------------------------
7552// find and add the enumeration to their classes, namespaces or files
7553
7554static void findEnums(const Entry *root)
7555{
7556 if (root->section.isEnum())
7557 {
7558 AUTO_TRACE("name={}",root->name);
7559 ClassDefMutable *cd = nullptr;
7560 FileDef *fd = nullptr;
7561 NamespaceDefMutable *nd = nullptr;
7562 MemberNameLinkedMap *mnsd = nullptr;
7563 bool isGlobal = false;
7564 bool isRelated = false;
7565 bool isMemberOf = false;
7566 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7567
7568 QCString name;
7569 QCString scope;
7570
7571 int i = root->name.findRev("::");
7572 if (i!=-1) // scope is specified
7573 {
7574 scope=root->name.left(i); // extract scope
7575 if (root->lang==SrcLangExt::CSharp)
7576 {
7577 scope = mangleCSharpGenericName(scope);
7578 }
7579 name=root->name.right(root->name.length()-i-2); // extract name
7580 if ((cd=getClassMutable(scope))==nullptr)
7581 {
7583 }
7584 }
7585 else // no scope, check the scope in which the docs where found
7586 {
7587 if (root->parent()->section.isScope() && !root->parent()->name.isEmpty()) // found enum docs inside a compound
7588 {
7589 scope=root->parent()->name;
7590 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7591 }
7592 name=root->name;
7593 }
7594
7595 if (!root->relates.isEmpty())
7596 { // related member, prefix user specified scope
7597 isRelated=TRUE;
7598 isMemberOf=(root->relatesType==RelatesType::MemberOf);
7599 if (getClass(root->relates)==nullptr && !scope.isEmpty())
7600 scope=mergeScopes(scope,root->relates);
7601 else
7602 scope=root->relates;
7603 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7604 }
7605
7606 if (cd && !name.isEmpty()) // found a enum inside a compound
7607 {
7608 //printf("Enum '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7609 fd=nullptr;
7611 isGlobal=false;
7612 }
7613 else if (nd) // found enum inside namespace
7614 {
7616 isGlobal=true;
7617 }
7618 else // found a global enum
7619 {
7620 fd=root->fileDef();
7622 isGlobal=true;
7623 }
7624
7625 if (!name.isEmpty())
7626 {
7627 // new enum type
7628 AUTO_TRACE_ADD("new enum {} at line {} of {}",name,root->bodyLine,root->fileName);
7629 auto md = createMemberDef(
7630 root->fileName,root->startLine,root->startColumn,
7631 QCString(),name,QCString(),QCString(),
7632 root->protection,Specifier::Normal,FALSE,
7633 isMemberOf ? Relationship::Foreign : isRelated ? Relationship::Related : Relationship::Member,
7636 auto mmd = toMemberDefMutable(md.get());
7637 mmd->setTagInfo(root->tagInfo());
7638 mmd->setLanguage(root->lang);
7639 mmd->setId(root->id);
7640 if (!isGlobal) mmd->setMemberClass(cd); else mmd->setFileDef(fd);
7641 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7642 mmd->setBodyDef(root->fileDef());
7643 mmd->setMemberSpecifiers(root->spec);
7644 mmd->setVhdlSpecifiers(root->vhdlSpec);
7645 mmd->setEnumBaseType(root->args);
7646 //printf("Enum %s definition at line %d of %s: protection=%d scope=%s\n",
7647 // qPrint(root->name),root->bodyLine,qPrint(root->fileName),root->protection,cd?qPrint(cd->name()):"<none>");
7648 mmd->addSectionsToDefinition(root->anchors);
7649 mmd->setMemberGroupId(root->mGrpId);
7651 mmd->addQualifiers(root->qualifiers);
7652 //printf("%s::setRefItems(%zu)\n",qPrint(md->name()),root->sli.size());
7653 mmd->setRefItems(root->sli);
7654 mmd->setRequirementReferences(root->rqli);
7655 //printf("found enum %s nd=%p\n",qPrint(md->name()),nd);
7656 bool defSet=FALSE;
7657
7658 QCString baseType = root->args;
7659 if (!baseType.isEmpty())
7660 {
7661 baseType.prepend(" : ");
7662 }
7663
7664 if (nd)
7665 {
7666 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES))
7667 {
7668 mmd->setDefinition(name+baseType);
7669 }
7670 else
7671 {
7672 mmd->setDefinition(nd->name()+"::"+name+baseType);
7673 }
7674 //printf("definition=%s\n",md->definition());
7675 defSet=TRUE;
7676 mmd->setNamespace(nd);
7677 nd->insertMember(md.get());
7678 }
7679
7680 // even if we have already added the enum to a namespace, we still
7681 // also want to add it to other appropriate places such as file
7682 // or class.
7683 if (isGlobal && (nd==nullptr || !nd->isAnonymous()))
7684 {
7685 if (!defSet) mmd->setDefinition(name+baseType);
7686 if (fd==nullptr && root->parent())
7687 {
7688 fd=root->parent()->fileDef();
7689 }
7690 if (fd)
7691 {
7692 mmd->setFileDef(fd);
7693 fd->insertMember(md.get());
7694 }
7695 }
7696 else if (cd)
7697 {
7698 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES))
7699 {
7700 mmd->setDefinition(name+baseType);
7701 }
7702 else
7703 {
7704 mmd->setDefinition(cd->name()+"::"+name+baseType);
7705 }
7706 cd->insertMember(md.get());
7707 cd->insertUsedFile(fd);
7708 }
7709 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7710 mmd->setDocsForDefinition(!root->proto);
7711 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7712 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7713
7714 //printf("Adding member=%s\n",qPrint(md->name()));
7715 addMemberToGroups(root,md.get());
7717
7718 MemberName *mn = mnsd->add(name);
7719 mn->push_back(std::move(md));
7720 }
7721 }
7722 else
7723 {
7724 for (const auto &e : root->children()) findEnums(e.get());
7725 }
7726}
7727
7728//----------------------------------------------------------------------
7729
7730static void addEnumValuesToEnums(const Entry *root)
7731{
7732 if (root->section.isEnum())
7733 // non anonymous enumeration
7734 {
7735 AUTO_TRACE("name={}",root->name);
7736 ClassDefMutable *cd = nullptr;
7737 FileDef *fd = nullptr;
7738 NamespaceDefMutable *nd = nullptr;
7739 MemberNameLinkedMap *mnsd = nullptr;
7740 bool isGlobal = false;
7741 bool isRelated = false;
7742 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7743
7744 QCString name;
7745 QCString scope;
7746
7747 int i = root->name.findRev("::");
7748 if (i!=-1) // scope is specified
7749 {
7750 scope=root->name.left(i); // extract scope
7751 if (root->lang==SrcLangExt::CSharp)
7752 {
7753 scope = mangleCSharpGenericName(scope);
7754 }
7755 name=root->name.right(root->name.length()-i-2); // extract name
7756 if ((cd=getClassMutable(scope))==nullptr)
7757 {
7759 }
7760 }
7761 else // no scope, check the scope in which the docs where found
7762 {
7763 if (root->parent()->section.isScope() && !root->parent()->name.isEmpty()) // found enum docs inside a compound
7764 {
7765 scope=root->parent()->name;
7766 if (root->lang==SrcLangExt::CSharp)
7767 {
7768 scope = mangleCSharpGenericName(scope);
7769 }
7770 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7771 }
7772 name=root->name;
7773 }
7774
7775 if (!root->relates.isEmpty())
7776 { // related member, prefix user specified scope
7777 isRelated=TRUE;
7778 if (getClassMutable(root->relates)==nullptr && !scope.isEmpty())
7779 scope=mergeScopes(scope,root->relates);
7780 else
7781 scope=root->relates;
7782 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7783 }
7784
7785 if (cd && !name.isEmpty()) // found a enum inside a compound
7786 {
7787 //printf("Enum in class '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7788 fd=nullptr;
7790 isGlobal=false;
7791 }
7792 else if (nd && !nd->isAnonymous()) // found enum inside namespace
7793 {
7794 //printf("Enum in namespace '%s'::'%s'\n",qPrint(nd->name()),qPrint(name));
7796 isGlobal=true;
7797 }
7798 else // found a global enum
7799 {
7800 fd=root->fileDef();
7801 //printf("Enum in file '%s': '%s'\n",qPrint(fd->name()),qPrint(name));
7803 isGlobal=true;
7804 }
7805
7806 if (!name.isEmpty())
7807 {
7808 //printf("** name=%s\n",qPrint(name));
7809 MemberName *mn = mnsd->find(name); // for all members with this name
7810 if (mn)
7811 {
7812 struct EnumValueInfo
7813 {
7814 EnumValueInfo(const QCString &n,std::unique_ptr<MemberDef> &&md) :
7815 name(n), member(std::move(md)) {}
7816 QCString name;
7817 std::unique_ptr<MemberDef> member;
7818 };
7819 std::vector< EnumValueInfo > extraMembers;
7820 // for each enum in this list
7821 for (const auto &imd : *mn)
7822 {
7823 MemberDefMutable *md = toMemberDefMutable(imd.get());
7824 // use raw pointer in this loop, since we modify mn and can then invalidate mdp.
7825 if (md && md->isEnumerate() && !root->children().empty())
7826 {
7827 AUTO_TRACE_ADD("enum {} with {} children",md->name(),root->children().size());
7828 for (const auto &e : root->children())
7829 {
7830 SrcLangExt sle = root->lang;
7831 bool isJavaLike = sle==SrcLangExt::CSharp || sle==SrcLangExt::Java || sle==SrcLangExt::XML;
7832 if ( isJavaLike || root->spec.isStrong())
7833 {
7834 if (sle == SrcLangExt::Cpp && e->section.isDefine()) continue;
7835 // Unlike classic C/C++ enums, for C++11, C# & Java enum
7836 // values are only visible inside the enum scope, so we must create
7837 // them here and only add them to the enum
7838 //printf("md->qualifiedName()=%s e->name=%s tagInfo=%p name=%s\n",
7839 // qPrint(md->qualifiedName()),qPrint(e->name),(void*)e->tagInfo(),qPrint(e->name));
7840 QCString qualifiedName = root->name;
7841 i = qualifiedName.findRev("::");
7842 if (i!=-1 && sle==SrcLangExt::CSharp)
7843 {
7844 qualifiedName = mangleCSharpGenericName(qualifiedName.left(i))+qualifiedName.mid(i);
7845 }
7846 if (isJavaLike)
7847 {
7848 qualifiedName=substitute(qualifiedName,"::",".");
7849 }
7850 if (md->qualifiedName()==qualifiedName) // enum value scope matches that of the enum
7851 {
7852 QCString fileName = e->fileName;
7853 if (fileName.isEmpty() && e->tagInfo())
7854 {
7855 fileName = e->tagInfo()->tagName;
7856 }
7857 AUTO_TRACE_ADD("strong enum value {}",e->name);
7858 auto fmd = createMemberDef(
7859 fileName,e->startLine,e->startColumn,
7860 e->type,e->name,e->args,QCString(),
7861 e->protection, Specifier::Normal,e->isStatic,Relationship::Member,
7863 auto fmmd = toMemberDefMutable(fmd.get());
7864 NamespaceDef *mnd = md->getNamespaceDef();
7865 if (md->getClassDef())
7866 fmmd->setMemberClass(md->getClassDef());
7867 else if (mnd && (mnd->isLinkable() || mnd->isAnonymous()))
7868 fmmd->setNamespace(mnd);
7869 else if (md->getFileDef())
7870 fmmd->setFileDef(md->getFileDef());
7871 fmmd->setOuterScope(md->getOuterScope());
7872 fmmd->setTagInfo(e->tagInfo());
7873 fmmd->setLanguage(e->lang);
7874 fmmd->setBodySegment(e->startLine,e->bodyLine,e->endBodyLine);
7875 fmmd->setBodyDef(e->fileDef());
7876 fmmd->setId(e->id);
7877 fmmd->setDocumentation(e->doc,e->docFile,e->docLine);
7878 fmmd->setBriefDescription(e->brief,e->briefFile,e->briefLine);
7879 fmmd->addSectionsToDefinition(e->anchors);
7880 fmmd->setInitializer(e->initializer.str());
7881 fmmd->setMaxInitLines(e->initLines);
7882 fmmd->setMemberGroupId(e->mGrpId);
7883 fmmd->setExplicitExternal(e->explicitExternal,fileName,e->startLine,e->startColumn);
7884 fmmd->setRefItems(e->sli);
7885 fmmd->setRequirementReferences(e->rqli);
7886 fmmd->setAnchor();
7887 md->insertEnumField(fmd.get());
7888 fmmd->setEnumScope(md,TRUE);
7889 extraMembers.emplace_back(e->name,std::move(fmd));
7890 }
7891 }
7892 else
7893 {
7894 AUTO_TRACE_ADD("enum value {}",e->name);
7895 //printf("e->name=%s isRelated=%d\n",qPrint(e->name),isRelated);
7896 MemberName *fmn=nullptr;
7897 MemberNameLinkedMap *emnsd = isRelated ? Doxygen::functionNameLinkedMap : mnsd;
7898 if (!e->name.isEmpty() && (fmn=emnsd->find(e->name)))
7899 // get list of members with the same name as the field
7900 {
7901 for (const auto &ifmd : *fmn)
7902 {
7903 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
7904 if (fmd && fmd->isEnumValue() && fmd->getOuterScope()==md->getOuterScope()) // in same scope
7905 {
7906 //printf("found enum value with same name %s in scope %s\n",
7907 // qPrint(fmd->name()),qPrint(fmd->getOuterScope()->name()));
7908 if (nd && !nd->isAnonymous())
7909 {
7910 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
7911 {
7912 const NamespaceDef *fnd=fmd->getNamespaceDef();
7913 if (fnd==nd) // enum value is inside a namespace
7914 {
7915 md->insertEnumField(fmd);
7916 fmd->setEnumScope(md);
7917 }
7918 }
7919 }
7920 else if (isGlobal)
7921 {
7922 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
7923 {
7924 const FileDef *ffd=fmd->getFileDef();
7925 if (ffd==fd && ffd==md->getFileDef()) // enum value has file scope
7926 {
7927 md->insertEnumField(fmd);
7928 fmd->setEnumScope(md);
7929 }
7930 }
7931 }
7932 else if (isRelated && cd) // reparent enum value to
7933 // match the enum's scope
7934 {
7935 md->insertEnumField(fmd); // add field def to list
7936 fmd->setEnumScope(md); // cross ref with enum name
7937 fmd->setEnumClassScope(cd); // cross ref with enum name
7938 fmd->setOuterScope(cd);
7939 fmd->makeRelated();
7940 cd->insertMember(fmd);
7941 }
7942 else
7943 {
7944 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
7945 {
7946 const ClassDef *fcd=fmd->getClassDef();
7947 if (fcd==cd) // enum value is inside a class
7948 {
7949 //printf("Inserting enum field %s in enum scope %s\n",
7950 // qPrint(fmd->name()),qPrint(md->name()));
7951 md->insertEnumField(fmd); // add field def to list
7952 fmd->setEnumScope(md); // cross ref with enum name
7953 }
7954 }
7955 }
7956 }
7957 }
7958 }
7959 }
7960 }
7961 }
7962 }
7963 // move the newly added members into mn
7964 for (auto &e : extraMembers)
7965 {
7966 MemberName *emn=mnsd->add(e.name);
7967 emn->push_back(std::move(e.member));
7968 }
7969 }
7970 }
7971 }
7972 else
7973 {
7974 for (const auto &e : root->children()) addEnumValuesToEnums(e.get());
7975 }
7976}
7977
7978//----------------------------------------------------------------------
7979
7980static void addEnumDocs(const Entry *root,MemberDefMutable *md)
7981{
7982 AUTO_TRACE();
7983 // documentation outside a compound overrides the documentation inside it
7984 {
7985 md->setDocumentation(root->doc,root->docFile,root->docLine);
7986 md->setDocsForDefinition(!root->proto);
7987 }
7988
7989 // brief descriptions inside a compound override the documentation
7990 // outside it
7991 {
7992 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7993 }
7994
7995 if (md->inbodyDocumentation().isEmpty() || !root->parent()->name.isEmpty())
7996 {
7998 }
7999
8000 if (root->mGrpId!=-1 && md->getMemberGroupId()==-1)
8001 {
8002 md->setMemberGroupId(root->mGrpId);
8003 }
8004
8006 md->setRefItems(root->sli);
8007 md->setRequirementReferences(root->rqli);
8008
8009 const GroupDef *gd=md->getGroupDef();
8010 if (gd==nullptr && !root->groups.empty()) // member not grouped but out-of-line documentation is
8011 {
8012 addMemberToGroups(root,md);
8013 }
8015}
8016
8017//----------------------------------------------------------------------
8018// Search for the name in the associated groups. If a matching member
8019// definition exists, then add the documentation to it and return TRUE,
8020// otherwise FALSE.
8021
8022static bool tryAddEnumDocsToGroupMember(const Entry *root,const QCString &name)
8023{
8024 for (const auto &g : root->groups)
8025 {
8026 const GroupDef *gd = Doxygen::groupLinkedMap->find(g.groupname);
8027 if (gd)
8028 {
8029 MemberList *ml = gd->getMemberList(MemberListType::DecEnumMembers());
8030 if (ml)
8031 {
8032 MemberDefMutable *md = toMemberDefMutable(ml->find(name));
8033 if (md)
8034 {
8035 addEnumDocs(root,md);
8036 return TRUE;
8037 }
8038 }
8039 }
8040 else if (!gd && g.pri == Grouping::GROUPING_INGROUP)
8041 {
8042 warn(root->fileName, root->startLine,
8043 "Found non-existing group '{}' for the command '{}', ignoring command",
8044 g.groupname, Grouping::getGroupPriName( g.pri )
8045 );
8046 }
8047 }
8048
8049 return FALSE;
8050}
8051
8052//----------------------------------------------------------------------
8053// find the documentation blocks for the enumerations
8054
8055static void findEnumDocumentation(const Entry *root)
8056{
8057 if (root->section.isEnumDoc() &&
8058 !root->name.isEmpty() &&
8059 root->name.at(0)!='@' // skip anonymous enums
8060 )
8061 {
8062 QCString name;
8063 QCString scope;
8064 int i = root->name.findRev("::");
8065 if (i!=-1) // scope is specified as part of the name
8066 {
8067 name=root->name.right(root->name.length()-i-2); // extract name
8068 scope=root->name.left(i); // extract scope
8069 //printf("Scope='%s' Name='%s'\n",qPrint(scope),qPrint(name));
8070 }
8071 else // just the name
8072 {
8073 name=root->name;
8074 }
8075 if (root->parent()->section.isScope() && !root->parent()->name.isEmpty()) // found enum docs inside a compound
8076 {
8077 if (!scope.isEmpty()) scope.prepend("::");
8078 scope.prepend(root->parent()->name);
8079 }
8080 const ClassDef *cd = getClass(scope);
8081 const NamespaceDef *nd=Doxygen::namespaceLinkedMap->find(scope);
8082 const FileDef *fd = root->fileDef();
8083 AUTO_TRACE("Found docs for enum with name '{}' and scope '{}' in context '{}' cd='{}', nd='{}' fd='{}'",
8084 name,scope,root->parent()->name,
8085 cd ? cd->name() : QCString("<none>"),
8086 nd ? nd->name() : QCString("<none>"),
8087 fd ? fd->name() : QCString("<none>"));
8088
8089 if (!name.isEmpty())
8090 {
8091 bool found = tryAddEnumDocsToGroupMember(root, name);
8092 if (!found)
8093 {
8094 MemberName *mn = cd ? Doxygen::memberNameLinkedMap->find(name) : Doxygen::functionNameLinkedMap->find(name);
8095 if (mn)
8096 {
8097 for (const auto &imd : *mn)
8098 {
8099 MemberDefMutable *md = toMemberDefMutable(imd.get());
8100 if (md && md->isEnumerate())
8101 {
8102 const ClassDef *mcd = md->getClassDef();
8103 const NamespaceDef *mnd = md->getNamespaceDef();
8104 const FileDef *mfd = md->getFileDef();
8105 if (cd && mcd==cd)
8106 {
8107 AUTO_TRACE_ADD("Match found for class scope");
8108 addEnumDocs(root,md);
8109 found = TRUE;
8110 break;
8111 }
8112 else if (cd==nullptr && mcd==nullptr && nd!=nullptr && mnd==nd)
8113 {
8114 AUTO_TRACE_ADD("Match found for namespace scope");
8115 addEnumDocs(root,md);
8116 found = TRUE;
8117 break;
8118 }
8119 else if (cd==nullptr && nd==nullptr && mcd==nullptr && mnd==nullptr && fd==mfd)
8120 {
8121 AUTO_TRACE_ADD("Match found for global scope");
8122 addEnumDocs(root,md);
8123 found = TRUE;
8124 break;
8125 }
8126 }
8127 }
8128 }
8129 }
8130 if (!found)
8131 {
8132 warn(root->fileName,root->startLine, "Documentation for undefined enum '{}' found.", name);
8133 }
8134 }
8135 }
8136 for (const auto &e : root->children()) findEnumDocumentation(e.get());
8137}
8138
8139// search for each enum (member or function) in mnl if it has documented
8140// enum values.
8141static void findDEV(const MemberNameLinkedMap &mnsd)
8142{
8143 // for each member name
8144 for (const auto &mn : mnsd)
8145 {
8146 // for each member definition
8147 for (const auto &imd : *mn)
8148 {
8149 MemberDefMutable *md = toMemberDefMutable(imd.get());
8150 if (md && md->isEnumerate()) // member is an enum
8151 {
8152 int documentedEnumValues=0;
8153 // for each enum value
8154 for (const auto &fmd : md->enumFieldList())
8155 {
8156 if (fmd->isLinkableInProject()) documentedEnumValues++;
8157 }
8158 // at least one enum value is documented
8159 if (documentedEnumValues>0) md->setDocumentedEnumValues(TRUE);
8160 }
8161 }
8162 }
8163}
8164
8165// search for each enum (member or function) if it has documented enum
8166// values.
8172
8173//----------------------------------------------------------------------
8174
8176{
8177 auto &index = Index::instance();
8178 // for each class member name
8179 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8180 {
8181 // for each member definition
8182 for (const auto &md : *mn)
8183 {
8184 index.addClassMemberNameToIndex(md.get());
8185 if (md->getModuleDef())
8186 {
8187 index.addModuleMemberNameToIndex(md.get());
8188 }
8189 }
8190 }
8191 // for each file/namespace function name
8192 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8193 {
8194 // for each member definition
8195 for (const auto &md : *mn)
8196 {
8197 if (md->getNamespaceDef())
8198 {
8199 index.addNamespaceMemberNameToIndex(md.get());
8200 }
8201 else
8202 {
8203 index.addFileMemberNameToIndex(md.get());
8204 }
8205 if (md->getModuleDef())
8206 {
8207 index.addModuleMemberNameToIndex(md.get());
8208 }
8209 }
8210 }
8211
8212 index.sortMemberIndexLists();
8213}
8214
8215//----------------------------------------------------------------------
8216
8217static void addToIndices()
8218{
8219 for (const auto &cd : *Doxygen::classLinkedMap)
8220 {
8221 if (cd->isLinkableInProject())
8222 {
8223 Doxygen::indexList->addIndexItem(cd.get(),nullptr);
8224 if (Doxygen::searchIndex.enabled())
8225 {
8226 Doxygen::searchIndex.setCurrentDoc(cd.get(),cd->anchor(),FALSE);
8227 Doxygen::searchIndex.addWord(cd->localName(),TRUE);
8228 }
8229 }
8230 }
8231
8232 for (const auto &cd : *Doxygen::conceptLinkedMap)
8233 {
8234 if (cd->isLinkableInProject())
8235 {
8236 Doxygen::indexList->addIndexItem(cd.get(),nullptr);
8237 if (Doxygen::searchIndex.enabled())
8238 {
8239 Doxygen::searchIndex.setCurrentDoc(cd.get(),cd->anchor(),FALSE);
8240 Doxygen::searchIndex.addWord(cd->localName(),TRUE);
8241 }
8242 }
8243 }
8244
8245 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8246 {
8247 if (nd->isLinkableInProject())
8248 {
8249 Doxygen::indexList->addIndexItem(nd.get(),nullptr);
8250 if (Doxygen::searchIndex.enabled())
8251 {
8252 Doxygen::searchIndex.setCurrentDoc(nd.get(),nd->anchor(),FALSE);
8253 Doxygen::searchIndex.addWord(nd->localName(),TRUE);
8254 }
8255 }
8256 }
8257
8258 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8259 {
8260 for (const auto &fd : *fn)
8261 {
8262 if (Doxygen::searchIndex.enabled() && fd->isLinkableInProject())
8263 {
8264 Doxygen::searchIndex.setCurrentDoc(fd.get(),fd->anchor(),FALSE);
8265 Doxygen::searchIndex.addWord(fd->localName(),TRUE);
8266 }
8267 }
8268 }
8269
8270 auto addWordsForTitle = [](const Definition *d,const QCString &anchor,const QCString &title)
8271 {
8272 Doxygen::indexList->addIndexItem(d,nullptr,QCString(),filterTitle(title));
8273 if (Doxygen::searchIndex.enabled())
8274 {
8275 Doxygen::searchIndex.setCurrentDoc(d,anchor,false);
8276 std::string s = title.str();
8277 static const reg::Ex re(R"(\a[\w-]*)");
8278 reg::Iterator it(s,re);
8280 for (; it!=end ; ++it)
8281 {
8282 const auto &match = *it;
8283 std::string matchStr = match.str();
8284 Doxygen::searchIndex.addWord(matchStr,true);
8285 }
8286 }
8287 };
8288
8289 for (const auto &gd : *Doxygen::groupLinkedMap)
8290 {
8291 if (gd->isLinkableInProject())
8292 {
8293 addWordsForTitle(gd.get(),gd->anchor(),gd->groupTitle());
8294 }
8295 }
8296
8297 for (const auto &pd : *Doxygen::pageLinkedMap)
8298 {
8299 if (pd->isLinkableInProject())
8300 {
8301 addWordsForTitle(pd.get(),pd->anchor(),pd->title());
8302 }
8303 }
8304
8306 {
8307 addWordsForTitle(Doxygen::mainPage.get(),Doxygen::mainPage->anchor(),Doxygen::mainPage->title());
8308 }
8309
8310 auto addMemberToSearchIndex = [](const MemberDef *md)
8311 {
8312 if (Doxygen::searchIndex.enabled())
8313 {
8314 Doxygen::searchIndex.setCurrentDoc(md,md->anchor(),FALSE);
8315 QCString ln=md->localName();
8316 QCString qn=md->qualifiedName();
8317 Doxygen::searchIndex.addWord(ln,TRUE);
8318 if (ln!=qn)
8319 {
8320 Doxygen::searchIndex.addWord(qn,TRUE);
8321 if (md->getClassDef())
8322 {
8323 Doxygen::searchIndex.addWord(md->getClassDef()->displayName(),TRUE);
8324 }
8325 if (md->getNamespaceDef())
8326 {
8327 Doxygen::searchIndex.addWord(md->getNamespaceDef()->displayName(),TRUE);
8328 }
8329 }
8330 }
8331 };
8332
8333 auto getScope = [](const MemberDef *md)
8334 {
8335 const Definition *scope = nullptr;
8336 if (md->getGroupDef()) scope = md->getGroupDef();
8337 else if (md->getClassDef()) scope = md->getClassDef();
8338 else if (md->getNamespaceDef()) scope = md->getNamespaceDef();
8339 else if (md->getFileDef()) scope = md->getFileDef();
8340 return scope;
8341 };
8342
8343 auto addMemberToIndices = [addMemberToSearchIndex,getScope](const MemberDef *md)
8344 {
8345 if (md->isLinkableInProject())
8346 {
8347 if (!(md->isEnumerate() && md->isAnonymous()))
8348 {
8349 Doxygen::indexList->addIndexItem(getScope(md),md);
8351 }
8352 if (md->isEnumerate())
8353 {
8354 for (const auto &fmd : md->enumFieldList())
8355 {
8356 Doxygen::indexList->addIndexItem(getScope(fmd),fmd);
8358 }
8359 }
8360 }
8361 };
8362
8363 // for each class member name
8364 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8365 {
8366 // for each member definition
8367 for (const auto &md : *mn)
8368 {
8369 addMemberToIndices(md.get());
8370 }
8371 }
8372 // for each file/namespace function name
8373 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8374 {
8375 // for each member definition
8376 for (const auto &md : *mn)
8377 {
8378 addMemberToIndices(md.get());
8379 }
8380 }
8381}
8382
8383//----------------------------------------------------------------------
8384
8386{
8387 // for each member name
8388 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8389 {
8390 // for each member definition
8391 for (const auto &imd : *mn)
8392 {
8393 MemberDefMutable *md = toMemberDefMutable(imd.get());
8394 if (md)
8395 {
8397 }
8398 }
8399 }
8400 // for each member name
8401 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8402 {
8403 // for each member definition
8404 for (const auto &imd : *mn)
8405 {
8406 MemberDefMutable *md = toMemberDefMutable(imd.get());
8407 if (md)
8408 {
8410 }
8411 }
8412 }
8413}
8414
8415// recursive helper function looking for reimplements/implemented
8416// by relations between class cd and direct or indirect base class bcd
8418{
8419 for (const auto &mn : cd->memberNameInfoLinkedMap()) // for each member in class cd with a unique name
8420 {
8421 for (const auto &imd : *mn) // for each member with a given name
8422 {
8423 MemberDefMutable *md = toMemberDefMutable(imd->memberDef());
8424 if (md && (md->isFunction() || md->isCSharpProperty())) // filter on reimplementable members
8425 {
8426 ClassDef *mbcd = bcd->classDef;
8427 if (mbcd && mbcd->isLinkable()) // filter on linkable classes
8428 {
8429 const auto &bmn = mbcd->memberNameInfoLinkedMap();
8430 const auto &bmni = bmn.find(mn->memberName());
8431 if (bmni) // there are base class members with the same name
8432 {
8433 for (const auto &ibmd : *bmni) // for base class member with that name
8434 {
8435 MemberDefMutable *bmd = toMemberDefMutable(ibmd->memberDef());
8436 if (bmd) // not part of an inline namespace
8437 {
8438 auto lang = bmd->getLanguage();
8439 auto compType = mbcd->compoundType();
8440 if (bmd->virtualness()!=Specifier::Normal ||
8441 lang==SrcLangExt::Python ||
8442 lang==SrcLangExt::Java ||
8443 lang==SrcLangExt::PHP ||
8444 compType==ClassDef::Interface ||
8445 compType==ClassDef::Protocol)
8446 {
8447 const ArgumentList &bmdAl = bmd->argumentList();
8448 const ArgumentList &mdAl = md->argumentList();
8449 //printf(" Base argList='%s'\n Super argList='%s'\n",
8450 // qPrint(argListToString(bmdAl)),
8451 // qPrint(argListToString(mdAl))
8452 // );
8453 if (
8454 lang==SrcLangExt::Python ||
8455 matchArguments2(bmd->getOuterScope(),bmd->getFileDef(),bmd->typeString(),&bmdAl,
8456 md->getOuterScope(), md->getFileDef(), md->typeString(),&mdAl,
8457 TRUE,lang
8458 )
8459 )
8460 {
8461 if (lang==SrcLangExt::Python && md->name().startsWith("__")) continue; // private members do not reimplement
8462 //printf("match!\n");
8463 const MemberDef *rmd = md->reimplements();
8464 if (rmd==nullptr) // not already assigned
8465 {
8466 //printf("%s: setting (new) reimplements member %s\n",qPrint(md->qualifiedName()),qPrint(bmd->qualifiedName()));
8467 md->setReimplements(bmd);
8468 }
8469 //printf("%s: add reimplementedBy member %s\n",qPrint(bmd->qualifiedName()),qPrint(md->qualifiedName()));
8470 bmd->insertReimplementedBy(md);
8471 }
8472 else
8473 {
8474 //printf("no match!\n");
8475 }
8476 }
8477 }
8478 }
8479 }
8480 }
8481 }
8482 }
8483 }
8484
8485 // do also for indirect base classes
8486 for (const auto &bbcd : bcd->classDef->baseClasses())
8487 {
8489 }
8490}
8491
8492//----------------------------------------------------------------------
8493// computes the relation between all members. For each member 'm'
8494// the members that override the implementation of 'm' are searched and
8495// the member that 'm' overrides is searched.
8496
8498{
8499 for (const auto &cd : *Doxygen::classLinkedMap)
8500 {
8501 if (cd->isLinkable())
8502 {
8503 for (const auto &bcd : cd->baseClasses())
8504 {
8506 }
8507 }
8508 }
8509}
8510
8511//----------------------------------------------------------------------------
8512
8514{
8515 // for each class
8516 for (const auto &cd : *Doxygen::classLinkedMap)
8517 {
8518 // that is a template
8519 for (const auto &ti : cd->getTemplateInstances())
8520 {
8521 ClassDefMutable *tcdm = toClassDefMutable(ti.classDef);
8522 if (tcdm)
8523 {
8524 tcdm->addMembersToTemplateInstance(cd.get(),cd->templateArguments(),ti.templSpec);
8525 }
8526 }
8527 }
8528}
8529
8530//----------------------------------------------------------------------------
8531
8532static void mergeCategories()
8533{
8534 AUTO_TRACE();
8535 // merge members of categories into the class they extend
8536 for (const auto &cd : *Doxygen::classLinkedMap)
8537 {
8538 int i=cd->name().find('(');
8539 if (i!=-1) // it is an Objective-C category
8540 {
8541 QCString baseName=cd->name().left(i);
8542 ClassDefMutable *baseClass=toClassDefMutable(Doxygen::classLinkedMap->find(baseName));
8543 if (baseClass)
8544 {
8545 AUTO_TRACE_ADD("merging members of category {} into {}",cd->name(),baseClass->name());
8546 baseClass->mergeCategory(cd.get());
8547 }
8548 }
8549 }
8550}
8551
8552// builds the list of all members for each class
8553
8555{
8556 // merge the member list of base classes into the inherited classes.
8557 for (const auto &cd : *Doxygen::classLinkedMap)
8558 {
8559 if (// !cd->isReference() && // not an external class
8560 cd->subClasses().empty() && // is a root of the hierarchy
8561 !cd->baseClasses().empty()) // and has at least one base class
8562 {
8563 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8564 if (cdm)
8565 {
8566 //printf("*** merging members for %s\n",qPrint(cd->name()));
8567 cdm->mergeMembers();
8568 }
8569 }
8570 }
8571 // now sort the member list of all members for all classes.
8572 for (const auto &cd : *Doxygen::classLinkedMap)
8573 {
8574 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8575 if (cdm)
8576 {
8577 cdm->sortAllMembersList();
8578 }
8579 }
8580}
8581
8582//----------------------------------------------------------------------------
8583
8585{
8586 auto processSourceFile = [](FileDef *fd,OutputList &ol,ClangTUParser *parser)
8587 {
8588 bool showSources = fd->generateSourceFile() && !Htags::useHtags; // sources need to be shown in the output
8589 bool parseSources = !fd->isReference() && Doxygen::parseSourcesNeeded; // we needed to parse the sources even if we do not show them
8590 if (showSources)
8591 {
8592 msg("Generating code for file {}...\n",fd->docName());
8593 fd->writeSourceHeader(ol);
8594 fd->writeSourceBody(ol,parser);
8595 fd->writeSourceFooter(ol);
8596 }
8597 else if (parseSources)
8598 {
8599 msg("Parsing code for file {}...\n",fd->docName());
8600 fd->parseSource(parser);
8601 }
8602 };
8603 if (!Doxygen::inputNameLinkedMap->empty())
8604 {
8605#if USE_LIBCLANG
8607 {
8608 StringUnorderedSet processedFiles;
8609
8610 // create a dictionary with files to process
8611 StringUnorderedSet filesToProcess;
8612
8613 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8614 {
8615 for (const auto &fd : *fn)
8616 {
8617 filesToProcess.insert(fd->absFilePath().str());
8618 }
8619 }
8620 // process source files (and their include dependencies)
8621 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8622 {
8623 for (const auto &fd : *fn)
8624 {
8625 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt::Cpp &&
8626 (fd->generateSourceFile() ||
8628 )
8629 )
8630 {
8631 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8632 clangParser->parse();
8633 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8634
8635 for (auto incFile : clangParser->filesInSameTU())
8636 {
8637 if (filesToProcess.find(incFile)!=filesToProcess.end() && // part of input
8638 fd->absFilePath()!=incFile && // not same file
8639 processedFiles.find(incFile)==processedFiles.end()) // not yet marked as processed
8640 {
8641 StringVector moreFiles;
8642 bool ambig = false;
8644 if (ifd && !ifd->isReference())
8645 {
8646 processSourceFile(ifd,*g_outputList,clangParser.get());
8647 processedFiles.insert(incFile);
8648 }
8649 }
8650 }
8651 processedFiles.insert(fd->absFilePath().str());
8652 }
8653 }
8654 }
8655 // process remaining files
8656 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8657 {
8658 for (const auto &fd : *fn)
8659 {
8660 if (processedFiles.find(fd->absFilePath().str())==processedFiles.end()) // not yet processed
8661 {
8662 if (fd->getLanguage()==SrcLangExt::Cpp) // C/C++ file, use clang parser
8663 {
8664 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8665 clangParser->parse();
8666 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8667 }
8668 else // non C/C++ file, use built-in parser
8669 {
8670 processSourceFile(fd.get(),*g_outputList,nullptr);
8671 }
8672 }
8673 }
8674 }
8675 }
8676 else
8677#endif
8678 {
8679 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8680 if (numThreads>1)
8681 {
8682 msg("Generating code files using {} threads.\n",numThreads);
8683 struct SourceContext
8684 {
8685 SourceContext(FileDef *fd_,bool gen_,const OutputList &ol_)
8686 : fd(fd_), generateSourceFile(gen_), ol(ol_) {}
8687 FileDef *fd;
8688 bool generateSourceFile;
8689 OutputList ol;
8690 };
8691 ThreadPool threadPool(numThreads);
8692 std::vector< std::future< std::shared_ptr<SourceContext> > > results;
8693 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8694 {
8695 for (const auto &fd : *fn)
8696 {
8697 bool generateSourceFile = fd->generateSourceFile() && !Htags::useHtags;
8698 auto ctx = std::make_shared<SourceContext>(fd.get(),generateSourceFile,*g_outputList);
8699 auto processFile = [ctx]()
8700 {
8701 if (ctx->generateSourceFile)
8702 {
8703 msg("Generating code for file {}...\n",ctx->fd->docName());
8704 }
8705 else
8706 {
8707 msg("Parsing code for file {}...\n",ctx->fd->docName());
8708 }
8709 StringVector filesInSameTu;
8710 ctx->fd->getAllIncludeFilesRecursively(filesInSameTu);
8711 if (ctx->generateSourceFile) // sources need to be shown in the output
8712 {
8713 ctx->fd->writeSourceHeader(ctx->ol);
8714 ctx->fd->writeSourceBody(ctx->ol,nullptr);
8715 ctx->fd->writeSourceFooter(ctx->ol);
8716 }
8717 else if (!ctx->fd->isReference() && Doxygen::parseSourcesNeeded)
8718 // we needed to parse the sources even if we do not show them
8719 {
8720 ctx->fd->parseSource(nullptr);
8721 }
8722 return ctx;
8723 };
8724 results.emplace_back(threadPool.queue(processFile));
8725 }
8726 }
8727 for (auto &f : results)
8728 {
8729 auto ctx = f.get();
8730 }
8731 }
8732 else // single threaded version
8733 {
8734 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8735 {
8736 for (const auto &fd : *fn)
8737 {
8738 StringVector filesInSameTu;
8739 fd->getAllIncludeFilesRecursively(filesInSameTu);
8740 processSourceFile(fd.get(),*g_outputList,nullptr);
8741 }
8742 }
8743 }
8744 }
8745 }
8746}
8747
8748//----------------------------------------------------------------------------
8749
8750static void generateFileDocs()
8751{
8752 if (Index::instance().numDocumentedFiles()==0) return;
8753
8754 if (!Doxygen::inputNameLinkedMap->empty())
8755 {
8756 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8757 if (numThreads>1) // multi threaded processing
8758 {
8759 struct DocContext
8760 {
8761 DocContext(FileDef *fd_,const OutputList &ol_)
8762 : fd(fd_), ol(ol_) {}
8763 FileDef *fd;
8764 OutputList ol;
8765 };
8766 ThreadPool threadPool(numThreads);
8767 std::vector< std::future< std::shared_ptr<DocContext> > > results;
8768 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8769 {
8770 for (const auto &fd : *fn)
8771 {
8772 bool doc = fd->isLinkableInProject();
8773 if (doc)
8774 {
8775 auto ctx = std::make_shared<DocContext>(fd.get(),*g_outputList);
8776 auto processFile = [ctx]() {
8777 msg("Generating docs for file {}...\n",ctx->fd->docName());
8778 ctx->fd->writeDocumentation(ctx->ol);
8779 return ctx;
8780 };
8781 results.emplace_back(threadPool.queue(processFile));
8782 }
8783 }
8784 }
8785 for (auto &f : results)
8786 {
8787 auto ctx = f.get();
8788 }
8789 }
8790 else // single threaded processing
8791 {
8792 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8793 {
8794 for (const auto &fd : *fn)
8795 {
8796 bool doc = fd->isLinkableInProject();
8797 if (doc)
8798 {
8799 msg("Generating docs for file {}...\n",fd->docName());
8800 fd->writeDocumentation(*g_outputList);
8801 }
8802 }
8803 }
8804 }
8805 }
8806}
8807
8808//----------------------------------------------------------------------------
8809
8811{
8812 // add source references for class definitions
8813 for (const auto &cd : *Doxygen::classLinkedMap)
8814 {
8815 const FileDef *fd=cd->getBodyDef();
8816 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8817 {
8818 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),nullptr);
8819 }
8820 }
8821 // add source references for concept definitions
8822 for (const auto &cd : *Doxygen::conceptLinkedMap)
8823 {
8824 const FileDef *fd=cd->getBodyDef();
8825 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8826 {
8827 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),nullptr);
8828 }
8829 }
8830 // add source references for namespace definitions
8831 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8832 {
8833 const FileDef *fd=nd->getBodyDef();
8834 if (fd && nd->isLinkableInProject() && nd->getStartDefLine()!=-1)
8835 {
8836 const_cast<FileDef*>(fd)->addSourceRef(nd->getStartDefLine(),nd.get(),nullptr);
8837 }
8838 }
8839
8840 // add source references for member names
8841 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8842 {
8843 for (const auto &md : *mn)
8844 {
8845 //printf("class member %s: def=%s body=%d link?=%d\n",
8846 // qPrint(md->name()),
8847 // md->getBodyDef()?qPrint(md->getBodyDef()->name()):"<none>",
8848 // md->getStartBodyLine(),md->isLinkableInProject());
8849 const FileDef *fd=md->getBodyDef();
8850 if (fd &&
8851 md->getStartDefLine()!=-1 &&
8852 md->isLinkableInProject() &&
8854 )
8855 {
8856 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8857 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8858 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8859 }
8860 }
8861 }
8862 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8863 {
8864 for (const auto &md : *mn)
8865 {
8866 const FileDef *fd=md->getBodyDef();
8867 //printf("member %s body=[%d,%d] fd=%p link=%d parseSources=%d\n",
8868 // qPrint(md->name()),
8869 // md->getStartBodyLine(),md->getEndBodyLine(),fd,
8870 // md->isLinkableInProject(),
8871 // Doxygen::parseSourcesNeeded);
8872 if (fd &&
8873 md->getStartDefLine()!=-1 &&
8874 md->isLinkableInProject() &&
8876 )
8877 {
8878 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8879 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8880 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8881 }
8882 }
8883 }
8884}
8885
8886//----------------------------------------------------------------------------
8887
8888// add the macro definitions found during preprocessing as file members
8889static void buildDefineList()
8890{
8891 AUTO_TRACE();
8892 for (const auto &s : g_inputFiles)
8893 {
8894 auto it = Doxygen::macroDefinitions.find(s);
8896 {
8897 for (const auto &def : it->second)
8898 {
8899 auto md = createMemberDef(
8900 def.fileName,def.lineNr,def.columnNr,
8901 "#define",def.name,def.args,QCString(),
8902 Protection::Public,Specifier::Normal,FALSE,Relationship::Member,MemberType::Define,
8903 ArgumentList(),ArgumentList(),"");
8904 auto mmd = toMemberDefMutable(md.get());
8905
8906 if (!def.args.isEmpty())
8907 {
8908 mmd->moveArgumentList(stringToArgumentList(SrcLangExt::Cpp, def.args));
8909 }
8910 mmd->setInitializer(def.definition);
8911 mmd->setFileDef(def.fileDef);
8912 mmd->setDefinition("#define "+def.name);
8913
8914 MemberName *mn=Doxygen::functionNameLinkedMap->add(def.name);
8915 if (def.fileDef)
8916 {
8917 const MemberList *defMl = def.fileDef->getMemberList(MemberListType::DocDefineMembers());
8918 if (defMl)
8919 {
8920 const MemberDef *defMd = defMl->findRev(def.name);
8921 if (defMd) // definition already stored
8922 {
8923 mmd->setRedefineCount(defMd->redefineCount()+1);
8924 }
8925 }
8926 def.fileDef->insertMember(md.get());
8927 }
8928 AUTO_TRACE_ADD("adding macro {} with definition {}",def.name,def.definition);
8929 mn->push_back(std::move(md));
8930 }
8931 }
8932 }
8933}
8934
8935//----------------------------------------------------------------------------
8936
8937static void sortMemberLists()
8938{
8939 // sort class member lists
8940 for (const auto &cd : *Doxygen::classLinkedMap)
8941 {
8942 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8943 if (cdm)
8944 {
8945 cdm->sortMemberLists();
8946 }
8947 }
8948
8949 // sort namespace member lists
8950 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8951 {
8953 if (ndm)
8954 {
8955 ndm->sortMemberLists();
8956 }
8957 }
8958
8959 // sort file member lists
8960 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8961 {
8962 for (const auto &fd : *fn)
8963 {
8964 fd->sortMemberLists();
8965 }
8966 }
8967
8968 // sort group member lists
8969 for (const auto &gd : *Doxygen::groupLinkedMap)
8970 {
8971 gd->sortMemberLists();
8972 }
8973
8975}
8976
8977//----------------------------------------------------------------------------
8978
8979static bool isSymbolHidden(const Definition *d)
8980{
8981 bool hidden = d->isHidden();
8982 const Definition *parent = d->getOuterScope();
8983 return parent ? hidden || isSymbolHidden(parent) : hidden;
8984}
8985
8987{
8988 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8989 if (numThreads>1)
8990 {
8991 ThreadPool threadPool(numThreads);
8992 std::vector < std::future< void > > results;
8993 // queue the work
8994 for (const auto &[name,symList] : *Doxygen::symbolMap)
8995 {
8996 for (const auto &def : symList)
8997 {
8999 if (dm && !isSymbolHidden(def) && !def->isArtificial() && def->isLinkableInProject())
9000 {
9001 auto processTooltip = [dm]() {
9002 dm->computeTooltip();
9003 };
9004 results.emplace_back(threadPool.queue(processTooltip));
9005 }
9006 }
9007 }
9008 // wait for the results
9009 for (auto &f : results)
9010 {
9011 f.get();
9012 }
9013 }
9014 else
9015 {
9016 for (const auto &[name,symList] : *Doxygen::symbolMap)
9017 {
9018 for (const auto &def : symList)
9019 {
9021 if (dm && !isSymbolHidden(def) && !def->isArtificial() && def->isLinkableInProject())
9022 {
9023 dm->computeTooltip();
9024 }
9025 }
9026 }
9027 }
9028}
9029
9030//----------------------------------------------------------------------------
9031
9033{
9034 for (const auto &cd : *Doxygen::classLinkedMap)
9035 {
9036 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9037 if (cdm)
9038 {
9039 cdm->setAnonymousEnumType();
9040 }
9041 }
9042}
9043
9044//----------------------------------------------------------------------------
9045
9046static void countMembers()
9047{
9048 for (const auto &cd : *Doxygen::classLinkedMap)
9049 {
9050 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9051 if (cdm)
9052 {
9053 cdm->countMembers();
9054 }
9055 }
9056
9057 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9058 {
9060 if (ndm)
9061 {
9062 ndm->countMembers();
9063 }
9064 }
9065
9066 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9067 {
9068 for (const auto &fd : *fn)
9069 {
9070 fd->countMembers();
9071 }
9072 }
9073
9074 for (const auto &gd : *Doxygen::groupLinkedMap)
9075 {
9076 gd->countMembers();
9077 }
9078
9079 auto &mm = ModuleManager::instance();
9080 mm.countMembers();
9081}
9082
9083
9084//----------------------------------------------------------------------------
9085// generate the documentation for all classes
9086
9087static void generateDocsForClassList(const std::vector<ClassDefMutable*> &classList)
9088{
9089 AUTO_TRACE();
9090 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9091 if (numThreads>1) // multi threaded processing
9092 {
9093 struct DocContext
9094 {
9095 DocContext(ClassDefMutable *cd_,const OutputList &ol_)
9096 : cd(cd_), ol(ol_) {}
9097 ClassDefMutable *cd;
9098 OutputList ol;
9099 };
9100 ThreadPool threadPool(numThreads);
9101 std::vector< std::future< std::shared_ptr<DocContext> > > results;
9102 for (const auto &cd : classList)
9103 {
9104 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
9105 if (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9106 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
9107 )
9108 {
9109 auto ctx = std::make_shared<DocContext>(cd,*g_outputList);
9110 auto processFile = [ctx]()
9111 {
9112 msg("Generating docs for compound {}...\n",ctx->cd->displayName());
9113
9114 // skip external references, anonymous compounds and
9115 // template instances
9116 if (!ctx->cd->isHidden() && !ctx->cd->isEmbeddedInOuterScope() &&
9117 ctx->cd->isLinkableInProject() && !ctx->cd->isImplicitTemplateInstance())
9118 {
9119 ctx->cd->writeDocumentation(ctx->ol);
9120 ctx->cd->writeMemberList(ctx->ol);
9121 }
9122
9123 // even for undocumented classes, the inner classes can be documented.
9124 ctx->cd->writeDocumentationForInnerClasses(ctx->ol);
9125 return ctx;
9126 };
9127 results.emplace_back(threadPool.queue(processFile));
9128 }
9129 }
9130 for (auto &f : results)
9131 {
9132 auto ctx = f.get();
9133 }
9134 }
9135 else // single threaded processing
9136 {
9137 for (const auto &cd : classList)
9138 {
9139 //printf("cd=%s getOuterScope=%p global=%p hidden=%d embeddedInOuterScope=%d\n",
9140 // qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope,cd->isHidden(),cd->isEmbeddedInOuterScope());
9141 if (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9142 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
9143 )
9144 {
9145 // skip external references, anonymous compounds and
9146 // template instances
9147 if ( !cd->isHidden() && !cd->isEmbeddedInOuterScope() &&
9148 cd->isLinkableInProject() && !cd->isImplicitTemplateInstance())
9149 {
9150 msg("Generating docs for compound {}...\n",cd->displayName());
9151
9152 cd->writeDocumentation(*g_outputList);
9153 cd->writeMemberList(*g_outputList);
9154 }
9155 // even for undocumented classes, the inner classes can be documented.
9156 cd->writeDocumentationForInnerClasses(*g_outputList);
9157 }
9158 }
9159 }
9160}
9161
9162static void addClassAndNestedClasses(std::vector<ClassDefMutable*> &list,ClassDefMutable *cd)
9163{
9164 list.push_back(cd);
9165 for (const auto &innerCdi : cd->getClasses())
9166 {
9167 ClassDefMutable *innerCd = toClassDefMutable(innerCdi);
9168 if (innerCd)
9169 {
9170 AUTO_TRACE("innerCd={} isLinkable={} isImplicitTemplateInstance={} protectLevelVisible={} embeddedInOuterScope={}",
9171 innerCd->name(),innerCd->isLinkableInProject(),innerCd->isImplicitTemplateInstance(),protectionLevelVisible(innerCd->protection()),
9172 innerCd->isEmbeddedInOuterScope());
9173 }
9174 if (innerCd && innerCd->isLinkableInProject() && !innerCd->isImplicitTemplateInstance() &&
9175 protectionLevelVisible(innerCd->protection()) &&
9176 !innerCd->isEmbeddedInOuterScope()
9177 )
9178 {
9179 list.push_back(innerCd);
9180 addClassAndNestedClasses(list,innerCd);
9181 }
9182 }
9183}
9184
9186{
9187 std::vector<ClassDefMutable*> classList;
9188 for (const auto &cdi : *Doxygen::classLinkedMap)
9189 {
9190 ClassDefMutable *cd = toClassDefMutable(cdi.get());
9191 if (cd && (cd->getOuterScope()==nullptr ||
9193 {
9194 addClassAndNestedClasses(classList,cd);
9195 }
9196 }
9197 for (const auto &cdi : *Doxygen::hiddenClassLinkedMap)
9198 {
9199 ClassDefMutable *cd = toClassDefMutable(cdi.get());
9200 if (cd && (cd->getOuterScope()==nullptr ||
9202 {
9203 addClassAndNestedClasses(classList,cd);
9204 }
9205 }
9206 generateDocsForClassList(classList);
9207}
9208
9209//----------------------------------------------------------------------------
9210
9212{
9213 for (const auto &cdi : *Doxygen::conceptLinkedMap)
9214 {
9216
9217 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
9218 if (cd &&
9219 (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9220 cd->getOuterScope()==Doxygen::globalScope // only look at global concepts
9221 ) && !cd->isHidden() && cd->isLinkableInProject()
9222 )
9223 {
9224 msg("Generating docs for concept {}...\n",cd->displayName());
9226 }
9227 }
9228}
9229
9230//----------------------------------------------------------------------------
9231
9233{
9234 for (const auto &mn : *Doxygen::memberNameLinkedMap)
9235 {
9236 for (const auto &imd : *mn)
9237 {
9238 MemberDefMutable *md = toMemberDefMutable(imd.get());
9239 //static int count=0;
9240 //printf("%04d Member '%s'\n",count++,qPrint(md->qualifiedName()));
9241 if (md && md->documentation().isEmpty() && md->briefDescription().isEmpty())
9242 { // no documentation yet
9243 const MemberDef *bmd = md->reimplements();
9244 while (bmd && bmd->documentation().isEmpty() &&
9245 bmd->briefDescription().isEmpty()
9246 )
9247 { // search up the inheritance tree for a documentation member
9248 //printf("bmd=%s class=%s\n",qPrint(bmd->name()),qPrint(bmd->getClassDef()->name()));
9249 bmd = bmd->reimplements();
9250 }
9251 if (bmd) // copy the documentation from the reimplemented member
9252 {
9253 md->setInheritsDocsFrom(bmd);
9254 md->setDocumentation(bmd->documentation(),bmd->docFile(),bmd->docLine());
9256 md->setBriefDescription(bmd->briefDescription(),bmd->briefFile(),bmd->briefLine());
9257 md->copyArgumentNames(bmd);
9259 }
9260 }
9261 }
9262 }
9263}
9264
9265//----------------------------------------------------------------------------
9266
9268{
9269 // for each file
9270 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9271 {
9272 for (const auto &fd : *fn)
9273 {
9274 fd->combineUsingRelations();
9275 }
9276 }
9277
9278 // for each namespace
9279 NamespaceDefSet visitedNamespaces;
9280 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9281 {
9283 if (ndm)
9284 {
9285 ndm->combineUsingRelations(visitedNamespaces);
9286 }
9287 }
9288}
9289
9290//----------------------------------------------------------------------------
9291
9293{
9294 // for each class
9295 for (const auto &cd : *Doxygen::classLinkedMap)
9296 {
9297 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9298 if (cdm)
9299 {
9301 }
9302 }
9303 // for each file
9304 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9305 {
9306 for (const auto &fd : *fn)
9307 {
9308 fd->addMembersToMemberGroup();
9309 }
9310 }
9311 // for each namespace
9312 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9313 {
9315 if (ndm)
9316 {
9318 }
9319 }
9320 // for each group
9321 for (const auto &gd : *Doxygen::groupLinkedMap)
9322 {
9323 gd->addMembersToMemberGroup();
9324 }
9326}
9327
9328//----------------------------------------------------------------------------
9329
9331{
9332 // for each class
9333 for (const auto &cd : *Doxygen::classLinkedMap)
9334 {
9335 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9336 if (cdm)
9337 {
9339 }
9340 }
9341 // for each file
9342 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9343 {
9344 for (const auto &fd : *fn)
9345 {
9346 fd->distributeMemberGroupDocumentation();
9347 }
9348 }
9349 // for each namespace
9350 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9351 {
9353 if (ndm)
9354 {
9356 }
9357 }
9358 // for each group
9359 for (const auto &gd : *Doxygen::groupLinkedMap)
9360 {
9361 gd->distributeMemberGroupDocumentation();
9362 }
9364}
9365
9366//----------------------------------------------------------------------------
9367
9369{
9370 // for each class
9371 for (const auto &cd : *Doxygen::classLinkedMap)
9372 {
9373 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9374 if (cdm)
9375 {
9377 }
9378 }
9379 // for each concept
9380 for (const auto &cd : *Doxygen::conceptLinkedMap)
9381 {
9382 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
9383 if (cdm)
9384 {
9386 }
9387 }
9388 // for each file
9389 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9390 {
9391 for (const auto &fd : *fn)
9392 {
9393 fd->findSectionsInDocumentation();
9394 }
9395 }
9396 // for each namespace
9397 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9398 {
9400 if (ndm)
9401 {
9403 }
9404 }
9405 // for each group
9406 for (const auto &gd : *Doxygen::groupLinkedMap)
9407 {
9408 gd->findSectionsInDocumentation();
9409 }
9410 // for each page
9411 for (const auto &pd : *Doxygen::pageLinkedMap)
9412 {
9413 pd->findSectionsInDocumentation();
9414 }
9415 // for each directory
9416 for (const auto &dd : *Doxygen::dirLinkedMap)
9417 {
9418 dd->findSectionsInDocumentation();
9419 }
9421 if (Doxygen::mainPage) Doxygen::mainPage->findSectionsInDocumentation();
9422}
9423
9424//----------------------------------------------------------------------
9425
9426
9428{
9429 // remove all references to classes from the cache
9430 // as there can be new template instances in the inheritance path
9431 // to this class. Optimization: only remove those classes that
9432 // have inheritance instances as direct or indirect sub classes.
9433 StringVector elementsToRemove;
9434 for (const auto &ci : *Doxygen::typeLookupCache)
9435 {
9436 const LookupInfo &li = ci.second;
9437 if (li.definition)
9438 {
9439 elementsToRemove.push_back(ci.first);
9440 }
9441 }
9442 for (const auto &k : elementsToRemove)
9443 {
9444 Doxygen::typeLookupCache->remove(k);
9445 }
9446
9447 // remove all cached typedef resolutions whose target is a
9448 // template class as this may now be a template instance
9449 // for each global function name
9450 for (const auto &fn : *Doxygen::functionNameLinkedMap)
9451 {
9452 // for each function with that name
9453 for (const auto &ifmd : *fn)
9454 {
9455 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
9456 if (fmd && fmd->isTypedefValCached())
9457 {
9458 const ClassDef *cd = fmd->getCachedTypedefVal();
9459 if (cd->isTemplate()) fmd->invalidateTypedefValCache();
9460 }
9461 }
9462 }
9463 // for each class method name
9464 for (const auto &nm : *Doxygen::memberNameLinkedMap)
9465 {
9466 // for each function with that name
9467 for (const auto &imd : *nm)
9468 {
9469 MemberDefMutable *md = toMemberDefMutable(imd.get());
9470 if (md && md->isTypedefValCached())
9471 {
9472 const ClassDef *cd = md->getCachedTypedefVal();
9473 if (cd->isTemplate()) md->invalidateTypedefValCache();
9474 }
9475 }
9476 }
9477}
9478
9479//----------------------------------------------------------------------------
9480
9482{
9483 // Remove all unresolved references to classes from the cache.
9484 // This is needed before resolving the inheritance relations, since
9485 // it would otherwise not find the inheritance relation
9486 // for C in the example below, as B::I was already found to be unresolvable
9487 // (which is correct if you ignore the inheritance relation between A and B).
9488 //
9489 // class A { class I {} };
9490 // class B : public A {};
9491 // class C : public B::I {};
9492
9493 StringVector elementsToRemove;
9494 for (const auto &ci : *Doxygen::typeLookupCache)
9495 {
9496 const LookupInfo &li = ci.second;
9497 if (li.definition==nullptr && li.typeDef==nullptr)
9498 {
9499 elementsToRemove.push_back(ci.first);
9500 }
9501 }
9502 for (const auto &k : elementsToRemove)
9503 {
9504 Doxygen::typeLookupCache->remove(k);
9505 }
9506
9507 // for each global function name
9508 for (const auto &fn : *Doxygen::functionNameLinkedMap)
9509 {
9510 // for each function with that name
9511 for (const auto &ifmd : *fn)
9512 {
9513 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
9514 if (fmd)
9515 {
9517 }
9518 }
9519 }
9520 // for each class method name
9521 for (const auto &nm : *Doxygen::memberNameLinkedMap)
9522 {
9523 // for each function with that name
9524 for (const auto &imd : *nm)
9525 {
9526 MemberDefMutable *md = toMemberDefMutable(imd.get());
9527 if (md)
9528 {
9530 }
9531 }
9532 }
9533
9534}
9535
9536//----------------------------------------------------------------------------
9537// Returns TRUE if the entry and member definition have equal file names,
9538// otherwise FALSE.
9539
9540static bool haveEqualFileNames(const Entry *root, const MemberDef *md)
9541{
9542 if (const FileDef *fd = md->getFileDef())
9543 {
9544 return fd->absFilePath() == root->fileName;
9545 }
9546 return false;
9547}
9548
9549//----------------------------------------------------------------------------
9550
9551static void addDefineDoc(const Entry *root, MemberDefMutable *md)
9552{
9553 md->setDocumentation(root->doc,root->docFile,root->docLine);
9554 md->setDocsForDefinition(!root->proto);
9555 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9556 if (md->inbodyDocumentation().isEmpty())
9557 {
9559 }
9560 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
9561 {
9562 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
9563 md->setBodyDef(root->fileDef());
9564 }
9566 md->setMaxInitLines(root->initLines);
9568 md->setRefItems(root->sli);
9569 md->setRequirementReferences(root->rqli);
9570 md->addQualifiers(root->qualifiers);
9571 if (root->mGrpId!=-1) md->setMemberGroupId(root->mGrpId);
9572 addMemberToGroups(root,md);
9574}
9575
9576//----------------------------------------------------------------------------
9577
9579{
9580 if ((root->section.isDefineDoc() || root->section.isDefine()) && !root->name.isEmpty())
9581 {
9582 //printf("found define '%s' '%s' brief='%s' doc='%s'\n",
9583 // qPrint(root->name),qPrint(root->args),qPrint(root->brief),qPrint(root->doc));
9584
9585 if (root->tagInfo() && !root->name.isEmpty()) // define read from a tag file
9586 {
9587 auto md = createMemberDef(root->tagInfo()->tagName,1,1,
9588 "#define",root->name,root->args,QCString(),
9589 Protection::Public,Specifier::Normal,FALSE,Relationship::Member,MemberType::Define,
9590 ArgumentList(),ArgumentList(),"");
9591 auto mmd = toMemberDefMutable(md.get());
9592 mmd->setTagInfo(root->tagInfo());
9593 mmd->setLanguage(root->lang);
9594 mmd->addQualifiers(root->qualifiers);
9595 //printf("Searching for '%s' fd=%p\n",qPrint(filePathName),fd);
9596 mmd->setFileDef(root->parent()->fileDef());
9597 //printf("Adding member=%s\n",qPrint(md->name()));
9599 mn->push_back(std::move(md));
9600 }
9602 if (mn)
9603 {
9604 int count=0;
9605 for (const auto &md : *mn)
9606 {
9607 if (md->memberType()==MemberType::Define) count++;
9608 }
9609 if (count==1)
9610 {
9611 for (const auto &imd : *mn)
9612 {
9613 MemberDefMutable *md = toMemberDefMutable(imd.get());
9614 if (md && md->memberType()==MemberType::Define)
9615 {
9616 addDefineDoc(root,md);
9617 }
9618 }
9619 }
9620 else if (count>1 &&
9621 (!root->doc.isEmpty() ||
9622 !root->brief.isEmpty() ||
9623 root->bodyLine!=-1
9624 )
9625 )
9626 // multiple defines don't know where to add docs
9627 // but maybe they are in different files together with their documentation
9628 {
9629 for (const auto &imd : *mn)
9630 {
9631 MemberDefMutable *md = toMemberDefMutable(imd.get());
9632 if (md && md->memberType()==MemberType::Define)
9633 {
9634 if (haveEqualFileNames(root, md) || isEntryInGroupOfMember(root, md))
9635 // doc and define in the same file or group assume they belong together.
9636 {
9637 addDefineDoc(root,md);
9638 }
9639 }
9640 }
9641 //warn("define {} found in the following files:\n",root->name);
9642 //warn("Cannot determine where to add the documentation found "
9643 // "at line {} of file {}. \n",
9644 // root->startLine,root->fileName);
9645 }
9646 }
9647 else if (!root->doc.isEmpty() || !root->brief.isEmpty()) // define not found
9648 {
9649 bool preEnabled = Config_getBool(ENABLE_PREPROCESSING);
9650 if (preEnabled)
9651 {
9652 warn(root->fileName,root->startLine,"documentation for unknown define {} found.",root->name);
9653 }
9654 else
9655 {
9656 warn(root->fileName,root->startLine, "found documented #define {} but ignoring it because ENABLE_PREPROCESSING is NO.", root->name);
9657 }
9658 }
9659 }
9660 for (const auto &e : root->children()) findDefineDocumentation(e.get());
9661}
9662
9663//----------------------------------------------------------------------------
9664
9665static void findDirDocumentation(const Entry *root)
9666{
9667 if (root->section.isDirDoc())
9668 {
9669 QCString normalizedName = root->name;
9670 normalizedName = substitute(normalizedName,"\\","/");
9671 //printf("root->docFile=%s normalizedName=%s\n",
9672 // qPrint(root->docFile),qPrint(normalizedName));
9673 if (root->docFile==normalizedName) // current dir?
9674 {
9675 int lastSlashPos=normalizedName.findRev('/');
9676 if (lastSlashPos!=-1) // strip file name
9677 {
9678 normalizedName=normalizedName.left(lastSlashPos);
9679 }
9680 }
9681 if (normalizedName.at(normalizedName.length()-1)!='/')
9682 {
9683 normalizedName+='/';
9684 }
9685 DirDef *matchingDir=nullptr;
9686 for (const auto &dir : *Doxygen::dirLinkedMap)
9687 {
9688 //printf("Dir: %s<->%s\n",qPrint(dir->name()),qPrint(normalizedName));
9689 if (dir->name().right(normalizedName.length())==normalizedName)
9690 {
9691 if (matchingDir)
9692 {
9693 warn(root->fileName,root->startLine,
9694 "\\dir command matches multiple directories.\n"
9695 " Applying the command for directory {}\n"
9696 " Ignoring the command for directory {}",
9697 matchingDir->name(),dir->name()
9698 );
9699 }
9700 else
9701 {
9702 matchingDir=dir.get();
9703 }
9704 }
9705 }
9706 if (matchingDir)
9707 {
9708 //printf("Match for with dir %s #anchor=%zu\n",qPrint(matchingDir->name()),root->anchors.size());
9709 matchingDir->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9710 matchingDir->setDocumentation(root->doc,root->docFile,root->docLine);
9711 matchingDir->setRefItems(root->sli);
9712 matchingDir->setRequirementReferences(root->rqli);
9713 matchingDir->addSectionsToDefinition(root->anchors);
9714 root->commandOverrides.apply_directoryGraph([&](bool b) { matchingDir->overrideDirectoryGraph(b); });
9715 addDirToGroups(root,matchingDir);
9716 }
9717 else
9718 {
9719 warn(root->fileName,root->startLine,"No matching directory found for command \\dir {}",normalizedName);
9720 }
9721 }
9722 for (const auto &e : root->children()) findDirDocumentation(e.get());
9723}
9724
9725//----------------------------------------------------------------------------
9727{
9728 if (root->section.isRequirementDoc())
9729 {
9731 }
9732 for (const auto &e : root->children()) buildRequirementsList(e.get());
9733}
9734
9735//----------------------------------------------------------------------------
9736// create a (sorted) list of separate documentation pages
9737
9738static void buildPageList(Entry *root)
9739{
9740 if (root->section.isPageDoc())
9741 {
9742 if (!root->name.isEmpty())
9743 {
9744 addRelatedPage(root);
9745 }
9746 }
9747 else if (root->section.isMainpageDoc())
9748 {
9749 QCString title=root->args.stripWhiteSpace();
9750 if (title.isEmpty()) title=theTranslator->trMainPage();
9751 //QCString name = Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9752 QCString name = "index";
9753 addRefItem(root->sli,
9754 name,
9755 theTranslator->trPage(TRUE,TRUE),
9756 name,
9757 title,
9758 QCString(),nullptr
9759 );
9760 }
9761 for (const auto &e : root->children()) buildPageList(e.get());
9762}
9763
9764// search for the main page defined in this project
9765static void findMainPage(Entry *root)
9766{
9767 if (root->section.isMainpageDoc())
9768 {
9769 if (Doxygen::mainPage==nullptr && root->tagInfo()==nullptr)
9770 {
9771 //printf("mainpage: docLine=%d startLine=%d\n",root->docLine,root->startLine);
9772 //printf("Found main page! \n======\n%s\n=======\n",qPrint(root->doc));
9773 QCString title=root->args.stripWhiteSpace();
9774 if (title.isEmpty()) title = Config_getString(PROJECT_NAME);
9775 //QCString indexName=Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9776 QCString indexName="index";
9778 indexName, root->brief+root->doc+root->inbodyDocs,title);
9779 //setFileNameForSections(root->anchors,"index",Doxygen::mainPage);
9780 Doxygen::mainPage->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9781 Doxygen::mainPage->setBodySegment(root->startLine,root->startLine,-1);
9782 Doxygen::mainPage->setFileName(indexName);
9783 Doxygen::mainPage->setLocalToc(root->localToc);
9785
9787 if (si)
9788 {
9789 if (!si->ref().isEmpty()) // we are from a tag file
9790 {
9791 // a page name is a label as well! but should no be double either
9793 Doxygen::mainPage->name(),
9794 indexName,
9795 root->startLine,
9796 Doxygen::mainPage->title(),
9798 0); // level 0
9799 }
9800 else if (si->lineNr() != -1)
9801 {
9802 warn(root->fileName,root->startLine,"multiple use of section label '{}' for main page, (first occurrence: {}, line {})",
9803 Doxygen::mainPage->name(),si->fileName(),si->lineNr());
9804 }
9805 else
9806 {
9807 warn(root->fileName,root->startLine,"multiple use of section label '{}' for main page, (first occurrence: {})",
9808 Doxygen::mainPage->name(),si->fileName());
9809 }
9810 }
9811 else
9812 {
9813 // a page name is a label as well! but should no be double either
9815 Doxygen::mainPage->name(),
9816 indexName,
9817 root->startLine,
9818 Doxygen::mainPage->title(),
9820 0); // level 0
9821 }
9822 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9823 }
9824 else if (root->tagInfo()==nullptr)
9825 {
9826 warn(root->fileName,root->startLine,
9827 "found more than one \\mainpage comment block! (first occurrence: {}, line {}), Skipping current block!",
9828 Doxygen::mainPage->docFile(),Doxygen::mainPage->getStartBodyLine());
9829 }
9830 }
9831 for (const auto &e : root->children()) findMainPage(e.get());
9832}
9833
9834// search for the main page imported via tag files and add only the section labels
9835static void findMainPageTagFiles(Entry *root)
9836{
9837 if (root->section.isMainpageDoc())
9838 {
9839 if (Doxygen::mainPage && root->tagInfo())
9840 {
9841 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9842 }
9843 }
9844 for (const auto &e : root->children()) findMainPageTagFiles(e.get());
9845}
9846
9847static void computePageRelations(Entry *root)
9848{
9849 if ((root->section.isPageDoc() || root->section.isMainpageDoc()) && !root->name.isEmpty())
9850 {
9851 PageDef *pd = root->section.isPageDoc() ?
9852 Doxygen::pageLinkedMap->find(root->name) :
9853 Doxygen::mainPage.get();
9854 if (pd)
9855 {
9856 for (const BaseInfo &bi : root->extends)
9857 {
9858 PageDef *subPd = Doxygen::pageLinkedMap->find(bi.name);
9859 if (pd==subPd)
9860 {
9861 term("page defined {} with label {} is a direct "
9862 "subpage of itself! Please remove this cyclic dependency.\n",
9863 warn_line(pd->docFile(),pd->docLine()),pd->name());
9864 }
9865 else if (subPd)
9866 {
9867 pd->addInnerCompound(subPd);
9868 //printf("*** Added subpage relation: %s->%s\n",
9869 // qPrint(pd->name()),qPrint(subPd->name()));
9870 }
9871 }
9872 }
9873 }
9874 for (const auto &e : root->children()) computePageRelations(e.get());
9875}
9876
9878{
9879 for (const auto &pd : *Doxygen::pageLinkedMap)
9880 {
9881 Definition *ppd = pd->getOuterScope();
9882 while (ppd)
9883 {
9884 if (ppd==pd.get())
9885 {
9886 term("page defined {} with label {} is a subpage "
9887 "of itself! Please remove this cyclic dependency.\n",
9888 warn_line(pd->docFile(),pd->docLine()),pd->name());
9889 }
9890 ppd=ppd->getOuterScope();
9891 }
9892 }
9893}
9894
9895//----------------------------------------------------------------------------
9896
9898{
9899 for (const auto &si : SectionManager::instance())
9900 {
9901 //printf("si->label='%s' si->definition=%s si->fileName='%s'\n",
9902 // qPrint(si->label),si->definition?qPrint(si->definition->name()):"<none>",
9903 // qPrint(si->fileName));
9904 PageDef *pd=nullptr;
9905
9906 // hack: the items of a todo/test/bug/deprecated list are all fragments from
9907 // different files, so the resulting section's all have the wrong file
9908 // name (not from the todo/test/bug/deprecated list, but from the file in
9909 // which they are defined). We correct this here by looking at the
9910 // generated section labels!
9912 {
9913 QCString label="_"+rl->listName(); // "_todo", "_test", ...
9914 if (si->label().left(label.length())==label)
9915 {
9916 si->setFileName(rl->listName());
9917 si->setGenerated(TRUE);
9918 break;
9919 }
9920 }
9921
9922 //printf("start: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
9923 if (!si->generated())
9924 {
9925 // if this section is in a page and the page is in a group, then we
9926 // have to adjust the link file name to point to the group.
9927 if (!si->fileName().isEmpty() &&
9928 (pd=Doxygen::pageLinkedMap->find(si->fileName())) &&
9929 pd->getGroupDef())
9930 {
9931 si->setFileName(pd->getGroupDef()->getOutputFileBase());
9932 }
9933
9934 if (si->definition())
9935 {
9936 // TODO: there should be one function in Definition that returns
9937 // the file to link to, so we can avoid the following tests.
9938 const GroupDef *gd=nullptr;
9939 if (si->definition()->definitionType()==Definition::TypeMember)
9940 {
9941 gd = (toMemberDef(si->definition()))->getGroupDef();
9942 }
9943
9944 if (gd)
9945 {
9946 si->setFileName(gd->getOutputFileBase());
9947 }
9948 else
9949 {
9950 //si->fileName=si->definition->getOutputFileBase();
9951 //printf("Setting si->fileName to %s\n",qPrint(si->fileName));
9952 }
9953 }
9954 }
9955 //printf("end: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
9956 }
9957}
9958
9959
9960
9961//----------------------------------------------------------------------------
9962// generate all separate documentation pages
9963
9964
9965static void generatePageDocs()
9966{
9967 //printf("documentedPages=%d real=%d\n",documentedPages,Doxygen::pageLinkedMap->count());
9968 if (Index::instance().numDocumentedPages()==0) return;
9969 for (const auto &pd : *Doxygen::pageLinkedMap)
9970 {
9971 if (!pd->getGroupDef() && !pd->isReference())
9972 {
9973 msg("Generating docs for page {}...\n",pd->name());
9974 pd->writeDocumentation(*g_outputList);
9975 }
9976 }
9977}
9978
9979//----------------------------------------------------------------------------
9980// create a (sorted) list & dictionary of example pages
9981
9982static void buildExampleList(Entry *root)
9983{
9984 if ((root->section.isExample() || root->section.isExampleLineno()) && !root->name.isEmpty())
9985 {
9986 if (Doxygen::exampleLinkedMap->find(root->name))
9987 {
9988 warn(root->fileName,root->startLine,"Example {} was already documented. Ignoring documentation found here.",root->name);
9989 }
9990 else
9991 {
9992 PageDef *pd = Doxygen::exampleLinkedMap->add(root->name,
9993 createPageDef(root->fileName,root->startLine,
9994 root->name,root->brief+root->doc+root->inbodyDocs,root->args));
9995 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9996 pd->setFileName(convertNameToFile(pd->name()+"-example",FALSE,TRUE));
9998 pd->setLanguage(root->lang);
9999 pd->setShowLineNo(root->section.isExampleLineno());
10000
10001 //we don't add example to groups
10002 //addExampleToGroups(root,pd);
10003 }
10004 }
10005 for (const auto &e : root->children()) buildExampleList(e.get());
10006}
10007
10008//----------------------------------------------------------------------------
10009// prints the Entry tree (for debugging)
10010
10011void printNavTree(Entry *root,int indent)
10012{
10014 {
10015 QCString indentStr;
10016 indentStr.fill(' ',indent);
10017 Debug::print(Debug::Entries,0,"{}{} at {}:{} (sec={}, spec={})\n",
10018 indentStr.isEmpty()?"":indentStr,
10019 root->name.isEmpty()?"<empty>":root->name,
10020 root->fileName,root->startLine,
10021 root->section.to_string(),
10022 root->spec.to_string());
10023 for (const auto &e : root->children())
10024 {
10025 printNavTree(e.get(),indent+2);
10026 }
10027 }
10028}
10029
10030
10031//----------------------------------------------------------------------------
10032// prints the Sections tree (for debugging)
10033
10035{
10037 {
10038 for (const auto &si : SectionManager::instance())
10039 {
10040 Debug::print(Debug::Sections,0,"Section = {}, file = {}, title = {}, type = {}, ref = {}\n",
10041 si->label(),si->fileName(),si->title(),si->type().level(),si->ref());
10042 }
10043 }
10044}
10045
10046
10047//----------------------------------------------------------------------------
10048// generate the example documentation
10049
10051{
10052 g_outputList->disable(OutputType::Man);
10053 for (const auto &pd : *Doxygen::exampleLinkedMap)
10054 {
10055 msg("Generating docs for example {}...\n",pd->name());
10056 SrcLangExt lang = getLanguageFromFileName(pd->name(), SrcLangExt::Unknown);
10057 if (lang != SrcLangExt::Unknown)
10058 {
10059 QCString ext = getFileNameExtension(pd->name());
10060 auto intf = Doxygen::parserManager->getCodeParser(ext);
10061 intf->resetCodeParserState();
10062 }
10063 QCString n=pd->getOutputFileBase();
10064 startFile(*g_outputList,n,false,n,pd->name());
10066 g_outputList->docify(pd->name());
10068 g_outputList->startContents();
10069 QCString lineNoOptStr;
10070 if (pd->showLineNo())
10071 {
10072 lineNoOptStr="{lineno}";
10073 }
10074 g_outputList->generateDoc(pd->docFile(), // file
10075 pd->docLine(), // startLine
10076 pd.get(), // context
10077 nullptr, // memberDef
10078 (pd->briefDescription().isEmpty()?"":pd->briefDescription()+"\n\n")+
10079 pd->documentation()+"\n\n\\include"+lineNoOptStr+" "+pd->name(), // docs
10080 DocOptions()
10081 .setIndexWords(true)
10082 .setExample(pd->name()));
10083 endFile(*g_outputList); // contains g_outputList->endContents()
10084 }
10086}
10087
10088//----------------------------------------------------------------------------
10089// generate module pages
10090
10092{
10093 for (const auto &gd : *Doxygen::groupLinkedMap)
10094 {
10095 if (!gd->isReference())
10096 {
10097 gd->writeDocumentation(*g_outputList);
10098 }
10099 }
10100}
10101
10102//----------------------------------------------------------------------------
10103// generate module pages
10104
10106{
10107 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10108 if (numThreads>1) // multi threaded processing
10109 {
10110 struct DocContext
10111 {
10112 DocContext(ClassDefMutable *cdm_,const OutputList &ol_)
10113 : cdm(cdm_), ol(ol_) {}
10114 ClassDefMutable *cdm;
10115 OutputList ol;
10116 };
10117 ThreadPool threadPool(numThreads);
10118 std::vector< std::future< std::shared_ptr<DocContext> > > results;
10119 // for each class in the namespace...
10120 for (const auto &cd : classList)
10121 {
10123 if (cdm)
10124 {
10125 auto ctx = std::make_shared<DocContext>(cdm,*g_outputList);
10126 auto processFile = [ctx]()
10127 {
10128 if ( ( ctx->cdm->isLinkableInProject() &&
10129 !ctx->cdm->isImplicitTemplateInstance()
10130 ) // skip external references, anonymous compounds and
10131 // template instances and nested classes
10132 && !ctx->cdm->isHidden() && !ctx->cdm->isEmbeddedInOuterScope()
10133 )
10134 {
10135 msg("Generating docs for compound {}...\n",ctx->cdm->displayName());
10136 ctx->cdm->writeDocumentation(ctx->ol);
10137 ctx->cdm->writeMemberList(ctx->ol);
10138 }
10139 ctx->cdm->writeDocumentationForInnerClasses(ctx->ol);
10140 return ctx;
10141 };
10142 results.emplace_back(threadPool.queue(processFile));
10143 }
10144 }
10145 // wait for the results
10146 for (auto &f : results)
10147 {
10148 auto ctx = f.get();
10149 }
10150 }
10151 else // single threaded processing
10152 {
10153 // for each class in the namespace...
10154 for (const auto &cd : classList)
10155 {
10157 if (cdm)
10158 {
10159 if ( ( cd->isLinkableInProject() &&
10160 !cd->isImplicitTemplateInstance()
10161 ) // skip external references, anonymous compounds and
10162 // template instances and nested classes
10163 && !cd->isHidden() && !cd->isEmbeddedInOuterScope()
10164 )
10165 {
10166 msg("Generating docs for compound {}...\n",cd->displayName());
10167
10170 }
10172 }
10173 }
10174 }
10175}
10176
10178{
10179 // for each concept in the namespace...
10180 for (const auto &cd : conceptList)
10181 {
10183 if ( cdm && cd->isLinkableInProject() && !cd->isHidden())
10184 {
10185 msg("Generating docs for concept {}...\n",cd->name());
10187 }
10188 }
10189}
10190
10192{
10193 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
10194
10195 //writeNamespaceIndex(*g_outputList);
10196
10197 // for each namespace...
10198 for (const auto &nd : *Doxygen::namespaceLinkedMap)
10199 {
10200 if (nd->isLinkableInProject())
10201 {
10203 if (ndm)
10204 {
10205 msg("Generating docs for namespace {}\n",nd->displayName());
10207 }
10208 }
10209
10210 generateNamespaceClassDocs(nd->getClasses());
10211 if (sliceOpt)
10212 {
10213 generateNamespaceClassDocs(nd->getInterfaces());
10214 generateNamespaceClassDocs(nd->getStructs());
10215 generateNamespaceClassDocs(nd->getExceptions());
10216 }
10217 generateNamespaceConceptDocs(nd->getConcepts());
10218 }
10219}
10220
10222{
10223 std::string oldDir = Dir::currentDirPath();
10224 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
10227 {
10228 err("failed to run html help compiler on {}\n", HtmlHelp::hhpFileName);
10229 }
10230 Dir::setCurrent(oldDir);
10231}
10232
10234{
10235 QCString args = Qhp::qhpFileName + " -o \"" + Qhp::getQchFileName() + "\"";
10236 std::string oldDir = Dir::currentDirPath();
10237 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
10238
10239 QCString qhgLocation=Config_getString(QHG_LOCATION);
10240 if (Debug::isFlagSet(Debug::Qhp)) // produce info for debugging
10241 {
10242 // run qhelpgenerator -v and extract the Qt version used
10243 QCString cmd=qhgLocation+ " -v 2>&1";
10244 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
10245 FILE *f=Portable::popen(cmd,"r");
10246 if (!f)
10247 {
10248 err("could not execute {}\n",qhgLocation);
10249 }
10250 else
10251 {
10252 const size_t bufSize = 1024;
10253 char inBuf[bufSize+1];
10254 size_t numRead=fread(inBuf,1,bufSize,f);
10255 inBuf[numRead] = '\0';
10256 Debug::print(Debug::Qhp,0,"{}",inBuf);
10258
10259 int qtVersion=0;
10260 static const reg::Ex versionReg(R"(Qt (\d+)\.(\d+)\.(\d+))");
10261 reg::Match match;
10262 std::string s = inBuf;
10263 if (reg::search(s,match,versionReg))
10264 {
10265 qtVersion = 10000*QCString(match[1].str()).toInt() +
10266 100*QCString(match[2].str()).toInt() +
10267 QCString(match[3].str()).toInt();
10268 }
10269 if (qtVersion>0 && (qtVersion<60000 || qtVersion >= 60205))
10270 {
10271 // dump the output of qhelpgenerator -c file.qhp
10272 // Qt<6 or Qt>=6.2.5 or higher, see https://bugreports.qt.io/browse/QTBUG-101070
10273 cmd=qhgLocation+ " -c " + Qhp::qhpFileName + " 2>&1";
10274 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
10275 f=Portable::popen(cmd,"r");
10276 if (!f)
10277 {
10278 err("could not execute {}\n",qhgLocation);
10279 }
10280 else
10281 {
10282 std::string output;
10283 while ((numRead=fread(inBuf,1,bufSize,f))>0)
10284 {
10285 inBuf[numRead] = '\0';
10286 output += inBuf;
10287 }
10289 Debug::print(Debug::Qhp,0,"{}",output);
10290 }
10291 }
10292 }
10293 }
10294
10295 if (Portable::system(qhgLocation, args, FALSE))
10296 {
10297 err("failed to run qhelpgenerator on {}\n",Qhp::qhpFileName);
10298 }
10299 Dir::setCurrent(oldDir);
10300}
10301
10302//----------------------------------------------------------------------------
10303
10305{
10306 // check dot path
10307 QCString dotPath = Config_getString(DOT_PATH);
10308 if (!dotPath.isEmpty())
10309 {
10310 FileInfo fi(dotPath.str());
10311 if (!(fi.exists() && fi.isFile()) )// not an existing user specified path + exec
10312 {
10313 dotPath = dotPath+"/dot"+Portable::commandExtension();
10314 FileInfo dp(dotPath.str());
10315 if (!dp.exists() || !dp.isFile())
10316 {
10317 warn_uncond("the dot tool could not be found as '{}'\n",dotPath);
10318 dotPath = "dot";
10319 dotPath += Portable::commandExtension();
10320 }
10321 }
10322#if defined(_WIN32) // convert slashes
10323 size_t l=dotPath.length();
10324 for (size_t i=0;i<l;i++) if (dotPath.at(i)=='/') dotPath.at(i)='\\';
10325#endif
10326 }
10327 else
10328 {
10329 dotPath = "dot";
10330 dotPath += Portable::commandExtension();
10331 }
10332 Doxygen::verifiedDotPath = dotPath;
10334}
10335
10336//----------------------------------------------------------------------------
10337
10338/*! Generate a template version of the configuration file.
10339 * If the \a shortList parameter is TRUE a configuration file without
10340 * comments will be generated.
10341 */
10342static void generateConfigFile(const QCString &configFile,bool shortList,
10343 bool updateOnly=FALSE)
10344{
10345 std::ofstream f;
10346 bool fileOpened=openOutputFile(configFile,f);
10347 bool writeToStdout=configFile=="-";
10348 if (fileOpened)
10349 {
10350 TextStream t(&f);
10351 Config::writeTemplate(t,shortList,updateOnly);
10352 if (!writeToStdout)
10353 {
10354 if (!updateOnly)
10355 {
10356 msg("\n\nConfiguration file '{}' created.\n\n",configFile);
10357 msg("Now edit the configuration file and enter\n\n");
10358 if (configFile!="Doxyfile" && configFile!="doxyfile")
10359 msg(" doxygen {}\n\n",configFile);
10360 else
10361 msg(" doxygen\n\n");
10362 msg("to generate the documentation for your project\n\n");
10363 }
10364 else
10365 {
10366 msg("\n\nConfiguration file '{}' updated.\n\n",configFile);
10367 }
10368 }
10369 }
10370 else
10371 {
10372 term("Cannot open file {} for writing\n",configFile);
10373 }
10374}
10375
10377{
10378 std::ofstream f;
10379 bool fileOpened=openOutputFile("-",f);
10380 if (fileOpened)
10381 {
10382 TextStream t(&f);
10383 Config::compareDoxyfile(t,diffList);
10384 }
10385 else
10386 {
10387 term("Cannot open stdout for writing\n");
10388 }
10389}
10390
10391//----------------------------------------------------------------------------
10392// read and parse a tag file
10393
10394static void readTagFile(const std::shared_ptr<Entry> &root,const QCString &tagLine)
10395{
10396 QCString fileName;
10397 QCString destName;
10398 int eqPos = tagLine.find('=');
10399 if (eqPos!=-1) // tag command contains a destination
10400 {
10401 fileName = tagLine.left(eqPos).stripWhiteSpace();
10402 destName = tagLine.right(tagLine.length()-eqPos-1).stripWhiteSpace();
10403 if (fileName.isEmpty() || destName.isEmpty()) return;
10404 //printf("insert tagDestination %s->%s\n",qPrint(fi.fileName()),qPrint(destName));
10405 }
10406 else
10407 {
10408 fileName = tagLine;
10409 }
10410
10411 FileInfo fi(fileName.str());
10412 if (!fi.exists() || !fi.isFile())
10413 {
10414 err("Tag file '{}' does not exist or is not a file. Skipping it...\n",fileName);
10415 return;
10416 }
10417
10418 if (Doxygen::tagFileSet.find(fi.absFilePath()) != Doxygen::tagFileSet.end()) return;
10419
10420 Doxygen::tagFileSet.emplace(fi.absFilePath());
10421
10422 if (!destName.isEmpty())
10423 {
10424 Doxygen::tagDestinationMap.emplace(fi.absFilePath(), destName.str());
10425 msg("Reading tag file '{}', location '{}'...\n",fileName,destName);
10426 }
10427 else
10428 {
10429 msg("Reading tag file '{}'...\n",fileName);
10430 }
10431
10432 parseTagFile(root,fi.absFilePath().c_str());
10433}
10434
10435//----------------------------------------------------------------------------
10437{
10438 const StringVector &latexExtraStyleSheet = Config_getList(LATEX_EXTRA_STYLESHEET);
10439 for (const auto &sheet : latexExtraStyleSheet)
10440 {
10441 std::string fileName = sheet;
10442 if (!fileName.empty())
10443 {
10444 FileInfo fi(fileName);
10445 if (!fi.exists())
10446 {
10447 err("Style sheet '{}' specified by LATEX_EXTRA_STYLESHEET does not exist!\n",fileName);
10448 }
10449 else if (fi.isDir())
10450 {
10451 err("Style sheet '{}' specified by LATEX_EXTRA_STYLESHEET is a directory, it has to be a file!\n", fileName);
10452 }
10453 else
10454 {
10455 QCString destFileName = Config_getString(LATEX_OUTPUT)+"/"+fi.fileName();
10457 {
10458 destFileName += LATEX_STYLE_EXTENSION;
10459 }
10460 copyFile(fileName, destFileName);
10461 }
10462 }
10463 }
10464}
10465
10466//----------------------------------------------------------------------------
10467static void copyStyleSheet()
10468{
10469 QCString htmlStyleSheet = Config_getString(HTML_STYLESHEET);
10470 if (!htmlStyleSheet.isEmpty())
10471 {
10472 if (!htmlStyleSheet.startsWith("http:") && !htmlStyleSheet.startsWith("https:"))
10473 {
10474 FileInfo fi(htmlStyleSheet.str());
10475 if (!fi.exists())
10476 {
10477 err("Style sheet '{}' specified by HTML_STYLESHEET does not exist!\n",htmlStyleSheet);
10478 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
10479 }
10480 else if (fi.isDir())
10481 {
10482 err("Style sheet '{}' specified by HTML_STYLESHEET is a directory, it has to be a file!\n",htmlStyleSheet);
10483 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
10484 }
10485 else
10486 {
10487 QCString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
10488 copyFile(htmlStyleSheet,destFileName);
10489 }
10490 }
10491 }
10492 const StringVector &htmlExtraStyleSheet = Config_getList(HTML_EXTRA_STYLESHEET);
10493 for (const auto &sheet : htmlExtraStyleSheet)
10494 {
10495 QCString fileName(sheet);
10496 if (!fileName.isEmpty() && !fileName.startsWith("http:") && !fileName.startsWith("https:"))
10497 {
10498 FileInfo fi(fileName.str());
10499 if (!fi.exists())
10500 {
10501 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET does not exist!\n",fileName);
10502 }
10503 else if (fi.fileName()=="doxygen.css" || fi.fileName()=="tabs.css" || fi.fileName()=="navtree.css")
10504 {
10505 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET is already a built-in stylesheet. Please use a different name\n",fi.fileName());
10506 }
10507 else if (fi.isDir())
10508 {
10509 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET is a directory, it has to be a file!\n",fileName);
10510 }
10511 else
10512 {
10513 QCString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
10514 copyFile(fileName, destFileName);
10515 }
10516 }
10517 }
10518}
10519
10520static void copyLogo(const QCString &outputOption)
10521{
10522 QCString projectLogo = projectLogoFile();
10523 if (!projectLogo.isEmpty())
10524 {
10525 FileInfo fi(projectLogo.str());
10526 if (!fi.exists())
10527 {
10528 err("Project logo '{}' specified by PROJECT_LOGO does not exist!\n",projectLogo);
10529 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
10530 }
10531 else if (fi.isDir())
10532 {
10533 err("Project logo '{}' specified by PROJECT_LOGO is a directory, it has to be a file!\n",projectLogo);
10534 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
10535 }
10536 else
10537 {
10538 QCString destFileName = outputOption+"/"+fi.fileName();
10539 copyFile(projectLogo,destFileName);
10540 Doxygen::indexList->addImageFile(fi.fileName());
10541 }
10542 }
10543}
10544
10545static void copyIcon(const QCString &outputOption)
10546{
10547 QCString projectIcon = Config_getString(PROJECT_ICON);
10548 if (!projectIcon.isEmpty())
10549 {
10550 FileInfo fi(projectIcon.str());
10551 if (!fi.exists())
10552 {
10553 err("Project icon '{}' specified by PROJECT_ICON does not exist!\n",projectIcon);
10554 projectIcon = Config_updateString(PROJECT_ICON,""); // revert to the default
10555 }
10556 else if (fi.isDir())
10557 {
10558 err("Project icon '{}' specified by PROJECT_ICON is a directory, it has to be a file!\n",projectIcon);
10559 projectIcon = Config_updateString(PROJECT_ICON,""); // revert to the default
10560 }
10561 else
10562 {
10563 QCString destFileName = outputOption+"/"+fi.fileName();
10564 copyFile(projectIcon,destFileName);
10565 Doxygen::indexList->addImageFile(fi.fileName());
10566 }
10567 }
10568}
10569
10570static void copyExtraFiles(const StringVector &files,const QCString &filesOption,const QCString &outputOption)
10571{
10572 for (const auto &fileName : files)
10573 {
10574 if (!fileName.empty())
10575 {
10576 FileInfo fi(fileName);
10577 if (!fi.exists())
10578 {
10579 err("Extra file '{}' specified in {} does not exist!\n", fileName,filesOption);
10580 }
10581 else if (fi.isDir())
10582 {
10583 err("Extra file '{}' specified in {} is a directory, it has to be a file!\n", fileName,filesOption);
10584 }
10585 else
10586 {
10587 QCString destFileName = outputOption+"/"+fi.fileName();
10588 Doxygen::indexList->addImageFile(fi.fileName());
10589 copyFile(fileName, destFileName);
10590 }
10591 }
10592 }
10593}
10594
10595//----------------------------------------------------------------------------
10596
10598{
10599 for (const auto &fn : *Doxygen::inputNameLinkedMap)
10600 {
10601 struct FileEntry
10602 {
10603 FileEntry(const QCString &p,FileDef *fd) : path(p), fileDef(fd) {}
10604 QCString path;
10605 FileDef *fileDef;
10606 };
10607
10608 // collect the entry for which to compute the longest common prefix (LCP) of the path
10609 std::vector<FileEntry> fileEntries;
10610 for (const auto &fd : *fn)
10611 {
10612 if (!fd->isReference()) // skip external references
10613 {
10614 fileEntries.emplace_back(fd->getPath(),fd.get());
10615 }
10616 }
10617
10618 size_t size = fileEntries.size();
10619
10620 if (size==1) // name if unique, so diskname is simply the name
10621 {
10622 FileDef *fd = fileEntries[0].fileDef;
10623 fd->setDiskName(fn->fileName());
10624 }
10625 else if (size>1) // multiple occurrences of the same file name
10626 {
10627 // sort the array
10628 std::stable_sort(fileEntries.begin(),
10629 fileEntries.end(),
10630 [](const FileEntry &fe1,const FileEntry &fe2)
10631 { return qstricmp_sort(fe1.path,fe2.path)<0; }
10632 );
10633
10634 // since the entries are sorted, the common prefix of the whole array is same
10635 // as the common prefix between the first and last entry
10636 const FileEntry &first = fileEntries[0];
10637 const FileEntry &last = fileEntries[size-1];
10638 int first_path_size = static_cast<int>(first.path.size())-1; // -1 to skip trailing slash
10639 int last_path_size = static_cast<int>(last.path.size())-1; // -1 to skip trailing slash
10640 int j=0;
10641 int i=0;
10642 for (i=0;i<first_path_size && i<last_path_size;i++)
10643 {
10644 if (first.path[i]=='/') j=i;
10645 if (first.path[i]!=last.path[i]) break;
10646 }
10647 if (i==first_path_size && i<last_path_size && last.path[i]=='/')
10648 {
10649 // case first='some/path' and last='some/path/more' => match is 'some/path'
10650 j=first_path_size;
10651 }
10652 else if (i==last_path_size && i<first_path_size && first.path[i]=='/')
10653 {
10654 // case first='some/path/more' and last='some/path' => match is 'some/path'
10655 j=last_path_size;
10656 }
10657
10658 // add non-common part of the path to the name
10659 for (auto &fileEntry : fileEntries)
10660 {
10661 QCString prefix = fileEntry.path.right(fileEntry.path.length()-j-1);
10662 fileEntry.fileDef->setName(prefix+fn->fileName());
10663 //printf("!!!!!!!! non unique disk name=%s:%s\n",qPrint(prefix),fn->fileName());
10664 fileEntry.fileDef->setDiskName(prefix+fn->fileName());
10665 }
10666 }
10667 }
10668}
10669
10670
10671
10672//----------------------------------------------------------------------------
10673
10674static std::unique_ptr<OutlineParserInterface> getParserForFile(const QCString &fn)
10675{
10676 QCString fileName=fn;
10677 QCString extension;
10678 int sep = fileName.findRev('/');
10679 int ei = fileName.findRev('.');
10680 if (ei!=-1 && (sep==-1 || ei>sep)) // matches dir/file.ext but not dir.1/file
10681 {
10682 extension=fileName.right(fileName.length()-ei);
10683 }
10684 else
10685 {
10686 extension = ".no_extension";
10687 }
10688
10689 return Doxygen::parserManager->getOutlineParser(extension);
10690}
10691
10692static std::shared_ptr<Entry> parseFile(OutlineParserInterface &parser,
10693 FileDef *fd,const QCString &fn,
10694 ClangTUParser *clangParser,bool newTU)
10695{
10696 QCString fileName=fn;
10697 AUTO_TRACE("fileName={}",fileName);
10698 QCString extension;
10699 int ei = fileName.findRev('.');
10700 if (ei!=-1)
10701 {
10702 extension=fileName.right(fileName.length()-ei);
10703 }
10704 else
10705 {
10706 extension = ".no_extension";
10707 }
10708
10709 FileInfo fi(fileName.str());
10710 std::string preBuf;
10711
10712 if (Config_getBool(ENABLE_PREPROCESSING) &&
10713 parser.needsPreprocessing(extension))
10714 {
10715 Preprocessor preprocessor;
10716 const StringVector &includePath = Config_getList(INCLUDE_PATH);
10717 for (const auto &s : includePath)
10718 {
10719 std::string absPath = FileInfo(s).absFilePath();
10720 preprocessor.addSearchDir(absPath);
10721 }
10722 std::string inBuf;
10723 msg("Preprocessing {}...\n",fn);
10724 readInputFile(fileName,inBuf);
10725 addTerminalCharIfMissing(inBuf,'\n');
10726 preprocessor.processFile(fileName,inBuf,preBuf);
10727 }
10728 else // no preprocessing
10729 {
10730 msg("Reading {}...\n",fn);
10731 readInputFile(fileName,preBuf);
10732 addTerminalCharIfMissing(preBuf,'\n');
10733 }
10734
10735 std::string convBuf;
10736 convBuf.reserve(preBuf.size()+1024);
10737
10738 // convert multi-line C++ comments to C style comments
10739 convertCppComments(preBuf,convBuf,fileName.str());
10740
10741 std::shared_ptr<Entry> fileRoot = std::make_shared<Entry>();
10742 // use language parse to parse the file
10743 if (clangParser)
10744 {
10745 if (newTU) clangParser->parse();
10746 clangParser->switchToFile(fd);
10747 }
10748 parser.parseInput(fileName,convBuf.data(),fileRoot,clangParser);
10749 fileRoot->setFileDef(fd);
10750 return fileRoot;
10751}
10752
10753//! parse the list of input files
10754static void parseFilesMultiThreading(const std::shared_ptr<Entry> &root)
10755{
10756 AUTO_TRACE();
10757#if USE_LIBCLANG
10759 {
10760 StringUnorderedSet processedFiles;
10761
10762 // create a dictionary with files to process
10763 StringUnorderedSet filesToProcess;
10764 for (const auto &s : g_inputFiles)
10765 {
10766 filesToProcess.insert(s);
10767 }
10768
10769 std::mutex processedFilesLock;
10770 // process source files (and their include dependencies)
10771 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10772 msg("Processing input using {} threads.\n",numThreads);
10773 ThreadPool threadPool(numThreads);
10774 using FutureType = std::vector< std::shared_ptr<Entry> >;
10775 std::vector< std::future< FutureType > > results;
10776 for (const auto &s : g_inputFiles)
10777 {
10778 bool ambig = false;
10779 QCString qs = s;
10781 ASSERT(fd!=nullptr);
10782 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt::Cpp) // this is a source file
10783 {
10784 // lambda representing the work to executed by a thread
10785 auto processFile = [qs,&filesToProcess,&processedFilesLock,&processedFiles]() {
10786 bool ambig_l = false;
10787 std::vector< std::shared_ptr<Entry> > roots;
10789 auto clangParser = ClangParser::instance()->createTUParser(fd_l);
10790 auto parser = getParserForFile(qs);
10791 auto fileRoot { parseFile(*parser.get(),fd_l,qs,clangParser.get(),true) };
10792 roots.push_back(fileRoot);
10793
10794 // Now process any include files in the same translation unit
10795 // first. When libclang is used this is much more efficient.
10796 for (auto incFile : clangParser->filesInSameTU())
10797 {
10798 QCString qincFile = incFile;
10799 if (filesToProcess.find(incFile)!=filesToProcess.end())
10800 {
10801 bool needsToBeProcessed = false;
10802 {
10803 std::lock_guard<std::mutex> lock(processedFilesLock);
10804 needsToBeProcessed = processedFiles.find(incFile)==processedFiles.end();
10805 if (needsToBeProcessed) processedFiles.insert(incFile);
10806 }
10807 if (qincFile!=qs && needsToBeProcessed)
10808 {
10809 FileDef *ifd=findFileDef(Doxygen::inputNameLinkedMap,qincFile,ambig_l);
10810 if (ifd && !ifd->isReference())
10811 {
10812 //printf(" Processing %s in same translation unit as %s\n",incFile,qPrint(s));
10813 fileRoot = parseFile(*parser.get(),ifd,qincFile,clangParser.get(),false);
10814 roots.push_back(fileRoot);
10815 }
10816 }
10817 }
10818 }
10819 return roots;
10820 };
10821 // dispatch the work and collect the future results
10822 results.emplace_back(threadPool.queue(processFile));
10823 }
10824 }
10825 // synchronize with the Entry result lists produced and add them to the root
10826 for (auto &f : results)
10827 {
10828 auto l = f.get();
10829 for (auto &e : l)
10830 {
10831 root->moveToSubEntryAndKeep(e);
10832 }
10833 }
10834 // process remaining files
10835 results.clear();
10836 for (const auto &s : g_inputFiles)
10837 {
10838 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
10839 {
10840 // lambda representing the work to executed by a thread
10841 auto processFile = [s]() {
10842 bool ambig = false;
10843 QCString qs = s;
10844 std::vector< std::shared_ptr<Entry> > roots;
10846 auto parser { getParserForFile(qs) };
10847 bool useClang = getLanguageFromFileName(qs)==SrcLangExt::Cpp;
10848 if (useClang)
10849 {
10850 auto clangParser = ClangParser::instance()->createTUParser(fd);
10851 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
10852 roots.push_back(fileRoot);
10853 }
10854 else
10855 {
10856 auto fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10857 roots.push_back(fileRoot);
10858 }
10859 return roots;
10860 };
10861 results.emplace_back(threadPool.queue(processFile));
10862 }
10863 }
10864 // synchronize with the Entry result lists produced and add them to the root
10865 for (auto &f : results)
10866 {
10867 auto l = f.get();
10868 for (auto &e : l)
10869 {
10870 root->moveToSubEntryAndKeep(e);
10871 }
10872 }
10873 }
10874 else // normal processing
10875#endif
10876 {
10877 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10878 msg("Processing input using {} threads.\n",numThreads);
10879 ThreadPool threadPool(numThreads);
10880 using FutureType = std::shared_ptr<Entry>;
10881 std::vector< std::future< FutureType > > results;
10882 for (const auto &s : g_inputFiles)
10883 {
10884 // lambda representing the work to executed by a thread
10885 auto processFile = [s]() {
10886 bool ambig = false;
10887 QCString qs = s;
10889 auto parser = getParserForFile(qs);
10890 auto fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10891 return fileRoot;
10892 };
10893 // dispatch the work and collect the future results
10894 results.emplace_back(threadPool.queue(processFile));
10895 }
10896 // synchronize with the Entry results produced and add them to the root
10897 for (auto &f : results)
10898 {
10899 root->moveToSubEntryAndKeep(f.get());
10900 }
10901 }
10902}
10903
10904//! parse the list of input files
10905static void parseFilesSingleThreading(const std::shared_ptr<Entry> &root)
10906{
10907 AUTO_TRACE();
10908#if USE_LIBCLANG
10910 {
10911 StringUnorderedSet processedFiles;
10912
10913 // create a dictionary with files to process
10914 StringUnorderedSet filesToProcess;
10915 for (const auto &s : g_inputFiles)
10916 {
10917 filesToProcess.insert(s);
10918 }
10919
10920 // process source files (and their include dependencies)
10921 for (const auto &s : g_inputFiles)
10922 {
10923 bool ambig = false;
10924 QCString qs =s;
10926 ASSERT(fd!=nullptr);
10927 if (fd->isSource() && !fd->isReference() && getLanguageFromFileName(qs)==SrcLangExt::Cpp) // this is a source file
10928 {
10929 auto clangParser = ClangParser::instance()->createTUParser(fd);
10930 auto parser { getParserForFile(qs) };
10931 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
10932 root->moveToSubEntryAndKeep(fileRoot);
10933 processedFiles.insert(s);
10934
10935 // Now process any include files in the same translation unit
10936 // first. When libclang is used this is much more efficient.
10937 for (auto incFile : clangParser->filesInSameTU())
10938 {
10939 //printf(" file %s\n",qPrint(incFile));
10940 if (filesToProcess.find(incFile)!=filesToProcess.end() && // file need to be processed
10941 processedFiles.find(incFile)==processedFiles.end()) // and is not processed already
10942 {
10944 if (ifd && !ifd->isReference())
10945 {
10946 //printf(" Processing %s in same translation unit as %s\n",qPrint(incFile),qPrint(qs));
10947 fileRoot = parseFile(*parser.get(),ifd,incFile,clangParser.get(),false);
10948 root->moveToSubEntryAndKeep(fileRoot);
10949 processedFiles.insert(incFile);
10950 }
10951 }
10952 }
10953 }
10954 }
10955 // process remaining files
10956 for (const auto &s : g_inputFiles)
10957 {
10958 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
10959 {
10960 bool ambig = false;
10961 QCString qs = s;
10963 if (getLanguageFromFileName(qs)==SrcLangExt::Cpp) // not yet processed
10964 {
10965 auto clangParser = ClangParser::instance()->createTUParser(fd);
10966 auto parser { getParserForFile(qs) };
10967 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
10968 root->moveToSubEntryAndKeep(fileRoot);
10969 }
10970 else
10971 {
10972 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(qs) };
10973 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10974 root->moveToSubEntryAndKeep(fileRoot);
10975 }
10976 processedFiles.insert(s);
10977 }
10978 }
10979 }
10980 else // normal processing
10981#endif
10982 {
10983 for (const auto &s : g_inputFiles)
10984 {
10985 bool ambig = false;
10986 QCString qs = s;
10988 ASSERT(fd!=nullptr);
10989 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(qs) };
10990 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10991 root->moveToSubEntryAndKeep(std::move(fileRoot));
10992 }
10993 }
10994}
10995
10996// resolves a path that may include symlinks, if a recursive symlink is
10997// found an empty string is returned.
10998static std::string resolveSymlink(const std::string &path)
10999{
11000 int sepPos=0;
11001 int oldPos=0;
11002 StringUnorderedSet nonSymlinks;
11003 StringUnorderedSet known;
11004 QCString result(path);
11005 QCString oldPrefix = "/";
11006 do
11007 {
11008#if defined(_WIN32)
11009 // UNC path, skip server and share name
11010 if (sepPos==0 && (result.startsWith("//") || result.startsWith("\\\\")))
11011 sepPos = result.find('/',2);
11012 if (sepPos!=-1)
11013 sepPos = result.find('/',sepPos+1);
11014#else
11015 sepPos = result.find('/',sepPos+1);
11016#endif
11017 QCString prefix = sepPos==-1 ? result : result.left(sepPos);
11018 if (nonSymlinks.find(prefix.str())==nonSymlinks.end())
11019 {
11020 FileInfo fi(prefix.str());
11021 if (fi.isSymLink())
11022 {
11023 QCString target = fi.readLink();
11024 bool isRelative = FileInfo(target.str()).isRelative();
11025 if (isRelative)
11026 {
11027 target = Dir::cleanDirPath(oldPrefix.str()+"/"+target.str());
11028 }
11029 if (sepPos!=-1)
11030 {
11031 if (fi.isDir() && target.length()>0 && target.at(target.length()-1)!='/')
11032 {
11033 target+='/';
11034 }
11035 target+=result.mid(sepPos);
11036 }
11037 result = Dir::cleanDirPath(target.str());
11038 if (known.find(result.str())!=known.end()) return std::string(); // recursive symlink!
11039 known.insert(result.str());
11040 if (isRelative)
11041 {
11042 sepPos = oldPos;
11043 }
11044 else // link to absolute path
11045 {
11046 sepPos = 0;
11047 oldPrefix = "/";
11048 }
11049 }
11050 else
11051 {
11052 nonSymlinks.insert(prefix.str());
11053 oldPrefix = prefix;
11054 }
11055 oldPos = sepPos;
11056 }
11057 }
11058 while (sepPos!=-1);
11059 return Dir::cleanDirPath(result.str());
11060}
11061
11063
11064//----------------------------------------------------------------------------
11065// Read all files matching at least one pattern in 'patList' in the
11066// directory represented by 'fi'.
11067// The directory is read iff the recursiveFlag is set.
11068// The contents of all files is append to the input string
11069
11070static void readDir(FileInfo *fi,
11071 FileNameLinkedMap *fnMap,
11072 StringUnorderedSet *exclSet,
11073 const StringVector *patList,
11074 const StringVector *exclPatList,
11075 StringVector *resultList,
11076 StringUnorderedSet *resultSet,
11077 bool errorIfNotExist,
11078 bool recursive,
11079 StringUnorderedSet *killSet,
11080 StringUnorderedSet *paths
11081 )
11082{
11083 std::string dirName = fi->absFilePath();
11084 if (paths && !dirName.empty())
11085 {
11086 paths->insert(dirName);
11087 }
11088 //printf("%s isSymLink()=%d\n",qPrint(dirName),fi->isSymLink());
11089 if (fi->isSymLink())
11090 {
11091 dirName = resolveSymlink(dirName);
11092 if (dirName.empty())
11093 {
11094 //printf("RECURSIVE SYMLINK: %s\n",qPrint(dirName));
11095 return; // recursive symlink
11096 }
11097 }
11098
11099 if (g_pathsVisited.find(dirName)!=g_pathsVisited.end())
11100 {
11101 //printf("PATH ALREADY VISITED: %s\n",qPrint(dirName));
11102 return; // already visited path
11103 }
11104 g_pathsVisited.insert(dirName);
11105
11106 Dir dir(dirName);
11107 msg("Searching for files in directory {}\n", fi->absFilePath());
11108 //printf("killSet=%p count=%d\n",killSet,killSet ? (int)killSet->count() : -1);
11109
11110 StringVector dirResultList;
11111
11112 for (const auto &dirEntry : dir.iterator())
11113 {
11114 FileInfo cfi(dirEntry.path());
11115 auto checkPatterns = [&]() -> bool
11116 {
11117 return (patList==nullptr || patternMatch(cfi,*patList)) &&
11118 (exclPatList==nullptr || !patternMatch(cfi,*exclPatList)) &&
11119 (killSet==nullptr || killSet->find(cfi.absFilePath())==killSet->end());
11120 };
11121
11122 if (exclSet==nullptr || exclSet->find(cfi.absFilePath())==exclSet->end())
11123 { // file should not be excluded
11124 //printf("killSet->find(%s)\n",qPrint(cfi->absFilePath()));
11125 if (Config_getBool(EXCLUDE_SYMLINKS) && cfi.isSymLink())
11126 {
11127 }
11128 else if (!cfi.exists() || !cfi.isReadable())
11129 {
11130 if (errorIfNotExist && checkPatterns())
11131 {
11132 warn_uncond("source '{}' is not a readable file or directory... skipping.\n",cfi.absFilePath());
11133 }
11134 }
11135 else if (cfi.isFile() && checkPatterns())
11136 {
11137 std::string name=cfi.fileName();
11138 std::string path=cfi.dirPath()+"/";
11139 std::string fullName=path+name;
11140 if (fnMap)
11141 {
11142 auto fd = createFileDef(path,name);
11143 FileName *fn=nullptr;
11144 if (!name.empty())
11145 {
11146 fn = fnMap->add(name,fullName);
11147 fn->push_back(std::move(fd));
11148 }
11149 }
11150 dirResultList.push_back(fullName);
11151 if (resultSet) resultSet->insert(fullName);
11152 if (killSet) killSet->insert(fullName);
11153 }
11154 else if (recursive &&
11155 cfi.isDir() &&
11156 (exclPatList==nullptr || !patternMatch(cfi,*exclPatList)) &&
11157 cfi.fileName().at(0)!='.') // skip "." ".." and ".dir"
11158 {
11159 FileInfo acfi(cfi.absFilePath());
11160 readDir(&acfi,fnMap,exclSet,
11161 patList,exclPatList,&dirResultList,resultSet,errorIfNotExist,
11162 recursive,killSet,paths);
11163 }
11164 }
11165 }
11166 if (resultList && !dirResultList.empty())
11167 {
11168 // sort the resulting list to make the order platform independent.
11169 std::stable_sort(dirResultList.begin(),
11170 dirResultList.end(),
11171 [](const auto &f1,const auto &f2) { return qstricmp_sort(f1.c_str(),f2.c_str())<0; });
11172
11173 // append the sorted results to resultList
11174 resultList->insert(resultList->end(), dirResultList.begin(), dirResultList.end());
11175 }
11176}
11177
11178
11179//----------------------------------------------------------------------------
11180// read a file or all files in a directory and append their contents to the
11181// input string. The names of the files are appended to the 'fiList' list.
11182
11184 FileNameLinkedMap *fnMap,
11185 StringUnorderedSet *exclSet,
11186 const StringVector *patList,
11187 const StringVector *exclPatList,
11188 StringVector *resultList,
11189 StringUnorderedSet *resultSet,
11190 bool recursive,
11191 bool errorIfNotExist,
11192 StringUnorderedSet *killSet,
11193 StringUnorderedSet *paths
11194 )
11195{
11196 //printf("killSet count=%d\n",killSet ? (int)killSet->size() : -1);
11197 // strip trailing slashes
11198 if (s.isEmpty()) return;
11199
11200 g_pathsVisited.clear();
11201
11202 FileInfo fi(s.str());
11203 //printf("readFileOrDirectory(%s)\n",s);
11204 {
11205 if (exclSet==nullptr || exclSet->find(fi.absFilePath())==exclSet->end())
11206 {
11207 if (Config_getBool(EXCLUDE_SYMLINKS) && fi.isSymLink())
11208 {
11209 }
11210 else if (!fi.exists() || !fi.isReadable())
11211 {
11212 if (errorIfNotExist)
11213 {
11214 warn_uncond("source '{}' is not a readable file or directory... skipping.\n",s);
11215 }
11216 }
11217 else if (fi.isFile())
11218 {
11219 std::string dirPath = fi.dirPath(true);
11220 std::string filePath = fi.absFilePath();
11221 if (paths && !dirPath.empty())
11222 {
11223 paths->insert(dirPath);
11224 }
11225 //printf("killSet.find(%s)=%d\n",qPrint(fi.absFilePath()),killSet.find(fi.absFilePath())!=killSet.end());
11226 if (killSet==nullptr || killSet->find(filePath)==killSet->end())
11227 {
11228 std::string name=fi.fileName();
11229 if (fnMap)
11230 {
11231 auto fd = createFileDef(dirPath+"/",name);
11232 if (!name.empty())
11233 {
11234 FileName *fn = fnMap->add(name,filePath);
11235 fn->push_back(std::move(fd));
11236 }
11237 }
11238 if (resultList || resultSet)
11239 {
11240 if (resultList) resultList->push_back(filePath);
11241 if (resultSet) resultSet->insert(filePath);
11242 }
11243
11244 if (killSet) killSet->insert(fi.absFilePath());
11245 }
11246 }
11247 else if (fi.isDir()) // readable dir
11248 {
11249 readDir(&fi,fnMap,exclSet,patList,
11250 exclPatList,resultList,resultSet,errorIfNotExist,
11251 recursive,killSet,paths);
11252 }
11253 }
11254 }
11255}
11256
11257//----------------------------------------------------------------------------
11258
11260{
11261 QCString anchor;
11263 {
11264 MemberDef *md = toMemberDef(d);
11265 anchor=":"+md->anchor();
11266 }
11267 QCString scope;
11268 QCString fn = d->getOutputFileBase();
11271 {
11272 scope = fn;
11273 }
11274 t << "REPLACE INTO symbols (symbol_id,scope_id,name,file,line) VALUES('"
11275 << fn+anchor << "','"
11276 << scope << "','"
11277 << d->name() << "','"
11278 << d->getDefFileName() << "','"
11279 << d->getDefLine()
11280 << "');\n";
11281}
11282
11283static void dumpSymbolMap()
11284{
11285 std::ofstream f = Portable::openOutputStream("symbols.sql");
11286 if (f.is_open())
11287 {
11288 TextStream t(&f);
11289 for (const auto &[name,symList] : *Doxygen::symbolMap)
11290 {
11291 for (const auto &def : symList)
11292 {
11293 dumpSymbol(t,def);
11294 }
11295 }
11296 }
11297}
11298
11299// print developer options of Doxygen
11300static void devUsage()
11301{
11303 msg("Developer parameters:\n");
11304 msg(" -m dump symbol map\n");
11305 msg(" -b making messages output unbuffered\n");
11306 msg(" -c <file> process input file as a comment block and produce HTML output\n");
11307#if ENABLE_TRACING
11308 msg(" -t [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout)\n");
11309 msg(" -t_time [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout),\n"
11310 " and include time and thread information\n");
11311#endif
11312 msg(" -d <level> enable a debug level, such as (multiple invocations of -d are possible):\n");
11314}
11315
11316
11317//----------------------------------------------------------------------------
11318// print the version of Doxygen
11319
11320static void version(const bool extended)
11321{
11323 QCString versionString = getFullVersion();
11324 msg("{}\n",versionString);
11325 if (extended)
11326 {
11327 QCString extVers;
11328 if (!extVers.isEmpty()) extVers+= ", ";
11329 extVers += "sqlite3 ";
11330 extVers += sqlite3_libversion();
11331#if USE_LIBCLANG
11332 if (!extVers.isEmpty()) extVers+= ", ";
11333 extVers += "clang support ";
11334 extVers += CLANG_VERSION_STRING;
11335#endif
11336 if (!extVers.isEmpty())
11337 {
11338 int lastComma = extVers.findRev(',');
11339 if (lastComma != -1) extVers = extVers.replace(lastComma,1," and");
11340 msg(" with {}.\n",extVers);
11341 }
11342 }
11343}
11344
11345//----------------------------------------------------------------------------
11346// print the usage of Doxygen
11347
11348static void usage(const QCString &name,const QCString &versionString)
11349{
11351 msg("Doxygen version {0}\nCopyright Dimitri van Heesch 1997-2025\n\n"
11352 "You can use Doxygen in a number of ways:\n\n"
11353 "1) Use Doxygen to generate a template configuration file*:\n"
11354 " {1} [-s] -g [configName]\n\n"
11355 "2) Use Doxygen to update an old configuration file*:\n"
11356 " {1} [-s] -u [configName]\n\n"
11357 "3) Use Doxygen to generate documentation using an existing "
11358 "configuration file*:\n"
11359 " {1} [configName]\n\n"
11360 "4) Use Doxygen to generate a template file controlling the layout of the\n"
11361 " generated documentation:\n"
11362 " {1} -l [layoutFileName]\n\n"
11363 " In case layoutFileName is omitted DoxygenLayout.xml will be used as filename.\n"
11364 " If - is used for layoutFileName Doxygen will write to standard output.\n\n"
11365 "5) Use Doxygen to generate a template style sheet file for RTF, HTML or Latex.\n"
11366 " RTF: {1} -w rtf styleSheetFile\n"
11367 " HTML: {1}-w html headerFile footerFile styleSheetFile [configFile]\n"
11368 " LaTeX: {1} -w latex headerFile footerFile styleSheetFile [configFile]\n\n"
11369 "6) Use Doxygen to generate a rtf extensions file\n"
11370 " {1} -e rtf extensionsFile\n\n"
11371 " If - is used for extensionsFile Doxygen will write to standard output.\n\n"
11372 "7) Use Doxygen to compare the used configuration file with the template configuration file\n"
11373 " {1} -x [configFile]\n\n"
11374 " Use Doxygen to compare the used configuration file with the template configuration file\n"
11375 " without replacing the environment variables or CMake type replacement variables\n"
11376 " {1} -x_noenv [configFile]\n\n"
11377 "8) Use Doxygen to show a list of built-in emojis.\n"
11378 " {1} -f emoji outputFileName\n\n"
11379 " If - is used for outputFileName Doxygen will write to standard output.\n\n"
11380 "*) If -s is specified the comments of the configuration items in the config file will be omitted.\n"
11381 " If configName is omitted 'Doxyfile' will be used as a default.\n"
11382 " If - is used for configFile Doxygen will write / read the configuration to /from standard output / input.\n\n"
11383 "If -q is used for a Doxygen documentation run, Doxygen will see this as if QUIET=YES has been set.\n\n"
11384 "-v print version string, -V print extended version information\n"
11385 "-h,-? prints usage help information\n"
11386 "{1} -d prints additional usage flags for debugging purposes\n",versionString,name);
11387}
11388
11389//----------------------------------------------------------------------------
11390// read the argument of option 'c' from the comment argument list and
11391// update the option index 'optInd'.
11392
11393static const char *getArg(int argc,char **argv,int &optInd)
11394{
11395 char *s=nullptr;
11396 if (qstrlen(&argv[optInd][2])>0)
11397 s=&argv[optInd][2];
11398 else if (optInd+1<argc && argv[optInd+1][0]!='-')
11399 s=argv[++optInd];
11400 return s;
11401}
11402
11403//----------------------------------------------------------------------------
11404
11405/** @brief /dev/null outline parser */
11407{
11408 public:
11409 void parseInput(const QCString &/* file */, const char * /* buf */,const std::shared_ptr<Entry> &, ClangTUParser*) override {}
11410 bool needsPreprocessing(const QCString &) const override { return FALSE; }
11411 void parsePrototype(const QCString &) override {}
11412};
11413
11414
11415template<class T> std::function< std::unique_ptr<T>() > make_parser_factory()
11416{
11417 return []() { return std::make_unique<T>(); };
11418}
11419
11421{
11422 initResources();
11423 QCString lang = Portable::getenv("LC_ALL");
11424 if (!lang.isEmpty()) Portable::setenv("LANG",lang);
11425 std::setlocale(LC_ALL,"");
11426 std::setlocale(LC_CTYPE,"C"); // to get isspace(0xA0)==0, needed for UTF-8
11427 std::setlocale(LC_NUMERIC,"C");
11428
11430
11454
11455 // register any additional parsers here...
11456
11458
11459#if USE_LIBCLANG
11461#endif
11470 Doxygen::pageLinkedMap = new PageLinkedMap; // all doc pages
11471 Doxygen::exampleLinkedMap = new PageLinkedMap; // all examples
11472 //Doxygen::tagDestinationDict.setAutoDelete(TRUE);
11474
11475 // initialization of these globals depends on
11476 // configuration switches so we need to postpone these
11477 Doxygen::globalScope = nullptr;
11486
11487}
11488
11520
11521static int computeIdealCacheParam(size_t v)
11522{
11523 //printf("computeIdealCacheParam(v=%u)\n",v);
11524
11525 int r=0;
11526 while (v!=0)
11527 {
11528 v >>= 1;
11529 r++;
11530 }
11531 // r = log2(v)
11532
11533 // convert to a valid cache size value
11534 return std::max(0,std::min(r-16,9));
11535}
11536
11537void readConfiguration(int argc, char **argv)
11538{
11539 QCString versionString = getFullVersion();
11540
11541 // helper that calls \a func to write to file \a fileName via a TextStream
11542 auto writeFile = [](const char *fileName,std::function<void(TextStream&)> func) -> bool
11543 {
11544 std::ofstream f;
11545 if (openOutputFile(fileName,f))
11546 {
11547 TextStream t(&f);
11548 func(t);
11549 return true;
11550 }
11551 return false;
11552 };
11553
11554
11555 /**************************************************************************
11556 * Handle arguments *
11557 **************************************************************************/
11558
11559 int optInd=1;
11560 QCString configName;
11561 QCString traceName;
11562 bool genConfig=false;
11563 bool shortList=false;
11564 bool traceTiming=false;
11566 bool updateConfig=false;
11567 bool quiet = false;
11568 while (optInd<argc && argv[optInd][0]=='-' &&
11569 (isalpha(argv[optInd][1]) || argv[optInd][1]=='?' ||
11570 argv[optInd][1]=='-')
11571 )
11572 {
11573 switch(argv[optInd][1])
11574 {
11575 case 'g':
11576 {
11577 genConfig=TRUE;
11578 }
11579 break;
11580 case 'l':
11581 {
11582 QCString layoutName;
11583 if (optInd+1>=argc)
11584 {
11585 layoutName="DoxygenLayout.xml";
11586 }
11587 else
11588 {
11589 layoutName=argv[optInd+1];
11590 }
11591 writeDefaultLayoutFile(layoutName);
11593 exit(0);
11594 }
11595 break;
11596 case 'c':
11597 if (optInd+1>=argc) // no file name given
11598 {
11599 msg("option \"-c\" is missing the file name to read\n");
11600 devUsage();
11602 exit(1);
11603 }
11604 else
11605 {
11606 g_commentFileName=argv[optInd+1];
11607 optInd++;
11608 }
11609 g_singleComment=true;
11610 quiet=true;
11611 break;
11612 case 'd':
11613 {
11614 QCString debugLabel=getArg(argc,argv,optInd);
11615 if (debugLabel.isEmpty())
11616 {
11617 devUsage();
11619 exit(0);
11620 }
11621 int retVal = Debug::setFlagStr(debugLabel);
11622 if (!retVal)
11623 {
11624 msg("option \"-d\" has unknown debug specifier: \"{}\".\n",debugLabel);
11625 devUsage();
11627 exit(1);
11628 }
11629 }
11630 break;
11631 case 't':
11632 {
11633#if ENABLE_TRACING
11634 if (!strcmp(argv[optInd]+1,"t_time"))
11635 {
11636 traceTiming = true;
11637 }
11638 else if (!strcmp(argv[optInd]+1,"t"))
11639 {
11640 traceTiming = false;
11641 }
11642 else
11643 {
11644 err("option should be \"-t\" or \"-t_time\", found: \"{}\".\n",argv[optInd]);
11646 exit(1);
11647 }
11648 if (optInd+1>=argc || argv[optInd+1][0] == '-') // no file name given
11649 {
11650 traceName="stdout";
11651 }
11652 else
11653 {
11654 traceName=argv[optInd+1];
11655 optInd++;
11656 }
11657#else
11658 err("support for option \"-t\" has not been compiled in (use a debug build or a release build with tracing enabled).\n");
11660 exit(1);
11661#endif
11662 }
11663 break;
11664 case 'x':
11665 if (!strcmp(argv[optInd]+1,"x_noenv")) diffList=Config::CompareMode::CompressedNoEnv;
11666 else if (!strcmp(argv[optInd]+1,"x")) diffList=Config::CompareMode::Compressed;
11667 else
11668 {
11669 err("option should be \"-x\" or \"-x_noenv\", found: \"{}\".\n",argv[optInd]);
11671 exit(1);
11672 }
11673 break;
11674 case 's':
11675 shortList=TRUE;
11676 break;
11677 case 'u':
11678 updateConfig=TRUE;
11679 break;
11680 case 'e':
11681 {
11682 QCString formatName=getArg(argc,argv,optInd);
11683 if (formatName.isEmpty())
11684 {
11685 err("option \"-e\" is missing format specifier rtf.\n");
11687 exit(1);
11688 }
11689 if (qstricmp(formatName.data(),"rtf")==0)
11690 {
11691 if (optInd+1>=argc)
11692 {
11693 err("option \"-e rtf\" is missing an extensions file name\n");
11695 exit(1);
11696 }
11697 writeFile(argv[optInd+1],RTFGenerator::writeExtensionsFile);
11699 exit(0);
11700 }
11701 err("option \"-e\" has invalid format specifier.\n");
11703 exit(1);
11704 }
11705 break;
11706 case 'f':
11707 {
11708 QCString listName=getArg(argc,argv,optInd);
11709 if (listName.isEmpty())
11710 {
11711 err("option \"-f\" is missing list specifier.\n");
11713 exit(1);
11714 }
11715 if (qstricmp(listName.data(),"emoji")==0)
11716 {
11717 if (optInd+1>=argc)
11718 {
11719 err("option \"-f emoji\" is missing an output file name\n");
11721 exit(1);
11722 }
11723 writeFile(argv[optInd+1],[](TextStream &t) { EmojiEntityMapper::instance().writeEmojiFile(t); });
11725 exit(0);
11726 }
11727 err("option \"-f\" has invalid list specifier.\n");
11729 exit(1);
11730 }
11731 break;
11732 case 'w':
11733 {
11734 QCString formatName=getArg(argc,argv,optInd);
11735 if (formatName.isEmpty())
11736 {
11737 err("option \"-w\" is missing format specifier rtf, html or latex\n");
11739 exit(1);
11740 }
11741 if (qstricmp(formatName.data(),"rtf")==0)
11742 {
11743 if (optInd+1>=argc)
11744 {
11745 err("option \"-w rtf\" is missing a style sheet file name\n");
11747 exit(1);
11748 }
11749 if (!writeFile(argv[optInd+1],RTFGenerator::writeStyleSheetFile))
11750 {
11751 err("error opening RTF style sheet file {}!\n",argv[optInd+1]);
11753 exit(1);
11754 }
11756 exit(0);
11757 }
11758 else if (qstricmp(formatName.data(),"html")==0)
11759 {
11760 Config::init();
11761 if (optInd+4<argc || FileInfo("Doxyfile").exists() || FileInfo("doxyfile").exists())
11762 // explicit config file mentioned or default found on disk
11763 {
11764 QCString df = optInd+4<argc ? argv[optInd+4] : (FileInfo("Doxyfile").exists() ? QCString("Doxyfile") : QCString("doxyfile"));
11765 if (!Config::parse(df)) // parse the config file
11766 {
11767 err("error opening or reading configuration file {}!\n",argv[optInd+4]);
11769 exit(1);
11770 }
11771 }
11772 if (optInd+3>=argc)
11773 {
11774 err("option \"-w html\" does not have enough arguments\n");
11776 exit(1);
11777 }
11781 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11782 writeFile(argv[optInd+1],[&](TextStream &t) { HtmlGenerator::writeHeaderFile(t,argv[optInd+3]); });
11783 writeFile(argv[optInd+2],HtmlGenerator::writeFooterFile);
11784 writeFile(argv[optInd+3],HtmlGenerator::writeStyleSheetFile);
11786 exit(0);
11787 }
11788 else if (qstricmp(formatName.data(),"latex")==0)
11789 {
11790 Config::init();
11791 if (optInd+4<argc || FileInfo("Doxyfile").exists() || FileInfo("doxyfile").exists())
11792 {
11793 QCString df = optInd+4<argc ? argv[optInd+4] : (FileInfo("Doxyfile").exists() ? QCString("Doxyfile") : QCString("doxyfile"));
11794 if (!Config::parse(df))
11795 {
11796 err("error opening or reading configuration file {}!\n",argv[optInd+4]);
11798 exit(1);
11799 }
11800 }
11801 if (optInd+3>=argc)
11802 {
11803 err("option \"-w latex\" does not have enough arguments\n");
11805 exit(1);
11806 }
11810 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11811 writeFile(argv[optInd+1],LatexGenerator::writeHeaderFile);
11812 writeFile(argv[optInd+2],LatexGenerator::writeFooterFile);
11813 writeFile(argv[optInd+3],LatexGenerator::writeStyleSheetFile);
11815 exit(0);
11816 }
11817 else
11818 {
11819 err("Illegal format specifier \"{}\": should be one of rtf, html or latex\n",formatName);
11821 exit(1);
11822 }
11823 }
11824 break;
11825 case 'm':
11827 break;
11828 case 'v':
11829 version(false);
11831 exit(0);
11832 break;
11833 case 'V':
11834 version(true);
11836 exit(0);
11837 break;
11838 case '-':
11839 if (qstrcmp(&argv[optInd][2],"help")==0)
11840 {
11841 usage(argv[0],versionString);
11842 exit(0);
11843 }
11844 else if (qstrcmp(&argv[optInd][2],"version")==0)
11845 {
11846 version(false);
11848 exit(0);
11849 }
11850 else if ((qstrcmp(&argv[optInd][2],"Version")==0) ||
11851 (qstrcmp(&argv[optInd][2],"VERSION")==0))
11852 {
11853 version(true);
11855 exit(0);
11856 }
11857 else
11858 {
11859 err("Unknown option \"-{}\"\n",&argv[optInd][1]);
11860 usage(argv[0],versionString);
11861 exit(1);
11862 }
11863 break;
11864 case 'b':
11865 setvbuf(stdout,nullptr,_IONBF,0);
11866 break;
11867 case 'q':
11868 quiet = true;
11869 break;
11870 case 'h':
11871 case '?':
11872 usage(argv[0],versionString);
11873 exit(0);
11874 break;
11875 default:
11876 err("Unknown option \"-{:c}\"\n",argv[optInd][1]);
11877 usage(argv[0],versionString);
11878 exit(1);
11879 }
11880 optInd++;
11881 }
11882
11883 /**************************************************************************
11884 * Parse or generate the config file *
11885 **************************************************************************/
11886
11887 initTracing(traceName.data(),traceTiming);
11888 TRACE("Doxygen version used: {}",getFullVersion());
11889 Config::init();
11890
11891 FileInfo configFileInfo1("Doxyfile"),configFileInfo2("doxyfile");
11892 if (optInd>=argc)
11893 {
11894 if (configFileInfo1.exists())
11895 {
11896 configName="Doxyfile";
11897 }
11898 else if (configFileInfo2.exists())
11899 {
11900 configName="doxyfile";
11901 }
11902 else if (genConfig)
11903 {
11904 configName="Doxyfile";
11905 }
11906 else
11907 {
11908 err("Doxyfile not found and no input file specified!\n");
11909 usage(argv[0],versionString);
11910 exit(1);
11911 }
11912 }
11913 else
11914 {
11915 FileInfo fi(argv[optInd]);
11916 if (fi.exists() || qstrcmp(argv[optInd],"-")==0 || genConfig)
11917 {
11918 configName=argv[optInd];
11919 }
11920 else
11921 {
11922 err("configuration file {} not found!\n",argv[optInd]);
11923 usage(argv[0],versionString);
11924 exit(1);
11925 }
11926 }
11927
11928 if (genConfig)
11929 {
11930 generateConfigFile(configName,shortList);
11932 exit(0);
11933 }
11934
11935 if (!Config::parse(configName,updateConfig,diffList))
11936 {
11937 err("could not open or read configuration file {}!\n",configName);
11939 exit(1);
11940 }
11941
11942 if (diffList!=Config::CompareMode::Full)
11943 {
11945 compareDoxyfile(diffList);
11947 exit(0);
11948 }
11949
11950 if (updateConfig)
11951 {
11953 generateConfigFile(configName,shortList,TRUE);
11955 exit(0);
11956 }
11957
11958 /* Perlmod wants to know the path to the config file.*/
11959 FileInfo configFileInfo(configName.str());
11960 setPerlModDoxyfile(configFileInfo.absFilePath());
11961
11962 /* handle -q option */
11963 if (quiet) Config_updateBool(QUIET,TRUE);
11964}
11965
11966/** check and resolve config options */
11968{
11969 AUTO_TRACE();
11970
11975}
11976
11977/** adjust globals that depend on configuration settings. */
11979{
11980 AUTO_TRACE();
11981 Doxygen::globalNamespaceDef = createNamespaceDef("<globalScope>",1,1,"<globalScope>");
11991
11992 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11993
11994 /* Set the global html file extension. */
11995 Doxygen::htmlFileExtension = Config_getString(HTML_FILE_EXTENSION);
11996
11997
11999 Config_getBool(CALLER_GRAPH) ||
12000 Config_getBool(REFERENCES_RELATION) ||
12001 Config_getBool(REFERENCED_BY_RELATION);
12002
12003 /**************************************************************************
12004 * Add custom extension mappings
12005 **************************************************************************/
12006
12007 const StringVector &extMaps = Config_getList(EXTENSION_MAPPING);
12008 for (const auto &mapping : extMaps)
12009 {
12010 QCString mapStr = mapping;
12011 int i=mapStr.find('=');
12012 if (i==-1)
12013 {
12014 continue;
12015 }
12016 else
12017 {
12018 QCString ext = mapStr.left(i).stripWhiteSpace().lower();
12019 QCString language = mapStr.mid(i+1).stripWhiteSpace().lower();
12020 if (ext.isEmpty() || language.isEmpty())
12021 {
12022 continue;
12023 }
12024
12025 if (!updateLanguageMapping(ext,language))
12026 {
12027 err("Failed to map file extension '{}' to unsupported language '{}'.\n"
12028 "Check the EXTENSION_MAPPING setting in the config file.\n",
12029 ext,language);
12030 }
12031 else
12032 {
12033 msg("Adding custom extension mapping: '{}' will be treated as language '{}'\n",
12034 ext,language);
12035 }
12036 }
12037 }
12038 // create input file exncodings
12039
12040 // check INPUT_ENCODING
12041 void *cd = portable_iconv_open("UTF-8",Config_getString(INPUT_ENCODING).data());
12042 if (cd==reinterpret_cast<void *>(-1))
12043 {
12044 term("unsupported character conversion: '{}'->'UTF-8': {}\n"
12045 "Check the 'INPUT_ENCODING' setting in the config file!\n",
12046 Config_getString(INPUT_ENCODING),strerror(errno));
12047 }
12048 else
12049 {
12051 }
12052
12053 // check and split INPUT_FILE_ENCODING
12054 const StringVector &fileEncod = Config_getList(INPUT_FILE_ENCODING);
12055 for (const auto &mapping : fileEncod)
12056 {
12057 QCString mapStr = mapping;
12058 int i=mapStr.find('=');
12059 if (i==-1)
12060 {
12061 continue;
12062 }
12063 else
12064 {
12065 QCString pattern = mapStr.left(i).stripWhiteSpace().lower();
12066 QCString encoding = mapStr.mid(i+1).stripWhiteSpace().lower();
12067 if (pattern.isEmpty() || encoding.isEmpty())
12068 {
12069 continue;
12070 }
12071 cd = portable_iconv_open("UTF-8",encoding.data());
12072 if (cd==reinterpret_cast<void *>(-1))
12073 {
12074 term("unsupported character conversion: '{}'->'UTF-8': {}\n"
12075 "Check the 'INPUT_FILE_ENCODING' setting in the config file!\n",
12076 encoding,strerror(errno));
12077 }
12078 else
12079 {
12081 }
12082
12083 Doxygen::inputFileEncodingList.emplace_back(pattern, encoding);
12084 }
12085 }
12086
12087 // add predefined macro name to a dictionary
12088 const StringVector &expandAsDefinedList =Config_getList(EXPAND_AS_DEFINED);
12089 for (const auto &s : expandAsDefinedList)
12090 {
12092 }
12093
12094 // read aliases and store them in a dictionary
12095 readAliases();
12096
12097 // store number of spaces in a tab into Doxygen::spaces
12098 int tabSize = Config_getInt(TAB_SIZE);
12099 Doxygen::spaces.resize(tabSize);
12100 for (int sp=0; sp<tabSize; sp++) Doxygen::spaces.at(sp)=' ';
12101 Doxygen::spaces.at(tabSize)='\0';
12102}
12103
12104#ifdef HAS_SIGNALS
12105static void stopDoxygen(int)
12106{
12107 signal(SIGINT,SIG_DFL); // Re-register signal handler for default action
12108 Dir thisDir;
12109 msg("Cleaning up...\n");
12110 if (!Doxygen::filterDBFileName.isEmpty())
12111 {
12112 thisDir.remove(Doxygen::filterDBFileName.str());
12113 }
12114 killpg(0,SIGINT);
12116 exitTracing();
12117 exit(1);
12118}
12119#endif
12120
12121static void writeTagFile()
12122{
12123 QCString generateTagFile = Config_getString(GENERATE_TAGFILE);
12124 if (generateTagFile.isEmpty()) return;
12125
12126 std::ofstream f = Portable::openOutputStream(generateTagFile);
12127 if (!f.is_open())
12128 {
12129 err("cannot open tag file {} for writing\n", generateTagFile);
12130 return;
12131 }
12132 TextStream tagFile(&f);
12133 tagFile << "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>\n";
12134 tagFile << "<tagfile doxygen_version=\"" << getDoxygenVersion() << "\"";
12135 std::string gitVersion = getGitVersion();
12136 if (!gitVersion.empty())
12137 {
12138 tagFile << " doxygen_gitid=\"" << gitVersion << "\"";
12139 }
12140 tagFile << ">\n";
12141
12142 // for each file
12143 for (const auto &fn : *Doxygen::inputNameLinkedMap)
12144 {
12145 for (const auto &fd : *fn)
12146 {
12147 if (fd->isLinkableInProject()) fd->writeTagFile(tagFile);
12148 }
12149 }
12150 // for each class
12151 for (const auto &cd : *Doxygen::classLinkedMap)
12152 {
12153 ClassDefMutable *cdm = toClassDefMutable(cd.get());
12154 if (cdm && cdm->isLinkableInProject())
12155 {
12156 cdm->writeTagFile(tagFile);
12157 }
12158 }
12159 // for each concept
12160 for (const auto &cd : *Doxygen::conceptLinkedMap)
12161 {
12162 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
12163 if (cdm && cdm->isLinkableInProject())
12164 {
12165 cdm->writeTagFile(tagFile);
12166 }
12167 }
12168 // for each namespace
12169 for (const auto &nd : *Doxygen::namespaceLinkedMap)
12170 {
12172 if (ndm && nd->isLinkableInProject())
12173 {
12174 ndm->writeTagFile(tagFile);
12175 }
12176 }
12177 // for each group
12178 for (const auto &gd : *Doxygen::groupLinkedMap)
12179 {
12180 if (gd->isLinkableInProject()) gd->writeTagFile(tagFile);
12181 }
12182 // for each module
12183 for (const auto &mod : ModuleManager::instance().modules())
12184 {
12185 if (mod->isLinkableInProject()) mod->writeTagFile(tagFile);
12186 }
12187 // for each page
12188 for (const auto &pd : *Doxygen::pageLinkedMap)
12189 {
12190 if (pd->isLinkableInProject()) pd->writeTagFile(tagFile);
12191 }
12192 // for requirements
12194 // for each directory
12195 for (const auto &dd : *Doxygen::dirLinkedMap)
12196 {
12197 if (dd->isLinkableInProject()) dd->writeTagFile(tagFile);
12198 }
12199 if (Doxygen::mainPage) Doxygen::mainPage->writeTagFile(tagFile);
12200
12201 tagFile << "</tagfile>\n";
12202}
12203
12204static void exitDoxygen() noexcept
12205{
12206 if (!g_successfulRun) // premature exit
12207 {
12208 Dir thisDir;
12209 msg("Exiting...\n");
12210 if (!Doxygen::filterDBFileName.isEmpty())
12211 {
12212 thisDir.remove(Doxygen::filterDBFileName.str());
12213 }
12214 }
12215}
12216
12217static QCString createOutputDirectory(const QCString &baseDirName,
12218 const QCString &formatDirName,
12219 const char *defaultDirName)
12220{
12221 QCString result = formatDirName;
12222 if (result.isEmpty())
12223 {
12224 result = baseDirName + defaultDirName;
12225 }
12226 else if (formatDirName[0]!='/' && (formatDirName.length()==1 || formatDirName[1]!=':'))
12227 {
12228 result.prepend(baseDirName+"/");
12229 }
12230 Dir formatDir(result.str());
12231 if (!formatDir.exists() && !formatDir.mkdir(result.str()))
12232 {
12233 term("Could not create output directory {}\n", result);
12234 }
12235 return result;
12236}
12237
12239{
12240 StringUnorderedSet killSet;
12241
12242 const StringVector &exclPatterns = Config_getList(EXCLUDE_PATTERNS);
12243 bool alwaysRecursive = Config_getBool(RECURSIVE);
12244 StringUnorderedSet excludeNameSet;
12245
12246 // gather names of all files in the include path
12247 g_s.begin("Searching for include files...\n");
12248 killSet.clear();
12249 const StringVector &includePathList = Config_getList(INCLUDE_PATH);
12250 for (const auto &s : includePathList)
12251 {
12252 size_t plSize = Config_getList(INCLUDE_FILE_PATTERNS).size();
12253 const StringVector &pl = plSize==0 ? Config_getList(FILE_PATTERNS) :
12254 Config_getList(INCLUDE_FILE_PATTERNS);
12255 readFileOrDirectory(s, // s
12257 nullptr, // exclSet
12258 &pl, // patList
12259 &exclPatterns, // exclPatList
12260 nullptr, // resultList
12261 nullptr, // resultSet
12262 false, // INCLUDE_PATH isn't recursive
12263 TRUE, // errorIfNotExist
12264 &killSet); // killSet
12265 }
12266 g_s.end();
12267
12268 g_s.begin("Searching for example files...\n");
12269 killSet.clear();
12270 const StringVector &examplePathList = Config_getList(EXAMPLE_PATH);
12271 for (const auto &s : examplePathList)
12272 {
12273 readFileOrDirectory(s, // s
12275 nullptr, // exclSet
12276 &Config_getList(EXAMPLE_PATTERNS), // patList
12277 nullptr, // exclPatList
12278 nullptr, // resultList
12279 nullptr, // resultSet
12280 (alwaysRecursive || Config_getBool(EXAMPLE_RECURSIVE)), // recursive
12281 TRUE, // errorIfNotExist
12282 &killSet); // killSet
12283 }
12284 g_s.end();
12285
12286 g_s.begin("Searching for images...\n");
12287 killSet.clear();
12288 const StringVector &imagePathList=Config_getList(IMAGE_PATH);
12289 for (const auto &s : imagePathList)
12290 {
12291 readFileOrDirectory(s, // s
12293 nullptr, // exclSet
12294 nullptr, // patList
12295 nullptr, // exclPatList
12296 nullptr, // resultList
12297 nullptr, // resultSet
12298 alwaysRecursive, // recursive
12299 TRUE, // errorIfNotExist
12300 &killSet); // killSet
12301 }
12302 g_s.end();
12303
12304 g_s.begin("Searching for dot files...\n");
12305 killSet.clear();
12306 const StringVector &dotFileList=Config_getList(DOTFILE_DIRS);
12307 for (const auto &s : dotFileList)
12308 {
12309 readFileOrDirectory(s, // s
12311 nullptr, // exclSet
12312 nullptr, // patList
12313 nullptr, // exclPatList
12314 nullptr, // resultList
12315 nullptr, // resultSet
12316 alwaysRecursive, // recursive
12317 TRUE, // errorIfNotExist
12318 &killSet); // killSet
12319 }
12320 g_s.end();
12321
12322 g_s.begin("Searching for msc files...\n");
12323 killSet.clear();
12324 const StringVector &mscFileList=Config_getList(MSCFILE_DIRS);
12325 for (const auto &s : mscFileList)
12326 {
12327 readFileOrDirectory(s, // s
12329 nullptr, // exclSet
12330 nullptr, // patList
12331 nullptr, // exclPatList
12332 nullptr, // resultList
12333 nullptr, // resultSet
12334 alwaysRecursive, // recursive
12335 TRUE, // errorIfNotExist
12336 &killSet); // killSet
12337 }
12338 g_s.end();
12339
12340 g_s.begin("Searching for dia files...\n");
12341 killSet.clear();
12342 const StringVector &diaFileList=Config_getList(DIAFILE_DIRS);
12343 for (const auto &s : diaFileList)
12344 {
12345 readFileOrDirectory(s, // s
12347 nullptr, // exclSet
12348 nullptr, // patList
12349 nullptr, // exclPatList
12350 nullptr, // resultList
12351 nullptr, // resultSet
12352 alwaysRecursive, // recursive
12353 TRUE, // errorIfNotExist
12354 &killSet); // killSet
12355 }
12356 g_s.end();
12357
12358 g_s.begin("Searching for plantuml files...\n");
12359 killSet.clear();
12360 const StringVector &plantUmlFileList=Config_getList(PLANTUMLFILE_DIRS);
12361 for (const auto &s : plantUmlFileList)
12362 {
12363 readFileOrDirectory(s, // s
12365 nullptr, // exclSet
12366 nullptr, // patList
12367 nullptr, // exclPatList
12368 nullptr, // resultList
12369 nullptr, // resultSet
12370 alwaysRecursive, // recursive
12371 TRUE, // errorIfNotExist
12372 &killSet); // killSet
12373 }
12374 g_s.end();
12375
12376 g_s.begin("Searching for files to exclude\n");
12377 const StringVector &excludeList = Config_getList(EXCLUDE);
12378 for (const auto &s : excludeList)
12379 {
12380 readFileOrDirectory(s, // s
12381 nullptr, // fnDict
12382 nullptr, // exclSet
12383 &Config_getList(FILE_PATTERNS), // patList
12384 nullptr, // exclPatList
12385 nullptr, // resultList
12386 &excludeNameSet, // resultSet
12387 alwaysRecursive, // recursive
12388 FALSE); // errorIfNotExist
12389 }
12390 g_s.end();
12391
12392 /**************************************************************************
12393 * Determine Input Files *
12394 **************************************************************************/
12395
12396 g_s.begin("Searching INPUT for files to process...\n");
12397 killSet.clear();
12398 Doxygen::inputPaths.clear();
12399 const StringVector &inputList=Config_getList(INPUT);
12400 for (const auto &s : inputList)
12401 {
12402 QCString path = s;
12403 size_t l = path.length();
12404 if (l>0)
12405 {
12406 // strip trailing slashes
12407 if (path.at(l-1)=='\\' || path.at(l-1)=='/') path=path.left(l-1);
12408
12410 path, // s
12412 &excludeNameSet, // exclSet
12413 &Config_getList(FILE_PATTERNS), // patList
12414 &exclPatterns, // exclPatList
12415 &g_inputFiles, // resultList
12416 nullptr, // resultSet
12417 alwaysRecursive, // recursive
12418 TRUE, // errorIfNotExist
12419 &killSet, // killSet
12420 &Doxygen::inputPaths); // paths
12421 }
12422 }
12423
12424 // Sort the FileDef objects by full path to get a predictable ordering over multiple runs
12425 std::stable_sort(Doxygen::inputNameLinkedMap->begin(),
12427 [](const auto &f1,const auto &f2)
12428 {
12429 return qstricmp_sort(f1->fullName(),f2->fullName())<0;
12430 });
12431 for (auto &fileName : *Doxygen::inputNameLinkedMap)
12432 {
12433 if (fileName->size()>1)
12434 {
12435 std::stable_sort(fileName->begin(),fileName->end(),[](const auto &f1,const auto &f2)
12436 {
12437 return qstricmp_sort(f1->absFilePath(),f2->absFilePath())<0;
12438 });
12439 }
12440 }
12441 if (Doxygen::inputNameLinkedMap->empty())
12442 {
12443 warn_uncond("No files to be processed, please check your settings, in particular INPUT, FILE_PATTERNS, and RECURSIVE\n");
12444 }
12445 g_s.end();
12446}
12447
12448
12450{
12451 if (Config_getBool(MARKDOWN_SUPPORT))
12452 {
12453 QCString mdfileAsMainPage = Config_getString(USE_MDFILE_AS_MAINPAGE);
12454 if (mdfileAsMainPage.isEmpty()) return;
12455 FileInfo fi(mdfileAsMainPage.data());
12456 if (!fi.exists())
12457 {
12458 warn_uncond("Specified markdown mainpage '{}' does not exist\n",mdfileAsMainPage);
12459 return;
12460 }
12461 bool ambig = false;
12462 if (findFileDef(Doxygen::inputNameLinkedMap,fi.absFilePath(),ambig)==nullptr)
12463 {
12464 warn_uncond("Specified markdown mainpage '{}' has not been defined as input file\n",mdfileAsMainPage);
12465 return;
12466 }
12467 }
12468}
12469
12471{
12472 AUTO_TRACE();
12473 std::atexit(exitDoxygen);
12474
12475 Portable::correctPath(Config_getList(EXTERNAL_TOOL_PATH));
12476
12477#if USE_LIBCLANG
12478 Doxygen::clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING);
12479#endif
12480
12481 // we would like to show the versionString earlier, but we first have to handle the configuration file
12482 // to know the value of the QUIET setting.
12483 QCString versionString = getFullVersion();
12484 msg("Doxygen version used: {}\n",versionString);
12485
12487
12488 /**************************************************************************
12489 * Make sure the output directory exists
12490 **************************************************************************/
12491 QCString outputDirectory = Config_getString(OUTPUT_DIRECTORY);
12492 if (!g_singleComment)
12493 {
12494 if (outputDirectory.isEmpty())
12495 {
12496 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,Dir::currentDirPath());
12497 }
12498 else
12499 {
12500 Dir dir(outputDirectory.str());
12501 if (!dir.exists())
12502 {
12504 if (!dir.mkdir(outputDirectory.str()))
12505 {
12506 term("tag OUTPUT_DIRECTORY: Output directory '{}' does not "
12507 "exist and cannot be created\n",outputDirectory);
12508 }
12509 else
12510 {
12511 msg("Notice: Output directory '{}' does not exist. "
12512 "I have created it for you.\n", outputDirectory);
12513 }
12514 dir.setPath(outputDirectory.str());
12515 }
12516 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,dir.absPath());
12517 }
12518 }
12519 AUTO_TRACE_ADD("outputDirectory={}",outputDirectory);
12520
12521 /**************************************************************************
12522 * Initialize global lists and dictionaries
12523 **************************************************************************/
12524
12525 // also scale lookup cache with SYMBOL_CACHE_SIZE
12526 int cacheSize = Config_getInt(LOOKUP_CACHE_SIZE);
12527 if (cacheSize<0) cacheSize=0;
12528 if (cacheSize>9) cacheSize=9;
12529 uint32_t lookupSize = 65536 << cacheSize;
12532
12533#ifdef HAS_SIGNALS
12534 signal(SIGINT, stopDoxygen);
12535#endif
12536
12537 uint32_t pid = Portable::pid();
12538 Doxygen::filterDBFileName.sprintf("doxygen_filterdb_%d.tmp",pid);
12539 Doxygen::filterDBFileName.prepend(outputDirectory+"/");
12540
12541 /**************************************************************************
12542 * Check/create output directories *
12543 **************************************************************************/
12544
12545 bool generateHtml = Config_getBool(GENERATE_HTML);
12546 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
12547 bool generateXml = Config_getBool(GENERATE_XML);
12548 bool generateLatex = Config_getBool(GENERATE_LATEX);
12549 bool generateRtf = Config_getBool(GENERATE_RTF);
12550 bool generateMan = Config_getBool(GENERATE_MAN);
12551 bool generateSql = Config_getBool(GENERATE_SQLITE3);
12552 QCString htmlOutput;
12553 QCString docbookOutput;
12554 QCString xmlOutput;
12555 QCString latexOutput;
12556 QCString rtfOutput;
12557 QCString manOutput;
12558 QCString sqlOutput;
12559
12560 if (!g_singleComment)
12561 {
12562 if (generateHtml)
12563 {
12564 htmlOutput = createOutputDirectory(outputDirectory,Config_getString(HTML_OUTPUT),"/html");
12565 Config_updateString(HTML_OUTPUT,htmlOutput);
12566
12567 QCString sitemapUrl = Config_getString(SITEMAP_URL);
12568 bool generateSitemap = !sitemapUrl.isEmpty();
12569 if (generateSitemap && !sitemapUrl.endsWith("/"))
12570 {
12571 Config_updateString(SITEMAP_URL,sitemapUrl+"/");
12572 }
12573
12574 // add HTML indexers that are enabled
12575 bool generateHtmlHelp = Config_getBool(GENERATE_HTMLHELP);
12576 bool generateEclipseHelp = Config_getBool(GENERATE_ECLIPSEHELP);
12577 bool generateQhp = Config_getBool(GENERATE_QHP);
12578 bool generateTreeView = Config_getBool(GENERATE_TREEVIEW);
12579 bool generateDocSet = Config_getBool(GENERATE_DOCSET);
12580 if (generateEclipseHelp) Doxygen::indexList->addIndex<EclipseHelp>();
12581 if (generateHtmlHelp) Doxygen::indexList->addIndex<HtmlHelp>();
12582 if (generateQhp) Doxygen::indexList->addIndex<Qhp>();
12583 if (generateSitemap) Doxygen::indexList->addIndex<Sitemap>();
12584 if (generateTreeView) Doxygen::indexList->addIndex<FTVHelp>(TRUE);
12585 if (generateDocSet) Doxygen::indexList->addIndex<DocSets>();
12586 Doxygen::indexList->addIndex<Crawlmap>();
12587 Doxygen::indexList->initialize();
12588 }
12589
12590 if (generateDocbook)
12591 {
12592 docbookOutput = createOutputDirectory(outputDirectory,Config_getString(DOCBOOK_OUTPUT),"/docbook");
12593 Config_updateString(DOCBOOK_OUTPUT,docbookOutput);
12594 }
12595
12596 if (generateXml)
12597 {
12598 xmlOutput = createOutputDirectory(outputDirectory,Config_getString(XML_OUTPUT),"/xml");
12599 Config_updateString(XML_OUTPUT,xmlOutput);
12600 }
12601
12602 if (generateLatex)
12603 {
12604 latexOutput = createOutputDirectory(outputDirectory,Config_getString(LATEX_OUTPUT), "/latex");
12605 Config_updateString(LATEX_OUTPUT,latexOutput);
12606 }
12607
12608 if (generateRtf)
12609 {
12610 rtfOutput = createOutputDirectory(outputDirectory,Config_getString(RTF_OUTPUT),"/rtf");
12611 Config_updateString(RTF_OUTPUT,rtfOutput);
12612 }
12613
12614 if (generateMan)
12615 {
12616 manOutput = createOutputDirectory(outputDirectory,Config_getString(MAN_OUTPUT),"/man");
12617 Config_updateString(MAN_OUTPUT,manOutput);
12618 }
12619
12620 if (generateSql)
12621 {
12622 sqlOutput = createOutputDirectory(outputDirectory,Config_getString(SQLITE3_OUTPUT),"/sqlite3");
12623 Config_updateString(SQLITE3_OUTPUT,sqlOutput);
12624 }
12625 }
12626
12627 if (Config_getBool(HAVE_DOT))
12628 {
12629 QCString curFontPath = Config_getString(DOT_FONTPATH);
12630 if (curFontPath.isEmpty())
12631 {
12632 Portable::getenv("DOTFONTPATH");
12633 QCString newFontPath = ".";
12634 if (!curFontPath.isEmpty())
12635 {
12636 newFontPath+=Portable::pathListSeparator();
12637 newFontPath+=curFontPath;
12638 }
12639 Portable::setenv("DOTFONTPATH",qPrint(newFontPath));
12640 }
12641 else
12642 {
12643 Portable::setenv("DOTFONTPATH",qPrint(curFontPath));
12644 }
12645 }
12646
12647 /**************************************************************************
12648 * Handle layout file *
12649 **************************************************************************/
12650
12652 QCString layoutFileName = Config_getString(LAYOUT_FILE);
12653 bool defaultLayoutUsed = FALSE;
12654 if (layoutFileName.isEmpty())
12655 {
12656 layoutFileName = Config_updateString(LAYOUT_FILE,"DoxygenLayout.xml");
12657 defaultLayoutUsed = TRUE;
12658 }
12659 AUTO_TRACE_ADD("defaultLayoutUsed={}, layoutFileName={}",defaultLayoutUsed,layoutFileName);
12660
12661 FileInfo fi(layoutFileName.str());
12662 if (fi.exists())
12663 {
12664 msg("Parsing layout file {}...\n",layoutFileName);
12665 LayoutDocManager::instance().parse(layoutFileName);
12666 }
12667 else if (!defaultLayoutUsed)
12668 {
12669 warn_uncond("failed to open layout file '{}' for reading! Using default settings.\n",layoutFileName);
12670 }
12671 printLayout();
12672
12673 /**************************************************************************
12674 * Read and preprocess input *
12675 **************************************************************************/
12676
12677 // prevent search in the output directories
12678 StringVector exclPatterns = Config_getList(EXCLUDE_PATTERNS);
12679 if (generateHtml) exclPatterns.push_back(htmlOutput.str());
12680 if (generateDocbook) exclPatterns.push_back(docbookOutput.str());
12681 if (generateXml) exclPatterns.push_back(xmlOutput.str());
12682 if (generateLatex) exclPatterns.push_back(latexOutput.str());
12683 if (generateRtf) exclPatterns.push_back(rtfOutput.str());
12684 if (generateMan) exclPatterns.push_back(manOutput.str());
12685 Config_updateList(EXCLUDE_PATTERNS,exclPatterns);
12686
12687 if (!g_singleComment)
12688 {
12690
12692 }
12693
12694 // Notice: the order of the function calls below is very important!
12695
12696 if (generateHtml && !Config_getBool(USE_MATHJAX))
12697 {
12699 }
12700 if (generateRtf)
12701 {
12703 }
12704 if (generateDocbook)
12705 {
12707 }
12708
12710
12711 /**************************************************************************
12712 * Handle Tag Files *
12713 **************************************************************************/
12714
12715 std::shared_ptr<Entry> root = std::make_shared<Entry>();
12716
12717 if (!g_singleComment)
12718 {
12719 msg("Reading and parsing tag files\n");
12720 const StringVector &tagFileList = Config_getList(TAGFILES);
12721 for (const auto &s : tagFileList)
12722 {
12723 readTagFile(root,s.c_str());
12724 }
12725 }
12726
12727 /**************************************************************************
12728 * Parse source files *
12729 **************************************************************************/
12730
12731 addSTLSupport(root);
12732
12733 g_s.begin("Parsing files\n");
12734 if (g_singleComment)
12735 {
12736 //printf("Parsing comment %s\n",qPrint(g_commentFileName));
12737 if (g_commentFileName=="-")
12738 {
12739 std::string text = fileToString(g_commentFileName).str();
12740 addTerminalCharIfMissing(text,'\n');
12741 generateHtmlForComment("stdin.md",text);
12742 }
12743 else if (FileInfo(g_commentFileName.str()).isFile())
12744 {
12745 std::string text;
12747 addTerminalCharIfMissing(text,'\n');
12749 }
12750 else
12751 {
12752 }
12754 exit(0);
12755 }
12756 else
12757 {
12758 if (Config_getInt(NUM_PROC_THREADS)==1)
12759 {
12761 }
12762 else
12763 {
12765 }
12766 }
12767 g_s.end();
12768
12769 /**************************************************************************
12770 * Gather information *
12771 **************************************************************************/
12772
12773 g_s.begin("Building macro definition list...\n");
12775 g_s.end();
12776
12777 g_s.begin("Building group list...\n");
12778 buildGroupList(root.get());
12779 organizeSubGroups(root.get());
12780 g_s.end();
12781
12782 g_s.begin("Building directory list...\n");
12784 findDirDocumentation(root.get());
12785 g_s.end();
12786
12787 g_s.begin("Building namespace list...\n");
12788 buildNamespaceList(root.get());
12789 findUsingDirectives(root.get());
12790 g_s.end();
12791
12792 g_s.begin("Building file list...\n");
12793 buildFileList(root.get());
12794 g_s.end();
12795
12796 g_s.begin("Building class list...\n");
12797 buildClassList(root.get());
12798 g_s.end();
12799
12800 g_s.begin("Building concept list...\n");
12801 buildConceptList(root.get());
12802 g_s.end();
12803
12804 // build list of using declarations here (global list)
12805 buildListOfUsingDecls(root.get());
12806 g_s.end();
12807
12808 g_s.begin("Computing nesting relations for classes...\n");
12810 g_s.end();
12811 // 1.8.2-20121111: no longer add nested classes to the group as well
12812 //distributeClassGroupRelations();
12813
12814 // calling buildClassList may result in cached relations that
12815 // become invalid after resolveClassNestingRelations(), that's why
12816 // we need to clear the cache here
12817 Doxygen::typeLookupCache->clear();
12818 // we don't need the list of using declaration anymore
12819 g_usingDeclarations.clear();
12820
12821 g_s.begin("Associating documentation with classes...\n");
12822 buildClassDocList(root.get());
12823 g_s.end();
12824
12825 g_s.begin("Associating documentation with concepts...\n");
12826 buildConceptDocList(root.get());
12828 g_s.end();
12829
12830 g_s.begin("Associating documentation with modules...\n");
12831 findModuleDocumentation(root.get());
12832 g_s.end();
12833
12834 g_s.begin("Building example list...\n");
12835 buildExampleList(root.get());
12836 g_s.end();
12837
12838 g_s.begin("Searching for enumerations...\n");
12839 findEnums(root.get());
12840 g_s.end();
12841
12842 // Since buildVarList calls isVarWithConstructor
12843 // and this calls getResolvedClass we need to process
12844 // typedefs first so the relations between classes via typedefs
12845 // are properly resolved. See bug 536385 for an example.
12846 g_s.begin("Searching for documented typedefs...\n");
12847 buildTypedefList(root.get());
12848 g_s.end();
12849
12850 if (Config_getBool(OPTIMIZE_OUTPUT_SLICE))
12851 {
12852 g_s.begin("Searching for documented sequences...\n");
12853 buildSequenceList(root.get());
12854 g_s.end();
12855
12856 g_s.begin("Searching for documented dictionaries...\n");
12857 buildDictionaryList(root.get());
12858 g_s.end();
12859 }
12860
12861 g_s.begin("Searching for members imported via using declarations...\n");
12862 // this should be after buildTypedefList in order to properly import
12863 // used typedefs
12864 findUsingDeclarations(root.get(),TRUE); // do for python packages first
12865 findUsingDeclarations(root.get(),FALSE); // then the rest
12866 g_s.end();
12867
12868 g_s.begin("Searching for included using directives...\n");
12870 g_s.end();
12871
12872 g_s.begin("Searching for documented variables...\n");
12873 buildVarList(root.get());
12874 g_s.end();
12875
12876 g_s.begin("Building interface member list...\n");
12877 buildInterfaceAndServiceList(root.get()); // UNO IDL
12878
12879 g_s.begin("Building member list...\n"); // using class info only !
12880 buildFunctionList(root.get());
12881 g_s.end();
12882
12883 g_s.begin("Searching for friends...\n");
12884 findFriends();
12885 g_s.end();
12886
12887 g_s.begin("Searching for documented defines...\n");
12888 findDefineDocumentation(root.get());
12889 g_s.end();
12890
12891 g_s.begin("Computing class inheritance relations...\n");
12892 findClassEntries(root.get());
12894 g_s.end();
12895
12896 g_s.begin("Computing class usage relations...\n");
12898 g_s.end();
12899
12900 if (Config_getBool(INLINE_SIMPLE_STRUCTS))
12901 {
12902 g_s.begin("Searching for tag less structs...\n");
12904 g_s.end();
12905 }
12906
12907 g_s.begin("Flushing cached template relations that have become invalid...\n");
12909 g_s.end();
12910
12911 g_s.begin("Warn for undocumented namespaces...\n");
12913 g_s.end();
12914
12915 g_s.begin("Computing class relations...\n");
12918 if (Config_getBool(OPTIMIZE_OUTPUT_VHDL))
12919 {
12921 }
12923 g_classEntries.clear();
12924 g_s.end();
12925
12926 g_s.begin("Add enum values to enums...\n");
12927 addEnumValuesToEnums(root.get());
12928 findEnumDocumentation(root.get());
12929 g_s.end();
12930
12931 g_s.begin("Searching for member function documentation...\n");
12932 findObjCMethodDefinitions(root.get());
12933 findMemberDocumentation(root.get()); // may introduce new members !
12934 findUsingDeclImports(root.get()); // may introduce new members !
12935 g_usingClassMap.clear();
12939 g_s.end();
12940
12941 // moved to after finding and copying documentation,
12942 // as this introduces new members see bug 722654
12943 g_s.begin("Creating members for template instances...\n");
12945 g_s.end();
12946
12947 g_s.begin("Building page list...\n");
12948 buildPageList(root.get());
12949 g_s.end();
12950
12951 g_s.begin("Building requirements list...\n");
12952 buildRequirementsList(root.get());
12953 g_s.end();
12954
12955 g_s.begin("Search for main page...\n");
12956 findMainPage(root.get());
12957 findMainPageTagFiles(root.get());
12958 g_s.end();
12959
12960 g_s.begin("Computing page relations...\n");
12961 computePageRelations(root.get());
12963 g_s.end();
12964
12965 g_s.begin("Determining the scope of groups...\n");
12966 findGroupScope(root.get());
12967 g_s.end();
12968
12969 g_s.begin("Computing module relations...\n");
12970 auto &mm = ModuleManager::instance();
12971 mm.resolvePartitions();
12972 mm.resolveImports();
12973 mm.collectExportedSymbols();
12974 g_s.end();
12975
12976 auto memberNameComp = [](const MemberNameLinkedMap::Ptr &n1,const MemberNameLinkedMap::Ptr &n2)
12977 {
12978 return qstricmp_sort(n1->memberName().data()+getPrefixIndex(n1->memberName()),
12979 n2->memberName().data()+getPrefixIndex(n2->memberName())
12980 )<0;
12981 };
12982
12983 auto classComp = [](const ClassLinkedMap::Ptr &c1,const ClassLinkedMap::Ptr &c2)
12984 {
12985 if (Config_getBool(SORT_BY_SCOPE_NAME))
12986 {
12987 return qstricmp_sort(c1->name(), c2->name())<0;
12988 }
12989 else
12990 {
12991 int i = qstricmp_sort(c1->className(), c2->className());
12992 return i==0 ? qstricmp_sort(c1->name(), c2->name())<0 : i<0;
12993 }
12994 };
12995
12996 auto namespaceComp = [](const NamespaceLinkedMap::Ptr &n1,const NamespaceLinkedMap::Ptr &n2)
12997 {
12998 return qstricmp_sort(n1->name(),n2->name())<0;
12999 };
13000
13001 auto conceptComp = [](const ConceptLinkedMap::Ptr &c1,const ConceptLinkedMap::Ptr &c2)
13002 {
13003 return qstricmp_sort(c1->name(),c2->name())<0;
13004 };
13005
13006 g_s.begin("Sorting lists...\n");
13007 std::stable_sort(Doxygen::memberNameLinkedMap->begin(),
13009 memberNameComp);
13010 std::stable_sort(Doxygen::functionNameLinkedMap->begin(),
13012 memberNameComp);
13013 std::stable_sort(Doxygen::hiddenClassLinkedMap->begin(),
13015 classComp);
13016 std::stable_sort(Doxygen::classLinkedMap->begin(),
13018 classComp);
13019 std::stable_sort(Doxygen::conceptLinkedMap->begin(),
13021 conceptComp);
13022 std::stable_sort(Doxygen::namespaceLinkedMap->begin(),
13024 namespaceComp);
13025 g_s.end();
13026
13027 g_s.begin("Determining which enums are documented\n");
13029 g_s.end();
13030
13031 g_s.begin("Computing member relations...\n");
13034 g_s.end();
13035
13036 g_s.begin("Building full member lists recursively...\n");
13038 g_s.end();
13039
13040 g_s.begin("Adding members to member groups.\n");
13042 g_s.end();
13043
13044 if (Config_getBool(DISTRIBUTE_GROUP_DOC))
13045 {
13046 g_s.begin("Distributing member group documentation.\n");
13048 g_s.end();
13049 }
13050
13051 g_s.begin("Computing member references...\n");
13053 g_s.end();
13054
13055 if (Config_getBool(INHERIT_DOCS))
13056 {
13057 g_s.begin("Inheriting documentation...\n");
13059 g_s.end();
13060 }
13061
13062
13063 // compute the shortest possible names of all files
13064 // without losing the uniqueness of the file names.
13065 g_s.begin("Generating disk names...\n");
13067 g_s.end();
13068
13069 g_s.begin("Adding source references...\n");
13071 g_s.end();
13072
13073 g_s.begin("Adding xrefitems...\n");
13076 g_s.end();
13077
13078 g_s.begin("Adding requirements...\n");
13081 g_s.end();
13082
13083 g_s.begin("Sorting member lists...\n");
13085 g_s.end();
13086
13087 g_s.begin("Setting anonymous enum type...\n");
13089 g_s.end();
13090
13091 g_s.begin("Computing dependencies between directories...\n");
13093 g_s.end();
13094
13095 g_s.begin("Generating citations page...\n");
13097 g_s.end();
13098
13099 g_s.begin("Counting members...\n");
13100 countMembers();
13101 g_s.end();
13102
13103 g_s.begin("Counting data structures...\n");
13105 g_s.end();
13106
13107 g_s.begin("Resolving user defined references...\n");
13109 g_s.end();
13110
13111 g_s.begin("Finding anchors and sections in the documentation...\n");
13113 g_s.end();
13114
13115 g_s.begin("Transferring function references...\n");
13117 g_s.end();
13118
13119 g_s.begin("Combining using relations...\n");
13121 g_s.end();
13122
13124 g_s.begin("Adding members to index pages...\n");
13126 addToIndices();
13127 g_s.end();
13128
13129 g_s.begin("Correcting members for VHDL...\n");
13131 g_s.end();
13132
13133 g_s.begin("Computing tooltip texts...\n");
13135 g_s.end();
13136
13137 if (Config_getBool(SORT_GROUP_NAMES))
13138 {
13139 std::stable_sort(Doxygen::groupLinkedMap->begin(),
13141 [](const auto &g1,const auto &g2)
13142 { return g1->groupTitle() < g2->groupTitle(); });
13143
13144 for (const auto &gd : *Doxygen::groupLinkedMap)
13145 {
13146 gd->sortSubGroups();
13147 }
13148 }
13149
13150 printNavTree(root.get(),0);
13152}
13153
13155{
13156 AUTO_TRACE();
13157 /**************************************************************************
13158 * Initialize output generators *
13159 **************************************************************************/
13160
13161 /// add extra languages for which we can only produce syntax highlighted code
13163
13164 //// dump all symbols
13165 if (g_dumpSymbolMap)
13166 {
13167 dumpSymbolMap();
13168 exit(0);
13169 }
13170
13171 bool generateHtml = Config_getBool(GENERATE_HTML);
13172 bool generateLatex = Config_getBool(GENERATE_LATEX);
13173 bool generateMan = Config_getBool(GENERATE_MAN);
13174 bool generateRtf = Config_getBool(GENERATE_RTF);
13175 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
13176
13177
13179 if (generateHtml)
13180 {
13184 }
13185 if (generateLatex)
13186 {
13189 }
13190 if (generateDocbook)
13191 {
13194 }
13195 if (generateMan)
13196 {
13197 g_outputList->add<ManGenerator>();
13199 }
13200 if (generateRtf)
13201 {
13202 g_outputList->add<RTFGenerator>();
13204 }
13205 if (Config_getBool(USE_HTAGS))
13206 {
13208 QCString htmldir = Config_getString(HTML_OUTPUT);
13209 if (!Htags::execute(htmldir))
13210 err("USE_HTAGS is YES but htags(1) failed. \n");
13211 else if (!Htags::loadFilemap(htmldir))
13212 err("htags(1) ended normally but failed to load the filemap. \n");
13213 }
13214
13215 /**************************************************************************
13216 * Generate documentation *
13217 **************************************************************************/
13218
13219 g_s.begin("Generating style sheet...\n");
13220 //printf("writing style info\n");
13221 g_outputList->writeStyleInfo(0); // write first part
13222 g_s.end();
13223
13224 bool searchEngine = Config_getBool(SEARCHENGINE);
13225 bool serverBasedSearch = Config_getBool(SERVER_BASED_SEARCH);
13226
13227 g_s.begin("Generating search indices...\n");
13228 if (searchEngine && !serverBasedSearch && generateHtml)
13229 {
13231 }
13232
13233 // generate search indices (need to do this before writing other HTML
13234 // pages as these contain a drop down menu with options depending on
13235 // what categories we find in this function.
13236 if (generateHtml && searchEngine)
13237 {
13238 QCString searchDirName = Config_getString(HTML_OUTPUT)+"/search";
13239 Dir searchDir(searchDirName.str());
13240 if (!searchDir.exists() && !searchDir.mkdir(searchDirName.str()))
13241 {
13242 term("Could not create search results directory '{}' $PWD='{}'\n",
13243 searchDirName,Dir::currentDirPath());
13244 }
13245 HtmlGenerator::writeSearchData(searchDirName);
13246 if (!serverBasedSearch) // client side search index
13247 {
13249 }
13250 }
13251 g_s.end();
13252
13253 // copy static stuff
13254 if (generateHtml)
13255 {
13257 copyLogo(Config_getString(HTML_OUTPUT));
13258 copyIcon(Config_getString(HTML_OUTPUT));
13259 copyExtraFiles(Config_getList(HTML_EXTRA_FILES),"HTML_EXTRA_FILES",Config_getString(HTML_OUTPUT));
13260 }
13261 if (generateLatex)
13262 {
13264 copyLogo(Config_getString(LATEX_OUTPUT));
13265 copyIcon(Config_getString(LATEX_OUTPUT));
13266 copyExtraFiles(Config_getList(LATEX_EXTRA_FILES),"LATEX_EXTRA_FILES",Config_getString(LATEX_OUTPUT));
13267 }
13268 if (generateDocbook)
13269 {
13270 copyLogo(Config_getString(DOCBOOK_OUTPUT));
13271 copyIcon(Config_getString(DOCBOOK_OUTPUT));
13272 }
13273 if (generateRtf)
13274 {
13275 copyLogo(Config_getString(RTF_OUTPUT));
13276 copyIcon(Config_getString(RTF_OUTPUT));
13277 copyExtraFiles(Config_getList(RTF_EXTRA_FILES),"RTF_EXTRA_FILES",Config_getString(RTF_OUTPUT));
13278 }
13279
13281 if (fm.hasFormulas() && generateHtml
13282 && !Config_getBool(USE_MATHJAX))
13283 {
13284 g_s.begin("Generating images for formulas in HTML...\n");
13285 fm.generateImages(Config_getString(HTML_OUTPUT), Config_getEnum(HTML_FORMULA_FORMAT)==HTML_FORMULA_FORMAT_t::svg ?
13287 g_s.end();
13288 }
13289 if (fm.hasFormulas() && generateRtf)
13290 {
13291 g_s.begin("Generating images for formulas in RTF...\n");
13293 g_s.end();
13294 }
13295
13296 if (fm.hasFormulas() && generateDocbook)
13297 {
13298 g_s.begin("Generating images for formulas in Docbook...\n");
13300 g_s.end();
13301 }
13302
13303 g_s.begin("Generating example documentation...\n");
13305 g_s.end();
13306
13307 g_s.begin("Generating file sources...\n");
13309 g_s.end();
13310
13311 g_s.begin("Generating file documentation...\n");
13313 g_s.end();
13314
13315 g_s.begin("Generating page documentation...\n");
13317 g_s.end();
13318
13319 g_s.begin("Generating group documentation...\n");
13321 g_s.end();
13322
13323 g_s.begin("Generating class documentation...\n");
13325 g_s.end();
13326
13327 g_s.begin("Generating concept documentation...\n");
13329 g_s.end();
13330
13331 g_s.begin("Generating module documentation...\n");
13333 g_s.end();
13334
13335 g_s.begin("Generating namespace documentation...\n");
13337 g_s.end();
13338
13339 if (Config_getBool(GENERATE_LEGEND))
13340 {
13341 g_s.begin("Generating graph info page...\n");
13343 g_s.end();
13344 }
13345
13346 g_s.begin("Generating directory documentation...\n");
13348 g_s.end();
13349
13350 if (g_outputList->size()>0)
13351 {
13353 }
13354
13355 g_s.begin("finalizing index lists...\n");
13356 Doxygen::indexList->finalize();
13357 g_s.end();
13358
13359 g_s.begin("writing tag file...\n");
13360 writeTagFile();
13361 g_s.end();
13362
13363 if (Config_getBool(GENERATE_XML))
13364 {
13365 g_s.begin("Generating XML output...\n");
13367 generateXML();
13369 g_s.end();
13370 }
13371 if (Config_getBool(GENERATE_SQLITE3))
13372 {
13373 g_s.begin("Generating SQLITE3 output...\n");
13375 g_s.end();
13376 }
13377
13378 if (Config_getBool(GENERATE_AUTOGEN_DEF))
13379 {
13380 g_s.begin("Generating AutoGen DEF output...\n");
13381 generateDEF();
13382 g_s.end();
13383 }
13384 if (Config_getBool(GENERATE_PERLMOD))
13385 {
13386 g_s.begin("Generating Perl module output...\n");
13388 g_s.end();
13389 }
13390 if (generateHtml && searchEngine && serverBasedSearch)
13391 {
13392 g_s.begin("Generating search index\n");
13393 if (Doxygen::searchIndex.kind()==SearchIndexIntf::Internal) // write own search index
13394 {
13396 Doxygen::searchIndex.write(Config_getString(HTML_OUTPUT)+"/search/search.idx");
13397 }
13398 else // write data for external search index
13399 {
13401 QCString searchDataFile = Config_getString(SEARCHDATA_FILE);
13402 if (searchDataFile.isEmpty())
13403 {
13404 searchDataFile="searchdata.xml";
13405 }
13406 if (!Portable::isAbsolutePath(searchDataFile.data()))
13407 {
13408 searchDataFile.prepend(Config_getString(OUTPUT_DIRECTORY)+"/");
13409 }
13410 Doxygen::searchIndex.write(searchDataFile);
13411 }
13412 g_s.end();
13413 }
13414
13415 if (generateRtf)
13416 {
13417 g_s.begin("Combining RTF output...\n");
13418 if (!RTFGenerator::preProcessFileInplace(Config_getString(RTF_OUTPUT),"refman.rtf"))
13419 {
13420 err("An error occurred during post-processing the RTF files!\n");
13421 }
13422 g_s.end();
13423 }
13424
13425 g_s.begin("Running plantuml with JAVA...\n");
13427 g_s.end();
13428
13429 if (Config_getBool(HAVE_DOT))
13430 {
13431 g_s.begin("Running dot...\n");
13433 g_s.end();
13434 }
13435
13436 if (generateHtml &&
13437 Config_getBool(GENERATE_HTMLHELP) &&
13438 !Config_getString(HHC_LOCATION).isEmpty())
13439 {
13440 g_s.begin("Running html help compiler...\n");
13442 g_s.end();
13443 }
13444
13445 if ( generateHtml &&
13446 Config_getBool(GENERATE_QHP) &&
13447 !Config_getString(QHG_LOCATION).isEmpty())
13448 {
13449 g_s.begin("Running qhelpgenerator...\n");
13451 g_s.end();
13452 }
13453
13454 g_outputList->cleanup();
13456
13457 msg("type lookup cache used {}/{} hits={} misses={}\n",
13459 Doxygen::typeLookupCache->capacity(),
13461 Doxygen::typeLookupCache->misses());
13462 msg("symbol lookup cache used {}/{} hits={} misses={}\n",
13464 Doxygen::symbolLookupCache->capacity(),
13466 Doxygen::symbolLookupCache->misses());
13467 int typeCacheParam = computeIdealCacheParam(static_cast<size_t>(Doxygen::typeLookupCache->misses()*2/3)); // part of the cache is flushed, hence the 2/3 correction factor
13468 int symbolCacheParam = computeIdealCacheParam(static_cast<size_t>(Doxygen::symbolLookupCache->misses()));
13469 int cacheParam = std::max(typeCacheParam,symbolCacheParam);
13470 if (cacheParam>Config_getInt(LOOKUP_CACHE_SIZE))
13471 {
13472 msg("Note: based on cache misses the ideal setting for LOOKUP_CACHE_SIZE is {} at the cost of higher memory usage.\n",cacheParam);
13473 }
13474
13476 {
13477
13478 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
13479 if (numThreads<1) numThreads=1;
13480 msg("Total elapsed time: {:.6f} seconds\n(of which an average of {:.6f} seconds per thread waiting for external tools to finish)\n",
13481 (static_cast<double>(Debug::elapsedTime())),
13482 Portable::getSysElapsedTime()/static_cast<double>(numThreads)
13483 );
13484 g_s.print();
13485
13487 msg("finished...\n");
13489 }
13490 else
13491 {
13492 msg("finished...\n");
13493 }
13494
13495
13496 /**************************************************************************
13497 * Start cleaning up *
13498 **************************************************************************/
13499
13501
13503 Dir thisDir;
13504 thisDir.remove(Doxygen::filterDBFileName.str());
13506 exitTracing();
13508 delete Doxygen::clangUsrMap;
13510
13511 //dumpDocNodeSizes();
13512}
void readAliases()
Definition aliases.cpp:161
constexpr auto prefix
Definition anchor.cpp:44
std::vector< ArgumentList > ArgumentLists
Definition arguments.h:147
This class represents an function or template argument list.
Definition arguments.h:65
RefQualifierType refQualifier() const
Definition arguments.h:116
bool noParameters() const
Definition arguments.h:117
bool pureSpecifier() const
Definition arguments.h:113
iterator end()
Definition arguments.h:94
void setTrailingReturnType(const QCString &s)
Definition arguments.cpp:36
bool hasParameters() const
Definition arguments.h:76
bool isDeleted() const
Definition arguments.h:115
QCString trailingReturnType() const
Definition arguments.h:114
size_t size() const
Definition arguments.h:100
void setPureSpecifier(bool b)
Definition arguments.h:121
bool constSpecifier() const
Definition arguments.h:111
void push_back(const Argument &a)
Definition arguments.h:102
bool empty() const
Definition arguments.h:99
void setConstSpecifier(bool b)
Definition arguments.h:119
void setRefQualifier(RefQualifierType t)
Definition arguments.h:126
void setIsDeleted(bool b)
Definition arguments.h:125
iterator begin()
Definition arguments.h:93
bool volatileSpecifier() const
Definition arguments.h:112
void setNoParameters(bool b)
Definition arguments.h:127
void setVolatileSpecifier(bool b)
Definition arguments.h:120
Definition cache.h:32
static CitationManager & instance()
Definition cite.cpp:85
void clear()
clears the database
Definition cite.cpp:110
void generatePage()
Generate the citations page.
Definition cite.cpp:331
std::unique_ptr< ClangTUParser > createTUParser(const FileDef *fd) const
static ClangParser * instance()
Returns the one and only instance of the class.
Clang parser object for a single translation unit, which consists of a source file and the directly o...
Definition clangparser.h:25
void switchToFile(const FileDef *fd)
Switches to another file within the translation unit started with start().
void parse()
Parse the file given at construction time as a translation unit This file should already be preproces...
A abstract class representing of a compound symbol.
Definition classdef.h:104
virtual const ArgumentList & templateArguments() const =0
Returns the template arguments of this class.
virtual QCString compoundTypeString() const =0
Returns the type of compound as a string.
virtual void writeDocumentation(OutputList &ol) const =0
virtual void writeMemberList(OutputList &ol) const =0
virtual bool isTemplate() const =0
Returns TRUE if this class is a template.
virtual const BaseClassList & baseClasses() const =0
Returns the list of base classes from which this class directly inherits.
virtual const MemberDef * getMemberByName(const QCString &) const =0
Returns the member with the given name.
virtual const TemplateInstanceList & getTemplateInstances() const =0
Returns a sorted dictionary with all template instances found for this template class.
virtual int isBaseClass(const ClassDef *bcd, bool followInstances, const QCString &templSpec=QCString()) const =0
Returns TRUE iff bcd is a direct or indirect base class of this class.
virtual ArgumentLists getTemplateParameterLists() const =0
Returns the template parameter lists that form the template declaration of this class.
virtual Protection protection() const =0
Return the protection level (Public,Protected,Private) in which this compound was found.
virtual MemberList * getMemberList(MemberListType lt) const =0
Returns the members in the list identified by lt.
virtual bool isForwardDeclared() const =0
Returns TRUE if this class represents a forward declaration of a template class.
virtual bool isSubClass(ClassDef *bcd, int level=0) const =0
Returns TRUE iff bcd is a direct or indirect sub class of this class.
virtual void moveTo(Definition *)=0
virtual const TemplateNameMap & getTemplateBaseClassNames() const =0
virtual bool isEmbeddedInOuterScope() const =0
virtual const MemberNameInfoLinkedMap & memberNameInfoLinkedMap() const =0
Returns a dictionary of all members.
virtual bool isImplicitTemplateInstance() const =0
virtual QCString qualifiedNameWithTemplateParameters(const ArgumentLists *actualParams=nullptr, uint32_t *actualParamIndex=nullptr) const =0
virtual std::unique_ptr< ClassDef > deepCopy(const QCString &name) const =0
virtual const ClassDef * templateMaster() const =0
Returns the template master of which this class is an instance.
CompoundType
The various compound types.
Definition classdef.h:109
@ Singleton
Definition classdef.h:117
@ Interface
Definition classdef.h:112
@ Exception
Definition classdef.h:115
virtual CompoundType compoundType() const =0
Returns the type of compound this is, i.e.
virtual bool containsOverload(const MemberDef *md) const =0
virtual ClassLinkedRefMap getClasses() const =0
returns the classes nested into this class
virtual FileDef * getFileDef() const =0
Returns the namespace this compound is in, or 0 if it has a global scope.
virtual QCString requiresClause() const =0
virtual void writeTagFile(TextStream &) const =0
virtual void writeDocumentationForInnerClasses(OutputList &ol) const =0
virtual void computeAnchors()=0
virtual void addTypeConstraints()=0
virtual void overrideCollaborationGraph(bool e)=0
virtual void setClassName(const QCString &name)=0
virtual void countMembers()=0
virtual void addMembersToMemberGroup()=0
virtual void makeTemplateArgument(bool b=TRUE)=0
virtual void setTemplateBaseClassNames(const TemplateNameMap &templateNames)=0
virtual void insertExplicitTemplateInstance(ClassDef *instance, const QCString &spec)=0
virtual void setMetaData(const QCString &md)=0
virtual void setFileDef(FileDef *fd)=0
virtual void addUsedClass(ClassDef *cd, const QCString &accessName, Protection prot)=0
virtual void reclassifyMember(MemberDefMutable *md, MemberType t)=0
virtual ClassDef * insertTemplateInstance(const QCString &fileName, int startLine, int startColumn, const QCString &templSpec, bool &freshInstance)=0
virtual void insertBaseClass(ClassDef *, const QCString &name, Protection p, Specifier s, const QCString &t=QCString())=0
virtual void setTemplateArguments(const ArgumentList &al)=0
virtual void setTemplateMaster(const ClassDef *tm)=0
virtual void mergeCategory(ClassDef *category)=0
virtual void addQualifiers(const StringVector &qualifiers)=0
virtual void setClassSpecifier(TypeSpecifier spec)=0
virtual void insertSubClass(ClassDef *, Protection p, Specifier s, const QCString &t=QCString())=0
virtual void insertUsedFile(const FileDef *)=0
virtual void setRequiresClause(const QCString &req)=0
virtual void setTagLessReference(const ClassDef *cd)=0
virtual void setUsedOnly(bool b)=0
virtual void sortMemberLists()=0
virtual void setProtection(Protection p)=0
virtual void setTypeConstraints(const ArgumentList &al)=0
virtual void overrideInheritanceGraph(CLASS_GRAPH_t e)=0
virtual void setAnonymousEnumType()=0
virtual void setCompoundType(CompoundType t)=0
virtual void distributeMemberGroupDocumentation()=0
virtual void findSectionsInDocumentation()=0
virtual void addUsedByClass(ClassDef *cd, const QCString &accessName, Protection prot)=0
virtual void insertMember(MemberDef *)=0
virtual void sortAllMembersList()=0
virtual void addMembersToTemplateInstance(const ClassDef *cd, const ArgumentList &templateArguments, const QCString &templSpec)=0
virtual void mergeMembers()=0
virtual void setIsStatic(bool b)=0
virtual void setSubGrouping(bool enabled)=0
virtual void addCodePart(const QCString &code, int lineNr, int colNr)=0
virtual void setFileDef(FileDef *fd)=0
virtual void writeTagFile(TextStream &)=0
virtual void setInitializer(const QCString &init)=0
virtual void addDocPart(const QCString &doc, int lineNr, int colNr)=0
virtual void writeDocumentation(OutputList &ol)=0
virtual void setGroupId(int id)=0
virtual void findSectionsInDocumentation()=0
virtual void setTemplateArguments(const ArgumentList &al)=0
@ ExtCmd
Definition debug.h:36
@ Sections
Definition debug.h:48
@ Time
Definition debug.h:35
@ Qhp
Definition debug.h:44
@ Entries
Definition debug.h:47
static void printFlags()
Definition debug.cpp:137
static void clearFlag(const DebugMask mask)
Definition debug.cpp:122
static bool isFlagSet(const DebugMask mask)
Definition debug.cpp:132
static void print(DebugMask mask, int prio, fmt::format_string< Args... > fmt, Args &&... args)
Definition debug.h:76
static double elapsedTime()
Definition debug.cpp:200
static void startTimer()
Definition debug.cpp:195
static bool setFlagStr(const QCString &label)
Definition debug.cpp:103
static void setFlag(const DebugMask mask)
Definition debug.cpp:117
The common base class of all entity definitions found in the sources.
Definition definition.h:77
virtual QCString docFile() const =0
virtual const QCString & localName() const =0
virtual int getEndBodyLine() const =0
virtual SrcLangExt getLanguage() const =0
Returns the programming language this definition was written in.
virtual int docLine() const =0
virtual QCString getDefFileName() const =0
virtual bool isLinkable() const =0
virtual int getDefLine() const =0
virtual DefType definitionType() const =0
virtual QCString anchor() const =0
virtual int inbodyLine() const =0
virtual const FileDef * getBodyDef() const =0
virtual int briefLine() const =0
virtual bool hasDocumentation() const =0
virtual bool isLinkableInProject() const =0
virtual QCString briefDescription(bool abbreviate=FALSE) const =0
virtual bool isAnonymous() const =0
virtual bool isHidden() const =0
virtual const Definition * findInnerCompound(const QCString &name) const =0
virtual int getStartDefLine() const =0
virtual const GroupList & partOfGroups() const =0
virtual QCString documentation() const =0
virtual QCString qualifiedName() const =0
virtual QCString displayName(bool includeScope=TRUE) const =0
virtual bool isArtificial() const =0
virtual QCString briefFile() const =0
virtual QCString getOutputFileBase() const =0
virtual Definition * getOuterScope() const =0
virtual int getStartBodyLine() const =0
virtual int getDefColumn() const =0
virtual bool isReference() const =0
virtual QCString inbodyDocumentation() const =0
virtual QCString inbodyFile() const =0
virtual const QCString & name() const =0
virtual void mergeReferencedBy(const Definition *other)=0
virtual void setExported(bool b)=0
virtual void setBodySegment(int defLine, int bls, int ble)=0
virtual void setName(const QCString &name)=0
virtual void setHidden(bool b)=0
virtual void mergeReferences(const Definition *other)=0
virtual void setDocumentation(const QCString &d, const QCString &docFile, int docLine, bool stripWhiteSpace=TRUE)=0
virtual void setDefFile(const QCString &df, int defLine, int defColumn)=0
virtual void addInnerCompound(Definition *d)=0
virtual void addSectionsToDefinition(const std::vector< const SectionInfo * > &anchorList)=0
virtual void setInbodyDocumentation(const QCString &d, const QCString &docFile, int docLine)=0
virtual void setLanguage(SrcLangExt lang)=0
virtual void setOuterScope(Definition *d)=0
virtual void setArtificial(bool b)=0
virtual void setId(const QCString &name)=0
virtual void makePartOfGroup(GroupDef *gd)=0
virtual void setBodyDef(const FileDef *fd)=0
virtual void setBriefDescription(const QCString &b, const QCString &briefFile, int briefLine)=0
virtual void setReference(const QCString &r)=0
virtual void setRequirementReferences(const RequirementRefs &rqli)=0
virtual void setRefItems(const RefItemVector &sli)=0
virtual void computeTooltip()=0
A model of a directory symbol.
Definition dirdef.h:110
virtual void overrideDirectoryGraph(bool e)=0
Class representing a directory in the file system.
Definition dir.h:75
static std::string currentDirPath()
Definition dir.cpp:342
std::string absPath() const
Definition dir.cpp:364
bool mkdir(const std::string &path, bool acceptsAbsPath=true) const
Definition dir.cpp:295
void setPath(const std::string &path)
Definition dir.cpp:229
bool remove(const std::string &path, bool acceptsAbsPath=true) const
Definition dir.cpp:314
DirIterator iterator() const
Definition dir.cpp:239
static std::string cleanDirPath(const std::string &path)
Definition dir.cpp:357
static bool setCurrent(const std::string &path)
Definition dir.cpp:350
bool exists() const
Definition dir.cpp:257
A linked map of directories.
Definition dirdef.h:175
A class that generates docset files.
Definition docsets.h:36
static void init()
bool run()
Definition dot.cpp:128
static DotManager * instance()
Definition dot.cpp:78
static NamespaceLinkedMap * namespaceLinkedMap
Definition doxygen.h:114
static ConceptLinkedMap * conceptLinkedMap
Definition doxygen.h:97
static bool suppressDocWarnings
Definition doxygen.h:131
static FileNameLinkedMap * plantUmlFileNameLinkedMap
Definition doxygen.h:109
static bool parseSourcesNeeded
Definition doxygen.h:122
static StringUnorderedSet inputPaths
Definition doxygen.h:103
static std::unique_ptr< PageDef > mainPage
Definition doxygen.h:100
static bool clangAssistedParsing
Definition doxygen.h:137
static StringUnorderedSet expandAsDefinedSet
Definition doxygen.h:118
static FileNameLinkedMap * inputNameLinkedMap
Definition doxygen.h:104
static ParserManager * parserManager
Definition doxygen.h:130
static Cache< std::string, LookupInfo > * typeLookupCache
Definition doxygen.h:126
static InputFileEncodingList inputFileEncodingList
Definition doxygen.h:139
static ClassLinkedMap * classLinkedMap
Definition doxygen.h:95
static MemberNameLinkedMap * functionNameLinkedMap
Definition doxygen.h:111
static PageLinkedMap * exampleLinkedMap
Definition doxygen.h:98
static FileNameLinkedMap * dotFileNameLinkedMap
Definition doxygen.h:106
static NamespaceDefMutable * globalScope
Definition doxygen.h:120
static FileNameLinkedMap * imageNameLinkedMap
Definition doxygen.h:105
static FileNameLinkedMap * mscFileNameLinkedMap
Definition doxygen.h:107
static QCString verifiedDotPath
Definition doxygen.h:138
static MemberGroupInfoMap memberGroupInfoMap
Definition doxygen.h:117
static QCString spaces
Definition doxygen.h:134
static IndexList * indexList
Definition doxygen.h:133
static StaticInitMap staticInitMap
Definition doxygen.h:142
static Cache< std::string, LookupInfo > * symbolLookupCache
Definition doxygen.h:127
static StringMap tagDestinationMap
Definition doxygen.h:115
static std::mutex countFlowKeywordsMutex
Definition doxygen.h:140
static ClassLinkedMap * hiddenClassLinkedMap
Definition doxygen.h:96
static FileNameLinkedMap * diaFileNameLinkedMap
Definition doxygen.h:108
static QCString htmlFileExtension
Definition doxygen.h:121
static QCString filterDBFileName
Definition doxygen.h:132
static PageLinkedMap * pageLinkedMap
Definition doxygen.h:99
static bool generatingXmlOutput
Definition doxygen.h:135
static std::unique_ptr< NamespaceDef > globalNamespaceDef
Definition doxygen.h:119
static DefinesPerFileList macroDefinitions
Definition doxygen.h:136
static DirLinkedMap * dirLinkedMap
Definition doxygen.h:128
static NamespaceAliasInfoMap namespaceAliasMap
Definition doxygen.h:112
static MemberNameLinkedMap * memberNameLinkedMap
Definition doxygen.h:110
static SymbolMap< Definition > * symbolMap
Definition doxygen.h:124
static StringUnorderedSet tagFileSet
Definition doxygen.h:116
static FileNameLinkedMap * includeNameLinkedMap
Definition doxygen.h:101
static FileNameLinkedMap * exampleNameLinkedMap
Definition doxygen.h:102
static SearchIndexIntf searchIndex
Definition doxygen.h:123
static DirRelationLinkedMap dirRelations
Definition doxygen.h:129
static std::mutex addExampleMutex
Definition doxygen.h:141
static ClangUsrMap * clangUsrMap
Definition doxygen.h:125
static GroupLinkedMap * groupLinkedMap
Definition doxygen.h:113
Generator for Eclipse help files.
Definition eclipsehelp.h:44
static EmojiEntityMapper & instance()
Returns the one and only instance of the Emoji entity mapper.
Definition emoji.cpp:1978
void writeEmojiFile(TextStream &t)
Writes the list of supported emojis to the given file.
Definition emoji.cpp:1999
Represents an unstructured piece of information, about an entity found in the sources.
Definition entry.h:117
TextStream initializer
initial value (for variables)
Definition entry.h:198
VhdlSpecifier vhdlSpec
VHDL specifiers.
Definition entry.h:184
bool subGrouping
automatically group class members?
Definition entry.h:189
RequirementRefs rqli
references to requirements
Definition entry.h:228
const std::vector< std::shared_ptr< Entry > > & children() const
Definition entry.h:140
bool proto
prototype ?
Definition entry.h:188
GroupDocType groupDocType
Definition entry.h:232
QCString metaData
Slice metadata.
Definition entry.h:235
int docLine
line number at which the documentation was found
Definition entry.h:202
QCString bitfields
member's bit fields
Definition entry.h:194
ArgumentList typeConstr
where clause (C#) for type constraints
Definition entry.h:216
void markAsProcessed() const
Definition entry.h:168
int endBodyLine
line number where the definition ends
Definition entry.h:219
bool exported
is the symbol exported from a C++20 module
Definition entry.h:190
const TagInfo * tagInfo() const
Definition entry.h:178
QCString includeName
include name (3 arg of \class)
Definition entry.h:200
QCString id
libclang id
Definition entry.h:233
ArgumentLists tArgLists
template argument declarations
Definition entry.h:196
LocalToc localToc
Definition entry.h:234
MethodTypes mtype
signal, slot, (dcop) method, or property?
Definition entry.h:182
@ GROUPDOC_NORMAL
defgroup
Definition entry.h:122
SrcLangExt lang
programming language in which this entry was found
Definition entry.h:229
Entry * parent() const
Definition entry.h:135
QCString inbodyDocs
documentation inside the body of a function
Definition entry.h:207
int startColumn
start column of entry in the source
Definition entry.h:226
QCString relates
related class (doc block)
Definition entry.h:210
bool explicitExternal
explicitly defined as external?
Definition entry.h:187
std::vector< const SectionInfo * > anchors
list of anchors defined in this entry
Definition entry.h:223
QCString fileName
file this entry was extracted from
Definition entry.h:224
RelatesType relatesType
how relates is handled
Definition entry.h:211
QCString write
property write accessor
Definition entry.h:213
QCString args
member argument string
Definition entry.h:193
QCString type
member type
Definition entry.h:174
std::vector< Grouping > groups
list of groups this entry belongs to
Definition entry.h:222
CommandOverrides commandOverrides
store info for commands whose default can be overridden
Definition entry.h:191
QCString exception
throw specification
Definition entry.h:215
int startLine
start line of entry in the source
Definition entry.h:225
QCString req
C++20 requires clause.
Definition entry.h:236
ArgumentList argList
member arguments as a list
Definition entry.h:195
QCString name
member name
Definition entry.h:175
QCString includeFile
include file (2 arg of \class, must be unique)
Definition entry.h:199
int inbodyLine
line number at which the body doc was found
Definition entry.h:208
EntryType section
entry type (see Sections);
Definition entry.h:173
QCString briefFile
file in which the brief desc. was found
Definition entry.h:206
int bodyLine
line number of the body in the source
Definition entry.h:217
int mGrpId
member group id
Definition entry.h:220
std::vector< BaseInfo > extends
list of base classes
Definition entry.h:221
Specifier virt
virtualness of the entry
Definition entry.h:192
std::vector< std::string > qualifiers
qualifiers specified with the qualifier command
Definition entry.h:237
QCString doc
documentation block (partly parsed)
Definition entry.h:201
QCString read
property read accessor
Definition entry.h:212
RefItemVector sli
special lists (test/todo/bug/deprecated/..) this entry is in
Definition entry.h:227
QCString docFile
file in which the documentation was found
Definition entry.h:203
Protection protection
class protection
Definition entry.h:181
bool artificial
Artificially introduced item.
Definition entry.h:231
bool hidden
does this represent an entity that is hidden from the output
Definition entry.h:230
QCString brief
brief description (doc block)
Definition entry.h:204
int briefLine
line number at which the brief desc. was found
Definition entry.h:205
FileDef * fileDef() const
Definition entry.h:170
int initLines
define/variable initializer lines to show
Definition entry.h:185
bool isStatic
static ?
Definition entry.h:186
QCString inbodyFile
file in which the body doc was found
Definition entry.h:209
TypeSpecifier spec
class/member specifiers
Definition entry.h:183
QCString inside
name of the class in which documents are found
Definition entry.h:214
Wrapper class for the Entry type.
Definition types.h:816
constexpr bool isCompoundDoc() const noexcept
Definition types.h:826
constexpr bool isFile() const noexcept
Definition types.h:825
constexpr bool isDoc() const noexcept
Definition types.h:827
ENTRY_TYPES constexpr bool isCompound() const noexcept
Definition types.h:823
std::string to_string() const
Definition types.h:828
constexpr bool isScope() const noexcept
Definition types.h:824
A class that generates a dynamic tree view side panel.
Definition ftvhelp.h:41
A model of a file symbol.
Definition filedef.h:99
virtual void addUsingDeclaration(const Definition *d)=0
virtual void removeMember(MemberDef *md)=0
virtual void insertClass(ClassDef *cd)=0
virtual void insertConcept(ConceptDef *cd)=0
virtual void overrideIncludeGraph(bool e)=0
virtual void writeSourceHeader(OutputList &ol)=0
virtual bool generateSourceFile() const =0
virtual const LinkedRefMap< NamespaceDef > & getUsedNamespaces() const =0
virtual QCString absFilePath() const =0
virtual bool isSource() const =0
virtual void parseSource(ClangTUParser *clangParser)=0
virtual void getAllIncludeFilesRecursively(StringVector &incFiles) const =0
virtual void setDiskName(const QCString &name)=0
virtual void writeSourceFooter(OutputList &ol)=0
virtual void writeSourceBody(OutputList &ol, ClangTUParser *clangParser)=0
virtual void addUsingDirective(NamespaceDef *nd)=0
virtual void overrideIncludedByGraph(bool e)=0
virtual const QCString & docName() const =0
virtual void insertMember(MemberDef *md)=0
virtual void insertNamespace(NamespaceDef *nd)=0
Minimal replacement for QFileInfo.
Definition fileinfo.h:23
std::string readLink() const
Definition fileinfo.cpp:84
bool isRelative() const
Definition fileinfo.cpp:58
bool isSymLink() const
Definition fileinfo.cpp:77
bool exists() const
Definition fileinfo.cpp:30
std::string fileName() const
Definition fileinfo.cpp:118
bool isReadable() const
Definition fileinfo.cpp:44
bool isDir() const
Definition fileinfo.cpp:70
bool isFile() const
Definition fileinfo.cpp:63
std::string dirPath(bool absPath=true) const
Definition fileinfo.cpp:137
std::string absFilePath() const
Definition fileinfo.cpp:101
Class representing all files with a certain base name.
Definition filename.h:30
Ordered dictionary of FileName objects.
Definition filename.h:73
void initFromRepository(const QCString &dir)
Definition formula.cpp:60
bool hasFormulas() const
Definition formula.cpp:720
void checkRepositories()
Definition formula.cpp:173
static FormulaManager & instance()
Definition formula.cpp:54
void generateImages(const QCString &outputDir, Format format, HighDPI hd=HighDPI::Off)
Definition formula.cpp:636
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:1193
static void writeSearchPage()
Definition htmlgen.cpp:3160
static void writeFooterFile(TextStream &t)
Definition htmlgen.cpp:1527
static void writeTabData()
Additional initialization after indices have been created.
Definition htmlgen.cpp:1344
static void writeSearchData(const QCString &dir)
Definition htmlgen.cpp:1353
static void writeExternalSearchPage()
Definition htmlgen.cpp:3259
static void writeStyleSheetFile(TextStream &t)
Definition htmlgen.cpp:1515
static void writeHeaderFile(TextStream &t, const QCString &cssname)
Definition htmlgen.cpp:1521
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:106
void countDataStructures()
Definition index.cpp:262
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:697
static void writeStyleSheetFile(TextStream &t)
Definition latexgen.cpp:703
static void writeHeaderFile(TextStream &t)
Definition latexgen.cpp:691
static void init()
Definition latexgen.cpp:633
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:4153
void addSearchDir(const QCString &dir)
Definition pre.l:4135
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:57
QCString ref() const
Definition section.h:71
QCString fileName() const
Definition section.h:73
int lineNr() const
Definition section.h:72
SectionInfo * replace(const QCString &label, const QCString &fileName, int lineNr, const QCString &title, SectionType type, int level, const QCString &ref=QCString())
Definition section.h:156
SectionInfo * add(const SectionInfo &si)
Definition section.h:138
static SectionManager & instance()
returns a reference to the singleton
Definition section.h:178
static constexpr int Page
Definition section.h:31
std::vector< stat > stats
Definition doxygen.cpp:269
void print()
Definition doxygen.cpp:246
void begin(const char *name)
Definition doxygen.cpp:233
void end()
Definition doxygen.cpp:239
std::chrono::steady_clock::time_point startTime
Definition doxygen.cpp:270
const Definition * resolveSymbol(const Definition *scope, const QCString &name, const QCString &args=QCString(), bool checkCV=false, bool insideCode=false, bool onlyLinkable=false)
Find the symbool definition matching name within the scope set.
const ClassDef * resolveClass(const Definition *scope, const QCString &name, bool maybeUnlinkable=false, bool mayBeHidden=false)
Find the class definition matching name within the scope set.
QCString getTemplateSpec() const
In case a call to resolveClass() points to a template specialization, the template part is return via...
ClassDefMutable * resolveClassMutable(const Definition *scope, const QCString &name, bool mayBeUnlinkable=false, bool mayBeHidden=false)
Wrapper around resolveClass that returns a mutable interface to the class object or a nullptr if the ...
const MemberDef * getTypedef() const
In case a call to resolveClass() resolves to a type member (e.g.
Text streaming class that buffers data.
Definition textstream.h:36
std::string str() const
Return the contents of the buffer as a std::string object.
Definition textstream.h:216
Class managing a pool of worker threads.
Definition threadpool.h:48
auto queue(F &&f, Args &&... args) -> std::future< decltype(f(args...))>
Queue the callable function f for the threads to execute.
Definition threadpool.h:77
Wrapper class for a number of boolean properties.
Definition types.h:654
std::string to_string() const
Definition types.h:698
static void correctMemberProperties(MemberDefMutable *md)
static void computeVhdlComponentRelations()
ClassDefMutable * toClassDefMutable(Definition *d)
ClassDef * getClass(const QCString &n)
std::unique_ptr< ClassDef > createClassDefAlias(const Definition *newScope, const ClassDef *cd)
Definition classdef.cpp:789
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:560
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:470
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:1328
static void findInheritedTemplateInstances()
Definition doxygen.cpp:5294
void printNavTree(Entry *root, int indent)
static void addClassToContext(const Entry *root)
Definition doxygen.cpp:938
static void makeTemplateInstanceRelation(const Entry *root, ClassDefMutable *cd)
Definition doxygen.cpp:5309
static StringUnorderedSet g_pathsVisited(1009)
static int findEndOfTemplate(const QCString &s, size_t startPos)
Definition doxygen.cpp:3080
static void buildGroupList(const Entry *root)
Definition doxygen.cpp:431
static void insertMemberAlias(Definition *outerScope, const MemberDef *md)
Definition doxygen.cpp:6677
static void findUsingDeclarations(const Entry *root, bool filterPythonPackages)
Definition doxygen.cpp:2053
static void flushCachedTemplateRelations()
Definition doxygen.cpp:9427
static bool isRecursiveBaseClass(const QCString &scope, const QCString &name)
Definition doxygen.cpp:4852
static void copyLatexStyleSheet()
static void generateDocsForClassList(const std::vector< ClassDefMutable * > &classList)
Definition doxygen.cpp:9087
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:2876
static bool isSpecialization(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists)
Definition doxygen.cpp:5978
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:2473
static void computeTemplateClassRelations()
Definition doxygen.cpp:5388
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:8141
static void runQHelpGenerator()
static void addConceptToContext(const Entry *root)
Definition doxygen.cpp:1160
static void addRelatedPage(Entry *root)
Definition doxygen.cpp:329
void initDoxygen()
static void addIncludeFile(DefMutable *cd, FileDef *ifd, const Entry *root)
Definition doxygen.cpp:588
static StringVector g_inputFiles
Definition doxygen.cpp:188
void printSectionsTree()
class Statistics g_s
static void createUsingMemberImportForClass(const Entry *root, ClassDefMutable *cd, const MemberDef *md, const QCString &fileName, const QCString &memName)
Definition doxygen.cpp:2157
static void generateXRefPages()
Definition doxygen.cpp:5567
static void findUsingDeclImports(const Entry *root)
Definition doxygen.cpp:2206
static void copyStyleSheet()
FindBaseClassRelation_Mode
Definition doxygen.cpp:286
@ Undocumented
Definition doxygen.cpp:289
@ TemplateInstances
Definition doxygen.cpp:287
@ DocumentedOnly
Definition doxygen.cpp:288
void distributeClassGroupRelations()
Definition doxygen.cpp:1491
static void generateGroupDocs()
static void findDirDocumentation(const Entry *root)
Definition doxygen.cpp:9665
void checkConfiguration()
check and resolve config options
QCString stripTemplateSpecifiers(const QCString &s)
Definition doxygen.cpp:685
static bool findClassRelation(const Entry *root, Definition *context, ClassDefMutable *cd, const BaseInfo *bi, const TemplateNameMap &templateNames, FindBaseClassRelation_Mode mode, bool isArtificial)
Definition doxygen.cpp:4894
static void resolveTemplateInstanceInType(const Entry *root, const Definition *scope, const MemberDef *md)
Definition doxygen.cpp:4822
static void organizeSubGroupsFiltered(const Entry *root, bool additional)
Definition doxygen.cpp:471
static void warnUndocumentedNamespaces()
Definition doxygen.cpp:5343
static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments, const std::string &name)
Definition doxygen.cpp:4501
static NamespaceDef * findUsedNamespace(const LinkedRefMap< NamespaceDef > &unl, const QCString &name)
Definition doxygen.cpp:1884
static void buildConceptList(const Entry *root)
Definition doxygen.cpp:1321
static void resolveClassNestingRelations()
Definition doxygen.cpp:1376
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:6720
static void findClassEntries(const Entry *root)
Definition doxygen.cpp:5257
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:1610
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:6190
static void vhdlCorrectMemberProperties()
Definition doxygen.cpp:8385
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:4771
static void substituteTemplatesInArgList(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists, const ArgumentList &src, ArgumentList &dst)
Definition doxygen.cpp:6092
static void computeMemberReferences()
Definition doxygen.cpp:5457
static void generateConfigFile(const QCString &configFile, bool shortList, bool updateOnly=FALSE)
static void transferRelatedFunctionDocumentation()
Definition doxygen.cpp:4415
static void addMembersToMemberGroup()
Definition doxygen.cpp:9292
static bool tryAddEnumDocsToGroupMember(const Entry *root, const QCString &name)
Definition doxygen.cpp:8022
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:6609
static void findMainPageTagFiles(Entry *root)
Definition doxygen.cpp:9835
static void distributeConceptGroups()
Definition doxygen.cpp:1343
static void transferFunctionDocumentation()
Definition doxygen.cpp:4334
static void setAnonymousEnumType()
Definition doxygen.cpp:9032
static void sortMemberLists()
Definition doxygen.cpp:8937
static void createTemplateInstanceMembers()
Definition doxygen.cpp:8513
void transferStaticInstanceInitializers()
Definition doxygen.cpp:4464
static void findObjCMethodDefinitions(const Entry *root)
Definition doxygen.cpp:7526
static void dumpSymbolMap()
static void buildTypedefList(const Entry *root)
Definition doxygen.cpp:3403
static void findGroupScope(const Entry *root)
Definition doxygen.cpp:446
static void generateFileDocs()
Definition doxygen.cpp:8750
static void findDefineDocumentation(Entry *root)
Definition doxygen.cpp:9578
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:4564
void parseInput()
static void findMemberDocumentation(const Entry *root)
Definition doxygen.cpp:7496
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:6136
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:5771
static void distributeMemberGroupDocumentation()
Definition doxygen.cpp:9330
static void generateNamespaceClassDocs(const ClassLinkedRefMap &classList)
static void addEnumValuesToEnums(const Entry *root)
Definition doxygen.cpp:7730
static void generatePageDocs()
Definition doxygen.cpp:9965
static void resolveUserReferences()
Definition doxygen.cpp:9897
static void buildRequirementsList(Entry *root)
Definition doxygen.cpp:9726
static void compareDoxyfile(Config::CompareMode diffList)
static void addPageToContext(PageDef *pd, Entry *root)
Definition doxygen.cpp:310
static void buildVarList(const Entry *root)
Definition doxygen.cpp:3540
static void buildSequenceList(const Entry *root)
Definition doxygen.cpp:3503
static void generateFileSources()
Definition doxygen.cpp:8584
static QCString substituteTemplatesInString(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists, const std::string &src)
Definition doxygen.cpp:6008
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:2634
static void generateClassDocs()
Definition doxygen.cpp:9185
static void buildNamespaceList(const Entry *root)
Definition doxygen.cpp:1715
static int computeIdealCacheParam(size_t v)
static void readTagFile(const std::shared_ptr< Entry > &root, const QCString &tagLine)
static void findIncludedUsingDirectives()
Definition doxygen.cpp:2456
static void addDefineDoc(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:9551
static void addMemberDocs(const Entry *root, MemberDefMutable *md, const QCString &funcDecl, const ArgumentList *al, bool over_load, TypeSpecifier spec)
Definition doxygen.cpp:5581
static void countMembers()
Definition doxygen.cpp:9046
void clearAll()
Definition doxygen.cpp:202
static void devUsage()
static void addInterfaceOrServiceToServiceOrSingleton(const Entry *root, ClassDefMutable *cd, QCString const &rname)
Definition doxygen.cpp:3572
static void organizeSubGroups(const Entry *root)
Definition doxygen.cpp:490
static void applyMemberOverrideOptions(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:2145
void searchInputFiles()
static void findFriends()
Definition doxygen.cpp:4237
static void findEnums(const Entry *root)
Definition doxygen.cpp:7554
static void dumpSymbol(TextStream &t, Definition *d)
static void addClassAndNestedClasses(std::vector< ClassDefMutable * > &list, ClassDefMutable *cd)
Definition doxygen.cpp:9162
static void addEnumDocs(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:7980
static void addListReferences()
Definition doxygen.cpp:5558
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:5235
static Definition * findScopeFromQualifiedName(NamespaceDefMutable *startScope, const QCString &n, FileDef *fileScope, const TagInfo *tagInfo)
Definition doxygen.cpp:781
static ClassDef * findClassWithinClassContext(Definition *context, ClassDef *cd, const QCString &name)
Definition doxygen.cpp:4529
static void buildGroupListFiltered(const Entry *root, bool additional, bool includeExternal)
Definition doxygen.cpp:360
static void runHtmlHelpCompiler()
static QCString extractClassName(const Entry *root)
Definition doxygen.cpp:5266
static void addMembersToIndex()
Definition doxygen.cpp:8175
static bool g_dumpSymbolMap
Definition doxygen.cpp:192
static void version(const bool extended)
static void addGlobalFunction(const Entry *root, const QCString &rname, const QCString &sc)
Definition doxygen.cpp:3823
static OutputList * g_outputList
Definition doxygen.cpp:189
static void findMainPage(Entry *root)
Definition doxygen.cpp:9765
static ClassDef::CompoundType convertToCompoundType(EntryType section, TypeSpecifier specifier)
Definition doxygen.cpp:896
static void findUsingDirectives(const Entry *root)
Definition doxygen.cpp:1897
std::unique_ptr< ArgumentList > getTemplateArgumentsFromName(const QCString &name, const ArgumentLists &tArgLists)
Definition doxygen.cpp:866
static bool g_successfulRun
Definition doxygen.cpp:191
static void addSourceReferences()
Definition doxygen.cpp:8810
std::function< std::unique_ptr< T >() > make_parser_factory()
static void buildExampleList(Entry *root)
Definition doxygen.cpp:9982
static void inheritDocumentation()
Definition doxygen.cpp:9232
static void flushUnresolvedRelations()
Definition doxygen.cpp:9481
static bool isSymbolHidden(const Definition *d)
Definition doxygen.cpp:8979
void readConfiguration(int argc, char **argv)
static void readDir(FileInfo *fi, FileNameLinkedMap *fnMap, StringUnorderedSet *exclSet, const StringVector *patList, const StringVector *exclPatList, StringVector *resultList, StringUnorderedSet *resultSet, bool errorIfNotExist, bool recursive, StringUnorderedSet *killSet, StringUnorderedSet *paths)
static QCString g_commentFileName
Definition doxygen.cpp:193
static void findDocumentedEnumValues()
Definition doxygen.cpp:8167
static void findTagLessClasses()
Definition doxygen.cpp:1691
static void generateDiskNames()
static void addToIndices()
Definition doxygen.cpp:8217
static void computeClassRelations()
Definition doxygen.cpp:5363
static void buildFunctionList(const Entry *root)
Definition doxygen.cpp:3932
static void checkPageRelations()
Definition doxygen.cpp:9877
static void findModuleDocumentation(const Entry *root)
Definition doxygen.cpp:1311
static void findEnumDocumentation(const Entry *root)
Definition doxygen.cpp:8055
static void computePageRelations(Entry *root)
Definition doxygen.cpp:9847
static void filterMemberDocumentation(const Entry *root, const QCString &relates)
Definition doxygen.cpp:7346
static QCString createOutputDirectory(const QCString &baseDirName, const QCString &formatDirName, const char *defaultDirName)
void initResources()
static bool isVarWithConstructor(const Entry *root)
Definition doxygen.cpp:2936
static StringSet g_usingDeclarations
Definition doxygen.cpp:190
static void buildDictionaryList(const Entry *root)
Definition doxygen.cpp:3521
static bool haveEqualFileNames(const Entry *root, const MemberDef *md)
Definition doxygen.cpp:9540
static void generateNamespaceDocs()
static void buildClassDocList(const Entry *root)
Definition doxygen.cpp:1146
static void buildPageList(Entry *root)
Definition doxygen.cpp:9738
static void writeTagFile()
static Definition * buildScopeFromQualifiedName(const QCString &name_, SrcLangExt lang, const TagInfo *tagInfo)
Definition doxygen.cpp:712
static void addRequirementReferences()
Definition doxygen.cpp:5550
static void computeVerifiedDotPath()
static bool g_singleComment
Definition doxygen.cpp:194
static void findSectionsInDocumentation()
Definition doxygen.cpp:9368
static void mergeCategories()
Definition doxygen.cpp:8532
static const StringUnorderedSet g_compoundKeywords
Definition doxygen.cpp:199
static bool scopeIsTemplate(const Definition *d)
Definition doxygen.cpp:5994
static void buildFileList(const Entry *root)
Definition doxygen.cpp:502
static ClassDefMutable * createTagLessInstance(const ClassDef *rootCd, const ClassDef *templ, const QCString &fieldName)
Definition doxygen.cpp:1524
static void buildClassList(const Entry *root)
Definition doxygen.cpp:1136
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:3687
static std::unique_ptr< OutlineParserInterface > getParserForFile(const QCString &fn)
static void findUsedTemplateInstances()
Definition doxygen.cpp:5327
static void computeTooltipTexts()
Definition doxygen.cpp:8986
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:3148
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:6546
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:4720
static void parseFilesSingleThreading(const std::shared_ptr< Entry > &root)
parse the list of input files
static void buildCompleteMemberLists()
Definition doxygen.cpp:8554
static void generateConceptDocs()
Definition doxygen.cpp:9211
static void combineUsingRelations()
Definition doxygen.cpp:9267
static const char * getArg(int argc, char **argv, int &optInd)
static const ClassDef * findClassDefinition(FileDef *fd, NamespaceDef *nd, const QCString &scopeName)
Definition doxygen.cpp:5732
static void checkMarkdownMainfile()
static std::unordered_map< std::string, std::vector< ClassDefMutable * > > g_usingClassMap
Definition doxygen.cpp:2204
static void buildConceptDocList(const Entry *root)
Definition doxygen.cpp:1331
static int findTemplateSpecializationPosition(const QCString &name)
Definition doxygen.cpp:4864
static bool isEntryInGroupOfMember(const Entry *root, const MemberDef *md, bool allowNoGroup=false)
Definition doxygen.cpp:5747
static void applyToAllDefinitions(Func func)
Definition doxygen.cpp:5493
static void parseFilesMultiThreading(const std::shared_ptr< Entry > &root)
parse the list of input files
static void transferFunctionReferences()
Definition doxygen.cpp:4367
static void buildDefineList()
Definition doxygen.cpp:8889
static void buildInterfaceAndServiceList(const Entry *root)
Definition doxygen.cpp:3635
static void computeMemberRelations()
Definition doxygen.cpp:8497
static void buildListOfUsingDecls(const Entry *root)
Definition doxygen.cpp:2040
static void computeMemberRelationsForBaseClass(const ClassDef *cd, const BaseClassDef *bcd)
Definition doxygen.cpp:8417
static std::multimap< std::string, const Entry * > g_classEntries
Definition doxygen.cpp:187
std::vector< InputFileEncoding > InputFileEncodingList
Definition doxygen.h:80
std::unordered_map< std::string, BodyInfo > StaticInitMap
Definition doxygen.h:84
std::unordered_map< std::string, NamespaceAliasInfo > NamespaceAliasInfoMap
Definition doxygen.h:86
std::unordered_map< std::string, const Definition * > ClangUsrMap
Definition doxygen.h:82
std::unique_ptr< FileDef > createFileDef(const QCString &p, const QCString &n, const QCString &ref, const QCString &dn)
Definition filedef.cpp:269
FileDef * toFileDef(Definition *d)
Definition filedef.cpp:1967
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:177
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:384
void endFile(OutputList &ol, bool skipNavIndex, bool skipEndContents, const QCString &navPath)
Definition index.cpp:427
void writeGraphInfo(OutputList &ol)
Definition index.cpp:4066
void endTitle(OutputList &ol, const QCString &fileName, const QCString &name)
Definition index.cpp:394
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:401
void writeIndexHierarchy(OutputList &ol)
Definition index.cpp:5780
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:517
bool isAbsolutePath(const QCString &fileName)
Definition portable.cpp:498
FILE * popen(const QCString &name, const QCString &type)
Definition portable.cpp:480
std::ofstream openOutputStream(const QCString &name, bool append=false)
Definition portable.cpp:649
double getSysElapsedTime()
Definition portable.cpp:98
QCString pathListSeparator()
Definition portable.cpp:384
uint32_t pid()
Definition portable.cpp:249
int pclose(FILE *stream)
Definition portable.cpp:489
int system(const QCString &command, const QCString &args, bool commandHasConsole=true)
Definition portable.cpp:106
void setenv(const QCString &variable, const QCString &value)
Definition portable.cpp:287
const char * commandExtension()
Definition portable.cpp:462
QCString getenv(const QCString &variable)
Definition portable.cpp:322
void setShortDir()
Definition portable.cpp:554
QCString trunc(const QCString &s, size_t numChars=15)
Definition trace.h:56
void replaceNamespaceAliases(QCString &name)
std::unique_ptr< NamespaceDef > createNamespaceDefAlias(const Definition *newScope, const NamespaceDef *nd)
Factory method to create an alias of an existing namespace.
std::unique_ptr< NamespaceDef > createNamespaceDef(const QCString &defFileName, int defLine, int defColumn, const QCString &name, const QCString &ref, const QCString &refFile, const QCString &type, bool isPublished)
Factory method to create new NamespaceDef instance.
NamespaceDef * getResolvedNamespace(const QCString &name)
NamespaceDef * toNamespaceDef(Definition *d)
NamespaceDefMutable * toNamespaceDefMutable(Definition *d)
NamespaceDefMutable * getResolvedNamespaceMutable(const QCString &key)
std::unordered_set< const NamespaceDef * > NamespaceDefSet
bool search(std::string_view str, Match &match, const Ex &re, size_t pos)
Search in a given string str starting at position pos for a match against regular expression re.
Definition regex.cpp:844
std::unique_ptr< PageDef > createPageDef(const QCString &f, int l, const QCString &n, const QCString &d, const QCString &t)
Definition pagedef.cpp:82
void generatePerlMod()
void setPerlModDoxyfile(const QCString &qs)
Portable versions of functions that are platform dependent.
int portable_iconv_close(void *cd)
void * portable_iconv_open(const char *tocode, const char *fromcode)
int qstricmp(const char *s1, const char *s2)
Definition qcstring.cpp:530
QCString substitute(const QCString &s, const QCString &src, const QCString &dst)
substitute all occurrences of src in s by dst
Definition qcstring.cpp:571
int qstricmp_sort(const char *str1, const char *str2)
Definition qcstring.h:86
const char * qPrint(const char *s)
Definition qcstring.h:687
#define TRUE
Definition qcstring.h:37
#define FALSE
Definition qcstring.h:34
uint32_t qstrlen(const char *str)
Returns the length of string str, or 0 if a null pointer is passed.
Definition qcstring.h:58
#define ASSERT(x)
Definition qcstring.h:39
int qstrcmp(const char *str1, const char *str2)
Definition qcstring.h:69
void initSearchIndexer()
void finalizeSearchIndexer()
static void addMemberToSearchIndex(const MemberDef *md)
void createJavaScriptSearchIndex()
void writeJavaScriptSearchIndex()
Javascript based search engine.
void generateHtmlForComment(const std::string &fn, const std::string &text)
Helper for implemented the -c option of doxygen, which produces HTML output for a given doxygen forma...
void generateSqlite3()
void addSTLSupport(std::shared_ptr< Entry > &root)
Add stub entries for the most used classes in the standard template library.
Some helper functions for std::string.
void addTerminalCharIfMissing(std::string &s, char c)
Definition stringutil.h:84
This class contains the information about the argument of a function or template.
Definition arguments.h:27
QCString type
Definition arguments.h:42
QCString name
Definition arguments.h:44
QCString defval
Definition arguments.h:46
QCString array
Definition arguments.h:45
Class that contains information about an inheritance relation.
Definition classdef.h:55
ClassDef * classDef
Class definition that this relation inherits from.
Definition classdef.h:60
This class stores information about an inheritance relation.
Definition entry.h:91
Protection prot
inheritance type
Definition entry.h:96
Specifier virt
virtualness
Definition entry.h:97
QCString name
the name of the base class
Definition entry.h:95
Data associated with description found in the body.
Definition definition.h:64
Grouping info.
Definition types.h:227
QCString groupname
name of the group
Definition types.h:257
static constexpr const char * getGroupPriName(GroupPri_t priority) noexcept
Definition types.h:240
@ GROUPING_INGROUP
membership in group was defined by @ingroup
Definition types.h:236
GroupPri_t pri
priority of this definition
Definition types.h:258
static bool execute(const QCString &htmldir)
Definition htags.cpp:38
static bool loadFilemap(const QCString &htmldir)
Definition htags.cpp:107
static bool useHtags
Definition htags.h:23
const Definition * definition
Definition doxygen.h:59
const MemberDef * typeDef
Definition doxygen.h:60
stat(const char *n, double el)
Definition doxygen.cpp:267
const char * name
Definition doxygen.cpp:264
This struct is used to capture the tag file information for an Entry.
Definition entry.h:104
QCString fileName
Definition entry.h:106
QCString tagName
Definition entry.h:105
void parseTagFile(const std::shared_ptr< Entry > &root, const char *fullName)
void exitTracing()
Definition trace.cpp:52
void initTracing(const QCString &logFile, bool timing)
Definition trace.cpp:22
#define TRACE(...)
Definition trace.h:77
MemberType
Definition types.h:552
@ Enumeration
Definition types.h:557
@ EnumValue
Definition types.h:558
@ Dictionary
Definition types.h:568
@ Interface
Definition types.h:565
@ Sequence
Definition types.h:567
@ Variable
Definition types.h:555
@ Property
Definition types.h:563
@ Typedef
Definition types.h:556
@ Function
Definition types.h:554
@ Service
Definition types.h:566
Protection
Definition types.h:32
SrcLangExt
Definition types.h:207
Relationship
Definition types.h:167
Specifier
Definition types.h:80
bool matchArguments2(const Definition *srcScope, const FileDef *srcFileScope, const QCString &srcReturnType, const ArgumentList *srcAl, const Definition *dstScope, const FileDef *dstFileScope, const QCString &dstReturnType, const ArgumentList *dstAl, bool checkCV, SrcLangExt lang)
Definition util.cpp:1998
QCString removeRedundantWhiteSpace(const QCString &s)
Definition util.cpp:568
QCString mergeScopes(const QCString &leftScope, const QCString &rightScope)
Definition util.cpp:4575
QCString normalizeNonTemplateArgumentsInString(const QCString &name, const Definition *context, const ArgumentList &formalArgs)
Definition util.cpp:4281
SrcLangExt getLanguageFromFileName(const QCString &fileName, SrcLangExt defLang)
Definition util.cpp:5191
bool protectionLevelVisible(Protection prot)
Definition util.cpp:5937
bool matchTemplateArguments(const ArgumentList &srcAl, const ArgumentList &dstAl)
Definition util.cpp:2242
void addCodeOnlyMappings()
Definition util.cpp:5185
QCString substituteTemplateArgumentsInString(const QCString &nm, const ArgumentList &formalArgs, const ArgumentList *actualArgs)
Definition util.cpp:4346
int extractClassNameFromType(const QCString &type, int &pos, QCString &name, QCString &templSpec, SrcLangExt lang)
Definition util.cpp:4196
bool leftScopeMatch(const QCString &scope, const QCString &name)
Definition util.cpp:882
QCString stripAnonymousNamespaceScope(const QCString &s)
Definition util.cpp:231
QCString stripFromIncludePath(const QCString &path)
Definition util.cpp:330
bool checkIfTypedef(const Definition *scope, const FileDef *fileScope, const QCString &n)
Definition util.cpp:5295
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:5530
bool patternMatch(const FileInfo &fi, const StringVector &patList)
Definition util.cpp:5684
bool openOutputFile(const QCString &outFile, std::ofstream &f)
Definition util.cpp:6296
QCString tempArgListToString(const ArgumentList &al, SrcLangExt lang, bool includeDefault)
Definition util.cpp:1276
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:4805
QCString showFileDefMatches(const FileNameLinkedMap *fnMap, const QCString &n)
Definition util.cpp:2999
QCString fileToString(const QCString &name, bool filter, bool isSourceCode)
Definition util.cpp:1471
QCString filterTitle(const QCString &title)
Definition util.cpp:5610
QCString removeAnonymousScopes(const QCString &str)
Definition util.cpp:162
QCString resolveTypeDef(const Definition *context, const QCString &qualifiedName, const Definition **typedefContext)
Definition util.cpp:374
bool checkExtension(const QCString &fName, const QCString &ext)
Definition util.cpp:4897
int computeQualifiedIndex(const QCString &name)
Return the index of the last :: in the string name that is still before the first <.
Definition util.cpp:6824
void initDefaultExtensionMapping()
Definition util.cpp:5118
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:4967
QCString convertNameToFile(const QCString &name, bool allowDots, bool allowUnderscore)
Definition util.cpp:3485
QCString langToString(SrcLangExt lang)
Returns a string representation of lang.
Definition util.cpp:5891
EntryType guessSection(const QCString &name)
Definition util.cpp:339
void extractNamespaceName(const QCString &scopeName, QCString &className, QCString &namespaceName, bool allowEmptyClass)
Definition util.cpp:3678
QCString argListToString(const ArgumentList &al, bool useCanonicalType, bool showDefVals)
Definition util.cpp:1231
QCString getLanguageSpecificSeparator(SrcLangExt lang, bool classScope)
Returns the scope separator to use given the programming language lang.
Definition util.cpp:5897
void mergeMemberOverrideOptions(MemberDefMutable *md1, MemberDefMutable *md2)
Definition util.cpp:6861
QCString mangleCSharpGenericName(const QCString &name)
Definition util.cpp:6913
QCString projectLogoFile()
Definition util.cpp:3121
void mergeArguments(ArgumentList &srcAl, ArgumentList &dstAl, bool forceNameOverwrite)
Definition util.cpp:2098
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:5857
QCString stripTemplateSpecifiersFromScope(const QCString &fullName, bool parentOnly, QCString *pLastScopeStripped, QCString scopeName, bool allowArtificial)
Definition util.cpp:4508
QCString getOverloadDocs()
Definition util.cpp:4070
void cleanupInlineGraph()
Definition util.cpp:6990
int getPrefixIndex(const QCString &name)
Definition util.cpp:3212
bool rightScopeMatch(const QCString &scope, const QCString &name)
Definition util.cpp:871
bool updateLanguageMapping(const QCString &extension, const QCString &language)
Definition util.cpp:5086
QCString getFileNameExtension(const QCString &fn)
Definition util.cpp:5233
QCString replaceAnonymousScopes(const QCString &s, const QCString &replacement)
Definition util.cpp:219
FileDef * findFileDef(const FileNameLinkedMap *fnMap, const QCString &n, bool &ambig)
Definition util.cpp:2871
int getScopeFragment(const QCString &s, int p, int *l)
Definition util.cpp:4620
void addHtmlExtensionIfMissing(QCString &fName)
Definition util.cpp:4902
A bunch of utility functions.
void generateXML()
Definition xmlgen.cpp:2268