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 "version.h"
30#include "doxygen.h"
31#include "scanner.h"
32#include "entry.h"
33#include "index.h"
34#include "indexlist.h"
35#include "message.h"
36#include "config.h"
37#include "util.h"
38#include "pre.h"
39#include "tagreader.h"
40#include "dot.h"
41#include "msc.h"
42#include "docparser.h"
43#include "dirdef.h"
44#include "outputlist.h"
45#include "declinfo.h"
46#include "htmlgen.h"
47#include "latexgen.h"
48#include "mangen.h"
49#include "language.h"
50#include "debug.h"
51#include "htmlhelp.h"
52#include "qhp.h"
53#include "sitemap.h"
54#include "ftvhelp.h"
55#include "defargs.h"
56#include "rtfgen.h"
57#include "sqlite3gen.h"
58#include "xmlgen.h"
59#include "docbookgen.h"
60#include "defgen.h"
61#include "perlmodgen.h"
62#include "reflist.h"
63#include "pagedef.h"
64#include "commentcnv.h"
65#include "cmdmapper.h"
66#include "searchindex_js.h"
67#include "parserintf.h"
68#include "htags.h"
69#include "pycode.h"
70#include "pyscanner.h"
71#include "fortrancode.h"
72#include "fortranscanner.h"
73#include "xmlcode.h"
74#include "sqlcode.h"
75#include "lexcode.h"
76#include "lexscanner.h"
77#include "code.h"
78#include "portable.h"
79#include "vhdljjparser.h"
80#include "vhdldocgen.h"
81#include "vhdlcode.h"
82#include "eclipsehelp.h"
83#include "cite.h"
84#include "markdown.h"
85#include "arguments.h"
86#include "memberlist.h"
87#include "layout.h"
88#include "groupdef.h"
89#include "classlist.h"
90#include "namespacedef.h"
91#include "filename.h"
92#include "membername.h"
93#include "membergroup.h"
94#include "docsets.h"
95#include "formula.h"
96#include "settings.h"
97#include "fileparser.h"
98#include "emoji.h"
99#include "plantuml.h"
100#include "stlsupport.h"
101#include "threadpool.h"
102#include "clangparser.h"
103#include "symbolresolver.h"
104#include "regex.h"
105#include "aliases.h"
106#include "fileinfo.h"
107#include "dir.h"
108#include "conceptdef.h"
109#include "trace.h"
110#include "moduledef.h"
111#include "stringutil.h"
112#include "singlecomment.h"
113
114#include <sqlite3.h>
115
116#if USE_LIBCLANG
117#if defined(__GNUC__)
118#pragma GCC diagnostic push
119#pragma GCC diagnostic ignored "-Wshadow"
120#endif
121#include <clang/Basic/Version.h>
122#if defined(__GNUC__)
123#pragma GCC diagnostic pop
124#endif
125#endif
126
127// provided by the generated file resources.cpp
128extern void initResources();
129
130#if !defined(_WIN32) || defined(__CYGWIN__)
131#include <signal.h>
132#define HAS_SIGNALS
133#endif
134
135// globally accessible variables
147FileNameLinkedMap *Doxygen::includeNameLinkedMap = nullptr; // include names
153FileNameLinkedMap *Doxygen::plantUmlFileNameLinkedMap = nullptr;// plantuml files
155StringMap Doxygen::tagDestinationMap; // all tag locations
156StringUnorderedSet Doxygen::tagFileSet; // all tag file names
157StringUnorderedSet Doxygen::expandAsDefinedSet; // all macros that should be expanded
158MemberGroupInfoMap Doxygen::memberGroupInfoMap; // dictionary of the member groups heading
159std::unique_ptr<PageDef> Doxygen::mainPage;
160std::unique_ptr<NamespaceDef> Doxygen::globalNamespaceDef;
182std::mutex Doxygen::addExampleMutex;
184
185// locally accessible globals
186static std::multimap< std::string, const Entry* > g_classEntries;
188static OutputList *g_outputList = nullptr; // list of output generating objects
189static StringSet g_usingDeclarations; // used classes
190static bool g_successfulRun = FALSE;
191static bool g_dumpSymbolMap = FALSE;
193static bool g_singleComment=false;
194
195
196
197// keywords recognized as compounds
199{ "template class", "template struct", "class", "struct", "union", "interface", "exception" };
200
202{
203 g_inputFiles.clear();
204 //g_excludeNameDict.clear();
205 //delete g_outputList; g_outputList=nullptr;
206
211 Doxygen::pageLinkedMap->clear();
224 Doxygen::mainPage.reset();
226}
227
229{
230 public:
232 void begin(const char *name)
233 {
234 msg("{}", name);
235 stats.emplace_back(name,0);
236 startTime = std::chrono::steady_clock::now();
237 }
238 void end()
239 {
240 std::chrono::steady_clock::time_point endTime = std::chrono::steady_clock::now();
241 stats.back().elapsed = static_cast<double>(std::chrono::duration_cast<
242 std::chrono::microseconds>(endTime - startTime).count())/1000000.0;
243 warn_flush();
244 }
245 void print()
246 {
247 bool restore=FALSE;
249 {
251 restore=TRUE;
252 }
253 msg("----------------------\n");
254 for (const auto &s : stats)
255 {
256 msg("Spent {:.6f} seconds in {}",s.elapsed,s.name);
257 }
258 if (restore) Debug::setFlag(Debug::Time);
259 }
260 private:
261 struct stat
262 {
263 const char *name;
264 double elapsed;
265 //stat() : name(nullptr),elapsed(0) {}
266 stat(const char *n, double el) : name(n),elapsed(el) {}
267 };
268 std::vector<stat> stats;
269 std::chrono::steady_clock::time_point startTime;
271
272
273static void addMemberDocs(const Entry *root,MemberDefMutable *md, const QCString &funcDecl,
274 const ArgumentList *al,bool over_load,TypeSpecifier spec);
275static void findMember(const Entry *root,
276 const QCString &relates,
277 const QCString &type,
278 const QCString &args,
279 QCString funcDecl,
280 bool overloaded,
281 bool isFunc
282 );
283
290
291
292static bool findClassRelation(
293 const Entry *root,
294 Definition *context,
295 ClassDefMutable *cd,
296 const BaseInfo *bi,
297 const TemplateNameMap &templateNames,
298 /*bool insertUndocumented*/
300 bool isArtificial
301 );
302
303//----------------------------------------------------------------------------
304
306 FileDef *fileScope,const TagInfo *tagInfo);
307static void resolveTemplateInstanceInType(const Entry *root,const Definition *scope,const MemberDef *md);
308
309static void addPageToContext(PageDef *pd,Entry *root)
310{
311 if (root->parent()) // add the page to it's scope
312 {
313 QCString scope = root->parent()->name;
314 if (root->parent()->section.isPackageDoc())
315 {
316 scope=substitute(scope,".","::");
317 }
318 scope = stripAnonymousNamespaceScope(scope);
319 scope+="::"+pd->name();
321 if (d)
322 {
323 pd->setPageScope(d);
324 }
325 }
326}
327
328static void addRelatedPage(Entry *root)
329{
330 GroupDef *gd=nullptr;
331 for (const Grouping &g : root->groups)
332 {
333 if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname))) break;
334 }
335 //printf("---> addRelatedPage() %s gd=%p\n",qPrint(root->name),gd);
336 QCString doc=root->doc+root->inbodyDocs;
337
338 PageDef *pd = addRelatedPage(root->name,root->args,doc,
339 root->docFile,
340 root->docLine,
341 root->startLine,
342 root->sli,
343 gd,root->tagInfo(),
344 FALSE,
345 root->lang
346 );
347 if (pd)
348 {
349 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
351 pd->setLocalToc(root->localToc);
352 addPageToContext(pd,root);
353 }
354}
355
356static void buildGroupListFiltered(const Entry *root,bool additional, bool includeExternal)
357{
358 if (root->section.isGroupDoc() && !root->name.isEmpty() &&
359 ((!includeExternal && root->tagInfo()==nullptr) ||
360 ( includeExternal && root->tagInfo()!=nullptr))
361 )
362 {
363 AUTO_TRACE("additional={} includeExternal={}",additional,includeExternal);
364 if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
365 (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
366 {
367 GroupDef *gd = Doxygen::groupLinkedMap->find(root->name);
368 AUTO_TRACE_ADD("Processing group '{}':'{}' gd={}", root->type,root->name,(void*)gd);
369
370 if (gd)
371 {
372 if ( !gd->hasGroupTitle() )
373 {
374 gd->setGroupTitle( root->type );
375 }
376 else if ( root->type.length() > 0 && root->name != root->type && gd->groupTitle() != root->type )
377 {
378 warn( root->fileName,root->startLine,
379 "group {}: ignoring title \"{}\" that does not match old title \"{}\"",
380 root->name, root->type, gd->groupTitle() );
381 }
382 gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
383 gd->setDocumentation( root->doc, root->docFile, root->docLine );
384 gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
386 gd->setRefItems(root->sli);
387 gd->setLanguage(root->lang);
389 {
390 root->commandOverrides.apply_groupGraph([&](bool b) { gd->overrideGroupGraph(b); });
391 }
392 }
393 else
394 {
395 if (root->tagInfo())
396 {
397 gd = Doxygen::groupLinkedMap->add(root->name,
398 std::unique_ptr<GroupDef>(
399 createGroupDef(root->fileName,root->startLine,root->name,root->type,root->tagInfo()->fileName)));
400 gd->setReference(root->tagInfo()->tagName);
401 }
402 else
403 {
404 gd = Doxygen::groupLinkedMap->add(root->name,
405 std::unique_ptr<GroupDef>(
406 createGroupDef(root->fileName,root->startLine,root->name,root->type)));
407 }
408 gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
409 // allow empty docs for group
410 gd->setDocumentation(!root->doc.isEmpty() ? root->doc : QCString(" "),root->docFile,root->docLine,FALSE);
411 gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
413 gd->setRefItems(root->sli);
414 gd->setLanguage(root->lang);
416 {
417 root->commandOverrides.apply_groupGraph([&](bool b) { gd->overrideGroupGraph(b); });
418 }
419 }
420 }
421 }
422 for (const auto &e : root->children()) buildGroupListFiltered(e.get(),additional,includeExternal);
423}
424
425static void buildGroupList(const Entry *root)
426{
427 // --- first process only local groups
428 // first process the @defgroups blocks
430 // then process the @addtogroup, @weakgroup blocks
432
433 // --- then also process external groups
434 // first process the @defgroups blocks
436 // then process the @addtogroup, @weakgroup blocks
438}
439
440static void findGroupScope(const Entry *root)
441{
442 if (root->section.isGroupDoc() && !root->name.isEmpty() &&
443 root->parent() && !root->parent()->name.isEmpty())
444 {
445 GroupDef *gd = Doxygen::groupLinkedMap->find(root->name);
446 if (gd)
447 {
448 QCString scope = root->parent()->name;
449 if (root->parent()->section.isPackageDoc())
450 {
451 scope=substitute(scope,".","::");
452 }
453 scope = stripAnonymousNamespaceScope(scope);
454 scope+="::"+gd->name();
456 if (d)
457 {
458 gd->setGroupScope(d);
459 }
460 }
461 }
462 for (const auto &e : root->children()) findGroupScope(e.get());
463}
464
465static void organizeSubGroupsFiltered(const Entry *root,bool additional)
466{
467 if (root->section.isGroupDoc() && !root->name.isEmpty())
468 {
469 AUTO_TRACE("additional={}",additional);
470 if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
471 (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
472 {
473 GroupDef *gd = Doxygen::groupLinkedMap->find(root->name);
474 if (gd)
475 {
476 AUTO_TRACE_ADD("adding {} to group {}",root->name,gd->name());
477 addGroupToGroups(root,gd);
478 }
479 }
480 }
481 for (const auto &e : root->children()) organizeSubGroupsFiltered(e.get(),additional);
482}
483
484static void organizeSubGroups(const Entry *root)
485{
486 //printf("Defining groups\n");
487 // first process the @defgroups blocks
489 //printf("Additional groups\n");
490 // then process the @addtogroup, @weakgroup blocks
492}
493
494//----------------------------------------------------------------------
495
496static void buildFileList(const Entry *root)
497{
498 if ((root->section.isFileDoc() || (root->section.isFile() && Config_getBool(EXTRACT_ALL))) &&
499 !root->name.isEmpty() && !root->tagInfo() // skip any file coming from tag files
500 )
501 {
502 bool ambig = false;
504 if (!fd || ambig)
505 {
506 bool save_ambig = ambig;
507 // use the directory of the file to see if the described file is in the same
508 // directory as the describing file.
509 QCString fn = root->fileName;
510 int newIndex=fn.findRev('/');
511 if (newIndex<0)
512 {
513 fn = root->name;
514 }
515 else
516 {
517 fn = fn.left(newIndex)+"/"+root->name;
518 }
520 if (!fd) ambig = save_ambig;
521 }
522 //printf("**************** root->name=%s fd=%p\n",qPrint(root->name),(void*)fd);
523 if (fd && !ambig)
524 {
525 //printf("Adding documentation!\n");
526 // using FALSE in setDocumentation is small hack to make sure a file
527 // is documented even if a \file command is used without further
528 // documentation
529 fd->setDocumentation(root->doc,root->docFile,root->docLine,FALSE);
530 fd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
532 fd->setRefItems(root->sli);
533 root->commandOverrides.apply_includeGraph ([&](bool b) { fd->overrideIncludeGraph(b); });
534 root->commandOverrides.apply_includedByGraph([&](bool b) { fd->overrideIncludedByGraph(b); });
535 for (const Grouping &g : root->groups)
536 {
537 GroupDef *gd=nullptr;
538 if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname)))
539 {
540 if (!gd->containsFile(fd))
541 {
542 gd->addFile(fd);
543 fd->makePartOfGroup(gd);
544 //printf("File %s: in group %s\n",qPrint(fd->name()),qPrint(gd->name()));
545 }
546 }
547 else if (!gd && g.pri == Grouping::GROUPING_INGROUP)
548 {
549 warn(root->fileName, root->startLine,
550 "Found non-existing group '{}' for the command '{}', ignoring command",
552 );
553 }
554 }
555 }
556 else
557 {
559 text.sprintf("the name '%s' supplied as "
560 "the argument in the \\file statement ",
561 qPrint(root->name));
562 if (ambig) // name is ambiguous
563 {
564 text+="matches the following input files:\n";
566 text+="\n";
567 text+="Please use a more specific name by "
568 "including a (larger) part of the path!";
569 }
570 else // name is not an input file
571 {
572 text+="is not an input file";
573 }
574 warn(root->fileName,root->startLine,"{}", text);
575 }
576 }
577 for (const auto &e : root->children()) buildFileList(e.get());
578}
579
580template<class DefMutable>
581static void addIncludeFile(DefMutable *cd,FileDef *ifd,const Entry *root)
582{
583 if (
584 (!root->doc.stripWhiteSpace().isEmpty() ||
585 !root->brief.stripWhiteSpace().isEmpty() ||
586 Config_getBool(EXTRACT_ALL)
587 ) && root->protection!=Protection::Private
588 )
589 {
590 //printf(">>>>>> includeFile=%s\n",qPrint(root->includeFile));
591
592 bool local=Config_getBool(FORCE_LOCAL_INCLUDES);
593 QCString includeFile = root->includeFile;
594 if (!includeFile.isEmpty() && includeFile.at(0)=='"')
595 {
596 local = TRUE;
597 includeFile=includeFile.mid(1,includeFile.length()-2);
598 }
599 else if (!includeFile.isEmpty() && includeFile.at(0)=='<')
600 {
601 local = FALSE;
602 includeFile=includeFile.mid(1,includeFile.length()-2);
603 }
604
605 bool ambig = false;
606 FileDef *fd=nullptr;
607 // see if we need to include a verbatim copy of the header file
608 //printf("root->includeFile=%s\n",qPrint(root->includeFile));
609 if (!includeFile.isEmpty() &&
610 (fd=findFileDef(Doxygen::inputNameLinkedMap,includeFile,ambig))==nullptr
611 )
612 { // explicit request
613 QCString text;
614 text.sprintf("the name '%s' supplied as "
615 "the argument of the \\class, \\struct, \\union, or \\include command ",
616 qPrint(includeFile)
617 );
618 if (ambig) // name is ambiguous
619 {
620 text+="matches the following input files:\n";
622 text+="\n";
623 text+="Please use a more specific name by "
624 "including a (larger) part of the path!";
625 }
626 else // name is not an input file
627 {
628 text+="is not an input file";
629 }
630 warn(root->fileName,root->startLine, "{}", text);
631 }
632 else if (includeFile.isEmpty() && ifd &&
633 // see if the file extension makes sense
634 guessSection(ifd->name()).isHeader())
635 { // implicit assumption
636 fd=ifd;
637 }
638
639 // if a file is found, we mark it as a source file.
640 if (fd)
641 {
642 QCString iName = !root->includeName.isEmpty() ?
643 root->includeName : includeFile;
644 if (!iName.isEmpty()) // user specified include file
645 {
646 if (iName.at(0)=='<') local=FALSE; // explicit override
647 else if (iName.at(0)=='"') local=TRUE;
648 if (iName.at(0)=='"' || iName.at(0)=='<')
649 {
650 iName=iName.mid(1,iName.length()-2); // strip quotes or brackets
651 }
652 if (iName.isEmpty())
653 {
654 iName=fd->name();
655 }
656 }
657 else if (!Config_getList(STRIP_FROM_INC_PATH).empty())
658 {
660 }
661 else // use name of the file containing the class definition
662 {
663 iName=fd->name();
664 }
665 if (fd->generateSourceFile()) // generate code for header
666 {
667 cd->setIncludeFile(fd,iName,local,!root->includeName.isEmpty());
668 }
669 else // put #include in the class documentation without link
670 {
671 cd->setIncludeFile(nullptr,iName,local,TRUE);
672 }
673 }
674 }
675}
676
677
679{
680 size_t l = s.length();
681 int count=0;
682 int round=0;
683 QCString result;
684 for (size_t i=0;i<l;i++)
685 {
686 char c=s.at(i);
687 if (c=='(') round++;
688 else if (c==')' && round>0) round--;
689 else if (c=='<' && round==0) count++;
690 if (count==0)
691 {
692 result+=c;
693 }
694 if (c=='>' && round==0 && count>0) count--;
695 }
696 //printf("stripTemplateSpecifiers(%s)=%s\n",qPrint(s),qPrint(result));
697 return result;
698}
699
700/*! returns the Definition object belonging to the first \a level levels of
701 * full qualified name \a name. Creates an artificial scope if the scope is
702 * not found and set the parent/child scope relation if the scope is found.
703 */
704[[maybe_unused]]
705static Definition *buildScopeFromQualifiedName(const QCString &name_,SrcLangExt lang,const TagInfo *tagInfo)
706{
707 QCString name = stripTemplateSpecifiers(name_);
708 name.stripPrefix("::");
709 int level = name.contains("::");
710 //printf("buildScopeFromQualifiedName(%s) level=%d\n",qPrint(name),level);
711 int i=0, p=0, l=0;
713 QCString fullScope;
714 while (i<level)
715 {
716 int idx=getScopeFragment(name,p,&l);
717 if (idx==-1) return prevScope;
718 QCString nsName = name.mid(idx,l);
719 if (nsName.isEmpty()) return prevScope;
720 if (!fullScope.isEmpty()) fullScope+="::";
721 fullScope+=nsName;
722 NamespaceDef *nd=Doxygen::namespaceLinkedMap->find(fullScope);
723 DefinitionMutable *innerScope = toDefinitionMutable(nd);
724 ClassDef *cd=nullptr;
725 if (nd==nullptr) cd = getClass(fullScope);
726 if (nd==nullptr && cd) // scope is a class
727 {
728 innerScope = toDefinitionMutable(cd);
729 }
730 else if (nd==nullptr && cd==nullptr && fullScope.find('<')==-1) // scope is not known and could be a namespace!
731 {
732 // introduce bogus namespace
733 //printf("++ adding dummy namespace %s to %s tagInfo=%p\n",qPrint(nsName),qPrint(prevScope->name()),(void*)tagInfo);
734 NamespaceDefMutable *newNd=
736 Doxygen::namespaceLinkedMap->add(fullScope,
738 "[generated]",1,1,fullScope,
739 tagInfo?tagInfo->tagName:QCString(),
740 tagInfo?tagInfo->fileName:QCString())));
741 if (newNd)
742 {
743 newNd->setLanguage(lang);
744 newNd->setArtificial(TRUE);
745 // add namespace to the list
746 innerScope = newNd;
747 }
748 }
749 else // scope is a namespace
750 {
751 }
752 if (innerScope)
753 {
754 // make the parent/child scope relation
755 DefinitionMutable *prevScopeMutable = toDefinitionMutable(prevScope);
756 if (prevScopeMutable)
757 {
758 prevScopeMutable->addInnerCompound(toDefinition(innerScope));
759 }
760 innerScope->setOuterScope(prevScope);
761 }
762 else // current scope is a class, so return only the namespace part...
763 {
764 return prevScope;
765 }
766 // proceed to the next scope fragment
767 p=idx+l+2;
768 prevScope=toDefinition(innerScope);
769 i++;
770 }
771 return prevScope;
772}
773
775 FileDef *fileScope,const TagInfo *tagInfo)
776{
777 //printf("<findScopeFromQualifiedName(%s,%s)\n",startScope ? qPrint(startScope->name()) : 0, qPrint(n));
778 Definition *resultScope=toDefinition(startScope);
779 if (resultScope==nullptr) resultScope=Doxygen::globalScope;
781 int l1 = 0;
782 int i1 = getScopeFragment(scope,0,&l1);
783 if (i1==-1)
784 {
785 //printf(">no fragments!\n");
786 return resultScope;
787 }
788 int p=i1+l1,l2=0,i2=0;
789 while ((i2=getScopeFragment(scope,p,&l2))!=-1)
790 {
791 QCString nestedNameSpecifier = scope.mid(i1,l1);
792 Definition *orgScope = resultScope;
793 //printf(" nestedNameSpecifier=%s\n",qPrint(nestedNameSpecifier));
794 resultScope = const_cast<Definition*>(resultScope->findInnerCompound(nestedNameSpecifier));
795 //printf(" resultScope=%p\n",resultScope);
796 if (resultScope==nullptr)
797 {
798 if (orgScope==Doxygen::globalScope && fileScope && !fileScope->getUsedNamespaces().empty())
799 // also search for used namespaces
800 {
801 for (const auto &nd : fileScope->getUsedNamespaces())
802 {
804 if (mnd)
805 {
806 resultScope = findScopeFromQualifiedName(toNamespaceDefMutable(nd),n,fileScope,tagInfo);
807 if (resultScope!=nullptr) break;
808 }
809 }
810 if (resultScope)
811 {
812 // for a nested class A::I in used namespace N, we get
813 // N::A::I while looking for A, so we should compare
814 // resultScope->name() against scope.left(i2+l2)
815 //printf(" -> result=%s scope=%s\n",qPrint(resultScope->name()),qPrint(scope));
816 if (rightScopeMatch(resultScope->name(),scope.left(i2+l2)))
817 {
818 break;
819 }
820 goto nextFragment;
821 }
822 }
823
824 // also search for used classes. Complication: we haven't been able
825 // to put them in the right scope yet, because we are still resolving
826 // the scope relations!
827 // Therefore loop through all used classes and see if there is a right
828 // scope match between the used class and nestedNameSpecifier.
829 for (const auto &usedName : g_usingDeclarations)
830 {
831 //printf("Checking using class %s\n",qPrint(usedName));
832 if (rightScopeMatch(usedName,nestedNameSpecifier))
833 {
834 // ui.currentKey() is the fully qualified name of nestedNameSpecifier
835 // so use this instead.
836 QCString fqn = usedName + scope.right(scope.length()-p);
837 resultScope = buildScopeFromQualifiedName(fqn,startScope->getLanguage(),nullptr);
838 //printf("Creating scope from fqn=%s result %p\n",qPrint(fqn),resultScope);
839 if (resultScope)
840 {
841 //printf("> Match! resultScope=%s\n",qPrint(resultScope->name()));
842 return resultScope;
843 }
844 }
845 }
846
847 //printf("> name %s not found in scope %s\n",qPrint(nestedNameSpecifier),qPrint(orgScope->name()));
848 return nullptr;
849 }
850 nextFragment:
851 i1=i2;
852 l1=l2;
853 p=i2+l2;
854 }
855 //printf(">findScopeFromQualifiedName scope %s\n",qPrint(resultScope->name()));
856 return resultScope;
857}
858
859std::unique_ptr<ArgumentList> getTemplateArgumentsFromName(
860 const QCString &name,
861 const ArgumentLists &tArgLists)
862{
863 // for each scope fragment, check if it is a template and advance through
864 // the list if so.
865 int i=0, p=0;
866 auto alIt = tArgLists.begin();
867 while ((i=name.find("::",p))!=-1 && alIt!=tArgLists.end())
868 {
869 NamespaceDef *nd = Doxygen::namespaceLinkedMap->find(name.left(i));
870 if (nd==nullptr)
871 {
872 ClassDef *cd = getClass(name.left(i));
873 if (cd)
874 {
875 if (!cd->templateArguments().empty())
876 {
877 ++alIt;
878 }
879 }
880 }
881 p=i+2;
882 }
883 return alIt!=tArgLists.end() ?
884 std::make_unique<ArgumentList>(*alIt) :
885 std::unique_ptr<ArgumentList>();
886}
887
888static
890{
892
893 if (specifier.isStruct())
895 else if (specifier.isUnion())
896 sec=ClassDef::Union;
897 else if (specifier.isCategory())
899 else if (specifier.isInterface())
901 else if (specifier.isProtocol())
903 else if (specifier.isException())
905 else if (specifier.isService())
907 else if (specifier.isSingleton())
909
910 if (section.isUnionDoc())
911 sec=ClassDef::Union;
912 else if (section.isStructDoc())
914 else if (section.isInterfaceDoc())
916 else if (section.isProtocolDoc())
918 else if (section.isCategoryDoc())
920 else if (section.isExceptionDoc())
922 else if (section.isServiceDoc())
924 else if (section.isSingletonDoc())
926
927 return sec;
928}
929
930
931static void addClassToContext(const Entry *root)
932{
933 AUTO_TRACE("name={}",root->name);
934 FileDef *fd = root->fileDef();
935
936 QCString scName;
937 if (root->parent()->section.isScope())
938 {
939 scName=root->parent()->name;
940 }
941 // name without parent's scope
942 QCString fullName = root->name;
943
944 // strip off any template parameters (but not those for specializations)
945 int idx=fullName.find('>');
946 if (idx!=-1 && root->lang==SrcLangExt::CSharp) // mangle A<S,T>::N as A-2-g::N
947 {
948 fullName = mangleCSharpGenericName(fullName.left(idx+1))+fullName.mid(idx+1);
949 }
950 fullName=stripTemplateSpecifiersFromScope(fullName);
951
952 // name with scope (if not present already)
953 QCString qualifiedName = fullName;
954 if (!scName.isEmpty() && !leftScopeMatch(scName,fullName))
955 {
956 qualifiedName.prepend(scName+"::");
957 }
958
959 // see if we already found the class before
960 ClassDefMutable *cd = getClassMutable(qualifiedName);
961
962 AUTO_TRACE_ADD("Found class with name '{}', qualifiedName '{}'", cd ? cd->name() : root->name, qualifiedName);
963
964 if (cd)
965 {
966 fullName=cd->name();
967 AUTO_TRACE_ADD("Existing class '{}'",cd->name());
968 //if (cd->templateArguments()==0)
969 //{
970 // //printf("existing ClassDef tempArgList=%p specScope=%s\n",root->tArgList,qPrint(root->scopeSpec));
971 // cd->setTemplateArguments(tArgList);
972 //}
973
974 cd->setDocumentation(root->doc,root->docFile,root->docLine);
975 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
976 root->commandOverrides.apply_collaborationGraph([&](bool b ) { cd->overrideCollaborationGraph(b); });
977 root->commandOverrides.apply_inheritanceGraph ([&](CLASS_GRAPH_t gt) { cd->overrideInheritanceGraph(gt); });
978
979 if (!root->spec.isForwardDecl() && cd->isForwardDeclared())
980 {
981 cd->setDefFile(root->fileName,root->startLine,root->startColumn);
982 if (root->bodyLine!=-1)
983 {
984 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
985 cd->setBodyDef(fd);
986 }
987 }
988
989 if (cd->templateArguments().empty() || (cd->isForwardDeclared() && !root->spec.isForwardDecl()))
990 {
991 // this happens if a template class declared with @class is found
992 // before the actual definition or if a forward declaration has different template
993 // parameter names.
994 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(cd->name(),root->tArgLists);
995 if (tArgList)
996 {
997 cd->setTemplateArguments(*tArgList);
998 }
999 }
1000 if (cd->requiresClause().isEmpty() && !root->req.isEmpty())
1001 {
1002 cd->setRequiresClause(root->req);
1003 }
1004
1006
1007 cd->setMetaData(root->metaData);
1008 }
1009 else // new class
1010 {
1012
1013 QCString className;
1014 QCString namespaceName;
1015 extractNamespaceName(fullName,className,namespaceName);
1016
1017 AUTO_TRACE_ADD("New class: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1018 fullName, namespaceName, className, Trace::trunc(root->brief), Trace::trunc(root->doc));
1019
1020 QCString tagName;
1021 QCString refFileName;
1022 const TagInfo *tagInfo = root->tagInfo();
1023 if (tagInfo)
1024 {
1025 tagName = tagInfo->tagName;
1026 refFileName = tagInfo->fileName;
1027 if (fullName.find("::")!=-1)
1028 // symbols imported via tag files may come without the parent scope,
1029 // so we artificially create it here
1030 {
1031 buildScopeFromQualifiedName(fullName,root->lang,tagInfo);
1032 }
1033 }
1034 std::unique_ptr<ArgumentList> tArgList;
1035 int i=0;
1036 if ((root->lang==SrcLangExt::CSharp || root->lang==SrcLangExt::Java) &&
1037 (i=fullName.find('<'))!=-1)
1038 {
1039 // a Java/C# generic class looks like a C++ specialization, so we need to split the
1040 // name and template arguments here
1041 tArgList = stringToArgumentList(root->lang,fullName.mid(i));
1042 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
1043 // A -> A
1044 // A<T> -> A-1-g
1045 // A<T,S> -> A-2-g
1046 {
1047 fullName=mangleCSharpGenericName(fullName);
1048 }
1049 else
1050 {
1051 fullName=fullName.left(i);
1052 }
1053 }
1054 else
1055 {
1056 tArgList = getTemplateArgumentsFromName(fullName,root->tArgLists);
1057 }
1058 // add class to the list
1059 cd = toClassDefMutable(
1060 Doxygen::classLinkedMap->add(fullName,
1061 createClassDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1062 fullName,sec,tagName,refFileName,TRUE,root->spec.isEnum()) ));
1063 if (cd)
1064 {
1065 AUTO_TRACE_ADD("New class '{}' type={} #tArgLists={} tagInfo={} hidden={} artificial={}",
1066 fullName,cd->compoundTypeString(),root->tArgLists.size(),
1067 fmt::ptr(tagInfo),root->hidden,root->artificial);
1068 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1069 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1070 cd->setLanguage(root->lang);
1071 cd->setId(root->id);
1072 cd->setHidden(root->hidden);
1073 cd->setArtificial(root->artificial);
1074 cd->setClassSpecifier(root->spec);
1075 cd->addQualifiers(root->qualifiers);
1076 cd->setTypeConstraints(root->typeConstr);
1077 root->commandOverrides.apply_collaborationGraph([&](bool b ) { cd->overrideCollaborationGraph(b); });
1078 root->commandOverrides.apply_inheritanceGraph ([&](CLASS_GRAPH_t gt) { cd->overrideInheritanceGraph(gt); });
1079
1080 if (tArgList)
1081 {
1082 cd->setTemplateArguments(*tArgList);
1083 }
1084 cd->setRequiresClause(root->req);
1085 cd->setProtection(root->protection);
1086 cd->setIsStatic(root->isStatic);
1087
1088 // file definition containing the class cd
1089 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1090 cd->setBodyDef(fd);
1091
1092 cd->setMetaData(root->metaData);
1093
1094 cd->insertUsedFile(fd);
1095 }
1096 else
1097 {
1098 AUTO_TRACE_ADD("Class {} not added, already exists as alias", fullName);
1099 }
1100 }
1101
1102 if (cd)
1103 {
1105 if (!root->subGrouping) cd->setSubGrouping(FALSE);
1106 if (!root->spec.isForwardDecl())
1107 {
1108 if (cd->hasDocumentation())
1109 {
1110 addIncludeFile(cd,fd,root);
1111 }
1112 if (fd && root->section.isCompound())
1113 {
1114 AUTO_TRACE_ADD("Inserting class {} in file {} (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1115 cd->setFileDef(fd);
1116 fd->insertClass(cd);
1117 }
1118 }
1119 addClassToGroups(root,cd);
1121 cd->setRefItems(root->sli);
1122 }
1123}
1124
1125//----------------------------------------------------------------------
1126// build a list of all classes mentioned in the documentation
1127// and all classes that have a documentation block before their definition.
1128static void buildClassList(const Entry *root)
1129{
1130 if ((root->section.isCompound() || root->section.isObjcImpl()) && !root->name.isEmpty())
1131 {
1132 AUTO_TRACE();
1133 addClassToContext(root);
1134 }
1135 for (const auto &e : root->children()) buildClassList(e.get());
1136}
1137
1138static void buildClassDocList(const Entry *root)
1139{
1140 if ((root->section.isCompoundDoc()) && !root->name.isEmpty())
1141 {
1142 AUTO_TRACE();
1143 addClassToContext(root);
1144 }
1145 for (const auto &e : root->children()) buildClassDocList(e.get());
1146}
1147
1148//----------------------------------------------------------------------
1149// build a list of all classes mentioned in the documentation
1150// and all classes that have a documentation block before their definition.
1151
1152static void addConceptToContext(const Entry *root)
1153{
1154 AUTO_TRACE();
1155 FileDef *fd = root->fileDef();
1156
1157 QCString scName;
1158 if (root->parent()->section.isScope())
1159 {
1160 scName=root->parent()->name;
1161 }
1162
1163 // name with scope (if not present already)
1164 QCString qualifiedName = root->name;
1165 if (!scName.isEmpty() && !leftScopeMatch(qualifiedName,scName))
1166 {
1167 qualifiedName.prepend(scName+"::");
1168 }
1169
1170 // see if we already found the concept before
1171 ConceptDefMutable *cd = getConceptMutable(qualifiedName);
1172
1173 AUTO_TRACE_ADD("Found concept with name '{}' (qualifiedName='{}')", cd ? cd->name() : root->name, qualifiedName);
1174
1175 if (cd)
1176 {
1177 qualifiedName=cd->name();
1178 AUTO_TRACE_ADD("Existing concept '{}'",cd->name());
1179
1180 cd->setDocumentation(root->doc,root->docFile,root->docLine);
1181 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1182
1183 addIncludeFile(cd,fd,root);
1184 }
1185 else // new concept
1186 {
1187 QCString className;
1188 QCString namespaceName;
1189 extractNamespaceName(qualifiedName,className,namespaceName);
1190
1191 AUTO_TRACE_ADD("New concept: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1192 qualifiedName,namespaceName,className,root->brief,root->doc);
1193
1194 QCString tagName;
1195 QCString refFileName;
1196 const TagInfo *tagInfo = root->tagInfo();
1197 if (tagInfo)
1198 {
1199 tagName = tagInfo->tagName;
1200 refFileName = tagInfo->fileName;
1201 if (qualifiedName.find("::")!=-1)
1202 // symbols imported via tag files may come without the parent scope,
1203 // so we artificially create it here
1204 {
1205 buildScopeFromQualifiedName(qualifiedName,root->lang,tagInfo);
1206 }
1207 }
1208 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(qualifiedName,root->tArgLists);
1209 // add concept to the list
1211 Doxygen::conceptLinkedMap->add(qualifiedName,
1212 createConceptDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1213 qualifiedName,tagName,refFileName)));
1214 if (cd)
1215 {
1216 AUTO_TRACE_ADD("New concept '{}' #tArgLists={} tagInfo={}",
1217 qualifiedName,root->tArgLists.size(),fmt::ptr(tagInfo));
1218 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1219 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1220 cd->setLanguage(root->lang);
1221 cd->setId(root->id);
1222 cd->setHidden(root->hidden);
1223 cd->setGroupId(root->mGrpId);
1224 if (tArgList)
1225 {
1226 cd->setTemplateArguments(*tArgList);
1227 }
1228 cd->setInitializer(root->initializer.str());
1229 // file definition containing the class cd
1230 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1231 cd->setBodyDef(fd);
1232 addIncludeFile(cd,fd,root);
1233
1234 // also add namespace to the correct structural context
1235 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,qualifiedName,nullptr,tagInfo);
1237 {
1239 if (dm)
1240 {
1241 dm->addInnerCompound(cd);
1242 }
1243 cd->setOuterScope(d);
1244 }
1245 }
1246 else
1247 {
1248 AUTO_TRACE_ADD("Concept '{}' not added, already exists (as alias)", qualifiedName);
1249 }
1250 }
1251
1252 if (cd)
1253 {
1255 if (fd)
1256 {
1257 AUTO_TRACE_ADD("Inserting concept '{}' in file '{}' (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1258 cd->setFileDef(fd);
1259 fd->insertConcept(cd);
1260 }
1261 addConceptToGroups(root,cd);
1263 cd->setRefItems(root->sli);
1264 }
1265}
1266
1267static void findModuleDocumentation(const Entry *root)
1268{
1269 if (root->section.isModuleDoc())
1270 {
1271 AUTO_TRACE();
1273 }
1274 for (const auto &e : root->children()) findModuleDocumentation(e.get());
1275}
1276
1277static void buildConceptList(const Entry *root)
1278{
1279 if (root->section.isConcept())
1280 {
1281 AUTO_TRACE();
1282 addConceptToContext(root);
1283 }
1284 for (const auto &e : root->children()) buildConceptList(e.get());
1285}
1286
1287static void buildConceptDocList(const Entry *root)
1288{
1289 if (root->section.isConceptDoc())
1290 {
1291 AUTO_TRACE();
1292 addConceptToContext(root);
1293 }
1294 for (const auto &e : root->children()) buildConceptDocList(e.get());
1295}
1296
1297// This routine is to allow @ingroup X @{ concept A; concept B; @} to work
1298// (same also works for variable and functions because of logic in MemberGroup::insertMember)
1300{
1301 AUTO_TRACE();
1302 for (const auto &cd : *Doxygen::conceptLinkedMap)
1303 {
1304 if (cd->groupId()!=DOX_NOGROUP)
1305 {
1306 for (const auto &ocd : *Doxygen::conceptLinkedMap)
1307 {
1308 if (cd!=ocd && cd->groupId()==ocd->groupId() &&
1309 !cd->partOfGroups().empty() && ocd->partOfGroups().empty())
1310 {
1311 ConceptDefMutable *ocdm = toConceptDefMutable(ocd.get());
1312 if (ocdm)
1313 {
1314 for (const auto &gd : cd->partOfGroups())
1315 {
1316 if (gd)
1317 {
1318 AUTO_TRACE_ADD("making concept '{}' part of group '{}'",ocdm->name(),gd->name());
1319 ocdm->makePartOfGroup(gd);
1320 gd->addConcept(ocd.get());
1321 }
1322 }
1323 }
1324 }
1325 }
1326 }
1327 }
1328}
1329
1330//----------------------------------------------------------------------
1331
1333{
1334 ClassDefSet visitedClasses;
1335
1336 bool done=FALSE;
1337 //int iteration=0;
1338 while (!done)
1339 {
1340 done=TRUE;
1341 //++iteration;
1342 struct ClassAlias
1343 {
1344 ClassAlias(const QCString &name,std::unique_ptr<ClassDef> cd,DefinitionMutable *ctx) :
1345 aliasFullName(name),aliasCd(std::move(cd)), aliasContext(ctx) {}
1346 QCString aliasFullName;
1347 std::unique_ptr<ClassDef> aliasCd;
1348 DefinitionMutable *aliasContext;
1349 };
1350 std::vector<ClassAlias> aliases;
1351 for (const auto &icd : *Doxygen::classLinkedMap)
1352 {
1353 ClassDefMutable *cd = toClassDefMutable(icd.get());
1354 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1355 {
1356 QCString name = stripAnonymousNamespaceScope(icd->name());
1357 //printf("processing=%s, iteration=%d\n",qPrint(cd->name()),iteration);
1358 // also add class to the correct structural context
1360 name,icd->getFileDef(),nullptr);
1361 if (d)
1362 {
1363 //printf("****** adding %s to scope %s in iteration %d\n",qPrint(cd->name()),qPrint(d->name()),iteration);
1365 if (dm)
1366 {
1367 dm->addInnerCompound(cd);
1368 }
1369 cd->setOuterScope(d);
1370
1371 // for inline namespace add an alias of the class to the outer scope
1373 {
1375 //printf("nd->isInline()=%d\n",nd->isInline());
1376 if (nd && nd->isInline())
1377 {
1378 d = d->getOuterScope();
1379 if (d)
1380 {
1381 dm = toDefinitionMutable(d);
1382 if (dm)
1383 {
1384 auto aliasCd = createClassDefAlias(d,cd);
1385 QCString aliasFullName = d->qualifiedName()+"::"+aliasCd->localName();
1386 aliases.emplace_back(aliasFullName,std::move(aliasCd),dm);
1387 //printf("adding %s to %s as %s\n",qPrint(aliasCd->name()),qPrint(d->name()),qPrint(aliasFullName));
1388 }
1389 }
1390 }
1391 else
1392 {
1393 break;
1394 }
1395 }
1396
1397 visitedClasses.insert(icd.get());
1398 done=FALSE;
1399 }
1400 //else
1401 //{
1402 // printf("****** ignoring %s: scope not (yet) found in iteration %d\n",qPrint(cd->name()),iteration);
1403 //}
1404 }
1405 }
1406 // add aliases
1407 for (auto &alias : aliases)
1408 {
1409 ClassDef *aliasCd = Doxygen::classLinkedMap->add(alias.aliasFullName,std::move(alias.aliasCd));
1410 if (aliasCd)
1411 {
1412 alias.aliasContext->addInnerCompound(aliasCd);
1413 }
1414 }
1415 }
1416
1417 //give warnings for unresolved compounds
1418 for (const auto &icd : *Doxygen::classLinkedMap)
1419 {
1420 ClassDefMutable *cd = toClassDefMutable(icd.get());
1421 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1422 {
1424 /// create the scope artificially
1425 // anyway, so we can at least relate scopes properly.
1426 Definition *d = buildScopeFromQualifiedName(name,cd->getLanguage(),nullptr);
1427 if (d && d!=cd && !cd->getDefFileName().isEmpty())
1428 // avoid recursion in case of redundant scopes, i.e: namespace N { class N::C {}; }
1429 // for this case doxygen assumes the existence of a namespace N::N in which C is to be found!
1430 // also avoid warning for stuff imported via a tagfile.
1431 {
1433 if (dm)
1434 {
1435 dm->addInnerCompound(cd);
1436 }
1437 cd->setOuterScope(d);
1438 warn(cd->getDefFileName(),cd->getDefLine(),
1439 "Incomplete input: scope for class {} not found!{}",name,
1440 name.startsWith("std::") ? " Try enabling BUILTIN_STL_SUPPORT." : ""
1441 );
1442 }
1443 }
1444 }
1445}
1446
1448{
1449 //bool inlineGroupedClasses = Config_getBool(INLINE_GROUPED_CLASSES);
1450 //if (!inlineGroupedClasses) return;
1451 //printf("** distributeClassGroupRelations()\n");
1452
1453 ClassDefSet visitedClasses;
1454 for (const auto &cd : *Doxygen::classLinkedMap)
1455 {
1456 //printf("Checking %s\n",qPrint(cd->name()));
1457 // distribute the group to nested classes as well
1458 if (visitedClasses.find(cd.get())==visitedClasses.end() && !cd->partOfGroups().empty())
1459 {
1460 //printf(" Candidate for merging\n");
1461 GroupDef *gd = cd->partOfGroups().front();
1462 for (auto &ncd : cd->getClasses())
1463 {
1465 if (ncdm && ncdm->partOfGroups().empty())
1466 {
1467 //printf(" Adding %s to group '%s'\n",qPrint(ncd->name()),
1468 // gd->groupTitle());
1469 ncdm->makePartOfGroup(gd);
1470 gd->addClass(ncdm);
1471 }
1472 }
1473 visitedClasses.insert(cd.get()); // only visit every class once
1474 }
1475 }
1476}
1477
1478//----------------------------
1479
1480static ClassDefMutable *createTagLessInstance(const ClassDef *rootCd,const ClassDef *templ,const QCString &fieldName)
1481{
1482 QCString fullName = removeAnonymousScopes(templ->name());
1483 if (fullName.endsWith("::")) fullName=fullName.left(fullName.length()-2);
1484 fullName+="."+fieldName;
1485
1486 //printf("** adding class %s based on %s\n",qPrint(fullName),qPrint(templ->name()));
1488 Doxygen::classLinkedMap->add(fullName,
1490 templ->getDefLine(),
1491 templ->getDefColumn(),
1492 fullName,
1493 templ->compoundType())));
1494 if (cd)
1495 {
1496 cd->setDocumentation(templ->documentation(),templ->docFile(),templ->docLine()); // copy docs to definition
1497 cd->setBriefDescription(templ->briefDescription(),templ->briefFile(),templ->briefLine());
1498 cd->setLanguage(templ->getLanguage());
1499 cd->setBodySegment(templ->getDefLine(),templ->getStartBodyLine(),templ->getEndBodyLine());
1500 cd->setBodyDef(templ->getBodyDef());
1501
1502 cd->setOuterScope(rootCd->getOuterScope());
1503 if (rootCd->getOuterScope()!=Doxygen::globalScope)
1504 {
1505 DefinitionMutable *outerScope = toDefinitionMutable(rootCd->getOuterScope());
1506 if (outerScope)
1507 {
1508 outerScope->addInnerCompound(cd);
1509 }
1510 }
1511
1512 FileDef *fd = templ->getFileDef();
1513 if (fd)
1514 {
1515 cd->setFileDef(fd);
1516 fd->insertClass(cd);
1517 }
1518 for (auto &gd : rootCd->partOfGroups())
1519 {
1520 cd->makePartOfGroup(gd);
1521 gd->addClass(cd);
1522 }
1523
1524 MemberList *ml = templ->getMemberList(MemberListType::PubAttribs());
1525 if (ml)
1526 {
1527 for (const auto &md : *ml)
1528 {
1529 //printf(" Member %s type=%s\n",qPrint(md->name()),md->typeString());
1530 auto newMd = createMemberDef(md->getDefFileName(),md->getDefLine(),md->getDefColumn(),
1531 md->typeString(),md->name(),md->argsString(),md->excpString(),
1532 md->protection(),md->virtualness(),md->isStatic(),Relationship::Member,
1533 md->memberType(),
1534 ArgumentList(),ArgumentList(),"");
1535 MemberDefMutable *imd = toMemberDefMutable(newMd.get());
1536 imd->setMemberClass(cd);
1537 imd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
1538 imd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
1539 imd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
1540 imd->setMemberSpecifiers(md->getMemberSpecifiers());
1541 imd->setVhdlSpecifiers(md->getVhdlSpecifiers());
1542 imd->setMemberGroupId(md->getMemberGroupId());
1543 imd->setInitializer(md->initializer());
1544 imd->setRequiresClause(md->requiresClause());
1545 imd->setMaxInitLines(md->initializerLines());
1546 imd->setBitfields(md->bitfieldString());
1547 imd->setLanguage(md->getLanguage());
1548 cd->insertMember(imd);
1549 MemberName *mn = Doxygen::memberNameLinkedMap->add(md->name());
1550 mn->push_back(std::move(newMd));
1551 }
1552 }
1553 }
1554 return cd;
1555}
1556
1557/** Look through the members of class \a cd and its public members.
1558 * If there is a member m of a tag less struct/union,
1559 * then we create a duplicate of the struct/union with the name of the
1560 * member to identify it.
1561 * So if cd has name S, then the tag less struct/union will get name S.m
1562 * Since tag less structs can be nested we need to call this function
1563 * recursively. Later on we need to patch the member types so we keep
1564 * track of the hierarchy of classes we create.
1565 */
1566static void processTagLessClasses(const ClassDef *rootCd,
1567 const ClassDef *cd,
1568 ClassDefMutable *tagParentCd,
1569 const QCString &prefix,int count)
1570{
1571 //printf("%d: processTagLessClasses %s\n",count,qPrint(cd->name()));
1572 //printf("checking members for %s\n",qPrint(cd->name()));
1573 if (tagParentCd && !cd->getClasses().empty())
1574 {
1575 MemberList *ml = cd->getMemberList(MemberListType::PubAttribs());
1576 if (ml)
1577 {
1578 int pos=0;
1579 for (const auto &md : *ml)
1580 {
1581 QCString type = md->typeString();
1582 if (type.find("::@")!=-1) // member of tag less struct/union
1583 {
1584 for (const auto &icd : cd->getClasses())
1585 {
1586 //printf(" member %s: type='%s'\n",qPrint(md->name()),qPrint(type));
1587 //printf(" comparing '%s'<->'%s'\n",qPrint(type),qPrint(icd->name()));
1588 if (type.find(icd->name())!=-1) // matching tag less struct/union
1589 {
1590 QCString name = md->name();
1591 if (md->isAnonymous()) name = "__unnamed" + QCString().setNum(pos++)+"__";
1592 if (!prefix.isEmpty()) name.prepend(prefix+".");
1593 //printf(" found %s for class %s\n",qPrint(name),qPrint(cd->name()));
1594 ClassDefMutable *ncd = createTagLessInstance(rootCd,icd,name);
1595 if (ncd)
1596 {
1597 processTagLessClasses(rootCd,icd,ncd,name,count+1);
1598 //printf(" addTagged %s to %s\n",qPrint(ncd->name()),qPrint(tagParentCd->name()));
1599 ncd->setTagLessReference(icd);
1600
1601 // replace tag-less type for generated/original member
1602 // by newly created class name.
1603 // note the difference between changing cd and tagParentCd.
1604 // for the initial call this is the same pointer, but for
1605 // recursive calls cd is the original tag-less struct (of which
1606 // there is only one instance) and tagParentCd is the newly
1607 // generated tagged struct of which there can be multiple instances!
1608 MemberList *pml = tagParentCd->getMemberList(MemberListType::PubAttribs());
1609 if (pml)
1610 {
1611 for (const auto &pmd : *pml)
1612 {
1614 if (pmdm && pmd->name()==md->name())
1615 {
1616 pmdm->setAccessorType(ncd,substitute(pmd->typeString(),icd->name(),ncd->name()));
1617 //pmd->setType(substitute(pmd->typeString(),icd->name(),ncd->name()));
1618 }
1619 }
1620 }
1621 }
1622 }
1623 }
1624 }
1625 }
1626 }
1627 }
1628}
1629
1630static void findTagLessClasses(std::vector<ClassDefMutable*> &candidates,ClassDef *cd)
1631{
1632 for (const auto &icd : cd->getClasses())
1633 {
1634 if (icd->name().find("@")==-1) // process all non-anonymous inner classes
1635 {
1636 findTagLessClasses(candidates,icd);
1637 }
1638 }
1639
1641 if (cdm)
1642 {
1643 candidates.push_back(cdm);
1644 }
1645}
1646
1648{
1649 std::vector<ClassDefMutable *> candidates;
1650 for (auto &cd : *Doxygen::classLinkedMap)
1651 {
1652 Definition *scope = cd->getOuterScope();
1653 if (scope && scope->definitionType()!=Definition::TypeClass) // that is not nested
1654 {
1655 findTagLessClasses(candidates,cd.get());
1656 }
1657 }
1658
1659 // since processTagLessClasses is potentially adding classes to Doxygen::classLinkedMap
1660 // we need to call it outside of the loop above, otherwise the iterator gets invalidated!
1661 for (auto &cd : candidates)
1662 {
1663 processTagLessClasses(cd,cd,cd,"",0); // process tag less inner struct/classes
1664 }
1665}
1666
1667
1668//----------------------------------------------------------------------
1669// build a list of all namespaces mentioned in the documentation
1670// and all namespaces that have a documentation block before their definition.
1671static void buildNamespaceList(const Entry *root)
1672{
1673 if (
1674 (root->section.isNamespace() ||
1675 root->section.isNamespaceDoc() ||
1676 root->section.isPackageDoc()
1677 ) &&
1678 !root->name.isEmpty()
1679 )
1680 {
1681 AUTO_TRACE("name={}",root->name);
1682
1683 QCString fName = root->name;
1684 if (root->section.isPackageDoc())
1685 {
1686 fName=substitute(fName,".","::");
1687 }
1688
1689 QCString fullName = stripAnonymousNamespaceScope(fName);
1690 if (!fullName.isEmpty())
1691 {
1692 AUTO_TRACE_ADD("Found namespace {} in {} at line {}",root->name,root->fileName,root->startLine);
1693 NamespaceDef *ndi = Doxygen::namespaceLinkedMap->find(fullName);
1694 if (ndi) // existing namespace
1695 {
1697 if (nd) // non-inline namespace
1698 {
1699 AUTO_TRACE_ADD("Existing namespace");
1700 nd->setDocumentation(root->doc,root->docFile,root->docLine);
1701 nd->setName(fullName); // change name to match docs
1703 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1704 if (nd->getLanguage()==SrcLangExt::Unknown)
1705 {
1706 nd->setLanguage(root->lang);
1707 }
1708 if (root->tagInfo()==nullptr && nd->isReference() && !(root->doc.isEmpty() && root->brief.isEmpty()))
1709 // if we previously found namespace nd in a tag file and now we find a
1710 // documented namespace with the same name in the project, then remove
1711 // the tag file reference
1712 {
1713 nd->setReference("");
1714 nd->setFileName(fullName);
1715 }
1716 nd->setMetaData(root->metaData);
1717
1718 // file definition containing the namespace nd
1719 FileDef *fd=root->fileDef();
1720 if (nd->isArtificial())
1721 {
1722 nd->setArtificial(FALSE); // found namespace explicitly, so cannot be artificial
1723 nd->setDefFile(root->fileName,root->startLine,root->startColumn);
1724 }
1725 // insert the namespace in the file definition
1726 if (fd) fd->insertNamespace(nd);
1727 addNamespaceToGroups(root,nd);
1728 nd->setRefItems(root->sli);
1729 }
1730 }
1731 else // fresh namespace
1732 {
1733 QCString tagName;
1734 QCString tagFileName;
1735 const TagInfo *tagInfo = root->tagInfo();
1736 if (tagInfo)
1737 {
1738 tagName = tagInfo->tagName;
1739 tagFileName = tagInfo->fileName;
1740 }
1741 AUTO_TRACE_ADD("new namespace {} lang={} tagName={}",fullName,langToString(root->lang),tagName);
1742 // add namespace to the list
1744 Doxygen::namespaceLinkedMap->add(fullName,
1745 createNamespaceDef(tagInfo?tagName:root->fileName,root->startLine,
1746 root->startColumn,fullName,tagName,tagFileName,
1747 root->type,root->spec.isPublished())));
1748 if (nd)
1749 {
1750 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1751 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1753 nd->setHidden(root->hidden);
1754 nd->setArtificial(root->artificial);
1755 nd->setLanguage(root->lang);
1756 nd->setId(root->id);
1757 nd->setMetaData(root->metaData);
1758 nd->setInline(root->spec.isInline());
1759 nd->setExported(root->exported);
1760
1761 addNamespaceToGroups(root,nd);
1762 nd->setRefItems(root->sli);
1763
1764 // file definition containing the namespace nd
1765 FileDef *fd=root->fileDef();
1766 // insert the namespace in the file definition
1767 if (fd) fd->insertNamespace(nd);
1768
1769 // the empty string test is needed for extract all case
1770 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1771 nd->insertUsedFile(fd);
1772 nd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1773 nd->setBodyDef(fd);
1774
1775 // also add namespace to the correct structural context
1776 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,fullName,nullptr,tagInfo);
1777 AUTO_TRACE_ADD("adding namespace {} to context {}",nd->name(),d ? d->name() : QCString("<none>"));
1778 if (d==nullptr) // we didn't find anything, create the scope artificially
1779 // anyway, so we can at least relate scopes properly.
1780 {
1781 d = buildScopeFromQualifiedName(fullName,nd->getLanguage(),tagInfo);
1783 if (dm)
1784 {
1785 dm->addInnerCompound(nd);
1786 }
1787 nd->setOuterScope(d);
1788 // TODO: Due to the order in which the tag file is written
1789 // a nested class can be found before its parent!
1790 }
1791 else
1792 {
1794 if (dm)
1795 {
1796 dm->addInnerCompound(nd);
1797 }
1798 nd->setOuterScope(d);
1799 // in case of d is an inline namespace, alias insert nd in the part scope of d.
1801 {
1802 NamespaceDef *pnd = toNamespaceDef(d);
1803 if (pnd && pnd->isInline())
1804 {
1805 d = d->getOuterScope();
1806 if (d)
1807 {
1808 dm = toDefinitionMutable(d);
1809 if (dm)
1810 {
1811 auto aliasNd = createNamespaceDefAlias(d,nd);
1812 dm->addInnerCompound(aliasNd.get());
1813 QCString aliasName = aliasNd->name();
1814 AUTO_TRACE_ADD("adding alias {} to {}",aliasName,d->name());
1815 Doxygen::namespaceLinkedMap->add(aliasName,std::move(aliasNd));
1816 }
1817 }
1818 else
1819 {
1820 break;
1821 }
1822 }
1823 else
1824 {
1825 break;
1826 }
1827 }
1828 }
1829 }
1830 }
1831 }
1832 }
1833 for (const auto &e : root->children()) buildNamespaceList(e.get());
1834}
1835
1836//----------------------------------------------------------------------
1837
1839 const QCString &name)
1840{
1841 NamespaceDef *usingNd =nullptr;
1842 for (auto &und : unl)
1843 {
1844 QCString uScope=und->name()+"::";
1845 usingNd = getResolvedNamespace(uScope+name);
1846 if (usingNd!=nullptr) break;
1847 }
1848 return usingNd;
1849}
1850
1851static void findUsingDirectives(const Entry *root)
1852{
1853 if (root->section.isUsingDir())
1854 {
1855 AUTO_TRACE("Found using directive {} at line {} of {}",root->name,root->startLine,root->fileName);
1856 QCString name=substitute(root->name,".","::");
1857 if (name.endsWith("::"))
1858 {
1859 name=name.left(name.length()-2);
1860 }
1861 if (!name.isEmpty())
1862 {
1863 NamespaceDef *usingNd = nullptr;
1864 NamespaceDefMutable *nd = nullptr;
1865 FileDef *fd = root->fileDef();
1866 QCString nsName;
1867
1868 // see if the using statement was found inside a namespace or inside
1869 // the global file scope.
1870 if (root->parent() && root->parent()->section.isNamespace() &&
1871 (fd==nullptr || fd->getLanguage()!=SrcLangExt::Java) // not a .java file
1872 )
1873 {
1874 nsName=stripAnonymousNamespaceScope(root->parent()->name);
1875 if (!nsName.isEmpty())
1876 {
1877 nd = getResolvedNamespaceMutable(nsName);
1878 }
1879 }
1880
1881 // find the scope in which the 'using' namespace is defined by prepending
1882 // the possible scopes in which the using statement was found, starting
1883 // with the most inner scope and going to the most outer scope (i.e.
1884 // file scope).
1885 int scopeOffset = static_cast<int>(nsName.length());
1886 do
1887 {
1888 QCString scope=scopeOffset>0 ?
1889 nsName.left(scopeOffset)+"::" : QCString();
1890 usingNd = getResolvedNamespace(scope+name);
1891 //printf("Trying with scope='%s' usingNd=%p\n",(scope+qPrint(name)),usingNd);
1892 if (scopeOffset==0)
1893 {
1894 scopeOffset=-1;
1895 }
1896 else if ((scopeOffset=nsName.findRev("::",scopeOffset-1))==-1)
1897 {
1898 scopeOffset=0;
1899 }
1900 } while (scopeOffset>=0 && usingNd==nullptr);
1901
1902 if (usingNd==nullptr && nd) // not found, try used namespaces in this scope
1903 // or in one of the parent namespace scopes
1904 {
1905 const NamespaceDefMutable *pnd = nd;
1906 while (pnd && usingNd==nullptr)
1907 {
1908 // also try with one of the used namespaces found earlier
1910
1911 // goto the parent
1912 Definition *s = pnd->getOuterScope();
1914 {
1916 }
1917 else
1918 {
1919 pnd = nullptr;
1920 }
1921 }
1922 }
1923 if (usingNd==nullptr && fd) // still nothing, also try used namespace in the
1924 // global scope
1925 {
1926 usingNd = findUsedNamespace(fd->getUsedNamespaces(),name);
1927 }
1928
1929 //printf("%s -> %s\n",qPrint(name),usingNd?qPrint(usingNd->name()):"<none>");
1930
1931 // add the namespace the correct scope
1932 if (usingNd)
1933 {
1934 //printf("using fd=%p nd=%p\n",fd,nd);
1935 if (nd)
1936 {
1937 //printf("Inside namespace %s\n",qPrint(nd->name()));
1938 nd->addUsingDirective(usingNd);
1939 }
1940 else if (fd)
1941 {
1942 //printf("Inside file %s\n",qPrint(fd->name()));
1943 fd->addUsingDirective(usingNd);
1944 }
1945 }
1946 else // unknown namespace, but add it anyway.
1947 {
1948 AUTO_TRACE_ADD("new unknown namespace {} lang={} hidden={}",name,langToString(root->lang),root->hidden);
1949 // add namespace to the list
1952 createNamespaceDef(root->fileName,root->startLine,root->startColumn,name)));
1953 if (nd)
1954 {
1955 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1956 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1958 nd->setHidden(root->hidden);
1959 nd->setArtificial(TRUE);
1960 nd->setLanguage(root->lang);
1961 nd->setId(root->id);
1962 nd->setMetaData(root->metaData);
1963 nd->setInline(root->spec.isInline());
1964 nd->setExported(root->exported);
1965
1966 for (const Grouping &g : root->groups)
1967 {
1968 GroupDef *gd=nullptr;
1969 if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname)))
1970 gd->addNamespace(nd);
1971 }
1972
1973 // insert the namespace in the file definition
1974 if (fd)
1975 {
1976 fd->insertNamespace(nd);
1977 fd->addUsingDirective(nd);
1978 }
1979
1980 // the empty string test is needed for extract all case
1981 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1982 nd->insertUsedFile(fd);
1983 nd->setRefItems(root->sli);
1984 }
1985 }
1986 }
1987 }
1988 for (const auto &e : root->children()) findUsingDirectives(e.get());
1989}
1990
1991//----------------------------------------------------------------------
1992
1993static void buildListOfUsingDecls(const Entry *root)
1994{
1995 if (root->section.isUsingDecl() &&
1996 !root->parent()->section.isCompound() // not a class/struct member
1997 )
1998 {
1999 QCString name = substitute(root->name,".","::");
2000 g_usingDeclarations.insert(name.str());
2001 }
2002 for (const auto &e : root->children()) buildListOfUsingDecls(e.get());
2003}
2004
2005
2006static void findUsingDeclarations(const Entry *root,bool filterPythonPackages)
2007{
2008 if (root->section.isUsingDecl() &&
2009 !root->parent()->section.isCompound() && // not a class/struct member
2010 (!filterPythonPackages || (root->lang==SrcLangExt::Python && root->fileName.endsWith("__init__.py")))
2011 )
2012 {
2013 AUTO_TRACE("Found using declaration '{}' at line {} of {} inside section {}",
2014 root->name,root->startLine,root->fileName,root->parent()->section);
2015 if (!root->name.isEmpty())
2016 {
2017 const Definition *usingDef = nullptr;
2018 NamespaceDefMutable *nd = nullptr;
2019 FileDef *fd = root->fileDef();
2020 QCString scName;
2021
2022 // see if the using statement was found inside a namespace or inside
2023 // the global file scope.
2024 if (root->parent()->section.isNamespace())
2025 {
2026 scName=root->parent()->name;
2027 if (!scName.isEmpty())
2028 {
2029 nd = getResolvedNamespaceMutable(scName);
2030 }
2031 }
2032
2033 // Assume the using statement was used to import a class.
2034 // Find the scope in which the 'using' namespace is defined by prepending
2035 // the possible scopes in which the using statement was found, starting
2036 // with the most inner scope and going to the most outer scope (i.e.
2037 // file scope).
2038
2039 QCString name = substitute(root->name,".","::"); //Java/C# scope->internal
2040
2041 SymbolResolver resolver;
2042 const Definition *scope = nd;
2043 if (nd==nullptr) scope = fd;
2044 usingDef = resolver.resolveSymbol(scope,name);
2045
2046 //printf("usingDef(scope=%s,name=%s)=%s\n",qPrint(nd?nd->qualifiedName():""),qPrint(name),usingDef?qPrint(usingDef->qualifiedName()):"nullptr");
2047
2048 if (!usingDef)
2049 {
2050 usingDef = getClass(name); // try direct lookup, this is needed to get
2051 // builtin STL classes to properly resolve, e.g.
2052 // vector -> std::vector
2053 }
2054 if (!usingDef)
2055 {
2056 usingDef = Doxygen::hiddenClassLinkedMap->find(name); // check if it is already hidden
2057 }
2058#if 0
2059 if (!usingDef)
2060 {
2061 AUTO_TRACE_ADD("New using class '{}' (sec={})! #tArgLists={}",
2062 name,root->section,root->tArgLists.size());
2065 createClassDef( "<using>",1,1, name, ClassDef::Class)));
2066 if (usingCd)
2067 {
2068 usingCd->setArtificial(TRUE);
2069 usingCd->setLanguage(root->lang);
2070 usingDef = usingCd;
2071 }
2072 }
2073#endif
2074 else
2075 {
2076 AUTO_TRACE_ADD("Found used type '{}' in scope='{}'",
2077 usingDef->name(), nd ? nd->name(): fd ? fd->name() : QCString("<unknown>"));
2078 }
2079
2080 if (usingDef)
2081 {
2082 if (nd)
2083 {
2084 nd->addUsingDeclaration(usingDef);
2085 }
2086 else if (fd)
2087 {
2088 fd->addUsingDeclaration(usingDef);
2089 }
2090 }
2091 }
2092 }
2093 for (const auto &e : root->children()) findUsingDeclarations(e.get(),filterPythonPackages);
2094}
2095
2096//----------------------------------------------------------------------
2097
2099{
2100 root->commandOverrides.apply_callGraph ([&](bool b) { md->overrideCallGraph(b); });
2101 root->commandOverrides.apply_callerGraph ([&](bool b) { md->overrideCallerGraph(b); });
2102 root->commandOverrides.apply_referencedByRelation([&](bool b) { md->overrideReferencedByRelation(b); });
2103 root->commandOverrides.apply_referencesRelation ([&](bool b) { md->overrideReferencesRelation(b); });
2104 root->commandOverrides.apply_inlineSource ([&](bool b) { md->overrideInlineSource(b); });
2105 root->commandOverrides.apply_enumValues ([&](bool b) { md->overrideEnumValues(b); });
2106}
2107
2108//----------------------------------------------------------------------
2109
2111 const QCString &fileName,const QCString &memName)
2112{
2113 AUTO_TRACE("creating new member {} for class {}",memName,cd->name());
2114 const ArgumentList &templAl = md->templateArguments();
2115 const ArgumentList &al = md->argumentList();
2116 auto newMd = createMemberDef(
2117 fileName,root->startLine,root->startColumn,
2118 md->typeString(),memName,md->argsString(),
2119 md->excpString(),root->protection,root->virt,
2120 md->isStatic(),Relationship::Member,md->memberType(),
2121 templAl,al,root->metaData
2122 );
2123 auto newMmd = toMemberDefMutable(newMd.get());
2124 newMmd->setMemberClass(cd);
2125 cd->insertMember(newMd.get());
2126 if (!root->doc.isEmpty() || !root->brief.isEmpty())
2127 {
2128 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2129 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2130 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2131 }
2132 else
2133 {
2134 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2135 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2136 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2137 }
2138 newMmd->setDefinition(md->definition());
2139 applyMemberOverrideOptions(root,newMmd);
2140 newMmd->addQualifiers(root->qualifiers);
2141 newMmd->setBitfields(md->bitfieldString());
2142 newMmd->addSectionsToDefinition(root->anchors);
2143 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2144 newMmd->setBodyDef(md->getBodyDef());
2145 newMmd->setInitializer(md->initializer());
2146 newMmd->setRequiresClause(md->requiresClause());
2147 newMmd->setMaxInitLines(md->initializerLines());
2148 newMmd->setMemberGroupId(root->mGrpId);
2149 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2150 newMmd->setVhdlSpecifiers(md->getVhdlSpecifiers());
2151 newMmd->setLanguage(root->lang);
2152 newMmd->setId(root->id);
2153 MemberName *mn = Doxygen::memberNameLinkedMap->add(memName);
2154 mn->push_back(std::move(newMd));
2155}
2156
2157static std::unordered_map<std::string,std::vector<ClassDefMutable*>> g_usingClassMap;
2158
2159static void findUsingDeclImports(const Entry *root)
2160{
2161 if (root->section.isUsingDecl() &&
2162 root->parent()->section.isCompound() // in a class/struct member
2163 )
2164 {
2165 AUTO_TRACE("Found using declaration '{}' inside section {}", root->name, root->parent()->section);
2166 QCString fullName=removeRedundantWhiteSpace(root->parent()->name);
2167 fullName=stripAnonymousNamespaceScope(fullName);
2168 fullName=stripTemplateSpecifiersFromScope(fullName);
2169 ClassDefMutable *cd = getClassMutable(fullName);
2170 if (cd)
2171 {
2172 AUTO_TRACE_ADD("found class '{}'",cd->name());
2173 int i=root->name.findRev("::");
2174 if (i!=-1)
2175 {
2176 QCString scope=root->name.left(i);
2177 QCString memName=root->name.right(root->name.length()-i-2);
2178 SymbolResolver resolver;
2179 const ClassDef *bcd = resolver.resolveClass(cd,scope); // todo: file in fileScope parameter
2180 AUTO_TRACE_ADD("name={} scope={} bcd={}",scope,cd?cd->name():"<none>",bcd?bcd->name():"<none>");
2181 if (bcd && bcd!=cd)
2182 {
2183 AUTO_TRACE_ADD("found class '{}' memName='{}'",bcd->name(),memName);
2185 const MemberNameInfo *mni = mnlm.find(memName);
2186 if (mni)
2187 {
2188 for (auto &mi : *mni)
2189 {
2190 const MemberDef *md = mi->memberDef();
2191 if (md && md->protection()!=Protection::Private)
2192 {
2193 AUTO_TRACE_ADD("found member '{}'",mni->memberName());
2194 QCString fileName = root->fileName;
2195 if (fileName.isEmpty() && root->tagInfo())
2196 {
2197 fileName = root->tagInfo()->tagName;
2198 }
2199 if (!cd->containsOverload(md))
2200 {
2201 createUsingMemberImportForClass(root,cd,md,fileName,memName);
2202 // also insert the member into copies of the class
2203 auto it = g_usingClassMap.find(cd->qualifiedName().str());
2204 if (it != g_usingClassMap.end())
2205 {
2206 for (const auto &copyCd : it->second)
2207 {
2208 createUsingMemberImportForClass(root,copyCd,md,fileName,memName);
2209 }
2210 }
2211 }
2212 }
2213 }
2214 }
2215 }
2216 }
2217 }
2218 }
2219 else if (root->section.isUsingDecl() &&
2220 (root->parent()->section.isNamespace() || root->parent()->section.isEmpty()) && // namespace or global member
2221 root->lang==SrcLangExt::Cpp // do we also want this for e.g. Fortran? (see test case 095)
2222 )
2223 {
2224 AUTO_TRACE("Found using declaration '{}' inside section {}", root->name, root->parent()->section);
2225 Definition *scope = nullptr;
2226 NamespaceDefMutable *nd = nullptr;
2227 FileDef *fd = root->parent()->fileDef();
2228 if (!root->parent()->name.isEmpty())
2229 {
2230 QCString fullName=removeRedundantWhiteSpace(root->parent()->name);
2231 fullName=stripAnonymousNamespaceScope(fullName);
2233 scope = nd;
2234 }
2235 else
2236 {
2237 scope = fd;
2238 }
2239 if (scope)
2240 {
2241 AUTO_TRACE_ADD("found scope '{}'",scope->name());
2242 SymbolResolver resolver;
2243 const Definition *def = resolver.resolveSymbol(root->name.startsWith("::") ? nullptr : scope,root->name);
2244 if (def && def->definitionType()==Definition::TypeMember)
2245 {
2246 int i=root->name.findRev("::");
2247 QCString memName;
2248 if (i!=-1)
2249 {
2250 memName = root->name.right(root->name.length()-i-2);
2251 }
2252 else
2253 {
2254 memName = root->name;
2255 }
2256 const MemberDef *md = toMemberDef(def);
2257 AUTO_TRACE_ADD("found member '{}' for name '{}'",md->qualifiedName(),root->name);
2258 QCString fileName = root->fileName;
2259 if (fileName.isEmpty() && root->tagInfo())
2260 {
2261 fileName = root->tagInfo()->tagName;
2262 }
2263 const ArgumentList &templAl = md->templateArguments();
2264 const ArgumentList &al = md->argumentList();
2265
2266 auto newMd = createMemberDef(
2267 fileName,root->startLine,root->startColumn,
2268 md->typeString(),memName,md->argsString(),
2269 md->excpString(),root->protection,root->virt,
2270 md->isStatic(),Relationship::Member,md->memberType(),
2271 templAl,al,root->metaData
2272 );
2273 auto newMmd = toMemberDefMutable(newMd.get());
2274 if (nd)
2275 {
2276 newMmd->setNamespace(nd);
2277 nd->insertMember(newMd.get());
2278 }
2279 if (fd)
2280 {
2281 newMmd->setFileDef(fd);
2282 fd->insertMember(newMd.get());
2283 }
2284 if (!root->doc.isEmpty() || !root->brief.isEmpty())
2285 {
2286 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2287 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2288 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2289 }
2290 else
2291 {
2292 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2293 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2294 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2295 }
2296 newMmd->setDefinition(md->definition());
2297 applyMemberOverrideOptions(root,newMmd);
2298 newMmd->addQualifiers(root->qualifiers);
2299 newMmd->setBitfields(md->bitfieldString());
2300 newMmd->addSectionsToDefinition(root->anchors);
2301 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2302 newMmd->setBodyDef(md->getBodyDef());
2303 newMmd->setInitializer(md->initializer());
2304 newMmd->setRequiresClause(md->requiresClause());
2305 newMmd->setMaxInitLines(md->initializerLines());
2306 newMmd->setMemberGroupId(root->mGrpId);
2307 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2308 newMmd->setVhdlSpecifiers(md->getVhdlSpecifiers());
2309 newMmd->setLanguage(root->lang);
2310 newMmd->setId(root->id);
2311 MemberName *mn = Doxygen::functionNameLinkedMap->add(memName);
2312 mn->push_back(std::move(newMd));
2313#if 0 // insert an alias instead of a copy
2314 const MemberDef *md = toMemberDef(def);
2315 AUTO_TRACE_ADD("found member '{}' for name '{}'",md->qualifiedName(),root->name);
2316 auto aliasMd = createMemberDefAlias(nd,md);
2317 QCString aliasFullName = nd->qualifiedName()+"::"+aliasMd->localName();
2318 if (nd && aliasMd.get())
2319 {
2320 nd->insertMember(aliasMd.get());
2321 }
2322 if (fd && aliasMd.get())
2323 {
2324 fd->insertMember(aliasMd.get());
2325 }
2326 MemberName *mn = Doxygen::memberNameLinkedMap->add(aliasFullName);
2327 mn->push_back(std::move(aliasMd));
2328#endif
2329 }
2330 else if (def && def->definitionType()==Definition::TypeClass)
2331 {
2332 const ClassDef *cd = toClassDef(def);
2333 QCString copyFullName;
2334 if (nd==nullptr)
2335 {
2336 copyFullName = cd->localName();
2337 }
2338 else
2339 {
2340 copyFullName = nd->qualifiedName()+"::"+cd->localName();
2341 }
2342 if (Doxygen::classLinkedMap->find(copyFullName)==nullptr)
2343 {
2345 Doxygen::classLinkedMap->add(copyFullName,
2346 cd->deepCopy(copyFullName)));
2347 AUTO_TRACE_ADD("found class '{}' for name '{}' copy '{}' obj={}",cd->qualifiedName(),root->name,copyFullName,(void*)ncdm);
2348 g_usingClassMap[cd->qualifiedName().str()].push_back(ncdm);
2349 if (ncdm)
2350 {
2351 if (nd) ncdm->moveTo(nd);
2352 if ((!root->doc.isEmpty() || !root->brief.isEmpty())) // use docs at using statement
2353 {
2354 ncdm->setDocumentation(root->doc,root->docFile,root->docLine);
2355 ncdm->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2356 }
2357 else // use docs from used class
2358 {
2359 ncdm->setDocumentation(cd->documentation(),cd->docFile(),cd->docLine());
2361 }
2362 if (nd)
2363 {
2364 nd->addInnerCompound(ncdm);
2365 nd->addUsingDeclaration(ncdm);
2366 }
2367 if (fd)
2368 {
2369 if (ncdm) ncdm->setFileDef(fd);
2370 fd->insertClass(ncdm);
2371 fd->addUsingDeclaration(ncdm);
2372 }
2373 }
2374 }
2375#if 0 // insert an alias instead of a copy
2376 auto aliasCd = createClassDefAlias(nd,cd);
2377 QCString aliasFullName;
2378 if (nd==nullptr)
2379 {
2380 aliasFullName = aliasCd->localName();
2381 }
2382 else
2383 {
2384 aliasFullName = nd->qualifiedName()+"::"+aliasCd->localName();
2385 }
2386 AUTO_TRACE_ADD("found class '{}' for name '{}' aliasFullName='{}'",cd->qualifiedName(),root->name,aliasFullName);
2387 auto acd = Doxygen::classLinkedMap->add(aliasFullName,std::move(aliasCd));
2388 if (nd && acd)
2389 {
2390 nd->addInnerCompound(acd);
2391 }
2392 if (fd && acd)
2393 {
2394 fd->insertClass(acd);
2395 }
2396#endif
2397 }
2398 else if (scope)
2399 {
2400 AUTO_TRACE_ADD("no symbol with name '{}' in scope {}",root->name,scope->name());
2401 }
2402 }
2403 }
2404 for (const auto &e : root->children()) findUsingDeclImports(e.get());
2405}
2406
2407//----------------------------------------------------------------------
2408
2410{
2411 FileDefSet visitedFiles;
2412 // then recursively add using directives found in #include files
2413 // to files that have not been visited.
2414 for (const auto &fn : *Doxygen::inputNameLinkedMap)
2415 {
2416 for (const auto &fd : *fn)
2417 {
2418 //printf("----- adding using directives for file %s\n",qPrint(fd->name()));
2419 fd->addIncludedUsingDirectives(visitedFiles);
2420 }
2421 }
2422}
2423
2424//----------------------------------------------------------------------
2425
2427 const Entry *root,
2428 ClassDefMutable *cd,
2429 MemberType mtype,
2430 const QCString &type,
2431 const QCString &name,
2432 const QCString &args,
2433 bool fromAnnScope,
2434 MemberDef *fromAnnMemb,
2435 Protection prot,
2436 Relationship related)
2437{
2439 QCString scopeSeparator="::";
2440 SrcLangExt lang = cd->getLanguage();
2441 if (lang==SrcLangExt::Java || lang==SrcLangExt::CSharp)
2442 {
2443 qualScope = substitute(qualScope,"::",".");
2444 scopeSeparator=".";
2445 }
2446 AUTO_TRACE("class variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} ann={} init='{}'",
2447 root->fileName, type, qualScope, name, args, root->protection, mtype, lang, fromAnnScope, root->initializer.str());
2448
2449 QCString def;
2450 if (!type.isEmpty())
2451 {
2452 if (related!=Relationship::Member || mtype==MemberType::Friend || Config_getBool(HIDE_SCOPE_NAMES))
2453 {
2454 if (root->spec.isAlias()) // turn 'typedef B A' into 'using A'
2455 {
2456 def="using "+name;
2457 }
2458 else
2459 {
2460 def=type+" "+name+args;
2461 }
2462 }
2463 else
2464 {
2465 if (root->spec.isAlias()) // turn 'typedef B C::A' into 'using C::A'
2466 {
2467 def="using "+qualScope+scopeSeparator+name;
2468 }
2469 else
2470 {
2471 def=type+" "+qualScope+scopeSeparator+name+args;
2472 }
2473 }
2474 }
2475 else
2476 {
2477 if (Config_getBool(HIDE_SCOPE_NAMES))
2478 {
2479 def=name+args;
2480 }
2481 else
2482 {
2483 def=qualScope+scopeSeparator+name+args;
2484 }
2485 }
2486 def.stripPrefix("static ");
2487
2488 // see if the member is already found in the same scope
2489 // (this may be the case for a static member that is initialized
2490 // outside the class)
2492 if (mn)
2493 {
2494 for (const auto &imd : *mn)
2495 {
2496 //printf("md->getClassDef()=%p cd=%p type=[%s] md->typeString()=[%s]\n",
2497 // md->getClassDef(),cd,qPrint(type),md->typeString());
2498 MemberDefMutable *md = toMemberDefMutable(imd.get());
2499 if (md &&
2500 md->getClassDef()==cd &&
2501 ((lang==SrcLangExt::Python && type.isEmpty() && !md->typeString().isEmpty()) ||
2503 // member already in the scope
2504 {
2505
2506 if (root->lang==SrcLangExt::ObjC &&
2507 root->mtype==MethodTypes::Property &&
2509 { // Objective-C 2.0 property
2510 // turn variable into a property
2511 md->setProtection(root->protection);
2513 }
2514 addMemberDocs(root,md,def,nullptr,FALSE,root->spec);
2515 AUTO_TRACE_ADD("Member already found!");
2516 return md;
2517 }
2518 }
2519 }
2520
2521 QCString fileName = root->fileName;
2522 if (fileName.isEmpty() && root->tagInfo())
2523 {
2524 fileName = root->tagInfo()->tagName;
2525 }
2526
2527 // new member variable, typedef or enum value
2528 auto md = createMemberDef(
2529 fileName,root->startLine,root->startColumn,
2530 type,name,args,root->exception,
2531 prot,Specifier::Normal,root->isStatic,related,
2532 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2533 ArgumentList(), root->metaData);
2534 auto mmd = toMemberDefMutable(md.get());
2535 mmd->setTagInfo(root->tagInfo());
2536 mmd->setMemberClass(cd); // also sets outer scope (i.e. getOuterScope())
2537 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2538 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2539 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2540 mmd->setDefinition(def);
2541 mmd->setBitfields(root->bitfields);
2542 mmd->addSectionsToDefinition(root->anchors);
2543 mmd->setFromAnonymousScope(fromAnnScope);
2544 mmd->setFromAnonymousMember(fromAnnMemb);
2545 //md->setIndentDepth(indentDepth);
2546 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2547 mmd->setInitializer(root->initializer.str());
2548 mmd->setMaxInitLines(root->initLines);
2549 mmd->setMemberGroupId(root->mGrpId);
2550 mmd->setMemberSpecifiers(root->spec);
2551 mmd->setVhdlSpecifiers(root->vhdlSpec);
2552 mmd->setReadAccessor(root->read);
2553 mmd->setWriteAccessor(root->write);
2555 mmd->setHidden(root->hidden);
2556 mmd->setArtificial(root->artificial);
2557 mmd->setLanguage(root->lang);
2558 mmd->setId(root->id);
2559 addMemberToGroups(root,md.get());
2561 mmd->setBodyDef(root->fileDef());
2562 mmd->addQualifiers(root->qualifiers);
2563
2564 AUTO_TRACE_ADD("Adding new member to class '{}'",cd->name());
2565 cd->insertMember(md.get());
2566 mmd->setRefItems(root->sli);
2567
2568 cd->insertUsedFile(root->fileDef());
2569 root->markAsProcessed();
2570
2571 if (mtype==MemberType::Typedef)
2572 {
2573 resolveTemplateInstanceInType(root,cd,md.get());
2574 }
2575
2576 // add the member to the global list
2577 MemberDef *result = md.get();
2578 mn = Doxygen::memberNameLinkedMap->add(name);
2579 mn->push_back(std::move(md));
2580
2581 return result;
2582}
2583
2584//----------------------------------------------------------------------
2585
2587 const Entry *root,
2588 MemberType mtype,
2589 const QCString &scope,
2590 const QCString &type,
2591 const QCString &name,
2592 const QCString &args,
2593 bool fromAnnScope,
2594 MemberDef *fromAnnMemb)
2595{
2596 AUTO_TRACE("global variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} init='{}'",
2597 root->fileName, type, scope, name, args, root->protection, mtype, root->lang, root->initializer.str());
2598
2599 FileDef *fd = root->fileDef();
2600
2601 // see if we have a typedef that should hide a struct or union
2602 if (mtype==MemberType::Typedef && Config_getBool(TYPEDEF_HIDES_STRUCT))
2603 {
2604 QCString ttype = type;
2605 ttype.stripPrefix("typedef ");
2606 if (ttype.stripPrefix("struct ") || ttype.stripPrefix("union "))
2607 {
2608 static const reg::Ex re(R"(\a\w*)");
2609 reg::Match match;
2610 std::string typ = ttype.str();
2611 if (reg::search(typ,match,re))
2612 {
2613 QCString typeValue = match.str();
2614 ClassDefMutable *cd = getClassMutable(typeValue);
2615 if (cd)
2616 {
2617 // this typedef should hide compound name cd, so we
2618 // change the name that is displayed from cd.
2619 cd->setClassName(name);
2620 cd->setDocumentation(root->doc,root->docFile,root->docLine);
2621 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2622 return nullptr;
2623 }
2624 }
2625 }
2626 }
2627
2628 // see if the function is inside a namespace
2629 NamespaceDefMutable *nd = nullptr;
2630 if (!scope.isEmpty())
2631 {
2632 if (scope.find('@')!=-1) return nullptr; // anonymous scope!
2633 nd = getResolvedNamespaceMutable(scope);
2634 }
2635 QCString def;
2636
2637 // determine the definition of the global variable
2638 if (nd && !nd->isAnonymous() &&
2639 !Config_getBool(HIDE_SCOPE_NAMES)
2640 )
2641 // variable is inside a namespace, so put the scope before the name
2642 {
2643 SrcLangExt lang = nd->getLanguage();
2645
2646 if (!type.isEmpty())
2647 {
2648 if (root->spec.isAlias()) // turn 'typedef B NS::A' into 'using NS::A'
2649 {
2650 def="using "+nd->name()+sep+name;
2651 }
2652 else // normal member
2653 {
2654 def=type+" "+nd->name()+sep+name+args;
2655 }
2656 }
2657 else
2658 {
2659 def=nd->name()+sep+name+args;
2660 }
2661 }
2662 else
2663 {
2664 if (!type.isEmpty() && !root->name.isEmpty())
2665 {
2666 if (name.at(0)=='@') // dummy variable representing anonymous union
2667 {
2668 def=type;
2669 }
2670 else
2671 {
2672 if (root->spec.isAlias()) // turn 'typedef B A' into 'using A'
2673 {
2674 def="using "+root->name;
2675 }
2676 else // normal member
2677 {
2678 def=type+" "+name+args;
2679 }
2680 }
2681 }
2682 else
2683 {
2684 def=name+args;
2685 }
2686 }
2687 def.stripPrefix("static ");
2688
2690 if (mn)
2691 {
2692 //QCString nscope=removeAnonymousScopes(scope);
2693 //NamespaceDef *nd=nullptr;
2694 //if (!nscope.isEmpty())
2695 if (!scope.isEmpty())
2696 {
2697 nd = getResolvedNamespaceMutable(scope);
2698 }
2699 for (const auto &imd : *mn)
2700 {
2701 MemberDefMutable *md = toMemberDefMutable(imd.get());
2702 if (md &&
2703 ((nd==nullptr && md->getNamespaceDef()==nullptr && md->getFileDef() &&
2704 root->fileName==md->getFileDef()->absFilePath()
2705 ) // both variable names in the same file
2706 || (nd!=nullptr && md->getNamespaceDef()==nd) // both in same namespace
2707 )
2708 && !md->isDefine() // function style #define's can be "overloaded" by typedefs or variables
2709 && !md->isEnumerate() // in C# an enum value and enum can have the same name
2710 )
2711 // variable already in the scope
2712 {
2713 bool isPHPArray = md->getLanguage()==SrcLangExt::PHP &&
2714 md->argsString()!=args &&
2715 args.find('[')!=-1;
2716 bool staticsInDifferentFiles =
2717 root->isStatic && md->isStatic() &&
2718 root->fileName!=md->getDefFileName();
2719
2720 if (md->getFileDef() &&
2721 !isPHPArray && // not a php array
2722 !staticsInDifferentFiles
2723 )
2724 // not a php array variable
2725 {
2726 AUTO_TRACE_ADD("variable already found: scope='{}'",md->getOuterScope()->name());
2727 addMemberDocs(root,md,def,nullptr,FALSE,root->spec);
2728 md->setRefItems(root->sli);
2729 // if md is a variable forward declaration and root is the definition that
2730 // turn md into the definition
2731 if (!root->explicitExternal && md->isExternal())
2732 {
2733 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
2735 }
2736 // if md is the definition and root point at a declaration, then add the
2737 // declaration info
2738 else if (root->explicitExternal && !md->isExternal())
2739 {
2740 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
2741 }
2742 return md;
2743 }
2744 }
2745 }
2746 }
2747
2748 QCString fileName = root->fileName;
2749 if (fileName.isEmpty() && root->tagInfo())
2750 {
2751 fileName = root->tagInfo()->tagName;
2752 }
2753
2754 AUTO_TRACE_ADD("new variable, namespace='{}'",nd?nd->name():QCString("<global>"));
2755 // new global variable, enum value or typedef
2756 auto md = createMemberDef(
2757 fileName,root->startLine,root->startColumn,
2758 type,name,args,QCString(),
2759 root->protection, Specifier::Normal,root->isStatic,Relationship::Member,
2760 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2761 root->argList, root->metaData);
2762 auto mmd = toMemberDefMutable(md.get());
2763 mmd->setTagInfo(root->tagInfo());
2764 mmd->setMemberSpecifiers(root->spec);
2765 mmd->setVhdlSpecifiers(root->vhdlSpec);
2766 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2767 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2768 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2769 mmd->addSectionsToDefinition(root->anchors);
2770 mmd->setFromAnonymousScope(fromAnnScope);
2771 mmd->setFromAnonymousMember(fromAnnMemb);
2772 mmd->setInitializer(root->initializer.str());
2773 mmd->setMaxInitLines(root->initLines);
2774 mmd->setMemberGroupId(root->mGrpId);
2775 mmd->setDefinition(def);
2776 mmd->setLanguage(root->lang);
2777 mmd->setId(root->id);
2779 mmd->setExplicitExternal(root->explicitExternal,fileName,root->startLine,root->startColumn);
2780 mmd->addQualifiers(root->qualifiers);
2781 //md->setOuterScope(fd);
2782 if (!root->explicitExternal)
2783 {
2784 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2785 mmd->setBodyDef(fd);
2786 }
2787 addMemberToGroups(root,md.get());
2789
2790 mmd->setRefItems(root->sli);
2791 if (nd && !nd->isAnonymous())
2792 {
2793 mmd->setNamespace(nd);
2794 nd->insertMember(md.get());
2795 }
2796
2797 // add member to the file (we do this even if we have already inserted
2798 // it into the namespace.
2799 if (fd)
2800 {
2801 mmd->setFileDef(fd);
2802 fd->insertMember(md.get());
2803 }
2804
2805 root->markAsProcessed();
2806
2807 if (mtype==MemberType::Typedef)
2808 {
2809 resolveTemplateInstanceInType(root,nd,md.get());
2810 }
2811
2812 // add member definition to the list of globals
2813 MemberDef *result = md.get();
2814 mn = Doxygen::functionNameLinkedMap->add(name);
2815 mn->push_back(std::move(md));
2816
2817
2818
2819 return result;
2820}
2821
2822/*! See if the return type string \a type is that of a function pointer
2823 * \returns -1 if this is not a function pointer variable or
2824 * the index at which the closing brace of (...*name) was found.
2825 */
2826static int findFunctionPtr(const std::string &type,SrcLangExt lang, int *pLength=nullptr)
2827{
2828 AUTO_TRACE("type='{}' lang={}",type,lang);
2829 if (lang == SrcLangExt::Fortran || lang == SrcLangExt::VHDL)
2830 {
2831 return -1; // Fortran and VHDL do not have function pointers
2832 }
2833
2834 static const reg::Ex re(R"(\‍([^)]*[*&^][^)]*\))");
2835 reg::Match match;
2836 size_t i=std::string::npos;
2837 size_t l=0;
2838 if (reg::search(type,match,re)) // contains (...*...) or (...&...) or (...^...)
2839 {
2840 i = match.position();
2841 l = match.length();
2842 }
2843 if (i!=std::string::npos)
2844 {
2845 size_t di = type.find("decltype(");
2846 if (di!=std::string::npos && di<i)
2847 {
2848 i = std::string::npos;
2849 }
2850 }
2851 size_t bb=type.find('<');
2852 size_t be=type.rfind('>');
2853 bool templFp = false;
2854 if (be!=std::string::npos) {
2855 size_t cc_ast = type.find("::*");
2856 size_t cc_amp = type.find("::&");
2857 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>::*)'
2858 }
2859
2860 if (!type.empty() && // return type is non-empty
2861 i!=std::string::npos && // contains (...*...)
2862 type.find("operator")==std::string::npos && // not an operator
2863 (type.find(")(")==std::string::npos || type.find("typedef ")!=std::string::npos) &&
2864 // not a function pointer return type
2865 (!(bb<i && i<be) || templFp) // bug665855: avoid treating "typedef A<void (T*)> type" as a function pointer
2866 )
2867 {
2868 if (pLength) *pLength=static_cast<int>(l);
2869 //printf("findFunctionPtr=%d\n",(int)i);
2870 AUTO_TRACE_EXIT("result={}",i);
2871 return static_cast<int>(i);
2872 }
2873 else
2874 {
2875 //printf("findFunctionPtr=%d\n",-1);
2876 AUTO_TRACE_EXIT("result=-1");
2877 return -1;
2878 }
2879}
2880
2881//--------------------------------------------------------------------------------------
2882
2883/*! Returns TRUE iff \a type is a class within scope \a context.
2884 * Used to detect variable declarations that look like function prototypes.
2885 */
2886static bool isVarWithConstructor(const Entry *root)
2887{
2888 bool result = false;
2889 bool typeIsClass = false;
2890 bool typePtrType = false;
2891 QCString type;
2892 Definition *ctx = nullptr;
2893 FileDef *fd = root->fileDef();
2894 SymbolResolver resolver(fd);
2895
2896 AUTO_TRACE("isVarWithConstructor({})",root->name);
2897 if (root->parent()->section.isCompound())
2898 { // inside a class
2899 result=FALSE;
2900 AUTO_TRACE_EXIT("inside class: result={}",result);
2901 return result;
2902 }
2903 else if ((fd != nullptr) && (fd->name().endsWith(".c") || fd->name().endsWith(".h")))
2904 { // inside a .c file
2905 result=FALSE;
2906 AUTO_TRACE_EXIT("inside C file: result={}",result);
2907 return result;
2908 }
2909 if (root->type.isEmpty())
2910 {
2911 result=FALSE;
2912 AUTO_TRACE_EXIT("no type: result={}",result);
2913 return result;
2914 }
2915 if (!root->parent()->name.isEmpty())
2916 {
2917 ctx=Doxygen::namespaceLinkedMap->find(root->parent()->name);
2918 }
2919 type = root->type;
2920 // remove qualifiers
2921 findAndRemoveWord(type,"const");
2922 findAndRemoveWord(type,"static");
2923 findAndRemoveWord(type,"volatile");
2924 typePtrType = type.find('*')!=-1 || type.find('&')!=-1;
2925 if (!typePtrType)
2926 {
2927 typeIsClass = resolver.resolveClass(ctx,type)!=nullptr;
2928 int ti=0;
2929 if (!typeIsClass && (ti=type.find('<'))!=-1)
2930 {
2931 typeIsClass=resolver.resolveClass(ctx,type.left(ti))!=nullptr;
2932 }
2933 }
2934 if (typeIsClass) // now we still have to check if the arguments are
2935 // types or values. Since we do not have complete type info
2936 // we need to rely on heuristics :-(
2937 {
2938 if (root->argList.empty())
2939 {
2940 result=FALSE; // empty arg list -> function prototype.
2941 AUTO_TRACE_EXIT("empty arg list: result={}",result);
2942 return result;
2943 }
2944 for (const Argument &a : root->argList)
2945 {
2946 static const reg::Ex initChars(R"([\d"'&*!^]+)");
2947 reg::Match match;
2948 if (!a.name.isEmpty() || !a.defval.isEmpty())
2949 {
2950 std::string name = a.name.str();
2951 if (reg::search(name,match,initChars) && match.position()==0)
2952 {
2953 result=TRUE;
2954 }
2955 else
2956 {
2957 result=FALSE; // arg has (type,name) pair -> function prototype
2958 }
2959 AUTO_TRACE_EXIT("function prototype: result={}",result);
2960 return result;
2961 }
2962 if (!a.type.isEmpty() &&
2963 (a.type.at(a.type.length()-1)=='*' ||
2964 a.type.at(a.type.length()-1)=='&'))
2965 // type ends with * or & => pointer or reference
2966 {
2967 result=FALSE;
2968 AUTO_TRACE_EXIT("pointer or reference: result={}",result);
2969 return result;
2970 }
2971 if (a.type.isEmpty() || resolver.resolveClass(ctx,a.type)!=nullptr)
2972 {
2973 result=FALSE; // arg type is a known type
2974 AUTO_TRACE_EXIT("known type: result={}",result);
2975 return result;
2976 }
2977 if (checkIfTypedef(ctx,fd,a.type))
2978 {
2979 result=FALSE; // argument is a typedef
2980 AUTO_TRACE_EXIT("typedef: result={}",result);
2981 return result;
2982 }
2983 std::string atype = a.type.str();
2984 if (reg::search(atype,match,initChars) && match.position()==0)
2985 {
2986 result=TRUE; // argument type starts with typical initializer char
2987 AUTO_TRACE_EXIT("argument with init char: result={}",result);
2988 return result;
2989 }
2990 std::string resType=resolveTypeDef(ctx,a.type).str();
2991 if (resType.empty()) resType=atype;
2992 static const reg::Ex idChars(R"(\a\w*)");
2993 if (reg::search(resType,match,idChars) && match.position()==0) // resType starts with identifier
2994 {
2995 resType=match.str();
2996 if (resType=="int" || resType=="long" ||
2997 resType=="float" || resType=="double" ||
2998 resType=="char" || resType=="void" ||
2999 resType=="signed" || resType=="unsigned" ||
3000 resType=="const" || resType=="volatile" )
3001 {
3002 result=FALSE; // type keyword -> function prototype
3003 AUTO_TRACE_EXIT("type keyword: result={}",result);
3004 return result;
3005 }
3006 }
3007 }
3008 result=TRUE;
3009 }
3010
3011 AUTO_TRACE_EXIT("end: result={}",result);
3012 return result;
3013}
3014
3015//--------------------------------------------------------------------------------------
3016
3017/*! Searches for the end of a template in prototype \a s starting from
3018 * character position \a startPos. If the end was found the position
3019 * of the closing > is returned, otherwise -1 is returned.
3020 *
3021 * Handles exotic cases such as
3022 * \code
3023 * Class<(id<0)>
3024 * Class<bits<<2>
3025 * Class<"<">
3026 * Class<'<'>
3027 * Class<(")<")>
3028 * \endcode
3029 */
3030static int findEndOfTemplate(const QCString &s,size_t startPos)
3031{
3032 // locate end of template
3033 size_t e=startPos;
3034 int brCount=1;
3035 int roundCount=0;
3036 size_t len = s.length();
3037 bool insideString=FALSE;
3038 bool insideChar=FALSE;
3039 char pc = 0;
3040 while (e<len && brCount!=0)
3041 {
3042 char c=s.at(e);
3043 switch(c)
3044 {
3045 case '<':
3046 if (!insideString && !insideChar)
3047 {
3048 if (e<len-1 && s.at(e+1)=='<')
3049 e++;
3050 else if (roundCount==0)
3051 brCount++;
3052 }
3053 break;
3054 case '>':
3055 if (!insideString && !insideChar)
3056 {
3057 if (e<len-1 && s.at(e+1)=='>')
3058 e++;
3059 else if (roundCount==0)
3060 brCount--;
3061 }
3062 break;
3063 case '(':
3064 if (!insideString && !insideChar)
3065 roundCount++;
3066 break;
3067 case ')':
3068 if (!insideString && !insideChar)
3069 roundCount--;
3070 break;
3071 case '"':
3072 if (!insideChar)
3073 {
3074 if (insideString && pc!='\\')
3075 insideString=FALSE;
3076 else
3077 insideString=TRUE;
3078 }
3079 break;
3080 case '\'':
3081 if (!insideString)
3082 {
3083 if (insideChar && pc!='\\')
3084 insideChar=FALSE;
3085 else
3086 insideChar=TRUE;
3087 }
3088 break;
3089 }
3090 pc = c;
3091 e++;
3092 }
3093 return brCount==0 ? static_cast<int>(e) : -1;
3094}
3095
3096//--------------------------------------------------------------------------------------
3097
3098static void addVariable(const Entry *root,int isFuncPtr=-1)
3099{
3100 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
3101
3102 AUTO_TRACE("VARIABLE_SEC: type='{}' name='{}' args='{}' bodyLine={} endBodyLine={} mGrpId={} relates='{}'",
3103 root->type, root->name, root->args, root->bodyLine, root->endBodyLine, root->mGrpId, root->relates);
3104 //printf("root->parent->name=%s\n",qPrint(root->parent->name));
3105
3106 QCString type = root->type;
3107 QCString name = root->name;
3108 QCString args = root->args;
3109 if (type.isEmpty() && name.find("operator")==-1 &&
3110 (name.find('*')!=-1 || name.find('&')!=-1))
3111 {
3112 // recover from parse error caused by redundant braces
3113 // like in "int *(var[10]);", which is parsed as
3114 // type="" name="int *" args="(var[10])"
3115
3116 type=name;
3117 std::string sargs = args.str();
3118 static const reg::Ex reName(R"(\a\w*)");
3119 reg::Match match;
3120 if (reg::search(sargs,match,reName))
3121 {
3122 name = match.str(); // e.g. 'var' in '(var[10])'
3123 sargs = match.suffix().str(); // e.g. '[10]) in '(var[10])'
3124 size_t j = sargs.find(')');
3125 if (j!=std::string::npos) args=sargs.substr(0,j); // extract, e.g '[10]' from '[10])'
3126 }
3127 }
3128 else
3129 {
3130 int i=isFuncPtr;
3131 if (i==-1 && (root->spec.isAlias())==0) i=findFunctionPtr(type.str(),root->lang); // for typedefs isFuncPtr is not yet set
3132 AUTO_TRACE_ADD("functionPtr={}",i!=-1?"yes":"no");
3133 if (i>=0) // function pointer
3134 {
3135 int ai = type.find('[',i);
3136 if (ai>i) // function pointer array
3137 {
3138 args.prepend(type.right(type.length()-ai));
3139 type=type.left(ai);
3140 }
3141 else if (type.find(')',i)!=-1) // function ptr, not variable like "int (*bla)[10]"
3142 {
3143 type=type.left(type.length()-1);
3144 args.prepend(") ");
3145 }
3146 }
3147 }
3148 AUTO_TRACE_ADD("after correction: type='{}' name='{}' args='{}'",type,name,args);
3149
3150 QCString scope;
3151 name=removeRedundantWhiteSpace(name);
3152
3153 // find the scope of this variable
3154 int index = computeQualifiedIndex(name);
3155 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3156 // grouped members are stored with full scope
3157 {
3158 buildScopeFromQualifiedName(name.left(index+2),root->lang,root->tagInfo());
3159 scope=name.left(index);
3160 name=name.mid(index+2);
3161 }
3162 else
3163 {
3164 Entry *p = root->parent();
3165 while (p->section.isScope())
3166 {
3167 QCString scopeName = p->name;
3168 if (!scopeName.isEmpty())
3169 {
3170 scope.prepend(scopeName);
3171 break;
3172 }
3173 p=p->parent();
3174 }
3175 }
3176
3177 type=type.stripWhiteSpace();
3178 ClassDefMutable *cd=nullptr;
3179 bool isRelated=FALSE;
3180 bool isMemberOf=FALSE;
3181
3182 QCString classScope=stripAnonymousNamespaceScope(scope);
3183 if (root->lang==SrcLangExt::CSharp)
3184 {
3185 classScope=mangleCSharpGenericName(classScope);
3186 }
3187 else
3188 {
3189 classScope=stripTemplateSpecifiersFromScope(classScope,FALSE);
3190 }
3191 QCString annScopePrefix=scope.left(scope.length()-classScope.length());
3192
3193
3194 // Look for last :: not part of template specifier
3195 int p=-1;
3196 for (size_t i=0;i<name.length()-1;i++)
3197 {
3198 if (name[i]==':' && name[i+1]==':')
3199 {
3200 p=static_cast<int>(i);
3201 }
3202 else if (name[i]=='<') // skip over template parts,
3203 // i.e. A::B<C::D> => p=1 and
3204 // A<B::C>::D => p=8
3205 {
3206 int e = findEndOfTemplate(name,i+1);
3207 if (e!=-1) i=static_cast<int>(e);
3208 }
3209 }
3210
3211 if (p!=-1) // found it
3212 {
3213 if (type=="friend class" || type=="friend struct" ||
3214 type=="friend union")
3215 {
3216 cd=getClassMutable(scope);
3217 if (cd)
3218 {
3219 addVariableToClass(root, // entry
3220 cd, // class to add member to
3221 MemberType::Friend, // type of member
3222 type, // type value as string
3223 name, // name of the member
3224 args, // arguments as string
3225 FALSE, // from Anonymous scope
3226 nullptr, // anonymous member
3227 Protection::Public, // protection
3228 Relationship::Member // related to a class
3229 );
3230 }
3231 }
3232 if (root->bodyLine!=-1 && root->endBodyLine!=-1) // store the body location for later use
3233 {
3234 Doxygen::staticInitMap.emplace(name.str(),BodyInfo{root->startLine,root->bodyLine,root->endBodyLine});
3235 }
3236
3237
3238 AUTO_TRACE_ADD("static variable {} body=[{}..{}]",name,root->bodyLine,root->endBodyLine);
3239 return; /* skip this member, because it is a
3240 * static variable definition (always?), which will be
3241 * found in a class scope as well, but then we know the
3242 * correct protection level, so only then it will be
3243 * inserted in the correct list!
3244 */
3245 }
3246
3248 if (type=="@")
3250 else if (type.startsWith("typedef "))
3251 mtype=MemberType::Typedef;
3252 else if (type.startsWith("friend "))
3253 mtype=MemberType::Friend;
3254 else if (root->mtype==MethodTypes::Property)
3256 else if (root->mtype==MethodTypes::Event)
3257 mtype=MemberType::Event;
3258 else if (type.find("sequence<") != -1)
3259 mtype=sliceOpt ? MemberType::Sequence : MemberType::Typedef;
3260 else if (type.find("dictionary<") != -1)
3262
3263 if (!root->relates.isEmpty()) // related variable
3264 {
3265 isRelated=TRUE;
3266 isMemberOf=(root->relatesType==RelatesType::MemberOf);
3267 if (getClass(root->relates)==nullptr && !scope.isEmpty())
3268 scope=mergeScopes(scope,root->relates);
3269 else
3270 scope=root->relates;
3271 }
3272
3273 cd=getClassMutable(scope);
3274 if (cd==nullptr && classScope!=scope) cd=getClassMutable(classScope);
3275 if (cd)
3276 {
3277 MemberDef *md=nullptr;
3278
3279 // if cd is an anonymous (=tag less) scope we insert the member
3280 // into a non-anonymous parent scope as well. This is needed to
3281 // be able to refer to it using \var or \fn
3282
3283 //int indentDepth=0;
3284 int si=scope.find('@');
3285 //int anonyScopes = 0;
3286 //bool added=FALSE;
3287
3288 bool inlineSimpleStructs = Config_getBool(INLINE_SIMPLE_STRUCTS);
3289 Relationship relationship = isMemberOf ? Relationship::Foreign :
3290 isRelated ? Relationship::Related :
3291 Relationship::Member ;
3292 if (si!=-1 && !inlineSimpleStructs) // anonymous scope or type
3293 {
3294 QCString pScope;
3295 ClassDefMutable *pcd=nullptr;
3296 pScope = scope.left(std::max(si-2,0)); // scope without tag less parts
3297 if (!pScope.isEmpty())
3298 pScope.prepend(annScopePrefix);
3299 else if (annScopePrefix.length()>2)
3300 pScope=annScopePrefix.left(annScopePrefix.length()-2);
3301 if (name.at(0)!='@')
3302 {
3303 if (!pScope.isEmpty() && (pcd=getClassMutable(pScope)))
3304 {
3305 AUTO_TRACE_ADD("Adding anonymous member to scope '{}'",pScope);
3306 md=addVariableToClass(root, // entry
3307 pcd, // class to add member to
3308 mtype, // member type
3309 type, // type value as string
3310 name, // member name
3311 args, // arguments as string
3312 TRUE, // from anonymous scope
3313 nullptr, // from anonymous member
3314 root->protection,
3315 relationship
3316 );
3317 //added=TRUE;
3318 }
3319 else // anonymous scope inside namespace or file => put variable in the global scope
3320 {
3321 if (mtype==MemberType::Variable)
3322 {
3323 AUTO_TRACE_ADD("Adding anonymous member to global scope '{}'");
3324 md=addVariableToFile(root,mtype,pScope,type,name,args,TRUE,nullptr);
3325 }
3326 //added=TRUE;
3327 }
3328 }
3329 }
3330
3331 addVariableToClass(root, // entry
3332 cd, // class to add member to
3333 mtype, // member type
3334 type, // type value as string
3335 name, // name of the member
3336 args, // arguments as string
3337 FALSE, // from anonymous scope
3338 md, // from anonymous member
3339 root->protection,
3340 relationship
3341 );
3342 }
3343 else if (!name.isEmpty()) // global variable
3344 {
3345 addVariableToFile(root,mtype,scope,type,name,args,FALSE,/*nullptr,*/nullptr);
3346 }
3347
3348}
3349
3350//----------------------------------------------------------------------
3351// Searches the Entry tree for typedef documentation sections.
3352// If found they are stored in their class or in the global list.
3353static void buildTypedefList(const Entry *root)
3354{
3355 //printf("buildVarList(%s)\n",qPrint(rootNav->name()));
3356 if (!root->name.isEmpty() &&
3357 root->section.isVariable() &&
3358 root->type.find("typedef ")!=-1 // its a typedef
3359 )
3360 {
3361 AUTO_TRACE();
3363 QCString scope;
3364 int index = computeQualifiedIndex(rname);
3365 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3366 // grouped members are stored with full scope
3367 {
3368 buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
3369 scope=rname.left(index);
3370 rname=rname.mid(index+2);
3371 }
3372 else
3373 {
3374 scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
3375 }
3378 MemberName *mn = Doxygen::functionNameLinkedMap->find(rname);
3379 bool found=false;
3380 if (mn) // symbol with the same name already found
3381 {
3382 for (auto &imd : *mn)
3383 {
3384 if (!imd->isTypedef())
3385 continue;
3386
3387 QCString rtype = root->type;
3388 rtype.stripPrefix("typedef ");
3389
3390 // merge the typedefs only if they're not both grouped, and both are
3391 // either part of the same class, part of the same namespace, or both
3392 // are global (i.e., neither in a class or a namespace)
3393 bool notBothGrouped = root->groups.empty() || imd->getGroupDef()==nullptr; // see example #100
3394 bool bothSameScope = (!cd && !nd) || (cd && imd->getClassDef() == cd) || (nd && imd->getNamespaceDef() == nd);
3395 //printf("imd->isTypedef()=%d imd->typeString()=%s root->type=%s\n",imd->isTypedef(),
3396 // qPrint(imd->typeString()),qPrint(root->type));
3397 if (notBothGrouped && bothSameScope && imd->typeString()==rtype)
3398 {
3399 MemberDefMutable *md = toMemberDefMutable(imd.get());
3400 if (md)
3401 {
3402 md->setDocumentation(root->doc,root->docFile,root->docLine);
3404 md->setDocsForDefinition(!root->proto);
3405 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3407 md->setRefItems(root->sli);
3408 md->addQualifiers(root->qualifiers);
3409
3410 // merge ingroup specifiers
3411 if (md->getGroupDef()==nullptr && !root->groups.empty())
3412 {
3413 addMemberToGroups(root,md);
3414 }
3415 else if (md->getGroupDef()!=nullptr && root->groups.empty())
3416 {
3417 //printf("existing member is grouped, new member not\n");
3418 }
3419 else if (md->getGroupDef()!=nullptr && !root->groups.empty())
3420 {
3421 //printf("both members are grouped\n");
3422 }
3423 found=true;
3424 break;
3425 }
3426 }
3427 }
3428 }
3429 if (found)
3430 {
3431 AUTO_TRACE_ADD("typedef '{}' already found",rname);
3432 // mark the entry as processed, as we copied everything from it elsewhere
3433 // also, otherwise, due to containing `typedef` it may later get treated
3434 // as a function typedef in filterMemberDocumentation, which is incorrect
3435 root->markAsProcessed();
3436 }
3437 else
3438 {
3439 AUTO_TRACE_ADD("new typedef '{}'",rname);
3440 addVariable(root);
3441 }
3442
3443 }
3444 for (const auto &e : root->children())
3445 if (!e->section.isEnum())
3446 buildTypedefList(e.get());
3447}
3448
3449//----------------------------------------------------------------------
3450// Searches the Entry tree for sequence documentation sections.
3451// If found they are stored in the global list.
3452static void buildSequenceList(const Entry *root)
3453{
3454 if (!root->name.isEmpty() &&
3455 root->section.isVariable() &&
3456 root->type.find("sequence<")!=-1 // it's a sequence
3457 )
3458 {
3459 AUTO_TRACE();
3460 addVariable(root);
3461 }
3462 for (const auto &e : root->children())
3463 if (!e->section.isEnum())
3464 buildSequenceList(e.get());
3465}
3466
3467//----------------------------------------------------------------------
3468// Searches the Entry tree for dictionary documentation sections.
3469// If found they are stored in the global list.
3470static void buildDictionaryList(const Entry *root)
3471{
3472 if (!root->name.isEmpty() &&
3473 root->section.isVariable() &&
3474 root->type.find("dictionary<")!=-1 // it's a dictionary
3475 )
3476 {
3477 AUTO_TRACE();
3478 addVariable(root);
3479 }
3480 for (const auto &e : root->children())
3481 if (!e->section.isEnum())
3482 buildDictionaryList(e.get());
3483}
3484
3485//----------------------------------------------------------------------
3486// Searches the Entry tree for Variable documentation sections.
3487// If found they are stored in their class or in the global list.
3488
3489static void buildVarList(const Entry *root)
3490{
3491 //printf("buildVarList(%s) section=%08x\n",qPrint(rootNav->name()),rootNav->section());
3492 int isFuncPtr=-1;
3493 if (!root->name.isEmpty() &&
3494 (root->type.isEmpty() || g_compoundKeywords.find(root->type.str())==g_compoundKeywords.end()) &&
3495 (
3496 (root->section.isVariable() && // it's a variable
3497 root->type.find("typedef ")==-1 // and not a typedef
3498 ) ||
3499 (root->section.isFunction() && // or maybe a function pointer variable
3500 (isFuncPtr=findFunctionPtr(root->type.str(),root->lang))!=-1
3501 ) ||
3502 (root->section.isFunction() && // class variable initialized by constructor
3504 )
3505 )
3506 ) // documented variable
3507 {
3508 AUTO_TRACE();
3509 addVariable(root,isFuncPtr);
3510 }
3511 for (const auto &e : root->children())
3512 if (!e->section.isEnum())
3513 buildVarList(e.get());
3514}
3515
3516//----------------------------------------------------------------------
3517// Searches the Entry tree for Interface sections (UNO IDL only).
3518// If found they are stored in their service or in the global list.
3519//
3520
3522 const Entry *root,
3523 ClassDefMutable *cd,
3524 QCString const& rname)
3525{
3526 FileDef *fd = root->fileDef();
3527 enum MemberType type = root->section.isExportedInterface() ? MemberType::Interface : MemberType::Service;
3528 QCString fileName = root->fileName;
3529 if (fileName.isEmpty() && root->tagInfo())
3530 {
3531 fileName = root->tagInfo()->tagName;
3532 }
3533 auto md = createMemberDef(
3534 fileName, root->startLine, root->startColumn, root->type, rname,
3535 "", "", root->protection, root->virt, root->isStatic, Relationship::Member,
3536 type, ArgumentList(), root->argList, root->metaData);
3537 auto mmd = toMemberDefMutable(md.get());
3538 mmd->setTagInfo(root->tagInfo());
3539 mmd->setMemberClass(cd);
3540 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3541 mmd->setDocsForDefinition(false);
3542 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3543 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3544 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3545 mmd->setMemberSpecifiers(root->spec);
3546 mmd->setVhdlSpecifiers(root->vhdlSpec);
3547 mmd->setMemberGroupId(root->mGrpId);
3548 mmd->setTypeConstraints(root->typeConstr);
3549 mmd->setLanguage(root->lang);
3550 mmd->setBodyDef(fd);
3551 mmd->setFileDef(fd);
3552 mmd->addSectionsToDefinition(root->anchors);
3553 QCString const def = root->type + " " + rname;
3554 mmd->setDefinition(def);
3556 mmd->addQualifiers(root->qualifiers);
3557
3558 AUTO_TRACE("Interface member: fileName='{}' type='{}' name='{}' mtype='{}' prot={} virt={} state={} proto={} def='{}'",
3559 fileName,root->type,rname,type,root->protection,root->virt,root->isStatic,root->proto,def);
3560
3561 // add member to the class cd
3562 cd->insertMember(md.get());
3563 // also add the member as a "base" (to get nicer diagrams)
3564 // "optional" interface/service get Protected which turns into dashed line
3565 BaseInfo base(rname,
3566 root->spec.isOptional() ? Protection::Protected : Protection::Public, Specifier::Normal);
3567 TemplateNameMap templateNames;
3568 findClassRelation(root,cd,cd,&base,templateNames,DocumentedOnly,true) ||
3569 findClassRelation(root,cd,cd,&base,templateNames,Undocumented,true);
3570 // add file to list of used files
3571 cd->insertUsedFile(fd);
3572
3573 addMemberToGroups(root,md.get());
3575 root->markAsProcessed();
3576 mmd->setRefItems(root->sli);
3577
3578 // add member to the global list of all members
3579 MemberName *mn = Doxygen::memberNameLinkedMap->add(rname);
3580 mn->push_back(std::move(md));
3581}
3582
3583static void buildInterfaceAndServiceList(const Entry *root)
3584{
3585 if (root->section.isExportedInterface() || root->section.isIncludedService())
3586 {
3587 AUTO_TRACE("Exported interface/included service: type='{}' scope='{}' name='{}' args='{}'"
3588 " relates='{}' relatesType='{}' file='{}' line={} bodyLine={} #tArgLists={}"
3589 " mGrpId={} spec={} proto={} docFile='{}'",
3590 root->type, root->parent()->name, root->name, root->args,
3591 root->relates, root->relatesType, root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(),
3592 root->mGrpId, root->spec, root->proto, root->docFile);
3593
3595
3596 if (!rname.isEmpty())
3597 {
3598 QCString scope = root->parent()->name;
3599 ClassDefMutable *cd = getClassMutable(scope);
3600 assert(cd);
3601 if (cd && ((ClassDef::Interface == cd->compoundType()) ||
3602 (ClassDef::Service == cd->compoundType()) ||
3604 {
3606 }
3607 else
3608 {
3609 assert(false); // was checked by scanner.l
3610 }
3611 }
3612 else if (rname.isEmpty())
3613 {
3614 warn(root->fileName,root->startLine,
3615 "Illegal member name found.");
3616 }
3617 }
3618 // can only have these in IDL anyway
3619 switch (root->lang)
3620 {
3621 case SrcLangExt::Unknown: // fall through (root node always is Unknown)
3622 case SrcLangExt::IDL:
3623 for (const auto &e : root->children()) buildInterfaceAndServiceList(e.get());
3624 break;
3625 default:
3626 return; // nothing to do here
3627 }
3628}
3629
3630
3631//----------------------------------------------------------------------
3632// Searches the Entry tree for Function sections.
3633// If found they are stored in their class or in the global list.
3634
3635static void addMethodToClass(const Entry *root,ClassDefMutable *cd,
3636 const QCString &rtype,const QCString &rname,const QCString &rargs,
3637 bool isFriend,
3638 Protection protection,bool stat,Specifier virt,TypeSpecifier spec,
3639 const QCString &relates
3640 )
3641{
3642 FileDef *fd=root->fileDef();
3643
3644 QCString type = rtype;
3645 QCString args = rargs;
3646
3648 name.stripPrefix("::");
3649
3651 if (isFriend) mtype=MemberType::Friend;
3652 else if (root->mtype==MethodTypes::Signal) mtype=MemberType::Signal;
3653 else if (root->mtype==MethodTypes::Slot) mtype=MemberType::Slot;
3654 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType::DCOP;
3655
3656 // strip redundant template specifier for constructors
3657 int i = -1;
3658 int j = -1;
3659 if ((fd==nullptr || fd->getLanguage()==SrcLangExt::Cpp) &&
3660 !name.startsWith("operator ") && // not operator
3661 (i=name.find('<'))!=-1 && // containing <
3662 (j=name.find('>'))!=-1 && // or >
3663 (j!=i+2 || name.at(i+1)!='=') // but not the C++20 spaceship operator <=>
3664 )
3665 {
3666 name=name.left(i);
3667 }
3668
3669 QCString fileName = root->fileName;
3670 if (fileName.isEmpty() && root->tagInfo())
3671 {
3672 fileName = root->tagInfo()->tagName;
3673 }
3674
3675 //printf("root->name='%s; args='%s' root->argList='%s'\n",
3676 // qPrint(root->name),qPrint(args),qPrint(argListToString(root->argList))
3677 // );
3678
3679 // adding class member
3680 Relationship relationship = relates.isEmpty() ? Relationship::Member :
3681 root->relatesType==RelatesType::MemberOf ? Relationship::Foreign :
3682 Relationship::Related ;
3683 auto md = createMemberDef(
3684 fileName,root->startLine,root->startColumn,
3685 type,name,args,root->exception,
3686 protection,virt,
3687 stat && root->relatesType!=RelatesType::MemberOf,
3688 relationship,
3689 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3690 root->argList, root->metaData);
3691 auto mmd = toMemberDefMutable(md.get());
3692 mmd->setTagInfo(root->tagInfo());
3693 mmd->setMemberClass(cd);
3694 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3695 mmd->setDocsForDefinition(!root->proto);
3696 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3697 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3698 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3699 mmd->setMemberSpecifiers(spec);
3700 mmd->setVhdlSpecifiers(root->vhdlSpec);
3701 mmd->setMemberGroupId(root->mGrpId);
3702 mmd->setTypeConstraints(root->typeConstr);
3703 mmd->setLanguage(root->lang);
3704 mmd->setRequiresClause(root->req);
3705 mmd->setId(root->id);
3706 mmd->setBodyDef(fd);
3707 mmd->setFileDef(fd);
3708 mmd->addSectionsToDefinition(root->anchors);
3709 QCString def;
3711 SrcLangExt lang = cd->getLanguage();
3712 QCString scopeSeparator=getLanguageSpecificSeparator(lang);
3713 if (scopeSeparator!="::")
3714 {
3715 qualScope = substitute(qualScope,"::",scopeSeparator);
3716 }
3717 if (lang==SrcLangExt::PHP)
3718 {
3719 // for PHP we use Class::method and Namespace\method
3720 scopeSeparator="::";
3721 }
3722 if (!relates.isEmpty() || isFriend || Config_getBool(HIDE_SCOPE_NAMES))
3723 {
3724 if (!type.isEmpty())
3725 {
3726 def=type+" "+name; //+optArgs;
3727 }
3728 else
3729 {
3730 def=name; //+optArgs;
3731 }
3732 }
3733 else
3734 {
3735 if (!type.isEmpty())
3736 {
3737 def=type+" "+qualScope+scopeSeparator+name; //+optArgs;
3738 }
3739 else
3740 {
3741 def=qualScope+scopeSeparator+name; //+optArgs;
3742 }
3743 }
3744 def.stripPrefix("friend ");
3745 mmd->setDefinition(def);
3747 mmd->addQualifiers(root->qualifiers);
3748
3749 AUTO_TRACE("function member: type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
3750 type, qualScope, rname, args, root->proto, def);
3751
3752 // add member to the class cd
3753 cd->insertMember(md.get());
3754 // add file to list of used files
3755 cd->insertUsedFile(fd);
3756
3757 addMemberToGroups(root,md.get());
3759 root->markAsProcessed();
3760 mmd->setRefItems(root->sli);
3761
3762 // add member to the global list of all members
3763 //printf("Adding member=%s class=%s\n",qPrint(md->name()),qPrint(cd->name()));
3765 mn->push_back(std::move(md));
3766}
3767
3768//------------------------------------------------------------------------------------------
3769
3770static void addGlobalFunction(const Entry *root,const QCString &rname,const QCString &sc)
3771{
3772 QCString scope = sc;
3773
3774 // new global function
3776 auto md = createMemberDef(
3777 root->fileName,root->startLine,root->startColumn,
3778 root->type,name,root->args,root->exception,
3779 root->protection,root->virt,root->isStatic,Relationship::Member,
3781 !root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3782 root->argList,root->metaData);
3783 auto mmd = toMemberDefMutable(md.get());
3784 mmd->setTagInfo(root->tagInfo());
3785 mmd->setLanguage(root->lang);
3786 mmd->setId(root->id);
3787 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3788 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3789 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3790 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
3791 mmd->setDocsForDefinition(!root->proto);
3792 mmd->setTypeConstraints(root->typeConstr);
3793 //md->setBody(root->body);
3794 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3795 FileDef *fd=root->fileDef();
3796 mmd->setBodyDef(fd);
3797 mmd->addSectionsToDefinition(root->anchors);
3798 mmd->setMemberSpecifiers(root->spec);
3799 mmd->setVhdlSpecifiers(root->vhdlSpec);
3800 mmd->setMemberGroupId(root->mGrpId);
3801 mmd->setRequiresClause(root->req);
3802 mmd->setExplicitExternal(root->explicitExternal,root->fileName,root->startLine,root->startColumn);
3803
3804 NamespaceDefMutable *nd = nullptr;
3805 // see if the function is inside a namespace that was not part of
3806 // the name already (in that case nd should be non-zero already)
3807 if (root->parent()->section.isNamespace())
3808 {
3809 //QCString nscope=removeAnonymousScopes(root->parent()->name);
3810 QCString nscope=root->parent()->name;
3811 if (!nscope.isEmpty())
3812 {
3813 nd = getResolvedNamespaceMutable(nscope);
3814 }
3815 }
3816 else if (root->parent()->section.isGroupDoc() && !scope.isEmpty())
3817 {
3819 }
3820
3821 if (!scope.isEmpty())
3822 {
3824 if (sep!="::")
3825 {
3826 scope = substitute(scope,"::",sep);
3827 }
3828 scope+=sep;
3829 }
3830
3831 if (Config_getBool(HIDE_SCOPE_NAMES)) scope = "";
3832 QCString def;
3833 //QCString optArgs = root->argList.empty() ? QCString() : root->args;
3834 if (!root->type.isEmpty())
3835 {
3836 def=root->type+" "+scope+name; //+optArgs;
3837 }
3838 else
3839 {
3840 def=scope+name; //+optArgs;
3841 }
3842 AUTO_TRACE("new non-member function type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
3843 root->type,scope,rname,root->args,root->proto,def);
3844 mmd->setDefinition(def);
3846 mmd->addQualifiers(root->qualifiers);
3847
3848 mmd->setRefItems(root->sli);
3849 if (nd && !nd->name().isEmpty() && nd->name().at(0)!='@')
3850 {
3851 // add member to namespace
3852 mmd->setNamespace(nd);
3853 nd->insertMember(md.get());
3854 }
3855 if (fd)
3856 {
3857 // add member to the file (we do this even if we have already
3858 // inserted it into the namespace)
3859 mmd->setFileDef(fd);
3860 fd->insertMember(md.get());
3861 }
3862
3863 addMemberToGroups(root,md.get());
3865 if (root->relatesType == RelatesType::Simple) // if this is a relatesalso command,
3866 // allow find Member to pick it up
3867 {
3868 root->markAsProcessed(); // Otherwise we have finished with this entry.
3869 }
3870
3871 // add member to the list of file members
3873 mn->push_back(std::move(md));
3874}
3875
3876//------------------------------------------------------------------------------------------
3877
3878static void buildFunctionList(const Entry *root)
3879{
3880 if (root->section.isFunction())
3881 {
3882 AUTO_TRACE("member function: type='{}' scope='{}' name='{}' args='{}' relates='{}' relatesType='{}'"
3883 " file='{}' line={} bodyLine={} #tArgLists={} mGrpId={}"
3884 " spec={} proto={} docFile='{}'",
3885 root->type, root->parent()->name, root->name, root->args, root->relates, root->relatesType,
3886 root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(), root->mGrpId,
3887 root->spec, root->proto, root->docFile);
3888
3889 bool isFriend=root->type=="friend" || root->type.find("friend ")!=-1;
3891 //printf("rname=%s\n",qPrint(rname));
3892
3893 QCString scope;
3894 int index = computeQualifiedIndex(rname);
3895 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3896 // grouped members are stored with full scope
3897 {
3898 buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
3899 scope=rname.left(index);
3900 rname=rname.mid(index+2);
3901 }
3902 else
3903 {
3904 scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
3905 }
3906 if (!rname.isEmpty() && scope.find('@')==-1)
3907 {
3908 // check if this function's parent is a class
3909 if (root->lang==SrcLangExt::CSharp)
3910 {
3911 scope=mangleCSharpGenericName(scope);
3912 }
3913 else
3914 {
3916 }
3917
3918 FileDef *rfd=root->fileDef();
3919
3920 int memIndex=rname.findRev("::");
3921
3923 if (cd && scope+"::"==rname.left(scope.length()+2)) // found A::f inside A
3924 {
3925 // strip scope from name
3926 rname=rname.right(rname.length()-root->parent()->name.length()-2);
3927 }
3928
3929 bool isMember=FALSE;
3930 if (memIndex!=-1)
3931 {
3932 int ts=rname.find('<');
3933 int te=rname.find('>');
3934 if (memIndex>0 && (ts==-1 || te==-1))
3935 {
3936 // note: the following code was replaced by inMember=TRUE to deal with a
3937 // function rname='X::foo' of class X inside a namespace also called X...
3938 // bug id 548175
3939 //nd = Doxygen::namespaceLinkedMap->find(rname.left(memIndex));
3940 //isMember = nd==nullptr;
3941 //if (nd)
3942 //{
3943 // // strip namespace scope from name
3944 // scope=rname.left(memIndex);
3945 // rname=rname.right(rname.length()-memIndex-2);
3946 //}
3947 isMember = TRUE;
3948 }
3949 else
3950 {
3951 isMember=memIndex<ts || memIndex>te;
3952 }
3953 }
3954
3955 if (!root->parent()->name.isEmpty() && root->parent()->section.isCompound() && cd)
3956 {
3957 AUTO_TRACE_ADD("member '{}' of class '{}'", rname,cd->name());
3958 addMethodToClass(root,cd,root->type,rname,root->args,isFriend,
3959 root->protection,root->isStatic,root->virt,root->spec,root->relates);
3960 }
3961 else if (root->parent()->section.isObjcImpl() && cd)
3962 {
3963 const MemberDef *md = cd->getMemberByName(rname);
3964 if (md)
3965 {
3966 MemberDefMutable *mdm = toMemberDefMutable(const_cast<MemberDef*>(md));
3967 if (mdm)
3968 {
3969 mdm->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3970 mdm->setBodyDef(root->fileDef());
3971 }
3972 }
3973 }
3974 else if (!root->parent()->section.isCompound() && !root->parent()->section.isObjcImpl() &&
3975 !isMember &&
3976 (root->relates.isEmpty() || root->relatesType==RelatesType::Duplicate) &&
3977 !root->type.startsWith("extern ") && !root->type.startsWith("typedef ")
3978 )
3979 // no member => unrelated function
3980 {
3981 /* check the uniqueness of the function name in the file.
3982 * A file could contain a function prototype and a function definition
3983 * or even multiple function prototypes.
3984 */
3985 bool found=FALSE;
3986 MemberDef *md_found=nullptr;
3987 MemberName *mn = Doxygen::functionNameLinkedMap->find(rname);
3988 if (mn)
3989 {
3990 AUTO_TRACE_ADD("function '{}' already found",rname);
3991 for (const auto &imd : *mn)
3992 {
3993 MemberDefMutable *md = toMemberDefMutable(imd.get());
3994 if (md)
3995 {
3996 const NamespaceDef *mnd = md->getNamespaceDef();
3997 NamespaceDef *rnd = nullptr;
3998 //printf("root namespace=%s\n",qPrint(rootNav->parent()->name()));
3999 QCString fullScope = scope;
4000 QCString parentScope = root->parent()->name;
4001 if (!parentScope.isEmpty() && !leftScopeMatch(parentScope,scope))
4002 {
4003 if (!scope.isEmpty()) fullScope.prepend("::");
4004 fullScope.prepend(parentScope);
4005 }
4006 //printf("fullScope=%s\n",qPrint(fullScope));
4007 rnd = getResolvedNamespace(fullScope);
4008 const FileDef *mfd = md->getFileDef();
4009 QCString nsName,rnsName;
4010 if (mnd) nsName = mnd->name();
4011 if (rnd) rnsName = rnd->name();
4012 //printf("matching arguments for %s%s %s%s\n",
4013 // qPrint(md->name()),md->argsString(),qPrint(rname),qPrint(argListToString(root->argList)));
4014 const ArgumentList &mdAl = md->argumentList();
4015 const ArgumentList &mdTempl = md->templateArguments();
4016
4017 // in case of template functions, we need to check if the
4018 // functions have the same number of template parameters
4019 bool sameTemplateArgs = TRUE;
4020 bool matchingReturnTypes = TRUE;
4021 bool sameRequiresClause = TRUE;
4022 if (!mdTempl.empty() && !root->tArgLists.empty())
4023 {
4024 sameTemplateArgs = matchTemplateArguments(mdTempl,root->tArgLists.back());
4025 if (md->typeString()!=removeRedundantWhiteSpace(root->type))
4026 {
4027 matchingReturnTypes = FALSE;
4028 }
4029 if (md->requiresClause()!=root->req)
4030 {
4031 sameRequiresClause = FALSE;
4032 }
4033 }
4034 else if (!mdTempl.empty() || !root->tArgLists.empty())
4035 { // if one has template parameters and the other doesn't then that also counts as a
4036 // difference
4037 sameTemplateArgs = FALSE;
4038 }
4039
4040 bool staticsInDifferentFiles =
4041 root->isStatic && md->isStatic() && root->fileName!=md->getDefFileName();
4042
4043 if (
4044 matchArguments2(md->getOuterScope(),mfd,&mdAl,
4045 rnd ? rnd : Doxygen::globalScope,rfd,&root->argList,
4046 FALSE,root->lang) &&
4047 sameTemplateArgs &&
4048 matchingReturnTypes &&
4049 sameRequiresClause &&
4050 !staticsInDifferentFiles
4051 )
4052 {
4053 GroupDef *gd=nullptr;
4054 if (!root->groups.empty() && !root->groups.front().groupname.isEmpty())
4055 {
4056 gd = Doxygen::groupLinkedMap->find(root->groups.front().groupname);
4057 }
4058 //printf("match!\n");
4059 //printf("mnd=%p rnd=%p nsName=%s rnsName=%s\n",mnd,rnd,qPrint(nsName),qPrint(rnsName));
4060 // see if we need to create a new member
4061 found=(mnd && rnd && nsName==rnsName) || // members are in the same namespace
4062 ((mnd==nullptr && rnd==nullptr && mfd!=nullptr && // no external reference and
4063 mfd->absFilePath()==root->fileName // prototype in the same file
4064 )
4065 );
4066 // otherwise, allow a duplicate global member with the same argument list
4067 if (!found && gd && gd==md->getGroupDef() && nsName==rnsName)
4068 {
4069 // member is already in the group, so we don't want to add it again.
4070 found=TRUE;
4071 }
4072
4073 AUTO_TRACE_ADD("combining function with prototype found={} in namespace '{}'",found,nsName);
4074
4075 if (found)
4076 {
4077 // merge argument lists
4078 ArgumentList mergedArgList = root->argList;
4079 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.isEmpty());
4080 // merge documentation
4081 if (md->documentation().isEmpty() && !root->doc.isEmpty())
4082 {
4083 if (root->proto)
4084 {
4086 }
4087 else
4088 {
4090 }
4091 }
4092
4093 md->setDocumentation(root->doc,root->docFile,root->docLine);
4095 md->setDocsForDefinition(!root->proto);
4096 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
4097 {
4098 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
4099 md->setBodyDef(rfd);
4100 }
4101
4102 if (md->briefDescription().isEmpty() && !root->brief.isEmpty())
4103 {
4104 md->setArgsString(root->args);
4105 }
4106 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
4107
4109
4111 md->addQualifiers(root->qualifiers);
4112
4113 // merge ingroup specifiers
4114 if (md->getGroupDef()==nullptr && !root->groups.empty())
4115 {
4116 addMemberToGroups(root,md);
4117 }
4118 else if (md->getGroupDef()!=nullptr && root->groups.empty())
4119 {
4120 //printf("existing member is grouped, new member not\n");
4121 }
4122 else if (md->getGroupDef()!=nullptr && !root->groups.empty())
4123 {
4124 //printf("both members are grouped\n");
4125 }
4127
4128 // if md is a declaration and root is the corresponding
4129 // definition, then turn md into a definition.
4130 if (md->isPrototype() && !root->proto)
4131 {
4132 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
4133 md->setPrototype(FALSE,root->fileName,root->startLine,root->startColumn);
4134 }
4135 // if md is already the definition, then add the declaration info
4136 else if (!md->isPrototype() && root->proto)
4137 {
4138 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
4139 }
4140 }
4141 }
4142 }
4143 if (found)
4144 {
4145 md_found = md;
4146 break;
4147 }
4148 }
4149 }
4150 if (!found) /* global function is unique with respect to the file */
4151 {
4152 addGlobalFunction(root,rname,scope);
4153 }
4154 else
4155 {
4156 FileDef *fd=root->fileDef();
4157 if (fd)
4158 {
4159 // add member to the file (we do this even if we have already
4160 // inserted it into the namespace)
4161 fd->insertMember(md_found);
4162 }
4163 }
4164
4165 AUTO_TRACE_ADD("unrelated function type='{}' name='{}' args='{}'",root->type,rname,root->args);
4166 }
4167 else
4168 {
4169 AUTO_TRACE_ADD("function '{}' is not processed",rname);
4170 }
4171 }
4172 else if (rname.isEmpty())
4173 {
4174 warn(root->fileName,root->startLine,
4175 "Illegal member name found."
4176 );
4177 }
4178 }
4179 for (const auto &e : root->children()) buildFunctionList(e.get());
4180}
4181
4182//----------------------------------------------------------------------
4183
4184static void findFriends()
4185{
4186 AUTO_TRACE();
4187 for (const auto &fn : *Doxygen::functionNameLinkedMap) // for each global function name
4188 {
4189 MemberName *mn = Doxygen::memberNameLinkedMap->find(fn->memberName());
4190 if (mn)
4191 { // there are members with the same name
4192 // for each function with that name
4193 for (const auto &ifmd : *fn)
4194 {
4195 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
4196 // for each member with that name
4197 for (const auto &immd : *mn)
4198 {
4199 MemberDefMutable *mmd = toMemberDefMutable(immd.get());
4200 //printf("Checking for matching arguments
4201 // mmd->isRelated()=%d mmd->isFriend()=%d mmd->isFunction()=%d\n",
4202 // mmd->isRelated(),mmd->isFriend(),mmd->isFunction());
4203 if (fmd && mmd &&
4204 (mmd->isFriend() || (mmd->isRelated() && mmd->isFunction())) &&
4205 matchArguments2(mmd->getOuterScope(), mmd->getFileDef(), &mmd->argumentList(),
4206 fmd->getOuterScope(), fmd->getFileDef(), &fmd->argumentList(),
4207 TRUE,mmd->getLanguage()
4208 )
4209
4210 ) // if the member is related and the arguments match then the
4211 // function is actually a friend.
4212 {
4213 AUTO_TRACE_ADD("Merging related global and member '{}' isFriend={} isRelated={} isFunction={}",
4214 mmd->name(),mmd->isFriend(),mmd->isRelated(),mmd->isFunction());
4215 const ArgumentList &mmdAl = mmd->argumentList();
4216 const ArgumentList &fmdAl = fmd->argumentList();
4217 mergeArguments(const_cast<ArgumentList&>(fmdAl),const_cast<ArgumentList&>(mmdAl));
4218
4219 // reset argument lists to add missing default parameters
4220 QCString mmdAlStr = argListToString(mmdAl);
4221 QCString fmdAlStr = argListToString(fmdAl);
4222 mmd->setArgsString(mmdAlStr);
4223 fmd->setArgsString(fmdAlStr);
4224 mmd->moveDeclArgumentList(std::make_unique<ArgumentList>(mmdAl));
4225 fmd->moveDeclArgumentList(std::make_unique<ArgumentList>(fmdAl));
4226 AUTO_TRACE_ADD("friend args='{}' member args='{}'",argListToString(fmd->argumentList()),argListToString(mmd->argumentList()));
4227
4228 if (!fmd->documentation().isEmpty())
4229 {
4230 mmd->setDocumentation(fmd->documentation(),fmd->docFile(),fmd->docLine());
4231 }
4232 else if (!mmd->documentation().isEmpty())
4233 {
4234 fmd->setDocumentation(mmd->documentation(),mmd->docFile(),mmd->docLine());
4235 }
4236 if (mmd->briefDescription().isEmpty() && !fmd->briefDescription().isEmpty())
4237 {
4238 mmd->setBriefDescription(fmd->briefDescription(),fmd->briefFile(),fmd->briefLine());
4239 }
4240 else if (!mmd->briefDescription().isEmpty() && !fmd->briefDescription().isEmpty())
4241 {
4242 fmd->setBriefDescription(mmd->briefDescription(),mmd->briefFile(),mmd->briefLine());
4243 }
4244 if (!fmd->inbodyDocumentation().isEmpty())
4245 {
4247 }
4248 else if (!mmd->inbodyDocumentation().isEmpty())
4249 {
4251 }
4252 //printf("body mmd %d fmd %d\n",mmd->getStartBodyLine(),fmd->getStartBodyLine());
4253 if (mmd->getStartBodyLine()==-1 && fmd->getStartBodyLine()!=-1)
4254 {
4255 mmd->setBodySegment(fmd->getDefLine(),fmd->getStartBodyLine(),fmd->getEndBodyLine());
4256 mmd->setBodyDef(fmd->getBodyDef());
4257 //mmd->setBodyMember(fmd);
4258 }
4259 else if (mmd->getStartBodyLine()!=-1 && fmd->getStartBodyLine()==-1)
4260 {
4261 fmd->setBodySegment(mmd->getDefLine(),mmd->getStartBodyLine(),mmd->getEndBodyLine());
4262 fmd->setBodyDef(mmd->getBodyDef());
4263 //fmd->setBodyMember(mmd);
4264 }
4266
4268
4269 mmd->addQualifiers(fmd->getQualifiers());
4270 fmd->addQualifiers(mmd->getQualifiers());
4271
4272 }
4273 }
4274 }
4275 }
4276 }
4277}
4278
4279//----------------------------------------------------------------------
4280
4282{
4283 AUTO_TRACE();
4284
4285 // find matching function declaration and definitions.
4286 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4287 {
4288 //printf("memberName=%s count=%zu\n",qPrint(mn->memberName()),mn->size());
4289 /* find a matching function declaration and definition for this function */
4290 for (const auto &imdec : *mn)
4291 {
4292 MemberDefMutable *mdec = toMemberDefMutable(imdec.get());
4293 if (mdec &&
4294 (mdec->isPrototype() ||
4295 (mdec->isVariable() && mdec->isExternal())
4296 ))
4297 {
4298 for (const auto &imdef : *mn)
4299 {
4300 MemberDefMutable *mdef = toMemberDefMutable(imdef.get());
4301 if (mdef && mdec!=mdef &&
4302 mdec->getNamespaceDef()==mdef->getNamespaceDef())
4303 {
4305 }
4306 }
4307 }
4308 }
4309 }
4310}
4311
4312//----------------------------------------------------------------------
4313
4315{
4316 AUTO_TRACE();
4317 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4318 {
4319 MemberDefMutable *mdef=nullptr,*mdec=nullptr;
4320 /* find a matching function declaration and definition for this function */
4321 for (const auto &imd : *mn)
4322 {
4323 MemberDefMutable *md = toMemberDefMutable(imd.get());
4324 if (md)
4325 {
4326 if (md->isPrototype())
4327 mdec=md;
4328 else if (md->isVariable() && md->isExternal())
4329 mdec=md;
4330
4331 if (md->isFunction() && !md->isStatic() && !md->isPrototype())
4332 mdef=md;
4333 else if (md->isVariable() && !md->isExternal() && !md->isStatic())
4334 mdef=md;
4335 }
4336
4337 if (mdef && mdec) break;
4338 }
4339 if (mdef && mdec)
4340 {
4341 const ArgumentList &mdefAl = mdef->argumentList();
4342 const ArgumentList &mdecAl = mdec->argumentList();
4343 if (
4344 matchArguments2(mdef->getOuterScope(),mdef->getFileDef(),const_cast<ArgumentList*>(&mdefAl),
4345 mdec->getOuterScope(),mdec->getFileDef(),const_cast<ArgumentList*>(&mdecAl),
4346 TRUE,mdef->getLanguage()
4347 )
4348 ) /* match found */
4349 {
4350 AUTO_TRACE_ADD("merging references for mdec={} mdef={}",mdec->name(),mdef->name());
4351 mdef->mergeReferences(mdec);
4352 mdec->mergeReferences(mdef);
4353 mdef->mergeReferencedBy(mdec);
4354 mdec->mergeReferencedBy(mdef);
4355 }
4356 }
4357 }
4358}
4359
4360//----------------------------------------------------------------------
4361
4363{
4364 AUTO_TRACE();
4365 // find match between function declaration and definition for
4366 // related functions
4367 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4368 {
4369 /* find a matching function declaration and definition for this function */
4370 // for each global function
4371 for (const auto &imd : *mn)
4372 {
4373 MemberDefMutable *md = toMemberDefMutable(imd.get());
4374 if (md)
4375 {
4376 //printf(" Function '%s'\n",qPrint(md->name()));
4377 MemberName *rmn = Doxygen::memberNameLinkedMap->find(md->name());
4378 if (rmn) // check if there is a member with the same name
4379 {
4380 //printf(" Member name found\n");
4381 // for each member with the same name
4382 for (const auto &irmd : *rmn)
4383 {
4384 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
4385 //printf(" Member found: related='%d'\n",rmd->isRelated());
4386 if (rmd &&
4387 (rmd->isRelated() || rmd->isForeign()) && // related function
4389 rmd->getOuterScope(),rmd->getFileDef(),&rmd->argumentList(),
4390 TRUE,md->getLanguage()
4391 )
4392 )
4393 {
4394 AUTO_TRACE_ADD("Found related member '{}'",md->name());
4395 if (rmd->relatedAlso())
4396 md->setRelatedAlso(rmd->relatedAlso());
4397 else if (rmd->isForeign())
4398 md->makeForeign();
4399 else
4400 md->makeRelated();
4401 }
4402 }
4403 }
4404 }
4405 }
4406 }
4407}
4408
4409//----------------------------------------------------------------------
4410
4412{
4413 AUTO_TRACE();
4414 for (const auto &[qualifiedName,bodyInfo] : Doxygen::staticInitMap)
4415 {
4416 size_t i=qualifiedName.rfind("::");
4417 if (i!=std::string::npos)
4418 {
4419 QCString scope = qualifiedName.substr(0,i);
4420 QCString name = qualifiedName.substr(i+2);
4421 MemberName *mn = Doxygen::memberNameLinkedMap->find(name);
4422 if (mn)
4423 {
4424 for (const auto &imd : *mn)
4425 {
4426 MemberDefMutable *md = toMemberDefMutable(imd.get());
4427 if (md && md->qualifiedName().str()==qualifiedName && md->isVariable())
4428 {
4429 AUTO_TRACE_ADD("found static member {} body [{}..{}]\n",
4430 md->qualifiedName(),bodyInfo.startLine,bodyInfo.endLine);
4431 md->setBodySegment(bodyInfo.defLine,
4432 bodyInfo.startLine,
4433 bodyInfo.endLine);
4434 }
4435 }
4436 }
4437 }
4438 }
4439}
4440
4441//----------------------------------------------------------------------
4442
4443/*! make a dictionary of all template arguments of class cd
4444 * that are part of the base class name.
4445 * Example: A template class A with template arguments <R,S,T>
4446 * that inherits from B<T,T,S> will have T and S in the dictionary.
4447 */
4448static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments,const std::string &name)
4449{
4450 std::map<std::string,int> templateNames;
4451 int count=0;
4452 for (const Argument &arg : templateArguments)
4453 {
4454 static const reg::Ex re(R"(\a[\w:]*)");
4455 reg::Iterator it(name,re);
4457 for (; it!=end ; ++it)
4458 {
4459 const auto &match = *it;
4460 std::string n = match.str();
4461 if (n==arg.name.str())
4462 {
4463 if (templateNames.find(n)==templateNames.end())
4464 {
4465 templateNames.emplace(n,count);
4466 }
4467 }
4468 }
4469 }
4470 return templateNames;
4471}
4472
4473/*! Searches a class from within \a context and \a cd and returns its
4474 * definition if found (otherwise nullptr is returned).
4475 */
4477{
4478 ClassDef *result=nullptr;
4479 if (cd==nullptr)
4480 {
4481 return result;
4482 }
4483 FileDef *fd=cd->getFileDef();
4484 SymbolResolver resolver(fd);
4485 if (context && cd!=context)
4486 {
4487 result = const_cast<ClassDef*>(resolver.resolveClass(context,name,true,true));
4488 }
4489 //printf("1. result=%p\n",result);
4490 if (result==nullptr)
4491 {
4492 result = const_cast<ClassDef*>(resolver.resolveClass(cd,name,true,true));
4493 }
4494 //printf("2. result=%p\n",result);
4495 if (result==nullptr) // try direct class, needed for namespaced classes imported via tag files (see bug624095)
4496 {
4497 result = getClass(name);
4498 }
4499 //printf("3. result=%p\n",result);
4500 //printf("** Trying to find %s within context %s class %s result=%s lookup=%p\n",
4501 // qPrint(name),
4502 // context ? qPrint(context->name()) : "<none>",
4503 // cd ? qPrint(cd->name()) : "<none>",
4504 // result ? qPrint(result->name()) : "<none>",
4505 // Doxygen::classLinkedMap->find(name)
4506 // );
4507 return result;
4508}
4509
4510
4511static void findUsedClassesForClass(const Entry *root,
4512 Definition *context,
4513 ClassDefMutable *masterCd,
4514 ClassDefMutable *instanceCd,
4515 bool isArtificial,
4516 const ArgumentList *actualArgs = nullptr,
4517 const TemplateNameMap &templateNames = TemplateNameMap()
4518 )
4519{
4520 AUTO_TRACE();
4521 const ArgumentList &formalArgs = masterCd->templateArguments();
4522 for (auto &mni : masterCd->memberNameInfoLinkedMap())
4523 {
4524 for (auto &mi : *mni)
4525 {
4526 const MemberDef *md=mi->memberDef();
4527 if (md->isVariable() || md->isObjCProperty()) // for each member variable in this class
4528 {
4529 AUTO_TRACE_ADD("Found variable '{}' in class '{}'",md->name(),masterCd->name());
4530 QCString type = normalizeNonTemplateArgumentsInString(md->typeString(),masterCd,formalArgs);
4531 QCString typedefValue = md->getLanguage()==SrcLangExt::Java ? type : resolveTypeDef(masterCd,type);
4532 if (!typedefValue.isEmpty())
4533 {
4534 type = typedefValue;
4535 }
4536 int pos=0;
4537 QCString usedClassName;
4538 QCString templSpec;
4539 bool found=FALSE;
4540 // the type can contain template variables, replace them if present
4541 type = substituteTemplateArgumentsInString(type,formalArgs,actualArgs);
4542
4543 //printf(" template substitution gives=%s\n",qPrint(type));
4544 while (!found && extractClassNameFromType(type,pos,usedClassName,templSpec,root->lang)!=-1)
4545 {
4546 // find the type (if any) that matches usedClassName
4547 SymbolResolver resolver(masterCd->getFileDef());
4548 const ClassDefMutable *typeCd = resolver.resolveClassMutable(masterCd,usedClassName,false,true);
4549 //printf("====> usedClassName=%s -> typeCd=%s\n",
4550 // qPrint(usedClassName),typeCd?qPrint(typeCd->name()):"<none>");
4551 if (typeCd)
4552 {
4553 usedClassName = typeCd->name();
4554 }
4555
4556 int sp=usedClassName.find('<');
4557 if (sp==-1) sp=0;
4558 // replace any namespace aliases
4559 replaceNamespaceAliases(usedClassName);
4560 // add any template arguments to the class
4561 QCString usedName = removeRedundantWhiteSpace(usedClassName+templSpec);
4562 //printf(" usedName=%s usedClassName=%s templSpec=%s\n",qPrint(usedName),qPrint(usedClassName),qPrint(templSpec));
4563
4564 TemplateNameMap formTemplateNames;
4565 if (templateNames.empty())
4566 {
4567 formTemplateNames = getTemplateArgumentsInName(formalArgs,usedName.str());
4568 }
4569 BaseInfo bi(usedName,Protection::Public,Specifier::Normal);
4570 findClassRelation(root,context,instanceCd,&bi,formTemplateNames,TemplateInstances,isArtificial);
4571
4572 for (const Argument &arg : masterCd->templateArguments())
4573 {
4574 if (arg.name==usedName) // type is a template argument
4575 {
4576 ClassDef *usedCd = Doxygen::hiddenClassLinkedMap->find(usedName);
4577 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4578 if (usedCd==nullptr)
4579 {
4580 usedCdm = toClassDefMutable(
4581 Doxygen::hiddenClassLinkedMap->add(usedName,
4583 masterCd->getDefFileName(),masterCd->getDefLine(),
4584 masterCd->getDefColumn(),
4585 usedName,
4586 ClassDef::Class)));
4587 if (usedCdm)
4588 {
4589 //printf("making %s a template argument!!!\n",qPrint(usedCd->name()));
4590 usedCdm->makeTemplateArgument();
4591 usedCdm->setUsedOnly(TRUE);
4592 usedCdm->setLanguage(masterCd->getLanguage());
4593 usedCd = usedCdm;
4594 }
4595 }
4596 if (usedCd)
4597 {
4598 found=TRUE;
4599 AUTO_TRACE_ADD("case 1: adding used class '{}'", usedCd->name());
4600 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4601 if (usedCdm)
4602 {
4603 if (isArtificial) usedCdm->setArtificial(TRUE);
4604 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4605 }
4606 }
4607 }
4608 }
4609
4610 if (!found)
4611 {
4612 ClassDef *usedCd=findClassWithinClassContext(context,masterCd,usedName);
4613 //printf("Looking for used class %s: result=%s master=%s\n",
4614 // qPrint(usedName),usedCd?qPrint(usedCd->name()):"<none>",masterCd?qPrint(masterCd->name()):"<none>");
4615
4616 if (usedCd)
4617 {
4618 found=TRUE;
4619 AUTO_TRACE_ADD("case 2: adding used class '{}'", usedCd->name());
4620 instanceCd->addUsedClass(usedCd,md->name(),md->protection()); // class exists
4621 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4622 if (usedCdm)
4623 {
4624 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4625 }
4626 }
4627 }
4628 }
4629 if (!found && !type.isEmpty()) // used class is not documented in any scope
4630 {
4631 ClassDef *usedCd = Doxygen::hiddenClassLinkedMap->find(type);
4632 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4633 if (usedCd==nullptr && !Config_getBool(HIDE_UNDOC_RELATIONS))
4634 {
4635 if (type.endsWith("(*") || type.endsWith("(^")) // type is a function pointer
4636 {
4637 type+=md->argsString();
4638 }
4639 AUTO_TRACE_ADD("New undocumented used class '{}'", type);
4640 usedCdm = toClassDefMutable(
4643 masterCd->getDefFileName(),masterCd->getDefLine(),
4644 masterCd->getDefColumn(),
4645 type,ClassDef::Class)));
4646 if (usedCdm)
4647 {
4648 usedCdm->setUsedOnly(TRUE);
4649 usedCdm->setLanguage(masterCd->getLanguage());
4650 usedCd = usedCdm;
4651 }
4652 }
4653 if (usedCd)
4654 {
4655 AUTO_TRACE_ADD("case 3: adding used class '{}'", usedCd->name());
4656 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4657 if (usedCdm)
4658 {
4659 if (isArtificial) usedCdm->setArtificial(TRUE);
4660 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4661 }
4662 }
4663 }
4664 }
4665 }
4666 }
4667}
4668
4670 const Entry *root,
4671 Definition *context,
4672 ClassDefMutable *masterCd,
4673 ClassDefMutable *instanceCd,
4675 bool isArtificial,
4676 const ArgumentList *actualArgs = nullptr,
4677 const TemplateNameMap &templateNames=TemplateNameMap()
4678 )
4679{
4680 AUTO_TRACE("name={}",root->name);
4681 // The base class could ofcouse also be a non-nested class
4682 const ArgumentList &formalArgs = masterCd->templateArguments();
4683 for (const BaseInfo &bi : root->extends)
4684 {
4685 //printf("masterCd=%s bi.name='%s' #actualArgs=%d\n",
4686 // qPrint(masterCd->localName()),qPrint(bi.name),actualArgs ? (int)actualArgs->size() : -1);
4687 TemplateNameMap formTemplateNames;
4688 if (templateNames.empty())
4689 {
4690 formTemplateNames = getTemplateArgumentsInName(formalArgs,bi.name.str());
4691 }
4692 BaseInfo tbi = bi;
4693 tbi.name = substituteTemplateArgumentsInString(bi.name,formalArgs,actualArgs);
4694 //printf("masterCd=%p instanceCd=%p bi->name=%s tbi.name=%s\n",(void*)masterCd,(void*)instanceCd,qPrint(bi.name),qPrint(tbi.name));
4695
4696 if (mode==DocumentedOnly)
4697 {
4698 // find a documented base class in the correct scope
4699 if (!findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,DocumentedOnly,isArtificial))
4700 {
4701 // 1.8.2: decided to show inheritance relations even if not documented,
4702 // we do make them artificial, so they do not appear in the index
4703 //if (!Config_getBool(HIDE_UNDOC_RELATIONS))
4704 bool b = Config_getBool(HIDE_UNDOC_RELATIONS) ? TRUE : isArtificial;
4705 //{
4706 // no documented base class -> try to find an undocumented one
4707 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,Undocumented,b);
4708 //}
4709 }
4710 }
4711 else if (mode==TemplateInstances)
4712 {
4713 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,TemplateInstances,isArtificial);
4714 }
4715 }
4716}
4717
4718//----------------------------------------------------------------------
4719
4720static void findTemplateInstanceRelation(const Entry *root,
4721 Definition *context,
4722 ClassDefMutable *templateClass,const QCString &templSpec,
4723 const TemplateNameMap &templateNames,
4724 bool isArtificial)
4725{
4726 AUTO_TRACE("Derived from template '{}' with parameters '{}' isArtificial={}",
4727 templateClass->name(),templSpec,isArtificial);
4728
4729 QCString tempArgsStr = tempArgListToString(templateClass->templateArguments(),root->lang,false);
4730 bool existingClass = templSpec==tempArgsStr;
4731 if (existingClass) return; // avoid recursion
4732
4733 bool freshInstance=FALSE;
4734 ClassDefMutable *instanceClass = toClassDefMutable(
4735 templateClass->insertTemplateInstance(
4736 root->fileName,root->startLine,root->startColumn,templSpec,freshInstance));
4737 if (instanceClass)
4738 {
4739 if (freshInstance)
4740 {
4741 instanceClass->setArtificial(TRUE);
4742 instanceClass->setLanguage(root->lang);
4743
4744 AUTO_TRACE_ADD("found fresh instance '{}'",instanceClass->name());
4745 instanceClass->setTemplateBaseClassNames(templateNames);
4746
4747 // search for new template instances caused by base classes of
4748 // instanceClass
4749 auto it_pair = g_classEntries.equal_range(templateClass->name().str());
4750 for (auto it=it_pair.first ; it!=it_pair.second ; ++it)
4751 {
4752 const Entry *templateRoot = it->second;
4753 AUTO_TRACE_ADD("template root found '{}' templSpec='{}'",templateRoot->name,templSpec);
4754 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(root->lang,templSpec);
4755 findBaseClassesForClass(templateRoot,context,templateClass,instanceClass,
4756 TemplateInstances,isArtificial,templArgs.get(),templateNames);
4757
4758 findUsedClassesForClass(templateRoot,context,templateClass,instanceClass,
4759 isArtificial,templArgs.get(),templateNames);
4760 }
4761 }
4762 else
4763 {
4764 AUTO_TRACE_ADD("instance already exists");
4765 }
4766 }
4767}
4768
4769//----------------------------------------------------------------------
4770
4771static void resolveTemplateInstanceInType(const Entry *root,const Definition *scope,const MemberDef *md)
4772{
4773 // For a statement like 'using X = T<A>', add a template instance 'T<A>' as a symbol, so it can
4774 // be used to match arguments (see issue #11111)
4775 AUTO_TRACE();
4776 QCString ttype = md->typeString();
4777 ttype.stripPrefix("typedef ");
4778 int ti=ttype.find('<');
4779 if (ti!=-1)
4780 {
4781 QCString templateClassName = ttype.left(ti);
4782 SymbolResolver resolver(root->fileDef());
4783 ClassDefMutable *baseClass = resolver.resolveClassMutable(scope ? scope : Doxygen::globalScope,
4784 templateClassName, true, true);
4785 AUTO_TRACE_ADD("templateClassName={} baseClass={}",templateClassName,baseClass?baseClass->name():"<none>");
4786 if (baseClass)
4787 {
4788 const ArgumentList &tl = baseClass->templateArguments();
4789 TemplateNameMap templateNames = getTemplateArgumentsInName(tl,templateClassName.str());
4791 baseClass,
4792 ttype.mid(ti),
4793 templateNames,
4794 baseClass->isArtificial());
4795 }
4796 }
4797}
4798
4799//----------------------------------------------------------------------
4800
4801static bool isRecursiveBaseClass(const QCString &scope,const QCString &name)
4802{
4803 QCString n=name;
4804 int index=n.find('<');
4805 if (index!=-1)
4806 {
4807 n=n.left(index);
4808 }
4809 bool result = rightScopeMatch(scope,n);
4810 return result;
4811}
4812
4814{
4815 if (name.isEmpty()) return 0;
4816 int l = static_cast<int>(name.length());
4817 if (name[l-1]=='>') // search backward to find the matching <, allowing nested <...> and strings.
4818 {
4819 int count=1;
4820 int i=l-2;
4821 char insideQuote=0;
4822 while (count>0 && i>=0)
4823 {
4824 char c = name[i--];
4825 switch (c)
4826 {
4827 case '>': if (!insideQuote) count++; break;
4828 case '<': if (!insideQuote) count--; break;
4829 case '\'': if (!insideQuote) insideQuote=c;
4830 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
4831 break;
4832 case '"': if (!insideQuote) insideQuote=c;
4833 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
4834 break;
4835 default: break;
4836 }
4837 }
4838 if (i>=0) l=i+1;
4839 }
4840 return l;
4841}
4842
4844 const Entry *root,
4845 Definition *context,
4846 ClassDefMutable *cd,
4847 const BaseInfo *bi,
4848 const TemplateNameMap &templateNames,
4850 bool isArtificial
4851 )
4852{
4853 AUTO_TRACE("name={} base={} isArtificial={} mode={}",cd->name(),bi->name,isArtificial,(int)mode);
4854
4855 QCString biName=bi->name;
4856 bool explicitGlobalScope=FALSE;
4857 if (biName.startsWith("::")) // explicit global scope
4858 {
4859 biName=biName.right(biName.length()-2);
4860 explicitGlobalScope=TRUE;
4861 }
4862
4863 Entry *parentNode=root->parent();
4864 bool lastParent=FALSE;
4865 do // for each parent scope, starting with the largest scope
4866 // (in case of nested classes)
4867 {
4868 QCString scopeName= parentNode ? parentNode->name : QCString();
4869 int scopeOffset=explicitGlobalScope ? 0 : static_cast<int>(scopeName.length());
4870 do // try all parent scope prefixes, starting with the largest scope
4871 {
4872 //printf("scopePrefix='%s' biName='%s'\n",
4873 // qPrint(scopeName.left(scopeOffset)),qPrint(biName));
4874
4875 QCString baseClassName=biName;
4876 if (scopeOffset>0)
4877 {
4878 baseClassName.prepend(scopeName.left(scopeOffset)+"::");
4879 }
4880 if (root->lang==SrcLangExt::CSharp)
4881 {
4882 baseClassName = mangleCSharpGenericName(baseClassName);
4883 }
4884 AUTO_TRACE_ADD("cd='{}' baseClassName='{}'",cd->name(),baseClassName);
4885 SymbolResolver resolver(cd->getFileDef());
4886 ClassDefMutable *baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
4887 baseClassName,
4888 mode==Undocumented,
4889 true
4890 );
4891 const MemberDef *baseClassTypeDef = resolver.getTypedef();
4892 QCString templSpec = resolver.getTemplateSpec();
4893 //printf("baseClassName=%s baseClass=%p cd=%p explicitGlobalScope=%d\n",
4894 // qPrint(baseClassName),baseClass,cd,explicitGlobalScope);
4895 //printf(" scope='%s' baseClassName='%s' baseClass=%s templSpec=%s\n",
4896 // cd ? qPrint(cd->name()):"<none>",
4897 // qPrint(baseClassName),
4898 // baseClass?qPrint(baseClass->name()):"<none>",
4899 // qPrint(templSpec)
4900 // );
4901 //if (baseClassName.left(root->name.length())!=root->name ||
4902 // baseClassName.at(root->name.length())!='<'
4903 // ) // Check for base class with the same name.
4904 // // If found then look in the outer scope for a match
4905 // // and prevent recursion.
4906 if (!isRecursiveBaseClass(root->name,baseClassName)
4907 || explicitGlobalScope
4908 // sadly isRecursiveBaseClass always true for UNO IDL ifc/svc members
4909 // (i.e. this is needed for addInterfaceOrServiceToServiceOrSingleton)
4910 || (root->lang==SrcLangExt::IDL &&
4911 (root->section.isExportedInterface() ||
4912 root->section.isIncludedService()))
4913 )
4914 {
4915 AUTO_TRACE_ADD("class relation '{}' inherited/used by '{}' found prot={} virt={} templSpec='{}'",
4916 baseClassName, root->name, bi->prot, bi->virt, templSpec);
4917
4918 int i=findTemplateSpecializationPosition(baseClassName);
4919 int si=baseClassName.findRev("::",i);
4920 if (si==-1) si=0;
4921 if (baseClass==nullptr && static_cast<size_t>(i)!=baseClassName.length())
4922 // base class has template specifiers
4923 {
4924 // TODO: here we should try to find the correct template specialization
4925 // but for now, we only look for the unspecialized base class.
4926 int e=findEndOfTemplate(baseClassName,i+1);
4927 //printf("baseClass==0 i=%d e=%d\n",i,e);
4928 if (e!=-1) // end of template was found at e
4929 {
4930 templSpec = removeRedundantWhiteSpace(baseClassName.mid(i,e-i));
4931 baseClassName = baseClassName.left(i)+baseClassName.right(baseClassName.length()-e);
4932 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
4933 baseClassName,
4934 mode==Undocumented,
4935 true
4936 );
4937 baseClassTypeDef = resolver.getTypedef();
4938 //printf("baseClass=%p -> baseClass=%s templSpec=%s\n",
4939 // baseClass,qPrint(baseClassName),qPrint(templSpec));
4940 }
4941 }
4942 else if (baseClass && !templSpec.isEmpty()) // we have a known class, but also
4943 // know it is a template, so see if
4944 // we can also link to the explicit
4945 // instance (for instance if a class
4946 // derived from a template argument)
4947 {
4948 //printf("baseClass=%s templSpec=%s\n",qPrint(baseClass->name()),qPrint(templSpec));
4949 ClassDefMutable *templClass=getClassMutable(baseClass->name()+templSpec);
4950 if (templClass)
4951 {
4952 // use the template instance instead of the template base.
4953 baseClass = templClass;
4954 templSpec.clear();
4955 }
4956 }
4957
4958 //printf("cd=%p baseClass=%p\n",cd,baseClass);
4959 bool found=baseClass!=nullptr && (baseClass!=cd || mode==TemplateInstances);
4960 AUTO_TRACE_ADD("1. found={}",found);
4961 if (!found && si!=-1)
4962 {
4963 // replace any namespace aliases
4964 replaceNamespaceAliases(baseClassName);
4965 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
4966 baseClassName,
4967 mode==Undocumented,
4968 true
4969 );
4970 baseClassTypeDef = resolver.getTypedef();
4971 found=baseClass!=nullptr && baseClass!=cd;
4972 if (found) templSpec = resolver.getTemplateSpec();
4973 }
4974 AUTO_TRACE_ADD("2. found={}",found);
4975
4976 if (!found)
4977 {
4978 baseClass=toClassDefMutable(findClassWithinClassContext(context,cd,baseClassName));
4979 //printf("findClassWithinClassContext(%s,%s)=%p\n",
4980 // qPrint(cd->name()),qPrint(baseClassName),baseClass);
4981 found = baseClass!=nullptr && baseClass!=cd;
4982
4983 }
4984 AUTO_TRACE_ADD("3. found={}",found);
4985 if (!found)
4986 {
4987 // for PHP the "use A\B as C" construct map class C to A::B, so we lookup
4988 // the class name also in the alias mapping.
4989 auto it = Doxygen::namespaceAliasMap.find(baseClassName.str());
4990 if (it!=Doxygen::namespaceAliasMap.end()) // see if it is indeed a class.
4991 {
4992 baseClass=getClassMutable(it->second.alias);
4993 found = baseClass!=nullptr && baseClass!=cd;
4994 }
4995 }
4996 bool isATemplateArgument = templateNames.find(biName.str())!=templateNames.end();
4997
4998 AUTO_TRACE_ADD("4. found={}",found);
4999 if (found)
5000 {
5001 AUTO_TRACE_ADD("Documented base class '{}' templSpec='{}'",biName,templSpec);
5002 // add base class to this class
5003
5004 // if templSpec is not empty then we should "instantiate"
5005 // the template baseClass. A new ClassDef should be created
5006 // to represent the instance. To be able to add the (instantiated)
5007 // members and documentation of a template class
5008 // (inserted in that template class at a later stage),
5009 // the template should know about its instances.
5010 // the instantiation process, should be done in a recursive way,
5011 // since instantiating a template may introduce new inheritance
5012 // relations.
5013 if (!templSpec.isEmpty() && mode==TemplateInstances)
5014 {
5015 // if baseClass is actually a typedef then we should not
5016 // instantiate it, since typedefs are in a different namespace
5017 // see bug531637 for an example where this would otherwise hang
5018 // Doxygen
5019 if (baseClassTypeDef==nullptr)
5020 {
5021 //printf(" => findTemplateInstanceRelation: %s\n",qPrint(baseClass->name()));
5022 findTemplateInstanceRelation(root,context,baseClass,templSpec,templateNames,baseClass->isArtificial());
5023 }
5024 }
5025 else if (mode==DocumentedOnly || mode==Undocumented)
5026 {
5027 //printf(" => insert base class\n");
5028 QCString usedName;
5029 if (baseClassTypeDef)
5030 {
5031 usedName=biName;
5032 //printf("***** usedName=%s templSpec=%s\n",qPrint(usedName),qPrint(templSpec));
5033 }
5034 Protection prot = bi->prot;
5035 if (Config_getBool(SIP_SUPPORT)) prot=Protection::Public;
5036 if (cd!=baseClass && !cd->isSubClass(baseClass) && baseClass->isBaseClass(cd,true,templSpec)==0) // check for recursion, see bug690787
5037 {
5038 AUTO_TRACE_ADD("insertBaseClass name={} prot={} virt={} templSpec={}",usedName,prot,bi->virt,templSpec);
5039 cd->insertBaseClass(baseClass,usedName,prot,bi->virt,templSpec);
5040 // add this class as super class to the base class
5041 baseClass->insertSubClass(cd,prot,bi->virt,templSpec);
5042 }
5043 else
5044 {
5045 warn(root->fileName,root->startLine,
5046 "Detected potential recursive class relation "
5047 "between class {} and base class {}!",
5048 cd->name(),baseClass->name()
5049 );
5050 }
5051 }
5052 return TRUE;
5053 }
5054 else if (mode==Undocumented && (scopeOffset==0 || isATemplateArgument))
5055 {
5056 AUTO_TRACE_ADD("New undocumented base class '{}' baseClassName='{}' templSpec='{}' isArtificial={}",
5057 biName,baseClassName,templSpec,isArtificial);
5058 baseClass=nullptr;
5059 if (isATemplateArgument)
5060 {
5061 baseClass = toClassDefMutable(Doxygen::hiddenClassLinkedMap->find(baseClassName));
5062 if (baseClass==nullptr) // not found (or alias)
5063 {
5064 baseClass= toClassDefMutable(
5065 Doxygen::hiddenClassLinkedMap->add(baseClassName,
5066 createClassDef(root->fileName,root->startLine,root->startColumn,
5067 baseClassName,
5068 ClassDef::Class)));
5069 if (baseClass) // really added (not alias)
5070 {
5071 if (isArtificial) baseClass->setArtificial(TRUE);
5072 baseClass->setLanguage(root->lang);
5073 }
5074 }
5075 }
5076 else
5077 {
5078 baseClass = toClassDefMutable(Doxygen::classLinkedMap->find(baseClassName));
5079 //printf("*** classDDict->find(%s)=%p biName=%s templSpec=%s\n",
5080 // qPrint(baseClassName),baseClass,qPrint(biName),qPrint(templSpec));
5081 if (baseClass==nullptr) // not found (or alias)
5082 {
5083 baseClass = toClassDefMutable(
5084 Doxygen::classLinkedMap->add(baseClassName,
5085 createClassDef(root->fileName,root->startLine,root->startColumn,
5086 baseClassName,
5087 ClassDef::Class)));
5088 if (baseClass) // really added (not alias)
5089 {
5090 if (isArtificial) baseClass->setArtificial(TRUE);
5091 baseClass->setLanguage(root->lang);
5092 si = baseClassName.findRev("::");
5093 if (si!=-1) // class is nested
5094 {
5095 Definition *sd = findScopeFromQualifiedName(Doxygen::globalScope,baseClassName.left(si),nullptr,root->tagInfo());
5096 if (sd==nullptr || sd==Doxygen::globalScope) // outer scope not found
5097 {
5098 baseClass->setArtificial(TRUE); // see bug678139
5099 }
5100 }
5101 }
5102 }
5103 }
5104 if (baseClass)
5105 {
5106 if (biName.endsWith("-p"))
5107 {
5108 biName="<"+biName.left(biName.length()-2)+">";
5109 }
5110 if (!cd->isSubClass(baseClass) && cd!=baseClass && cd->isBaseClass(baseClass,true,templSpec)==0) // check for recursion
5111 {
5112 AUTO_TRACE_ADD("insertBaseClass name={} prot={} virt={} templSpec={}",biName,bi->prot,bi->virt,templSpec);
5113 // add base class to this class
5114 cd->insertBaseClass(baseClass,biName,bi->prot,bi->virt,templSpec);
5115 // add this class as super class to the base class
5116 baseClass->insertSubClass(cd,bi->prot,bi->virt,templSpec);
5117 }
5118 // the undocumented base was found in this file
5119 baseClass->insertUsedFile(root->fileDef());
5120
5121 Definition *scope = buildScopeFromQualifiedName(baseClass->name(),root->lang,nullptr);
5122 if (scope!=baseClass)
5123 {
5124 baseClass->setOuterScope(scope);
5125 }
5126
5127 if (baseClassName.endsWith("-p"))
5128 {
5130 }
5131 return TRUE;
5132 }
5133 else
5134 {
5135 AUTO_TRACE_ADD("Base class '{}' not created (alias?)",biName);
5136 }
5137 }
5138 else
5139 {
5140 AUTO_TRACE_ADD("Base class '{}' not found",biName);
5141 }
5142 }
5143 else
5144 {
5145 if (mode!=TemplateInstances)
5146 {
5147 warn(root->fileName,root->startLine,
5148 "Detected potential recursive class relation "
5149 "between class {} and base class {}!",
5150 root->name,baseClassName
5151 );
5152 }
5153 // for mode==TemplateInstance this case is quite common and
5154 // indicates a relation between a template class and a template
5155 // instance with the same name.
5156 }
5157 if (scopeOffset==0)
5158 {
5159 scopeOffset=-1;
5160 }
5161 else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1)
5162 {
5163 scopeOffset=0;
5164 }
5165 //printf("new scopeOffset='%d'",scopeOffset);
5166 } while (scopeOffset>=0);
5167
5168 if (parentNode==nullptr)
5169 {
5170 lastParent=TRUE;
5171 }
5172 else
5173 {
5174 parentNode=parentNode->parent();
5175 }
5176 } while (lastParent);
5177
5178 return FALSE;
5179}
5180
5181//----------------------------------------------------------------------
5182// Computes the base and super classes for each class in the tree
5183
5184static bool isClassSection(const Entry *root)
5185{
5186 if ( !root->name.isEmpty() )
5187 {
5188 if (root->section.isCompound())
5189 // is it a compound (class, struct, union, interface ...)
5190 {
5191 return TRUE;
5192 }
5193 else if (root->section.isCompoundDoc())
5194 // is it a documentation block with inheritance info.
5195 {
5196 bool hasExtends = !root->extends.empty();
5197 if (hasExtends) return TRUE;
5198 }
5199 }
5200 return FALSE;
5201}
5202
5203
5204/*! Builds a dictionary of all entry nodes in the tree starting with \a root
5205 */
5206static void findClassEntries(const Entry *root)
5207{
5208 if (isClassSection(root))
5209 {
5210 g_classEntries.emplace(root->name.str(),root);
5211 }
5212 for (const auto &e : root->children()) findClassEntries(e.get());
5213}
5214
5216{
5217 // strip any anonymous scopes first
5220 int i=0;
5221 if ((root->lang==SrcLangExt::CSharp || root->lang==SrcLangExt::Java) &&
5222 (i=bName.find('<'))!=-1)
5223 {
5224 // a Java/C# generic class looks like a C++ specialization, so we need to strip the
5225 // template part before looking for matches
5226 if (root->lang==SrcLangExt::CSharp)
5227 {
5228 bName = mangleCSharpGenericName(root->name);
5229 }
5230 else
5231 {
5232 bName = bName.left(i);
5233 }
5234 }
5235 return bName;
5236}
5237
5238/*! Using the dictionary build by findClassEntries(), this
5239 * function will look for additional template specialization that
5240 * exists as inheritance relations only. These instances will be
5241 * added to the template they are derived from.
5242 */
5244{
5245 AUTO_TRACE();
5246 ClassDefSet visitedClasses;
5247 for (const auto &[name,root] : g_classEntries)
5248 {
5249 QCString bName = extractClassName(root);
5250 ClassDefMutable *cdm = getClassMutable(bName);
5251 if (cdm)
5252 {
5254 }
5255 }
5256}
5257
5259{
5260 AUTO_TRACE("root->name={} cd={}",root->name,cd->name());
5261 int i = root->name.find('<');
5262 int j = root->name.findRev('>');
5263 int k = root->name.find("::",j+1); // A<T::B> => ok, A<T>::B => nok
5264 if (i!=-1 && j!=-1 && k==-1 && root->lang!=SrcLangExt::CSharp && root->lang!=SrcLangExt::Java)
5265 {
5266 ClassDefMutable *master = getClassMutable(root->name.left(i));
5267 if (master && master!=cd && !cd->templateMaster())
5268 {
5269 AUTO_TRACE_ADD("class={} master={}",cd->name(),cd->templateMaster()?cd->templateMaster()->name():"<none>",master->name());
5270 cd->setTemplateMaster(master);
5271 master->insertExplicitTemplateInstance(cd,root->name.mid(i));
5272 }
5273 }
5274}
5275
5277{
5278 AUTO_TRACE();
5279 for (const auto &[name,root] : g_classEntries)
5280 {
5281 QCString bName = extractClassName(root);
5282 ClassDefMutable *cdm = getClassMutable(bName);
5283 if (cdm)
5284 {
5285 findUsedClassesForClass(root,cdm,cdm,cdm,TRUE);
5287 cdm->addTypeConstraints();
5288 }
5289 }
5290}
5291
5293{
5294 AUTO_TRACE();
5295 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5296 {
5297 if (!nd->hasDocumentation())
5298 {
5299 if ((guessSection(nd->getDefFileName()).isHeader() ||
5300 nd->getLanguage() == SrcLangExt::Fortran) && // Fortran doesn't have header files.
5301 !Config_getBool(HIDE_UNDOC_NAMESPACES) // undocumented namespaces are visible
5302 )
5303 {
5304 warn_undoc(nd->getDefFileName(),nd->getDefLine(), "{} {} is not documented.",
5305 nd->getLanguage() == SrcLangExt::Fortran ? "Module" : "Namespace",
5306 nd->name());
5307 }
5308 }
5309 }
5310}
5311
5313{
5314 AUTO_TRACE();
5315 for (const auto &[name,root] : g_classEntries)
5316 {
5317 QCString bName = extractClassName(root);
5318 ClassDefMutable *cd = getClassMutable(bName);
5319 if (cd)
5320 {
5322 }
5323 size_t numMembers = cd ? cd->memberNameInfoLinkedMap().size() : 0;
5324 if ((cd==nullptr || (!cd->hasDocumentation() && !cd->isReference())) && numMembers>0 && !bName.endsWith("::"))
5325 {
5326 if (!root->name.isEmpty() && root->name.find('@')==-1 && // normal name
5327 (guessSection(root->fileName).isHeader() ||
5328 Config_getBool(EXTRACT_LOCAL_CLASSES)) && // not defined in source file
5329 protectionLevelVisible(root->protection) && // hidden by protection
5330 !Config_getBool(HIDE_UNDOC_CLASSES) // undocumented class are visible
5331 )
5332 warn_undoc(root->fileName,root->startLine, "Compound {} is not documented.", root->name);
5333 }
5334 }
5335}
5336
5338{
5339 AUTO_TRACE();
5340 for (const auto &[name,root] : g_classEntries)
5341 {
5345 // strip any anonymous scopes first
5346 if (cd && !cd->getTemplateInstances().empty())
5347 {
5348 AUTO_TRACE_ADD("Template class '{}'",cd->name());
5349 for (const auto &ti : cd->getTemplateInstances()) // for each template instance
5350 {
5351 ClassDefMutable *tcd=toClassDefMutable(ti.classDef);
5352 if (tcd)
5353 {
5354 AUTO_TRACE_ADD("Template instance '{}'",tcd->name());
5355 QCString templSpec = ti.templSpec;
5356 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(tcd->getLanguage(),templSpec);
5357 for (const BaseInfo &bi : root->extends)
5358 {
5359 // check if the base class is a template argument
5360 BaseInfo tbi = bi;
5361 const ArgumentList &tl = cd->templateArguments();
5362 if (!tl.empty())
5363 {
5364 TemplateNameMap baseClassNames = tcd->getTemplateBaseClassNames();
5365 TemplateNameMap templateNames = getTemplateArgumentsInName(tl,bi.name.str());
5366 // for each template name that we inherit from we need to
5367 // substitute the formal with the actual arguments
5368 TemplateNameMap actualTemplateNames;
5369 for (const auto &tn_kv : templateNames)
5370 {
5371 size_t templIndex = tn_kv.second;
5372 Argument actArg;
5373 bool hasActArg=FALSE;
5374 if (templIndex<templArgs->size())
5375 {
5376 actArg=templArgs->at(templIndex);
5377 hasActArg=TRUE;
5378 }
5379 if (hasActArg &&
5380 baseClassNames.find(actArg.type.str())!=baseClassNames.end() &&
5381 actualTemplateNames.find(actArg.type.str())==actualTemplateNames.end()
5382 )
5383 {
5384 actualTemplateNames.emplace(actArg.type.str(),static_cast<int>(templIndex));
5385 }
5386 }
5387
5388 tbi.name = substituteTemplateArgumentsInString(bi.name,tl,templArgs.get());
5389 // find a documented base class in the correct scope
5390 if (!findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,DocumentedOnly,FALSE))
5391 {
5392 // no documented base class -> try to find an undocumented one
5393 findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,Undocumented,TRUE);
5394 }
5395 }
5396 }
5397 }
5398 }
5399 }
5400 }
5401}
5402
5403//-----------------------------------------------------------------------
5404// compute the references (anchors in HTML) for each function in the file
5405
5407{
5408 AUTO_TRACE();
5409 for (const auto &cd : *Doxygen::classLinkedMap)
5410 {
5411 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5412 if (cdm)
5413 {
5414 cdm->computeAnchors();
5415 }
5416 }
5417 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5418 {
5419 for (const auto &fd : *fn)
5420 {
5421 fd->computeAnchors();
5422 }
5423 }
5424 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5425 {
5427 if (ndm)
5428 {
5429 ndm->computeAnchors();
5430 }
5431 }
5432 for (const auto &gd : *Doxygen::groupLinkedMap)
5433 {
5434 gd->computeAnchors();
5435 }
5436}
5437
5438//----------------------------------------------------------------------
5439
5441{
5442 AUTO_TRACE();
5443 for (const auto &cd : *Doxygen::classLinkedMap)
5444 {
5445 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5446 if (cdm)
5447 {
5448 cdm->addListReferences();
5449 }
5450 }
5451
5452 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5453 {
5454 for (const auto &fd : *fn)
5455 {
5456 fd->addListReferences();
5457 }
5458 }
5459
5460 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5461 {
5463 if (ndm)
5464 {
5465 ndm->addListReferences();
5466 }
5467 }
5468
5469 for (const auto &gd : *Doxygen::groupLinkedMap)
5470 {
5471 gd->addListReferences();
5472 }
5473
5474 for (const auto &pd : *Doxygen::pageLinkedMap)
5475 {
5476 QCString name = pd->getOutputFileBase();
5477 if (pd->getGroupDef())
5478 {
5479 name = pd->getGroupDef()->getOutputFileBase();
5480 }
5481 {
5482 const RefItemVector &xrefItems = pd->xrefListItems();
5483 addRefItem(xrefItems,
5484 name,
5485 theTranslator->trPage(TRUE,TRUE),
5486 name,pd->title(),QCString(),nullptr);
5487 }
5488 }
5489
5490 for (const auto &dd : *Doxygen::dirLinkedMap)
5491 {
5492 QCString name = dd->getOutputFileBase();
5493 //if (dd->getGroupDef())
5494 //{
5495 // name = dd->getGroupDef()->getOutputFileBase();
5496 //}
5497 const RefItemVector &xrefItems = dd->xrefListItems();
5498 addRefItem(xrefItems,
5499 name,
5500 theTranslator->trDir(TRUE,TRUE),
5501 name,dd->displayName(),QCString(),nullptr);
5502 }
5503
5505}
5506
5507//----------------------------------------------------------------------
5508
5510{
5511 AUTO_TRACE();
5513 {
5514 rl->generatePage();
5515 }
5516}
5517
5518//----------------------------------------------------------------------
5519// Copy the documentation in entry 'root' to member definition 'md' and
5520// set the function declaration of the member to 'funcDecl'. If the boolean
5521// over_load is set the standard overload text is added.
5522
5523static void addMemberDocs(const Entry *root,
5524 MemberDefMutable *md, const QCString &funcDecl,
5525 const ArgumentList *al,
5526 bool over_load,
5527 TypeSpecifier spec
5528 )
5529{
5530 if (md==nullptr) return;
5531 AUTO_TRACE("scope='{}' name='{}' args='{}' funcDecl='{}' mSpec={}",
5532 root->parent()->name,md->name(),md->argsString(),funcDecl,spec);
5533 if (!root->section.isDoc()) // @fn or @var does not need to specify the complete definition, so don't overwrite it
5534 {
5535 QCString fDecl=funcDecl;
5536 // strip extern specifier
5537 fDecl.stripPrefix("extern ");
5538 md->setDefinition(fDecl);
5539 }
5541 md->addQualifiers(root->qualifiers);
5543 const NamespaceDef *nd=md->getNamespaceDef();
5544 QCString fullName;
5545 if (cd)
5546 fullName = cd->name();
5547 else if (nd)
5548 fullName = nd->name();
5549
5550 if (!fullName.isEmpty()) fullName+="::";
5551 fullName+=md->name();
5552 FileDef *rfd=root->fileDef();
5553
5554 // TODO determine scope based on root not md
5555 Definition *rscope = md->getOuterScope();
5556
5557 const ArgumentList &mdAl = md->argumentList();
5558 if (al)
5559 {
5560 ArgumentList mergedAl = *al;
5561 //printf("merging arguments (1) docs=%d\n",root->doc.isEmpty());
5562 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedAl,!root->doc.isEmpty());
5563 }
5564 else
5565 {
5566 if (
5567 matchArguments2( md->getOuterScope(), md->getFileDef(),const_cast<ArgumentList*>(&mdAl),
5568 rscope,rfd,&root->argList,
5569 TRUE, root->lang
5570 )
5571 )
5572 {
5573 //printf("merging arguments (2)\n");
5574 ArgumentList mergedArgList = root->argList;
5575 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.isEmpty());
5576 }
5577 }
5578 if (over_load) // the \overload keyword was used
5579 {
5581 if (!root->doc.isEmpty())
5582 {
5583 doc+="<p>";
5584 doc+=root->doc;
5585 }
5586 md->setDocumentation(doc,root->docFile,root->docLine);
5588 md->setDocsForDefinition(!root->proto);
5589 }
5590 else
5591 {
5592 //printf("overwrite!\n");
5593 md->setDocumentation(root->doc,root->docFile,root->docLine);
5594 md->setDocsForDefinition(!root->proto);
5595
5596 //printf("overwrite!\n");
5597 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
5598
5599 if (
5600 (md->inbodyDocumentation().isEmpty() ||
5601 !root->parent()->name.isEmpty()
5602 ) && !root->inbodyDocs.isEmpty()
5603 )
5604 {
5606 }
5607 }
5608
5609 //printf("initializer: '%s'(isEmpty=%d) '%s'(isEmpty=%d)\n",
5610 // qPrint(md->initializer()),md->initializer().isEmpty(),
5611 // qPrint(root->initializer),root->initializer.isEmpty()
5612 // );
5613 std::string rootInit = root->initializer.str();
5614 if (md->initializer().isEmpty() && !rootInit.empty())
5615 {
5616 //printf("setInitializer\n");
5617 md->setInitializer(rootInit);
5618 }
5619 if (md->requiresClause().isEmpty() && !root->req.isEmpty())
5620 {
5621 md->setRequiresClause(root->req);
5622 }
5623
5624 md->setMaxInitLines(root->initLines);
5625
5626 if (rfd)
5627 {
5628 if ((md->getStartBodyLine()==-1 && root->bodyLine!=-1)
5629 )
5630 {
5631 //printf("Setting new body segment [%d,%d]\n",root->bodyLine,root->endBodyLine);
5632 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
5633 md->setBodyDef(rfd);
5634 }
5635
5636 md->setRefItems(root->sli);
5637 }
5638
5640 md->addQualifiers(root->qualifiers);
5641
5642 md->mergeMemberSpecifiers(spec);
5644 addMemberToGroups(root,md);
5646 if (cd) cd->insertUsedFile(rfd);
5647 //printf("root->mGrpId=%d\n",root->mGrpId);
5648 if (root->mGrpId!=-1)
5649 {
5650 if (md->getMemberGroupId()!=-1)
5651 {
5652 if (md->getMemberGroupId()!=root->mGrpId)
5653 {
5654 warn(root->fileName,root->startLine,
5655 "member {} belongs to two different groups. The second one found here will be ignored.",
5656 md->name()
5657 );
5658 }
5659 }
5660 else // set group id
5661 {
5662 //printf("setMemberGroupId=%d md=%s\n",root->mGrpId,qPrint(md->name()));
5663 md->setMemberGroupId(root->mGrpId);
5664 }
5665 }
5666 md->addQualifiers(root->qualifiers);
5667}
5668
5669//----------------------------------------------------------------------
5670// find a class definition given the scope name and (optionally) a
5671// template list specifier
5672
5674 const QCString &scopeName)
5675{
5676 SymbolResolver resolver(fd);
5677 const ClassDef *tcd = resolver.resolveClass(nd,scopeName,true,true);
5678 //printf("findClassDefinition(fd=%s,ns=%s,scopeName=%s)='%s'\n",
5679 // qPrint(fd?fd->name():""),qPrint(nd?nd->name():""),
5680 // qPrint(scopeName),qPrint(tcd?tcd->name():""));
5681 return tcd;
5682}
5683
5684//----------------------------------------------------------------------------
5685// Returns TRUE, if the entry belongs to the group of the member definition,
5686// otherwise FALSE.
5687
5688static bool isEntryInGroupOfMember(const Entry *root,const MemberDef *md,bool allowNoGroup=false)
5689{
5690 const GroupDef *gd = md->getGroupDef();
5691 if (!gd)
5692 {
5693 return allowNoGroup;
5694 }
5695
5696 for (const auto &g : root->groups)
5697 {
5698 if (g.groupname == gd->name())
5699 {
5700 return true; // matching group
5701 }
5702 }
5703
5704 return false;
5705}
5706
5707//----------------------------------------------------------------------
5708// Adds the documentation contained in 'root' to a global function
5709// with name 'name' and argument list 'args' (for overloading) and
5710// function declaration 'decl' to the corresponding member definition.
5711
5712static bool findGlobalMember(const Entry *root,
5713 const QCString &namespaceName,
5714 const QCString &type,
5715 const QCString &name,
5716 const QCString &tempArg,
5717 const QCString &,
5718 const QCString &decl,
5719 TypeSpecifier /* spec */)
5720{
5721 AUTO_TRACE("namespace='{}' type='{}' name='{}' tempArg='{}' decl='{}'",namespaceName,type,name,tempArg,decl);
5722 QCString n=name;
5723 if (n.isEmpty()) return FALSE;
5724 if (n.find("::")!=-1) return FALSE; // skip undefined class members
5725 MemberName *mn=Doxygen::functionNameLinkedMap->find(n+tempArg); // look in function dictionary
5726 if (mn==nullptr)
5727 {
5728 mn=Doxygen::functionNameLinkedMap->find(n); // try without template arguments
5729 }
5730 if (mn) // function name defined
5731 {
5732 AUTO_TRACE_ADD("Found symbol name");
5733 //int count=0;
5734 bool found=FALSE;
5735 for (const auto &md : *mn)
5736 {
5737 // If the entry has groups, then restrict the search to members which are
5738 // in one of the groups of the entry. If md is not associated with a group yet,
5739 // allow this documentation entry to add the group info.
5740 if (!root->groups.empty() && !isEntryInGroupOfMember(root, md.get(), true))
5741 {
5742 continue;
5743 }
5744
5745 const NamespaceDef *nd=nullptr;
5746 if (md->isAlias() && md->getOuterScope() &&
5747 md->getOuterScope()->definitionType()==Definition::TypeNamespace)
5748 {
5749 nd = toNamespaceDef(md->getOuterScope());
5750 }
5751 else
5752 {
5753 nd = md->getNamespaceDef();
5754 }
5755
5756 // special case for strong enums
5757 int enumNamePos=0;
5758 if (nd && md->isEnumValue() && (enumNamePos=namespaceName.findRev("::"))!=-1)
5759 { // md part of a strong enum in a namespace?
5760 QCString enumName = namespaceName.mid(enumNamePos+2);
5761 if (namespaceName.left(enumNamePos)==nd->name())
5762 {
5763 MemberName *enumMn=Doxygen::functionNameLinkedMap->find(enumName);
5764 if (enumMn)
5765 {
5766 for (const auto &emd : *enumMn)
5767 {
5768 found = emd->isStrong() && md->getEnumScope()==emd.get();
5769 if (found)
5770 {
5771 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,nullptr,FALSE,root->spec);
5772 break;
5773 }
5774 }
5775 }
5776 }
5777 if (found)
5778 {
5779 break;
5780 }
5781 }
5782 else if (nd==nullptr && md->isEnumValue()) // md part of global strong enum?
5783 {
5784 MemberName *enumMn=Doxygen::functionNameLinkedMap->find(namespaceName);
5785 if (enumMn)
5786 {
5787 for (const auto &emd : *enumMn)
5788 {
5789 found = emd->isStrong() && md->getEnumScope()==emd.get();
5790 if (found)
5791 {
5792 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,nullptr,FALSE,root->spec);
5793 break;
5794 }
5795 }
5796 }
5797 }
5798
5799 const FileDef *fd=root->fileDef();
5800 //printf("File %s\n",fd ? qPrint(fd->name()) : "<none>");
5802 if (fd)
5803 {
5804 nl = fd->getUsedNamespaces();
5805 }
5806 //printf("NamespaceList %p\n",nl);
5807
5808 // search in the list of namespaces that are imported via a
5809 // using declaration
5810 bool viaUsingDirective = nd && nl.find(nd->qualifiedName())!=nullptr;
5811
5812 if ((namespaceName.isEmpty() && nd==nullptr) || // not in a namespace
5813 (nd && nd->name()==namespaceName) || // or in the same namespace
5814 viaUsingDirective // member in 'using' namespace
5815 )
5816 {
5817 AUTO_TRACE_ADD("Try to add member '{}' to scope '{}'",md->name(),namespaceName);
5818
5819 NamespaceDef *rnd = nullptr;
5820 if (!namespaceName.isEmpty()) rnd = Doxygen::namespaceLinkedMap->find(namespaceName);
5821
5822 const ArgumentList &mdAl = md.get()->argumentList();
5823 bool matching=
5824 (mdAl.empty() && root->argList.empty()) ||
5825 md->isVariable() || md->isTypedef() || /* in case of function pointers */
5826 matchArguments2(md->getOuterScope(),md.get()->getFileDef(),&mdAl,
5827 rnd ? rnd : Doxygen::globalScope,fd,&root->argList,
5828 FALSE,root->lang);
5829
5830 // for template members we need to check if the number of
5831 // template arguments is the same, otherwise we are dealing with
5832 // different functions.
5833 if (matching && !root->tArgLists.empty())
5834 {
5835 const ArgumentList &mdTempl = md->templateArguments();
5836 if (root->tArgLists.back().size()!=mdTempl.size())
5837 {
5838 matching=FALSE;
5839 }
5840 }
5841
5842 //printf("%s<->%s\n",
5843 // qPrint(argListToString(md->argumentList())),
5844 // qPrint(argListToString(root->argList)));
5845
5846 // For static members we also check if the comment block was found in
5847 // the same file. This is needed because static members with the same
5848 // name can be in different files. Thus it would be wrong to just
5849 // put the comment block at the first syntactically matching member. If
5850 // the comment block belongs to a group of the static member, then add
5851 // the documentation even if it is in a different file.
5852 if (matching && md->isStatic() &&
5853 md->getDefFileName()!=root->fileName &&
5854 mn->size()>1 &&
5855 !isEntryInGroupOfMember(root,md.get()))
5856 {
5857 matching = FALSE;
5858 }
5859
5860 // for template member we also need to check the return type and requires
5861 if (!md->templateArguments().empty() && !root->tArgLists.empty())
5862 {
5863 //printf("Comparing return types '%s'<->'%s'\n",
5864 // md->typeString(),type);
5865 if (md->templateArguments().size()!=root->tArgLists.back().size() ||
5866 md->typeString()!=type ||
5867 md->requiresClause()!=root->req)
5868 {
5869 //printf(" ---> no matching\n");
5870 matching = FALSE;
5871 }
5872 }
5873
5874 if (matching) // add docs to the member
5875 {
5876 AUTO_TRACE_ADD("Match found");
5877 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,&root->argList,FALSE,root->spec);
5878 found=TRUE;
5879 break;
5880 }
5881 }
5882 }
5883 if (!found && root->relatesType!=RelatesType::Duplicate && root->section.isFunction()) // no match
5884 {
5885 QCString fullFuncDecl=decl;
5886 if (!root->argList.empty()) fullFuncDecl+=argListToString(root->argList,TRUE);
5887 QCString warnMsg = "no matching file member found for \n"+fullFuncDecl;
5888 if (mn->size()>0)
5889 {
5890 warnMsg+="\nPossible candidates:";
5891 for (const auto &md : *mn)
5892 {
5893 warnMsg+="\n '";
5894 warnMsg+=replaceAnonymousScopes(md->declaration());
5895 warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
5896 }
5897 }
5898 warn(root->fileName,root->startLine, "{}", qPrint(warnMsg));
5899 }
5900 }
5901 else // got docs for an undefined member!
5902 {
5903 if (root->type!="friend class" &&
5904 root->type!="friend struct" &&
5905 root->type!="friend union" &&
5906 root->type!="friend" &&
5907 (!Config_getBool(TYPEDEF_HIDES_STRUCT) ||
5908 root->type.find("typedef ")==-1)
5909 )
5910 {
5911 warn(root->fileName,root->startLine,
5912 "documented symbol '{}' was not declared or defined.",qPrint(decl)
5913 );
5914 }
5915 }
5916 return TRUE;
5917}
5918
5920 const ArgumentLists &srcTempArgLists,
5921 const ArgumentLists &dstTempArgLists
5922 )
5923{
5924 auto srcIt = srcTempArgLists.begin();
5925 auto dstIt = dstTempArgLists.begin();
5926 while (srcIt!=srcTempArgLists.end() && dstIt!=dstTempArgLists.end())
5927 {
5928 if ((*srcIt).size()!=(*dstIt).size()) return TRUE;
5929 ++srcIt;
5930 ++dstIt;
5931 }
5932 return FALSE;
5933}
5934
5935static bool scopeIsTemplate(const Definition *d)
5936{
5937 bool result=FALSE;
5938 //printf("> scopeIsTemplate(%s)\n",qPrint(d?d->name():"null"));
5940 {
5941 auto cd = toClassDef(d);
5942 result = cd->templateArguments().hasParameters() || cd->templateMaster()!=nullptr ||
5944 }
5945 //printf("< scopeIsTemplate=%d\n",result);
5946 return result;
5947}
5948
5950 const ArgumentLists &srcTempArgLists,
5951 const ArgumentLists &dstTempArgLists,
5952 const std::string &src
5953 )
5954{
5955 std::string dst;
5956 static const reg::Ex re(R"(\a\w*)");
5957 reg::Iterator it(src,re);
5959 //printf("type=%s\n",qPrint(sa->type));
5960 size_t p=0;
5961 for (; it!=end ; ++it) // for each word in srcType
5962 {
5963 const auto &match = *it;
5964 size_t i = match.position();
5965 size_t l = match.length();
5966 bool found=FALSE;
5967 dst+=src.substr(p,i-p);
5968 std::string name=match.str();
5969
5970 auto srcIt = srcTempArgLists.begin();
5971 auto dstIt = dstTempArgLists.begin();
5972 while (srcIt!=srcTempArgLists.end() && !found)
5973 {
5974 const ArgumentList *tdAli = nullptr;
5975 std::vector<Argument>::const_iterator tdaIt;
5976 if (dstIt!=dstTempArgLists.end())
5977 {
5978 tdAli = &(*dstIt);
5979 tdaIt = tdAli->begin();
5980 ++dstIt;
5981 }
5982
5983 const ArgumentList &tsaLi = *srcIt;
5984 for (auto tsaIt = tsaLi.begin(); tsaIt!=tsaLi.end() && !found; ++tsaIt)
5985 {
5986 Argument tsa = *tsaIt;
5987 const Argument *tda = nullptr;
5988 if (tdAli && tdaIt!=tdAli->end())
5989 {
5990 tda = &(*tdaIt);
5991 ++tdaIt;
5992 }
5993 //if (tda) printf("tsa=%s|%s tda=%s|%s\n",
5994 // qPrint(tsa.type),qPrint(tsa.name),
5995 // qPrint(tda->type),qPrint(tda->name));
5996 if (name==tsa.name.str())
5997 {
5998 if (tda && tda->name.isEmpty())
5999 {
6000 QCString tdaName = tda->name;
6001 QCString tdaType = tda->type;
6002 int vc=0;
6003 if (tdaType.startsWith("class ")) vc=6;
6004 else if (tdaType.startsWith("typename ")) vc=9;
6005 if (vc>0) // convert type=="class T" to type=="class" name=="T"
6006 {
6007 tdaName = tdaType.mid(vc);
6008 }
6009 if (!tdaName.isEmpty())
6010 {
6011 name=tdaName.str(); // substitute
6012 found=TRUE;
6013 }
6014 }
6015 }
6016 }
6017
6018 //printf(" srcList='%s' dstList='%s faList='%s'\n",
6019 // qPrint(argListToString(srclali.current())),
6020 // qPrint(argListToString(dstlali.current())),
6021 // funcTempArgList ? qPrint(argListToString(funcTempArgList)) : "<none>");
6022 ++srcIt;
6023 }
6024 dst+=name;
6025 p=i+l;
6026 }
6027 dst+=src.substr(p);
6028 //printf(" substituteTemplatesInString(%s)=%s\n",
6029 // qPrint(src),qPrint(dst));
6030 return dst;
6031}
6032
6034 const ArgumentLists &srcTempArgLists,
6035 const ArgumentLists &dstTempArgLists,
6036 const ArgumentList &src,
6037 ArgumentList &dst
6038 )
6039{
6040 auto dstIt = dst.begin();
6041 for (const Argument &sa : src)
6042 {
6043 QCString dstType = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.type.str());
6044 QCString dstArray = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.array.str());
6045 if (dstIt == dst.end())
6046 {
6047 Argument da = sa;
6048 da.type = dstType;
6049 da.array = dstArray;
6050 dst.push_back(da);
6051 dstIt = dst.end();
6052 }
6053 else
6054 {
6055 Argument da = *dstIt;
6056 da.type = dstType;
6057 da.array = dstArray;
6058 ++dstIt;
6059 }
6060 }
6065 srcTempArgLists,dstTempArgLists,
6066 src.trailingReturnType().str()));
6067 dst.setIsDeleted(src.isDeleted());
6068 dst.setRefQualifier(src.refQualifier());
6069 dst.setNoParameters(src.noParameters());
6070 //printf("substituteTemplatesInArgList: replacing %s with %s\n",
6071 // qPrint(argListToString(src)),qPrint(argListToString(dst))
6072 // );
6073}
6074
6075//-------------------------------------------------------------------------------------------
6076
6077static void addLocalObjCMethod(const Entry *root,
6078 const QCString &scopeName,
6079 const QCString &funcType,const QCString &funcName,const QCString &funcArgs,
6080 const QCString &exceptions,const QCString &funcDecl,
6081 TypeSpecifier spec)
6082{
6083 AUTO_TRACE();
6084 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
6085 ClassDefMutable *cd=nullptr;
6086 if (Config_getBool(EXTRACT_LOCAL_METHODS) && (cd=getClassMutable(scopeName)))
6087 {
6088 AUTO_TRACE_ADD("Local objective C method '{}' scopeName='{}'",root->name,scopeName);
6089 auto md = createMemberDef(
6090 root->fileName,root->startLine,root->startColumn,
6091 funcType,funcName,funcArgs,exceptions,
6092 root->protection,root->virt,root->isStatic,Relationship::Member,
6094 auto mmd = toMemberDefMutable(md.get());
6095 mmd->setTagInfo(root->tagInfo());
6096 mmd->setLanguage(root->lang);
6097 mmd->setId(root->id);
6098 mmd->makeImplementationDetail();
6099 mmd->setMemberClass(cd);
6100 mmd->setDefinition(funcDecl);
6102 mmd->addQualifiers(root->qualifiers);
6103 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6104 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6105 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6106 mmd->setDocsForDefinition(!root->proto);
6107 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6108 mmd->addSectionsToDefinition(root->anchors);
6109 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6110 FileDef *fd=root->fileDef();
6111 mmd->setBodyDef(fd);
6112 mmd->setMemberSpecifiers(spec);
6113 mmd->setVhdlSpecifiers(root->vhdlSpec);
6114 mmd->setMemberGroupId(root->mGrpId);
6115 cd->insertMember(md.get());
6116 cd->insertUsedFile(fd);
6117 mmd->setRefItems(root->sli);
6118
6120 mn->push_back(std::move(md));
6121 }
6122 else
6123 {
6124 // local objective C method found for class without interface
6125 }
6126}
6127
6128//-------------------------------------------------------------------------------------------
6129
6130static void addMemberFunction(const Entry *root,
6131 MemberName *mn,
6132 const QCString &scopeName,
6133 const QCString &namespaceName,
6134 const QCString &className,
6135 const QCString &funcTyp,
6136 const QCString &funcName,
6137 const QCString &funcArgs,
6138 const QCString &funcTempList,
6139 const QCString &exceptions,
6140 const QCString &type,
6141 const QCString &args,
6142 bool isFriend,
6143 TypeSpecifier spec,
6144 const QCString &relates,
6145 const QCString &funcDecl,
6146 bool overloaded,
6147 bool isFunc)
6148{
6149 AUTO_TRACE();
6150 QCString funcType = funcTyp;
6151 int count=0;
6152 int noMatchCount=0;
6153 bool memFound=FALSE;
6154 for (const auto &imd : *mn)
6155 {
6156 MemberDefMutable *md = toMemberDefMutable(imd.get());
6157 if (md==nullptr) continue;
6159 if (cd==nullptr) continue;
6160 //AUTO_TRACE_ADD("member definition found, scope needed='{}' scope='{}' args='{}' fileName='{}'",
6161 // scopeName, cd->name(), md->argsString(), root->fileName);
6162 FileDef *fd=root->fileDef();
6163 NamespaceDef *nd=nullptr;
6164 if (!namespaceName.isEmpty()) nd=getResolvedNamespace(namespaceName);
6165
6166 //printf("scopeName %s->%s\n",qPrint(scopeName),
6167 // qPrint(stripTemplateSpecifiersFromScope(scopeName,FALSE)));
6168
6169 // if the member we are searching for is an enum value that is part of
6170 // a "strong" enum, we need to look into the fields of the enum for a match
6171 int enumNamePos=0;
6172 if (md->isEnumValue() && (enumNamePos=className.findRev("::"))!=-1)
6173 {
6174 QCString enumName = className.mid(enumNamePos+2);
6175 QCString fullScope = className.left(enumNamePos);
6176 if (!namespaceName.isEmpty()) fullScope.prepend(namespaceName+"::");
6177 if (fullScope==cd->name())
6178 {
6179 MemberName *enumMn=Doxygen::memberNameLinkedMap->find(enumName);
6180 //printf("enumMn(%s)=%p\n",qPrint(className),(void*)enumMn);
6181 if (enumMn)
6182 {
6183 for (const auto &emd : *enumMn)
6184 {
6185 memFound = emd->isStrong() && md->getEnumScope()==emd.get();
6186 if (memFound)
6187 {
6188 addMemberDocs(root,md,funcDecl,nullptr,overloaded,spec);
6189 count++;
6190 }
6191 if (memFound) break;
6192 }
6193 }
6194 }
6195 }
6196 if (memFound) break;
6197
6198 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
6199 if (tcd==nullptr && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
6200 {
6201 // don't be fooled by anonymous scopes
6202 tcd=cd;
6203 }
6204 //printf("Looking for %s inside nd=%s result=%s cd=%s\n",
6205 // qPrint(scopeName),nd?qPrint(nd->name()):"<none>",tcd?qPrint(tcd->name()):"",qPrint(cd->name()));
6206
6207 if (cd && tcd==cd) // member's classes match
6208 {
6209 AUTO_TRACE_ADD("class definition '{}' found",cd->name());
6210
6211 // get the template parameter lists found at the member declaration
6212 ArgumentLists declTemplArgs = cd->getTemplateParameterLists();
6213 const ArgumentList &templAl = md->templateArguments();
6214 if (!templAl.empty())
6215 {
6216 declTemplArgs.push_back(templAl);
6217 }
6218
6219 // get the template parameter lists found at the member definition
6220 const ArgumentLists &defTemplArgs = root->tArgLists;
6221 //printf("defTemplArgs=%p\n",defTemplArgs);
6222
6223 // do we replace the decl argument lists with the def argument lists?
6224 bool substDone=FALSE;
6225 ArgumentList argList;
6226
6227 /* substitute the occurrences of class template names in the
6228 * argument list before matching
6229 */
6230 const ArgumentList &mdAl = md->argumentList();
6231 if (declTemplArgs.size()>0 && declTemplArgs.size()==defTemplArgs.size())
6232 {
6233 /* the function definition has template arguments
6234 * and the class definition also has template arguments, so
6235 * we must substitute the template names of the class by that
6236 * of the function definition before matching.
6237 */
6238 substituteTemplatesInArgList(declTemplArgs,defTemplArgs,mdAl,argList);
6239
6240 substDone=TRUE;
6241 }
6242 else /* no template arguments, compare argument lists directly */
6243 {
6244 argList = mdAl;
6245 }
6246
6247 AUTO_TRACE_ADD("matching '{}'<=>'{}' className='{}' namespaceName='{}'",
6248 argListToString(argList,TRUE),argListToString(root->argList,TRUE),className,namespaceName);
6249
6250 bool matching=
6251 md->isVariable() || md->isTypedef() || // needed for function pointers
6253 md->getClassDef(),md->getFileDef(),&argList,
6254 cd,fd,&root->argList,
6255 TRUE,root->lang);
6256
6257 if (md->getLanguage()==SrcLangExt::ObjC && md->isVariable() && root->section.isFunction())
6258 {
6259 matching = FALSE; // don't match methods and attributes with the same name
6260 }
6261
6262 // for template member we also need to check the return type
6263 if (!md->templateArguments().empty() && !root->tArgLists.empty())
6264 {
6265 QCString memType = md->typeString();
6266 memType.stripPrefix("static "); // see bug700696
6268 className+"::",""); // see bug700693 & bug732594
6270 className+"::",""); // see bug758900
6271 AUTO_TRACE_ADD("Comparing return types '{}'<->'{}' #args {}<->{}",
6272 md->typeString(),funcType,md->templateArguments().size(),root->tArgLists.back().size());
6273 if (md->templateArguments().size()!=root->tArgLists.back().size() || memType!=funcType)
6274 {
6275 //printf(" ---> no matching\n");
6276 matching = FALSE;
6277 }
6278 }
6279 else if (defTemplArgs.size()>declTemplArgs.size())
6280 {
6281 AUTO_TRACE_ADD("Different number of template arguments {} vs {}",defTemplArgs.size(),declTemplArgs.size());
6282 // avoid matching a non-template function in a template class against a
6283 // template function with the same name and parameters, see issue #10184
6284 substDone = false;
6285 matching = false;
6286 }
6287 bool rootIsUserDoc = root->section.isMemberDoc();
6288 bool classIsTemplate = scopeIsTemplate(md->getClassDef());
6289 bool mdIsTemplate = md->templateArguments().hasParameters();
6290 bool classOrMdIsTemplate = mdIsTemplate || classIsTemplate;
6291 bool rootIsTemplate = !root->tArgLists.empty();
6292 //printf("classIsTemplate=%d mdIsTemplate=%d rootIsTemplate=%d\n",classIsTemplate,mdIsTemplate,rootIsTemplate);
6293 if (!rootIsUserDoc && // don't check out-of-line @fn references, see bug722457
6294 (mdIsTemplate || rootIsTemplate) && // either md or root is a template
6295 ((classOrMdIsTemplate && !rootIsTemplate) || (!classOrMdIsTemplate && rootIsTemplate))
6296 )
6297 {
6298 // Method with template return type does not match method without return type
6299 // even if the parameters are the same. See also bug709052
6300 AUTO_TRACE_ADD("Comparing return types: template v.s. non-template");
6301 matching = FALSE;
6302 }
6303
6304 AUTO_TRACE_ADD("Match results of matchArguments2='{}' substDone='{}'",matching,substDone);
6305
6306 if (substDone) // found a new argument list
6307 {
6308 if (matching) // replace member's argument list
6309 {
6311 md->moveArgumentList(std::make_unique<ArgumentList>(argList));
6312 }
6313 else // no match
6314 {
6315 if (!funcTempList.isEmpty() &&
6316 isSpecialization(declTemplArgs,defTemplArgs))
6317 {
6318 // check if we are dealing with a partial template
6319 // specialization. In this case we add it to the class
6320 // even though the member arguments do not match.
6321
6322 addMethodToClass(root,cd,type,md->name(),args,isFriend,
6323 md->protection(),md->isStatic(),md->virtualness(),spec,relates);
6324 return;
6325 }
6326 }
6327 }
6328 if (matching)
6329 {
6330 addMemberDocs(root,md,funcDecl,nullptr,overloaded,spec);
6331 count++;
6332 memFound=TRUE;
6333 }
6334 }
6335 else if (cd && cd!=tcd) // we did find a class with the same name as cd
6336 // but in a different namespace
6337 {
6338 noMatchCount++;
6339 }
6340
6341 if (memFound) break;
6342 }
6343 if (count==0 && root->parent() && root->parent()->section.isObjcImpl())
6344 {
6345 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
6346 return;
6347 }
6348 if (count==0 && !(isFriend && funcType=="class"))
6349 {
6350 int candidates=0;
6351 const ClassDef *ecd = nullptr, *ucd = nullptr;
6352 MemberDef *emd = nullptr, *umd = nullptr;
6353 //printf("Assume template class\n");
6354 for (const auto &md : *mn)
6355 {
6356 MemberDef *cmd=md.get();
6358 ClassDefMutable *ccd=cdmdm ? cdmdm->getClassDefMutable() : nullptr;
6359 //printf("ccd->name()==%s className=%s\n",qPrint(ccd->name()),qPrint(className));
6360 if (ccd!=nullptr && rightScopeMatch(ccd->name(),className))
6361 {
6362 const ArgumentList &templAl = md->templateArguments();
6363 if (!root->tArgLists.empty() && !templAl.empty() &&
6364 root->tArgLists.back().size()<=templAl.size())
6365 {
6366 AUTO_TRACE_ADD("add template specialization");
6367 addMethodToClass(root,ccd,type,md->name(),args,isFriend,
6368 root->protection,root->isStatic,root->virt,spec,relates);
6369 return;
6370 }
6371 if (argListToString(md->argumentList(),FALSE,FALSE) ==
6373 { // exact argument list match -> remember
6374 ucd = ecd = ccd;
6375 umd = emd = cmd;
6376 AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': exact match",
6377 className,ccd->name(),md->argsString());
6378 }
6379 else // arguments do not match, but member name and scope do -> remember
6380 {
6381 ucd = ccd;
6382 umd = cmd;
6383 AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': no match",
6384 className,ccd->name(),md->argsString());
6385 }
6386 candidates++;
6387 }
6388 }
6389 bool strictProtoMatching = Config_getBool(STRICT_PROTO_MATCHING);
6390 if (!strictProtoMatching)
6391 {
6392 if (candidates==1 && ucd && umd)
6393 {
6394 // we didn't find an actual match on argument lists, but there is only 1 member with this
6395 // name in the same scope, so that has to be the one.
6396 addMemberDocs(root,toMemberDefMutable(umd),funcDecl,nullptr,overloaded,spec);
6397 return;
6398 }
6399 else if (candidates>1 && ecd && emd)
6400 {
6401 // we didn't find a unique match using type resolution,
6402 // but one of the matches has the exact same signature so
6403 // we take that one.
6404 addMemberDocs(root,toMemberDefMutable(emd),funcDecl,nullptr,overloaded,spec);
6405 return;
6406 }
6407 }
6408
6409 QCString warnMsg = "no ";
6410 if (noMatchCount>1) warnMsg+="uniquely ";
6411 warnMsg+="matching class member found for \n";
6412
6413 for (const ArgumentList &al : root->tArgLists)
6414 {
6415 warnMsg+=" template ";
6416 warnMsg+=tempArgListToString(al,root->lang);
6417 warnMsg+='\n';
6418 }
6419
6420 QCString fullFuncDecl=funcDecl;
6421 if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
6422
6423 warnMsg+=" ";
6424 warnMsg+=fullFuncDecl;
6425
6426 if (candidates>0 || noMatchCount>=1)
6427 {
6428 warnMsg+="\nPossible candidates:";
6429
6430 NamespaceDef *nd=nullptr;
6431 if (!namespaceName.isEmpty()) nd=getResolvedNamespace(namespaceName);
6432 FileDef *fd=root->fileDef();
6433
6434 for (const auto &md : *mn)
6435 {
6436 const ClassDef *cd=md->getClassDef();
6437 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
6438 if (tcd==nullptr && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
6439 {
6440 // don't be fooled by anonymous scopes
6441 tcd=cd;
6442 }
6443 if (cd!=nullptr && (rightScopeMatch(cd->name(),className) || (cd!=tcd)))
6444 {
6445 warnMsg+='\n';
6446 const ArgumentList &templAl = md->templateArguments();
6447 warnMsg+=" '";
6448 if (templAl.hasParameters())
6449 {
6450 warnMsg+="template ";
6451 warnMsg+=tempArgListToString(templAl,root->lang);
6452 warnMsg+='\n';
6453 warnMsg+=" ";
6454 }
6455 if (!md->typeString().isEmpty())
6456 {
6457 warnMsg+=md->typeString();
6458 warnMsg+=' ';
6459 }
6461 if (!qScope.isEmpty())
6462 warnMsg+=qScope+"::"+md->name();
6463 warnMsg+=md->argsString();
6464 warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
6465 }
6466 }
6467 }
6468 warn(root->fileName,root->startLine,"{}",warnMsg);
6469 }
6470}
6471
6472//-------------------------------------------------------------------------------------------
6473
6474static void addMemberSpecialization(const Entry *root,
6475 MemberName *mn,
6476 ClassDefMutable *cd,
6477 const QCString &funcType,
6478 const QCString &funcName,
6479 const QCString &funcArgs,
6480 const QCString &funcDecl,
6481 const QCString &exceptions,
6482 TypeSpecifier spec
6483 )
6484{
6485 AUTO_TRACE("funcType={} funcName={} funcArgs={} funcDecl={} spec={}",funcType,funcName,funcArgs,funcDecl,spec);
6486 MemberDef *declMd=nullptr;
6487 for (const auto &md : *mn)
6488 {
6489 if (md->getClassDef()==cd)
6490 {
6491 // TODO: we should probably also check for matching arguments
6492 declMd = md.get();
6493 break;
6494 }
6495 }
6497 ArgumentList tArgList;
6498 // getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6499 auto md = createMemberDef(
6500 root->fileName,root->startLine,root->startColumn,
6501 funcType,funcName,funcArgs,exceptions,
6502 declMd ? declMd->protection() : root->protection,
6503 root->virt,root->isStatic,Relationship::Member,
6504 mtype,tArgList,root->argList,root->metaData);
6505 auto mmd = toMemberDefMutable(md.get());
6506 //printf("new specialized member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6507 mmd->setTagInfo(root->tagInfo());
6508 mmd->setLanguage(root->lang);
6509 mmd->setId(root->id);
6510 mmd->setMemberClass(cd);
6511 mmd->setTemplateSpecialization(TRUE);
6512 mmd->setTypeConstraints(root->typeConstr);
6513 mmd->setDefinition(funcDecl);
6515 mmd->addQualifiers(root->qualifiers);
6516 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6517 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6518 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6519 mmd->setDocsForDefinition(!root->proto);
6520 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6521 mmd->addSectionsToDefinition(root->anchors);
6522 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6523 FileDef *fd=root->fileDef();
6524 mmd->setBodyDef(fd);
6525 mmd->setMemberSpecifiers(spec);
6526 mmd->setVhdlSpecifiers(root->vhdlSpec);
6527 mmd->setMemberGroupId(root->mGrpId);
6528 cd->insertMember(md.get());
6529 mmd->setRefItems(root->sli);
6530
6531 mn->push_back(std::move(md));
6532}
6533
6534//-------------------------------------------------------------------------------------------
6535
6536static void addOverloaded(const Entry *root,MemberName *mn,
6537 const QCString &funcType,const QCString &funcName,const QCString &funcArgs,
6538 const QCString &funcDecl,const QCString &exceptions,TypeSpecifier spec)
6539{
6540 // for unique overloaded member we allow the class to be
6541 // omitted, this is to be Qt compatible. Using this should
6542 // however be avoided, because it is error prone
6543 bool sameClass=false;
6544 if (mn->size()>0)
6545 {
6546 // check if all members with the same name are also in the same class
6547 sameClass = std::equal(mn->begin()+1,mn->end(),mn->begin(),
6548 [](const auto &md1,const auto &md2)
6549 { return md1->getClassDef()->name()==md2->getClassDef()->name(); });
6550 }
6551 if (sameClass)
6552 {
6553 MemberDefMutable *mdm = toMemberDefMutable(mn->front().get());
6554 ClassDefMutable *cd = mdm ? mdm->getClassDefMutable() : nullptr;
6555 if (cd==nullptr) return;
6556
6558 if (root->mtype==MethodTypes::Signal) mtype=MemberType::Signal;
6559 else if (root->mtype==MethodTypes::Slot) mtype=MemberType::Slot;
6560 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType::DCOP;
6561
6562 // new overloaded member function
6563 std::unique_ptr<ArgumentList> tArgList =
6564 getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6565 //printf("new related member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6566 auto md = createMemberDef(
6567 root->fileName,root->startLine,root->startColumn,
6568 funcType,funcName,funcArgs,exceptions,
6569 root->protection,root->virt,root->isStatic,Relationship::Related,
6570 mtype,tArgList ? *tArgList : ArgumentList(),root->argList,root->metaData);
6571 auto mmd = toMemberDefMutable(md.get());
6572 mmd->setTagInfo(root->tagInfo());
6573 mmd->setLanguage(root->lang);
6574 mmd->setId(root->id);
6575 mmd->setTypeConstraints(root->typeConstr);
6576 mmd->setMemberClass(cd);
6577 mmd->setDefinition(funcDecl);
6579 mmd->addQualifiers(root->qualifiers);
6581 doc+="<p>";
6582 doc+=root->doc;
6583 mmd->setDocumentation(doc,root->docFile,root->docLine);
6584 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6585 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6586 mmd->setDocsForDefinition(!root->proto);
6587 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6588 mmd->addSectionsToDefinition(root->anchors);
6589 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6590 FileDef *fd=root->fileDef();
6591 mmd->setBodyDef(fd);
6592 mmd->setMemberSpecifiers(spec);
6593 mmd->setVhdlSpecifiers(root->vhdlSpec);
6594 mmd->setMemberGroupId(root->mGrpId);
6595 cd->insertMember(md.get());
6596 cd->insertUsedFile(fd);
6597 mmd->setRefItems(root->sli);
6598
6599 mn->push_back(std::move(md));
6600 }
6601}
6602
6603static void insertMemberAlias(Definition *outerScope,const MemberDef *md)
6604{
6605 if (outerScope && outerScope!=Doxygen::globalScope)
6606 {
6607 auto aliasMd = createMemberDefAlias(outerScope,md);
6608 if (outerScope->definitionType()==Definition::TypeClass)
6609 {
6610 ClassDefMutable *cdm = toClassDefMutable(outerScope);
6611 if (cdm)
6612 {
6613 cdm->insertMember(aliasMd.get());
6614 }
6615 }
6616 else if (outerScope->definitionType()==Definition::TypeNamespace)
6617 {
6618 NamespaceDefMutable *ndm = toNamespaceDefMutable(outerScope);
6619 if (ndm)
6620 {
6621 ndm->insertMember(aliasMd.get());
6622 }
6623 }
6624 else if (outerScope->definitionType()==Definition::TypeFile)
6625 {
6626 toFileDef(outerScope)->insertMember(aliasMd.get());
6627 }
6628 if (aliasMd)
6629 {
6630 Doxygen::functionNameLinkedMap->add(md->name())->push_back(std::move(aliasMd));
6631 }
6632 }
6633}
6634
6635//-------------------------------------------------------------------------------------------
6636
6637/*! This function tries to find a member (in a documented class/file/namespace)
6638 * that corresponds to the function/variable declaration given in \a funcDecl.
6639 *
6640 * The boolean \a overloaded is used to specify whether or not a standard
6641 * overload documentation line should be generated.
6642 *
6643 * The boolean \a isFunc is a hint that indicates that this is a function
6644 * instead of a variable or typedef.
6645 */
6646static void findMember(const Entry *root,
6647 const QCString &relates,
6648 const QCString &type,
6649 const QCString &args,
6650 QCString funcDecl,
6651 bool overloaded,
6652 bool isFunc
6653 )
6654{
6655 AUTO_TRACE("root='{}' funcDecl='{}' related='{}' overload={} isFunc={} mGrpId={} #tArgList={} spec={} lang={}",
6656 root->name, funcDecl, relates, overloaded, isFunc, root->mGrpId, root->tArgLists.size(),
6657 root->spec, root->lang);
6658
6659 QCString scopeName;
6660 QCString className;
6661 QCString namespaceName;
6662 QCString funcType;
6663 QCString funcName;
6664 QCString funcArgs;
6665 QCString funcTempList;
6666 QCString exceptions;
6667 QCString funcSpec;
6668 bool isRelated=false;
6669 bool isMemberOf=false;
6670 bool isFriend=false;
6671 bool done=false;
6672 TypeSpecifier spec = root->spec;
6673 while (!done)
6674 {
6675 done=true;
6676 if (funcDecl.stripPrefix("friend ")) // treat friends as related members
6677 {
6678 isFriend=true;
6679 done=false;
6680 }
6681 if (funcDecl.stripPrefix("inline "))
6682 {
6683 spec.setInline(true);
6684 done=false;
6685 }
6686 if (funcDecl.stripPrefix("explicit "))
6687 {
6688 spec.setExplicit(true);
6689 done=false;
6690 }
6691 if (funcDecl.stripPrefix("mutable "))
6692 {
6693 spec.setMutable(true);
6694 done=false;
6695 }
6696 if (funcDecl.stripPrefix("thread_local "))
6697 {
6698 spec.setThreadLocal(true);
6699 done=false;
6700 }
6701 if (funcDecl.stripPrefix("virtual "))
6702 {
6703 done=false;
6704 }
6705 }
6706
6707 // delete any ; from the function declaration
6708 int sep=0;
6709 while ((sep=funcDecl.find(';'))!=-1)
6710 {
6711 funcDecl=(funcDecl.left(sep)+funcDecl.right(funcDecl.length()-sep-1)).stripWhiteSpace();
6712 }
6713
6714 // make sure the first character is a space to simplify searching.
6715 if (!funcDecl.isEmpty() && funcDecl[0]!=' ') funcDecl.prepend(" ");
6716
6717 // remove some superfluous spaces
6718 funcDecl= substitute(
6719 substitute(
6720 substitute(funcDecl,"~ ","~"),
6721 ":: ","::"
6722 ),
6723 " ::","::"
6724 ).stripWhiteSpace();
6725
6726 //printf("funcDecl='%s'\n",qPrint(funcDecl));
6727 if (isFriend && funcDecl.startsWith("class "))
6728 {
6729 //printf("friend class\n");
6730 funcDecl=funcDecl.right(funcDecl.length()-6);
6731 funcName = funcDecl;
6732 }
6733 else if (isFriend && funcDecl.startsWith("struct "))
6734 {
6735 funcDecl=funcDecl.right(funcDecl.length()-7);
6736 funcName = funcDecl;
6737 }
6738 else
6739 {
6740 // extract information from the declarations
6741 parseFuncDecl(funcDecl,root->lang,scopeName,funcType,funcName,
6742 funcArgs,funcTempList,exceptions
6743 );
6744 }
6745
6746 // the class name can also be a namespace name, we decide this later.
6747 // if a related class name is specified and the class name could
6748 // not be derived from the function declaration, then use the
6749 // related field.
6750 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}' funcType='{}' funcName='{}' funcArgs='{}'",
6751 scopeName,className,namespaceName,funcType,funcName,funcArgs);
6752 if (!relates.isEmpty())
6753 { // related member, prefix user specified scope
6754 isRelated=TRUE;
6755 isMemberOf=(root->relatesType == RelatesType::MemberOf);
6756 if (getClass(relates)==nullptr && !scopeName.isEmpty())
6757 {
6758 scopeName= mergeScopes(scopeName,relates);
6759 }
6760 else
6761 {
6762 scopeName = relates;
6763 }
6764 }
6765
6766 if (relates.isEmpty() && root->parent() &&
6767 (root->parent()->section.isScope() || root->parent()->section.isObjcImpl()) &&
6768 !root->parent()->name.isEmpty()) // see if we can combine scopeName
6769 // with the scope in which it was found
6770 {
6771 QCString joinedName = root->parent()->name+"::"+scopeName;
6772 if (!scopeName.isEmpty() &&
6773 (getClass(joinedName) || Doxygen::namespaceLinkedMap->find(joinedName)))
6774 {
6775 scopeName = joinedName;
6776 }
6777 else
6778 {
6779 scopeName = mergeScopes(root->parent()->name,scopeName);
6780 }
6781 }
6782 else // see if we can prefix a namespace or class that is used from the file
6783 {
6784 FileDef *fd=root->fileDef();
6785 if (fd)
6786 {
6787 for (const auto &fnd : fd->getUsedNamespaces())
6788 {
6789 QCString joinedName = fnd->name()+"::"+scopeName;
6790 if (Doxygen::namespaceLinkedMap->find(joinedName))
6791 {
6792 scopeName=joinedName;
6793 break;
6794 }
6795 }
6796 }
6797 }
6799 removeRedundantWhiteSpace(scopeName),false,&funcSpec,QCString(),false);
6800
6801 // funcSpec contains the last template specifiers of the given scope.
6802 // If this method does not have any template arguments or they are
6803 // empty while funcSpec is not empty we assume this is a
6804 // specialization of a method. If not, we clear the funcSpec and treat
6805 // this as a normal method of a template class.
6806 if (!(root->tArgLists.size()>0 &&
6807 root->tArgLists.front().size()==0
6808 )
6809 )
6810 {
6811 funcSpec.clear();
6812 }
6813
6814 //namespaceName=removeAnonymousScopes(namespaceName);
6815 if (!Config_getBool(EXTRACT_ANON_NSPACES) && scopeName.find('@')!=-1) return; // skip stuff in anonymous namespace...
6816
6817 // split scope into a namespace and a class part
6818 extractNamespaceName(scopeName,className,namespaceName,TRUE);
6819 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}'",scopeName,className,namespaceName);
6820
6821 //printf("namespaceName='%s' className='%s'\n",qPrint(namespaceName),qPrint(className));
6822 // merge class and namespace scopes again
6823 scopeName.clear();
6824 if (!namespaceName.isEmpty())
6825 {
6826 if (className.isEmpty())
6827 {
6828 scopeName=namespaceName;
6829 }
6830 else if (!relates.isEmpty() || // relates command with explicit scope
6831 !getClass(className)) // class name only exists in a namespace
6832 {
6833 scopeName=namespaceName+"::"+className;
6834 }
6835 else
6836 {
6837 scopeName=className;
6838 }
6839 }
6840 else if (!className.isEmpty())
6841 {
6842 scopeName=className;
6843 }
6844 //printf("new scope='%s'\n",qPrint(scopeName));
6845
6846 QCString tempScopeName=scopeName;
6847 ClassDefMutable *cd=getClassMutable(scopeName);
6848 if (cd)
6849 {
6850 if (funcSpec.isEmpty())
6851 {
6852 uint32_t argListIndex=0;
6853 tempScopeName=cd->qualifiedNameWithTemplateParameters(&root->tArgLists,&argListIndex);
6854 }
6855 else
6856 {
6857 tempScopeName=scopeName+funcSpec;
6858 }
6859 }
6860 //printf("scopeName=%s cd=%p root->tArgLists=%p result=%s\n",
6861 // qPrint(scopeName),cd,root->tArgLists,qPrint(tempScopeName));
6862
6863 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
6864 // rebuild the function declaration (needed to get the scope right).
6865 if (!scopeName.isEmpty() && !isRelated && !isFriend && !Config_getBool(HIDE_SCOPE_NAMES))
6866 {
6867 if (!funcType.isEmpty())
6868 {
6869 if (isFunc) // a function -> we use argList for the arguments
6870 {
6871 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcTempList;
6872 }
6873 else
6874 {
6875 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcArgs;
6876 }
6877 }
6878 else
6879 {
6880 if (isFunc) // a function => we use argList for the arguments
6881 {
6882 funcDecl=tempScopeName+"::"+funcName+funcTempList;
6883 }
6884 else // variable => add 'argument' list
6885 {
6886 funcDecl=tempScopeName+"::"+funcName+funcArgs;
6887 }
6888 }
6889 }
6890 else // build declaration without scope
6891 {
6892 if (!funcType.isEmpty()) // but with a type
6893 {
6894 if (isFunc) // function => omit argument list
6895 {
6896 funcDecl=funcType+" "+funcName+funcTempList;
6897 }
6898 else // variable => add 'argument' list
6899 {
6900 funcDecl=funcType+" "+funcName+funcArgs;
6901 }
6902 }
6903 else // no type
6904 {
6905 if (isFunc)
6906 {
6907 funcDecl=funcName+funcTempList;
6908 }
6909 else
6910 {
6911 funcDecl=funcName+funcArgs;
6912 }
6913 }
6914 }
6915
6916 if (funcType=="template class" && !funcTempList.isEmpty())
6917 return; // ignore explicit template instantiations
6918
6919 AUTO_TRACE_ADD("Parse results: namespaceName='{}' className=`{}` funcType='{}' funcSpec='{}' "
6920 " funcName='{}' funcArgs='{}' funcTempList='{}' funcDecl='{}' relates='{}'"
6921 " exceptions='{}' isRelated={} isMemberOf={} isFriend={} isFunc={}",
6922 namespaceName, className, funcType, funcSpec,
6923 funcName, funcArgs, funcTempList, funcDecl, relates,
6924 exceptions, isRelated, isMemberOf, isFriend, isFunc);
6925
6926 if (!funcName.isEmpty()) // function name is valid
6927 {
6928 // check if 'className' is actually a scoped enum, in which case we need to
6929 // process it as a global, see issue #6471
6930 bool strongEnum = false;
6931 MemberName *mn=nullptr;
6932 if (!className.isEmpty() && (mn=Doxygen::functionNameLinkedMap->find(className)))
6933 {
6934 for (const auto &imd : *mn)
6935 {
6936 MemberDefMutable *md = toMemberDefMutable(imd.get());
6937 Definition *mdScope = nullptr;
6938 if (md && md->isEnumerate() && md->isStrong() && (mdScope=md->getOuterScope()) &&
6939 // need filter for the correct scope, see issue #9668
6940 ((namespaceName.isEmpty() && mdScope==Doxygen::globalScope) || (mdScope->name()==namespaceName)))
6941 {
6942 AUTO_TRACE_ADD("'{}' is a strong enum! (namespace={} md->getOuterScope()->name()={})",md->name(),namespaceName,md->getOuterScope()->name());
6943 strongEnum = true;
6944 // pass the scope name name as a 'namespace' to the findGlobalMember function
6945 if (!namespaceName.isEmpty())
6946 {
6947 namespaceName+="::"+className;
6948 }
6949 else
6950 {
6951 namespaceName=className;
6952 }
6953 }
6954 }
6955 }
6956
6957 if (funcName.startsWith("operator ")) // strip class scope from cast operator
6958 {
6959 funcName = substitute(funcName,className+"::","");
6960 }
6961 mn = nullptr;
6962 if (!funcTempList.isEmpty()) // try with member specialization
6963 {
6964 mn=Doxygen::memberNameLinkedMap->find(funcName+funcTempList);
6965 }
6966 if (mn==nullptr) // try without specialization
6967 {
6968 mn=Doxygen::memberNameLinkedMap->find(funcName);
6969 }
6970 if (!isRelated && !strongEnum && mn) // function name already found
6971 {
6972 AUTO_TRACE_ADD("member name exists ({} members with this name)",mn->size());
6973 if (!className.isEmpty()) // class name is valid
6974 {
6975 if (funcSpec.isEmpty()) // not a member specialization
6976 {
6977 addMemberFunction(root,mn,scopeName,namespaceName,className,funcType,funcName,
6978 funcArgs,funcTempList,exceptions,
6979 type,args,isFriend,spec,relates,funcDecl,overloaded,isFunc);
6980 }
6981 else if (cd) // member specialization
6982 {
6983 addMemberSpecialization(root,mn,cd,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
6984 }
6985 else
6986 {
6987 //printf("*** Specialized member %s of unknown scope %s%s found!\n",
6988 // qPrint(scopeName),qPrint(funcName),qPrint(funcArgs));
6989 }
6990 }
6991 else if (overloaded) // check if the function belongs to only one class
6992 {
6993 addOverloaded(root,mn,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
6994 }
6995 else // unrelated function with the same name as a member
6996 {
6997 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
6998 {
6999 QCString fullFuncDecl=funcDecl;
7000 if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
7001 warn(root->fileName,root->startLine,
7002 "Cannot determine class for function\n{}",
7003 fullFuncDecl
7004 );
7005 }
7006 }
7007 }
7008 else if (isRelated && !relates.isEmpty())
7009 {
7010 AUTO_TRACE_ADD("related function scopeName='{}' className='{}'",scopeName,className);
7011 if (className.isEmpty()) className=relates;
7012 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
7013 if ((cd=getClassMutable(scopeName)))
7014 {
7015 bool newMember=TRUE; // assume we have a new member
7016 MemberDefMutable *mdDefine=nullptr;
7017 {
7018 mn = Doxygen::functionNameLinkedMap->find(funcName);
7019 if (mn)
7020 {
7021 for (const auto &imd : *mn)
7022 {
7023 MemberDefMutable *md = toMemberDefMutable(imd.get());
7024 if (md && md->isDefine())
7025 {
7026 mdDefine = md;
7027 break;
7028 }
7029 }
7030 }
7031 }
7032
7033 if (mdDefine) // macro definition is already created by the preprocessor and inserted as a file member
7034 {
7035 //printf("moving #define %s into class %s\n",qPrint(mdDefine->name()),qPrint(cd->name()));
7036
7037 // take mdDefine from the Doxygen::functionNameLinkedMap (without deleting the data)
7038 auto mdDefineTaken = Doxygen::functionNameLinkedMap->take(funcName,mdDefine);
7039 // insert it as a class member
7040 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==nullptr)
7041 {
7042 mn=Doxygen::memberNameLinkedMap->add(funcName);
7043 }
7044
7045 if (mdDefine->getFileDef())
7046 {
7047 mdDefine->getFileDef()->removeMember(mdDefine);
7048 }
7049 mdDefine->makeRelated();
7050 mdDefine->setMemberClass(cd);
7051 mdDefine->moveTo(cd);
7052 cd->insertMember(mdDefine);
7053 // also insert the member as an alias in the parent's scope, so it can be referenced also without cd's scope
7054 insertMemberAlias(cd->getOuterScope(),mdDefine);
7055 mn->push_back(std::move(mdDefineTaken));
7056 }
7057 else // normal member, needs to be created and added to the class
7058 {
7059 FileDef *fd=root->fileDef();
7060
7061 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==nullptr)
7062 {
7063 mn=Doxygen::memberNameLinkedMap->add(funcName);
7064 }
7065 else
7066 {
7067 // see if we got another member with matching arguments
7068 MemberDefMutable *rmd_found = nullptr;
7069 for (const auto &irmd : *mn)
7070 {
7071 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
7072 if (rmd)
7073 {
7074 const ArgumentList &rmdAl = rmd->argumentList();
7075
7076 newMember=
7077 className!=rmd->getOuterScope()->name() ||
7078 !matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),&rmdAl,
7079 cd,fd,&root->argList,
7080 TRUE,root->lang);
7081 if (!newMember)
7082 {
7083 rmd_found = rmd;
7084 }
7085 }
7086 }
7087 if (rmd_found) // member already exists as rmd -> add docs
7088 {
7089 AUTO_TRACE_ADD("addMemberDocs for related member {}",root->name);
7090 addMemberDocs(root,rmd_found,funcDecl,nullptr,overloaded,spec);
7091 newMember=false;
7092 }
7093 }
7094
7095 if (newMember) // need to create a new member
7096 {
7098 switch (root->mtype)
7099 {
7100 case MethodTypes::Method: mtype = MemberType::Function; break;
7101 case MethodTypes::Signal: mtype = MemberType::Signal; break;
7102 case MethodTypes::Slot: mtype = MemberType::Slot; break;
7103 case MethodTypes::DCOP: mtype = MemberType::DCOP; break;
7104 case MethodTypes::Property: mtype = MemberType::Property; break;
7105 case MethodTypes::Event: mtype = MemberType::Event; break;
7106 }
7107
7108 //printf("New related name '%s' '%d'\n",qPrint(funcName),
7109 // root->argList ? (int)root->argList->count() : -1);
7110
7111 // first note that we pass:
7112 // (root->tArgLists ? root->tArgLists->last() : nullptr)
7113 // for the template arguments for the new "member."
7114 // this accurately reflects the template arguments of
7115 // the related function, which don't have to do with
7116 // those of the related class.
7117 auto md = createMemberDef(
7118 root->fileName,root->startLine,root->startColumn,
7119 funcType,funcName,funcArgs,exceptions,
7120 root->protection,root->virt,
7121 root->isStatic,
7122 isMemberOf ? Relationship::Foreign : Relationship::Related,
7123 mtype,
7124 (!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList()),
7125 funcArgs.isEmpty() ? ArgumentList() : root->argList,
7126 root->metaData);
7127 auto mmd = toMemberDefMutable(md.get());
7128
7129 // also insert the member as an alias in the parent's scope, so it can be referenced also without cd's scope
7130 insertMemberAlias(cd->getOuterScope(),md.get());
7131
7132 // we still have the problem that
7133 // MemberDef::writeDocumentation() in memberdef.cpp
7134 // writes the template argument list for the class,
7135 // as if this member is a member of the class.
7136 // fortunately, MemberDef::writeDocumentation() has
7137 // a special mechanism that allows us to totally
7138 // override the set of template argument lists that
7139 // are printed. We use that and set it to the
7140 // template argument lists of the related function.
7141 //
7142 mmd->setDefinitionTemplateParameterLists(root->tArgLists);
7143
7144 mmd->setTagInfo(root->tagInfo());
7145
7146 //printf("Related member name='%s' decl='%s' bodyLine='%d'\n",
7147 // qPrint(funcName),qPrint(funcDecl),root->bodyLine);
7148
7149 // try to find the matching line number of the body from the
7150 // global function list
7151 bool found=FALSE;
7152 if (root->bodyLine==-1)
7153 {
7154 MemberName *rmn=Doxygen::functionNameLinkedMap->find(funcName);
7155 if (rmn)
7156 {
7157 const MemberDefMutable *rmd_found=nullptr;
7158 for (const auto &irmd : *rmn)
7159 {
7160 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
7161 if (rmd)
7162 {
7163 const ArgumentList &rmdAl = rmd->argumentList();
7164 // check for matching argument lists
7165 if (
7166 matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),&rmdAl,
7167 cd,fd,&root->argList,
7168 TRUE,root->lang)
7169 )
7170 {
7171 found=TRUE;
7172 rmd_found = rmd;
7173 break;
7174 }
7175 }
7176 }
7177 if (rmd_found) // member found -> copy line number info
7178 {
7179 mmd->setBodySegment(rmd_found->getDefLine(),rmd_found->getStartBodyLine(),rmd_found->getEndBodyLine());
7180 mmd->setBodyDef(rmd_found->getBodyDef());
7181 //md->setBodyMember(rmd);
7182 }
7183 }
7184 }
7185 if (!found) // line number could not be found or is available in this
7186 // entry
7187 {
7188 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7189 mmd->setBodyDef(fd);
7190 }
7191
7192 //if (root->mGrpId!=-1)
7193 //{
7194 // md->setMemberGroup(memberGroupDict[root->mGrpId]);
7195 //}
7196 mmd->setMemberClass(cd);
7197 mmd->setMemberSpecifiers(spec);
7198 mmd->setVhdlSpecifiers(root->vhdlSpec);
7199 mmd->setDefinition(funcDecl);
7201 mmd->addQualifiers(root->qualifiers);
7202 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7203 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7204 mmd->setDocsForDefinition(!root->proto);
7205 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
7206 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7207 mmd->addSectionsToDefinition(root->anchors);
7208 mmd->setMemberGroupId(root->mGrpId);
7209 mmd->setLanguage(root->lang);
7210 mmd->setId(root->id);
7211 //md->setMemberDefTemplateArguments(root->mtArgList);
7212 cd->insertMember(md.get());
7213 cd->insertUsedFile(fd);
7214 mmd->setRefItems(root->sli);
7215 if (root->relatesType==RelatesType::Duplicate) mmd->setRelatedAlso(cd);
7216 addMemberToGroups(root,md.get());
7218 //printf("Adding member=%s\n",qPrint(md->name()));
7219 mn->push_back(std::move(md));
7220 }
7221 if (root->relatesType==RelatesType::Duplicate)
7222 {
7223 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
7224 {
7225 QCString fullFuncDecl=funcDecl;
7226 if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
7227 warn(root->fileName,root->startLine,
7228 "Cannot determine file/namespace for relatedalso function\n{}",
7229 fullFuncDecl
7230 );
7231 }
7232 }
7233 }
7234 }
7235 else
7236 {
7237 warn_undoc(root->fileName,root->startLine, "class '{}' for related function '{}' is not documented.", className,funcName);
7238 }
7239 }
7240 else if (root->parent() && root->parent()->section.isObjcImpl())
7241 {
7242 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
7243 }
7244 else // unrelated not overloaded member found
7245 {
7246 bool globMem = findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec);
7247 if (className.isEmpty() && !globMem)
7248 {
7249 warn(root->fileName,root->startLine, "class for member '{}' cannot be found.", funcName);
7250 }
7251 else if (!className.isEmpty() && !globMem)
7252 {
7253 warn(root->fileName,root->startLine,
7254 "member '{}' of class '{}' cannot be found",
7255 funcName,className);
7256 }
7257 }
7258 }
7259 else
7260 {
7261 // this should not be called
7262 warn(root->fileName,root->startLine,"member with no name found.");
7263 }
7264 return;
7265}
7266
7267//----------------------------------------------------------------------
7268// find the members corresponding to the different documentation blocks
7269// that are extracted from the sources.
7270
7271static void filterMemberDocumentation(const Entry *root,const QCString &relates)
7272{
7273 AUTO_TRACE("root->type='{}' root->inside='{}' root->name='{}' root->args='{}' section={} root->spec={} root->mGrpId={}",
7274 root->type,root->inside,root->name,root->args,root->section,root->spec,root->mGrpId);
7275 //printf("root->parent()->name=%s\n",qPrint(root->parent()->name));
7276 bool isFunc=TRUE;
7277
7278 QCString type = root->type;
7279 QCString args = root->args;
7280 int i=-1, l=0;
7281 if ( // detect func variable/typedef to func ptr
7282 (i=findFunctionPtr(type.str(),root->lang,&l))!=-1
7283 )
7284 {
7285 //printf("Fixing function pointer!\n");
7286 // fix type and argument
7287 args.prepend(type.right(type.length()-i-l));
7288 type=type.left(i+l);
7289 //printf("Results type=%s,name=%s,args=%s\n",qPrint(type),qPrint(root->name),qPrint(args));
7290 isFunc=FALSE;
7291 }
7292 else if ((type.startsWith("typedef ") && args.find('(')!=-1))
7293 // detect function types marked as functions
7294 {
7295 isFunc=FALSE;
7296 }
7297
7298 //printf("Member %s isFunc=%d\n",qPrint(root->name),isFunc);
7299 if (root->section.isMemberDoc())
7300 {
7301 //printf("Documentation for inline member '%s' found args='%s'\n",
7302 // qPrint(root->name),qPrint(args));
7303 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
7304 if (type.isEmpty())
7305 {
7306 findMember(root,
7307 relates,
7308 type,
7309 args,
7310 root->name + args + root->exception,
7311 FALSE,
7312 isFunc);
7313 }
7314 else
7315 {
7316 findMember(root,
7317 relates,
7318 type,
7319 args,
7320 type + " " + root->name + args + root->exception,
7321 FALSE,
7322 isFunc);
7323 }
7324 }
7325 else if (root->section.isOverloadDoc())
7326 {
7327 //printf("Overloaded member %s found\n",qPrint(root->name));
7328 findMember(root,
7329 relates,
7330 type,
7331 args,
7332 root->name,
7333 TRUE,
7334 isFunc);
7335 }
7336 else if
7337 ((root->section.isFunction() // function
7338 ||
7339 (root->section.isVariable() && // variable
7340 !type.isEmpty() && // with a type
7341 g_compoundKeywords.find(type.str())==g_compoundKeywords.end() // that is not a keyword
7342 // (to skip forward declaration of class etc.)
7343 )
7344 )
7345 )
7346 {
7347 //printf("Documentation for member '%s' found args='%s' excp='%s'\n",
7348 // qPrint(root->name),qPrint(args),qPrint(root->exception));
7349 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
7350 //printf("Inside=%s\n Relates=%s\n",qPrint(root->inside),qPrint(relates));
7351 if (type=="friend class" || type=="friend struct" ||
7352 type=="friend union")
7353 {
7354 findMember(root,
7355 relates,
7356 type,
7357 args,
7358 type+" "+root->name,
7359 FALSE,FALSE);
7360
7361 }
7362 else if (!type.isEmpty())
7363 {
7364 findMember(root,
7365 relates,
7366 type,
7367 args,
7368 type+" "+ root->inside + root->name + args + root->exception,
7369 FALSE,isFunc);
7370 }
7371 else
7372 {
7373 findMember(root,
7374 relates,
7375 type,
7376 args,
7377 root->inside + root->name + args + root->exception,
7378 FALSE,isFunc);
7379 }
7380 }
7381 else if (root->section.isDefine() && !relates.isEmpty())
7382 {
7383 findMember(root,
7384 relates,
7385 type,
7386 args,
7387 root->name + args,
7388 FALSE,
7389 !args.isEmpty());
7390 }
7391 else if (root->section.isVariableDoc())
7392 {
7393 //printf("Documentation for variable %s found\n",qPrint(root->name));
7394 //if (!relates.isEmpty()) printf(" Relates %s\n",qPrint(relates));
7395 findMember(root,
7396 relates,
7397 type,
7398 args,
7399 root->name,
7400 FALSE,
7401 FALSE);
7402 }
7403 else if (root->section.isExportedInterface() ||
7404 root->section.isIncludedService())
7405 {
7406 findMember(root,
7407 relates,
7408 type,
7409 args,
7410 type + " " + root->name,
7411 FALSE,
7412 FALSE);
7413 }
7414 else
7415 {
7416 // skip section
7417 //printf("skip section\n");
7418 }
7419}
7420
7421static void findMemberDocumentation(const Entry *root)
7422{
7423 if (root->section.isMemberDoc() ||
7424 root->section.isOverloadDoc() ||
7425 root->section.isFunction() ||
7426 root->section.isVariable() ||
7427 root->section.isVariableDoc() ||
7428 root->section.isDefine() ||
7429 root->section.isIncludedService() ||
7430 root->section.isExportedInterface()
7431 )
7432 {
7433 AUTO_TRACE();
7434 if (root->relatesType==RelatesType::Duplicate && !root->relates.isEmpty())
7435 {
7437 }
7439 }
7440 for (const auto &e : root->children())
7441 {
7442 if (!e->section.isEnum())
7443 {
7444 findMemberDocumentation(e.get());
7445 }
7446 }
7447}
7448
7449//----------------------------------------------------------------------
7450
7451static void findObjCMethodDefinitions(const Entry *root)
7452{
7453 AUTO_TRACE();
7454 for (const auto &objCImpl : root->children())
7455 {
7456 if (objCImpl->section.isObjcImpl())
7457 {
7458 for (const auto &objCMethod : objCImpl->children())
7459 {
7460 if (objCMethod->section.isFunction())
7461 {
7462 //printf(" Found ObjC method definition %s\n",qPrint(objCMethod->name));
7463 findMember(objCMethod.get(),
7464 objCMethod->relates,
7465 objCMethod->type,
7466 objCMethod->args,
7467 objCMethod->type+" "+objCImpl->name+"::"+objCMethod->name+" "+objCMethod->args,
7468 FALSE,TRUE);
7469 objCMethod->section=EntryType::makeEmpty();
7470 }
7471 }
7472 }
7473 }
7474}
7475
7476//----------------------------------------------------------------------
7477// find and add the enumeration to their classes, namespaces or files
7478
7479static void findEnums(const Entry *root)
7480{
7481 if (root->section.isEnum())
7482 {
7483 AUTO_TRACE("name={}",root->name);
7484 ClassDefMutable *cd = nullptr;
7485 FileDef *fd = nullptr;
7486 NamespaceDefMutable *nd = nullptr;
7487 MemberNameLinkedMap *mnsd = nullptr;
7488 bool isGlobal = false;
7489 bool isRelated = false;
7490 bool isMemberOf = false;
7491 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7492
7493 QCString name;
7494 QCString scope;
7495
7496 int i = root->name.findRev("::");
7497 if (i!=-1) // scope is specified
7498 {
7499 scope=root->name.left(i); // extract scope
7500 if (root->lang==SrcLangExt::CSharp)
7501 {
7502 scope = mangleCSharpGenericName(scope);
7503 }
7504 name=root->name.right(root->name.length()-i-2); // extract name
7505 if ((cd=getClassMutable(scope))==nullptr)
7506 {
7508 }
7509 }
7510 else // no scope, check the scope in which the docs where found
7511 {
7512 if (root->parent()->section.isScope() && !root->parent()->name.isEmpty()) // found enum docs inside a compound
7513 {
7514 scope=root->parent()->name;
7515 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7516 }
7517 name=root->name;
7518 }
7519
7520 if (!root->relates.isEmpty())
7521 { // related member, prefix user specified scope
7522 isRelated=TRUE;
7523 isMemberOf=(root->relatesType==RelatesType::MemberOf);
7524 if (getClass(root->relates)==nullptr && !scope.isEmpty())
7525 scope=mergeScopes(scope,root->relates);
7526 else
7527 scope=root->relates;
7528 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7529 }
7530
7531 if (cd && !name.isEmpty()) // found a enum inside a compound
7532 {
7533 //printf("Enum '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7534 fd=nullptr;
7536 isGlobal=false;
7537 }
7538 else if (nd) // found enum inside namespace
7539 {
7541 isGlobal=true;
7542 }
7543 else // found a global enum
7544 {
7545 fd=root->fileDef();
7547 isGlobal=true;
7548 }
7549
7550 if (!name.isEmpty())
7551 {
7552 // new enum type
7553 AUTO_TRACE_ADD("new enum {} at line {} of {}",name,root->bodyLine,root->fileName);
7554 auto md = createMemberDef(
7555 root->fileName,root->startLine,root->startColumn,
7556 QCString(),name,QCString(),QCString(),
7557 root->protection,Specifier::Normal,FALSE,
7558 isMemberOf ? Relationship::Foreign : isRelated ? Relationship::Related : Relationship::Member,
7561 auto mmd = toMemberDefMutable(md.get());
7562 mmd->setTagInfo(root->tagInfo());
7563 mmd->setLanguage(root->lang);
7564 mmd->setId(root->id);
7565 if (!isGlobal) mmd->setMemberClass(cd); else mmd->setFileDef(fd);
7566 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7567 mmd->setBodyDef(root->fileDef());
7568 mmd->setMemberSpecifiers(root->spec);
7569 mmd->setVhdlSpecifiers(root->vhdlSpec);
7570 mmd->setEnumBaseType(root->args);
7571 //printf("Enum %s definition at line %d of %s: protection=%d scope=%s\n",
7572 // qPrint(root->name),root->bodyLine,qPrint(root->fileName),root->protection,cd?qPrint(cd->name()):"<none>");
7573 mmd->addSectionsToDefinition(root->anchors);
7574 mmd->setMemberGroupId(root->mGrpId);
7576 mmd->addQualifiers(root->qualifiers);
7577 //printf("%s::setRefItems(%zu)\n",qPrint(md->name()),root->sli.size());
7578 mmd->setRefItems(root->sli);
7579 //printf("found enum %s nd=%p\n",qPrint(md->name()),nd);
7580 bool defSet=FALSE;
7581
7582 QCString baseType = root->args;
7583 if (!baseType.isEmpty())
7584 {
7585 baseType.prepend(" : ");
7586 }
7587
7588 if (nd)
7589 {
7590 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES))
7591 {
7592 mmd->setDefinition(name+baseType);
7593 }
7594 else
7595 {
7596 mmd->setDefinition(nd->name()+"::"+name+baseType);
7597 }
7598 //printf("definition=%s\n",md->definition());
7599 defSet=TRUE;
7600 mmd->setNamespace(nd);
7601 nd->insertMember(md.get());
7602 }
7603
7604 // even if we have already added the enum to a namespace, we still
7605 // also want to add it to other appropriate places such as file
7606 // or class.
7607 if (isGlobal && (nd==nullptr || !nd->isAnonymous()))
7608 {
7609 if (!defSet) mmd->setDefinition(name+baseType);
7610 if (fd==nullptr && root->parent())
7611 {
7612 fd=root->parent()->fileDef();
7613 }
7614 if (fd)
7615 {
7616 mmd->setFileDef(fd);
7617 fd->insertMember(md.get());
7618 }
7619 }
7620 else if (cd)
7621 {
7622 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES))
7623 {
7624 mmd->setDefinition(name+baseType);
7625 }
7626 else
7627 {
7628 mmd->setDefinition(cd->name()+"::"+name+baseType);
7629 }
7630 cd->insertMember(md.get());
7631 cd->insertUsedFile(fd);
7632 }
7633 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7634 mmd->setDocsForDefinition(!root->proto);
7635 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7636 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7637
7638 //printf("Adding member=%s\n",qPrint(md->name()));
7639 addMemberToGroups(root,md.get());
7641
7642 MemberName *mn = mnsd->add(name);
7643 mn->push_back(std::move(md));
7644 }
7645 }
7646 else
7647 {
7648 for (const auto &e : root->children()) findEnums(e.get());
7649 }
7650}
7651
7652//----------------------------------------------------------------------
7653
7654static void addEnumValuesToEnums(const Entry *root)
7655{
7656 if (root->section.isEnum())
7657 // non anonymous enumeration
7658 {
7659 AUTO_TRACE("name={}",root->name);
7660 ClassDefMutable *cd = nullptr;
7661 FileDef *fd = nullptr;
7662 NamespaceDefMutable *nd = nullptr;
7663 MemberNameLinkedMap *mnsd = nullptr;
7664 bool isGlobal = false;
7665 bool isRelated = false;
7666 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7667
7668 QCString name;
7669 QCString scope;
7670
7671 int i = root->name.findRev("::");
7672 if (i!=-1) // scope is specified
7673 {
7674 scope=root->name.left(i); // extract scope
7675 if (root->lang==SrcLangExt::CSharp)
7676 {
7677 scope = mangleCSharpGenericName(scope);
7678 }
7679 name=root->name.right(root->name.length()-i-2); // extract name
7680 if ((cd=getClassMutable(scope))==nullptr)
7681 {
7683 }
7684 }
7685 else // no scope, check the scope in which the docs where found
7686 {
7687 if (root->parent()->section.isScope() && !root->parent()->name.isEmpty()) // found enum docs inside a compound
7688 {
7689 scope=root->parent()->name;
7690 if (root->lang==SrcLangExt::CSharp)
7691 {
7692 scope = mangleCSharpGenericName(scope);
7693 }
7694 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7695 }
7696 name=root->name;
7697 }
7698
7699 if (!root->relates.isEmpty())
7700 { // related member, prefix user specified scope
7701 isRelated=TRUE;
7702 if (getClassMutable(root->relates)==nullptr && !scope.isEmpty())
7703 scope=mergeScopes(scope,root->relates);
7704 else
7705 scope=root->relates;
7706 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7707 }
7708
7709 if (cd && !name.isEmpty()) // found a enum inside a compound
7710 {
7711 //printf("Enum in class '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7712 fd=nullptr;
7714 isGlobal=false;
7715 }
7716 else if (nd && !nd->isAnonymous()) // found enum inside namespace
7717 {
7718 //printf("Enum in namespace '%s'::'%s'\n",qPrint(nd->name()),qPrint(name));
7720 isGlobal=true;
7721 }
7722 else // found a global enum
7723 {
7724 fd=root->fileDef();
7725 //printf("Enum in file '%s': '%s'\n",qPrint(fd->name()),qPrint(name));
7727 isGlobal=true;
7728 }
7729
7730 if (!name.isEmpty())
7731 {
7732 //printf("** name=%s\n",qPrint(name));
7733 MemberName *mn = mnsd->find(name); // for all members with this name
7734 if (mn)
7735 {
7736 struct EnumValueInfo
7737 {
7738 EnumValueInfo(const QCString &n,std::unique_ptr<MemberDef> &&md) :
7739 name(n), member(std::move(md)) {}
7740 QCString name;
7741 std::unique_ptr<MemberDef> member;
7742 };
7743 std::vector< EnumValueInfo > extraMembers;
7744 // for each enum in this list
7745 for (const auto &imd : *mn)
7746 {
7747 MemberDefMutable *md = toMemberDefMutable(imd.get());
7748 // use raw pointer in this loop, since we modify mn and can then invalidate mdp.
7749 if (md && md->isEnumerate() && !root->children().empty())
7750 {
7751 AUTO_TRACE_ADD("enum {} with {} children",md->name(),root->children().size());
7752 for (const auto &e : root->children())
7753 {
7754 SrcLangExt sle = root->lang;
7755 bool isJavaLike = sle==SrcLangExt::CSharp || sle==SrcLangExt::Java || sle==SrcLangExt::XML;
7756 if ( isJavaLike || root->spec.isStrong())
7757 {
7758 if (sle == SrcLangExt::Cpp && e->section.isDefine()) continue;
7759 // Unlike classic C/C++ enums, for C++11, C# & Java enum
7760 // values are only visible inside the enum scope, so we must create
7761 // them here and only add them to the enum
7762 //printf("md->qualifiedName()=%s e->name=%s tagInfo=%p name=%s\n",
7763 // qPrint(md->qualifiedName()),qPrint(e->name),(void*)e->tagInfo(),qPrint(e->name));
7764 QCString qualifiedName = root->name;
7765 i = qualifiedName.findRev("::");
7766 if (i!=-1 && sle==SrcLangExt::CSharp)
7767 {
7768 qualifiedName = mangleCSharpGenericName(qualifiedName.left(i))+qualifiedName.mid(i);
7769 }
7770 if (isJavaLike)
7771 {
7772 qualifiedName=substitute(qualifiedName,"::",".");
7773 }
7774 if (md->qualifiedName()==qualifiedName) // enum value scope matches that of the enum
7775 {
7776 QCString fileName = e->fileName;
7777 if (fileName.isEmpty() && e->tagInfo())
7778 {
7779 fileName = e->tagInfo()->tagName;
7780 }
7781 AUTO_TRACE_ADD("strong enum value {}",e->name);
7782 auto fmd = createMemberDef(
7783 fileName,e->startLine,e->startColumn,
7784 e->type,e->name,e->args,QCString(),
7785 e->protection, Specifier::Normal,e->isStatic,Relationship::Member,
7787 auto fmmd = toMemberDefMutable(fmd.get());
7788 NamespaceDef *mnd = md->getNamespaceDef();
7789 if (md->getClassDef())
7790 fmmd->setMemberClass(md->getClassDef());
7791 else if (mnd && (mnd->isLinkable() || mnd->isAnonymous()))
7792 fmmd->setNamespace(mnd);
7793 else if (md->getFileDef())
7794 fmmd->setFileDef(md->getFileDef());
7795 fmmd->setOuterScope(md->getOuterScope());
7796 fmmd->setTagInfo(e->tagInfo());
7797 fmmd->setLanguage(e->lang);
7798 fmmd->setBodySegment(e->startLine,e->bodyLine,e->endBodyLine);
7799 fmmd->setBodyDef(e->fileDef());
7800 fmmd->setId(e->id);
7801 fmmd->setDocumentation(e->doc,e->docFile,e->docLine);
7802 fmmd->setBriefDescription(e->brief,e->briefFile,e->briefLine);
7803 fmmd->addSectionsToDefinition(e->anchors);
7804 fmmd->setInitializer(e->initializer.str());
7805 fmmd->setMaxInitLines(e->initLines);
7806 fmmd->setMemberGroupId(e->mGrpId);
7807 fmmd->setExplicitExternal(e->explicitExternal,fileName,e->startLine,e->startColumn);
7808 fmmd->setRefItems(e->sli);
7809 fmmd->setAnchor();
7810 md->insertEnumField(fmd.get());
7811 fmmd->setEnumScope(md,TRUE);
7812 extraMembers.emplace_back(e->name,std::move(fmd));
7813 }
7814 }
7815 else
7816 {
7817 AUTO_TRACE_ADD("enum value {}",e->name);
7818 //printf("e->name=%s isRelated=%d\n",qPrint(e->name),isRelated);
7819 MemberName *fmn=nullptr;
7820 MemberNameLinkedMap *emnsd = isRelated ? Doxygen::functionNameLinkedMap : mnsd;
7821 if (!e->name.isEmpty() && (fmn=emnsd->find(e->name)))
7822 // get list of members with the same name as the field
7823 {
7824 for (const auto &ifmd : *fmn)
7825 {
7826 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
7827 if (fmd && fmd->isEnumValue() && fmd->getOuterScope()==md->getOuterScope()) // in same scope
7828 {
7829 //printf("found enum value with same name %s in scope %s\n",
7830 // qPrint(fmd->name()),qPrint(fmd->getOuterScope()->name()));
7831 if (nd && !nd->isAnonymous())
7832 {
7833 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
7834 {
7835 const NamespaceDef *fnd=fmd->getNamespaceDef();
7836 if (fnd==nd) // enum value is inside a namespace
7837 {
7838 md->insertEnumField(fmd);
7839 fmd->setEnumScope(md);
7840 }
7841 }
7842 }
7843 else if (isGlobal)
7844 {
7845 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
7846 {
7847 const FileDef *ffd=fmd->getFileDef();
7848 if (ffd==fd && ffd==md->getFileDef()) // enum value has file scope
7849 {
7850 md->insertEnumField(fmd);
7851 fmd->setEnumScope(md);
7852 }
7853 }
7854 }
7855 else if (isRelated && cd) // reparent enum value to
7856 // match the enum's scope
7857 {
7858 md->insertEnumField(fmd); // add field def to list
7859 fmd->setEnumScope(md); // cross ref with enum name
7860 fmd->setEnumClassScope(cd); // cross ref with enum name
7861 fmd->setOuterScope(cd);
7862 fmd->makeRelated();
7863 cd->insertMember(fmd);
7864 }
7865 else
7866 {
7867 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
7868 {
7869 const ClassDef *fcd=fmd->getClassDef();
7870 if (fcd==cd) // enum value is inside a class
7871 {
7872 //printf("Inserting enum field %s in enum scope %s\n",
7873 // qPrint(fmd->name()),qPrint(md->name()));
7874 md->insertEnumField(fmd); // add field def to list
7875 fmd->setEnumScope(md); // cross ref with enum name
7876 }
7877 }
7878 }
7879 }
7880 }
7881 }
7882 }
7883 }
7884 }
7885 }
7886 // move the newly added members into mn
7887 for (auto &e : extraMembers)
7888 {
7889 MemberName *emn=mnsd->add(e.name);
7890 emn->push_back(std::move(e.member));
7891 }
7892 }
7893 }
7894 }
7895 else
7896 {
7897 for (const auto &e : root->children()) addEnumValuesToEnums(e.get());
7898 }
7899}
7900
7901//----------------------------------------------------------------------
7902
7903static void addEnumDocs(const Entry *root,MemberDefMutable *md)
7904{
7905 AUTO_TRACE();
7906 // documentation outside a compound overrides the documentation inside it
7907 {
7908 md->setDocumentation(root->doc,root->docFile,root->docLine);
7909 md->setDocsForDefinition(!root->proto);
7910 }
7911
7912 // brief descriptions inside a compound override the documentation
7913 // outside it
7914 {
7915 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7916 }
7917
7918 if (md->inbodyDocumentation().isEmpty() || !root->parent()->name.isEmpty())
7919 {
7921 }
7922
7923 if (root->mGrpId!=-1 && md->getMemberGroupId()==-1)
7924 {
7925 md->setMemberGroupId(root->mGrpId);
7926 }
7927
7929 md->setRefItems(root->sli);
7930
7931 const GroupDef *gd=md->getGroupDef();
7932 if (gd==nullptr && !root->groups.empty()) // member not grouped but out-of-line documentation is
7933 {
7934 addMemberToGroups(root,md);
7935 }
7937}
7938
7939//----------------------------------------------------------------------
7940// Search for the name in the associated groups. If a matching member
7941// definition exists, then add the documentation to it and return TRUE,
7942// otherwise FALSE.
7943
7944static bool tryAddEnumDocsToGroupMember(const Entry *root,const QCString &name)
7945{
7946 for (const auto &g : root->groups)
7947 {
7948 const GroupDef *gd = Doxygen::groupLinkedMap->find(g.groupname);
7949 if (gd)
7950 {
7951 MemberList *ml = gd->getMemberList(MemberListType::DecEnumMembers());
7952 if (ml)
7953 {
7954 MemberDefMutable *md = toMemberDefMutable(ml->find(name));
7955 if (md)
7956 {
7957 addEnumDocs(root,md);
7958 return TRUE;
7959 }
7960 }
7961 }
7962 else if (!gd && g.pri == Grouping::GROUPING_INGROUP)
7963 {
7964 warn(root->fileName, root->startLine,
7965 "Found non-existing group '{}' for the command '{}', ignoring command",
7966 g.groupname, Grouping::getGroupPriName( g.pri )
7967 );
7968 }
7969 }
7970
7971 return FALSE;
7972}
7973
7974//----------------------------------------------------------------------
7975// find the documentation blocks for the enumerations
7976
7977static void findEnumDocumentation(const Entry *root)
7978{
7979 if (root->section.isEnumDoc() &&
7980 !root->name.isEmpty() &&
7981 root->name.at(0)!='@' // skip anonymous enums
7982 )
7983 {
7984 QCString name;
7985 QCString scope;
7986 int i = root->name.findRev("::");
7987 if (i!=-1) // scope is specified as part of the name
7988 {
7989 name=root->name.right(root->name.length()-i-2); // extract name
7990 scope=root->name.left(i); // extract scope
7991 //printf("Scope='%s' Name='%s'\n",qPrint(scope),qPrint(name));
7992 }
7993 else // just the name
7994 {
7995 name=root->name;
7996 }
7997 if (root->parent()->section.isScope() && !root->parent()->name.isEmpty()) // found enum docs inside a compound
7998 {
7999 if (!scope.isEmpty()) scope.prepend("::");
8000 scope.prepend(root->parent()->name);
8001 }
8002 const ClassDef *cd = getClass(scope);
8003 const NamespaceDef *nd=Doxygen::namespaceLinkedMap->find(scope);
8004 const FileDef *fd = root->fileDef();
8005 AUTO_TRACE("Found docs for enum with name '{}' and scope '{}' in context '{}' cd='{}', nd='{}' fd='{}'",
8006 name,scope,root->parent()->name,
8007 cd ? cd->name() : QCString("<none>"),
8008 nd ? nd->name() : QCString("<none>"),
8009 fd ? fd->name() : QCString("<none>"));
8010
8011 if (!name.isEmpty())
8012 {
8013 bool found = tryAddEnumDocsToGroupMember(root, name);
8014 if (!found)
8015 {
8016 MemberName *mn = cd ? Doxygen::memberNameLinkedMap->find(name) : Doxygen::functionNameLinkedMap->find(name);
8017 if (mn)
8018 {
8019 for (const auto &imd : *mn)
8020 {
8021 MemberDefMutable *md = toMemberDefMutable(imd.get());
8022 if (md && md->isEnumerate())
8023 {
8024 const ClassDef *mcd = md->getClassDef();
8025 const NamespaceDef *mnd = md->getNamespaceDef();
8026 const FileDef *mfd = md->getFileDef();
8027 if (cd && mcd==cd)
8028 {
8029 AUTO_TRACE_ADD("Match found for class scope");
8030 addEnumDocs(root,md);
8031 found = TRUE;
8032 break;
8033 }
8034 else if (cd==nullptr && mcd==nullptr && nd!=nullptr && mnd==nd)
8035 {
8036 AUTO_TRACE_ADD("Match found for namespace scope");
8037 addEnumDocs(root,md);
8038 found = TRUE;
8039 break;
8040 }
8041 else if (cd==nullptr && nd==nullptr && mcd==nullptr && mnd==nullptr && fd==mfd)
8042 {
8043 AUTO_TRACE_ADD("Match found for global scope");
8044 addEnumDocs(root,md);
8045 found = TRUE;
8046 break;
8047 }
8048 }
8049 }
8050 }
8051 }
8052 if (!found)
8053 {
8054 warn(root->fileName,root->startLine, "Documentation for undefined enum '{}' found.", name);
8055 }
8056 }
8057 }
8058 for (const auto &e : root->children()) findEnumDocumentation(e.get());
8059}
8060
8061// search for each enum (member or function) in mnl if it has documented
8062// enum values.
8063static void findDEV(const MemberNameLinkedMap &mnsd)
8064{
8065 // for each member name
8066 for (const auto &mn : mnsd)
8067 {
8068 // for each member definition
8069 for (const auto &imd : *mn)
8070 {
8071 MemberDefMutable *md = toMemberDefMutable(imd.get());
8072 if (md && md->isEnumerate()) // member is an enum
8073 {
8074 int documentedEnumValues=0;
8075 // for each enum value
8076 for (const auto &fmd : md->enumFieldList())
8077 {
8078 if (fmd->isLinkableInProject()) documentedEnumValues++;
8079 }
8080 // at least one enum value is documented
8081 if (documentedEnumValues>0) md->setDocumentedEnumValues(TRUE);
8082 }
8083 }
8084 }
8085}
8086
8087// search for each enum (member or function) if it has documented enum
8088// values.
8094
8095//----------------------------------------------------------------------
8096
8098{
8099 auto &index = Index::instance();
8100 // for each class member name
8101 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8102 {
8103 // for each member definition
8104 for (const auto &md : *mn)
8105 {
8106 index.addClassMemberNameToIndex(md.get());
8107 if (md->getModuleDef())
8108 {
8109 index.addModuleMemberNameToIndex(md.get());
8110 }
8111 }
8112 }
8113 // for each file/namespace function name
8114 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8115 {
8116 // for each member definition
8117 for (const auto &md : *mn)
8118 {
8119 if (md->getNamespaceDef())
8120 {
8121 index.addNamespaceMemberNameToIndex(md.get());
8122 }
8123 else
8124 {
8125 index.addFileMemberNameToIndex(md.get());
8126 }
8127 if (md->getModuleDef())
8128 {
8129 index.addModuleMemberNameToIndex(md.get());
8130 }
8131 }
8132 }
8133
8134 index.sortMemberIndexLists();
8135}
8136
8137//----------------------------------------------------------------------
8138
8139static void addToIndices()
8140{
8141 for (const auto &cd : *Doxygen::classLinkedMap)
8142 {
8143 if (cd->isLinkableInProject())
8144 {
8145 Doxygen::indexList->addIndexItem(cd.get(),nullptr);
8146 if (Doxygen::searchIndex.enabled())
8147 {
8148 Doxygen::searchIndex.setCurrentDoc(cd.get(),cd->anchor(),FALSE);
8149 Doxygen::searchIndex.addWord(cd->localName(),TRUE);
8150 }
8151 }
8152 }
8153
8154 for (const auto &cd : *Doxygen::conceptLinkedMap)
8155 {
8156 if (cd->isLinkableInProject())
8157 {
8158 Doxygen::indexList->addIndexItem(cd.get(),nullptr);
8159 if (Doxygen::searchIndex.enabled())
8160 {
8161 Doxygen::searchIndex.setCurrentDoc(cd.get(),cd->anchor(),FALSE);
8162 Doxygen::searchIndex.addWord(cd->localName(),TRUE);
8163 }
8164 }
8165 }
8166
8167 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8168 {
8169 if (nd->isLinkableInProject())
8170 {
8171 Doxygen::indexList->addIndexItem(nd.get(),nullptr);
8172 if (Doxygen::searchIndex.enabled())
8173 {
8174 Doxygen::searchIndex.setCurrentDoc(nd.get(),nd->anchor(),FALSE);
8175 Doxygen::searchIndex.addWord(nd->localName(),TRUE);
8176 }
8177 }
8178 }
8179
8180 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8181 {
8182 for (const auto &fd : *fn)
8183 {
8184 if (Doxygen::searchIndex.enabled() && fd->isLinkableInProject())
8185 {
8186 Doxygen::searchIndex.setCurrentDoc(fd.get(),fd->anchor(),FALSE);
8187 Doxygen::searchIndex.addWord(fd->localName(),TRUE);
8188 }
8189 }
8190 }
8191
8192 auto addWordsForTitle = [](const Definition *d,const QCString &anchor,const QCString &title)
8193 {
8194 Doxygen::indexList->addIndexItem(d,nullptr,QCString(),filterTitle(title));
8195 if (Doxygen::searchIndex.enabled())
8196 {
8197 Doxygen::searchIndex.setCurrentDoc(d,anchor,false);
8198 std::string s = title.str();
8199 static const reg::Ex re(R"(\a[\w-]*)");
8200 reg::Iterator it(s,re);
8202 for (; it!=end ; ++it)
8203 {
8204 const auto &match = *it;
8205 std::string matchStr = match.str();
8206 Doxygen::searchIndex.addWord(matchStr,true);
8207 }
8208 }
8209 };
8210
8211 for (const auto &gd : *Doxygen::groupLinkedMap)
8212 {
8213 if (gd->isLinkableInProject())
8214 {
8215 addWordsForTitle(gd.get(),gd->anchor(),gd->groupTitle());
8216 }
8217 }
8218
8219 for (const auto &pd : *Doxygen::pageLinkedMap)
8220 {
8221 if (pd->isLinkableInProject())
8222 {
8223 addWordsForTitle(pd.get(),pd->anchor(),pd->title());
8224 }
8225 }
8226
8228 {
8229 addWordsForTitle(Doxygen::mainPage.get(),Doxygen::mainPage->anchor(),Doxygen::mainPage->title());
8230 }
8231
8232 auto addMemberToSearchIndex = [](const MemberDef *md)
8233 {
8234 if (Doxygen::searchIndex.enabled())
8235 {
8236 Doxygen::searchIndex.setCurrentDoc(md,md->anchor(),FALSE);
8237 QCString ln=md->localName();
8238 QCString qn=md->qualifiedName();
8239 Doxygen::searchIndex.addWord(ln,TRUE);
8240 if (ln!=qn)
8241 {
8242 Doxygen::searchIndex.addWord(qn,TRUE);
8243 if (md->getClassDef())
8244 {
8245 Doxygen::searchIndex.addWord(md->getClassDef()->displayName(),TRUE);
8246 }
8247 if (md->getNamespaceDef())
8248 {
8249 Doxygen::searchIndex.addWord(md->getNamespaceDef()->displayName(),TRUE);
8250 }
8251 }
8252 }
8253 };
8254
8255 auto getScope = [](const MemberDef *md)
8256 {
8257 const Definition *scope = nullptr;
8258 if (md->getGroupDef()) scope = md->getGroupDef();
8259 else if (md->getClassDef()) scope = md->getClassDef();
8260 else if (md->getNamespaceDef()) scope = md->getNamespaceDef();
8261 else if (md->getFileDef()) scope = md->getFileDef();
8262 return scope;
8263 };
8264
8265 auto addMemberToIndices = [addMemberToSearchIndex,getScope](const MemberDef *md)
8266 {
8267 if (md->isLinkableInProject())
8268 {
8269 if (!(md->isEnumerate() && md->isAnonymous()))
8270 {
8271 Doxygen::indexList->addIndexItem(getScope(md),md);
8273 }
8274 if (md->isEnumerate())
8275 {
8276 for (const auto &fmd : md->enumFieldList())
8277 {
8278 Doxygen::indexList->addIndexItem(getScope(fmd),fmd);
8280 }
8281 }
8282 }
8283 };
8284
8285 // for each class member name
8286 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8287 {
8288 // for each member definition
8289 for (const auto &md : *mn)
8290 {
8291 addMemberToIndices(md.get());
8292 }
8293 }
8294 // for each file/namespace function name
8295 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8296 {
8297 // for each member definition
8298 for (const auto &md : *mn)
8299 {
8300 addMemberToIndices(md.get());
8301 }
8302 }
8303}
8304
8305//----------------------------------------------------------------------
8306
8308{
8309 // for each member name
8310 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8311 {
8312 // for each member definition
8313 for (const auto &imd : *mn)
8314 {
8315 MemberDefMutable *md = toMemberDefMutable(imd.get());
8316 if (md)
8317 {
8319 }
8320 }
8321 }
8322 // for each member name
8323 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8324 {
8325 // for each member definition
8326 for (const auto &imd : *mn)
8327 {
8328 MemberDefMutable *md = toMemberDefMutable(imd.get());
8329 if (md)
8330 {
8332 }
8333 }
8334 }
8335}
8336
8337// recursive helper function looking for reimplements/implemented
8338// by relations between class cd and direct or indirect base class bcd
8340{
8341 for (const auto &mn : cd->memberNameInfoLinkedMap()) // for each member in class cd with a unique name
8342 {
8343 for (const auto &imd : *mn) // for each member with a given name
8344 {
8345 MemberDefMutable *md = toMemberDefMutable(imd->memberDef());
8346 if (md && (md->isFunction() || md->isCSharpProperty())) // filter on reimplementable members
8347 {
8348 ClassDef *mbcd = bcd->classDef;
8349 if (mbcd && mbcd->isLinkable()) // filter on linkable classes
8350 {
8351 const auto &bmn = mbcd->memberNameInfoLinkedMap();
8352 const auto &bmni = bmn.find(mn->memberName());
8353 if (bmni) // there are base class members with the same name
8354 {
8355 for (const auto &ibmd : *bmni) // for base class member with that name
8356 {
8357 MemberDefMutable *bmd = toMemberDefMutable(ibmd->memberDef());
8358 if (bmd) // not part of an inline namespace
8359 {
8360 auto lang = bmd->getLanguage();
8361 auto compType = mbcd->compoundType();
8362 if (bmd->virtualness()!=Specifier::Normal ||
8363 lang==SrcLangExt::Python ||
8364 lang==SrcLangExt::Java ||
8365 lang==SrcLangExt::PHP ||
8366 compType==ClassDef::Interface ||
8367 compType==ClassDef::Protocol)
8368 {
8369 const ArgumentList &bmdAl = bmd->argumentList();
8370 const ArgumentList &mdAl = md->argumentList();
8371 //printf(" Base argList='%s'\n Super argList='%s'\n",
8372 // qPrint(argListToString(bmdAl)),
8373 // qPrint(argListToString(mdAl))
8374 // );
8375 if (
8376 lang==SrcLangExt::Python ||
8377 matchArguments2(bmd->getOuterScope(),bmd->getFileDef(),&bmdAl,
8378 md->getOuterScope(), md->getFileDef(), &mdAl,
8379 TRUE,lang
8380 )
8381 )
8382 {
8383 if (lang==SrcLangExt::Python && md->name().startsWith("__")) continue; // private members do not reimplement
8384 //printf("match!\n");
8385 const MemberDef *rmd = md->reimplements();
8386 if (rmd==nullptr) // not already assigned
8387 {
8388 //printf("%s: setting (new) reimplements member %s\n",qPrint(md->qualifiedName()),qPrint(bmd->qualifiedName()));
8389 md->setReimplements(bmd);
8390 }
8391 //printf("%s: add reimplementedBy member %s\n",qPrint(bmd->qualifiedName()),qPrint(md->qualifiedName()));
8392 bmd->insertReimplementedBy(md);
8393 }
8394 else
8395 {
8396 //printf("no match!\n");
8397 }
8398 }
8399 }
8400 }
8401 }
8402 }
8403 }
8404 }
8405 }
8406
8407 // do also for indirect base classes
8408 for (const auto &bbcd : bcd->classDef->baseClasses())
8409 {
8411 }
8412}
8413
8414//----------------------------------------------------------------------
8415// computes the relation between all members. For each member 'm'
8416// the members that override the implementation of 'm' are searched and
8417// the member that 'm' overrides is searched.
8418
8420{
8421 for (const auto &cd : *Doxygen::classLinkedMap)
8422 {
8423 if (cd->isLinkable())
8424 {
8425 for (const auto &bcd : cd->baseClasses())
8426 {
8428 }
8429 }
8430 }
8431}
8432
8433//----------------------------------------------------------------------------
8434
8436{
8437 // for each class
8438 for (const auto &cd : *Doxygen::classLinkedMap)
8439 {
8440 // that is a template
8441 for (const auto &ti : cd->getTemplateInstances())
8442 {
8443 ClassDefMutable *tcdm = toClassDefMutable(ti.classDef);
8444 if (tcdm)
8445 {
8446 tcdm->addMembersToTemplateInstance(cd.get(),cd->templateArguments(),ti.templSpec);
8447 }
8448 }
8449 }
8450}
8451
8452//----------------------------------------------------------------------------
8453
8454static void mergeCategories()
8455{
8456 AUTO_TRACE();
8457 // merge members of categories into the class they extend
8458 for (const auto &cd : *Doxygen::classLinkedMap)
8459 {
8460 int i=cd->name().find('(');
8461 if (i!=-1) // it is an Objective-C category
8462 {
8463 QCString baseName=cd->name().left(i);
8464 ClassDefMutable *baseClass=toClassDefMutable(Doxygen::classLinkedMap->find(baseName));
8465 if (baseClass)
8466 {
8467 AUTO_TRACE_ADD("merging members of category {} into {}",cd->name(),baseClass->name());
8468 baseClass->mergeCategory(cd.get());
8469 }
8470 }
8471 }
8472}
8473
8474// builds the list of all members for each class
8475
8477{
8478 // merge the member list of base classes into the inherited classes.
8479 for (const auto &cd : *Doxygen::classLinkedMap)
8480 {
8481 if (// !cd->isReference() && // not an external class
8482 cd->subClasses().empty() && // is a root of the hierarchy
8483 !cd->baseClasses().empty()) // and has at least one base class
8484 {
8485 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8486 if (cdm)
8487 {
8488 //printf("*** merging members for %s\n",qPrint(cd->name()));
8489 cdm->mergeMembers();
8490 }
8491 }
8492 }
8493 // now sort the member list of all members for all classes.
8494 for (const auto &cd : *Doxygen::classLinkedMap)
8495 {
8496 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8497 if (cdm)
8498 {
8499 cdm->sortAllMembersList();
8500 }
8501 }
8502}
8503
8504//----------------------------------------------------------------------------
8505
8507{
8508 auto processSourceFile = [](FileDef *fd,OutputList &ol,ClangTUParser *parser)
8509 {
8510 bool showSources = fd->generateSourceFile() && !Htags::useHtags; // sources need to be shown in the output
8511 bool parseSources = !fd->isReference() && Doxygen::parseSourcesNeeded; // we needed to parse the sources even if we do not show them
8512 if (showSources)
8513 {
8514 msg("Generating code for file {}...\n",fd->docName());
8515 fd->writeSourceHeader(ol);
8516 fd->writeSourceBody(ol,parser);
8517 fd->writeSourceFooter(ol);
8518 }
8519 else if (parseSources)
8520 {
8521 msg("Parsing code for file {}...\n",fd->docName());
8522 fd->parseSource(parser);
8523 }
8524 };
8525 if (!Doxygen::inputNameLinkedMap->empty())
8526 {
8527#if USE_LIBCLANG
8529 {
8530 StringUnorderedSet processedFiles;
8531
8532 // create a dictionary with files to process
8533 StringUnorderedSet filesToProcess;
8534
8535 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8536 {
8537 for (const auto &fd : *fn)
8538 {
8539 filesToProcess.insert(fd->absFilePath().str());
8540 }
8541 }
8542 // process source files (and their include dependencies)
8543 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8544 {
8545 for (const auto &fd : *fn)
8546 {
8547 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt::Cpp &&
8548 (fd->generateSourceFile() ||
8550 )
8551 )
8552 {
8553 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8554 clangParser->parse();
8555 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8556
8557 for (auto incFile : clangParser->filesInSameTU())
8558 {
8559 if (filesToProcess.find(incFile)!=filesToProcess.end() && // part of input
8560 fd->absFilePath()!=incFile && // not same file
8561 processedFiles.find(incFile)==processedFiles.end()) // not yet marked as processed
8562 {
8563 StringVector moreFiles;
8564 bool ambig = false;
8566 if (ifd && !ifd->isReference())
8567 {
8568 processSourceFile(ifd,*g_outputList,clangParser.get());
8569 processedFiles.insert(incFile);
8570 }
8571 }
8572 }
8573 processedFiles.insert(fd->absFilePath().str());
8574 }
8575 }
8576 }
8577 // process remaining files
8578 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8579 {
8580 for (const auto &fd : *fn)
8581 {
8582 if (processedFiles.find(fd->absFilePath().str())==processedFiles.end()) // not yet processed
8583 {
8584 if (fd->getLanguage()==SrcLangExt::Cpp) // C/C++ file, use clang parser
8585 {
8586 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8587 clangParser->parse();
8588 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8589 }
8590 else // non C/C++ file, use built-in parser
8591 {
8592 processSourceFile(fd.get(),*g_outputList,nullptr);
8593 }
8594 }
8595 }
8596 }
8597 }
8598 else
8599#endif
8600 {
8601 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8602 if (numThreads>1)
8603 {
8604 msg("Generating code files using {} threads.\n",numThreads);
8605 struct SourceContext
8606 {
8607 SourceContext(FileDef *fd_,bool gen_,const OutputList &ol_)
8608 : fd(fd_), generateSourceFile(gen_), ol(ol_) {}
8609 FileDef *fd;
8610 bool generateSourceFile;
8611 OutputList ol;
8612 };
8613 ThreadPool threadPool(numThreads);
8614 std::vector< std::future< std::shared_ptr<SourceContext> > > results;
8615 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8616 {
8617 for (const auto &fd : *fn)
8618 {
8619 bool generateSourceFile = fd->generateSourceFile() && !Htags::useHtags;
8620 auto ctx = std::make_shared<SourceContext>(fd.get(),generateSourceFile,*g_outputList);
8621 auto processFile = [ctx]()
8622 {
8623 if (ctx->generateSourceFile)
8624 {
8625 msg("Generating code for file {}...\n",ctx->fd->docName());
8626 }
8627 else
8628 {
8629 msg("Parsing code for file {}...\n",ctx->fd->docName());
8630 }
8631 StringVector filesInSameTu;
8632 ctx->fd->getAllIncludeFilesRecursively(filesInSameTu);
8633 if (ctx->generateSourceFile) // sources need to be shown in the output
8634 {
8635 ctx->fd->writeSourceHeader(ctx->ol);
8636 ctx->fd->writeSourceBody(ctx->ol,nullptr);
8637 ctx->fd->writeSourceFooter(ctx->ol);
8638 }
8639 else if (!ctx->fd->isReference() && Doxygen::parseSourcesNeeded)
8640 // we needed to parse the sources even if we do not show them
8641 {
8642 ctx->fd->parseSource(nullptr);
8643 }
8644 return ctx;
8645 };
8646 results.emplace_back(threadPool.queue(processFile));
8647 }
8648 }
8649 for (auto &f : results)
8650 {
8651 auto ctx = f.get();
8652 }
8653 }
8654 else // single threaded version
8655 {
8656 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8657 {
8658 for (const auto &fd : *fn)
8659 {
8660 StringVector filesInSameTu;
8661 fd->getAllIncludeFilesRecursively(filesInSameTu);
8662 processSourceFile(fd.get(),*g_outputList,nullptr);
8663 }
8664 }
8665 }
8666 }
8667 }
8668}
8669
8670//----------------------------------------------------------------------------
8671
8672static void generateFileDocs()
8673{
8674 if (Index::instance().numDocumentedFiles()==0) return;
8675
8676 if (!Doxygen::inputNameLinkedMap->empty())
8677 {
8678 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8679 if (numThreads>1) // multi threaded processing
8680 {
8681 struct DocContext
8682 {
8683 DocContext(FileDef *fd_,const OutputList &ol_)
8684 : fd(fd_), ol(ol_) {}
8685 FileDef *fd;
8686 OutputList ol;
8687 };
8688 ThreadPool threadPool(numThreads);
8689 std::vector< std::future< std::shared_ptr<DocContext> > > results;
8690 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8691 {
8692 for (const auto &fd : *fn)
8693 {
8694 bool doc = fd->isLinkableInProject();
8695 if (doc)
8696 {
8697 auto ctx = std::make_shared<DocContext>(fd.get(),*g_outputList);
8698 auto processFile = [ctx]() {
8699 msg("Generating docs for file {}...\n",ctx->fd->docName());
8700 ctx->fd->writeDocumentation(ctx->ol);
8701 return ctx;
8702 };
8703 results.emplace_back(threadPool.queue(processFile));
8704 }
8705 }
8706 }
8707 for (auto &f : results)
8708 {
8709 auto ctx = f.get();
8710 }
8711 }
8712 else // single threaded processing
8713 {
8714 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8715 {
8716 for (const auto &fd : *fn)
8717 {
8718 bool doc = fd->isLinkableInProject();
8719 if (doc)
8720 {
8721 msg("Generating docs for file {}...\n",fd->docName());
8722 fd->writeDocumentation(*g_outputList);
8723 }
8724 }
8725 }
8726 }
8727 }
8728}
8729
8730//----------------------------------------------------------------------------
8731
8733{
8734 // add source references for class definitions
8735 for (const auto &cd : *Doxygen::classLinkedMap)
8736 {
8737 const FileDef *fd=cd->getBodyDef();
8738 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8739 {
8740 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),nullptr);
8741 }
8742 }
8743 // add source references for concept definitions
8744 for (const auto &cd : *Doxygen::conceptLinkedMap)
8745 {
8746 const FileDef *fd=cd->getBodyDef();
8747 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8748 {
8749 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),nullptr);
8750 }
8751 }
8752 // add source references for namespace definitions
8753 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8754 {
8755 const FileDef *fd=nd->getBodyDef();
8756 if (fd && nd->isLinkableInProject() && nd->getStartDefLine()!=-1)
8757 {
8758 const_cast<FileDef*>(fd)->addSourceRef(nd->getStartDefLine(),nd.get(),nullptr);
8759 }
8760 }
8761
8762 // add source references for member names
8763 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8764 {
8765 for (const auto &md : *mn)
8766 {
8767 //printf("class member %s: def=%s body=%d link?=%d\n",
8768 // qPrint(md->name()),
8769 // md->getBodyDef()?qPrint(md->getBodyDef()->name()):"<none>",
8770 // md->getStartBodyLine(),md->isLinkableInProject());
8771 const FileDef *fd=md->getBodyDef();
8772 if (fd &&
8773 md->getStartDefLine()!=-1 &&
8774 md->isLinkableInProject() &&
8776 )
8777 {
8778 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8779 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8780 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8781 }
8782 }
8783 }
8784 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8785 {
8786 for (const auto &md : *mn)
8787 {
8788 const FileDef *fd=md->getBodyDef();
8789 //printf("member %s body=[%d,%d] fd=%p link=%d parseSources=%d\n",
8790 // qPrint(md->name()),
8791 // md->getStartBodyLine(),md->getEndBodyLine(),fd,
8792 // md->isLinkableInProject(),
8793 // Doxygen::parseSourcesNeeded);
8794 if (fd &&
8795 md->getStartDefLine()!=-1 &&
8796 md->isLinkableInProject() &&
8798 )
8799 {
8800 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8801 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8802 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8803 }
8804 }
8805 }
8806}
8807
8808//----------------------------------------------------------------------------
8809
8810// add the macro definitions found during preprocessing as file members
8811static void buildDefineList()
8812{
8813 AUTO_TRACE();
8814 for (const auto &s : g_inputFiles)
8815 {
8816 auto it = Doxygen::macroDefinitions.find(s);
8818 {
8819 for (const auto &def : it->second)
8820 {
8821 auto md = createMemberDef(
8822 def.fileName,def.lineNr,def.columnNr,
8823 "#define",def.name,def.args,QCString(),
8824 Protection::Public,Specifier::Normal,FALSE,Relationship::Member,MemberType::Define,
8825 ArgumentList(),ArgumentList(),"");
8826 auto mmd = toMemberDefMutable(md.get());
8827
8828 if (!def.args.isEmpty())
8829 {
8830 mmd->moveArgumentList(stringToArgumentList(SrcLangExt::Cpp, def.args));
8831 }
8832 mmd->setInitializer(def.definition);
8833 mmd->setFileDef(def.fileDef);
8834 mmd->setDefinition("#define "+def.name);
8835
8836 MemberName *mn=Doxygen::functionNameLinkedMap->add(def.name);
8837 if (def.fileDef)
8838 {
8839 const MemberList *defMl = def.fileDef->getMemberList(MemberListType::DocDefineMembers());
8840 if (defMl)
8841 {
8842 const MemberDef *defMd = defMl->findRev(def.name);
8843 if (defMd) // definition already stored
8844 {
8845 mmd->setRedefineCount(defMd->redefineCount()+1);
8846 }
8847 }
8848 def.fileDef->insertMember(md.get());
8849 }
8850 AUTO_TRACE_ADD("adding macro {} with definition {}",def.name,def.definition);
8851 mn->push_back(std::move(md));
8852 }
8853 }
8854 }
8855}
8856
8857//----------------------------------------------------------------------------
8858
8859static void sortMemberLists()
8860{
8861 // sort class member lists
8862 for (const auto &cd : *Doxygen::classLinkedMap)
8863 {
8864 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8865 if (cdm)
8866 {
8867 cdm->sortMemberLists();
8868 }
8869 }
8870
8871 // sort namespace member lists
8872 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8873 {
8875 if (ndm)
8876 {
8877 ndm->sortMemberLists();
8878 }
8879 }
8880
8881 // sort file member lists
8882 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8883 {
8884 for (const auto &fd : *fn)
8885 {
8886 fd->sortMemberLists();
8887 }
8888 }
8889
8890 // sort group member lists
8891 for (const auto &gd : *Doxygen::groupLinkedMap)
8892 {
8893 gd->sortMemberLists();
8894 }
8895
8897}
8898
8899//----------------------------------------------------------------------------
8900
8901static bool isSymbolHidden(const Definition *d)
8902{
8903 bool hidden = d->isHidden();
8904 const Definition *parent = d->getOuterScope();
8905 return parent ? hidden || isSymbolHidden(parent) : hidden;
8906}
8907
8909{
8910 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8911 if (numThreads>1)
8912 {
8913 ThreadPool threadPool(numThreads);
8914 std::vector < std::future< void > > results;
8915 // queue the work
8916 for (const auto &[name,symList] : *Doxygen::symbolMap)
8917 {
8918 for (const auto &def : symList)
8919 {
8921 if (dm && !isSymbolHidden(def) && !def->isArtificial() && def->isLinkableInProject())
8922 {
8923 auto processTooltip = [dm]() {
8924 dm->computeTooltip();
8925 };
8926 results.emplace_back(threadPool.queue(processTooltip));
8927 }
8928 }
8929 }
8930 // wait for the results
8931 for (auto &f : results)
8932 {
8933 f.get();
8934 }
8935 }
8936 else
8937 {
8938 for (const auto &[name,symList] : *Doxygen::symbolMap)
8939 {
8940 for (const auto &def : symList)
8941 {
8943 if (dm && !isSymbolHidden(def) && !def->isArtificial() && def->isLinkableInProject())
8944 {
8945 dm->computeTooltip();
8946 }
8947 }
8948 }
8949 }
8950}
8951
8952//----------------------------------------------------------------------------
8953
8955{
8956 for (const auto &cd : *Doxygen::classLinkedMap)
8957 {
8958 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8959 if (cdm)
8960 {
8961 cdm->setAnonymousEnumType();
8962 }
8963 }
8964}
8965
8966//----------------------------------------------------------------------------
8967
8968static void countMembers()
8969{
8970 for (const auto &cd : *Doxygen::classLinkedMap)
8971 {
8972 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8973 if (cdm)
8974 {
8975 cdm->countMembers();
8976 }
8977 }
8978
8979 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8980 {
8982 if (ndm)
8983 {
8984 ndm->countMembers();
8985 }
8986 }
8987
8988 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8989 {
8990 for (const auto &fd : *fn)
8991 {
8992 fd->countMembers();
8993 }
8994 }
8995
8996 for (const auto &gd : *Doxygen::groupLinkedMap)
8997 {
8998 gd->countMembers();
8999 }
9000
9001 auto &mm = ModuleManager::instance();
9002 mm.countMembers();
9003}
9004
9005
9006//----------------------------------------------------------------------------
9007// generate the documentation for all classes
9008
9009static void generateDocsForClassList(const std::vector<ClassDefMutable*> &classList)
9010{
9011 AUTO_TRACE();
9012 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9013 if (numThreads>1) // multi threaded processing
9014 {
9015 struct DocContext
9016 {
9017 DocContext(ClassDefMutable *cd_,const OutputList &ol_)
9018 : cd(cd_), ol(ol_) {}
9019 ClassDefMutable *cd;
9020 OutputList ol;
9021 };
9022 ThreadPool threadPool(numThreads);
9023 std::vector< std::future< std::shared_ptr<DocContext> > > results;
9024 for (const auto &cd : classList)
9025 {
9026 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
9027 if (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9028 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
9029 )
9030 {
9031 auto ctx = std::make_shared<DocContext>(cd,*g_outputList);
9032 auto processFile = [ctx]()
9033 {
9034 msg("Generating docs for compound {}...\n",ctx->cd->displayName());
9035
9036 // skip external references, anonymous compounds and
9037 // template instances
9038 if (!ctx->cd->isHidden() && !ctx->cd->isEmbeddedInOuterScope() &&
9039 ctx->cd->isLinkableInProject() && !ctx->cd->isImplicitTemplateInstance())
9040 {
9041 ctx->cd->writeDocumentation(ctx->ol);
9042 ctx->cd->writeMemberList(ctx->ol);
9043 }
9044
9045 // even for undocumented classes, the inner classes can be documented.
9046 ctx->cd->writeDocumentationForInnerClasses(ctx->ol);
9047 return ctx;
9048 };
9049 results.emplace_back(threadPool.queue(processFile));
9050 }
9051 }
9052 for (auto &f : results)
9053 {
9054 auto ctx = f.get();
9055 }
9056 }
9057 else // single threaded processing
9058 {
9059 for (const auto &cd : classList)
9060 {
9061 //printf("cd=%s getOuterScope=%p global=%p hidden=%d embeddedInOuterScope=%d\n",
9062 // qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope,cd->isHidden(),cd->isEmbeddedInOuterScope());
9063 if (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9064 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
9065 )
9066 {
9067 // skip external references, anonymous compounds and
9068 // template instances
9069 if ( !cd->isHidden() && !cd->isEmbeddedInOuterScope() &&
9070 cd->isLinkableInProject() && !cd->isImplicitTemplateInstance())
9071 {
9072 msg("Generating docs for compound {}...\n",cd->displayName());
9073
9074 cd->writeDocumentation(*g_outputList);
9075 cd->writeMemberList(*g_outputList);
9076 }
9077 // even for undocumented classes, the inner classes can be documented.
9078 cd->writeDocumentationForInnerClasses(*g_outputList);
9079 }
9080 }
9081 }
9082}
9083
9084static void addClassAndNestedClasses(std::vector<ClassDefMutable*> &list,ClassDefMutable *cd)
9085{
9086 list.push_back(cd);
9087 for (const auto &innerCdi : cd->getClasses())
9088 {
9089 ClassDefMutable *innerCd = toClassDefMutable(innerCdi);
9090 if (innerCd)
9091 {
9092 AUTO_TRACE("innerCd={} isLinkable={} isImplicitTemplateInstance={} protectLevelVisible={} embeddedInOuterScope={}",
9093 innerCd->name(),innerCd->isLinkableInProject(),innerCd->isImplicitTemplateInstance(),protectionLevelVisible(innerCd->protection()),
9094 innerCd->isEmbeddedInOuterScope());
9095 }
9096 if (innerCd && innerCd->isLinkableInProject() && !innerCd->isImplicitTemplateInstance() &&
9097 protectionLevelVisible(innerCd->protection()) &&
9098 !innerCd->isEmbeddedInOuterScope()
9099 )
9100 {
9101 list.push_back(innerCd);
9102 addClassAndNestedClasses(list,innerCd);
9103 }
9104 }
9105}
9106
9108{
9109 std::vector<ClassDefMutable*> classList;
9110 for (const auto &cdi : *Doxygen::classLinkedMap)
9111 {
9112 ClassDefMutable *cd = toClassDefMutable(cdi.get());
9113 if (cd && (cd->getOuterScope()==nullptr ||
9115 {
9116 addClassAndNestedClasses(classList,cd);
9117 }
9118 }
9119 for (const auto &cdi : *Doxygen::hiddenClassLinkedMap)
9120 {
9121 ClassDefMutable *cd = toClassDefMutable(cdi.get());
9122 if (cd && (cd->getOuterScope()==nullptr ||
9124 {
9125 addClassAndNestedClasses(classList,cd);
9126 }
9127 }
9128 generateDocsForClassList(classList);
9129}
9130
9131//----------------------------------------------------------------------------
9132
9134{
9135 for (const auto &cdi : *Doxygen::conceptLinkedMap)
9136 {
9138
9139 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
9140 if (cd &&
9141 (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9142 cd->getOuterScope()==Doxygen::globalScope // only look at global concepts
9143 ) && !cd->isHidden() && cd->isLinkableInProject()
9144 )
9145 {
9146 msg("Generating docs for concept {}...\n",cd->displayName());
9148 }
9149 }
9150}
9151
9152//----------------------------------------------------------------------------
9153
9155{
9156 for (const auto &mn : *Doxygen::memberNameLinkedMap)
9157 {
9158 for (const auto &imd : *mn)
9159 {
9160 MemberDefMutable *md = toMemberDefMutable(imd.get());
9161 //static int count=0;
9162 //printf("%04d Member '%s'\n",count++,qPrint(md->qualifiedName()));
9163 if (md && md->documentation().isEmpty() && md->briefDescription().isEmpty())
9164 { // no documentation yet
9165 const MemberDef *bmd = md->reimplements();
9166 while (bmd && bmd->documentation().isEmpty() &&
9167 bmd->briefDescription().isEmpty()
9168 )
9169 { // search up the inheritance tree for a documentation member
9170 //printf("bmd=%s class=%s\n",qPrint(bmd->name()),qPrint(bmd->getClassDef()->name()));
9171 bmd = bmd->reimplements();
9172 }
9173 if (bmd) // copy the documentation from the reimplemented member
9174 {
9175 md->setInheritsDocsFrom(bmd);
9176 md->setDocumentation(bmd->documentation(),bmd->docFile(),bmd->docLine());
9178 md->setBriefDescription(bmd->briefDescription(),bmd->briefFile(),bmd->briefLine());
9179 md->copyArgumentNames(bmd);
9181 }
9182 }
9183 }
9184 }
9185}
9186
9187//----------------------------------------------------------------------------
9188
9190{
9191 // for each file
9192 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9193 {
9194 for (const auto &fd : *fn)
9195 {
9196 fd->combineUsingRelations();
9197 }
9198 }
9199
9200 // for each namespace
9201 NamespaceDefSet visitedNamespaces;
9202 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9203 {
9205 if (ndm)
9206 {
9207 ndm->combineUsingRelations(visitedNamespaces);
9208 }
9209 }
9210}
9211
9212//----------------------------------------------------------------------------
9213
9215{
9216 // for each class
9217 for (const auto &cd : *Doxygen::classLinkedMap)
9218 {
9219 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9220 if (cdm)
9221 {
9223 }
9224 }
9225 // for each file
9226 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9227 {
9228 for (const auto &fd : *fn)
9229 {
9230 fd->addMembersToMemberGroup();
9231 }
9232 }
9233 // for each namespace
9234 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9235 {
9237 if (ndm)
9238 {
9240 }
9241 }
9242 // for each group
9243 for (const auto &gd : *Doxygen::groupLinkedMap)
9244 {
9245 gd->addMembersToMemberGroup();
9246 }
9248}
9249
9250//----------------------------------------------------------------------------
9251
9253{
9254 // for each class
9255 for (const auto &cd : *Doxygen::classLinkedMap)
9256 {
9257 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9258 if (cdm)
9259 {
9261 }
9262 }
9263 // for each file
9264 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9265 {
9266 for (const auto &fd : *fn)
9267 {
9268 fd->distributeMemberGroupDocumentation();
9269 }
9270 }
9271 // for each namespace
9272 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9273 {
9275 if (ndm)
9276 {
9278 }
9279 }
9280 // for each group
9281 for (const auto &gd : *Doxygen::groupLinkedMap)
9282 {
9283 gd->distributeMemberGroupDocumentation();
9284 }
9286}
9287
9288//----------------------------------------------------------------------------
9289
9291{
9292 // for each class
9293 for (const auto &cd : *Doxygen::classLinkedMap)
9294 {
9295 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9296 if (cdm)
9297 {
9299 }
9300 }
9301 // for each concept
9302 for (const auto &cd : *Doxygen::conceptLinkedMap)
9303 {
9304 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
9305 if (cdm)
9306 {
9308 }
9309 }
9310 // for each file
9311 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9312 {
9313 for (const auto &fd : *fn)
9314 {
9315 fd->findSectionsInDocumentation();
9316 }
9317 }
9318 // for each namespace
9319 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9320 {
9322 if (ndm)
9323 {
9325 }
9326 }
9327 // for each group
9328 for (const auto &gd : *Doxygen::groupLinkedMap)
9329 {
9330 gd->findSectionsInDocumentation();
9331 }
9332 // for each page
9333 for (const auto &pd : *Doxygen::pageLinkedMap)
9334 {
9335 pd->findSectionsInDocumentation();
9336 }
9337 // for each directory
9338 for (const auto &dd : *Doxygen::dirLinkedMap)
9339 {
9340 dd->findSectionsInDocumentation();
9341 }
9343 if (Doxygen::mainPage) Doxygen::mainPage->findSectionsInDocumentation();
9344}
9345
9346//----------------------------------------------------------------------
9347
9348
9350{
9351 // remove all references to classes from the cache
9352 // as there can be new template instances in the inheritance path
9353 // to this class. Optimization: only remove those classes that
9354 // have inheritance instances as direct or indirect sub classes.
9355 StringVector elementsToRemove;
9356 for (const auto &ci : *Doxygen::typeLookupCache)
9357 {
9358 const LookupInfo &li = ci.second;
9359 if (li.definition)
9360 {
9361 elementsToRemove.push_back(ci.first);
9362 }
9363 }
9364 for (const auto &k : elementsToRemove)
9365 {
9366 Doxygen::typeLookupCache->remove(k);
9367 }
9368
9369 // remove all cached typedef resolutions whose target is a
9370 // template class as this may now be a template instance
9371 // for each global function name
9372 for (const auto &fn : *Doxygen::functionNameLinkedMap)
9373 {
9374 // for each function with that name
9375 for (const auto &ifmd : *fn)
9376 {
9377 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
9378 if (fmd && fmd->isTypedefValCached())
9379 {
9380 const ClassDef *cd = fmd->getCachedTypedefVal();
9381 if (cd->isTemplate()) fmd->invalidateTypedefValCache();
9382 }
9383 }
9384 }
9385 // for each class method name
9386 for (const auto &nm : *Doxygen::memberNameLinkedMap)
9387 {
9388 // for each function with that name
9389 for (const auto &imd : *nm)
9390 {
9391 MemberDefMutable *md = toMemberDefMutable(imd.get());
9392 if (md && md->isTypedefValCached())
9393 {
9394 const ClassDef *cd = md->getCachedTypedefVal();
9395 if (cd->isTemplate()) md->invalidateTypedefValCache();
9396 }
9397 }
9398 }
9399}
9400
9401//----------------------------------------------------------------------------
9402
9404{
9405 // Remove all unresolved references to classes from the cache.
9406 // This is needed before resolving the inheritance relations, since
9407 // it would otherwise not find the inheritance relation
9408 // for C in the example below, as B::I was already found to be unresolvable
9409 // (which is correct if you ignore the inheritance relation between A and B).
9410 //
9411 // class A { class I {} };
9412 // class B : public A {};
9413 // class C : public B::I {};
9414
9415 StringVector elementsToRemove;
9416 for (const auto &ci : *Doxygen::typeLookupCache)
9417 {
9418 const LookupInfo &li = ci.second;
9419 if (li.definition==nullptr && li.typeDef==nullptr)
9420 {
9421 elementsToRemove.push_back(ci.first);
9422 }
9423 }
9424 for (const auto &k : elementsToRemove)
9425 {
9426 Doxygen::typeLookupCache->remove(k);
9427 }
9428
9429 // for each global function name
9430 for (const auto &fn : *Doxygen::functionNameLinkedMap)
9431 {
9432 // for each function with that name
9433 for (const auto &ifmd : *fn)
9434 {
9435 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
9436 if (fmd)
9437 {
9439 }
9440 }
9441 }
9442 // for each class method name
9443 for (const auto &nm : *Doxygen::memberNameLinkedMap)
9444 {
9445 // for each function with that name
9446 for (const auto &imd : *nm)
9447 {
9448 MemberDefMutable *md = toMemberDefMutable(imd.get());
9449 if (md)
9450 {
9452 }
9453 }
9454 }
9455
9456}
9457
9458//----------------------------------------------------------------------------
9459// Returns TRUE if the entry and member definition have equal file names,
9460// otherwise FALSE.
9461
9462static bool haveEqualFileNames(const Entry *root, const MemberDef *md)
9463{
9464 if (const FileDef *fd = md->getFileDef())
9465 {
9466 return fd->absFilePath() == root->fileName;
9467 }
9468 return false;
9469}
9470
9471//----------------------------------------------------------------------------
9472
9473static void addDefineDoc(const Entry *root, MemberDefMutable *md)
9474{
9475 md->setDocumentation(root->doc,root->docFile,root->docLine);
9476 md->setDocsForDefinition(!root->proto);
9477 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9478 if (md->inbodyDocumentation().isEmpty())
9479 {
9481 }
9482 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
9483 {
9484 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
9485 md->setBodyDef(root->fileDef());
9486 }
9488 md->setMaxInitLines(root->initLines);
9490 md->setRefItems(root->sli);
9491 md->addQualifiers(root->qualifiers);
9492 if (root->mGrpId!=-1) md->setMemberGroupId(root->mGrpId);
9493 addMemberToGroups(root,md);
9495}
9496
9497//----------------------------------------------------------------------------
9498
9500{
9501 if ((root->section.isDefineDoc() || root->section.isDefine()) && !root->name.isEmpty())
9502 {
9503 //printf("found define '%s' '%s' brief='%s' doc='%s'\n",
9504 // qPrint(root->name),qPrint(root->args),qPrint(root->brief),qPrint(root->doc));
9505
9506 if (root->tagInfo() && !root->name.isEmpty()) // define read from a tag file
9507 {
9508 auto md = createMemberDef(root->tagInfo()->tagName,1,1,
9509 "#define",root->name,root->args,QCString(),
9510 Protection::Public,Specifier::Normal,FALSE,Relationship::Member,MemberType::Define,
9511 ArgumentList(),ArgumentList(),"");
9512 auto mmd = toMemberDefMutable(md.get());
9513 mmd->setTagInfo(root->tagInfo());
9514 mmd->setLanguage(root->lang);
9515 mmd->addQualifiers(root->qualifiers);
9516 //printf("Searching for '%s' fd=%p\n",qPrint(filePathName),fd);
9517 mmd->setFileDef(root->parent()->fileDef());
9518 //printf("Adding member=%s\n",qPrint(md->name()));
9520 mn->push_back(std::move(md));
9521 }
9523 if (mn)
9524 {
9525 int count=0;
9526 for (const auto &md : *mn)
9527 {
9528 if (md->memberType()==MemberType::Define) count++;
9529 }
9530 if (count==1)
9531 {
9532 for (const auto &imd : *mn)
9533 {
9534 MemberDefMutable *md = toMemberDefMutable(imd.get());
9535 if (md && md->memberType()==MemberType::Define)
9536 {
9537 addDefineDoc(root,md);
9538 }
9539 }
9540 }
9541 else if (count>1 &&
9542 (!root->doc.isEmpty() ||
9543 !root->brief.isEmpty() ||
9544 root->bodyLine!=-1
9545 )
9546 )
9547 // multiple defines don't know where to add docs
9548 // but maybe they are in different files together with their documentation
9549 {
9550 for (const auto &imd : *mn)
9551 {
9552 MemberDefMutable *md = toMemberDefMutable(imd.get());
9553 if (md && md->memberType()==MemberType::Define)
9554 {
9555 if (haveEqualFileNames(root, md) || isEntryInGroupOfMember(root, md))
9556 // doc and define in the same file or group assume they belong together.
9557 {
9558 addDefineDoc(root,md);
9559 }
9560 }
9561 }
9562 //warn("define {} found in the following files:\n",root->name);
9563 //warn("Cannot determine where to add the documentation found "
9564 // "at line {} of file {}. \n",
9565 // root->startLine,root->fileName);
9566 }
9567 }
9568 else if (!root->doc.isEmpty() || !root->brief.isEmpty()) // define not found
9569 {
9570 bool preEnabled = Config_getBool(ENABLE_PREPROCESSING);
9571 if (preEnabled)
9572 {
9573 warn(root->fileName,root->startLine,"documentation for unknown define {} found.",root->name);
9574 }
9575 else
9576 {
9577 warn(root->fileName,root->startLine, "found documented #define {} but ignoring it because ENABLE_PREPROCESSING is NO.", root->name);
9578 }
9579 }
9580 }
9581 for (const auto &e : root->children()) findDefineDocumentation(e.get());
9582}
9583
9584//----------------------------------------------------------------------------
9585
9586static void findDirDocumentation(const Entry *root)
9587{
9588 if (root->section.isDirDoc())
9589 {
9590 QCString normalizedName = root->name;
9591 normalizedName = substitute(normalizedName,"\\","/");
9592 //printf("root->docFile=%s normalizedName=%s\n",
9593 // qPrint(root->docFile),qPrint(normalizedName));
9594 if (root->docFile==normalizedName) // current dir?
9595 {
9596 int lastSlashPos=normalizedName.findRev('/');
9597 if (lastSlashPos!=-1) // strip file name
9598 {
9599 normalizedName=normalizedName.left(lastSlashPos);
9600 }
9601 }
9602 if (normalizedName.at(normalizedName.length()-1)!='/')
9603 {
9604 normalizedName+='/';
9605 }
9606 DirDef *matchingDir=nullptr;
9607 for (const auto &dir : *Doxygen::dirLinkedMap)
9608 {
9609 //printf("Dir: %s<->%s\n",qPrint(dir->name()),qPrint(normalizedName));
9610 if (dir->name().right(normalizedName.length())==normalizedName)
9611 {
9612 if (matchingDir)
9613 {
9614 warn(root->fileName,root->startLine,
9615 "\\dir command matches multiple directories.\n"
9616 " Applying the command for directory {}\n"
9617 " Ignoring the command for directory {}",
9618 matchingDir->name(),dir->name()
9619 );
9620 }
9621 else
9622 {
9623 matchingDir=dir.get();
9624 }
9625 }
9626 }
9627 if (matchingDir)
9628 {
9629 //printf("Match for with dir %s #anchor=%zu\n",qPrint(matchingDir->name()),root->anchors.size());
9630 matchingDir->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9631 matchingDir->setDocumentation(root->doc,root->docFile,root->docLine);
9632 matchingDir->setRefItems(root->sli);
9633 matchingDir->addSectionsToDefinition(root->anchors);
9634 root->commandOverrides.apply_directoryGraph([&](bool b) { matchingDir->overrideDirectoryGraph(b); });
9635 addDirToGroups(root,matchingDir);
9636 }
9637 else
9638 {
9639 warn(root->fileName,root->startLine,"No matching directory found for command \\dir {}",normalizedName);
9640 }
9641 }
9642 for (const auto &e : root->children()) findDirDocumentation(e.get());
9643}
9644
9645
9646//----------------------------------------------------------------------------
9647// create a (sorted) list of separate documentation pages
9648
9649static void buildPageList(Entry *root)
9650{
9651 if (root->section.isPageDoc())
9652 {
9653 if (!root->name.isEmpty())
9654 {
9655 addRelatedPage(root);
9656 }
9657 }
9658 else if (root->section.isMainpageDoc())
9659 {
9660 QCString title=root->args.stripWhiteSpace();
9661 if (title.isEmpty()) title=theTranslator->trMainPage();
9662 //QCString name = Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9663 QCString name = "index";
9664 addRefItem(root->sli,
9665 name,
9666 "page",
9667 name,
9668 title,
9669 QCString(),nullptr
9670 );
9671 }
9672 for (const auto &e : root->children()) buildPageList(e.get());
9673}
9674
9675// search for the main page defined in this project
9676static void findMainPage(Entry *root)
9677{
9678 if (root->section.isMainpageDoc())
9679 {
9680 if (Doxygen::mainPage==nullptr && root->tagInfo()==nullptr)
9681 {
9682 //printf("mainpage: docLine=%d startLine=%d\n",root->docLine,root->startLine);
9683 //printf("Found main page! \n======\n%s\n=======\n",qPrint(root->doc));
9684 QCString title=root->args.stripWhiteSpace();
9685 if (title.isEmpty()) title = Config_getString(PROJECT_NAME);
9686 //QCString indexName=Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9687 QCString indexName="index";
9689 indexName, root->brief+root->doc+root->inbodyDocs,title);
9690 //setFileNameForSections(root->anchors,"index",Doxygen::mainPage);
9691 Doxygen::mainPage->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9692 Doxygen::mainPage->setBodySegment(root->startLine,root->startLine,-1);
9693 Doxygen::mainPage->setFileName(indexName);
9694 Doxygen::mainPage->setLocalToc(root->localToc);
9696
9698 if (si)
9699 {
9700 if (!si->ref().isEmpty()) // we are from a tag file
9701 {
9702 // a page name is a label as well! but should no be double either
9704 Doxygen::mainPage->name(),
9705 indexName,
9706 root->startLine,
9707 Doxygen::mainPage->title(),
9709 0); // level 0
9710 }
9711 else if (si->lineNr() != -1)
9712 {
9713 warn(root->fileName,root->startLine,"multiple use of section label '{}' for main page, (first occurrence: {}, line {})",
9714 Doxygen::mainPage->name(),si->fileName(),si->lineNr());
9715 }
9716 else
9717 {
9718 warn(root->fileName,root->startLine,"multiple use of section label '{}' for main page, (first occurrence: {})",
9719 Doxygen::mainPage->name(),si->fileName());
9720 }
9721 }
9722 else
9723 {
9724 // a page name is a label as well! but should no be double either
9726 Doxygen::mainPage->name(),
9727 indexName,
9728 root->startLine,
9729 Doxygen::mainPage->title(),
9731 0); // level 0
9732 }
9733 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9734 }
9735 else if (root->tagInfo()==nullptr)
9736 {
9737 warn(root->fileName,root->startLine,
9738 "found more than one \\mainpage comment block! (first occurrence: {}, line {}), Skipping current block!",
9739 Doxygen::mainPage->docFile(),Doxygen::mainPage->getStartBodyLine());
9740 }
9741 }
9742 for (const auto &e : root->children()) findMainPage(e.get());
9743}
9744
9745// search for the main page imported via tag files and add only the section labels
9746static void findMainPageTagFiles(Entry *root)
9747{
9748 if (root->section.isMainpageDoc())
9749 {
9750 if (Doxygen::mainPage && root->tagInfo())
9751 {
9752 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9753 }
9754 }
9755 for (const auto &e : root->children()) findMainPageTagFiles(e.get());
9756}
9757
9758static void computePageRelations(Entry *root)
9759{
9760 if ((root->section.isPageDoc() || root->section.isMainpageDoc()) && !root->name.isEmpty())
9761 {
9762 PageDef *pd = root->section.isPageDoc() ?
9763 Doxygen::pageLinkedMap->find(root->name) :
9764 Doxygen::mainPage.get();
9765 if (pd)
9766 {
9767 for (const BaseInfo &bi : root->extends)
9768 {
9769 PageDef *subPd = Doxygen::pageLinkedMap->find(bi.name);
9770 if (pd==subPd)
9771 {
9772 term("page defined {} with label {} is a direct "
9773 "subpage of itself! Please remove this cyclic dependency.\n",
9774 warn_line(pd->docFile(),pd->docLine()),pd->name());
9775 }
9776 else if (subPd)
9777 {
9778 pd->addInnerCompound(subPd);
9779 //printf("*** Added subpage relation: %s->%s\n",
9780 // qPrint(pd->name()),qPrint(subPd->name()));
9781 }
9782 }
9783 }
9784 }
9785 for (const auto &e : root->children()) computePageRelations(e.get());
9786}
9787
9789{
9790 for (const auto &pd : *Doxygen::pageLinkedMap)
9791 {
9792 Definition *ppd = pd->getOuterScope();
9793 while (ppd)
9794 {
9795 if (ppd==pd.get())
9796 {
9797 term("page defined {} with label {} is a subpage "
9798 "of itself! Please remove this cyclic dependency.\n",
9799 warn_line(pd->docFile(),pd->docLine()),pd->name());
9800 }
9801 ppd=ppd->getOuterScope();
9802 }
9803 }
9804}
9805
9806//----------------------------------------------------------------------------
9807
9809{
9810 for (const auto &si : SectionManager::instance())
9811 {
9812 //printf("si->label='%s' si->definition=%s si->fileName='%s'\n",
9813 // qPrint(si->label),si->definition?qPrint(si->definition->name()):"<none>",
9814 // qPrint(si->fileName));
9815 PageDef *pd=nullptr;
9816
9817 // hack: the items of a todo/test/bug/deprecated list are all fragments from
9818 // different files, so the resulting section's all have the wrong file
9819 // name (not from the todo/test/bug/deprecated list, but from the file in
9820 // which they are defined). We correct this here by looking at the
9821 // generated section labels!
9823 {
9824 QCString label="_"+rl->listName(); // "_todo", "_test", ...
9825 if (si->label().left(label.length())==label)
9826 {
9827 si->setFileName(rl->listName());
9828 si->setGenerated(TRUE);
9829 break;
9830 }
9831 }
9832
9833 //printf("start: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
9834 if (!si->generated())
9835 {
9836 // if this section is in a page and the page is in a group, then we
9837 // have to adjust the link file name to point to the group.
9838 if (!si->fileName().isEmpty() &&
9839 (pd=Doxygen::pageLinkedMap->find(si->fileName())) &&
9840 pd->getGroupDef())
9841 {
9842 si->setFileName(pd->getGroupDef()->getOutputFileBase());
9843 }
9844
9845 if (si->definition())
9846 {
9847 // TODO: there should be one function in Definition that returns
9848 // the file to link to, so we can avoid the following tests.
9849 const GroupDef *gd=nullptr;
9850 if (si->definition()->definitionType()==Definition::TypeMember)
9851 {
9852 gd = (toMemberDef(si->definition()))->getGroupDef();
9853 }
9854
9855 if (gd)
9856 {
9857 si->setFileName(gd->getOutputFileBase());
9858 }
9859 else
9860 {
9861 //si->fileName=si->definition->getOutputFileBase();
9862 //printf("Setting si->fileName to %s\n",qPrint(si->fileName));
9863 }
9864 }
9865 }
9866 //printf("end: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
9867 }
9868}
9869
9870
9871
9872//----------------------------------------------------------------------------
9873// generate all separate documentation pages
9874
9875
9876static void generatePageDocs()
9877{
9878 //printf("documentedPages=%d real=%d\n",documentedPages,Doxygen::pageLinkedMap->count());
9879 if (Index::instance().numDocumentedPages()==0) return;
9880 for (const auto &pd : *Doxygen::pageLinkedMap)
9881 {
9882 if (!pd->getGroupDef() && !pd->isReference())
9883 {
9884 msg("Generating docs for page {}...\n",pd->name());
9885 pd->writeDocumentation(*g_outputList);
9886 }
9887 }
9888}
9889
9890//----------------------------------------------------------------------------
9891// create a (sorted) list & dictionary of example pages
9892
9893static void buildExampleList(Entry *root)
9894{
9895 if ((root->section.isExample() || root->section.isExampleLineno()) && !root->name.isEmpty())
9896 {
9897 if (Doxygen::exampleLinkedMap->find(root->name))
9898 {
9899 warn(root->fileName,root->startLine,"Example {} was already documented. Ignoring documentation found here.",root->name);
9900 }
9901 else
9902 {
9903 PageDef *pd = Doxygen::exampleLinkedMap->add(root->name,
9904 createPageDef(root->fileName,root->startLine,
9905 root->name,root->brief+root->doc+root->inbodyDocs,root->args));
9906 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9907 pd->setFileName(convertNameToFile(pd->name()+"-example",FALSE,TRUE));
9909 pd->setLanguage(root->lang);
9910 pd->setShowLineNo(root->section.isExampleLineno());
9911
9912 //we don't add example to groups
9913 //addExampleToGroups(root,pd);
9914 }
9915 }
9916 for (const auto &e : root->children()) buildExampleList(e.get());
9917}
9918
9919//----------------------------------------------------------------------------
9920// prints the Entry tree (for debugging)
9921
9922void printNavTree(Entry *root,int indent)
9923{
9925 {
9926 QCString indentStr;
9927 indentStr.fill(' ',indent);
9928 Debug::print(Debug::Entries,0,"{}{} at {}:{} (sec={}, spec={})\n",
9929 indentStr.isEmpty()?"":indentStr,
9930 root->name.isEmpty()?"<empty>":root->name,
9931 root->fileName,root->startLine,
9932 root->section.to_string(),
9933 root->spec.to_string());
9934 for (const auto &e : root->children())
9935 {
9936 printNavTree(e.get(),indent+2);
9937 }
9938 }
9939}
9940
9941
9942//----------------------------------------------------------------------------
9943// prints the Sections tree (for debugging)
9944
9946{
9948 {
9949 for (const auto &si : SectionManager::instance())
9950 {
9951 Debug::print(Debug::Sections,0,"Section = {}, file = {}, title = {}, type = {}, ref = {}\n",
9952 si->label(),si->fileName(),si->title(),si->type().level(),si->ref());
9953 }
9954 }
9955}
9956
9957
9958//----------------------------------------------------------------------------
9959// generate the example documentation
9960
9962{
9963 g_outputList->disable(OutputType::Man);
9964 for (const auto &pd : *Doxygen::exampleLinkedMap)
9965 {
9966 msg("Generating docs for example {}...\n",pd->name());
9967 SrcLangExt lang = getLanguageFromFileName(pd->name(), SrcLangExt::Unknown);
9968 if (lang != SrcLangExt::Unknown)
9969 {
9970 QCString ext = getFileNameExtension(pd->name());
9971 auto intf = Doxygen::parserManager->getCodeParser(ext);
9972 intf->resetCodeParserState();
9973 }
9974 QCString n=pd->getOutputFileBase();
9975 startFile(*g_outputList,n,false,n,pd->name());
9977 g_outputList->docify(pd->name());
9979 g_outputList->startContents();
9980 QCString lineNoOptStr;
9981 if (pd->showLineNo())
9982 {
9983 lineNoOptStr="{lineno}";
9984 }
9985 g_outputList->generateDoc(pd->docFile(), // file
9986 pd->docLine(), // startLine
9987 pd.get(), // context
9988 nullptr, // memberDef
9989 (pd->briefDescription().isEmpty()?"":pd->briefDescription()+"\n\n")+
9990 pd->documentation()+"\n\n\\include"+lineNoOptStr+" "+pd->name(), // docs
9991 DocOptions()
9992 .setIndexWords(true)
9993 .setExample(pd->name()));
9994 endFile(*g_outputList); // contains g_outputList->endContents()
9995 }
9997}
9998
9999//----------------------------------------------------------------------------
10000// generate module pages
10001
10003{
10004 for (const auto &gd : *Doxygen::groupLinkedMap)
10005 {
10006 if (!gd->isReference())
10007 {
10008 gd->writeDocumentation(*g_outputList);
10009 }
10010 }
10011}
10012
10013//----------------------------------------------------------------------------
10014// generate module pages
10015
10017{
10018 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10019 if (numThreads>1) // multi threaded processing
10020 {
10021 struct DocContext
10022 {
10023 DocContext(ClassDefMutable *cdm_,const OutputList &ol_)
10024 : cdm(cdm_), ol(ol_) {}
10025 ClassDefMutable *cdm;
10026 OutputList ol;
10027 };
10028 ThreadPool threadPool(numThreads);
10029 std::vector< std::future< std::shared_ptr<DocContext> > > results;
10030 // for each class in the namespace...
10031 for (const auto &cd : classList)
10032 {
10034 if (cdm)
10035 {
10036 auto ctx = std::make_shared<DocContext>(cdm,*g_outputList);
10037 auto processFile = [ctx]()
10038 {
10039 if ( ( ctx->cdm->isLinkableInProject() &&
10040 !ctx->cdm->isImplicitTemplateInstance()
10041 ) // skip external references, anonymous compounds and
10042 // template instances and nested classes
10043 && !ctx->cdm->isHidden() && !ctx->cdm->isEmbeddedInOuterScope()
10044 )
10045 {
10046 msg("Generating docs for compound {}...\n",ctx->cdm->displayName());
10047 ctx->cdm->writeDocumentation(ctx->ol);
10048 ctx->cdm->writeMemberList(ctx->ol);
10049 }
10050 ctx->cdm->writeDocumentationForInnerClasses(ctx->ol);
10051 return ctx;
10052 };
10053 results.emplace_back(threadPool.queue(processFile));
10054 }
10055 }
10056 // wait for the results
10057 for (auto &f : results)
10058 {
10059 auto ctx = f.get();
10060 }
10061 }
10062 else // single threaded processing
10063 {
10064 // for each class in the namespace...
10065 for (const auto &cd : classList)
10066 {
10068 if (cdm)
10069 {
10070 if ( ( cd->isLinkableInProject() &&
10071 !cd->isImplicitTemplateInstance()
10072 ) // skip external references, anonymous compounds and
10073 // template instances and nested classes
10074 && !cd->isHidden() && !cd->isEmbeddedInOuterScope()
10075 )
10076 {
10077 msg("Generating docs for compound {}...\n",cd->displayName());
10078
10081 }
10083 }
10084 }
10085 }
10086}
10087
10089{
10090 // for each concept in the namespace...
10091 for (const auto &cd : conceptList)
10092 {
10094 if ( cdm && cd->isLinkableInProject() && !cd->isHidden())
10095 {
10096 msg("Generating docs for concept {}...\n",cd->name());
10098 }
10099 }
10100}
10101
10103{
10104 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
10105
10106 //writeNamespaceIndex(*g_outputList);
10107
10108 // for each namespace...
10109 for (const auto &nd : *Doxygen::namespaceLinkedMap)
10110 {
10111 if (nd->isLinkableInProject())
10112 {
10114 if (ndm)
10115 {
10116 msg("Generating docs for namespace {}\n",nd->displayName());
10118 }
10119 }
10120
10121 generateNamespaceClassDocs(nd->getClasses());
10122 if (sliceOpt)
10123 {
10124 generateNamespaceClassDocs(nd->getInterfaces());
10125 generateNamespaceClassDocs(nd->getStructs());
10126 generateNamespaceClassDocs(nd->getExceptions());
10127 }
10128 generateNamespaceConceptDocs(nd->getConcepts());
10129 }
10130}
10131
10133{
10134 std::string oldDir = Dir::currentDirPath();
10135 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
10138 {
10139 err("failed to run html help compiler on {}\n", HtmlHelp::hhpFileName);
10140 }
10141 Dir::setCurrent(oldDir);
10142}
10143
10145{
10146 QCString args = Qhp::qhpFileName + " -o \"" + Qhp::getQchFileName() + "\"";
10147 std::string oldDir = Dir::currentDirPath();
10148 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
10149
10150 QCString qhgLocation=Config_getString(QHG_LOCATION);
10151 if (Debug::isFlagSet(Debug::Qhp)) // produce info for debugging
10152 {
10153 // run qhelpgenerator -v and extract the Qt version used
10154 QCString cmd=qhgLocation+ " -v 2>&1";
10155 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
10156 FILE *f=Portable::popen(cmd,"r");
10157 if (!f)
10158 {
10159 err("could not execute {}\n",qhgLocation);
10160 }
10161 else
10162 {
10163 const size_t bufSize = 1024;
10164 char inBuf[bufSize+1];
10165 size_t numRead=fread(inBuf,1,bufSize,f);
10166 inBuf[numRead] = '\0';
10167 Debug::print(Debug::Qhp,0,"{}",inBuf);
10169
10170 int qtVersion=0;
10171 static const reg::Ex versionReg(R"(Qt (\d+)\.(\d+)\.(\d+))");
10172 reg::Match match;
10173 std::string s = inBuf;
10174 if (reg::search(inBuf,match,versionReg))
10175 {
10176 qtVersion = 10000*QCString(match[1].str()).toInt() +
10177 100*QCString(match[2].str()).toInt() +
10178 QCString(match[3].str()).toInt();
10179 }
10180 if (qtVersion>0 && (qtVersion<60000 || qtVersion >= 60205))
10181 {
10182 // dump the output of qhelpgenerator -c file.qhp
10183 // Qt<6 or Qt>=6.2.5 or higher, see https://bugreports.qt.io/browse/QTBUG-101070
10184 cmd=qhgLocation+ " -c " + Qhp::qhpFileName + " 2>&1";
10185 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
10186 f=Portable::popen(cmd,"r");
10187 if (!f)
10188 {
10189 err("could not execute {}\n",qhgLocation);
10190 }
10191 else
10192 {
10193 std::string output;
10194 while ((numRead=fread(inBuf,1,bufSize,f))>0)
10195 {
10196 inBuf[numRead] = '\0';
10197 output += inBuf;
10198 }
10200 Debug::print(Debug::Qhp,0,"{}",output);
10201 }
10202 }
10203 }
10204 }
10205
10206 if (Portable::system(qhgLocation, args, FALSE))
10207 {
10208 err("failed to run qhelpgenerator on {}\n",Qhp::qhpFileName);
10209 }
10210 Dir::setCurrent(oldDir);
10211}
10212
10213//----------------------------------------------------------------------------
10214
10216{
10217 // check dot path
10218 QCString dotPath = Config_getString(DOT_PATH);
10219 if (!dotPath.isEmpty())
10220 {
10221 FileInfo fi(dotPath.str());
10222 if (!(fi.exists() && fi.isFile()) )// not an existing user specified path + exec
10223 {
10224 dotPath = dotPath+"/dot"+Portable::commandExtension();
10225 FileInfo dp(dotPath.str());
10226 if (!dp.exists() || !dp.isFile())
10227 {
10228 warn_uncond("the dot tool could not be found as '{}'\n",dotPath);
10229 dotPath = "dot";
10230 dotPath += Portable::commandExtension();
10231 }
10232 }
10233#if defined(_WIN32) // convert slashes
10234 size_t l=dotPath.length();
10235 for (size_t i=0;i<l;i++) if (dotPath.at(i)=='/') dotPath.at(i)='\\';
10236#endif
10237 }
10238 else
10239 {
10240 dotPath = "dot";
10241 dotPath += Portable::commandExtension();
10242 }
10243 Doxygen::verifiedDotPath = dotPath;
10245}
10246
10247//----------------------------------------------------------------------------
10248
10249/*! Generate a template version of the configuration file.
10250 * If the \a shortList parameter is TRUE a configuration file without
10251 * comments will be generated.
10252 */
10253static void generateConfigFile(const QCString &configFile,bool shortList,
10254 bool updateOnly=FALSE)
10255{
10256 std::ofstream f;
10257 bool fileOpened=openOutputFile(configFile,f);
10258 bool writeToStdout=configFile=="-";
10259 if (fileOpened)
10260 {
10261 TextStream t(&f);
10262 Config::writeTemplate(t,shortList,updateOnly);
10263 if (!writeToStdout)
10264 {
10265 if (!updateOnly)
10266 {
10267 msg("\n\nConfiguration file '{}' created.\n\n",configFile);
10268 msg("Now edit the configuration file and enter\n\n");
10269 if (configFile!="Doxyfile" && configFile!="doxyfile")
10270 msg(" doxygen {}\n\n",configFile);
10271 else
10272 msg(" doxygen\n\n");
10273 msg("to generate the documentation for your project\n\n");
10274 }
10275 else
10276 {
10277 msg("\n\nConfiguration file '{}' updated.\n\n",configFile);
10278 }
10279 }
10280 }
10281 else
10282 {
10283 term("Cannot open file {} for writing\n",configFile);
10284 }
10285}
10286
10288{
10289 std::ofstream f;
10290 bool fileOpened=openOutputFile("-",f);
10291 if (fileOpened)
10292 {
10293 TextStream t(&f);
10294 Config::compareDoxyfile(t,diffList);
10295 }
10296 else
10297 {
10298 term("Cannot open stdout for writing\n");
10299 }
10300}
10301
10302//----------------------------------------------------------------------------
10303// read and parse a tag file
10304
10305static void readTagFile(const std::shared_ptr<Entry> &root,const QCString &tagLine)
10306{
10307 QCString fileName;
10308 QCString destName;
10309 int eqPos = tagLine.find('=');
10310 if (eqPos!=-1) // tag command contains a destination
10311 {
10312 fileName = tagLine.left(eqPos).stripWhiteSpace();
10313 destName = tagLine.right(tagLine.length()-eqPos-1).stripWhiteSpace();
10314 if (fileName.isEmpty() || destName.isEmpty()) return;
10315 //printf("insert tagDestination %s->%s\n",qPrint(fi.fileName()),qPrint(destName));
10316 }
10317 else
10318 {
10319 fileName = tagLine;
10320 }
10321
10322 FileInfo fi(fileName.str());
10323 if (!fi.exists() || !fi.isFile())
10324 {
10325 err("Tag file '{}' does not exist or is not a file. Skipping it...\n",fileName);
10326 return;
10327 }
10328
10329 if (Doxygen::tagFileSet.find(fi.absFilePath()) != Doxygen::tagFileSet.end()) return;
10330
10331 Doxygen::tagFileSet.emplace(fi.absFilePath());
10332
10333 if (!destName.isEmpty())
10334 {
10335 Doxygen::tagDestinationMap.emplace(fi.absFilePath(), destName.str());
10336 msg("Reading tag file '{}', location '{}'...\n",fileName,destName);
10337 }
10338 else
10339 {
10340 msg("Reading tag file '{}'...\n",fileName);
10341 }
10342
10343 parseTagFile(root,fi.absFilePath().c_str());
10344}
10345
10346//----------------------------------------------------------------------------
10348{
10349 const StringVector &latexExtraStyleSheet = Config_getList(LATEX_EXTRA_STYLESHEET);
10350 for (const auto &sheet : latexExtraStyleSheet)
10351 {
10352 std::string fileName = sheet;
10353 if (!fileName.empty())
10354 {
10355 FileInfo fi(fileName);
10356 if (!fi.exists())
10357 {
10358 err("Style sheet '{}' specified by LATEX_EXTRA_STYLESHEET does not exist!\n",fileName);
10359 }
10360 else if (fi.isDir())
10361 {
10362 err("Style sheet '{}' specified by LATEX_EXTRA_STYLESHEET is a directory, it has to be a file!\n", fileName);
10363 }
10364 else
10365 {
10366 QCString destFileName = Config_getString(LATEX_OUTPUT)+"/"+fi.fileName();
10368 {
10369 destFileName += LATEX_STYLE_EXTENSION;
10370 }
10371 copyFile(fileName, destFileName);
10372 }
10373 }
10374 }
10375}
10376
10377//----------------------------------------------------------------------------
10378static void copyStyleSheet()
10379{
10380 QCString htmlStyleSheet = Config_getString(HTML_STYLESHEET);
10381 if (!htmlStyleSheet.isEmpty())
10382 {
10383 if (!htmlStyleSheet.startsWith("http:") && !htmlStyleSheet.startsWith("https:"))
10384 {
10385 FileInfo fi(htmlStyleSheet.str());
10386 if (!fi.exists())
10387 {
10388 err("Style sheet '{}' specified by HTML_STYLESHEET does not exist!\n",htmlStyleSheet);
10389 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
10390 }
10391 else if (fi.isDir())
10392 {
10393 err("Style sheet '{}' specified by HTML_STYLESHEET is a directory, it has to be a file!\n",htmlStyleSheet);
10394 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
10395 }
10396 else
10397 {
10398 QCString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
10399 copyFile(htmlStyleSheet,destFileName);
10400 }
10401 }
10402 }
10403 const StringVector &htmlExtraStyleSheet = Config_getList(HTML_EXTRA_STYLESHEET);
10404 for (const auto &sheet : htmlExtraStyleSheet)
10405 {
10406 QCString fileName(sheet);
10407 if (!fileName.isEmpty() && !fileName.startsWith("http:") && !fileName.startsWith("https:"))
10408 {
10409 FileInfo fi(fileName.str());
10410 if (!fi.exists())
10411 {
10412 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET does not exist!\n",fileName);
10413 }
10414 else if (fi.fileName()=="doxygen.css" || fi.fileName()=="tabs.css" || fi.fileName()=="navtree.css")
10415 {
10416 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET is already a built-in stylesheet. Please use a different name\n",fi.fileName());
10417 }
10418 else if (fi.isDir())
10419 {
10420 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET is a directory, it has to be a file!\n",fileName);
10421 }
10422 else
10423 {
10424 QCString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
10425 copyFile(fileName, destFileName);
10426 }
10427 }
10428 }
10429}
10430
10431static void copyLogo(const QCString &outputOption)
10432{
10433 QCString projectLogo = projectLogoFile();
10434 if (!projectLogo.isEmpty())
10435 {
10436 FileInfo fi(projectLogo.str());
10437 if (!fi.exists())
10438 {
10439 err("Project logo '{}' specified by PROJECT_LOGO does not exist!\n",projectLogo);
10440 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
10441 }
10442 else if (fi.isDir())
10443 {
10444 err("Project logo '{}' specified by PROJECT_LOGO is a directory, it has to be a file!\n",projectLogo);
10445 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
10446 }
10447 else
10448 {
10449 QCString destFileName = outputOption+"/"+fi.fileName();
10450 copyFile(projectLogo,destFileName);
10451 Doxygen::indexList->addImageFile(fi.fileName());
10452 }
10453 }
10454}
10455
10456static void copyIcon(const QCString &outputOption)
10457{
10458 QCString projectIcon = Config_getString(PROJECT_ICON);
10459 if (!projectIcon.isEmpty())
10460 {
10461 FileInfo fi(projectIcon.str());
10462 if (!fi.exists())
10463 {
10464 err("Project icon '{}' specified by PROJECT_ICON does not exist!\n",projectIcon);
10465 projectIcon = Config_updateString(PROJECT_ICON,""); // revert to the default
10466 }
10467 else if (fi.isDir())
10468 {
10469 err("Project icon '{}' specified by PROJECT_ICON is a directory, it has to be a file!\n",projectIcon);
10470 projectIcon = Config_updateString(PROJECT_ICON,""); // revert to the default
10471 }
10472 else
10473 {
10474 QCString destFileName = outputOption+"/"+fi.fileName();
10475 copyFile(projectIcon,destFileName);
10476 Doxygen::indexList->addImageFile(fi.fileName());
10477 }
10478 }
10479}
10480
10481static void copyExtraFiles(const StringVector &files,const QCString &filesOption,const QCString &outputOption)
10482{
10483 for (const auto &fileName : files)
10484 {
10485 if (!fileName.empty())
10486 {
10487 FileInfo fi(fileName);
10488 if (!fi.exists())
10489 {
10490 err("Extra file '{}' specified in {} does not exist!\n", fileName,filesOption);
10491 }
10492 else if (fi.isDir())
10493 {
10494 err("Extra file '{}' specified in {} is a directory, it has to be a file!\n", fileName,filesOption);
10495 }
10496 else
10497 {
10498 QCString destFileName = outputOption+"/"+fi.fileName();
10499 Doxygen::indexList->addImageFile(fi.fileName());
10500 copyFile(fileName, destFileName);
10501 }
10502 }
10503 }
10504}
10505
10506//----------------------------------------------------------------------------
10507
10509{
10510 for (const auto &fn : *Doxygen::inputNameLinkedMap)
10511 {
10512 struct FileEntry
10513 {
10514 FileEntry(const QCString &p,FileDef *fd) : path(p), fileDef(fd) {}
10515 QCString path;
10516 FileDef *fileDef;
10517 };
10518
10519 // collect the entry for which to compute the longest common prefix (LCP) of the path
10520 std::vector<FileEntry> fileEntries;
10521 for (const auto &fd : *fn)
10522 {
10523 if (!fd->isReference()) // skip external references
10524 {
10525 fileEntries.emplace_back(fd->getPath(),fd.get());
10526 }
10527 }
10528
10529 size_t size = fileEntries.size();
10530
10531 if (size==1) // name if unique, so diskname is simply the name
10532 {
10533 FileDef *fd = fileEntries[0].fileDef;
10534 fd->setDiskName(fn->fileName());
10535 }
10536 else if (size>1) // multiple occurrences of the same file name
10537 {
10538 // sort the array
10539 std::stable_sort(fileEntries.begin(),
10540 fileEntries.end(),
10541 [](const FileEntry &fe1,const FileEntry &fe2)
10542 { return qstricmp_sort(fe1.path,fe2.path)<0; }
10543 );
10544
10545 // since the entries are sorted, the common prefix of the whole array is same
10546 // as the common prefix between the first and last entry
10547 const FileEntry &first = fileEntries[0];
10548 const FileEntry &last = fileEntries[size-1];
10549 int first_path_size = static_cast<int>(first.path.size())-1; // -1 to skip trailing slash
10550 int last_path_size = static_cast<int>(last.path.size())-1; // -1 to skip trailing slash
10551 int j=0;
10552 int i=0;
10553 for (i=0;i<first_path_size && i<last_path_size;i++)
10554 {
10555 if (first.path[i]=='/') j=i;
10556 if (first.path[i]!=last.path[i]) break;
10557 }
10558 if (i==first_path_size && i<last_path_size && last.path[i]=='/')
10559 {
10560 // case first='some/path' and last='some/path/more' => match is 'some/path'
10561 j=first_path_size;
10562 }
10563 else if (i==last_path_size && i<first_path_size && first.path[i]=='/')
10564 {
10565 // case first='some/path/more' and last='some/path' => match is 'some/path'
10566 j=last_path_size;
10567 }
10568
10569 // add non-common part of the path to the name
10570 for (auto &fileEntry : fileEntries)
10571 {
10572 QCString prefix = fileEntry.path.right(fileEntry.path.length()-j-1);
10573 fileEntry.fileDef->setName(prefix+fn->fileName());
10574 //printf("!!!!!!!! non unique disk name=%s:%s\n",qPrint(prefix),fn->fileName());
10575 fileEntry.fileDef->setDiskName(prefix+fn->fileName());
10576 }
10577 }
10578 }
10579}
10580
10581
10582
10583//----------------------------------------------------------------------------
10584
10585static std::unique_ptr<OutlineParserInterface> getParserForFile(const QCString &fn)
10586{
10587 QCString fileName=fn;
10588 QCString extension;
10589 int sep = fileName.findRev('/');
10590 int ei = fileName.findRev('.');
10591 if (ei!=-1 && (sep==-1 || ei>sep)) // matches dir/file.ext but not dir.1/file
10592 {
10593 extension=fileName.right(fileName.length()-ei);
10594 }
10595 else
10596 {
10597 extension = ".no_extension";
10598 }
10599
10600 return Doxygen::parserManager->getOutlineParser(extension);
10601}
10602
10603static std::shared_ptr<Entry> parseFile(OutlineParserInterface &parser,
10604 FileDef *fd,const QCString &fn,
10605 ClangTUParser *clangParser,bool newTU)
10606{
10607 QCString fileName=fn;
10608 AUTO_TRACE("fileName={}",fileName);
10609 QCString extension;
10610 int ei = fileName.findRev('.');
10611 if (ei!=-1)
10612 {
10613 extension=fileName.right(fileName.length()-ei);
10614 }
10615 else
10616 {
10617 extension = ".no_extension";
10618 }
10619
10620 FileInfo fi(fileName.str());
10621 std::string preBuf;
10622
10623 if (Config_getBool(ENABLE_PREPROCESSING) &&
10624 parser.needsPreprocessing(extension))
10625 {
10626 Preprocessor preprocessor;
10627 const StringVector &includePath = Config_getList(INCLUDE_PATH);
10628 for (const auto &s : includePath)
10629 {
10630 std::string absPath = FileInfo(s).absFilePath();
10631 preprocessor.addSearchDir(absPath);
10632 }
10633 std::string inBuf;
10634 msg("Preprocessing {}...\n",fn);
10635 readInputFile(fileName,inBuf);
10636 addTerminalCharIfMissing(inBuf,'\n');
10637 preprocessor.processFile(fileName,inBuf,preBuf);
10638 }
10639 else // no preprocessing
10640 {
10641 msg("Reading {}...\n",fn);
10642 readInputFile(fileName,preBuf);
10643 addTerminalCharIfMissing(preBuf,'\n');
10644 }
10645
10646 std::string convBuf;
10647 convBuf.reserve(preBuf.size()+1024);
10648
10649 // convert multi-line C++ comments to C style comments
10650 convertCppComments(preBuf,convBuf,fileName.str());
10651
10652 std::shared_ptr<Entry> fileRoot = std::make_shared<Entry>();
10653 // use language parse to parse the file
10654 if (clangParser)
10655 {
10656 if (newTU) clangParser->parse();
10657 clangParser->switchToFile(fd);
10658 }
10659 parser.parseInput(fileName,convBuf.data(),fileRoot,clangParser);
10660 fileRoot->setFileDef(fd);
10661 return fileRoot;
10662}
10663
10664//! parse the list of input files
10665static void parseFilesMultiThreading(const std::shared_ptr<Entry> &root)
10666{
10667 AUTO_TRACE();
10668#if USE_LIBCLANG
10670 {
10671 StringUnorderedSet processedFiles;
10672
10673 // create a dictionary with files to process
10674 StringUnorderedSet filesToProcess;
10675 for (const auto &s : g_inputFiles)
10676 {
10677 filesToProcess.insert(s);
10678 }
10679
10680 std::mutex processedFilesLock;
10681 // process source files (and their include dependencies)
10682 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10683 msg("Processing input using {} threads.\n",numThreads);
10684 ThreadPool threadPool(numThreads);
10685 using FutureType = std::vector< std::shared_ptr<Entry> >;
10686 std::vector< std::future< FutureType > > results;
10687 for (const auto &s : g_inputFiles)
10688 {
10689 bool ambig = false;
10690 QCString qs = s;
10692 ASSERT(fd!=nullptr);
10693 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt::Cpp) // this is a source file
10694 {
10695 // lambda representing the work to executed by a thread
10696 auto processFile = [qs,&filesToProcess,&processedFilesLock,&processedFiles]() {
10697 bool ambig_l = false;
10698 std::vector< std::shared_ptr<Entry> > roots;
10700 auto clangParser = ClangParser::instance()->createTUParser(fd_l);
10701 auto parser = getParserForFile(qs);
10702 auto fileRoot { parseFile(*parser.get(),fd_l,qs,clangParser.get(),true) };
10703 roots.push_back(fileRoot);
10704
10705 // Now process any include files in the same translation unit
10706 // first. When libclang is used this is much more efficient.
10707 for (auto incFile : clangParser->filesInSameTU())
10708 {
10709 QCString qincFile = incFile;
10710 if (filesToProcess.find(incFile)!=filesToProcess.end())
10711 {
10712 bool needsToBeProcessed = false;
10713 {
10714 std::lock_guard<std::mutex> lock(processedFilesLock);
10715 needsToBeProcessed = processedFiles.find(incFile)==processedFiles.end();
10716 if (needsToBeProcessed) processedFiles.insert(incFile);
10717 }
10718 if (qincFile!=qs && needsToBeProcessed)
10719 {
10720 FileDef *ifd=findFileDef(Doxygen::inputNameLinkedMap,qincFile,ambig_l);
10721 if (ifd && !ifd->isReference())
10722 {
10723 //printf(" Processing %s in same translation unit as %s\n",incFile,qPrint(s));
10724 fileRoot = parseFile(*parser.get(),ifd,qincFile,clangParser.get(),false);
10725 roots.push_back(fileRoot);
10726 }
10727 }
10728 }
10729 }
10730 return roots;
10731 };
10732 // dispatch the work and collect the future results
10733 results.emplace_back(threadPool.queue(processFile));
10734 }
10735 }
10736 // synchronize with the Entry result lists produced and add them to the root
10737 for (auto &f : results)
10738 {
10739 auto l = f.get();
10740 for (auto &e : l)
10741 {
10742 root->moveToSubEntryAndKeep(e);
10743 }
10744 }
10745 // process remaining files
10746 results.clear();
10747 for (const auto &s : g_inputFiles)
10748 {
10749 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
10750 {
10751 // lambda representing the work to executed by a thread
10752 auto processFile = [s]() {
10753 bool ambig = false;
10754 QCString qs = s;
10755 std::vector< std::shared_ptr<Entry> > roots;
10757 auto parser { getParserForFile(qs) };
10758 bool useClang = getLanguageFromFileName(qs)==SrcLangExt::Cpp;
10759 if (useClang)
10760 {
10761 auto clangParser = ClangParser::instance()->createTUParser(fd);
10762 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
10763 roots.push_back(fileRoot);
10764 }
10765 else
10766 {
10767 auto fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10768 roots.push_back(fileRoot);
10769 }
10770 return roots;
10771 };
10772 results.emplace_back(threadPool.queue(processFile));
10773 }
10774 }
10775 // synchronize with the Entry result lists produced and add them to the root
10776 for (auto &f : results)
10777 {
10778 auto l = f.get();
10779 for (auto &e : l)
10780 {
10781 root->moveToSubEntryAndKeep(e);
10782 }
10783 }
10784 }
10785 else // normal processing
10786#endif
10787 {
10788 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10789 msg("Processing input using {} threads.\n",numThreads);
10790 ThreadPool threadPool(numThreads);
10791 using FutureType = std::shared_ptr<Entry>;
10792 std::vector< std::future< FutureType > > results;
10793 for (const auto &s : g_inputFiles)
10794 {
10795 // lambda representing the work to executed by a thread
10796 auto processFile = [s]() {
10797 bool ambig = false;
10798 QCString qs = s;
10800 auto parser = getParserForFile(qs);
10801 auto fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10802 return fileRoot;
10803 };
10804 // dispatch the work and collect the future results
10805 results.emplace_back(threadPool.queue(processFile));
10806 }
10807 // synchronize with the Entry results produced and add them to the root
10808 for (auto &f : results)
10809 {
10810 root->moveToSubEntryAndKeep(f.get());
10811 }
10812 }
10813}
10814
10815//! parse the list of input files
10816static void parseFilesSingleThreading(const std::shared_ptr<Entry> &root)
10817{
10818 AUTO_TRACE();
10819#if USE_LIBCLANG
10821 {
10822 StringUnorderedSet processedFiles;
10823
10824 // create a dictionary with files to process
10825 StringUnorderedSet filesToProcess;
10826 for (const auto &s : g_inputFiles)
10827 {
10828 filesToProcess.insert(s);
10829 }
10830
10831 // process source files (and their include dependencies)
10832 for (const auto &s : g_inputFiles)
10833 {
10834 bool ambig = false;
10835 QCString qs =s;
10837 ASSERT(fd!=nullptr);
10838 if (fd->isSource() && !fd->isReference() && getLanguageFromFileName(qs)==SrcLangExt::Cpp) // this is a source file
10839 {
10840 auto clangParser = ClangParser::instance()->createTUParser(fd);
10841 auto parser { getParserForFile(qs) };
10842 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
10843 root->moveToSubEntryAndKeep(fileRoot);
10844 processedFiles.insert(s);
10845
10846 // Now process any include files in the same translation unit
10847 // first. When libclang is used this is much more efficient.
10848 for (auto incFile : clangParser->filesInSameTU())
10849 {
10850 //printf(" file %s\n",qPrint(incFile));
10851 if (filesToProcess.find(incFile)!=filesToProcess.end() && // file need to be processed
10852 processedFiles.find(incFile)==processedFiles.end()) // and is not processed already
10853 {
10855 if (ifd && !ifd->isReference())
10856 {
10857 //printf(" Processing %s in same translation unit as %s\n",qPrint(incFile),qPrint(qs));
10858 fileRoot = parseFile(*parser.get(),ifd,incFile,clangParser.get(),false);
10859 root->moveToSubEntryAndKeep(fileRoot);
10860 processedFiles.insert(incFile);
10861 }
10862 }
10863 }
10864 }
10865 }
10866 // process remaining files
10867 for (const auto &s : g_inputFiles)
10868 {
10869 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
10870 {
10871 bool ambig = false;
10872 QCString qs = s;
10874 if (getLanguageFromFileName(qs)==SrcLangExt::Cpp) // not yet processed
10875 {
10876 auto clangParser = ClangParser::instance()->createTUParser(fd);
10877 auto parser { getParserForFile(qs) };
10878 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
10879 root->moveToSubEntryAndKeep(fileRoot);
10880 }
10881 else
10882 {
10883 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(qs) };
10884 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10885 root->moveToSubEntryAndKeep(fileRoot);
10886 }
10887 processedFiles.insert(s);
10888 }
10889 }
10890 }
10891 else // normal processing
10892#endif
10893 {
10894 for (const auto &s : g_inputFiles)
10895 {
10896 bool ambig = false;
10897 QCString qs = s;
10899 ASSERT(fd!=nullptr);
10900 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(qs) };
10901 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10902 root->moveToSubEntryAndKeep(std::move(fileRoot));
10903 }
10904 }
10905}
10906
10907// resolves a path that may include symlinks, if a recursive symlink is
10908// found an empty string is returned.
10909static std::string resolveSymlink(const std::string &path)
10910{
10911 int sepPos=0;
10912 int oldPos=0;
10913 StringUnorderedSet nonSymlinks;
10914 StringUnorderedSet known;
10915 QCString result(path);
10916 QCString oldPrefix = "/";
10917 do
10918 {
10919#if defined(_WIN32)
10920 // UNC path, skip server and share name
10921 if (sepPos==0 && (result.startsWith("//") || result.startsWith("\\\\")))
10922 sepPos = result.find('/',2);
10923 if (sepPos!=-1)
10924 sepPos = result.find('/',sepPos+1);
10925#else
10926 sepPos = result.find('/',sepPos+1);
10927#endif
10928 QCString prefix = sepPos==-1 ? result : result.left(sepPos);
10929 if (nonSymlinks.find(prefix.str())==nonSymlinks.end())
10930 {
10931 FileInfo fi(prefix.str());
10932 if (fi.isSymLink())
10933 {
10934 QCString target = fi.readLink();
10935 bool isRelative = FileInfo(target.str()).isRelative();
10936 if (isRelative)
10937 {
10938 target = Dir::cleanDirPath(oldPrefix.str()+"/"+target.str());
10939 }
10940 if (sepPos!=-1)
10941 {
10942 if (fi.isDir() && target.length()>0 && target.at(target.length()-1)!='/')
10943 {
10944 target+='/';
10945 }
10946 target+=result.mid(sepPos);
10947 }
10948 result = Dir::cleanDirPath(target.str());
10949 if (known.find(result.str())!=known.end()) return std::string(); // recursive symlink!
10950 known.insert(result.str());
10951 if (isRelative)
10952 {
10953 sepPos = oldPos;
10954 }
10955 else // link to absolute path
10956 {
10957 sepPos = 0;
10958 oldPrefix = "/";
10959 }
10960 }
10961 else
10962 {
10963 nonSymlinks.insert(prefix.str());
10964 oldPrefix = prefix;
10965 }
10966 oldPos = sepPos;
10967 }
10968 }
10969 while (sepPos!=-1);
10970 return Dir::cleanDirPath(result.str());
10971}
10972
10974
10975//----------------------------------------------------------------------------
10976// Read all files matching at least one pattern in 'patList' in the
10977// directory represented by 'fi'.
10978// The directory is read iff the recursiveFlag is set.
10979// The contents of all files is append to the input string
10980
10981static void readDir(FileInfo *fi,
10982 FileNameLinkedMap *fnMap,
10983 StringUnorderedSet *exclSet,
10984 const StringVector *patList,
10985 const StringVector *exclPatList,
10986 StringVector *resultList,
10987 StringUnorderedSet *resultSet,
10988 bool errorIfNotExist,
10989 bool recursive,
10990 StringUnorderedSet *killSet,
10991 StringUnorderedSet *paths
10992 )
10993{
10994 std::string dirName = fi->absFilePath();
10995 if (paths && !dirName.empty())
10996 {
10997 paths->insert(dirName);
10998 }
10999 //printf("%s isSymLink()=%d\n",qPrint(dirName),fi->isSymLink());
11000 if (fi->isSymLink())
11001 {
11002 dirName = resolveSymlink(dirName);
11003 if (dirName.empty())
11004 {
11005 //printf("RECURSIVE SYMLINK: %s\n",qPrint(dirName));
11006 return; // recursive symlink
11007 }
11008 }
11009
11010 if (g_pathsVisited.find(dirName)!=g_pathsVisited.end())
11011 {
11012 //printf("PATH ALREADY VISITED: %s\n",qPrint(dirName));
11013 return; // already visited path
11014 }
11015 g_pathsVisited.insert(dirName);
11016
11017 Dir dir(dirName);
11018 msg("Searching for files in directory {}\n", fi->absFilePath());
11019 //printf("killSet=%p count=%d\n",killSet,killSet ? (int)killSet->count() : -1);
11020
11021 StringVector dirResultList;
11022
11023 for (const auto &dirEntry : dir.iterator())
11024 {
11025 FileInfo cfi(dirEntry.path());
11026 auto checkPatterns = [&]() -> bool
11027 {
11028 return (patList==nullptr || patternMatch(cfi,*patList)) &&
11029 (exclPatList==nullptr || !patternMatch(cfi,*exclPatList)) &&
11030 (killSet==nullptr || killSet->find(cfi.absFilePath())==killSet->end());
11031 };
11032
11033 if (exclSet==nullptr || exclSet->find(cfi.absFilePath())==exclSet->end())
11034 { // file should not be excluded
11035 //printf("killSet->find(%s)\n",qPrint(cfi->absFilePath()));
11036 if (Config_getBool(EXCLUDE_SYMLINKS) && cfi.isSymLink())
11037 {
11038 }
11039 else if (!cfi.exists() || !cfi.isReadable())
11040 {
11041 if (errorIfNotExist && checkPatterns())
11042 {
11043 warn_uncond("source '{}' is not a readable file or directory... skipping.\n",cfi.absFilePath());
11044 }
11045 }
11046 else if (cfi.isFile() && checkPatterns())
11047 {
11048 std::string name=cfi.fileName();
11049 std::string path=cfi.dirPath()+"/";
11050 std::string fullName=path+name;
11051 if (fnMap)
11052 {
11053 auto fd = createFileDef(path,name);
11054 FileName *fn=nullptr;
11055 if (!name.empty())
11056 {
11057 fn = fnMap->add(name,fullName);
11058 fn->push_back(std::move(fd));
11059 }
11060 }
11061 dirResultList.push_back(fullName);
11062 if (resultSet) resultSet->insert(fullName);
11063 if (killSet) killSet->insert(fullName);
11064 }
11065 else if (recursive &&
11066 cfi.isDir() &&
11067 (exclPatList==nullptr || !patternMatch(cfi,*exclPatList)) &&
11068 cfi.fileName().at(0)!='.') // skip "." ".." and ".dir"
11069 {
11070 FileInfo acfi(cfi.absFilePath());
11071 readDir(&acfi,fnMap,exclSet,
11072 patList,exclPatList,&dirResultList,resultSet,errorIfNotExist,
11073 recursive,killSet,paths);
11074 }
11075 }
11076 }
11077 if (resultList && !dirResultList.empty())
11078 {
11079 // sort the resulting list to make the order platform independent.
11080 std::stable_sort(dirResultList.begin(),
11081 dirResultList.end(),
11082 [](const auto &f1,const auto &f2) { return qstricmp_sort(f1.c_str(),f2.c_str())<0; });
11083
11084 // append the sorted results to resultList
11085 resultList->insert(resultList->end(), dirResultList.begin(), dirResultList.end());
11086 }
11087}
11088
11089
11090//----------------------------------------------------------------------------
11091// read a file or all files in a directory and append their contents to the
11092// input string. The names of the files are appended to the 'fiList' list.
11093
11095 FileNameLinkedMap *fnMap,
11096 StringUnorderedSet *exclSet,
11097 const StringVector *patList,
11098 const StringVector *exclPatList,
11099 StringVector *resultList,
11100 StringUnorderedSet *resultSet,
11101 bool recursive,
11102 bool errorIfNotExist,
11103 StringUnorderedSet *killSet,
11104 StringUnorderedSet *paths
11105 )
11106{
11107 //printf("killSet count=%d\n",killSet ? (int)killSet->size() : -1);
11108 // strip trailing slashes
11109 if (s.isEmpty()) return;
11110
11111 g_pathsVisited.clear();
11112
11113 FileInfo fi(s.str());
11114 //printf("readFileOrDirectory(%s)\n",s);
11115 {
11116 if (exclSet==nullptr || exclSet->find(fi.absFilePath())==exclSet->end())
11117 {
11118 if (Config_getBool(EXCLUDE_SYMLINKS) && fi.isSymLink())
11119 {
11120 }
11121 else if (!fi.exists() || !fi.isReadable())
11122 {
11123 if (errorIfNotExist)
11124 {
11125 warn_uncond("source '{}' is not a readable file or directory... skipping.\n",s);
11126 }
11127 }
11128 else if (fi.isFile())
11129 {
11130 std::string dirPath = fi.dirPath(true);
11131 std::string filePath = fi.absFilePath();
11132 if (paths && !dirPath.empty())
11133 {
11134 paths->insert(dirPath);
11135 }
11136 //printf("killSet.find(%s)=%d\n",qPrint(fi.absFilePath()),killSet.find(fi.absFilePath())!=killSet.end());
11137 if (killSet==nullptr || killSet->find(filePath)==killSet->end())
11138 {
11139 std::string name=fi.fileName();
11140 if (fnMap)
11141 {
11142 auto fd = createFileDef(dirPath+"/",name);
11143 if (!name.empty())
11144 {
11145 FileName *fn = fnMap->add(name,filePath);
11146 fn->push_back(std::move(fd));
11147 }
11148 }
11149 if (resultList || resultSet)
11150 {
11151 if (resultList) resultList->push_back(filePath);
11152 if (resultSet) resultSet->insert(filePath);
11153 }
11154
11155 if (killSet) killSet->insert(fi.absFilePath());
11156 }
11157 }
11158 else if (fi.isDir()) // readable dir
11159 {
11160 readDir(&fi,fnMap,exclSet,patList,
11161 exclPatList,resultList,resultSet,errorIfNotExist,
11162 recursive,killSet,paths);
11163 }
11164 }
11165 }
11166}
11167
11168//----------------------------------------------------------------------------
11169
11171{
11172 QCString anchor;
11174 {
11175 MemberDef *md = toMemberDef(d);
11176 anchor=":"+md->anchor();
11177 }
11178 QCString scope;
11179 QCString fn = d->getOutputFileBase();
11182 {
11183 scope = fn;
11184 }
11185 t << "REPLACE INTO symbols (symbol_id,scope_id,name,file,line) VALUES('"
11186 << fn+anchor << "','"
11187 << scope << "','"
11188 << d->name() << "','"
11189 << d->getDefFileName() << "','"
11190 << d->getDefLine()
11191 << "');\n";
11192}
11193
11194static void dumpSymbolMap()
11195{
11196 std::ofstream f = Portable::openOutputStream("symbols.sql");
11197 if (f.is_open())
11198 {
11199 TextStream t(&f);
11200 for (const auto &[name,symList] : *Doxygen::symbolMap)
11201 {
11202 for (const auto &def : symList)
11203 {
11204 dumpSymbol(t,def);
11205 }
11206 }
11207 }
11208}
11209
11210// print developer options of Doxygen
11211static void devUsage()
11212{
11214 msg("Developer parameters:\n");
11215 msg(" -m dump symbol map\n");
11216 msg(" -b making messages output unbuffered\n");
11217 msg(" -c <file> process input file as a comment block and produce HTML output\n");
11218#if ENABLE_TRACING
11219 msg(" -t [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout)\n");
11220 msg(" -t_time [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout),\n"
11221 " and include time and thread information\n");
11222#endif
11223 msg(" -d <level> enable a debug level, such as (multiple invocations of -d are possible):\n");
11225}
11226
11227
11228//----------------------------------------------------------------------------
11229// print the version of Doxygen
11230
11231static void version(const bool extended)
11232{
11234 QCString versionString = getFullVersion();
11235 msg("{}\n",versionString);
11236 if (extended)
11237 {
11238 QCString extVers;
11239 if (!extVers.isEmpty()) extVers+= ", ";
11240 extVers += "sqlite3 ";
11241 extVers += sqlite3_libversion();
11242#if USE_LIBCLANG
11243 if (!extVers.isEmpty()) extVers+= ", ";
11244 extVers += "clang support ";
11245 extVers += CLANG_VERSION_STRING;
11246#endif
11247 if (!extVers.isEmpty())
11248 {
11249 int lastComma = extVers.findRev(',');
11250 if (lastComma != -1) extVers = extVers.replace(lastComma,1," and");
11251 msg(" with {}.\n",extVers);
11252 }
11253 }
11254}
11255
11256//----------------------------------------------------------------------------
11257// print the usage of Doxygen
11258
11259static void usage(const QCString &name,const QCString &versionString)
11260{
11262 msg("Doxygen version {0}\nCopyright Dimitri van Heesch 1997-2025\n\n"
11263 "You can use Doxygen in a number of ways:\n\n"
11264 "1) Use Doxygen to generate a template configuration file*:\n"
11265 " {1} [-s] -g [configName]\n\n"
11266 "2) Use Doxygen to update an old configuration file*:\n"
11267 " {1} [-s] -u [configName]\n\n"
11268 "3) Use Doxygen to generate documentation using an existing "
11269 "configuration file*:\n"
11270 " {1} [configName]\n\n"
11271 "4) Use Doxygen to generate a template file controlling the layout of the\n"
11272 " generated documentation:\n"
11273 " {1} -l [layoutFileName]\n\n"
11274 " In case layoutFileName is omitted DoxygenLayout.xml will be used as filename.\n"
11275 " If - is used for layoutFileName Doxygen will write to standard output.\n\n"
11276 "5) Use Doxygen to generate a template style sheet file for RTF, HTML or Latex.\n"
11277 " RTF: {1} -w rtf styleSheetFile\n"
11278 " HTML: {1}-w html headerFile footerFile styleSheetFile [configFile]\n"
11279 " LaTeX: {1} -w latex headerFile footerFile styleSheetFile [configFile]\n\n"
11280 "6) Use Doxygen to generate a rtf extensions file\n"
11281 " {1} -e rtf extensionsFile\n\n"
11282 " If - is used for extensionsFile Doxygen will write to standard output.\n\n"
11283 "7) Use Doxygen to compare the used configuration file with the template configuration file\n"
11284 " {1} -x [configFile]\n\n"
11285 " Use Doxygen to compare the used configuration file with the template configuration file\n"
11286 " without replacing the environment variables or CMake type replacement variables\n"
11287 " {1} -x_noenv [configFile]\n\n"
11288 "8) Use Doxygen to show a list of built-in emojis.\n"
11289 " {1} -f emoji outputFileName\n\n"
11290 " If - is used for outputFileName Doxygen will write to standard output.\n\n"
11291 "*) If -s is specified the comments of the configuration items in the config file will be omitted.\n"
11292 " If configName is omitted 'Doxyfile' will be used as a default.\n"
11293 " If - is used for configFile Doxygen will write / read the configuration to /from standard output / input.\n\n"
11294 "If -q is used for a Doxygen documentation run, Doxygen will see this as if QUIET=YES has been set.\n\n"
11295 "-v print version string, -V print extended version information\n"
11296 "-h,-? prints usage help information\n"
11297 "{1} -d prints additional usage flags for debugging purposes\n",versionString,name);
11298}
11299
11300//----------------------------------------------------------------------------
11301// read the argument of option 'c' from the comment argument list and
11302// update the option index 'optInd'.
11303
11304static const char *getArg(int argc,char **argv,int &optInd)
11305{
11306 char *s=nullptr;
11307 if (qstrlen(&argv[optInd][2])>0)
11308 s=&argv[optInd][2];
11309 else if (optInd+1<argc && argv[optInd+1][0]!='-')
11310 s=argv[++optInd];
11311 return s;
11312}
11313
11314//----------------------------------------------------------------------------
11315
11316/** @brief /dev/null outline parser */
11318{
11319 public:
11320 void parseInput(const QCString &/* file */, const char * /* buf */,const std::shared_ptr<Entry> &, ClangTUParser*) override {}
11321 bool needsPreprocessing(const QCString &) const override { return FALSE; }
11322 void parsePrototype(const QCString &) override {}
11323};
11324
11325
11326template<class T> std::function< std::unique_ptr<T>() > make_parser_factory()
11327{
11328 return []() { return std::make_unique<T>(); };
11329}
11330
11332{
11333 initResources();
11334 QCString lang = Portable::getenv("LC_ALL");
11335 if (!lang.isEmpty()) Portable::setenv("LANG",lang);
11336 std::setlocale(LC_ALL,"");
11337 std::setlocale(LC_CTYPE,"C"); // to get isspace(0xA0)==0, needed for UTF-8
11338 std::setlocale(LC_NUMERIC,"C");
11339
11341
11365
11366 // register any additional parsers here...
11367
11369
11370#if USE_LIBCLANG
11372#endif
11381 Doxygen::pageLinkedMap = new PageLinkedMap; // all doc pages
11382 Doxygen::exampleLinkedMap = new PageLinkedMap; // all examples
11383 //Doxygen::tagDestinationDict.setAutoDelete(TRUE);
11385
11386 // initialization of these globals depends on
11387 // configuration switches so we need to postpone these
11388 Doxygen::globalScope = nullptr;
11397
11398}
11399
11431
11432static int computeIdealCacheParam(size_t v)
11433{
11434 //printf("computeIdealCacheParam(v=%u)\n",v);
11435
11436 int r=0;
11437 while (v!=0) v>>=1,r++;
11438 // r = log2(v)
11439
11440 // convert to a valid cache size value
11441 return std::max(0,std::min(r-16,9));
11442}
11443
11444void readConfiguration(int argc, char **argv)
11445{
11446 QCString versionString = getFullVersion();
11447
11448 // helper that calls \a func to write to file \a fileName via a TextStream
11449 auto writeFile = [](const char *fileName,std::function<void(TextStream&)> func) -> bool
11450 {
11451 std::ofstream f;
11452 if (openOutputFile(fileName,f))
11453 {
11454 TextStream t(&f);
11455 func(t);
11456 return true;
11457 }
11458 return false;
11459 };
11460
11461
11462 /**************************************************************************
11463 * Handle arguments *
11464 **************************************************************************/
11465
11466 int optInd=1;
11467 QCString configName;
11468 QCString traceName;
11469 bool genConfig=false;
11470 bool shortList=false;
11471 bool traceTiming=false;
11473 bool updateConfig=false;
11474 bool quiet = false;
11475 while (optInd<argc && argv[optInd][0]=='-' &&
11476 (isalpha(argv[optInd][1]) || argv[optInd][1]=='?' ||
11477 argv[optInd][1]=='-')
11478 )
11479 {
11480 switch(argv[optInd][1])
11481 {
11482 case 'g':
11483 {
11484 genConfig=TRUE;
11485 }
11486 break;
11487 case 'l':
11488 {
11489 QCString layoutName;
11490 if (optInd+1>=argc)
11491 {
11492 layoutName="DoxygenLayout.xml";
11493 }
11494 else
11495 {
11496 layoutName=argv[optInd+1];
11497 }
11498 writeDefaultLayoutFile(layoutName);
11500 exit(0);
11501 }
11502 break;
11503 case 'c':
11504 if (optInd+1>=argc) // no file name given
11505 {
11506 msg("option \"-c\" is missing the file name to read\n");
11507 devUsage();
11509 exit(1);
11510 }
11511 else
11512 {
11513 g_commentFileName=argv[optInd+1];
11514 optInd++;
11515 }
11516 g_singleComment=true;
11517 quiet=true;
11518 break;
11519 case 'd':
11520 {
11521 QCString debugLabel=getArg(argc,argv,optInd);
11522 if (debugLabel.isEmpty())
11523 {
11524 devUsage();
11526 exit(0);
11527 }
11528 int retVal = Debug::setFlagStr(debugLabel);
11529 if (!retVal)
11530 {
11531 msg("option \"-d\" has unknown debug specifier: \"{}\".\n",debugLabel);
11532 devUsage();
11534 exit(1);
11535 }
11536 }
11537 break;
11538 case 't':
11539 {
11540#if ENABLE_TRACING
11541 if (!strcmp(argv[optInd]+1,"t_time"))
11542 {
11543 traceTiming = true;
11544 }
11545 else if (!strcmp(argv[optInd]+1,"t"))
11546 {
11547 traceTiming = false;
11548 }
11549 else
11550 {
11551 err("option should be \"-t\" or \"-t_time\", found: \"{}\".\n",argv[optInd]);
11553 exit(1);
11554 }
11555 if (optInd+1>=argc || argv[optInd+1][0] == '-') // no file name given
11556 {
11557 traceName="stdout";
11558 }
11559 else
11560 {
11561 traceName=argv[optInd+1];
11562 optInd++;
11563 }
11564#else
11565 err("support for option \"-t\" has not been compiled in (use a debug build or a release build with tracing enabled).\n");
11567 exit(1);
11568#endif
11569 }
11570 break;
11571 case 'x':
11572 if (!strcmp(argv[optInd]+1,"x_noenv")) diffList=Config::CompareMode::CompressedNoEnv;
11573 else if (!strcmp(argv[optInd]+1,"x")) diffList=Config::CompareMode::Compressed;
11574 else
11575 {
11576 err("option should be \"-x\" or \"-x_noenv\", found: \"{}\".\n",argv[optInd]);
11578 exit(1);
11579 }
11580 break;
11581 case 's':
11582 shortList=TRUE;
11583 break;
11584 case 'u':
11585 updateConfig=TRUE;
11586 break;
11587 case 'e':
11588 {
11589 QCString formatName=getArg(argc,argv,optInd);
11590 if (formatName.isEmpty())
11591 {
11592 err("option \"-e\" is missing format specifier rtf.\n");
11594 exit(1);
11595 }
11596 if (qstricmp(formatName.data(),"rtf")==0)
11597 {
11598 if (optInd+1>=argc)
11599 {
11600 err("option \"-e rtf\" is missing an extensions file name\n");
11602 exit(1);
11603 }
11604 writeFile(argv[optInd+1],RTFGenerator::writeExtensionsFile);
11606 exit(0);
11607 }
11608 err("option \"-e\" has invalid format specifier.\n");
11610 exit(1);
11611 }
11612 break;
11613 case 'f':
11614 {
11615 QCString listName=getArg(argc,argv,optInd);
11616 if (listName.isEmpty())
11617 {
11618 err("option \"-f\" is missing list specifier.\n");
11620 exit(1);
11621 }
11622 if (qstricmp(listName.data(),"emoji")==0)
11623 {
11624 if (optInd+1>=argc)
11625 {
11626 err("option \"-f emoji\" is missing an output file name\n");
11628 exit(1);
11629 }
11630 writeFile(argv[optInd+1],[](TextStream &t) { EmojiEntityMapper::instance().writeEmojiFile(t); });
11632 exit(0);
11633 }
11634 err("option \"-f\" has invalid list specifier.\n");
11636 exit(1);
11637 }
11638 break;
11639 case 'w':
11640 {
11641 QCString formatName=getArg(argc,argv,optInd);
11642 if (formatName.isEmpty())
11643 {
11644 err("option \"-w\" is missing format specifier rtf, html or latex\n");
11646 exit(1);
11647 }
11648 if (qstricmp(formatName.data(),"rtf")==0)
11649 {
11650 if (optInd+1>=argc)
11651 {
11652 err("option \"-w rtf\" is missing a style sheet file name\n");
11654 exit(1);
11655 }
11656 if (!writeFile(argv[optInd+1],RTFGenerator::writeStyleSheetFile))
11657 {
11658 err("error opening RTF style sheet file {}!\n",argv[optInd+1]);
11660 exit(1);
11661 }
11663 exit(0);
11664 }
11665 else if (qstricmp(formatName.data(),"html")==0)
11666 {
11667 Config::init();
11668 if (optInd+4<argc || FileInfo("Doxyfile").exists() || FileInfo("doxyfile").exists())
11669 // explicit config file mentioned or default found on disk
11670 {
11671 QCString df = optInd+4<argc ? argv[optInd+4] : (FileInfo("Doxyfile").exists() ? QCString("Doxyfile") : QCString("doxyfile"));
11672 if (!Config::parse(df)) // parse the config file
11673 {
11674 err("error opening or reading configuration file {}!\n",argv[optInd+4]);
11676 exit(1);
11677 }
11678 }
11679 if (optInd+3>=argc)
11680 {
11681 err("option \"-w html\" does not have enough arguments\n");
11683 exit(1);
11684 }
11688 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11689 writeFile(argv[optInd+1],[&](TextStream &t) { HtmlGenerator::writeHeaderFile(t,argv[optInd+3]); });
11690 writeFile(argv[optInd+2],HtmlGenerator::writeFooterFile);
11691 writeFile(argv[optInd+3],HtmlGenerator::writeStyleSheetFile);
11693 exit(0);
11694 }
11695 else if (qstricmp(formatName.data(),"latex")==0)
11696 {
11697 Config::init();
11698 if (optInd+4<argc || FileInfo("Doxyfile").exists() || FileInfo("doxyfile").exists())
11699 {
11700 QCString df = optInd+4<argc ? argv[optInd+4] : (FileInfo("Doxyfile").exists() ? QCString("Doxyfile") : QCString("doxyfile"));
11701 if (!Config::parse(df))
11702 {
11703 err("error opening or reading configuration file {}!\n",argv[optInd+4]);
11705 exit(1);
11706 }
11707 }
11708 if (optInd+3>=argc)
11709 {
11710 err("option \"-w latex\" does not have enough arguments\n");
11712 exit(1);
11713 }
11717 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11718 writeFile(argv[optInd+1],LatexGenerator::writeHeaderFile);
11719 writeFile(argv[optInd+2],LatexGenerator::writeFooterFile);
11720 writeFile(argv[optInd+3],LatexGenerator::writeStyleSheetFile);
11722 exit(0);
11723 }
11724 else
11725 {
11726 err("Illegal format specifier \"{}\": should be one of rtf, html or latex\n",formatName);
11728 exit(1);
11729 }
11730 }
11731 break;
11732 case 'm':
11734 break;
11735 case 'v':
11736 version(false);
11738 exit(0);
11739 break;
11740 case 'V':
11741 version(true);
11743 exit(0);
11744 break;
11745 case '-':
11746 if (qstrcmp(&argv[optInd][2],"help")==0)
11747 {
11748 usage(argv[0],versionString);
11749 exit(0);
11750 }
11751 else if (qstrcmp(&argv[optInd][2],"version")==0)
11752 {
11753 version(false);
11755 exit(0);
11756 }
11757 else if ((qstrcmp(&argv[optInd][2],"Version")==0) ||
11758 (qstrcmp(&argv[optInd][2],"VERSION")==0))
11759 {
11760 version(true);
11762 exit(0);
11763 }
11764 else
11765 {
11766 err("Unknown option \"-{}\"\n",&argv[optInd][1]);
11767 usage(argv[0],versionString);
11768 exit(1);
11769 }
11770 break;
11771 case 'b':
11772 setvbuf(stdout,nullptr,_IONBF,0);
11773 break;
11774 case 'q':
11775 quiet = true;
11776 break;
11777 case 'h':
11778 case '?':
11779 usage(argv[0],versionString);
11780 exit(0);
11781 break;
11782 default:
11783 err("Unknown option \"-{:c}\"\n",argv[optInd][1]);
11784 usage(argv[0],versionString);
11785 exit(1);
11786 }
11787 optInd++;
11788 }
11789
11790 /**************************************************************************
11791 * Parse or generate the config file *
11792 **************************************************************************/
11793
11794 initTracing(traceName.data(),traceTiming);
11795 TRACE("Doxygen version used: {}",getFullVersion());
11796 Config::init();
11797
11798 FileInfo configFileInfo1("Doxyfile"),configFileInfo2("doxyfile");
11799 if (optInd>=argc)
11800 {
11801 if (configFileInfo1.exists())
11802 {
11803 configName="Doxyfile";
11804 }
11805 else if (configFileInfo2.exists())
11806 {
11807 configName="doxyfile";
11808 }
11809 else if (genConfig)
11810 {
11811 configName="Doxyfile";
11812 }
11813 else
11814 {
11815 err("Doxyfile not found and no input file specified!\n");
11816 usage(argv[0],versionString);
11817 exit(1);
11818 }
11819 }
11820 else
11821 {
11822 FileInfo fi(argv[optInd]);
11823 if (fi.exists() || qstrcmp(argv[optInd],"-")==0 || genConfig)
11824 {
11825 configName=argv[optInd];
11826 }
11827 else
11828 {
11829 err("configuration file {} not found!\n",argv[optInd]);
11830 usage(argv[0],versionString);
11831 exit(1);
11832 }
11833 }
11834
11835 if (genConfig)
11836 {
11837 generateConfigFile(configName,shortList);
11839 exit(0);
11840 }
11841
11842 if (!Config::parse(configName,updateConfig,diffList))
11843 {
11844 err("could not open or read configuration file {}!\n",configName);
11846 exit(1);
11847 }
11848
11849 if (diffList!=Config::CompareMode::Full)
11850 {
11852 compareDoxyfile(diffList);
11854 exit(0);
11855 }
11856
11857 if (updateConfig)
11858 {
11860 generateConfigFile(configName,shortList,TRUE);
11862 exit(0);
11863 }
11864
11865 /* Perlmod wants to know the path to the config file.*/
11866 FileInfo configFileInfo(configName.str());
11867 setPerlModDoxyfile(configFileInfo.absFilePath());
11868
11869 /* handle -q option */
11870 if (quiet) Config_updateBool(QUIET,TRUE);
11871}
11872
11873/** check and resolve config options */
11875{
11876 AUTO_TRACE();
11877
11882}
11883
11884/** adjust globals that depend on configuration settings. */
11886{
11887 AUTO_TRACE();
11888 Doxygen::globalNamespaceDef = createNamespaceDef("<globalScope>",1,1,"<globalScope>");
11898
11899 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11900
11901 /* Set the global html file extension. */
11902 Doxygen::htmlFileExtension = Config_getString(HTML_FILE_EXTENSION);
11903
11904
11906 Config_getBool(CALLER_GRAPH) ||
11907 Config_getBool(REFERENCES_RELATION) ||
11908 Config_getBool(REFERENCED_BY_RELATION);
11909
11910 /**************************************************************************
11911 * Add custom extension mappings
11912 **************************************************************************/
11913
11914 const StringVector &extMaps = Config_getList(EXTENSION_MAPPING);
11915 for (const auto &mapping : extMaps)
11916 {
11917 QCString mapStr = mapping;
11918 int i=mapStr.find('=');
11919 if (i==-1)
11920 {
11921 continue;
11922 }
11923 else
11924 {
11925 QCString ext = mapStr.left(i).stripWhiteSpace().lower();
11926 QCString language = mapStr.mid(i+1).stripWhiteSpace().lower();
11927 if (ext.isEmpty() || language.isEmpty())
11928 {
11929 continue;
11930 }
11931
11932 if (!updateLanguageMapping(ext,language))
11933 {
11934 err("Failed to map file extension '{}' to unsupported language '{}'.\n"
11935 "Check the EXTENSION_MAPPING setting in the config file.\n",
11936 ext,language);
11937 }
11938 else
11939 {
11940 msg("Adding custom extension mapping: '{}' will be treated as language '{}'\n",
11941 ext,language);
11942 }
11943 }
11944 }
11945 // create input file exncodings
11946
11947 // check INPUT_ENCODING
11948 void *cd = portable_iconv_open("UTF-8",Config_getString(INPUT_ENCODING).data());
11949 if (cd==reinterpret_cast<void *>(-1))
11950 {
11951 term("unsupported character conversion: '{}'->'UTF-8': {}\n"
11952 "Check the 'INPUT_ENCODING' setting in the config file!\n",
11953 Config_getString(INPUT_ENCODING),strerror(errno));
11954 }
11955 else
11956 {
11958 }
11959
11960 // check and split INPUT_FILE_ENCODING
11961 const StringVector &fileEncod = Config_getList(INPUT_FILE_ENCODING);
11962 for (const auto &mapping : fileEncod)
11963 {
11964 QCString mapStr = mapping;
11965 int i=mapStr.find('=');
11966 if (i==-1)
11967 {
11968 continue;
11969 }
11970 else
11971 {
11972 QCString pattern = mapStr.left(i).stripWhiteSpace().lower();
11973 QCString encoding = mapStr.mid(i+1).stripWhiteSpace().lower();
11974 if (pattern.isEmpty() || encoding.isEmpty())
11975 {
11976 continue;
11977 }
11978 cd = portable_iconv_open("UTF-8",encoding.data());
11979 if (cd==reinterpret_cast<void *>(-1))
11980 {
11981 term("unsupported character conversion: '{}'->'UTF-8': {}\n"
11982 "Check the 'INPUT_FILE_ENCODING' setting in the config file!\n",
11983 encoding,strerror(errno));
11984 }
11985 else
11986 {
11988 }
11989
11990 Doxygen::inputFileEncodingList.emplace_back(pattern, encoding);
11991 }
11992 }
11993
11994 // add predefined macro name to a dictionary
11995 const StringVector &expandAsDefinedList =Config_getList(EXPAND_AS_DEFINED);
11996 for (const auto &s : expandAsDefinedList)
11997 {
11999 }
12000
12001 // read aliases and store them in a dictionary
12002 readAliases();
12003
12004 // store number of spaces in a tab into Doxygen::spaces
12005 int tabSize = Config_getInt(TAB_SIZE);
12006 Doxygen::spaces.resize(tabSize);
12007 for (int sp=0; sp<tabSize; sp++) Doxygen::spaces.at(sp)=' ';
12008 Doxygen::spaces.at(tabSize)='\0';
12009}
12010
12011#ifdef HAS_SIGNALS
12012static void stopDoxygen(int)
12013{
12014 signal(SIGINT,SIG_DFL); // Re-register signal handler for default action
12015 Dir thisDir;
12016 msg("Cleaning up...\n");
12017 if (!Doxygen::filterDBFileName.isEmpty())
12018 {
12019 thisDir.remove(Doxygen::filterDBFileName.str());
12020 }
12021 killpg(0,SIGINT);
12023 exitTracing();
12024 exit(1);
12025}
12026#endif
12027
12028static void writeTagFile()
12029{
12030 QCString generateTagFile = Config_getString(GENERATE_TAGFILE);
12031 if (generateTagFile.isEmpty()) return;
12032
12033 std::ofstream f = Portable::openOutputStream(generateTagFile);
12034 if (!f.is_open())
12035 {
12036 err("cannot open tag file {} for writing\n", generateTagFile);
12037 return;
12038 }
12039 TextStream tagFile(&f);
12040 tagFile << "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>\n";
12041 tagFile << "<tagfile doxygen_version=\"" << getDoxygenVersion() << "\"";
12042 std::string gitVersion = getGitVersion();
12043 if (!gitVersion.empty())
12044 {
12045 tagFile << " doxygen_gitid=\"" << gitVersion << "\"";
12046 }
12047 tagFile << ">\n";
12048
12049 // for each file
12050 for (const auto &fn : *Doxygen::inputNameLinkedMap)
12051 {
12052 for (const auto &fd : *fn)
12053 {
12054 if (fd->isLinkableInProject()) fd->writeTagFile(tagFile);
12055 }
12056 }
12057 // for each class
12058 for (const auto &cd : *Doxygen::classLinkedMap)
12059 {
12060 ClassDefMutable *cdm = toClassDefMutable(cd.get());
12061 if (cdm && cdm->isLinkableInProject())
12062 {
12063 cdm->writeTagFile(tagFile);
12064 }
12065 }
12066 // for each concept
12067 for (const auto &cd : *Doxygen::conceptLinkedMap)
12068 {
12069 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
12070 if (cdm && cdm->isLinkableInProject())
12071 {
12072 cdm->writeTagFile(tagFile);
12073 }
12074 }
12075 // for each namespace
12076 for (const auto &nd : *Doxygen::namespaceLinkedMap)
12077 {
12079 if (ndm && nd->isLinkableInProject())
12080 {
12081 ndm->writeTagFile(tagFile);
12082 }
12083 }
12084 // for each group
12085 for (const auto &gd : *Doxygen::groupLinkedMap)
12086 {
12087 if (gd->isLinkableInProject()) gd->writeTagFile(tagFile);
12088 }
12089 // for each module
12090 for (const auto &mod : ModuleManager::instance().modules())
12091 {
12092 if (mod->isLinkableInProject()) mod->writeTagFile(tagFile);
12093 }
12094 // for each page
12095 for (const auto &pd : *Doxygen::pageLinkedMap)
12096 {
12097 if (pd->isLinkableInProject()) pd->writeTagFile(tagFile);
12098 }
12099 // for each directory
12100 for (const auto &dd : *Doxygen::dirLinkedMap)
12101 {
12102 if (dd->isLinkableInProject()) dd->writeTagFile(tagFile);
12103 }
12104 if (Doxygen::mainPage) Doxygen::mainPage->writeTagFile(tagFile);
12105
12106 tagFile << "</tagfile>\n";
12107}
12108
12109static void exitDoxygen() noexcept
12110{
12111 if (!g_successfulRun) // premature exit
12112 {
12113 Dir thisDir;
12114 msg("Exiting...\n");
12115 if (!Doxygen::filterDBFileName.isEmpty())
12116 {
12117 thisDir.remove(Doxygen::filterDBFileName.str());
12118 }
12119 }
12120}
12121
12122static QCString createOutputDirectory(const QCString &baseDirName,
12123 const QCString &formatDirName,
12124 const char *defaultDirName)
12125{
12126 QCString result = formatDirName;
12127 if (result.isEmpty())
12128 {
12129 result = baseDirName + defaultDirName;
12130 }
12131 else if (formatDirName[0]!='/' && (formatDirName.length()==1 || formatDirName[1]!=':'))
12132 {
12133 result.prepend(baseDirName+"/");
12134 }
12135 Dir formatDir(result.str());
12136 if (!formatDir.exists() && !formatDir.mkdir(result.str()))
12137 {
12138 term("Could not create output directory {}\n", result);
12139 }
12140 return result;
12141}
12142
12144{
12145 StringUnorderedSet killSet;
12146
12147 const StringVector &exclPatterns = Config_getList(EXCLUDE_PATTERNS);
12148 bool alwaysRecursive = Config_getBool(RECURSIVE);
12149 StringUnorderedSet excludeNameSet;
12150
12151 // gather names of all files in the include path
12152 g_s.begin("Searching for include files...\n");
12153 killSet.clear();
12154 const StringVector &includePathList = Config_getList(INCLUDE_PATH);
12155 for (const auto &s : includePathList)
12156 {
12157 size_t plSize = Config_getList(INCLUDE_FILE_PATTERNS).size();
12158 const StringVector &pl = plSize==0 ? Config_getList(FILE_PATTERNS) :
12159 Config_getList(INCLUDE_FILE_PATTERNS);
12160 readFileOrDirectory(s, // s
12162 nullptr, // exclSet
12163 &pl, // patList
12164 &exclPatterns, // exclPatList
12165 nullptr, // resultList
12166 nullptr, // resultSet
12167 false, // INCLUDE_PATH isn't recursive
12168 TRUE, // errorIfNotExist
12169 &killSet); // killSet
12170 }
12171 g_s.end();
12172
12173 g_s.begin("Searching for example files...\n");
12174 killSet.clear();
12175 const StringVector &examplePathList = Config_getList(EXAMPLE_PATH);
12176 for (const auto &s : examplePathList)
12177 {
12178 readFileOrDirectory(s, // s
12180 nullptr, // exclSet
12181 &Config_getList(EXAMPLE_PATTERNS), // patList
12182 nullptr, // exclPatList
12183 nullptr, // resultList
12184 nullptr, // resultSet
12185 (alwaysRecursive || Config_getBool(EXAMPLE_RECURSIVE)), // recursive
12186 TRUE, // errorIfNotExist
12187 &killSet); // killSet
12188 }
12189 g_s.end();
12190
12191 g_s.begin("Searching for images...\n");
12192 killSet.clear();
12193 const StringVector &imagePathList=Config_getList(IMAGE_PATH);
12194 for (const auto &s : imagePathList)
12195 {
12196 readFileOrDirectory(s, // s
12198 nullptr, // exclSet
12199 nullptr, // patList
12200 nullptr, // exclPatList
12201 nullptr, // resultList
12202 nullptr, // resultSet
12203 alwaysRecursive, // recursive
12204 TRUE, // errorIfNotExist
12205 &killSet); // killSet
12206 }
12207 g_s.end();
12208
12209 g_s.begin("Searching for dot files...\n");
12210 killSet.clear();
12211 const StringVector &dotFileList=Config_getList(DOTFILE_DIRS);
12212 for (const auto &s : dotFileList)
12213 {
12214 readFileOrDirectory(s, // s
12216 nullptr, // exclSet
12217 nullptr, // patList
12218 nullptr, // exclPatList
12219 nullptr, // resultList
12220 nullptr, // resultSet
12221 alwaysRecursive, // recursive
12222 TRUE, // errorIfNotExist
12223 &killSet); // killSet
12224 }
12225 g_s.end();
12226
12227 g_s.begin("Searching for msc files...\n");
12228 killSet.clear();
12229 const StringVector &mscFileList=Config_getList(MSCFILE_DIRS);
12230 for (const auto &s : mscFileList)
12231 {
12232 readFileOrDirectory(s, // s
12234 nullptr, // exclSet
12235 nullptr, // patList
12236 nullptr, // exclPatList
12237 nullptr, // resultList
12238 nullptr, // resultSet
12239 alwaysRecursive, // recursive
12240 TRUE, // errorIfNotExist
12241 &killSet); // killSet
12242 }
12243 g_s.end();
12244
12245 g_s.begin("Searching for dia files...\n");
12246 killSet.clear();
12247 const StringVector &diaFileList=Config_getList(DIAFILE_DIRS);
12248 for (const auto &s : diaFileList)
12249 {
12250 readFileOrDirectory(s, // s
12252 nullptr, // exclSet
12253 nullptr, // patList
12254 nullptr, // exclPatList
12255 nullptr, // resultList
12256 nullptr, // resultSet
12257 alwaysRecursive, // recursive
12258 TRUE, // errorIfNotExist
12259 &killSet); // killSet
12260 }
12261 g_s.end();
12262
12263 g_s.begin("Searching for plantuml files...\n");
12264 killSet.clear();
12265 const StringVector &plantUmlFileList=Config_getList(PLANTUMLFILE_DIRS);
12266 for (const auto &s : plantUmlFileList)
12267 {
12268 readFileOrDirectory(s, // s
12270 nullptr, // exclSet
12271 nullptr, // patList
12272 nullptr, // exclPatList
12273 nullptr, // resultList
12274 nullptr, // resultSet
12275 alwaysRecursive, // recursive
12276 TRUE, // errorIfNotExist
12277 &killSet); // killSet
12278 }
12279 g_s.end();
12280
12281 g_s.begin("Searching for files to exclude\n");
12282 const StringVector &excludeList = Config_getList(EXCLUDE);
12283 for (const auto &s : excludeList)
12284 {
12285 readFileOrDirectory(s, // s
12286 nullptr, // fnDict
12287 nullptr, // exclSet
12288 &Config_getList(FILE_PATTERNS), // patList
12289 nullptr, // exclPatList
12290 nullptr, // resultList
12291 &excludeNameSet, // resultSet
12292 alwaysRecursive, // recursive
12293 FALSE); // errorIfNotExist
12294 }
12295 g_s.end();
12296
12297 /**************************************************************************
12298 * Determine Input Files *
12299 **************************************************************************/
12300
12301 g_s.begin("Searching INPUT for files to process...\n");
12302 killSet.clear();
12303 Doxygen::inputPaths.clear();
12304 const StringVector &inputList=Config_getList(INPUT);
12305 for (const auto &s : inputList)
12306 {
12307 QCString path = s;
12308 size_t l = path.length();
12309 if (l>0)
12310 {
12311 // strip trailing slashes
12312 if (path.at(l-1)=='\\' || path.at(l-1)=='/') path=path.left(l-1);
12313
12315 path, // s
12317 &excludeNameSet, // exclSet
12318 &Config_getList(FILE_PATTERNS), // patList
12319 &exclPatterns, // exclPatList
12320 &g_inputFiles, // resultList
12321 nullptr, // resultSet
12322 alwaysRecursive, // recursive
12323 TRUE, // errorIfNotExist
12324 &killSet, // killSet
12325 &Doxygen::inputPaths); // paths
12326 }
12327 }
12328
12329 // Sort the FileDef objects by full path to get a predictable ordering over multiple runs
12330 std::stable_sort(Doxygen::inputNameLinkedMap->begin(),
12332 [](const auto &f1,const auto &f2)
12333 {
12334 return qstricmp_sort(f1->fullName(),f2->fullName())<0;
12335 });
12336 for (auto &fileName : *Doxygen::inputNameLinkedMap)
12337 {
12338 if (fileName->size()>1)
12339 {
12340 std::stable_sort(fileName->begin(),fileName->end(),[](const auto &f1,const auto &f2)
12341 {
12342 return qstricmp_sort(f1->absFilePath(),f2->absFilePath())<0;
12343 });
12344 }
12345 }
12346 if (Doxygen::inputNameLinkedMap->empty())
12347 {
12348 warn_uncond("No files to be processed, please check your settings, in particular INPUT, FILE_PATTERNS, and RECURSIVE\n");
12349 }
12350 g_s.end();
12351}
12352
12353
12355{
12356 if (Config_getBool(MARKDOWN_SUPPORT))
12357 {
12358 QCString mdfileAsMainPage = Config_getString(USE_MDFILE_AS_MAINPAGE);
12359 if (mdfileAsMainPage.isEmpty()) return;
12360 FileInfo fi(mdfileAsMainPage.data());
12361 if (!fi.exists())
12362 {
12363 warn_uncond("Specified markdown mainpage '{}' does not exist\n",mdfileAsMainPage);
12364 return;
12365 }
12366 bool ambig = false;
12367 if (findFileDef(Doxygen::inputNameLinkedMap,fi.absFilePath(),ambig)==nullptr)
12368 {
12369 warn_uncond("Specified markdown mainpage '{}' has not been defined as input file\n",mdfileAsMainPage);
12370 return;
12371 }
12372 }
12373}
12374
12376{
12377 AUTO_TRACE();
12378 std::atexit(exitDoxygen);
12379
12380 Portable::correctPath(Config_getList(EXTERNAL_TOOL_PATH));
12381
12382#if USE_LIBCLANG
12383 Doxygen::clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING);
12384#endif
12385
12386 // we would like to show the versionString earlier, but we first have to handle the configuration file
12387 // to know the value of the QUIET setting.
12388 QCString versionString = getFullVersion();
12389 msg("Doxygen version used: {}\n",versionString);
12390
12392
12393 /**************************************************************************
12394 * Make sure the output directory exists
12395 **************************************************************************/
12396 QCString outputDirectory = Config_getString(OUTPUT_DIRECTORY);
12397 if (!g_singleComment)
12398 {
12399 if (outputDirectory.isEmpty())
12400 {
12401 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,Dir::currentDirPath());
12402 }
12403 else
12404 {
12405 Dir dir(outputDirectory.str());
12406 if (!dir.exists())
12407 {
12409 if (!dir.mkdir(outputDirectory.str()))
12410 {
12411 term("tag OUTPUT_DIRECTORY: Output directory '{}' does not "
12412 "exist and cannot be created\n",outputDirectory);
12413 }
12414 else
12415 {
12416 msg("Notice: Output directory '{}' does not exist. "
12417 "I have created it for you.\n", outputDirectory);
12418 }
12419 dir.setPath(outputDirectory.str());
12420 }
12421 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,dir.absPath());
12422 }
12423 }
12424 AUTO_TRACE_ADD("outputDirectory={}",outputDirectory);
12425
12426 /**************************************************************************
12427 * Initialize global lists and dictionaries
12428 **************************************************************************/
12429
12430 // also scale lookup cache with SYMBOL_CACHE_SIZE
12431 int cacheSize = Config_getInt(LOOKUP_CACHE_SIZE);
12432 if (cacheSize<0) cacheSize=0;
12433 if (cacheSize>9) cacheSize=9;
12434 uint32_t lookupSize = 65536 << cacheSize;
12437
12438#ifdef HAS_SIGNALS
12439 signal(SIGINT, stopDoxygen);
12440#endif
12441
12442 uint32_t pid = Portable::pid();
12443 Doxygen::filterDBFileName.sprintf("doxygen_filterdb_%d.tmp",pid);
12444 Doxygen::filterDBFileName.prepend(outputDirectory+"/");
12445
12446 /**************************************************************************
12447 * Check/create output directories *
12448 **************************************************************************/
12449
12450 bool generateHtml = Config_getBool(GENERATE_HTML);
12451 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
12452 bool generateXml = Config_getBool(GENERATE_XML);
12453 bool generateLatex = Config_getBool(GENERATE_LATEX);
12454 bool generateRtf = Config_getBool(GENERATE_RTF);
12455 bool generateMan = Config_getBool(GENERATE_MAN);
12456 bool generateSql = Config_getBool(GENERATE_SQLITE3);
12457 QCString htmlOutput;
12458 QCString docbookOutput;
12459 QCString xmlOutput;
12460 QCString latexOutput;
12461 QCString rtfOutput;
12462 QCString manOutput;
12463 QCString sqlOutput;
12464
12465 if (!g_singleComment)
12466 {
12467 if (generateHtml)
12468 {
12469 htmlOutput = createOutputDirectory(outputDirectory,Config_getString(HTML_OUTPUT),"/html");
12470 Config_updateString(HTML_OUTPUT,htmlOutput);
12471
12472 QCString sitemapUrl = Config_getString(SITEMAP_URL);
12473 bool generateSitemap = !sitemapUrl.isEmpty();
12474 if (generateSitemap && !sitemapUrl.endsWith("/"))
12475 {
12476 Config_updateString(SITEMAP_URL,sitemapUrl+"/");
12477 }
12478
12479 // add HTML indexers that are enabled
12480 bool generateHtmlHelp = Config_getBool(GENERATE_HTMLHELP);
12481 bool generateEclipseHelp = Config_getBool(GENERATE_ECLIPSEHELP);
12482 bool generateQhp = Config_getBool(GENERATE_QHP);
12483 bool generateTreeView = Config_getBool(GENERATE_TREEVIEW);
12484 bool generateDocSet = Config_getBool(GENERATE_DOCSET);
12485 if (generateEclipseHelp) Doxygen::indexList->addIndex<EclipseHelp>();
12486 if (generateHtmlHelp) Doxygen::indexList->addIndex<HtmlHelp>();
12487 if (generateQhp) Doxygen::indexList->addIndex<Qhp>();
12488 if (generateSitemap) Doxygen::indexList->addIndex<Sitemap>();
12489 if (generateTreeView) Doxygen::indexList->addIndex<FTVHelp>(TRUE);
12490 if (generateDocSet) Doxygen::indexList->addIndex<DocSets>();
12491 Doxygen::indexList->addIndex<Crawlmap>();
12492 Doxygen::indexList->initialize();
12493 }
12494
12495 if (generateDocbook)
12496 {
12497 docbookOutput = createOutputDirectory(outputDirectory,Config_getString(DOCBOOK_OUTPUT),"/docbook");
12498 Config_updateString(DOCBOOK_OUTPUT,docbookOutput);
12499 }
12500
12501 if (generateXml)
12502 {
12503 xmlOutput = createOutputDirectory(outputDirectory,Config_getString(XML_OUTPUT),"/xml");
12504 Config_updateString(XML_OUTPUT,xmlOutput);
12505 }
12506
12507 if (generateLatex)
12508 {
12509 latexOutput = createOutputDirectory(outputDirectory,Config_getString(LATEX_OUTPUT), "/latex");
12510 Config_updateString(LATEX_OUTPUT,latexOutput);
12511 }
12512
12513 if (generateRtf)
12514 {
12515 rtfOutput = createOutputDirectory(outputDirectory,Config_getString(RTF_OUTPUT),"/rtf");
12516 Config_updateString(RTF_OUTPUT,rtfOutput);
12517 }
12518
12519 if (generateMan)
12520 {
12521 manOutput = createOutputDirectory(outputDirectory,Config_getString(MAN_OUTPUT),"/man");
12522 Config_updateString(MAN_OUTPUT,manOutput);
12523 }
12524
12525 if (generateSql)
12526 {
12527 sqlOutput = createOutputDirectory(outputDirectory,Config_getString(SQLITE3_OUTPUT),"/sqlite3");
12528 Config_updateString(SQLITE3_OUTPUT,sqlOutput);
12529 }
12530 }
12531
12532 if (Config_getBool(HAVE_DOT))
12533 {
12534 QCString curFontPath = Config_getString(DOT_FONTPATH);
12535 if (curFontPath.isEmpty())
12536 {
12537 Portable::getenv("DOTFONTPATH");
12538 QCString newFontPath = ".";
12539 if (!curFontPath.isEmpty())
12540 {
12541 newFontPath+=Portable::pathListSeparator();
12542 newFontPath+=curFontPath;
12543 }
12544 Portable::setenv("DOTFONTPATH",qPrint(newFontPath));
12545 }
12546 else
12547 {
12548 Portable::setenv("DOTFONTPATH",qPrint(curFontPath));
12549 }
12550 }
12551
12552 /**************************************************************************
12553 * Handle layout file *
12554 **************************************************************************/
12555
12557 QCString layoutFileName = Config_getString(LAYOUT_FILE);
12558 bool defaultLayoutUsed = FALSE;
12559 if (layoutFileName.isEmpty())
12560 {
12561 layoutFileName = Config_updateString(LAYOUT_FILE,"DoxygenLayout.xml");
12562 defaultLayoutUsed = TRUE;
12563 }
12564 AUTO_TRACE_ADD("defaultLayoutUsed={}, layoutFileName={}",defaultLayoutUsed,layoutFileName);
12565
12566 FileInfo fi(layoutFileName.str());
12567 if (fi.exists())
12568 {
12569 msg("Parsing layout file {}...\n",layoutFileName);
12570 LayoutDocManager::instance().parse(layoutFileName);
12571 }
12572 else if (!defaultLayoutUsed)
12573 {
12574 warn_uncond("failed to open layout file '{}' for reading! Using default settings.\n",layoutFileName);
12575 }
12576 printLayout();
12577
12578 /**************************************************************************
12579 * Read and preprocess input *
12580 **************************************************************************/
12581
12582 // prevent search in the output directories
12583 StringVector exclPatterns = Config_getList(EXCLUDE_PATTERNS);
12584 if (generateHtml) exclPatterns.push_back(htmlOutput.str());
12585 if (generateDocbook) exclPatterns.push_back(docbookOutput.str());
12586 if (generateXml) exclPatterns.push_back(xmlOutput.str());
12587 if (generateLatex) exclPatterns.push_back(latexOutput.str());
12588 if (generateRtf) exclPatterns.push_back(rtfOutput.str());
12589 if (generateMan) exclPatterns.push_back(manOutput.str());
12590 Config_updateList(EXCLUDE_PATTERNS,exclPatterns);
12591
12592 if (!g_singleComment)
12593 {
12595
12597 }
12598
12599 // Notice: the order of the function calls below is very important!
12600
12601 if (generateHtml && !Config_getBool(USE_MATHJAX))
12602 {
12604 }
12605 if (generateRtf)
12606 {
12608 }
12609 if (generateDocbook)
12610 {
12612 }
12613
12615
12616 /**************************************************************************
12617 * Handle Tag Files *
12618 **************************************************************************/
12619
12620 std::shared_ptr<Entry> root = std::make_shared<Entry>();
12621
12622 if (!g_singleComment)
12623 {
12624 msg("Reading and parsing tag files\n");
12625 const StringVector &tagFileList = Config_getList(TAGFILES);
12626 for (const auto &s : tagFileList)
12627 {
12628 readTagFile(root,s.c_str());
12629 }
12630 }
12631
12632 /**************************************************************************
12633 * Parse source files *
12634 **************************************************************************/
12635
12636 addSTLSupport(root);
12637
12638 g_s.begin("Parsing files\n");
12639 if (g_singleComment)
12640 {
12641 //printf("Parsing comment %s\n",qPrint(g_commentFileName));
12642 if (g_commentFileName=="-")
12643 {
12644 std::string text = fileToString(g_commentFileName).str();
12645 addTerminalCharIfMissing(text,'\n');
12646 generateHtmlForComment("stdin.md",text);
12647 }
12648 else if (FileInfo(g_commentFileName.str()).isFile())
12649 {
12650 std::string text;
12652 addTerminalCharIfMissing(text,'\n');
12654 }
12655 else
12656 {
12657 }
12659 exit(0);
12660 }
12661 else
12662 {
12663 if (Config_getInt(NUM_PROC_THREADS)==1)
12664 {
12666 }
12667 else
12668 {
12670 }
12671 }
12672 g_s.end();
12673
12674 /**************************************************************************
12675 * Gather information *
12676 **************************************************************************/
12677
12678 g_s.begin("Building macro definition list...\n");
12680 g_s.end();
12681
12682 g_s.begin("Building group list...\n");
12683 buildGroupList(root.get());
12684 organizeSubGroups(root.get());
12685 g_s.end();
12686
12687 g_s.begin("Building directory list...\n");
12689 findDirDocumentation(root.get());
12690 g_s.end();
12691
12692 g_s.begin("Building namespace list...\n");
12693 buildNamespaceList(root.get());
12694 findUsingDirectives(root.get());
12695 g_s.end();
12696
12697 g_s.begin("Building file list...\n");
12698 buildFileList(root.get());
12699 g_s.end();
12700
12701 g_s.begin("Building class list...\n");
12702 buildClassList(root.get());
12703 g_s.end();
12704
12705 g_s.begin("Building concept list...\n");
12706 buildConceptList(root.get());
12707 g_s.end();
12708
12709 // build list of using declarations here (global list)
12710 buildListOfUsingDecls(root.get());
12711 g_s.end();
12712
12713 g_s.begin("Computing nesting relations for classes...\n");
12715 g_s.end();
12716 // 1.8.2-20121111: no longer add nested classes to the group as well
12717 //distributeClassGroupRelations();
12718
12719 // calling buildClassList may result in cached relations that
12720 // become invalid after resolveClassNestingRelations(), that's why
12721 // we need to clear the cache here
12722 Doxygen::typeLookupCache->clear();
12723 // we don't need the list of using declaration anymore
12724 g_usingDeclarations.clear();
12725
12726 g_s.begin("Associating documentation with classes...\n");
12727 buildClassDocList(root.get());
12728 g_s.end();
12729
12730 g_s.begin("Associating documentation with concepts...\n");
12731 buildConceptDocList(root.get());
12733 g_s.end();
12734
12735 g_s.begin("Associating documentation with modules...\n");
12736 findModuleDocumentation(root.get());
12737 g_s.end();
12738
12739 g_s.begin("Building example list...\n");
12740 buildExampleList(root.get());
12741 g_s.end();
12742
12743 g_s.begin("Searching for enumerations...\n");
12744 findEnums(root.get());
12745 g_s.end();
12746
12747 // Since buildVarList calls isVarWithConstructor
12748 // and this calls getResolvedClass we need to process
12749 // typedefs first so the relations between classes via typedefs
12750 // are properly resolved. See bug 536385 for an example.
12751 g_s.begin("Searching for documented typedefs...\n");
12752 buildTypedefList(root.get());
12753 g_s.end();
12754
12755 if (Config_getBool(OPTIMIZE_OUTPUT_SLICE))
12756 {
12757 g_s.begin("Searching for documented sequences...\n");
12758 buildSequenceList(root.get());
12759 g_s.end();
12760
12761 g_s.begin("Searching for documented dictionaries...\n");
12762 buildDictionaryList(root.get());
12763 g_s.end();
12764 }
12765
12766 g_s.begin("Searching for members imported via using declarations...\n");
12767 // this should be after buildTypedefList in order to properly import
12768 // used typedefs
12769 findUsingDeclarations(root.get(),TRUE); // do for python packages first
12770 findUsingDeclarations(root.get(),FALSE); // then the rest
12771 g_s.end();
12772
12773 g_s.begin("Searching for included using directives...\n");
12775 g_s.end();
12776
12777 g_s.begin("Searching for documented variables...\n");
12778 buildVarList(root.get());
12779 g_s.end();
12780
12781 g_s.begin("Building interface member list...\n");
12782 buildInterfaceAndServiceList(root.get()); // UNO IDL
12783
12784 g_s.begin("Building member list...\n"); // using class info only !
12785 buildFunctionList(root.get());
12786 g_s.end();
12787
12788 g_s.begin("Searching for friends...\n");
12789 findFriends();
12790 g_s.end();
12791
12792 g_s.begin("Searching for documented defines...\n");
12793 findDefineDocumentation(root.get());
12794 g_s.end();
12795
12796 g_s.begin("Computing class inheritance relations...\n");
12797 findClassEntries(root.get());
12799 g_s.end();
12800
12801 g_s.begin("Computing class usage relations...\n");
12803 g_s.end();
12804
12805 if (Config_getBool(INLINE_SIMPLE_STRUCTS))
12806 {
12807 g_s.begin("Searching for tag less structs...\n");
12809 g_s.end();
12810 }
12811
12812 g_s.begin("Flushing cached template relations that have become invalid...\n");
12814 g_s.end();
12815
12816 g_s.begin("Warn for undocumented namespaces...\n");
12818 g_s.end();
12819
12820 g_s.begin("Computing class relations...\n");
12823 if (Config_getBool(OPTIMIZE_OUTPUT_VHDL))
12824 {
12826 }
12828 g_classEntries.clear();
12829 g_s.end();
12830
12831 g_s.begin("Add enum values to enums...\n");
12832 addEnumValuesToEnums(root.get());
12833 findEnumDocumentation(root.get());
12834 g_s.end();
12835
12836 g_s.begin("Searching for member function documentation...\n");
12837 findObjCMethodDefinitions(root.get());
12838 findMemberDocumentation(root.get()); // may introduce new members !
12839 findUsingDeclImports(root.get()); // may introduce new members !
12840 g_usingClassMap.clear();
12844 g_s.end();
12845
12846 // moved to after finding and copying documentation,
12847 // as this introduces new members see bug 722654
12848 g_s.begin("Creating members for template instances...\n");
12850 g_s.end();
12851
12852 g_s.begin("Building page list...\n");
12853 buildPageList(root.get());
12854 g_s.end();
12855
12856 g_s.begin("Search for main page...\n");
12857 findMainPage(root.get());
12858 findMainPageTagFiles(root.get());
12859 g_s.end();
12860
12861 g_s.begin("Computing page relations...\n");
12862 computePageRelations(root.get());
12864 g_s.end();
12865
12866 g_s.begin("Determining the scope of groups...\n");
12867 findGroupScope(root.get());
12868 g_s.end();
12869
12870 g_s.begin("Computing module relations...\n");
12871 auto &mm = ModuleManager::instance();
12872 mm.resolvePartitions();
12873 mm.resolveImports();
12874 mm.collectExportedSymbols();
12875 g_s.end();
12876
12877 auto memberNameComp = [](const MemberNameLinkedMap::Ptr &n1,const MemberNameLinkedMap::Ptr &n2)
12878 {
12879 return qstricmp_sort(n1->memberName().data()+getPrefixIndex(n1->memberName()),
12880 n2->memberName().data()+getPrefixIndex(n2->memberName())
12881 )<0;
12882 };
12883
12884 auto classComp = [](const ClassLinkedMap::Ptr &c1,const ClassLinkedMap::Ptr &c2)
12885 {
12886 if (Config_getBool(SORT_BY_SCOPE_NAME))
12887 {
12888 return qstricmp_sort(c1->name(), c2->name())<0;
12889 }
12890 else
12891 {
12892 int i = qstricmp_sort(c1->className(), c2->className());
12893 return i==0 ? qstricmp_sort(c1->name(), c2->name())<0 : i<0;
12894 }
12895 };
12896
12897 auto namespaceComp = [](const NamespaceLinkedMap::Ptr &n1,const NamespaceLinkedMap::Ptr &n2)
12898 {
12899 return qstricmp_sort(n1->name(),n2->name())<0;
12900 };
12901
12902 auto conceptComp = [](const ConceptLinkedMap::Ptr &c1,const ConceptLinkedMap::Ptr &c2)
12903 {
12904 return qstricmp_sort(c1->name(),c2->name())<0;
12905 };
12906
12907 g_s.begin("Sorting lists...\n");
12908 std::stable_sort(Doxygen::memberNameLinkedMap->begin(),
12910 memberNameComp);
12911 std::stable_sort(Doxygen::functionNameLinkedMap->begin(),
12913 memberNameComp);
12914 std::stable_sort(Doxygen::hiddenClassLinkedMap->begin(),
12916 classComp);
12917 std::stable_sort(Doxygen::classLinkedMap->begin(),
12919 classComp);
12920 std::stable_sort(Doxygen::conceptLinkedMap->begin(),
12922 conceptComp);
12923 std::stable_sort(Doxygen::namespaceLinkedMap->begin(),
12925 namespaceComp);
12926 g_s.end();
12927
12928 g_s.begin("Determining which enums are documented\n");
12930 g_s.end();
12931
12932 g_s.begin("Computing member relations...\n");
12935 g_s.end();
12936
12937 g_s.begin("Building full member lists recursively...\n");
12939 g_s.end();
12940
12941 g_s.begin("Adding members to member groups.\n");
12943 g_s.end();
12944
12945 if (Config_getBool(DISTRIBUTE_GROUP_DOC))
12946 {
12947 g_s.begin("Distributing member group documentation.\n");
12949 g_s.end();
12950 }
12951
12952 g_s.begin("Computing member references...\n");
12954 g_s.end();
12955
12956 if (Config_getBool(INHERIT_DOCS))
12957 {
12958 g_s.begin("Inheriting documentation...\n");
12960 g_s.end();
12961 }
12962
12963
12964 // compute the shortest possible names of all files
12965 // without losing the uniqueness of the file names.
12966 g_s.begin("Generating disk names...\n");
12968 g_s.end();
12969
12970 g_s.begin("Adding source references...\n");
12972 g_s.end();
12973
12974 g_s.begin("Adding xrefitems...\n");
12977 g_s.end();
12978
12979 g_s.begin("Sorting member lists...\n");
12981 g_s.end();
12982
12983 g_s.begin("Setting anonymous enum type...\n");
12985 g_s.end();
12986
12987 g_s.begin("Computing dependencies between directories...\n");
12989 g_s.end();
12990
12991 g_s.begin("Generating citations page...\n");
12993 g_s.end();
12994
12995 g_s.begin("Counting members...\n");
12996 countMembers();
12997 g_s.end();
12998
12999 g_s.begin("Counting data structures...\n");
13001 g_s.end();
13002
13003 g_s.begin("Resolving user defined references...\n");
13005 g_s.end();
13006
13007 g_s.begin("Finding anchors and sections in the documentation...\n");
13009 g_s.end();
13010
13011 g_s.begin("Transferring function references...\n");
13013 g_s.end();
13014
13015 g_s.begin("Combining using relations...\n");
13017 g_s.end();
13018
13020 g_s.begin("Adding members to index pages...\n");
13022 addToIndices();
13023 g_s.end();
13024
13025 g_s.begin("Correcting members for VHDL...\n");
13027 g_s.end();
13028
13029 g_s.begin("Computing tooltip texts...\n");
13031 g_s.end();
13032
13033 if (Config_getBool(SORT_GROUP_NAMES))
13034 {
13035 std::stable_sort(Doxygen::groupLinkedMap->begin(),
13037 [](const auto &g1,const auto &g2)
13038 { return g1->groupTitle() < g2->groupTitle(); });
13039
13040 for (const auto &gd : *Doxygen::groupLinkedMap)
13041 {
13042 gd->sortSubGroups();
13043 }
13044 }
13045
13046 printNavTree(root.get(),0);
13048}
13049
13051{
13052 AUTO_TRACE();
13053 /**************************************************************************
13054 * Initialize output generators *
13055 **************************************************************************/
13056
13057 /// add extra languages for which we can only produce syntax highlighted code
13059
13060 //// dump all symbols
13061 if (g_dumpSymbolMap)
13062 {
13063 dumpSymbolMap();
13064 exit(0);
13065 }
13066
13067 bool generateHtml = Config_getBool(GENERATE_HTML);
13068 bool generateLatex = Config_getBool(GENERATE_LATEX);
13069 bool generateMan = Config_getBool(GENERATE_MAN);
13070 bool generateRtf = Config_getBool(GENERATE_RTF);
13071 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
13072
13073
13075 if (generateHtml)
13076 {
13080 }
13081 if (generateLatex)
13082 {
13085 }
13086 if (generateDocbook)
13087 {
13090 }
13091 if (generateMan)
13092 {
13093 g_outputList->add<ManGenerator>();
13095 }
13096 if (generateRtf)
13097 {
13098 g_outputList->add<RTFGenerator>();
13100 }
13101 if (Config_getBool(USE_HTAGS))
13102 {
13104 QCString htmldir = Config_getString(HTML_OUTPUT);
13105 if (!Htags::execute(htmldir))
13106 err("USE_HTAGS is YES but htags(1) failed. \n");
13107 else if (!Htags::loadFilemap(htmldir))
13108 err("htags(1) ended normally but failed to load the filemap. \n");
13109 }
13110
13111 /**************************************************************************
13112 * Generate documentation *
13113 **************************************************************************/
13114
13115 g_s.begin("Generating style sheet...\n");
13116 //printf("writing style info\n");
13117 g_outputList->writeStyleInfo(0); // write first part
13118 g_s.end();
13119
13120 bool searchEngine = Config_getBool(SEARCHENGINE);
13121 bool serverBasedSearch = Config_getBool(SERVER_BASED_SEARCH);
13122
13123 g_s.begin("Generating search indices...\n");
13124 if (searchEngine && !serverBasedSearch && generateHtml)
13125 {
13127 }
13128
13129 // generate search indices (need to do this before writing other HTML
13130 // pages as these contain a drop down menu with options depending on
13131 // what categories we find in this function.
13132 if (generateHtml && searchEngine)
13133 {
13134 QCString searchDirName = Config_getString(HTML_OUTPUT)+"/search";
13135 Dir searchDir(searchDirName.str());
13136 if (!searchDir.exists() && !searchDir.mkdir(searchDirName.str()))
13137 {
13138 term("Could not create search results directory '{}' $PWD='{}'\n",
13139 searchDirName,Dir::currentDirPath());
13140 }
13141 HtmlGenerator::writeSearchData(searchDirName);
13142 if (!serverBasedSearch) // client side search index
13143 {
13145 }
13146 }
13147 g_s.end();
13148
13149 // copy static stuff
13150 if (generateHtml)
13151 {
13153 copyLogo(Config_getString(HTML_OUTPUT));
13154 copyIcon(Config_getString(HTML_OUTPUT));
13155 copyExtraFiles(Config_getList(HTML_EXTRA_FILES),"HTML_EXTRA_FILES",Config_getString(HTML_OUTPUT));
13156 }
13157 if (generateLatex)
13158 {
13160 copyLogo(Config_getString(LATEX_OUTPUT));
13161 copyIcon(Config_getString(LATEX_OUTPUT));
13162 copyExtraFiles(Config_getList(LATEX_EXTRA_FILES),"LATEX_EXTRA_FILES",Config_getString(LATEX_OUTPUT));
13163 }
13164 if (generateDocbook)
13165 {
13166 copyLogo(Config_getString(DOCBOOK_OUTPUT));
13167 copyIcon(Config_getString(DOCBOOK_OUTPUT));
13168 }
13169 if (generateRtf)
13170 {
13171 copyLogo(Config_getString(RTF_OUTPUT));
13172 copyIcon(Config_getString(RTF_OUTPUT));
13173 copyExtraFiles(Config_getList(RTF_EXTRA_FILES),"RTF_EXTRA_FILES",Config_getString(RTF_OUTPUT));
13174 }
13175
13177 if (fm.hasFormulas() && generateHtml
13178 && !Config_getBool(USE_MATHJAX))
13179 {
13180 g_s.begin("Generating images for formulas in HTML...\n");
13181 fm.generateImages(Config_getString(HTML_OUTPUT), Config_getEnum(HTML_FORMULA_FORMAT)==HTML_FORMULA_FORMAT_t::svg ?
13183 g_s.end();
13184 }
13185 if (fm.hasFormulas() && generateRtf)
13186 {
13187 g_s.begin("Generating images for formulas in RTF...\n");
13189 g_s.end();
13190 }
13191
13192 if (fm.hasFormulas() && generateDocbook)
13193 {
13194 g_s.begin("Generating images for formulas in Docbook...\n");
13196 g_s.end();
13197 }
13198
13199 g_s.begin("Generating example documentation...\n");
13201 g_s.end();
13202
13203 g_s.begin("Generating file sources...\n");
13205 g_s.end();
13206
13207 g_s.begin("Generating file documentation...\n");
13209 g_s.end();
13210
13211 g_s.begin("Generating page documentation...\n");
13213 g_s.end();
13214
13215 g_s.begin("Generating group documentation...\n");
13217 g_s.end();
13218
13219 g_s.begin("Generating class documentation...\n");
13221 g_s.end();
13222
13223 g_s.begin("Generating concept documentation...\n");
13225 g_s.end();
13226
13227 g_s.begin("Generating module documentation...\n");
13229 g_s.end();
13230
13231 g_s.begin("Generating namespace documentation...\n");
13233 g_s.end();
13234
13235 if (Config_getBool(GENERATE_LEGEND))
13236 {
13237 g_s.begin("Generating graph info page...\n");
13239 g_s.end();
13240 }
13241
13242 g_s.begin("Generating directory documentation...\n");
13244 g_s.end();
13245
13246 if (g_outputList->size()>0)
13247 {
13249 }
13250
13251 g_s.begin("finalizing index lists...\n");
13252 Doxygen::indexList->finalize();
13253 g_s.end();
13254
13255 g_s.begin("writing tag file...\n");
13256 writeTagFile();
13257 g_s.end();
13258
13259 if (Config_getBool(GENERATE_XML))
13260 {
13261 g_s.begin("Generating XML output...\n");
13263 generateXML();
13265 g_s.end();
13266 }
13267 if (Config_getBool(GENERATE_SQLITE3))
13268 {
13269 g_s.begin("Generating SQLITE3 output...\n");
13271 g_s.end();
13272 }
13273
13274 if (Config_getBool(GENERATE_AUTOGEN_DEF))
13275 {
13276 g_s.begin("Generating AutoGen DEF output...\n");
13277 generateDEF();
13278 g_s.end();
13279 }
13280 if (Config_getBool(GENERATE_PERLMOD))
13281 {
13282 g_s.begin("Generating Perl module output...\n");
13284 g_s.end();
13285 }
13286 if (generateHtml && searchEngine && serverBasedSearch)
13287 {
13288 g_s.begin("Generating search index\n");
13289 if (Doxygen::searchIndex.kind()==SearchIndexIntf::Internal) // write own search index
13290 {
13292 Doxygen::searchIndex.write(Config_getString(HTML_OUTPUT)+"/search/search.idx");
13293 }
13294 else // write data for external search index
13295 {
13297 QCString searchDataFile = Config_getString(SEARCHDATA_FILE);
13298 if (searchDataFile.isEmpty())
13299 {
13300 searchDataFile="searchdata.xml";
13301 }
13302 if (!Portable::isAbsolutePath(searchDataFile.data()))
13303 {
13304 searchDataFile.prepend(Config_getString(OUTPUT_DIRECTORY)+"/");
13305 }
13306 Doxygen::searchIndex.write(searchDataFile);
13307 }
13308 g_s.end();
13309 }
13310
13311 if (generateRtf)
13312 {
13313 g_s.begin("Combining RTF output...\n");
13314 if (!RTFGenerator::preProcessFileInplace(Config_getString(RTF_OUTPUT),"refman.rtf"))
13315 {
13316 err("An error occurred during post-processing the RTF files!\n");
13317 }
13318 g_s.end();
13319 }
13320
13321 g_s.begin("Running plantuml with JAVA...\n");
13323 g_s.end();
13324
13325 if (Config_getBool(HAVE_DOT))
13326 {
13327 g_s.begin("Running dot...\n");
13329 g_s.end();
13330 }
13331
13332 if (generateHtml &&
13333 Config_getBool(GENERATE_HTMLHELP) &&
13334 !Config_getString(HHC_LOCATION).isEmpty())
13335 {
13336 g_s.begin("Running html help compiler...\n");
13338 g_s.end();
13339 }
13340
13341 if ( generateHtml &&
13342 Config_getBool(GENERATE_QHP) &&
13343 !Config_getString(QHG_LOCATION).isEmpty())
13344 {
13345 g_s.begin("Running qhelpgenerator...\n");
13347 g_s.end();
13348 }
13349
13350 g_outputList->cleanup();
13351
13352 msg("type lookup cache used {}/{} hits={} misses={}\n",
13354 Doxygen::typeLookupCache->capacity(),
13356 Doxygen::typeLookupCache->misses());
13357 msg("symbol lookup cache used {}/{} hits={} misses={}\n",
13359 Doxygen::symbolLookupCache->capacity(),
13361 Doxygen::symbolLookupCache->misses());
13362 int typeCacheParam = computeIdealCacheParam(static_cast<size_t>(Doxygen::typeLookupCache->misses()*2/3)); // part of the cache is flushed, hence the 2/3 correction factor
13363 int symbolCacheParam = computeIdealCacheParam(static_cast<size_t>(Doxygen::symbolLookupCache->misses()));
13364 int cacheParam = std::max(typeCacheParam,symbolCacheParam);
13365 if (cacheParam>Config_getInt(LOOKUP_CACHE_SIZE))
13366 {
13367 msg("Note: based on cache misses the ideal setting for LOOKUP_CACHE_SIZE is {} at the cost of higher memory usage.\n",cacheParam);
13368 }
13369
13371 {
13372
13373 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
13374 if (numThreads<1) numThreads=1;
13375 msg("Total elapsed time: {:.6f} seconds\n(of which an average of {:.6f} seconds per thread waiting for external tools to finish)\n",
13376 (static_cast<double>(Debug::elapsedTime())),
13377 Portable::getSysElapsedTime()/static_cast<double>(numThreads)
13378 );
13379 g_s.print();
13380
13382 msg("finished...\n");
13384 }
13385 else
13386 {
13387 msg("finished...\n");
13388 }
13389
13390
13391 /**************************************************************************
13392 * Start cleaning up *
13393 **************************************************************************/
13394
13396
13398 Dir thisDir;
13399 thisDir.remove(Doxygen::filterDBFileName.str());
13401 exitTracing();
13403 delete Doxygen::clangUsrMap;
13405
13406 //dumpDocNodeSizes();
13407}
void readAliases()
Definition aliases.cpp:161
constexpr auto prefix
Definition anchor.cpp:44
std::vector< ArgumentList > ArgumentLists
Definition arguments.h:145
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.h:122
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:124
void setIsDeleted(bool b)
Definition arguments.h:123
iterator begin()
Definition arguments.h:93
bool volatileSpecifier() const
Definition arguments.h:112
void setNoParameters(bool b)
Definition arguments.h:125
void setVolatileSpecifier(bool b)
Definition arguments.h:120
Definition cache.h:32
static CitationManager & instance()
Definition cite.cpp:86
void clear()
clears the database
Definition cite.cpp:111
void generatePage()
Generate the citations page.
Definition cite.cpp:332
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 addListReferences()=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 setFileDef(FileDef *fd)=0
virtual void writeTagFile(TextStream &)=0
virtual void setInitializer(const QCString &init)=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:76
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 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:173
A class that generates docset files.
Definition docsets.h:36
static void init()
bool run()
Definition dot.cpp:128
static DotManager * instance()
Definition dot.cpp:78
static NamespaceLinkedMap * namespaceLinkedMap
Definition doxygen.h:115
static ConceptLinkedMap * conceptLinkedMap
Definition doxygen.h:98
static bool suppressDocWarnings
Definition doxygen.h:132
static FileNameLinkedMap * plantUmlFileNameLinkedMap
Definition doxygen.h:110
static bool parseSourcesNeeded
Definition doxygen.h:123
static StringUnorderedSet inputPaths
Definition doxygen.h:104
static std::unique_ptr< PageDef > mainPage
Definition doxygen.h:101
static bool clangAssistedParsing
Definition doxygen.h:138
static StringUnorderedSet expandAsDefinedSet
Definition doxygen.h:119
static FileNameLinkedMap * inputNameLinkedMap
Definition doxygen.h:105
static ParserManager * parserManager
Definition doxygen.h:131
static Cache< std::string, LookupInfo > * typeLookupCache
Definition doxygen.h:127
static InputFileEncodingList inputFileEncodingList
Definition doxygen.h:140
static ClassLinkedMap * classLinkedMap
Definition doxygen.h:96
static MemberNameLinkedMap * functionNameLinkedMap
Definition doxygen.h:112
static PageLinkedMap * exampleLinkedMap
Definition doxygen.h:99
static FileNameLinkedMap * dotFileNameLinkedMap
Definition doxygen.h:107
static NamespaceDefMutable * globalScope
Definition doxygen.h:121
static FileNameLinkedMap * imageNameLinkedMap
Definition doxygen.h:106
static FileNameLinkedMap * mscFileNameLinkedMap
Definition doxygen.h:108
static QCString verifiedDotPath
Definition doxygen.h:139
static MemberGroupInfoMap memberGroupInfoMap
Definition doxygen.h:118
static QCString spaces
Definition doxygen.h:135
static IndexList * indexList
Definition doxygen.h:134
static StaticInitMap staticInitMap
Definition doxygen.h:143
static Cache< std::string, LookupInfo > * symbolLookupCache
Definition doxygen.h:128
static StringMap tagDestinationMap
Definition doxygen.h:116
static std::mutex countFlowKeywordsMutex
Definition doxygen.h:141
static ClassLinkedMap * hiddenClassLinkedMap
Definition doxygen.h:97
static FileNameLinkedMap * diaFileNameLinkedMap
Definition doxygen.h:109
static QCString htmlFileExtension
Definition doxygen.h:122
static QCString filterDBFileName
Definition doxygen.h:133
static PageLinkedMap * pageLinkedMap
Definition doxygen.h:100
static bool generatingXmlOutput
Definition doxygen.h:136
static std::unique_ptr< NamespaceDef > globalNamespaceDef
Definition doxygen.h:120
static DefinesPerFileList macroDefinitions
Definition doxygen.h:137
static DirLinkedMap * dirLinkedMap
Definition doxygen.h:129
static NamespaceAliasInfoMap namespaceAliasMap
Definition doxygen.h:113
static MemberNameLinkedMap * memberNameLinkedMap
Definition doxygen.h:111
static SymbolMap< Definition > * symbolMap
Definition doxygen.h:125
static StringUnorderedSet tagFileSet
Definition doxygen.h:117
static FileNameLinkedMap * includeNameLinkedMap
Definition doxygen.h:102
static FileNameLinkedMap * exampleNameLinkedMap
Definition doxygen.h:103
static SearchIndexIntf searchIndex
Definition doxygen.h:124
static DirRelationLinkedMap dirRelations
Definition doxygen.h:130
static std::mutex addExampleMutex
Definition doxygen.h:142
static ClangUsrMap * clangUsrMap
Definition doxygen.h:126
static GroupLinkedMap * groupLinkedMap
Definition doxygen.h:114
Generator for Eclipse help files.
Definition eclipsehelp.h:44
static EmojiEntityMapper & instance()
Returns the one and only instance of the Emoji entity mapper.
Definition emoji.cpp:1978
void writeEmojiFile(TextStream &t)
Writes the list of supported emojis to the given file.
Definition emoji.cpp:1999
Represents an unstructured piece of information, about an entity found in the sources.
Definition entry.h:116
TextStream initializer
initial value (for variables)
Definition entry.h:197
VhdlSpecifier vhdlSpec
VHDL specifiers.
Definition entry.h:183
bool subGrouping
automatically group class members?
Definition entry.h:188
const std::vector< std::shared_ptr< Entry > > & children() const
Definition entry.h:139
bool proto
prototype ?
Definition entry.h:187
GroupDocType groupDocType
Definition entry.h:230
QCString metaData
Slice metadata.
Definition entry.h:233
int docLine
line number at which the documentation was found
Definition entry.h:201
QCString bitfields
member's bit fields
Definition entry.h:193
ArgumentList typeConstr
where clause (C#) for type constraints
Definition entry.h:215
void markAsProcessed() const
Definition entry.h:167
int endBodyLine
line number where the definition ends
Definition entry.h:218
bool exported
is the symbol exported from a C++20 module
Definition entry.h:189
const TagInfo * tagInfo() const
Definition entry.h:177
QCString includeName
include name (3 arg of \class)
Definition entry.h:199
QCString id
libclang id
Definition entry.h:231
ArgumentLists tArgLists
template argument declarations
Definition entry.h:195
LocalToc localToc
Definition entry.h:232
MethodTypes mtype
signal, slot, (dcop) method, or property?
Definition entry.h:181
@ GROUPDOC_NORMAL
defgroup
Definition entry.h:121
SrcLangExt lang
programming language in which this entry was found
Definition entry.h:227
Entry * parent() const
Definition entry.h:134
QCString inbodyDocs
documentation inside the body of a function
Definition entry.h:206
int startColumn
start column of entry in the source
Definition entry.h:225
QCString relates
related class (doc block)
Definition entry.h:209
bool explicitExternal
explicitly defined as external?
Definition entry.h:186
std::vector< const SectionInfo * > anchors
list of anchors defined in this entry
Definition entry.h:222
QCString fileName
file this entry was extracted from
Definition entry.h:223
RelatesType relatesType
how relates is handled
Definition entry.h:210
QCString write
property write accessor
Definition entry.h:212
QCString args
member argument string
Definition entry.h:192
QCString type
member type
Definition entry.h:173
std::vector< Grouping > groups
list of groups this entry belongs to
Definition entry.h:221
CommandOverrides commandOverrides
store info for commands whose default can be overridden
Definition entry.h:190
QCString exception
throw specification
Definition entry.h:214
int startLine
start line of entry in the source
Definition entry.h:224
QCString req
C++20 requires clause.
Definition entry.h:234
ArgumentList argList
member arguments as a list
Definition entry.h:194
QCString name
member name
Definition entry.h:174
QCString includeFile
include file (2 arg of \class, must be unique)
Definition entry.h:198
int inbodyLine
line number at which the body doc was found
Definition entry.h:207
EntryType section
entry type (see Sections);
Definition entry.h:172
QCString briefFile
file in which the brief desc. was found
Definition entry.h:205
int bodyLine
line number of the body in the source
Definition entry.h:216
int mGrpId
member group id
Definition entry.h:219
std::vector< BaseInfo > extends
list of base classes
Definition entry.h:220
Specifier virt
virtualness of the entry
Definition entry.h:191
std::vector< std::string > qualifiers
qualifiers specified with the qualifier command
Definition entry.h:235
QCString doc
documentation block (partly parsed)
Definition entry.h:200
QCString read
property read accessor
Definition entry.h:211
RefItemVector sli
special lists (test/todo/bug/deprecated/..) this entry is in
Definition entry.h:226
QCString docFile
file in which the documentation was found
Definition entry.h:202
Protection protection
class protection
Definition entry.h:180
bool artificial
Artificially introduced item.
Definition entry.h:229
bool hidden
does this represent an entity that is hidden from the output
Definition entry.h:228
QCString brief
brief description (doc block)
Definition entry.h:203
int briefLine
line number at which the brief desc. was found
Definition entry.h:204
FileDef * fileDef() const
Definition entry.h:169
int initLines
define/variable initializer lines to show
Definition entry.h:184
bool isStatic
static ?
Definition entry.h:185
QCString inbodyFile
file in which the body doc was found
Definition entry.h:208
TypeSpecifier spec
class/member specifiers
Definition entry.h:182
QCString inside
name of the class in which documents are found
Definition entry.h:213
Wrapper class for the Entry type.
Definition types.h:793
bool isDoc() const
Definition types.h:804
ENTRY_TYPES bool isCompound() const
Definition types.h:800
bool isFile() const
Definition types.h:802
std::string to_string() const
Definition types.h:805
bool isScope() const
Definition types.h:801
bool isCompoundDoc() const
Definition types.h:803
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:1203
static void writeSearchPage()
Definition htmlgen.cpp:3170
static void writeFooterFile(TextStream &t)
Definition htmlgen.cpp:1537
static void writeTabData()
Additional initialization after indices have been created.
Definition htmlgen.cpp:1354
static void writeSearchData(const QCString &dir)
Definition htmlgen.cpp:1363
static void writeExternalSearchPage()
Definition htmlgen.cpp:3269
static void writeStyleSheetFile(TextStream &t)
Definition htmlgen.cpp:1525
static void writeHeaderFile(TextStream &t, const QCString &cssname)
Definition htmlgen.cpp:1531
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:1435
void parse(const QCString &fileName, const char *data=nullptr)
Parses a user provided layout.
Definition layout.cpp:1468
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 addListReferences()
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 addListReferences()=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:147
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:4073
void addSearchDir(const QCString &dir)
Definition pre.l:4055
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
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:268
void print()
Definition doxygen.cpp:245
void begin(const char *name)
Definition doxygen.cpp:232
void end()
Definition doxygen.cpp:238
std::chrono::steady_clock::time_point startTime
Definition doxygen.cpp:269
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:229
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:682
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:788
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:559
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:469
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:90
#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:1102
void computeDirDependencies()
Definition dirdef.cpp:1176
void generateDirDocs(OutputList &ol)
Definition dirdef.cpp:1193
#define AUTO_TRACE_ADD(...)
Definition docnode.cpp:47
#define AUTO_TRACE(...)
Definition docnode.cpp:46
#define AUTO_TRACE_EXIT(...)
Definition docnode.cpp:48
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:1330
static void findInheritedTemplateInstances()
Definition doxygen.cpp:5243
void printNavTree(Entry *root, int indent)
Definition doxygen.cpp:9922
static void addClassToContext(const Entry *root)
Definition doxygen.cpp:931
static void makeTemplateInstanceRelation(const Entry *root, ClassDefMutable *cd)
Definition doxygen.cpp:5258
static StringUnorderedSet g_pathsVisited(1009)
static int findEndOfTemplate(const QCString &s, size_t startPos)
Definition doxygen.cpp:3030
static void buildGroupList(const Entry *root)
Definition doxygen.cpp:425
static void insertMemberAlias(Definition *outerScope, const MemberDef *md)
Definition doxygen.cpp:6603
static void findUsingDeclarations(const Entry *root, bool filterPythonPackages)
Definition doxygen.cpp:2006
static void flushCachedTemplateRelations()
Definition doxygen.cpp:9349
static bool isRecursiveBaseClass(const QCString &scope, const QCString &name)
Definition doxygen.cpp:4801
static void copyLatexStyleSheet()
static void generateDocsForClassList(const std::vector< ClassDefMutable * > &classList)
Definition doxygen.cpp:9009
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:2826
static bool isSpecialization(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists)
Definition doxygen.cpp:5919
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:2426
static void computeTemplateClassRelations()
Definition doxygen.cpp:5337
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:8063
static void runQHelpGenerator()
static void addConceptToContext(const Entry *root)
Definition doxygen.cpp:1152
static void addRelatedPage(Entry *root)
Definition doxygen.cpp:328
void initDoxygen()
static void addIncludeFile(DefMutable *cd, FileDef *ifd, const Entry *root)
Definition doxygen.cpp:581
static StringVector g_inputFiles
Definition doxygen.cpp:187
void printSectionsTree()
Definition doxygen.cpp:9945
class Statistics g_s
static void createUsingMemberImportForClass(const Entry *root, ClassDefMutable *cd, const MemberDef *md, const QCString &fileName, const QCString &memName)
Definition doxygen.cpp:2110
static void generateXRefPages()
Definition doxygen.cpp:5509
static void findUsingDeclImports(const Entry *root)
Definition doxygen.cpp:2159
static void copyStyleSheet()
FindBaseClassRelation_Mode
Definition doxygen.cpp:285
@ Undocumented
Definition doxygen.cpp:288
@ TemplateInstances
Definition doxygen.cpp:286
@ DocumentedOnly
Definition doxygen.cpp:287
void distributeClassGroupRelations()
Definition doxygen.cpp:1447
static void generateGroupDocs()
static void findDirDocumentation(const Entry *root)
Definition doxygen.cpp:9586
void checkConfiguration()
check and resolve config options
QCString stripTemplateSpecifiers(const QCString &s)
Definition doxygen.cpp:678
static bool findClassRelation(const Entry *root, Definition *context, ClassDefMutable *cd, const BaseInfo *bi, const TemplateNameMap &templateNames, FindBaseClassRelation_Mode mode, bool isArtificial)
Definition doxygen.cpp:4843
static void resolveTemplateInstanceInType(const Entry *root, const Definition *scope, const MemberDef *md)
Definition doxygen.cpp:4771
static void organizeSubGroupsFiltered(const Entry *root, bool additional)
Definition doxygen.cpp:465
static void warnUndocumentedNamespaces()
Definition doxygen.cpp:5292
static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments, const std::string &name)
Definition doxygen.cpp:4448
static NamespaceDef * findUsedNamespace(const LinkedRefMap< NamespaceDef > &unl, const QCString &name)
Definition doxygen.cpp:1838
static void buildConceptList(const Entry *root)
Definition doxygen.cpp:1277
static void resolveClassNestingRelations()
Definition doxygen.cpp:1332
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:6646
static void findClassEntries(const Entry *root)
Definition doxygen.cpp:5206
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:1566
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:6130
static void vhdlCorrectMemberProperties()
Definition doxygen.cpp:8307
static void generateExampleDocs()
Definition doxygen.cpp:9961
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:4720
static void substituteTemplatesInArgList(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists, const ArgumentList &src, ArgumentList &dst)
Definition doxygen.cpp:6033
static void computeMemberReferences()
Definition doxygen.cpp:5406
static void generateConfigFile(const QCString &configFile, bool shortList, bool updateOnly=FALSE)
static void transferRelatedFunctionDocumentation()
Definition doxygen.cpp:4362
static void addMembersToMemberGroup()
Definition doxygen.cpp:9214
static bool tryAddEnumDocsToGroupMember(const Entry *root, const QCString &name)
Definition doxygen.cpp:7944
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:6536
static void findMainPageTagFiles(Entry *root)
Definition doxygen.cpp:9746
static void distributeConceptGroups()
Definition doxygen.cpp:1299
static void transferFunctionDocumentation()
Definition doxygen.cpp:4281
static void setAnonymousEnumType()
Definition doxygen.cpp:8954
static void sortMemberLists()
Definition doxygen.cpp:8859
static void createTemplateInstanceMembers()
Definition doxygen.cpp:8435
void transferStaticInstanceInitializers()
Definition doxygen.cpp:4411
static void findObjCMethodDefinitions(const Entry *root)
Definition doxygen.cpp:7451
static void dumpSymbolMap()
static void buildTypedefList(const Entry *root)
Definition doxygen.cpp:3353
static void findGroupScope(const Entry *root)
Definition doxygen.cpp:440
static void generateFileDocs()
Definition doxygen.cpp:8672
static void findDefineDocumentation(Entry *root)
Definition doxygen.cpp:9499
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:4511
void parseInput()
static void findMemberDocumentation(const Entry *root)
Definition doxygen.cpp:7421
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:6077
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:5712
static void distributeMemberGroupDocumentation()
Definition doxygen.cpp:9252
static void generateNamespaceClassDocs(const ClassLinkedRefMap &classList)
static void addEnumValuesToEnums(const Entry *root)
Definition doxygen.cpp:7654
static void generatePageDocs()
Definition doxygen.cpp:9876
static void resolveUserReferences()
Definition doxygen.cpp:9808
static void compareDoxyfile(Config::CompareMode diffList)
static void addPageToContext(PageDef *pd, Entry *root)
Definition doxygen.cpp:309
static void buildVarList(const Entry *root)
Definition doxygen.cpp:3489
static void buildSequenceList(const Entry *root)
Definition doxygen.cpp:3452
static void generateFileSources()
Definition doxygen.cpp:8506
static QCString substituteTemplatesInString(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists, const std::string &src)
Definition doxygen.cpp:5949
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:2586
static void generateClassDocs()
Definition doxygen.cpp:9107
static void buildNamespaceList(const Entry *root)
Definition doxygen.cpp:1671
static int computeIdealCacheParam(size_t v)
static void readTagFile(const std::shared_ptr< Entry > &root, const QCString &tagLine)
static void findIncludedUsingDirectives()
Definition doxygen.cpp:2409
static void addDefineDoc(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:9473
static void addMemberDocs(const Entry *root, MemberDefMutable *md, const QCString &funcDecl, const ArgumentList *al, bool over_load, TypeSpecifier spec)
Definition doxygen.cpp:5523
static void countMembers()
Definition doxygen.cpp:8968
void clearAll()
Definition doxygen.cpp:201
static void devUsage()
static void addInterfaceOrServiceToServiceOrSingleton(const Entry *root, ClassDefMutable *cd, QCString const &rname)
Definition doxygen.cpp:3521
static void organizeSubGroups(const Entry *root)
Definition doxygen.cpp:484
static void applyMemberOverrideOptions(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:2098
void searchInputFiles()
static void findFriends()
Definition doxygen.cpp:4184
static void findEnums(const Entry *root)
Definition doxygen.cpp:7479
static void dumpSymbol(TextStream &t, Definition *d)
static void addClassAndNestedClasses(std::vector< ClassDefMutable * > &list, ClassDefMutable *cd)
Definition doxygen.cpp:9084
static void addEnumDocs(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:7903
static void addListReferences()
Definition doxygen.cpp:5440
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:5184
static Definition * findScopeFromQualifiedName(NamespaceDefMutable *startScope, const QCString &n, FileDef *fileScope, const TagInfo *tagInfo)
Definition doxygen.cpp:774
static ClassDef * findClassWithinClassContext(Definition *context, ClassDef *cd, const QCString &name)
Definition doxygen.cpp:4476
static void buildGroupListFiltered(const Entry *root, bool additional, bool includeExternal)
Definition doxygen.cpp:356
static void runHtmlHelpCompiler()
static QCString extractClassName(const Entry *root)
Definition doxygen.cpp:5215
static void addMembersToIndex()
Definition doxygen.cpp:8097
static bool g_dumpSymbolMap
Definition doxygen.cpp:191
static void version(const bool extended)
static void addGlobalFunction(const Entry *root, const QCString &rname, const QCString &sc)
Definition doxygen.cpp:3770
static OutputList * g_outputList
Definition doxygen.cpp:188
static void findMainPage(Entry *root)
Definition doxygen.cpp:9676
static ClassDef::CompoundType convertToCompoundType(EntryType section, TypeSpecifier specifier)
Definition doxygen.cpp:889
static void findUsingDirectives(const Entry *root)
Definition doxygen.cpp:1851
std::unique_ptr< ArgumentList > getTemplateArgumentsFromName(const QCString &name, const ArgumentLists &tArgLists)
Definition doxygen.cpp:859
static bool g_successfulRun
Definition doxygen.cpp:190
static void addSourceReferences()
Definition doxygen.cpp:8732
std::function< std::unique_ptr< T >() > make_parser_factory()
static void buildExampleList(Entry *root)
Definition doxygen.cpp:9893
static void inheritDocumentation()
Definition doxygen.cpp:9154
static void flushUnresolvedRelations()
Definition doxygen.cpp:9403
static bool isSymbolHidden(const Definition *d)
Definition doxygen.cpp:8901
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:192
static void findDocumentedEnumValues()
Definition doxygen.cpp:8089
static void findTagLessClasses()
Definition doxygen.cpp:1647
static void generateDiskNames()
static void addToIndices()
Definition doxygen.cpp:8139
static void computeClassRelations()
Definition doxygen.cpp:5312
static void buildFunctionList(const Entry *root)
Definition doxygen.cpp:3878
static void checkPageRelations()
Definition doxygen.cpp:9788
static void findModuleDocumentation(const Entry *root)
Definition doxygen.cpp:1267
static void findEnumDocumentation(const Entry *root)
Definition doxygen.cpp:7977
static void computePageRelations(Entry *root)
Definition doxygen.cpp:9758
static void filterMemberDocumentation(const Entry *root, const QCString &relates)
Definition doxygen.cpp:7271
static QCString createOutputDirectory(const QCString &baseDirName, const QCString &formatDirName, const char *defaultDirName)
void initResources()
static bool isVarWithConstructor(const Entry *root)
Definition doxygen.cpp:2886
static StringSet g_usingDeclarations
Definition doxygen.cpp:189
static void buildDictionaryList(const Entry *root)
Definition doxygen.cpp:3470
static bool haveEqualFileNames(const Entry *root, const MemberDef *md)
Definition doxygen.cpp:9462
static void generateNamespaceDocs()
static void buildClassDocList(const Entry *root)
Definition doxygen.cpp:1138
static void buildPageList(Entry *root)
Definition doxygen.cpp:9649
static void writeTagFile()
static Definition * buildScopeFromQualifiedName(const QCString &name_, SrcLangExt lang, const TagInfo *tagInfo)
Definition doxygen.cpp:705
static void computeVerifiedDotPath()
static bool g_singleComment
Definition doxygen.cpp:193
static void findSectionsInDocumentation()
Definition doxygen.cpp:9290
static void mergeCategories()
Definition doxygen.cpp:8454
static const StringUnorderedSet g_compoundKeywords
Definition doxygen.cpp:198
static bool scopeIsTemplate(const Definition *d)
Definition doxygen.cpp:5935
static void buildFileList(const Entry *root)
Definition doxygen.cpp:496
static ClassDefMutable * createTagLessInstance(const ClassDef *rootCd, const ClassDef *templ, const QCString &fieldName)
Definition doxygen.cpp:1480
static void buildClassList(const Entry *root)
Definition doxygen.cpp:1128
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:3635
static std::unique_ptr< OutlineParserInterface > getParserForFile(const QCString &fn)
static void findUsedTemplateInstances()
Definition doxygen.cpp:5276
static void computeTooltipTexts()
Definition doxygen.cpp:8908
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:3098
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:6474
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:4669
static void parseFilesSingleThreading(const std::shared_ptr< Entry > &root)
parse the list of input files
static void buildCompleteMemberLists()
Definition doxygen.cpp:8476
static void generateConceptDocs()
Definition doxygen.cpp:9133
static void combineUsingRelations()
Definition doxygen.cpp:9189
static const char * getArg(int argc, char **argv, int &optInd)
static const ClassDef * findClassDefinition(FileDef *fd, NamespaceDef *nd, const QCString &scopeName)
Definition doxygen.cpp:5673
static void checkMarkdownMainfile()
static std::unordered_map< std::string, std::vector< ClassDefMutable * > > g_usingClassMap
Definition doxygen.cpp:2157
static void buildConceptDocList(const Entry *root)
Definition doxygen.cpp:1287
static int findTemplateSpecializationPosition(const QCString &name)
Definition doxygen.cpp:4813
static bool isEntryInGroupOfMember(const Entry *root, const MemberDef *md, bool allowNoGroup=false)
Definition doxygen.cpp:5688
static void parseFilesMultiThreading(const std::shared_ptr< Entry > &root)
parse the list of input files
static void transferFunctionReferences()
Definition doxygen.cpp:4314
static void buildDefineList()
Definition doxygen.cpp:8811
static void buildInterfaceAndServiceList(const Entry *root)
Definition doxygen.cpp:3583
static void computeMemberRelations()
Definition doxygen.cpp:8419
static void buildListOfUsingDecls(const Entry *root)
Definition doxygen.cpp:1993
static void computeMemberRelationsForBaseClass(const ClassDef *cd, const BaseClassDef *bcd)
Definition doxygen.cpp:8339
static std::multimap< std::string, const Entry * > g_classEntries
Definition doxygen.cpp:186
std::vector< InputFileEncoding > InputFileEncodingList
Definition doxygen.h:81
std::unordered_map< std::string, BodyInfo > StaticInitMap
Definition doxygen.h:85
std::unordered_map< std::string, NamespaceAliasInfo > NamespaceAliasInfoMap
Definition doxygen.h:87
std::unordered_map< std::string, const Definition * > ClangUsrMap
Definition doxygen.h:83
std::unique_ptr< FileDef > createFileDef(const QCString &p, const QCString &n, const QCString &ref, const QCString &dn)
Definition filedef.cpp:268
FileDef * toFileDef(Definition *d)
Definition filedef.cpp:1954
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:175
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:4080
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:5794
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:1732
void printLayout()
Definition layout.cpp:1820
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:26
QCString warn_line(const QCString &file, int line)
Definition message.cpp:215
void initWarningFormat()
Definition message.cpp:237
void warn_flush()
Definition message.cpp:230
void finishWarnExit()
Definition message.cpp:295
#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:842
std::unique_ptr< PageDef > createPageDef(const QCString &f, int l, const QCString &n, const QCString &d, const QCString &t)
Definition pagedef.cpp:80
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:447
QCString substitute(const QCString &s, const QCString &src, const QCString &dst)
substitute all occurrences of src in s by dst
Definition qcstring.cpp:482
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
std::vector< RefItem * > RefItemVector
Definition reflist.h:133
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:90
Protection prot
inheritance type
Definition entry.h:95
Specifier virt
virtualness
Definition entry.h:96
QCString name
the name of the base class
Definition entry.h:94
Data associated with description found in the body.
Definition definition.h:63
Grouping info.
Definition types.h:227
QCString groupname
name of the group
Definition types.h:257
@ GROUPING_INGROUP
membership in group was defined by @ingroup
Definition types.h:236
static const char * getGroupPriName(GroupPri_t priority)
Definition types.h:240
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:60
const MemberDef * typeDef
Definition doxygen.h:61
stat(const char *n, double el)
Definition doxygen.cpp:266
const char * name
Definition doxygen.cpp:263
This struct is used to capture the tag file information for an Entry.
Definition entry.h:103
QCString fileName
Definition entry.h:105
QCString tagName
Definition entry.h:104
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
QCString removeRedundantWhiteSpace(const QCString &s)
Definition util.cpp:569
QCString mergeScopes(const QCString &leftScope, const QCString &rightScope)
Definition util.cpp:4518
QCString normalizeNonTemplateArgumentsInString(const QCString &name, const Definition *context, const ArgumentList &formalArgs)
Definition util.cpp:4224
SrcLangExt getLanguageFromFileName(const QCString &fileName, SrcLangExt defLang)
Definition util.cpp:5121
bool protectionLevelVisible(Protection prot)
Definition util.cpp:5863
bool matchTemplateArguments(const ArgumentList &srcAl, const ArgumentList &dstAl)
Definition util.cpp:2197
void addCodeOnlyMappings()
Definition util.cpp:5115
QCString substituteTemplateArgumentsInString(const QCString &nm, const ArgumentList &formalArgs, const ArgumentList *actualArgs)
Definition util.cpp:4289
int extractClassNameFromType(const QCString &type, int &pos, QCString &name, QCString &templSpec, SrcLangExt lang)
Definition util.cpp:4139
bool leftScopeMatch(const QCString &scope, const QCString &name)
Definition util.cpp:883
QCString stripAnonymousNamespaceScope(const QCString &s)
Definition util.cpp:232
QCString stripFromIncludePath(const QCString &path)
Definition util.cpp:331
bool checkIfTypedef(const Definition *scope, const FileDef *fileScope, const QCString &n)
Definition util.cpp:5225
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:5460
bool patternMatch(const FileInfo &fi, const StringVector &patList)
Definition util.cpp:5614
bool openOutputFile(const QCString &outFile, std::ofstream &f)
Definition util.cpp:6220
QCString tempArgListToString(const ArgumentList &al, SrcLangExt lang, bool includeDefault)
Definition util.cpp:1237
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:4740
QCString showFileDefMatches(const FileNameLinkedMap *fnMap, const QCString &n)
Definition util.cpp:2954
QCString fileToString(const QCString &name, bool filter, bool isSourceCode)
Definition util.cpp:1432
QCString filterTitle(const QCString &title)
Definition util.cpp:5540
QCString removeAnonymousScopes(const QCString &str)
Definition util.cpp:163
bool matchArguments2(const Definition *srcScope, const FileDef *srcFileScope, const ArgumentList *srcAl, const Definition *dstScope, const FileDef *dstFileScope, const ArgumentList *dstAl, bool checkCV, SrcLangExt lang)
Definition util.cpp:1959
QCString resolveTypeDef(const Definition *context, const QCString &qualifiedName, const Definition **typedefContext)
Definition util.cpp:375
bool checkExtension(const QCString &fName, const QCString &ext)
Definition util.cpp:4832
int computeQualifiedIndex(const QCString &name)
Return the index of the last :: in the string name that is still before the first <.
Definition util.cpp:6748
void initDefaultExtensionMapping()
Definition util.cpp:5048
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:4897
QCString convertNameToFile(const QCString &name, bool allowDots, bool allowUnderscore)
Definition util.cpp:3428
QCString langToString(SrcLangExt lang)
Returns a string representation of lang.
Definition util.cpp:5817
EntryType guessSection(const QCString &name)
Definition util.cpp:340
void extractNamespaceName(const QCString &scopeName, QCString &className, QCString &namespaceName, bool allowEmptyClass)
Definition util.cpp:3621
QCString argListToString(const ArgumentList &al, bool useCanonicalType, bool showDefVals)
Definition util.cpp:1192
QCString getLanguageSpecificSeparator(SrcLangExt lang, bool classScope)
Returns the scope separator to use given the programming language lang.
Definition util.cpp:5823
void mergeMemberOverrideOptions(MemberDefMutable *md1, MemberDefMutable *md2)
Definition util.cpp:6785
QCString mangleCSharpGenericName(const QCString &name)
Definition util.cpp:6837
QCString projectLogoFile()
Definition util.cpp:3070
void mergeArguments(ArgumentList &srcAl, ArgumentList &dstAl, bool forceNameOverwrite)
Definition util.cpp:2053
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:5783
QCString stripTemplateSpecifiersFromScope(const QCString &fullName, bool parentOnly, QCString *pLastScopeStripped, QCString scopeName, bool allowArtificial)
Definition util.cpp:4451
QCString getOverloadDocs()
Definition util.cpp:4013
int getPrefixIndex(const QCString &name)
Definition util.cpp:3161
bool rightScopeMatch(const QCString &scope, const QCString &name)
Definition util.cpp:872
bool updateLanguageMapping(const QCString &extension, const QCString &language)
Definition util.cpp:5016
QCString getFileNameExtension(const QCString &fn)
Definition util.cpp:5163
QCString replaceAnonymousScopes(const QCString &s, const QCString &replacement)
Definition util.cpp:220
FileDef * findFileDef(const FileNameLinkedMap *fnMap, const QCString &n, bool &ambig)
Definition util.cpp:2826
int getScopeFragment(const QCString &s, int p, int *l)
Definition util.cpp:4563
void addHtmlExtensionIfMissing(QCString &fName)
Definition util.cpp:4837
A bunch of utility functions.
void generateXML()
Definition xmlgen.cpp:2214