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) && (i=fullName.findRev('<'))!=-1)
1037 {
1038 // a Java/C# generic class looks like a C++ specialization, so we need to split the
1039 // name and template arguments here
1040 tArgList = stringToArgumentList(root->lang,fullName.mid(i));
1041 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
1042 // A -> A
1043 // A<T> -> A-1-g
1044 // A<T,S> -> A-2-g
1045 {
1046 fullName=mangleCSharpGenericName(fullName);
1047 }
1048 else
1049 {
1050 fullName=fullName.left(i);
1051 }
1052 }
1053 else
1054 {
1055 tArgList = getTemplateArgumentsFromName(fullName,root->tArgLists);
1056 }
1057 // add class to the list
1058 cd = toClassDefMutable(
1059 Doxygen::classLinkedMap->add(fullName,
1060 createClassDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1061 fullName,sec,tagName,refFileName,TRUE,root->spec.isEnum()) ));
1062 if (cd)
1063 {
1064 AUTO_TRACE_ADD("New class '{}' type={} #tArgLists={} tagInfo={} hidden={} artificial={}",
1065 fullName,cd->compoundTypeString(),root->tArgLists.size(),
1066 fmt::ptr(tagInfo),root->hidden,root->artificial);
1067 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1068 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1069 cd->setLanguage(root->lang);
1070 cd->setId(root->id);
1071 cd->setHidden(root->hidden);
1072 cd->setArtificial(root->artificial);
1073 cd->setClassSpecifier(root->spec);
1074 cd->addQualifiers(root->qualifiers);
1075 cd->setTypeConstraints(root->typeConstr);
1076 root->commandOverrides.apply_collaborationGraph([&](bool b ) { cd->overrideCollaborationGraph(b); });
1077 root->commandOverrides.apply_inheritanceGraph ([&](CLASS_GRAPH_t gt) { cd->overrideInheritanceGraph(gt); });
1078
1079 if (tArgList)
1080 {
1081 cd->setTemplateArguments(*tArgList);
1082 }
1083 cd->setRequiresClause(root->req);
1084 cd->setProtection(root->protection);
1085 cd->setIsStatic(root->isStatic);
1086
1087 // file definition containing the class cd
1088 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1089 cd->setBodyDef(fd);
1090
1091 cd->setMetaData(root->metaData);
1092
1093 cd->insertUsedFile(fd);
1094 }
1095 else
1096 {
1097 AUTO_TRACE_ADD("Class {} not added, already exists as alias", fullName);
1098 }
1099 }
1100
1101 if (cd)
1102 {
1104 if (!root->subGrouping) cd->setSubGrouping(FALSE);
1105 if (!root->spec.isForwardDecl())
1106 {
1107 if (cd->hasDocumentation())
1108 {
1109 addIncludeFile(cd,fd,root);
1110 }
1111 if (fd && root->section.isCompound())
1112 {
1113 AUTO_TRACE_ADD("Inserting class {} in file {} (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1114 cd->setFileDef(fd);
1115 fd->insertClass(cd);
1116 }
1117 }
1118 addClassToGroups(root,cd);
1120 cd->setRefItems(root->sli);
1121 }
1122}
1123
1124//----------------------------------------------------------------------
1125// build a list of all classes mentioned in the documentation
1126// and all classes that have a documentation block before their definition.
1127static void buildClassList(const Entry *root)
1128{
1129 if ((root->section.isCompound() || root->section.isObjcImpl()) && !root->name.isEmpty())
1130 {
1131 AUTO_TRACE();
1132 addClassToContext(root);
1133 }
1134 for (const auto &e : root->children()) buildClassList(e.get());
1135}
1136
1137static void buildClassDocList(const Entry *root)
1138{
1139 if ((root->section.isCompoundDoc()) && !root->name.isEmpty())
1140 {
1141 AUTO_TRACE();
1142 addClassToContext(root);
1143 }
1144 for (const auto &e : root->children()) buildClassDocList(e.get());
1145}
1146
1147//----------------------------------------------------------------------
1148// build a list of all classes mentioned in the documentation
1149// and all classes that have a documentation block before their definition.
1150
1151static void addConceptToContext(const Entry *root)
1152{
1153 AUTO_TRACE();
1154 FileDef *fd = root->fileDef();
1155
1156 QCString scName;
1157 if (root->parent()->section.isScope())
1158 {
1159 scName=root->parent()->name;
1160 }
1161
1162 // name with scope (if not present already)
1163 QCString qualifiedName = root->name;
1164 if (!scName.isEmpty() && !leftScopeMatch(qualifiedName,scName))
1165 {
1166 qualifiedName.prepend(scName+"::");
1167 }
1168
1169 // see if we already found the concept before
1170 ConceptDefMutable *cd = getConceptMutable(qualifiedName);
1171
1172 AUTO_TRACE_ADD("Found concept with name '{}' (qualifiedName='{}')", cd ? cd->name() : root->name, qualifiedName);
1173
1174 if (cd)
1175 {
1176 qualifiedName=cd->name();
1177 AUTO_TRACE_ADD("Existing concept '{}'",cd->name());
1178
1179 cd->setDocumentation(root->doc,root->docFile,root->docLine);
1180 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1181
1182 addIncludeFile(cd,fd,root);
1183 }
1184 else // new concept
1185 {
1186 QCString className;
1187 QCString namespaceName;
1188 extractNamespaceName(qualifiedName,className,namespaceName);
1189
1190 AUTO_TRACE_ADD("New concept: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1191 qualifiedName,namespaceName,className,root->brief,root->doc);
1192
1193 QCString tagName;
1194 QCString refFileName;
1195 const TagInfo *tagInfo = root->tagInfo();
1196 if (tagInfo)
1197 {
1198 tagName = tagInfo->tagName;
1199 refFileName = tagInfo->fileName;
1200 if (qualifiedName.find("::")!=-1)
1201 // symbols imported via tag files may come without the parent scope,
1202 // so we artificially create it here
1203 {
1204 buildScopeFromQualifiedName(qualifiedName,root->lang,tagInfo);
1205 }
1206 }
1207 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(qualifiedName,root->tArgLists);
1208 // add concept to the list
1210 Doxygen::conceptLinkedMap->add(qualifiedName,
1211 createConceptDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1212 qualifiedName,tagName,refFileName)));
1213 if (cd)
1214 {
1215 AUTO_TRACE_ADD("New concept '{}' #tArgLists={} tagInfo={}",
1216 qualifiedName,root->tArgLists.size(),fmt::ptr(tagInfo));
1217 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1218 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1219 cd->setLanguage(root->lang);
1220 cd->setId(root->id);
1221 cd->setHidden(root->hidden);
1222 cd->setGroupId(root->mGrpId);
1223 if (tArgList)
1224 {
1225 cd->setTemplateArguments(*tArgList);
1226 }
1227 cd->setInitializer(root->initializer.str());
1228 // file definition containing the class cd
1229 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1230 cd->setBodyDef(fd);
1231 addIncludeFile(cd,fd,root);
1232
1233 // also add namespace to the correct structural context
1234 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,qualifiedName,nullptr,tagInfo);
1236 {
1238 if (dm)
1239 {
1240 dm->addInnerCompound(cd);
1241 }
1242 cd->setOuterScope(d);
1243 }
1244 }
1245 else
1246 {
1247 AUTO_TRACE_ADD("Concept '{}' not added, already exists (as alias)", qualifiedName);
1248 }
1249 }
1250
1251 if (cd)
1252 {
1254 if (fd)
1255 {
1256 AUTO_TRACE_ADD("Inserting concept '{}' in file '{}' (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1257 cd->setFileDef(fd);
1258 fd->insertConcept(cd);
1259 }
1260 addConceptToGroups(root,cd);
1262 cd->setRefItems(root->sli);
1263 }
1264}
1265
1266static void findModuleDocumentation(const Entry *root)
1267{
1268 if (root->section.isModuleDoc())
1269 {
1270 AUTO_TRACE();
1272 }
1273 for (const auto &e : root->children()) findModuleDocumentation(e.get());
1274}
1275
1276static void buildConceptList(const Entry *root)
1277{
1278 if (root->section.isConcept())
1279 {
1280 AUTO_TRACE();
1281 addConceptToContext(root);
1282 }
1283 for (const auto &e : root->children()) buildConceptList(e.get());
1284}
1285
1286static void buildConceptDocList(const Entry *root)
1287{
1288 if (root->section.isConceptDoc())
1289 {
1290 AUTO_TRACE();
1291 addConceptToContext(root);
1292 }
1293 for (const auto &e : root->children()) buildConceptDocList(e.get());
1294}
1295
1296// This routine is to allow @ingroup X @{ concept A; concept B; @} to work
1297// (same also works for variable and functions because of logic in MemberGroup::insertMember)
1299{
1300 AUTO_TRACE();
1301 for (const auto &cd : *Doxygen::conceptLinkedMap)
1302 {
1303 if (cd->groupId()!=DOX_NOGROUP)
1304 {
1305 for (const auto &ocd : *Doxygen::conceptLinkedMap)
1306 {
1307 if (cd!=ocd && cd->groupId()==ocd->groupId() &&
1308 !cd->partOfGroups().empty() && ocd->partOfGroups().empty())
1309 {
1310 ConceptDefMutable *ocdm = toConceptDefMutable(ocd.get());
1311 if (ocdm)
1312 {
1313 for (const auto &gd : cd->partOfGroups())
1314 {
1315 if (gd)
1316 {
1317 AUTO_TRACE_ADD("making concept '{}' part of group '{}'",ocdm->name(),gd->name());
1318 ocdm->makePartOfGroup(gd);
1319 gd->addConcept(ocd.get());
1320 }
1321 }
1322 }
1323 }
1324 }
1325 }
1326 }
1327}
1328
1329//----------------------------------------------------------------------
1330
1332{
1333 ClassDefSet visitedClasses;
1334
1335 bool done=FALSE;
1336 //int iteration=0;
1337 while (!done)
1338 {
1339 done=TRUE;
1340 //++iteration;
1341 struct ClassAlias
1342 {
1343 ClassAlias(const QCString &name,std::unique_ptr<ClassDef> cd,DefinitionMutable *ctx) :
1344 aliasFullName(name),aliasCd(std::move(cd)), aliasContext(ctx) {}
1345 QCString aliasFullName;
1346 std::unique_ptr<ClassDef> aliasCd;
1347 DefinitionMutable *aliasContext;
1348 };
1349 std::vector<ClassAlias> aliases;
1350 for (const auto &icd : *Doxygen::classLinkedMap)
1351 {
1352 ClassDefMutable *cd = toClassDefMutable(icd.get());
1353 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1354 {
1355 QCString name = stripAnonymousNamespaceScope(icd->name());
1356 //printf("processing=%s, iteration=%d\n",qPrint(cd->name()),iteration);
1357 // also add class to the correct structural context
1359 name,icd->getFileDef(),nullptr);
1360 if (d)
1361 {
1362 //printf("****** adding %s to scope %s in iteration %d\n",qPrint(cd->name()),qPrint(d->name()),iteration);
1364 if (dm)
1365 {
1366 dm->addInnerCompound(cd);
1367 }
1368 cd->setOuterScope(d);
1369
1370 // for inline namespace add an alias of the class to the outer scope
1372 {
1374 //printf("nd->isInline()=%d\n",nd->isInline());
1375 if (nd && nd->isInline())
1376 {
1377 d = d->getOuterScope();
1378 if (d)
1379 {
1380 dm = toDefinitionMutable(d);
1381 if (dm)
1382 {
1383 auto aliasCd = createClassDefAlias(d,cd);
1384 QCString aliasFullName = d->qualifiedName()+"::"+aliasCd->localName();
1385 aliases.emplace_back(aliasFullName,std::move(aliasCd),dm);
1386 //printf("adding %s to %s as %s\n",qPrint(aliasCd->name()),qPrint(d->name()),qPrint(aliasFullName));
1387 }
1388 }
1389 }
1390 else
1391 {
1392 break;
1393 }
1394 }
1395
1396 visitedClasses.insert(icd.get());
1397 done=FALSE;
1398 }
1399 //else
1400 //{
1401 // printf("****** ignoring %s: scope not (yet) found in iteration %d\n",qPrint(cd->name()),iteration);
1402 //}
1403 }
1404 }
1405 // add aliases
1406 for (auto &alias : aliases)
1407 {
1408 ClassDef *aliasCd = Doxygen::classLinkedMap->add(alias.aliasFullName,std::move(alias.aliasCd));
1409 if (aliasCd)
1410 {
1411 alias.aliasContext->addInnerCompound(aliasCd);
1412 }
1413 }
1414 }
1415
1416 //give warnings for unresolved compounds
1417 for (const auto &icd : *Doxygen::classLinkedMap)
1418 {
1419 ClassDefMutable *cd = toClassDefMutable(icd.get());
1420 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1421 {
1423 /// create the scope artificially
1424 // anyway, so we can at least relate scopes properly.
1425 Definition *d = buildScopeFromQualifiedName(name,cd->getLanguage(),nullptr);
1426 if (d && d!=cd && !cd->getDefFileName().isEmpty())
1427 // avoid recursion in case of redundant scopes, i.e: namespace N { class N::C {}; }
1428 // for this case doxygen assumes the existence of a namespace N::N in which C is to be found!
1429 // also avoid warning for stuff imported via a tagfile.
1430 {
1432 if (dm)
1433 {
1434 dm->addInnerCompound(cd);
1435 }
1436 cd->setOuterScope(d);
1437 warn(cd->getDefFileName(),cd->getDefLine(),
1438 "Incomplete input: scope for class {} not found!{}",name,
1439 name.startsWith("std::") ? " Try enabling BUILTIN_STL_SUPPORT." : ""
1440 );
1441 }
1442 }
1443 }
1444}
1445
1447{
1448 //bool inlineGroupedClasses = Config_getBool(INLINE_GROUPED_CLASSES);
1449 //if (!inlineGroupedClasses) return;
1450 //printf("** distributeClassGroupRelations()\n");
1451
1452 ClassDefSet visitedClasses;
1453 for (const auto &cd : *Doxygen::classLinkedMap)
1454 {
1455 //printf("Checking %s\n",qPrint(cd->name()));
1456 // distribute the group to nested classes as well
1457 if (visitedClasses.find(cd.get())==visitedClasses.end() && !cd->partOfGroups().empty())
1458 {
1459 //printf(" Candidate for merging\n");
1460 GroupDef *gd = cd->partOfGroups().front();
1461 for (auto &ncd : cd->getClasses())
1462 {
1464 if (ncdm && ncdm->partOfGroups().empty())
1465 {
1466 //printf(" Adding %s to group '%s'\n",qPrint(ncd->name()),
1467 // gd->groupTitle());
1468 ncdm->makePartOfGroup(gd);
1469 gd->addClass(ncdm);
1470 }
1471 }
1472 visitedClasses.insert(cd.get()); // only visit every class once
1473 }
1474 }
1475}
1476
1477//----------------------------
1478
1479static ClassDefMutable *createTagLessInstance(const ClassDef *rootCd,const ClassDef *templ,const QCString &fieldName)
1480{
1481 QCString fullName = removeAnonymousScopes(templ->name());
1482 if (fullName.endsWith("::")) fullName=fullName.left(fullName.length()-2);
1483 fullName+="."+fieldName;
1484
1485 //printf("** adding class %s based on %s\n",qPrint(fullName),qPrint(templ->name()));
1487 Doxygen::classLinkedMap->add(fullName,
1489 templ->getDefLine(),
1490 templ->getDefColumn(),
1491 fullName,
1492 templ->compoundType())));
1493 if (cd)
1494 {
1495 cd->setDocumentation(templ->documentation(),templ->docFile(),templ->docLine()); // copy docs to definition
1496 cd->setBriefDescription(templ->briefDescription(),templ->briefFile(),templ->briefLine());
1497 cd->setLanguage(templ->getLanguage());
1498 cd->setBodySegment(templ->getDefLine(),templ->getStartBodyLine(),templ->getEndBodyLine());
1499 cd->setBodyDef(templ->getBodyDef());
1500
1501 cd->setOuterScope(rootCd->getOuterScope());
1502 if (rootCd->getOuterScope()!=Doxygen::globalScope)
1503 {
1504 DefinitionMutable *outerScope = toDefinitionMutable(rootCd->getOuterScope());
1505 if (outerScope)
1506 {
1507 outerScope->addInnerCompound(cd);
1508 }
1509 }
1510
1511 FileDef *fd = templ->getFileDef();
1512 if (fd)
1513 {
1514 cd->setFileDef(fd);
1515 fd->insertClass(cd);
1516 }
1517 for (auto &gd : rootCd->partOfGroups())
1518 {
1519 cd->makePartOfGroup(gd);
1520 gd->addClass(cd);
1521 }
1522
1523 MemberList *ml = templ->getMemberList(MemberListType::PubAttribs());
1524 if (ml)
1525 {
1526 for (const auto &md : *ml)
1527 {
1528 //printf(" Member %s type=%s\n",qPrint(md->name()),md->typeString());
1529 auto newMd = createMemberDef(md->getDefFileName(),md->getDefLine(),md->getDefColumn(),
1530 md->typeString(),md->name(),md->argsString(),md->excpString(),
1531 md->protection(),md->virtualness(),md->isStatic(),Relationship::Member,
1532 md->memberType(),
1533 ArgumentList(),ArgumentList(),"");
1534 MemberDefMutable *imd = toMemberDefMutable(newMd.get());
1535 imd->setMemberClass(cd);
1536 imd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
1537 imd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
1538 imd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
1539 imd->setMemberSpecifiers(md->getMemberSpecifiers());
1540 imd->setVhdlSpecifiers(md->getVhdlSpecifiers());
1541 imd->setMemberGroupId(md->getMemberGroupId());
1542 imd->setInitializer(md->initializer());
1543 imd->setRequiresClause(md->requiresClause());
1544 imd->setMaxInitLines(md->initializerLines());
1545 imd->setBitfields(md->bitfieldString());
1546 imd->setLanguage(md->getLanguage());
1547 cd->insertMember(imd);
1548 MemberName *mn = Doxygen::memberNameLinkedMap->add(md->name());
1549 mn->push_back(std::move(newMd));
1550 }
1551 }
1552 }
1553 return cd;
1554}
1555
1556/** Look through the members of class \a cd and its public members.
1557 * If there is a member m of a tag less struct/union,
1558 * then we create a duplicate of the struct/union with the name of the
1559 * member to identify it.
1560 * So if cd has name S, then the tag less struct/union will get name S.m
1561 * Since tag less structs can be nested we need to call this function
1562 * recursively. Later on we need to patch the member types so we keep
1563 * track of the hierarchy of classes we create.
1564 */
1565static void processTagLessClasses(const ClassDef *rootCd,
1566 const ClassDef *cd,
1567 ClassDefMutable *tagParentCd,
1568 const QCString &prefix,int count)
1569{
1570 //printf("%d: processTagLessClasses %s\n",count,qPrint(cd->name()));
1571 //printf("checking members for %s\n",qPrint(cd->name()));
1572 if (tagParentCd && !cd->getClasses().empty())
1573 {
1574 MemberList *ml = cd->getMemberList(MemberListType::PubAttribs());
1575 if (ml)
1576 {
1577 int pos=0;
1578 for (const auto &md : *ml)
1579 {
1580 QCString type = md->typeString();
1581 if (type.find("::@")!=-1) // member of tag less struct/union
1582 {
1583 for (const auto &icd : cd->getClasses())
1584 {
1585 //printf(" member %s: type='%s'\n",qPrint(md->name()),qPrint(type));
1586 //printf(" comparing '%s'<->'%s'\n",qPrint(type),qPrint(icd->name()));
1587 if (type.find(icd->name())!=-1) // matching tag less struct/union
1588 {
1589 QCString name = md->name();
1590 if (md->isAnonymous()) name = "__unnamed" + QCString().setNum(pos++)+"__";
1591 if (!prefix.isEmpty()) name.prepend(prefix+".");
1592 //printf(" found %s for class %s\n",qPrint(name),qPrint(cd->name()));
1593 ClassDefMutable *ncd = createTagLessInstance(rootCd,icd,name);
1594 if (ncd)
1595 {
1596 processTagLessClasses(rootCd,icd,ncd,name,count+1);
1597 //printf(" addTagged %s to %s\n",qPrint(ncd->name()),qPrint(tagParentCd->name()));
1598 ncd->setTagLessReference(icd);
1599
1600 // replace tag-less type for generated/original member
1601 // by newly created class name.
1602 // note the difference between changing cd and tagParentCd.
1603 // for the initial call this is the same pointer, but for
1604 // recursive calls cd is the original tag-less struct (of which
1605 // there is only one instance) and tagParentCd is the newly
1606 // generated tagged struct of which there can be multiple instances!
1607 MemberList *pml = tagParentCd->getMemberList(MemberListType::PubAttribs());
1608 if (pml)
1609 {
1610 for (const auto &pmd : *pml)
1611 {
1613 if (pmdm && pmd->name()==md->name())
1614 {
1615 pmdm->setAccessorType(ncd,substitute(pmd->typeString(),icd->name(),ncd->name()));
1616 //pmd->setType(substitute(pmd->typeString(),icd->name(),ncd->name()));
1617 }
1618 }
1619 }
1620 }
1621 }
1622 }
1623 }
1624 }
1625 }
1626 }
1627}
1628
1629static void findTagLessClasses(std::vector<ClassDefMutable*> &candidates,ClassDef *cd)
1630{
1631 for (const auto &icd : cd->getClasses())
1632 {
1633 if (icd->name().find("@")==-1) // process all non-anonymous inner classes
1634 {
1635 findTagLessClasses(candidates,icd);
1636 }
1637 }
1638
1640 if (cdm)
1641 {
1642 candidates.push_back(cdm);
1643 }
1644}
1645
1647{
1648 std::vector<ClassDefMutable *> candidates;
1649 for (auto &cd : *Doxygen::classLinkedMap)
1650 {
1651 Definition *scope = cd->getOuterScope();
1652 if (scope && scope->definitionType()!=Definition::TypeClass) // that is not nested
1653 {
1654 findTagLessClasses(candidates,cd.get());
1655 }
1656 }
1657
1658 // since processTagLessClasses is potentially adding classes to Doxygen::classLinkedMap
1659 // we need to call it outside of the loop above, otherwise the iterator gets invalidated!
1660 for (auto &cd : candidates)
1661 {
1662 processTagLessClasses(cd,cd,cd,"",0); // process tag less inner struct/classes
1663 }
1664}
1665
1666
1667//----------------------------------------------------------------------
1668// build a list of all namespaces mentioned in the documentation
1669// and all namespaces that have a documentation block before their definition.
1670static void buildNamespaceList(const Entry *root)
1671{
1672 if (
1673 (root->section.isNamespace() ||
1674 root->section.isNamespaceDoc() ||
1675 root->section.isPackageDoc()
1676 ) &&
1677 !root->name.isEmpty()
1678 )
1679 {
1680 AUTO_TRACE("name={}",root->name);
1681
1682 QCString fName = root->name;
1683 if (root->section.isPackageDoc())
1684 {
1685 fName=substitute(fName,".","::");
1686 }
1687
1688 QCString fullName = stripAnonymousNamespaceScope(fName);
1689 if (!fullName.isEmpty())
1690 {
1691 AUTO_TRACE_ADD("Found namespace {} in {} at line {}",root->name,root->fileName,root->startLine);
1692 NamespaceDef *ndi = Doxygen::namespaceLinkedMap->find(fullName);
1693 if (ndi) // existing namespace
1694 {
1696 if (nd) // non-inline namespace
1697 {
1698 AUTO_TRACE_ADD("Existing namespace");
1699 nd->setDocumentation(root->doc,root->docFile,root->docLine);
1700 nd->setName(fullName); // change name to match docs
1702 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1703 if (nd->getLanguage()==SrcLangExt::Unknown)
1704 {
1705 nd->setLanguage(root->lang);
1706 }
1707 if (root->tagInfo()==nullptr && nd->isReference() && !(root->doc.isEmpty() && root->brief.isEmpty()))
1708 // if we previously found namespace nd in a tag file and now we find a
1709 // documented namespace with the same name in the project, then remove
1710 // the tag file reference
1711 {
1712 nd->setReference("");
1713 nd->setFileName(fullName);
1714 }
1715 nd->setMetaData(root->metaData);
1716
1717 // file definition containing the namespace nd
1718 FileDef *fd=root->fileDef();
1719 if (nd->isArtificial())
1720 {
1721 nd->setArtificial(FALSE); // found namespace explicitly, so cannot be artificial
1722 nd->setDefFile(root->fileName,root->startLine,root->startColumn);
1723 }
1724 // insert the namespace in the file definition
1725 if (fd) fd->insertNamespace(nd);
1726 addNamespaceToGroups(root,nd);
1727 nd->setRefItems(root->sli);
1728 }
1729 }
1730 else // fresh namespace
1731 {
1732 QCString tagName;
1733 QCString tagFileName;
1734 const TagInfo *tagInfo = root->tagInfo();
1735 if (tagInfo)
1736 {
1737 tagName = tagInfo->tagName;
1738 tagFileName = tagInfo->fileName;
1739 }
1740 AUTO_TRACE_ADD("new namespace {} lang={} tagName={}",fullName,langToString(root->lang),tagName);
1741 // add namespace to the list
1743 Doxygen::namespaceLinkedMap->add(fullName,
1744 createNamespaceDef(tagInfo?tagName:root->fileName,root->startLine,
1745 root->startColumn,fullName,tagName,tagFileName,
1746 root->type,root->spec.isPublished())));
1747 if (nd)
1748 {
1749 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1750 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1752 nd->setHidden(root->hidden);
1753 nd->setArtificial(root->artificial);
1754 nd->setLanguage(root->lang);
1755 nd->setId(root->id);
1756 nd->setMetaData(root->metaData);
1757 nd->setInline(root->spec.isInline());
1758 nd->setExported(root->exported);
1759
1760 addNamespaceToGroups(root,nd);
1761 nd->setRefItems(root->sli);
1762
1763 // file definition containing the namespace nd
1764 FileDef *fd=root->fileDef();
1765 // insert the namespace in the file definition
1766 if (fd) fd->insertNamespace(nd);
1767
1768 // the empty string test is needed for extract all case
1769 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1770 nd->insertUsedFile(fd);
1771 nd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1772 nd->setBodyDef(fd);
1773
1774 // also add namespace to the correct structural context
1775 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,fullName,nullptr,tagInfo);
1776 AUTO_TRACE_ADD("adding namespace {} to context {}",nd->name(),d ? d->name() : QCString("<none>"));
1777 if (d==nullptr) // we didn't find anything, create the scope artificially
1778 // anyway, so we can at least relate scopes properly.
1779 {
1780 d = buildScopeFromQualifiedName(fullName,nd->getLanguage(),tagInfo);
1782 if (dm)
1783 {
1784 dm->addInnerCompound(nd);
1785 }
1786 nd->setOuterScope(d);
1787 // TODO: Due to the order in which the tag file is written
1788 // a nested class can be found before its parent!
1789 }
1790 else
1791 {
1793 if (dm)
1794 {
1795 dm->addInnerCompound(nd);
1796 }
1797 nd->setOuterScope(d);
1798 // in case of d is an inline namespace, alias insert nd in the part scope of d.
1800 {
1801 NamespaceDef *pnd = toNamespaceDef(d);
1802 if (pnd && pnd->isInline())
1803 {
1804 d = d->getOuterScope();
1805 if (d)
1806 {
1807 dm = toDefinitionMutable(d);
1808 if (dm)
1809 {
1810 auto aliasNd = createNamespaceDefAlias(d,nd);
1811 dm->addInnerCompound(aliasNd.get());
1812 QCString aliasName = aliasNd->name();
1813 AUTO_TRACE_ADD("adding alias {} to {}",aliasName,d->name());
1814 Doxygen::namespaceLinkedMap->add(aliasName,std::move(aliasNd));
1815 }
1816 }
1817 else
1818 {
1819 break;
1820 }
1821 }
1822 else
1823 {
1824 break;
1825 }
1826 }
1827 }
1828 }
1829 }
1830 }
1831 }
1832 for (const auto &e : root->children()) buildNamespaceList(e.get());
1833}
1834
1835//----------------------------------------------------------------------
1836
1838 const QCString &name)
1839{
1840 NamespaceDef *usingNd =nullptr;
1841 for (auto &und : unl)
1842 {
1843 QCString uScope=und->name()+"::";
1844 usingNd = getResolvedNamespace(uScope+name);
1845 if (usingNd!=nullptr) break;
1846 }
1847 return usingNd;
1848}
1849
1850static void findUsingDirectives(const Entry *root)
1851{
1852 if (root->section.isUsingDir())
1853 {
1854 AUTO_TRACE("Found using directive {} at line {} of {}",root->name,root->startLine,root->fileName);
1855 QCString name=substitute(root->name,".","::");
1856 if (name.endsWith("::"))
1857 {
1858 name=name.left(name.length()-2);
1859 }
1860 if (!name.isEmpty())
1861 {
1862 NamespaceDef *usingNd = nullptr;
1863 NamespaceDefMutable *nd = nullptr;
1864 FileDef *fd = root->fileDef();
1865 QCString nsName;
1866
1867 // see if the using statement was found inside a namespace or inside
1868 // the global file scope.
1869 if (root->parent() && root->parent()->section.isNamespace() &&
1870 (fd==nullptr || fd->getLanguage()!=SrcLangExt::Java) // not a .java file
1871 )
1872 {
1873 nsName=stripAnonymousNamespaceScope(root->parent()->name);
1874 if (!nsName.isEmpty())
1875 {
1876 nd = getResolvedNamespaceMutable(nsName);
1877 }
1878 }
1879
1880 // find the scope in which the 'using' namespace is defined by prepending
1881 // the possible scopes in which the using statement was found, starting
1882 // with the most inner scope and going to the most outer scope (i.e.
1883 // file scope).
1884 int scopeOffset = static_cast<int>(nsName.length());
1885 do
1886 {
1887 QCString scope=scopeOffset>0 ?
1888 nsName.left(scopeOffset)+"::" : QCString();
1889 usingNd = getResolvedNamespace(scope+name);
1890 //printf("Trying with scope='%s' usingNd=%p\n",(scope+qPrint(name)),usingNd);
1891 if (scopeOffset==0)
1892 {
1893 scopeOffset=-1;
1894 }
1895 else if ((scopeOffset=nsName.findRev("::",scopeOffset-1))==-1)
1896 {
1897 scopeOffset=0;
1898 }
1899 } while (scopeOffset>=0 && usingNd==nullptr);
1900
1901 if (usingNd==nullptr && nd) // not found, try used namespaces in this scope
1902 // or in one of the parent namespace scopes
1903 {
1904 const NamespaceDefMutable *pnd = nd;
1905 while (pnd && usingNd==nullptr)
1906 {
1907 // also try with one of the used namespaces found earlier
1909
1910 // goto the parent
1911 Definition *s = pnd->getOuterScope();
1913 {
1915 }
1916 else
1917 {
1918 pnd = nullptr;
1919 }
1920 }
1921 }
1922 if (usingNd==nullptr && fd) // still nothing, also try used namespace in the
1923 // global scope
1924 {
1925 usingNd = findUsedNamespace(fd->getUsedNamespaces(),name);
1926 }
1927
1928 //printf("%s -> %s\n",qPrint(name),usingNd?qPrint(usingNd->name()):"<none>");
1929
1930 // add the namespace the correct scope
1931 if (usingNd)
1932 {
1933 //printf("using fd=%p nd=%p\n",fd,nd);
1934 if (nd)
1935 {
1936 //printf("Inside namespace %s\n",qPrint(nd->name()));
1937 nd->addUsingDirective(usingNd);
1938 }
1939 else if (fd)
1940 {
1941 //printf("Inside file %s\n",qPrint(fd->name()));
1942 fd->addUsingDirective(usingNd);
1943 }
1944 }
1945 else // unknown namespace, but add it anyway.
1946 {
1947 AUTO_TRACE_ADD("new unknown namespace {} lang={} hidden={}",name,langToString(root->lang),root->hidden);
1948 // add namespace to the list
1951 createNamespaceDef(root->fileName,root->startLine,root->startColumn,name)));
1952 if (nd)
1953 {
1954 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1955 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1957 nd->setHidden(root->hidden);
1958 nd->setArtificial(TRUE);
1959 nd->setLanguage(root->lang);
1960 nd->setId(root->id);
1961 nd->setMetaData(root->metaData);
1962 nd->setInline(root->spec.isInline());
1963 nd->setExported(root->exported);
1964
1965 for (const Grouping &g : root->groups)
1966 {
1967 GroupDef *gd=nullptr;
1968 if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname)))
1969 gd->addNamespace(nd);
1970 }
1971
1972 // insert the namespace in the file definition
1973 if (fd)
1974 {
1975 fd->insertNamespace(nd);
1976 fd->addUsingDirective(nd);
1977 }
1978
1979 // the empty string test is needed for extract all case
1980 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1981 nd->insertUsedFile(fd);
1982 nd->setRefItems(root->sli);
1983 }
1984 }
1985 }
1986 }
1987 for (const auto &e : root->children()) findUsingDirectives(e.get());
1988}
1989
1990//----------------------------------------------------------------------
1991
1992static void buildListOfUsingDecls(const Entry *root)
1993{
1994 if (root->section.isUsingDecl() &&
1995 !root->parent()->section.isCompound() // not a class/struct member
1996 )
1997 {
1998 QCString name = substitute(root->name,".","::");
1999 g_usingDeclarations.insert(name.str());
2000 }
2001 for (const auto &e : root->children()) buildListOfUsingDecls(e.get());
2002}
2003
2004
2005static void findUsingDeclarations(const Entry *root,bool filterPythonPackages)
2006{
2007 if (root->section.isUsingDecl() &&
2008 !root->parent()->section.isCompound() && // not a class/struct member
2009 (!filterPythonPackages || (root->lang==SrcLangExt::Python && root->fileName.endsWith("__init__.py")))
2010 )
2011 {
2012 AUTO_TRACE("Found using declaration '{}' at line {} of {} inside section {}",
2013 root->name,root->startLine,root->fileName,root->parent()->section);
2014 if (!root->name.isEmpty())
2015 {
2016 const Definition *usingDef = nullptr;
2017 NamespaceDefMutable *nd = nullptr;
2018 FileDef *fd = root->fileDef();
2019 QCString scName;
2020
2021 // see if the using statement was found inside a namespace or inside
2022 // the global file scope.
2023 if (root->parent()->section.isNamespace())
2024 {
2025 scName=root->parent()->name;
2026 if (!scName.isEmpty())
2027 {
2028 nd = getResolvedNamespaceMutable(scName);
2029 }
2030 }
2031
2032 // Assume the using statement was used to import a class.
2033 // Find the scope in which the 'using' namespace is defined by prepending
2034 // the possible scopes in which the using statement was found, starting
2035 // with the most inner scope and going to the most outer scope (i.e.
2036 // file scope).
2037
2038 QCString name = substitute(root->name,".","::"); //Java/C# scope->internal
2039
2040 SymbolResolver resolver;
2041 const Definition *scope = nd;
2042 if (nd==nullptr) scope = fd;
2043 usingDef = resolver.resolveSymbol(scope,name);
2044
2045 //printf("usingDef(scope=%s,name=%s)=%s\n",qPrint(nd?nd->qualifiedName():""),qPrint(name),usingDef?qPrint(usingDef->qualifiedName()):"nullptr");
2046
2047 if (!usingDef)
2048 {
2049 usingDef = getClass(name); // try direct lookup, this is needed to get
2050 // builtin STL classes to properly resolve, e.g.
2051 // vector -> std::vector
2052 }
2053 if (!usingDef)
2054 {
2055 usingDef = Doxygen::hiddenClassLinkedMap->find(name); // check if it is already hidden
2056 }
2057#if 0
2058 if (!usingDef)
2059 {
2060 AUTO_TRACE_ADD("New using class '{}' (sec={})! #tArgLists={}",
2061 name,root->section,root->tArgLists.size());
2064 createClassDef( "<using>",1,1, name, ClassDef::Class)));
2065 if (usingCd)
2066 {
2067 usingCd->setArtificial(TRUE);
2068 usingCd->setLanguage(root->lang);
2069 usingDef = usingCd;
2070 }
2071 }
2072#endif
2073 else
2074 {
2075 AUTO_TRACE_ADD("Found used type '{}' in scope='{}'",
2076 usingDef->name(), nd ? nd->name(): fd ? fd->name() : QCString("<unknown>"));
2077 }
2078
2079 if (usingDef)
2080 {
2081 if (nd)
2082 {
2083 nd->addUsingDeclaration(usingDef);
2084 }
2085 else if (fd)
2086 {
2087 fd->addUsingDeclaration(usingDef);
2088 }
2089 }
2090 }
2091 }
2092 for (const auto &e : root->children()) findUsingDeclarations(e.get(),filterPythonPackages);
2093}
2094
2095//----------------------------------------------------------------------
2096
2098{
2099 root->commandOverrides.apply_callGraph ([&](bool b) { md->overrideCallGraph(b); });
2100 root->commandOverrides.apply_callerGraph ([&](bool b) { md->overrideCallerGraph(b); });
2101 root->commandOverrides.apply_referencedByRelation([&](bool b) { md->overrideReferencedByRelation(b); });
2102 root->commandOverrides.apply_referencesRelation ([&](bool b) { md->overrideReferencesRelation(b); });
2103 root->commandOverrides.apply_inlineSource ([&](bool b) { md->overrideInlineSource(b); });
2104 root->commandOverrides.apply_enumValues ([&](bool b) { md->overrideEnumValues(b); });
2105}
2106
2107//----------------------------------------------------------------------
2108
2110 const QCString &fileName,const QCString &memName)
2111{
2112 AUTO_TRACE("creating new member {} for class {}",memName,cd->name());
2113 const ArgumentList &templAl = md->templateArguments();
2114 const ArgumentList &al = md->argumentList();
2115 auto newMd = createMemberDef(
2116 fileName,root->startLine,root->startColumn,
2117 md->typeString(),memName,md->argsString(),
2118 md->excpString(),root->protection,root->virt,
2119 md->isStatic(),Relationship::Member,md->memberType(),
2120 templAl,al,root->metaData
2121 );
2122 auto newMmd = toMemberDefMutable(newMd.get());
2123 newMmd->setMemberClass(cd);
2124 cd->insertMember(newMd.get());
2125 if (!root->doc.isEmpty() || !root->brief.isEmpty())
2126 {
2127 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2128 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2129 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2130 }
2131 else
2132 {
2133 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2134 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2135 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2136 }
2137 newMmd->setDefinition(md->definition());
2138 applyMemberOverrideOptions(root,newMmd);
2139 newMmd->addQualifiers(root->qualifiers);
2140 newMmd->setBitfields(md->bitfieldString());
2141 newMmd->addSectionsToDefinition(root->anchors);
2142 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2143 newMmd->setBodyDef(md->getBodyDef());
2144 newMmd->setInitializer(md->initializer());
2145 newMmd->setRequiresClause(md->requiresClause());
2146 newMmd->setMaxInitLines(md->initializerLines());
2147 newMmd->setMemberGroupId(root->mGrpId);
2148 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2149 newMmd->setVhdlSpecifiers(md->getVhdlSpecifiers());
2150 newMmd->setLanguage(root->lang);
2151 newMmd->setId(root->id);
2152 MemberName *mn = Doxygen::memberNameLinkedMap->add(memName);
2153 mn->push_back(std::move(newMd));
2154}
2155
2156static std::unordered_map<std::string,std::vector<ClassDefMutable*>> g_usingClassMap;
2157
2158static void findUsingDeclImports(const Entry *root)
2159{
2160 if (root->section.isUsingDecl() &&
2161 root->parent()->section.isCompound() // in a class/struct member
2162 )
2163 {
2164 AUTO_TRACE("Found using declaration '{}' inside section {}", root->name, root->parent()->section);
2165 QCString fullName=removeRedundantWhiteSpace(root->parent()->name);
2166 fullName=stripAnonymousNamespaceScope(fullName);
2167 fullName=stripTemplateSpecifiersFromScope(fullName);
2168 ClassDefMutable *cd = getClassMutable(fullName);
2169 if (cd)
2170 {
2171 AUTO_TRACE_ADD("found class '{}'",cd->name());
2172 int i=root->name.findRev("::");
2173 if (i!=-1)
2174 {
2175 QCString scope=root->name.left(i);
2176 QCString memName=root->name.right(root->name.length()-i-2);
2177 SymbolResolver resolver;
2178 const ClassDef *bcd = resolver.resolveClass(cd,scope); // todo: file in fileScope parameter
2179 AUTO_TRACE_ADD("name={} scope={} bcd={}",scope,cd?cd->name():"<none>",bcd?bcd->name():"<none>");
2180 if (bcd && bcd!=cd)
2181 {
2182 AUTO_TRACE_ADD("found class '{}' memName='{}'",bcd->name(),memName);
2184 const MemberNameInfo *mni = mnlm.find(memName);
2185 if (mni)
2186 {
2187 for (auto &mi : *mni)
2188 {
2189 const MemberDef *md = mi->memberDef();
2190 if (md && md->protection()!=Protection::Private)
2191 {
2192 AUTO_TRACE_ADD("found member '{}'",mni->memberName());
2193 QCString fileName = root->fileName;
2194 if (fileName.isEmpty() && root->tagInfo())
2195 {
2196 fileName = root->tagInfo()->tagName;
2197 }
2198 if (!cd->containsOverload(md))
2199 {
2200 createUsingMemberImportForClass(root,cd,md,fileName,memName);
2201 // also insert the member into copies of the class
2202 auto it = g_usingClassMap.find(cd->qualifiedName().str());
2203 if (it != g_usingClassMap.end())
2204 {
2205 for (const auto &copyCd : it->second)
2206 {
2207 createUsingMemberImportForClass(root,copyCd,md,fileName,memName);
2208 }
2209 }
2210 }
2211 }
2212 }
2213 }
2214 }
2215 }
2216 }
2217 }
2218 else if (root->section.isUsingDecl() &&
2219 (root->parent()->section.isNamespace() || root->parent()->section.isEmpty()) && // namespace or global member
2220 root->lang==SrcLangExt::Cpp // do we also want this for e.g. Fortran? (see test case 095)
2221 )
2222 {
2223 AUTO_TRACE("Found using declaration '{}' inside section {}", root->name, root->parent()->section);
2224 Definition *scope = nullptr;
2225 NamespaceDefMutable *nd = nullptr;
2226 FileDef *fd = root->parent()->fileDef();
2227 if (!root->parent()->name.isEmpty())
2228 {
2229 QCString fullName=removeRedundantWhiteSpace(root->parent()->name);
2230 fullName=stripAnonymousNamespaceScope(fullName);
2232 scope = nd;
2233 }
2234 else
2235 {
2236 scope = fd;
2237 }
2238 if (scope)
2239 {
2240 AUTO_TRACE_ADD("found scope '{}'",scope->name());
2241 SymbolResolver resolver;
2242 const Definition *def = resolver.resolveSymbol(root->name.startsWith("::") ? nullptr : scope,root->name);
2243 if (def && def->definitionType()==Definition::TypeMember)
2244 {
2245 int i=root->name.findRev("::");
2246 QCString memName;
2247 if (i!=-1)
2248 {
2249 memName = root->name.right(root->name.length()-i-2);
2250 }
2251 else
2252 {
2253 memName = root->name;
2254 }
2255 const MemberDef *md = toMemberDef(def);
2256 AUTO_TRACE_ADD("found member '{}' for name '{}'",md->qualifiedName(),root->name);
2257 QCString fileName = root->fileName;
2258 if (fileName.isEmpty() && root->tagInfo())
2259 {
2260 fileName = root->tagInfo()->tagName;
2261 }
2262 const ArgumentList &templAl = md->templateArguments();
2263 const ArgumentList &al = md->argumentList();
2264
2265 auto newMd = createMemberDef(
2266 fileName,root->startLine,root->startColumn,
2267 md->typeString(),memName,md->argsString(),
2268 md->excpString(),root->protection,root->virt,
2269 md->isStatic(),Relationship::Member,md->memberType(),
2270 templAl,al,root->metaData
2271 );
2272 auto newMmd = toMemberDefMutable(newMd.get());
2273 if (nd)
2274 {
2275 newMmd->setNamespace(nd);
2276 nd->insertMember(newMd.get());
2277 }
2278 if (fd)
2279 {
2280 newMmd->setFileDef(fd);
2281 fd->insertMember(newMd.get());
2282 }
2283 if (!root->doc.isEmpty() || !root->brief.isEmpty())
2284 {
2285 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2286 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2287 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2288 }
2289 else
2290 {
2291 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2292 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2293 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2294 }
2295 newMmd->setDefinition(md->definition());
2296 applyMemberOverrideOptions(root,newMmd);
2297 newMmd->addQualifiers(root->qualifiers);
2298 newMmd->setBitfields(md->bitfieldString());
2299 newMmd->addSectionsToDefinition(root->anchors);
2300 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2301 newMmd->setBodyDef(md->getBodyDef());
2302 newMmd->setInitializer(md->initializer());
2303 newMmd->setRequiresClause(md->requiresClause());
2304 newMmd->setMaxInitLines(md->initializerLines());
2305 newMmd->setMemberGroupId(root->mGrpId);
2306 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2307 newMmd->setVhdlSpecifiers(md->getVhdlSpecifiers());
2308 newMmd->setLanguage(root->lang);
2309 newMmd->setId(root->id);
2310 MemberName *mn = Doxygen::functionNameLinkedMap->add(memName);
2311 mn->push_back(std::move(newMd));
2312#if 0 // insert an alias instead of a copy
2313 const MemberDef *md = toMemberDef(def);
2314 AUTO_TRACE_ADD("found member '{}' for name '{}'",md->qualifiedName(),root->name);
2315 auto aliasMd = createMemberDefAlias(nd,md);
2316 QCString aliasFullName = nd->qualifiedName()+"::"+aliasMd->localName();
2317 if (nd && aliasMd.get())
2318 {
2319 nd->insertMember(aliasMd.get());
2320 }
2321 if (fd && aliasMd.get())
2322 {
2323 fd->insertMember(aliasMd.get());
2324 }
2325 MemberName *mn = Doxygen::memberNameLinkedMap->add(aliasFullName);
2326 mn->push_back(std::move(aliasMd));
2327#endif
2328 }
2329 else if (def && def->definitionType()==Definition::TypeClass)
2330 {
2331 const ClassDef *cd = toClassDef(def);
2332 QCString copyFullName;
2333 if (nd==nullptr)
2334 {
2335 copyFullName = cd->localName();
2336 }
2337 else
2338 {
2339 copyFullName = nd->qualifiedName()+"::"+cd->localName();
2340 }
2341 if (Doxygen::classLinkedMap->find(copyFullName)==nullptr)
2342 {
2344 Doxygen::classLinkedMap->add(copyFullName,
2345 cd->deepCopy(copyFullName)));
2346 AUTO_TRACE_ADD("found class '{}' for name '{}' copy '{}' obj={}",cd->qualifiedName(),root->name,copyFullName,(void*)ncdm);
2347 g_usingClassMap[cd->qualifiedName().str()].push_back(ncdm);
2348 if (ncdm)
2349 {
2350 if (nd) ncdm->moveTo(nd);
2351 if ((!root->doc.isEmpty() || !root->brief.isEmpty())) // use docs at using statement
2352 {
2353 ncdm->setDocumentation(root->doc,root->docFile,root->docLine);
2354 ncdm->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2355 }
2356 else // use docs from used class
2357 {
2358 ncdm->setDocumentation(cd->documentation(),cd->docFile(),cd->docLine());
2360 }
2361 if (nd)
2362 {
2363 nd->addInnerCompound(ncdm);
2364 nd->addUsingDeclaration(ncdm);
2365 }
2366 if (fd)
2367 {
2368 if (ncdm) ncdm->setFileDef(fd);
2369 fd->insertClass(ncdm);
2370 fd->addUsingDeclaration(ncdm);
2371 }
2372 }
2373 }
2374#if 0 // insert an alias instead of a copy
2375 auto aliasCd = createClassDefAlias(nd,cd);
2376 QCString aliasFullName;
2377 if (nd==nullptr)
2378 {
2379 aliasFullName = aliasCd->localName();
2380 }
2381 else
2382 {
2383 aliasFullName = nd->qualifiedName()+"::"+aliasCd->localName();
2384 }
2385 AUTO_TRACE_ADD("found class '{}' for name '{}' aliasFullName='{}'",cd->qualifiedName(),root->name,aliasFullName);
2386 auto acd = Doxygen::classLinkedMap->add(aliasFullName,std::move(aliasCd));
2387 if (nd && acd)
2388 {
2389 nd->addInnerCompound(acd);
2390 }
2391 if (fd && acd)
2392 {
2393 fd->insertClass(acd);
2394 }
2395#endif
2396 }
2397 else if (scope)
2398 {
2399 AUTO_TRACE_ADD("no symbol with name '{}' in scope {}",root->name,scope->name());
2400 }
2401 }
2402 }
2403 for (const auto &e : root->children()) findUsingDeclImports(e.get());
2404}
2405
2406//----------------------------------------------------------------------
2407
2409{
2410 FileDefSet visitedFiles;
2411 // then recursively add using directives found in #include files
2412 // to files that have not been visited.
2413 for (const auto &fn : *Doxygen::inputNameLinkedMap)
2414 {
2415 for (const auto &fd : *fn)
2416 {
2417 //printf("----- adding using directives for file %s\n",qPrint(fd->name()));
2418 fd->addIncludedUsingDirectives(visitedFiles);
2419 }
2420 }
2421}
2422
2423//----------------------------------------------------------------------
2424
2426 const Entry *root,
2427 ClassDefMutable *cd,
2428 MemberType mtype,
2429 const QCString &type,
2430 const QCString &name,
2431 const QCString &args,
2432 bool fromAnnScope,
2433 MemberDef *fromAnnMemb,
2434 Protection prot,
2435 Relationship related)
2436{
2438 QCString scopeSeparator="::";
2439 SrcLangExt lang = cd->getLanguage();
2440 if (lang==SrcLangExt::Java || lang==SrcLangExt::CSharp)
2441 {
2442 qualScope = substitute(qualScope,"::",".");
2443 scopeSeparator=".";
2444 }
2445 AUTO_TRACE("class variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} ann={} init='{}'",
2446 root->fileName, type, qualScope, name, args, root->protection, mtype, lang, fromAnnScope, root->initializer.str());
2447
2448 QCString def;
2449 if (!type.isEmpty())
2450 {
2451 if (related!=Relationship::Member || mtype==MemberType::Friend || Config_getBool(HIDE_SCOPE_NAMES))
2452 {
2453 if (root->spec.isAlias()) // turn 'typedef B A' into 'using A'
2454 {
2455 def="using "+name;
2456 }
2457 else
2458 {
2459 def=type+" "+name+args;
2460 }
2461 }
2462 else
2463 {
2464 if (root->spec.isAlias()) // turn 'typedef B C::A' into 'using C::A'
2465 {
2466 def="using "+qualScope+scopeSeparator+name;
2467 }
2468 else
2469 {
2470 def=type+" "+qualScope+scopeSeparator+name+args;
2471 }
2472 }
2473 }
2474 else
2475 {
2476 if (Config_getBool(HIDE_SCOPE_NAMES))
2477 {
2478 def=name+args;
2479 }
2480 else
2481 {
2482 def=qualScope+scopeSeparator+name+args;
2483 }
2484 }
2485 def.stripPrefix("static ");
2486
2487 // see if the member is already found in the same scope
2488 // (this may be the case for a static member that is initialized
2489 // outside the class)
2491 if (mn)
2492 {
2493 for (const auto &imd : *mn)
2494 {
2495 //printf("md->getClassDef()=%p cd=%p type=[%s] md->typeString()=[%s]\n",
2496 // md->getClassDef(),cd,qPrint(type),md->typeString());
2497 MemberDefMutable *md = toMemberDefMutable(imd.get());
2498 if (md &&
2499 md->getClassDef()==cd &&
2500 ((lang==SrcLangExt::Python && type.isEmpty() && !md->typeString().isEmpty()) ||
2502 // member already in the scope
2503 {
2504
2505 if (root->lang==SrcLangExt::ObjC &&
2506 root->mtype==MethodTypes::Property &&
2508 { // Objective-C 2.0 property
2509 // turn variable into a property
2510 md->setProtection(root->protection);
2512 }
2513 addMemberDocs(root,md,def,nullptr,FALSE,root->spec);
2514 AUTO_TRACE_ADD("Member already found!");
2515 return md;
2516 }
2517 }
2518 }
2519
2520 QCString fileName = root->fileName;
2521 if (fileName.isEmpty() && root->tagInfo())
2522 {
2523 fileName = root->tagInfo()->tagName;
2524 }
2525
2526 // new member variable, typedef or enum value
2527 auto md = createMemberDef(
2528 fileName,root->startLine,root->startColumn,
2529 type,name,args,root->exception,
2530 prot,Specifier::Normal,root->isStatic,related,
2531 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2532 ArgumentList(), root->metaData);
2533 auto mmd = toMemberDefMutable(md.get());
2534 mmd->setTagInfo(root->tagInfo());
2535 mmd->setMemberClass(cd); // also sets outer scope (i.e. getOuterScope())
2536 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2537 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2538 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2539 mmd->setDefinition(def);
2540 mmd->setBitfields(root->bitfields);
2541 mmd->addSectionsToDefinition(root->anchors);
2542 mmd->setFromAnonymousScope(fromAnnScope);
2543 mmd->setFromAnonymousMember(fromAnnMemb);
2544 //md->setIndentDepth(indentDepth);
2545 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2546 mmd->setInitializer(root->initializer.str());
2547 mmd->setMaxInitLines(root->initLines);
2548 mmd->setMemberGroupId(root->mGrpId);
2549 mmd->setMemberSpecifiers(root->spec);
2550 mmd->setVhdlSpecifiers(root->vhdlSpec);
2551 mmd->setReadAccessor(root->read);
2552 mmd->setWriteAccessor(root->write);
2554 mmd->setHidden(root->hidden);
2555 mmd->setArtificial(root->artificial);
2556 mmd->setLanguage(root->lang);
2557 mmd->setId(root->id);
2558 addMemberToGroups(root,md.get());
2560 mmd->setBodyDef(root->fileDef());
2561 mmd->addQualifiers(root->qualifiers);
2562
2563 AUTO_TRACE_ADD("Adding new member to class '{}'",cd->name());
2564 cd->insertMember(md.get());
2565 mmd->setRefItems(root->sli);
2566
2567 cd->insertUsedFile(root->fileDef());
2568 root->markAsProcessed();
2569
2570 if (mtype==MemberType::Typedef)
2571 {
2572 resolveTemplateInstanceInType(root,cd,md.get());
2573 }
2574
2575 // add the member to the global list
2576 MemberDef *result = md.get();
2577 mn = Doxygen::memberNameLinkedMap->add(name);
2578 mn->push_back(std::move(md));
2579
2580 return result;
2581}
2582
2583//----------------------------------------------------------------------
2584
2586 const Entry *root,
2587 MemberType mtype,
2588 const QCString &scope,
2589 const QCString &type,
2590 const QCString &name,
2591 const QCString &args,
2592 bool fromAnnScope,
2593 MemberDef *fromAnnMemb)
2594{
2595 AUTO_TRACE("global variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} init='{}'",
2596 root->fileName, type, scope, name, args, root->protection, mtype, root->lang, root->initializer.str());
2597
2598 FileDef *fd = root->fileDef();
2599
2600 // see if we have a typedef that should hide a struct or union
2601 if (mtype==MemberType::Typedef && Config_getBool(TYPEDEF_HIDES_STRUCT))
2602 {
2603 QCString ttype = type;
2604 ttype.stripPrefix("typedef ");
2605 if (ttype.stripPrefix("struct ") || ttype.stripPrefix("union "))
2606 {
2607 static const reg::Ex re(R"(\a\w*)");
2608 reg::Match match;
2609 std::string typ = ttype.str();
2610 if (reg::search(typ,match,re))
2611 {
2612 QCString typeValue = match.str();
2613 ClassDefMutable *cd = getClassMutable(typeValue);
2614 if (cd)
2615 {
2616 // this typedef should hide compound name cd, so we
2617 // change the name that is displayed from cd.
2618 cd->setClassName(name);
2619 cd->setDocumentation(root->doc,root->docFile,root->docLine);
2620 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2621 return nullptr;
2622 }
2623 }
2624 }
2625 }
2626
2627 // see if the function is inside a namespace
2628 NamespaceDefMutable *nd = nullptr;
2629 if (!scope.isEmpty())
2630 {
2631 if (scope.find('@')!=-1) return nullptr; // anonymous scope!
2632 nd = getResolvedNamespaceMutable(scope);
2633 }
2634 QCString def;
2635
2636 // determine the definition of the global variable
2637 if (nd && !nd->isAnonymous() &&
2638 !Config_getBool(HIDE_SCOPE_NAMES)
2639 )
2640 // variable is inside a namespace, so put the scope before the name
2641 {
2642 SrcLangExt lang = nd->getLanguage();
2644
2645 if (!type.isEmpty())
2646 {
2647 if (root->spec.isAlias()) // turn 'typedef B NS::A' into 'using NS::A'
2648 {
2649 def="using "+nd->name()+sep+name;
2650 }
2651 else // normal member
2652 {
2653 def=type+" "+nd->name()+sep+name+args;
2654 }
2655 }
2656 else
2657 {
2658 def=nd->name()+sep+name+args;
2659 }
2660 }
2661 else
2662 {
2663 if (!type.isEmpty() && !root->name.isEmpty())
2664 {
2665 if (name.at(0)=='@') // dummy variable representing anonymous union
2666 {
2667 def=type;
2668 }
2669 else
2670 {
2671 if (root->spec.isAlias()) // turn 'typedef B A' into 'using A'
2672 {
2673 def="using "+root->name;
2674 }
2675 else // normal member
2676 {
2677 def=type+" "+name+args;
2678 }
2679 }
2680 }
2681 else
2682 {
2683 def=name+args;
2684 }
2685 }
2686 def.stripPrefix("static ");
2687
2689 if (mn)
2690 {
2691 //QCString nscope=removeAnonymousScopes(scope);
2692 //NamespaceDef *nd=nullptr;
2693 //if (!nscope.isEmpty())
2694 if (!scope.isEmpty())
2695 {
2696 nd = getResolvedNamespaceMutable(scope);
2697 }
2698 for (const auto &imd : *mn)
2699 {
2700 MemberDefMutable *md = toMemberDefMutable(imd.get());
2701 if (md &&
2702 ((nd==nullptr && md->getNamespaceDef()==nullptr && md->getFileDef() &&
2703 root->fileName==md->getFileDef()->absFilePath()
2704 ) // both variable names in the same file
2705 || (nd!=nullptr && md->getNamespaceDef()==nd) // both in same namespace
2706 )
2707 && !md->isDefine() // function style #define's can be "overloaded" by typedefs or variables
2708 && !md->isEnumerate() // in C# an enum value and enum can have the same name
2709 )
2710 // variable already in the scope
2711 {
2712 bool isPHPArray = md->getLanguage()==SrcLangExt::PHP &&
2713 md->argsString()!=args &&
2714 args.find('[')!=-1;
2715 bool staticsInDifferentFiles =
2716 root->isStatic && md->isStatic() &&
2717 root->fileName!=md->getDefFileName();
2718
2719 if (md->getFileDef() &&
2720 !isPHPArray && // not a php array
2721 !staticsInDifferentFiles
2722 )
2723 // not a php array variable
2724 {
2725 AUTO_TRACE_ADD("variable already found: scope='{}'",md->getOuterScope()->name());
2726 addMemberDocs(root,md,def,nullptr,FALSE,root->spec);
2727 md->setRefItems(root->sli);
2728 // if md is a variable forward declaration and root is the definition that
2729 // turn md into the definition
2730 if (!root->explicitExternal && md->isExternal())
2731 {
2732 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
2734 }
2735 // if md is the definition and root point at a declaration, then add the
2736 // declaration info
2737 else if (root->explicitExternal && !md->isExternal())
2738 {
2739 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
2740 }
2741 return md;
2742 }
2743 }
2744 }
2745 }
2746
2747 QCString fileName = root->fileName;
2748 if (fileName.isEmpty() && root->tagInfo())
2749 {
2750 fileName = root->tagInfo()->tagName;
2751 }
2752
2753 AUTO_TRACE_ADD("new variable, namespace='{}'",nd?nd->name():QCString("<global>"));
2754 // new global variable, enum value or typedef
2755 auto md = createMemberDef(
2756 fileName,root->startLine,root->startColumn,
2757 type,name,args,QCString(),
2758 root->protection, Specifier::Normal,root->isStatic,Relationship::Member,
2759 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2760 root->argList, root->metaData);
2761 auto mmd = toMemberDefMutable(md.get());
2762 mmd->setTagInfo(root->tagInfo());
2763 mmd->setMemberSpecifiers(root->spec);
2764 mmd->setVhdlSpecifiers(root->vhdlSpec);
2765 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2766 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2767 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2768 mmd->addSectionsToDefinition(root->anchors);
2769 mmd->setFromAnonymousScope(fromAnnScope);
2770 mmd->setFromAnonymousMember(fromAnnMemb);
2771 mmd->setInitializer(root->initializer.str());
2772 mmd->setMaxInitLines(root->initLines);
2773 mmd->setMemberGroupId(root->mGrpId);
2774 mmd->setDefinition(def);
2775 mmd->setLanguage(root->lang);
2776 mmd->setId(root->id);
2778 mmd->setExplicitExternal(root->explicitExternal,fileName,root->startLine,root->startColumn);
2779 mmd->addQualifiers(root->qualifiers);
2780 //md->setOuterScope(fd);
2781 if (!root->explicitExternal)
2782 {
2783 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2784 mmd->setBodyDef(fd);
2785 }
2786 addMemberToGroups(root,md.get());
2788
2789 mmd->setRefItems(root->sli);
2790 if (nd && !nd->isAnonymous())
2791 {
2792 mmd->setNamespace(nd);
2793 nd->insertMember(md.get());
2794 }
2795
2796 // add member to the file (we do this even if we have already inserted
2797 // it into the namespace.
2798 if (fd)
2799 {
2800 mmd->setFileDef(fd);
2801 fd->insertMember(md.get());
2802 }
2803
2804 root->markAsProcessed();
2805
2806 if (mtype==MemberType::Typedef)
2807 {
2808 resolveTemplateInstanceInType(root,nd,md.get());
2809 }
2810
2811 // add member definition to the list of globals
2812 MemberDef *result = md.get();
2813 mn = Doxygen::functionNameLinkedMap->add(name);
2814 mn->push_back(std::move(md));
2815
2816
2817
2818 return result;
2819}
2820
2821/*! See if the return type string \a type is that of a function pointer
2822 * \returns -1 if this is not a function pointer variable or
2823 * the index at which the closing brace of (...*name) was found.
2824 */
2825static int findFunctionPtr(const std::string &type,SrcLangExt lang, int *pLength=nullptr)
2826{
2827 AUTO_TRACE("type='{}' lang={}",type,lang);
2828 if (lang == SrcLangExt::Fortran || lang == SrcLangExt::VHDL)
2829 {
2830 return -1; // Fortran and VHDL do not have function pointers
2831 }
2832
2833 static const reg::Ex re(R"(\‍([^)]*[*&^][^)]*\))");
2834 reg::Match match;
2835 size_t i=std::string::npos;
2836 size_t l=0;
2837 if (reg::search(type,match,re)) // contains (...*...) or (...&...) or (...^...)
2838 {
2839 i = match.position();
2840 l = match.length();
2841 }
2842 if (i!=std::string::npos)
2843 {
2844 size_t di = type.find("decltype(");
2845 if (di!=std::string::npos && di<i)
2846 {
2847 i = std::string::npos;
2848 }
2849 }
2850 size_t bb=type.find('<');
2851 size_t be=type.rfind('>');
2852 bool templFp = false;
2853 if (be!=std::string::npos) {
2854 size_t cc_ast = type.find("::*");
2855 size_t cc_amp = type.find("::&");
2856 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>::*)'
2857 }
2858
2859 if (!type.empty() && // return type is non-empty
2860 i!=std::string::npos && // contains (...*...)
2861 type.find("operator")==std::string::npos && // not an operator
2862 (type.find(")(")==std::string::npos || type.find("typedef ")!=std::string::npos) &&
2863 // not a function pointer return type
2864 (!(bb<i && i<be) || templFp) // bug665855: avoid treating "typedef A<void (T*)> type" as a function pointer
2865 )
2866 {
2867 if (pLength) *pLength=static_cast<int>(l);
2868 //printf("findFunctionPtr=%d\n",(int)i);
2869 AUTO_TRACE_EXIT("result={}",i);
2870 return static_cast<int>(i);
2871 }
2872 else
2873 {
2874 //printf("findFunctionPtr=%d\n",-1);
2875 AUTO_TRACE_EXIT("result=-1");
2876 return -1;
2877 }
2878}
2879
2880//--------------------------------------------------------------------------------------
2881
2882/*! Returns TRUE iff \a type is a class within scope \a context.
2883 * Used to detect variable declarations that look like function prototypes.
2884 */
2885static bool isVarWithConstructor(const Entry *root)
2886{
2887 bool result = false;
2888 bool typeIsClass = false;
2889 bool typePtrType = false;
2890 QCString type;
2891 Definition *ctx = nullptr;
2892 FileDef *fd = root->fileDef();
2893 SymbolResolver resolver(fd);
2894
2895 AUTO_TRACE("isVarWithConstructor({})",root->name);
2896 if (root->parent()->section.isCompound())
2897 { // inside a class
2898 result=FALSE;
2899 AUTO_TRACE_EXIT("inside class: result={}",result);
2900 return result;
2901 }
2902 else if ((fd != nullptr) && (fd->name().endsWith(".c") || fd->name().endsWith(".h")))
2903 { // inside a .c file
2904 result=FALSE;
2905 AUTO_TRACE_EXIT("inside C file: result={}",result);
2906 return result;
2907 }
2908 if (root->type.isEmpty())
2909 {
2910 result=FALSE;
2911 AUTO_TRACE_EXIT("no type: result={}",result);
2912 return result;
2913 }
2914 if (!root->parent()->name.isEmpty())
2915 {
2916 ctx=Doxygen::namespaceLinkedMap->find(root->parent()->name);
2917 }
2918 type = root->type;
2919 // remove qualifiers
2920 findAndRemoveWord(type,"const");
2921 findAndRemoveWord(type,"static");
2922 findAndRemoveWord(type,"volatile");
2923 typePtrType = type.find('*')!=-1 || type.find('&')!=-1;
2924 if (!typePtrType)
2925 {
2926 typeIsClass = resolver.resolveClass(ctx,type)!=nullptr;
2927 int ti=0;
2928 if (!typeIsClass && (ti=type.find('<'))!=-1)
2929 {
2930 typeIsClass=resolver.resolveClass(ctx,type.left(ti))!=nullptr;
2931 }
2932 }
2933 if (typeIsClass) // now we still have to check if the arguments are
2934 // types or values. Since we do not have complete type info
2935 // we need to rely on heuristics :-(
2936 {
2937 if (root->argList.empty())
2938 {
2939 result=FALSE; // empty arg list -> function prototype.
2940 AUTO_TRACE_EXIT("empty arg list: result={}",result);
2941 return result;
2942 }
2943 for (const Argument &a : root->argList)
2944 {
2945 static const reg::Ex initChars(R"([\d"'&*!^]+)");
2946 reg::Match match;
2947 if (!a.name.isEmpty() || !a.defval.isEmpty())
2948 {
2949 std::string name = a.name.str();
2950 if (reg::search(name,match,initChars) && match.position()==0)
2951 {
2952 result=TRUE;
2953 }
2954 else
2955 {
2956 result=FALSE; // arg has (type,name) pair -> function prototype
2957 }
2958 AUTO_TRACE_EXIT("function prototype: result={}",result);
2959 return result;
2960 }
2961 if (!a.type.isEmpty() &&
2962 (a.type.at(a.type.length()-1)=='*' ||
2963 a.type.at(a.type.length()-1)=='&'))
2964 // type ends with * or & => pointer or reference
2965 {
2966 result=FALSE;
2967 AUTO_TRACE_EXIT("pointer or reference: result={}",result);
2968 return result;
2969 }
2970 if (a.type.isEmpty() || resolver.resolveClass(ctx,a.type)!=nullptr)
2971 {
2972 result=FALSE; // arg type is a known type
2973 AUTO_TRACE_EXIT("known type: result={}",result);
2974 return result;
2975 }
2976 if (checkIfTypedef(ctx,fd,a.type))
2977 {
2978 result=FALSE; // argument is a typedef
2979 AUTO_TRACE_EXIT("typedef: result={}",result);
2980 return result;
2981 }
2982 std::string atype = a.type.str();
2983 if (reg::search(atype,match,initChars) && match.position()==0)
2984 {
2985 result=TRUE; // argument type starts with typical initializer char
2986 AUTO_TRACE_EXIT("argument with init char: result={}",result);
2987 return result;
2988 }
2989 std::string resType=resolveTypeDef(ctx,a.type).str();
2990 if (resType.empty()) resType=atype;
2991 static const reg::Ex idChars(R"(\a\w*)");
2992 if (reg::search(resType,match,idChars) && match.position()==0) // resType starts with identifier
2993 {
2994 resType=match.str();
2995 if (resType=="int" || resType=="long" ||
2996 resType=="float" || resType=="double" ||
2997 resType=="char" || resType=="void" ||
2998 resType=="signed" || resType=="unsigned" ||
2999 resType=="const" || resType=="volatile" )
3000 {
3001 result=FALSE; // type keyword -> function prototype
3002 AUTO_TRACE_EXIT("type keyword: result={}",result);
3003 return result;
3004 }
3005 }
3006 }
3007 result=TRUE;
3008 }
3009
3010 AUTO_TRACE_EXIT("end: result={}",result);
3011 return result;
3012}
3013
3014//--------------------------------------------------------------------------------------
3015
3016/*! Searches for the end of a template in prototype \a s starting from
3017 * character position \a startPos. If the end was found the position
3018 * of the closing > is returned, otherwise -1 is returned.
3019 *
3020 * Handles exotic cases such as
3021 * \code
3022 * Class<(id<0)>
3023 * Class<bits<<2>
3024 * Class<"<">
3025 * Class<'<'>
3026 * Class<(")<")>
3027 * \endcode
3028 */
3029static int findEndOfTemplate(const QCString &s,size_t startPos)
3030{
3031 // locate end of template
3032 size_t e=startPos;
3033 int brCount=1;
3034 int roundCount=0;
3035 size_t len = s.length();
3036 bool insideString=FALSE;
3037 bool insideChar=FALSE;
3038 char pc = 0;
3039 while (e<len && brCount!=0)
3040 {
3041 char c=s.at(e);
3042 switch(c)
3043 {
3044 case '<':
3045 if (!insideString && !insideChar)
3046 {
3047 if (e<len-1 && s.at(e+1)=='<')
3048 e++;
3049 else if (roundCount==0)
3050 brCount++;
3051 }
3052 break;
3053 case '>':
3054 if (!insideString && !insideChar)
3055 {
3056 if (e<len-1 && s.at(e+1)=='>')
3057 e++;
3058 else if (roundCount==0)
3059 brCount--;
3060 }
3061 break;
3062 case '(':
3063 if (!insideString && !insideChar)
3064 roundCount++;
3065 break;
3066 case ')':
3067 if (!insideString && !insideChar)
3068 roundCount--;
3069 break;
3070 case '"':
3071 if (!insideChar)
3072 {
3073 if (insideString && pc!='\\')
3074 insideString=FALSE;
3075 else
3076 insideString=TRUE;
3077 }
3078 break;
3079 case '\'':
3080 if (!insideString)
3081 {
3082 if (insideChar && pc!='\\')
3083 insideChar=FALSE;
3084 else
3085 insideChar=TRUE;
3086 }
3087 break;
3088 }
3089 pc = c;
3090 e++;
3091 }
3092 return brCount==0 ? static_cast<int>(e) : -1;
3093}
3094
3095//--------------------------------------------------------------------------------------
3096
3097static void addVariable(const Entry *root,int isFuncPtr=-1)
3098{
3099 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
3100
3101 AUTO_TRACE("VARIABLE_SEC: type='{}' name='{}' args='{}' bodyLine={} endBodyLine={} mGrpId={} relates='{}'",
3102 root->type, root->name, root->args, root->bodyLine, root->endBodyLine, root->mGrpId, root->relates);
3103 //printf("root->parent->name=%s\n",qPrint(root->parent->name));
3104
3105 QCString type = root->type;
3106 QCString name = root->name;
3107 QCString args = root->args;
3108 if (type.isEmpty() && name.find("operator")==-1 &&
3109 (name.find('*')!=-1 || name.find('&')!=-1))
3110 {
3111 // recover from parse error caused by redundant braces
3112 // like in "int *(var[10]);", which is parsed as
3113 // type="" name="int *" args="(var[10])"
3114
3115 type=name;
3116 std::string sargs = args.str();
3117 static const reg::Ex reName(R"(\a\w*)");
3118 reg::Match match;
3119 if (reg::search(sargs,match,reName))
3120 {
3121 name = match.str(); // e.g. 'var' in '(var[10])'
3122 sargs = match.suffix().str(); // e.g. '[10]) in '(var[10])'
3123 size_t j = sargs.find(')');
3124 if (j!=std::string::npos) args=sargs.substr(0,j); // extract, e.g '[10]' from '[10])'
3125 }
3126 }
3127 else
3128 {
3129 int i=isFuncPtr;
3130 if (i==-1 && (root->spec.isAlias())==0) i=findFunctionPtr(type.str(),root->lang); // for typedefs isFuncPtr is not yet set
3131 AUTO_TRACE_ADD("functionPtr={}",i!=-1?"yes":"no");
3132 if (i>=0) // function pointer
3133 {
3134 int ai = type.find('[',i);
3135 if (ai>i) // function pointer array
3136 {
3137 args.prepend(type.right(type.length()-ai));
3138 type=type.left(ai);
3139 }
3140 else if (type.find(')',i)!=-1) // function ptr, not variable like "int (*bla)[10]"
3141 {
3142 type=type.left(type.length()-1);
3143 args.prepend(") ");
3144 }
3145 }
3146 }
3147 AUTO_TRACE_ADD("after correction: type='{}' name='{}' args='{}'",type,name,args);
3148
3149 QCString scope;
3150 name=removeRedundantWhiteSpace(name);
3151
3152 // find the scope of this variable
3153 int index = computeQualifiedIndex(name);
3154 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3155 // grouped members are stored with full scope
3156 {
3157 buildScopeFromQualifiedName(name.left(index+2),root->lang,root->tagInfo());
3158 scope=name.left(index);
3159 name=name.mid(index+2);
3160 }
3161 else
3162 {
3163 Entry *p = root->parent();
3164 while (p->section.isScope())
3165 {
3166 QCString scopeName = p->name;
3167 if (!scopeName.isEmpty())
3168 {
3169 scope.prepend(scopeName);
3170 break;
3171 }
3172 p=p->parent();
3173 }
3174 }
3175
3176 type=type.stripWhiteSpace();
3177 ClassDefMutable *cd=nullptr;
3178 bool isRelated=FALSE;
3179 bool isMemberOf=FALSE;
3180
3181 QCString classScope=stripAnonymousNamespaceScope(scope);
3182 if (root->lang==SrcLangExt::CSharp)
3183 {
3184 classScope=mangleCSharpGenericName(classScope);
3185 }
3186 else
3187 {
3188 classScope=stripTemplateSpecifiersFromScope(classScope,FALSE);
3189 }
3190 QCString annScopePrefix=scope.left(scope.length()-classScope.length());
3191
3192
3193 // Look for last :: not part of template specifier
3194 int p=-1;
3195 for (size_t i=0;i<name.length()-1;i++)
3196 {
3197 if (name[i]==':' && name[i+1]==':')
3198 {
3199 p=static_cast<int>(i);
3200 }
3201 else if (name[i]=='<') // skip over template parts,
3202 // i.e. A::B<C::D> => p=1 and
3203 // A<B::C>::D => p=8
3204 {
3205 int e = findEndOfTemplate(name,i+1);
3206 if (e!=-1) i=static_cast<int>(e);
3207 }
3208 }
3209
3210 if (p!=-1) // found it
3211 {
3212 if (type=="friend class" || type=="friend struct" ||
3213 type=="friend union")
3214 {
3215 cd=getClassMutable(scope);
3216 if (cd)
3217 {
3218 addVariableToClass(root, // entry
3219 cd, // class to add member to
3220 MemberType::Friend, // type of member
3221 type, // type value as string
3222 name, // name of the member
3223 args, // arguments as string
3224 FALSE, // from Anonymous scope
3225 nullptr, // anonymous member
3226 Protection::Public, // protection
3227 Relationship::Member // related to a class
3228 );
3229 }
3230 }
3231 if (root->bodyLine!=-1 && root->endBodyLine!=-1) // store the body location for later use
3232 {
3233 Doxygen::staticInitMap.emplace(name.str(),BodyInfo{root->startLine,root->bodyLine,root->endBodyLine});
3234 }
3235
3236
3237 AUTO_TRACE_ADD("static variable {} body=[{}..{}]",name,root->bodyLine,root->endBodyLine);
3238 return; /* skip this member, because it is a
3239 * static variable definition (always?), which will be
3240 * found in a class scope as well, but then we know the
3241 * correct protection level, so only then it will be
3242 * inserted in the correct list!
3243 */
3244 }
3245
3247 if (type=="@")
3249 else if (type.startsWith("typedef "))
3250 mtype=MemberType::Typedef;
3251 else if (type.startsWith("friend "))
3252 mtype=MemberType::Friend;
3253 else if (root->mtype==MethodTypes::Property)
3255 else if (root->mtype==MethodTypes::Event)
3256 mtype=MemberType::Event;
3257 else if (type.find("sequence<") != -1)
3258 mtype=sliceOpt ? MemberType::Sequence : MemberType::Typedef;
3259 else if (type.find("dictionary<") != -1)
3261
3262 if (!root->relates.isEmpty()) // related variable
3263 {
3264 isRelated=TRUE;
3265 isMemberOf=(root->relatesType==RelatesType::MemberOf);
3266 if (getClass(root->relates)==nullptr && !scope.isEmpty())
3267 scope=mergeScopes(scope,root->relates);
3268 else
3269 scope=root->relates;
3270 }
3271
3272 cd=getClassMutable(scope);
3273 if (cd==nullptr && classScope!=scope) cd=getClassMutable(classScope);
3274 if (cd)
3275 {
3276 MemberDef *md=nullptr;
3277
3278 // if cd is an anonymous (=tag less) scope we insert the member
3279 // into a non-anonymous parent scope as well. This is needed to
3280 // be able to refer to it using \var or \fn
3281
3282 //int indentDepth=0;
3283 int si=scope.find('@');
3284 //int anonyScopes = 0;
3285 //bool added=FALSE;
3286
3287 bool inlineSimpleStructs = Config_getBool(INLINE_SIMPLE_STRUCTS);
3288 Relationship relationship = isMemberOf ? Relationship::Foreign :
3289 isRelated ? Relationship::Related :
3290 Relationship::Member ;
3291 if (si!=-1 && !inlineSimpleStructs) // anonymous scope or type
3292 {
3293 QCString pScope;
3294 ClassDefMutable *pcd=nullptr;
3295 pScope = scope.left(std::max(si-2,0)); // scope without tag less parts
3296 if (!pScope.isEmpty())
3297 pScope.prepend(annScopePrefix);
3298 else if (annScopePrefix.length()>2)
3299 pScope=annScopePrefix.left(annScopePrefix.length()-2);
3300 if (name.at(0)!='@')
3301 {
3302 if (!pScope.isEmpty() && (pcd=getClassMutable(pScope)))
3303 {
3304 AUTO_TRACE_ADD("Adding anonymous member to scope '{}'",pScope);
3305 md=addVariableToClass(root, // entry
3306 pcd, // class to add member to
3307 mtype, // member type
3308 type, // type value as string
3309 name, // member name
3310 args, // arguments as string
3311 TRUE, // from anonymous scope
3312 nullptr, // from anonymous member
3313 root->protection,
3314 relationship
3315 );
3316 //added=TRUE;
3317 }
3318 else // anonymous scope inside namespace or file => put variable in the global scope
3319 {
3320 if (mtype==MemberType::Variable)
3321 {
3322 AUTO_TRACE_ADD("Adding anonymous member to global scope '{}'");
3323 md=addVariableToFile(root,mtype,pScope,type,name,args,TRUE,nullptr);
3324 }
3325 //added=TRUE;
3326 }
3327 }
3328 }
3329
3330 addVariableToClass(root, // entry
3331 cd, // class to add member to
3332 mtype, // member type
3333 type, // type value as string
3334 name, // name of the member
3335 args, // arguments as string
3336 FALSE, // from anonymous scope
3337 md, // from anonymous member
3338 root->protection,
3339 relationship
3340 );
3341 }
3342 else if (!name.isEmpty()) // global variable
3343 {
3344 addVariableToFile(root,mtype,scope,type,name,args,FALSE,/*nullptr,*/nullptr);
3345 }
3346
3347}
3348
3349//----------------------------------------------------------------------
3350// Searches the Entry tree for typedef documentation sections.
3351// If found they are stored in their class or in the global list.
3352static void buildTypedefList(const Entry *root)
3353{
3354 //printf("buildVarList(%s)\n",qPrint(rootNav->name()));
3355 if (!root->name.isEmpty() &&
3356 root->section.isVariable() &&
3357 root->type.find("typedef ")!=-1 // its a typedef
3358 )
3359 {
3360 AUTO_TRACE();
3362 QCString scope;
3363 int index = computeQualifiedIndex(rname);
3364 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3365 // grouped members are stored with full scope
3366 {
3367 buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
3368 scope=rname.left(index);
3369 rname=rname.mid(index+2);
3370 }
3371 else
3372 {
3373 scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
3374 }
3377 MemberName *mn = Doxygen::functionNameLinkedMap->find(rname);
3378 bool found=false;
3379 if (mn) // symbol with the same name already found
3380 {
3381 for (auto &imd : *mn)
3382 {
3383 if (!imd->isTypedef())
3384 continue;
3385
3386 QCString rtype = root->type;
3387 rtype.stripPrefix("typedef ");
3388
3389 // merge the typedefs only if they're not both grouped, and both are
3390 // either part of the same class, part of the same namespace, or both
3391 // are global (i.e., neither in a class or a namespace)
3392 bool notBothGrouped = root->groups.empty() || imd->getGroupDef()==nullptr; // see example #100
3393 bool bothSameScope = (!cd && !nd) || (cd && imd->getClassDef() == cd) || (nd && imd->getNamespaceDef() == nd);
3394 //printf("imd->isTypedef()=%d imd->typeString()=%s root->type=%s\n",imd->isTypedef(),
3395 // qPrint(imd->typeString()),qPrint(root->type));
3396 if (notBothGrouped && bothSameScope && imd->typeString()==rtype)
3397 {
3398 MemberDefMutable *md = toMemberDefMutable(imd.get());
3399 if (md)
3400 {
3401 md->setDocumentation(root->doc,root->docFile,root->docLine);
3403 md->setDocsForDefinition(!root->proto);
3404 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3406 md->setRefItems(root->sli);
3407 md->addQualifiers(root->qualifiers);
3408
3409 // merge ingroup specifiers
3410 if (md->getGroupDef()==nullptr && !root->groups.empty())
3411 {
3412 addMemberToGroups(root,md);
3413 }
3414 else if (md->getGroupDef()!=nullptr && root->groups.empty())
3415 {
3416 //printf("existing member is grouped, new member not\n");
3417 }
3418 else if (md->getGroupDef()!=nullptr && !root->groups.empty())
3419 {
3420 //printf("both members are grouped\n");
3421 }
3422 found=true;
3423 break;
3424 }
3425 }
3426 }
3427 }
3428 if (found)
3429 {
3430 AUTO_TRACE_ADD("typedef '{}' already found",rname);
3431 // mark the entry as processed, as we copied everything from it elsewhere
3432 // also, otherwise, due to containing `typedef` it may later get treated
3433 // as a function typedef in filterMemberDocumentation, which is incorrect
3434 root->markAsProcessed();
3435 }
3436 else
3437 {
3438 AUTO_TRACE_ADD("new typedef '{}'",rname);
3439 addVariable(root);
3440 }
3441
3442 }
3443 for (const auto &e : root->children())
3444 if (!e->section.isEnum())
3445 buildTypedefList(e.get());
3446}
3447
3448//----------------------------------------------------------------------
3449// Searches the Entry tree for sequence documentation sections.
3450// If found they are stored in the global list.
3451static void buildSequenceList(const Entry *root)
3452{
3453 if (!root->name.isEmpty() &&
3454 root->section.isVariable() &&
3455 root->type.find("sequence<")!=-1 // it's a sequence
3456 )
3457 {
3458 AUTO_TRACE();
3459 addVariable(root);
3460 }
3461 for (const auto &e : root->children())
3462 if (!e->section.isEnum())
3463 buildSequenceList(e.get());
3464}
3465
3466//----------------------------------------------------------------------
3467// Searches the Entry tree for dictionary documentation sections.
3468// If found they are stored in the global list.
3469static void buildDictionaryList(const Entry *root)
3470{
3471 if (!root->name.isEmpty() &&
3472 root->section.isVariable() &&
3473 root->type.find("dictionary<")!=-1 // it's a dictionary
3474 )
3475 {
3476 AUTO_TRACE();
3477 addVariable(root);
3478 }
3479 for (const auto &e : root->children())
3480 if (!e->section.isEnum())
3481 buildDictionaryList(e.get());
3482}
3483
3484//----------------------------------------------------------------------
3485// Searches the Entry tree for Variable documentation sections.
3486// If found they are stored in their class or in the global list.
3487
3488static void buildVarList(const Entry *root)
3489{
3490 //printf("buildVarList(%s) section=%08x\n",qPrint(rootNav->name()),rootNav->section());
3491 int isFuncPtr=-1;
3492 if (!root->name.isEmpty() &&
3493 (root->type.isEmpty() || g_compoundKeywords.find(root->type.str())==g_compoundKeywords.end()) &&
3494 (
3495 (root->section.isVariable() && // it's a variable
3496 root->type.find("typedef ")==-1 // and not a typedef
3497 ) ||
3498 (root->section.isFunction() && // or maybe a function pointer variable
3499 (isFuncPtr=findFunctionPtr(root->type.str(),root->lang))!=-1
3500 ) ||
3501 (root->section.isFunction() && // class variable initialized by constructor
3503 )
3504 )
3505 ) // documented variable
3506 {
3507 AUTO_TRACE();
3508 addVariable(root,isFuncPtr);
3509 }
3510 for (const auto &e : root->children())
3511 if (!e->section.isEnum())
3512 buildVarList(e.get());
3513}
3514
3515//----------------------------------------------------------------------
3516// Searches the Entry tree for Interface sections (UNO IDL only).
3517// If found they are stored in their service or in the global list.
3518//
3519
3521 const Entry *root,
3522 ClassDefMutable *cd,
3523 QCString const& rname)
3524{
3525 FileDef *fd = root->fileDef();
3526 enum MemberType type = root->section.isExportedInterface() ? MemberType::Interface : MemberType::Service;
3527 QCString fileName = root->fileName;
3528 if (fileName.isEmpty() && root->tagInfo())
3529 {
3530 fileName = root->tagInfo()->tagName;
3531 }
3532 auto md = createMemberDef(
3533 fileName, root->startLine, root->startColumn, root->type, rname,
3534 "", "", root->protection, root->virt, root->isStatic, Relationship::Member,
3535 type, ArgumentList(), root->argList, root->metaData);
3536 auto mmd = toMemberDefMutable(md.get());
3537 mmd->setTagInfo(root->tagInfo());
3538 mmd->setMemberClass(cd);
3539 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3540 mmd->setDocsForDefinition(false);
3541 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3542 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3543 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3544 mmd->setMemberSpecifiers(root->spec);
3545 mmd->setVhdlSpecifiers(root->vhdlSpec);
3546 mmd->setMemberGroupId(root->mGrpId);
3547 mmd->setTypeConstraints(root->typeConstr);
3548 mmd->setLanguage(root->lang);
3549 mmd->setBodyDef(fd);
3550 mmd->setFileDef(fd);
3551 mmd->addSectionsToDefinition(root->anchors);
3552 QCString const def = root->type + " " + rname;
3553 mmd->setDefinition(def);
3555 mmd->addQualifiers(root->qualifiers);
3556
3557 AUTO_TRACE("Interface member: fileName='{}' type='{}' name='{}' mtype='{}' prot={} virt={} state={} proto={} def='{}'",
3558 fileName,root->type,rname,type,root->protection,root->virt,root->isStatic,root->proto,def);
3559
3560 // add member to the class cd
3561 cd->insertMember(md.get());
3562 // also add the member as a "base" (to get nicer diagrams)
3563 // "optional" interface/service get Protected which turns into dashed line
3564 BaseInfo base(rname,
3565 root->spec.isOptional() ? Protection::Protected : Protection::Public, Specifier::Normal);
3566 TemplateNameMap templateNames;
3567 findClassRelation(root,cd,cd,&base,templateNames,DocumentedOnly,true) ||
3568 findClassRelation(root,cd,cd,&base,templateNames,Undocumented,true);
3569 // add file to list of used files
3570 cd->insertUsedFile(fd);
3571
3572 addMemberToGroups(root,md.get());
3574 root->markAsProcessed();
3575 mmd->setRefItems(root->sli);
3576
3577 // add member to the global list of all members
3578 MemberName *mn = Doxygen::memberNameLinkedMap->add(rname);
3579 mn->push_back(std::move(md));
3580}
3581
3582static void buildInterfaceAndServiceList(const Entry *root)
3583{
3584 if (root->section.isExportedInterface() || root->section.isIncludedService())
3585 {
3586 AUTO_TRACE("Exported interface/included service: type='{}' scope='{}' name='{}' args='{}'"
3587 " relates='{}' relatesType='{}' file='{}' line={} bodyLine={} #tArgLists={}"
3588 " mGrpId={} spec={} proto={} docFile='{}'",
3589 root->type, root->parent()->name, root->name, root->args,
3590 root->relates, root->relatesType, root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(),
3591 root->mGrpId, root->spec, root->proto, root->docFile);
3592
3594
3595 if (!rname.isEmpty())
3596 {
3597 QCString scope = root->parent()->name;
3598 ClassDefMutable *cd = getClassMutable(scope);
3599 assert(cd);
3600 if (cd && ((ClassDef::Interface == cd->compoundType()) ||
3601 (ClassDef::Service == cd->compoundType()) ||
3603 {
3605 }
3606 else
3607 {
3608 assert(false); // was checked by scanner.l
3609 }
3610 }
3611 else if (rname.isEmpty())
3612 {
3613 warn(root->fileName,root->startLine,
3614 "Illegal member name found.");
3615 }
3616 }
3617 // can only have these in IDL anyway
3618 switch (root->lang)
3619 {
3620 case SrcLangExt::Unknown: // fall through (root node always is Unknown)
3621 case SrcLangExt::IDL:
3622 for (const auto &e : root->children()) buildInterfaceAndServiceList(e.get());
3623 break;
3624 default:
3625 return; // nothing to do here
3626 }
3627}
3628
3629
3630//----------------------------------------------------------------------
3631// Searches the Entry tree for Function sections.
3632// If found they are stored in their class or in the global list.
3633
3634static void addMethodToClass(const Entry *root,ClassDefMutable *cd,
3635 const QCString &rtype,const QCString &rname,const QCString &rargs,
3636 bool isFriend,
3637 Protection protection,bool stat,Specifier virt,TypeSpecifier spec,
3638 const QCString &relates
3639 )
3640{
3641 FileDef *fd=root->fileDef();
3642
3643 QCString type = rtype;
3644 QCString args = rargs;
3645
3647 name.stripPrefix("::");
3648
3650 if (isFriend) mtype=MemberType::Friend;
3651 else if (root->mtype==MethodTypes::Signal) mtype=MemberType::Signal;
3652 else if (root->mtype==MethodTypes::Slot) mtype=MemberType::Slot;
3653 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType::DCOP;
3654
3655 // strip redundant template specifier for constructors
3656 int i = -1;
3657 int j = -1;
3658 if ((fd==nullptr || fd->getLanguage()==SrcLangExt::Cpp) &&
3659 !name.startsWith("operator ") && // not operator
3660 (i=name.find('<'))!=-1 && // containing <
3661 (j=name.find('>'))!=-1 && // or >
3662 (j!=i+2 || name.at(i+1)!='=') // but not the C++20 spaceship operator <=>
3663 )
3664 {
3665 name=name.left(i);
3666 }
3667
3668 QCString fileName = root->fileName;
3669 if (fileName.isEmpty() && root->tagInfo())
3670 {
3671 fileName = root->tagInfo()->tagName;
3672 }
3673
3674 //printf("root->name='%s; args='%s' root->argList='%s'\n",
3675 // qPrint(root->name),qPrint(args),qPrint(argListToString(root->argList))
3676 // );
3677
3678 // adding class member
3679 Relationship relationship = relates.isEmpty() ? Relationship::Member :
3680 root->relatesType==RelatesType::MemberOf ? Relationship::Foreign :
3681 Relationship::Related ;
3682 auto md = createMemberDef(
3683 fileName,root->startLine,root->startColumn,
3684 type,name,args,root->exception,
3685 protection,virt,
3686 stat && root->relatesType!=RelatesType::MemberOf,
3687 relationship,
3688 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3689 root->argList, root->metaData);
3690 auto mmd = toMemberDefMutable(md.get());
3691 mmd->setTagInfo(root->tagInfo());
3692 mmd->setMemberClass(cd);
3693 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3694 mmd->setDocsForDefinition(!root->proto);
3695 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3696 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3697 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3698 mmd->setMemberSpecifiers(spec);
3699 mmd->setVhdlSpecifiers(root->vhdlSpec);
3700 mmd->setMemberGroupId(root->mGrpId);
3701 mmd->setTypeConstraints(root->typeConstr);
3702 mmd->setLanguage(root->lang);
3703 mmd->setRequiresClause(root->req);
3704 mmd->setId(root->id);
3705 mmd->setBodyDef(fd);
3706 mmd->setFileDef(fd);
3707 mmd->addSectionsToDefinition(root->anchors);
3708 QCString def;
3710 SrcLangExt lang = cd->getLanguage();
3711 QCString scopeSeparator=getLanguageSpecificSeparator(lang);
3712 if (scopeSeparator!="::")
3713 {
3714 qualScope = substitute(qualScope,"::",scopeSeparator);
3715 }
3716 if (lang==SrcLangExt::PHP)
3717 {
3718 // for PHP we use Class::method and Namespace\method
3719 scopeSeparator="::";
3720 }
3721// QCString optArgs = root->argList.empty() ? args : QCString();
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.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 instanceClass->setArtificial(TRUE);
4740 instanceClass->setLanguage(root->lang);
4741
4742 if (freshInstance)
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 return tcd;
5679}
5680
5681//----------------------------------------------------------------------------
5682// Returns TRUE, if the entry belongs to the group of the member definition,
5683// otherwise FALSE.
5684
5685static bool isEntryInGroupOfMember(const Entry *root,const MemberDef *md,bool allowNoGroup=false)
5686{
5687 const GroupDef *gd = md->getGroupDef();
5688 if (!gd)
5689 {
5690 return allowNoGroup;
5691 }
5692
5693 for (const auto &g : root->groups)
5694 {
5695 if (g.groupname == gd->name())
5696 {
5697 return true; // matching group
5698 }
5699 }
5700
5701 return false;
5702}
5703
5704//----------------------------------------------------------------------
5705// Adds the documentation contained in 'root' to a global function
5706// with name 'name' and argument list 'args' (for overloading) and
5707// function declaration 'decl' to the corresponding member definition.
5708
5709static bool findGlobalMember(const Entry *root,
5710 const QCString &namespaceName,
5711 const QCString &type,
5712 const QCString &name,
5713 const QCString &tempArg,
5714 const QCString &,
5715 const QCString &decl,
5716 TypeSpecifier /* spec */)
5717{
5718 AUTO_TRACE("namespace='{}' type='{}' name='{}' tempArg='{}' decl='{}'",namespaceName,type,name,tempArg,decl);
5719 QCString n=name;
5720 if (n.isEmpty()) return FALSE;
5721 if (n.find("::")!=-1) return FALSE; // skip undefined class members
5722 MemberName *mn=Doxygen::functionNameLinkedMap->find(n+tempArg); // look in function dictionary
5723 if (mn==nullptr)
5724 {
5725 mn=Doxygen::functionNameLinkedMap->find(n); // try without template arguments
5726 }
5727 if (mn) // function name defined
5728 {
5729 AUTO_TRACE_ADD("Found symbol name");
5730 //int count=0;
5731 bool found=FALSE;
5732 for (const auto &md : *mn)
5733 {
5734 // If the entry has groups, then restrict the search to members which are
5735 // in one of the groups of the entry. If md is not associated with a group yet,
5736 // allow this documentation entry to add the group info.
5737 if (!root->groups.empty() && !isEntryInGroupOfMember(root, md.get(), true))
5738 {
5739 continue;
5740 }
5741
5742 const NamespaceDef *nd=nullptr;
5743 if (md->isAlias() && md->getOuterScope() &&
5744 md->getOuterScope()->definitionType()==Definition::TypeNamespace)
5745 {
5746 nd = toNamespaceDef(md->getOuterScope());
5747 }
5748 else
5749 {
5750 nd = md->getNamespaceDef();
5751 }
5752
5753 // special case for strong enums
5754 int enumNamePos=0;
5755 if (nd && md->isEnumValue() && (enumNamePos=namespaceName.findRev("::"))!=-1)
5756 { // md part of a strong enum in a namespace?
5757 QCString enumName = namespaceName.mid(enumNamePos+2);
5758 if (namespaceName.left(enumNamePos)==nd->name())
5759 {
5760 MemberName *enumMn=Doxygen::functionNameLinkedMap->find(enumName);
5761 if (enumMn)
5762 {
5763 for (const auto &emd : *enumMn)
5764 {
5765 found = emd->isStrong() && md->getEnumScope()==emd.get();
5766 if (found)
5767 {
5768 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,nullptr,FALSE,root->spec);
5769 break;
5770 }
5771 }
5772 }
5773 }
5774 if (found)
5775 {
5776 break;
5777 }
5778 }
5779 else if (nd==nullptr && md->isEnumValue()) // md part of global strong enum?
5780 {
5781 MemberName *enumMn=Doxygen::functionNameLinkedMap->find(namespaceName);
5782 if (enumMn)
5783 {
5784 for (const auto &emd : *enumMn)
5785 {
5786 found = emd->isStrong() && md->getEnumScope()==emd.get();
5787 if (found)
5788 {
5789 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,nullptr,FALSE,root->spec);
5790 break;
5791 }
5792 }
5793 }
5794 }
5795
5796 const FileDef *fd=root->fileDef();
5797 //printf("File %s\n",fd ? qPrint(fd->name()) : "<none>");
5799 if (fd)
5800 {
5801 nl = fd->getUsedNamespaces();
5802 }
5803 //printf("NamespaceList %p\n",nl);
5804
5805 // search in the list of namespaces that are imported via a
5806 // using declaration
5807 bool viaUsingDirective = nd && nl.find(nd->qualifiedName())!=nullptr;
5808
5809 if ((namespaceName.isEmpty() && nd==nullptr) || // not in a namespace
5810 (nd && nd->name()==namespaceName) || // or in the same namespace
5811 viaUsingDirective // member in 'using' namespace
5812 )
5813 {
5814 AUTO_TRACE_ADD("Try to add member '{}' to scope '{}'",md->name(),namespaceName);
5815
5816 NamespaceDef *rnd = nullptr;
5817 if (!namespaceName.isEmpty()) rnd = Doxygen::namespaceLinkedMap->find(namespaceName);
5818
5819 const ArgumentList &mdAl = md.get()->argumentList();
5820 bool matching=
5821 (mdAl.empty() && root->argList.empty()) ||
5822 md->isVariable() || md->isTypedef() || /* in case of function pointers */
5823 matchArguments2(md->getOuterScope(),md.get()->getFileDef(),&mdAl,
5824 rnd ? rnd : Doxygen::globalScope,fd,&root->argList,
5825 FALSE,root->lang);
5826
5827 // for template members we need to check if the number of
5828 // template arguments is the same, otherwise we are dealing with
5829 // different functions.
5830 if (matching && !root->tArgLists.empty())
5831 {
5832 const ArgumentList &mdTempl = md->templateArguments();
5833 if (root->tArgLists.back().size()!=mdTempl.size())
5834 {
5835 matching=FALSE;
5836 }
5837 }
5838
5839 //printf("%s<->%s\n",
5840 // qPrint(argListToString(md->argumentList())),
5841 // qPrint(argListToString(root->argList)));
5842
5843 // For static members we also check if the comment block was found in
5844 // the same file. This is needed because static members with the same
5845 // name can be in different files. Thus it would be wrong to just
5846 // put the comment block at the first syntactically matching member. If
5847 // the comment block belongs to a group of the static member, then add
5848 // the documentation even if it is in a different file.
5849 if (matching && md->isStatic() &&
5850 md->getDefFileName()!=root->fileName &&
5851 mn->size()>1 &&
5852 !isEntryInGroupOfMember(root,md.get()))
5853 {
5854 matching = FALSE;
5855 }
5856
5857 // for template member we also need to check the return type and requires
5858 if (!md->templateArguments().empty() && !root->tArgLists.empty())
5859 {
5860 //printf("Comparing return types '%s'<->'%s'\n",
5861 // md->typeString(),type);
5862 if (md->templateArguments().size()!=root->tArgLists.back().size() ||
5863 md->typeString()!=type ||
5864 md->requiresClause()!=root->req)
5865 {
5866 //printf(" ---> no matching\n");
5867 matching = FALSE;
5868 }
5869 }
5870
5871 if (matching) // add docs to the member
5872 {
5873 AUTO_TRACE_ADD("Match found");
5874 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,&root->argList,FALSE,root->spec);
5875 found=TRUE;
5876 break;
5877 }
5878 }
5879 }
5880 if (!found && root->relatesType!=RelatesType::Duplicate && root->section.isFunction()) // no match
5881 {
5882 QCString fullFuncDecl=decl;
5883 if (!root->argList.empty()) fullFuncDecl+=argListToString(root->argList,TRUE);
5884 QCString warnMsg = "no matching file member found for \n"+fullFuncDecl;
5885 if (mn->size()>0)
5886 {
5887 warnMsg+="\nPossible candidates:";
5888 for (const auto &md : *mn)
5889 {
5890 warnMsg+="\n '";
5891 warnMsg+=replaceAnonymousScopes(md->declaration());
5892 warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
5893 }
5894 }
5895 warn(root->fileName,root->startLine, "{}", qPrint(warnMsg));
5896 }
5897 }
5898 else // got docs for an undefined member!
5899 {
5900 if (root->type!="friend class" &&
5901 root->type!="friend struct" &&
5902 root->type!="friend union" &&
5903 root->type!="friend" &&
5904 (!Config_getBool(TYPEDEF_HIDES_STRUCT) ||
5905 root->type.find("typedef ")==-1)
5906 )
5907 {
5908 warn(root->fileName,root->startLine,
5909 "documented symbol '{}' was not declared or defined.",qPrint(decl)
5910 );
5911 }
5912 }
5913 return TRUE;
5914}
5915
5917 const ArgumentLists &srcTempArgLists,
5918 const ArgumentLists &dstTempArgLists
5919 )
5920{
5921 auto srcIt = srcTempArgLists.begin();
5922 auto dstIt = dstTempArgLists.begin();
5923 while (srcIt!=srcTempArgLists.end() && dstIt!=dstTempArgLists.end())
5924 {
5925 if ((*srcIt).size()!=(*dstIt).size()) return TRUE;
5926 ++srcIt;
5927 ++dstIt;
5928 }
5929 return FALSE;
5930}
5931
5932static bool scopeIsTemplate(const Definition *d)
5933{
5934 bool result=FALSE;
5935 //printf("> scopeIsTemplate(%s)\n",qPrint(d?d->name():"null"));
5937 {
5938 auto cd = toClassDef(d);
5939 result = cd->templateArguments().hasParameters() || cd->templateMaster()!=nullptr ||
5941 }
5942 //printf("< scopeIsTemplate=%d\n",result);
5943 return result;
5944}
5945
5947 const ArgumentLists &srcTempArgLists,
5948 const ArgumentLists &dstTempArgLists,
5949 const std::string &src
5950 )
5951{
5952 std::string dst;
5953 static const reg::Ex re(R"(\a\w*)");
5954 reg::Iterator it(src,re);
5956 //printf("type=%s\n",qPrint(sa->type));
5957 size_t p=0;
5958 for (; it!=end ; ++it) // for each word in srcType
5959 {
5960 const auto &match = *it;
5961 size_t i = match.position();
5962 size_t l = match.length();
5963 bool found=FALSE;
5964 dst+=src.substr(p,i-p);
5965 std::string name=match.str();
5966
5967 auto srcIt = srcTempArgLists.begin();
5968 auto dstIt = dstTempArgLists.begin();
5969 while (srcIt!=srcTempArgLists.end() && !found)
5970 {
5971 const ArgumentList *tdAli = nullptr;
5972 std::vector<Argument>::const_iterator tdaIt;
5973 if (dstIt!=dstTempArgLists.end())
5974 {
5975 tdAli = &(*dstIt);
5976 tdaIt = tdAli->begin();
5977 ++dstIt;
5978 }
5979
5980 const ArgumentList &tsaLi = *srcIt;
5981 for (auto tsaIt = tsaLi.begin(); tsaIt!=tsaLi.end() && !found; ++tsaIt)
5982 {
5983 Argument tsa = *tsaIt;
5984 const Argument *tda = nullptr;
5985 if (tdAli && tdaIt!=tdAli->end())
5986 {
5987 tda = &(*tdaIt);
5988 ++tdaIt;
5989 }
5990 //if (tda) printf("tsa=%s|%s tda=%s|%s\n",
5991 // qPrint(tsa.type),qPrint(tsa.name),
5992 // qPrint(tda->type),qPrint(tda->name));
5993 if (name==tsa.name.str())
5994 {
5995 if (tda && tda->name.isEmpty())
5996 {
5997 QCString tdaName = tda->name;
5998 QCString tdaType = tda->type;
5999 int vc=0;
6000 if (tdaType.startsWith("class ")) vc=6;
6001 else if (tdaType.startsWith("typename ")) vc=9;
6002 if (vc>0) // convert type=="class T" to type=="class" name=="T"
6003 {
6004 tdaName = tdaType.mid(vc);
6005 }
6006 if (!tdaName.isEmpty())
6007 {
6008 name=tdaName.str(); // substitute
6009 found=TRUE;
6010 }
6011 }
6012 }
6013 }
6014
6015 //printf(" srcList='%s' dstList='%s faList='%s'\n",
6016 // qPrint(argListToString(srclali.current())),
6017 // qPrint(argListToString(dstlali.current())),
6018 // funcTempArgList ? qPrint(argListToString(funcTempArgList)) : "<none>");
6019 ++srcIt;
6020 }
6021 dst+=name;
6022 p=i+l;
6023 }
6024 dst+=src.substr(p);
6025 //printf(" substituteTemplatesInString(%s)=%s\n",
6026 // qPrint(src),qPrint(dst));
6027 return dst;
6028}
6029
6031 const ArgumentLists &srcTempArgLists,
6032 const ArgumentLists &dstTempArgLists,
6033 const ArgumentList &src,
6034 ArgumentList &dst
6035 )
6036{
6037 auto dstIt = dst.begin();
6038 for (const Argument &sa : src)
6039 {
6040 QCString dstType = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.type.str());
6041 QCString dstArray = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.array.str());
6042 if (dstIt == dst.end())
6043 {
6044 Argument da = sa;
6045 da.type = dstType;
6046 da.array = dstArray;
6047 dst.push_back(da);
6048 dstIt = dst.end();
6049 }
6050 else
6051 {
6052 Argument da = *dstIt;
6053 da.type = dstType;
6054 da.array = dstArray;
6055 ++dstIt;
6056 }
6057 }
6062 srcTempArgLists,dstTempArgLists,
6063 src.trailingReturnType().str()));
6064 dst.setIsDeleted(src.isDeleted());
6065 dst.setRefQualifier(src.refQualifier());
6066 dst.setNoParameters(src.noParameters());
6067 //printf("substituteTemplatesInArgList: replacing %s with %s\n",
6068 // qPrint(argListToString(src)),qPrint(argListToString(dst))
6069 // );
6070}
6071
6072//-------------------------------------------------------------------------------------------
6073
6074static void addLocalObjCMethod(const Entry *root,
6075 const QCString &scopeName,
6076 const QCString &funcType,const QCString &funcName,const QCString &funcArgs,
6077 const QCString &exceptions,const QCString &funcDecl,
6078 TypeSpecifier spec)
6079{
6080 AUTO_TRACE();
6081 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
6082 ClassDefMutable *cd=nullptr;
6083 if (Config_getBool(EXTRACT_LOCAL_METHODS) && (cd=getClassMutable(scopeName)))
6084 {
6085 AUTO_TRACE_ADD("Local objective C method '{}' scopeName='{}'",root->name,scopeName);
6086 auto md = createMemberDef(
6087 root->fileName,root->startLine,root->startColumn,
6088 funcType,funcName,funcArgs,exceptions,
6089 root->protection,root->virt,root->isStatic,Relationship::Member,
6091 auto mmd = toMemberDefMutable(md.get());
6092 mmd->setTagInfo(root->tagInfo());
6093 mmd->setLanguage(root->lang);
6094 mmd->setId(root->id);
6095 mmd->makeImplementationDetail();
6096 mmd->setMemberClass(cd);
6097 mmd->setDefinition(funcDecl);
6099 mmd->addQualifiers(root->qualifiers);
6100 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6101 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6102 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6103 mmd->setDocsForDefinition(!root->proto);
6104 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6105 mmd->addSectionsToDefinition(root->anchors);
6106 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6107 FileDef *fd=root->fileDef();
6108 mmd->setBodyDef(fd);
6109 mmd->setMemberSpecifiers(spec);
6110 mmd->setVhdlSpecifiers(root->vhdlSpec);
6111 mmd->setMemberGroupId(root->mGrpId);
6112 cd->insertMember(md.get());
6113 cd->insertUsedFile(fd);
6114 mmd->setRefItems(root->sli);
6115
6117 mn->push_back(std::move(md));
6118 }
6119 else
6120 {
6121 // local objective C method found for class without interface
6122 }
6123}
6124
6125//-------------------------------------------------------------------------------------------
6126
6127static void addMemberFunction(const Entry *root,
6128 MemberName *mn,
6129 const QCString &scopeName,
6130 const QCString &namespaceName,
6131 const QCString &className,
6132 const QCString &funcTyp,
6133 const QCString &funcName,
6134 const QCString &funcArgs,
6135 const QCString &funcTempList,
6136 const QCString &exceptions,
6137 const QCString &type,
6138 const QCString &args,
6139 bool isFriend,
6140 TypeSpecifier spec,
6141 const QCString &relates,
6142 const QCString &funcDecl,
6143 bool overloaded,
6144 bool isFunc)
6145{
6146 AUTO_TRACE();
6147 QCString funcType = funcTyp;
6148 int count=0;
6149 int noMatchCount=0;
6150 bool memFound=FALSE;
6151 for (const auto &imd : *mn)
6152 {
6153 MemberDefMutable *md = toMemberDefMutable(imd.get());
6154 if (md==nullptr) continue;
6156 if (cd==nullptr) continue;
6157 //AUTO_TRACE_ADD("member definition found, scope needed='{}' scope='{}' args='{}' fileName='{}'",
6158 // scopeName, cd->name(), md->argsString(), root->fileName);
6159 FileDef *fd=root->fileDef();
6160 NamespaceDef *nd=nullptr;
6161 if (!namespaceName.isEmpty()) nd=getResolvedNamespace(namespaceName);
6162
6163 //printf("scopeName %s->%s\n",qPrint(scopeName),
6164 // qPrint(stripTemplateSpecifiersFromScope(scopeName,FALSE)));
6165
6166 // if the member we are searching for is an enum value that is part of
6167 // a "strong" enum, we need to look into the fields of the enum for a match
6168 int enumNamePos=0;
6169 if (md->isEnumValue() && (enumNamePos=className.findRev("::"))!=-1)
6170 {
6171 QCString enumName = className.mid(enumNamePos+2);
6172 QCString fullScope = className.left(enumNamePos);
6173 if (!namespaceName.isEmpty()) fullScope.prepend(namespaceName+"::");
6174 if (fullScope==cd->name())
6175 {
6176 MemberName *enumMn=Doxygen::memberNameLinkedMap->find(enumName);
6177 //printf("enumMn(%s)=%p\n",qPrint(className),(void*)enumMn);
6178 if (enumMn)
6179 {
6180 for (const auto &emd : *enumMn)
6181 {
6182 memFound = emd->isStrong() && md->getEnumScope()==emd.get();
6183 if (memFound)
6184 {
6185 addMemberDocs(root,md,funcDecl,nullptr,overloaded,spec);
6186 count++;
6187 }
6188 if (memFound) break;
6189 }
6190 }
6191 }
6192 }
6193 if (memFound) break;
6194
6195 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
6196 if (tcd==nullptr && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
6197 {
6198 // don't be fooled by anonymous scopes
6199 tcd=cd;
6200 }
6201 //printf("Looking for %s inside nd=%s result=%p (%s) cd=%p\n",
6202 // qPrint(scopeName),nd?qPrint(nd->name()):"<none>",tcd,tcd?qPrint(tcd->name()):"",cd);
6203
6204 if (cd && tcd==cd) // member's classes match
6205 {
6206 AUTO_TRACE_ADD("class definition '{}' found",cd->name());
6207
6208 // get the template parameter lists found at the member declaration
6209 ArgumentLists declTemplArgs = cd->getTemplateParameterLists();
6210 const ArgumentList &templAl = md->templateArguments();
6211 if (!templAl.empty())
6212 {
6213 declTemplArgs.push_back(templAl);
6214 }
6215
6216 // get the template parameter lists found at the member definition
6217 const ArgumentLists &defTemplArgs = root->tArgLists;
6218 //printf("defTemplArgs=%p\n",defTemplArgs);
6219
6220 // do we replace the decl argument lists with the def argument lists?
6221 bool substDone=FALSE;
6222 ArgumentList argList;
6223
6224 /* substitute the occurrences of class template names in the
6225 * argument list before matching
6226 */
6227 const ArgumentList &mdAl = md->argumentList();
6228 if (declTemplArgs.size()>0 && declTemplArgs.size()==defTemplArgs.size())
6229 {
6230 /* the function definition has template arguments
6231 * and the class definition also has template arguments, so
6232 * we must substitute the template names of the class by that
6233 * of the function definition before matching.
6234 */
6235 substituteTemplatesInArgList(declTemplArgs,defTemplArgs,mdAl,argList);
6236
6237 substDone=TRUE;
6238 }
6239 else /* no template arguments, compare argument lists directly */
6240 {
6241 argList = mdAl;
6242 }
6243
6244 AUTO_TRACE_ADD("matching '{}'<=>'{}' className='{}' namespaceName='{}'",
6245 argListToString(argList,TRUE),argListToString(root->argList,TRUE),className,namespaceName);
6246
6247 bool matching=
6248 md->isVariable() || md->isTypedef() || // needed for function pointers
6250 md->getClassDef(),md->getFileDef(),&argList,
6251 cd,fd,&root->argList,
6252 TRUE,root->lang);
6253
6254 if (md->getLanguage()==SrcLangExt::ObjC && md->isVariable() && root->section.isFunction())
6255 {
6256 matching = FALSE; // don't match methods and attributes with the same name
6257 }
6258
6259 // for template member we also need to check the return type
6260 if (!md->templateArguments().empty() && !root->tArgLists.empty())
6261 {
6262 QCString memType = md->typeString();
6263 memType.stripPrefix("static "); // see bug700696
6265 className+"::",""); // see bug700693 & bug732594
6267 className+"::",""); // see bug758900
6268 AUTO_TRACE_ADD("Comparing return types '{}'<->'{}' #args {}<->{}",
6269 md->typeString(),funcType,md->templateArguments().size(),root->tArgLists.back().size());
6270 if (md->templateArguments().size()!=root->tArgLists.back().size() || memType!=funcType)
6271 {
6272 //printf(" ---> no matching\n");
6273 matching = FALSE;
6274 }
6275 }
6276 else if (defTemplArgs.size()>declTemplArgs.size())
6277 {
6278 AUTO_TRACE_ADD("Different number of template arguments {} vs {}",defTemplArgs.size(),declTemplArgs.size());
6279 // avoid matching a non-template function in a template class against a
6280 // template function with the same name and parameters, see issue #10184
6281 substDone = false;
6282 matching = false;
6283 }
6284 bool rootIsUserDoc = root->section.isMemberDoc();
6285 bool classIsTemplate = scopeIsTemplate(md->getClassDef());
6286 bool mdIsTemplate = md->templateArguments().hasParameters();
6287 bool classOrMdIsTemplate = mdIsTemplate || classIsTemplate;
6288 bool rootIsTemplate = !root->tArgLists.empty();
6289 //printf("classIsTemplate=%d mdIsTemplate=%d rootIsTemplate=%d\n",classIsTemplate,mdIsTemplate,rootIsTemplate);
6290 if (!rootIsUserDoc && // don't check out-of-line @fn references, see bug722457
6291 (mdIsTemplate || rootIsTemplate) && // either md or root is a template
6292 ((classOrMdIsTemplate && !rootIsTemplate) || (!classOrMdIsTemplate && rootIsTemplate))
6293 )
6294 {
6295 // Method with template return type does not match method without return type
6296 // even if the parameters are the same. See also bug709052
6297 AUTO_TRACE_ADD("Comparing return types: template v.s. non-template");
6298 matching = FALSE;
6299 }
6300
6301 AUTO_TRACE_ADD("Match results of matchArguments2='{}' substDone='{}'",matching,substDone);
6302
6303 if (substDone) // found a new argument list
6304 {
6305 if (matching) // replace member's argument list
6306 {
6308 md->moveArgumentList(std::make_unique<ArgumentList>(argList));
6309 }
6310 else // no match
6311 {
6312 if (!funcTempList.isEmpty() &&
6313 isSpecialization(declTemplArgs,defTemplArgs))
6314 {
6315 // check if we are dealing with a partial template
6316 // specialization. In this case we add it to the class
6317 // even though the member arguments do not match.
6318
6319 addMethodToClass(root,cd,type,md->name(),args,isFriend,
6320 md->protection(),md->isStatic(),md->virtualness(),spec,relates);
6321 return;
6322 }
6323 }
6324 }
6325 if (matching)
6326 {
6327 addMemberDocs(root,md,funcDecl,nullptr,overloaded,spec);
6328 count++;
6329 memFound=TRUE;
6330 }
6331 }
6332 else if (cd && cd!=tcd) // we did find a class with the same name as cd
6333 // but in a different namespace
6334 {
6335 noMatchCount++;
6336 }
6337
6338 if (memFound) break;
6339 }
6340 if (count==0 && root->parent() && root->parent()->section.isObjcImpl())
6341 {
6342 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
6343 return;
6344 }
6345 if (count==0 && !(isFriend && funcType=="class"))
6346 {
6347 int candidates=0;
6348 const ClassDef *ecd = nullptr, *ucd = nullptr;
6349 MemberDef *emd = nullptr, *umd = nullptr;
6350 //printf("Assume template class\n");
6351 for (const auto &md : *mn)
6352 {
6353 MemberDef *cmd=md.get();
6355 ClassDefMutable *ccd=cdmdm ? cdmdm->getClassDefMutable() : nullptr;
6356 //printf("ccd->name()==%s className=%s\n",qPrint(ccd->name()),qPrint(className));
6357 if (ccd!=nullptr && rightScopeMatch(ccd->name(),className))
6358 {
6359 const ArgumentList &templAl = md->templateArguments();
6360 if (!root->tArgLists.empty() && !templAl.empty() &&
6361 root->tArgLists.back().size()<=templAl.size())
6362 {
6363 AUTO_TRACE_ADD("add template specialization");
6364 addMethodToClass(root,ccd,type,md->name(),args,isFriend,
6365 root->protection,root->isStatic,root->virt,spec,relates);
6366 return;
6367 }
6368 if (argListToString(md->argumentList(),FALSE,FALSE) ==
6370 { // exact argument list match -> remember
6371 ucd = ecd = ccd;
6372 umd = emd = cmd;
6373 AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': exact match",
6374 className,ccd->name(),md->argsString());
6375 }
6376 else // arguments do not match, but member name and scope do -> remember
6377 {
6378 ucd = ccd;
6379 umd = cmd;
6380 AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': no match",
6381 className,ccd->name(),md->argsString());
6382 }
6383 candidates++;
6384 }
6385 }
6386 bool strictProtoMatching = Config_getBool(STRICT_PROTO_MATCHING);
6387 if (!strictProtoMatching)
6388 {
6389 if (candidates==1 && ucd && umd)
6390 {
6391 // we didn't find an actual match on argument lists, but there is only 1 member with this
6392 // name in the same scope, so that has to be the one.
6393 addMemberDocs(root,toMemberDefMutable(umd),funcDecl,nullptr,overloaded,spec);
6394 return;
6395 }
6396 else if (candidates>1 && ecd && emd)
6397 {
6398 // we didn't find a unique match using type resolution,
6399 // but one of the matches has the exact same signature so
6400 // we take that one.
6401 addMemberDocs(root,toMemberDefMutable(emd),funcDecl,nullptr,overloaded,spec);
6402 return;
6403 }
6404 }
6405
6406 QCString warnMsg = "no ";
6407 if (noMatchCount>1) warnMsg+="uniquely ";
6408 warnMsg+="matching class member found for \n";
6409
6410 for (const ArgumentList &al : root->tArgLists)
6411 {
6412 warnMsg+=" template ";
6413 warnMsg+=tempArgListToString(al,root->lang);
6414 warnMsg+='\n';
6415 }
6416
6417 QCString fullFuncDecl=funcDecl;
6418 if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
6419
6420 warnMsg+=" ";
6421 warnMsg+=fullFuncDecl;
6422
6423 if (candidates>0 || noMatchCount>=1)
6424 {
6425 warnMsg+="\nPossible candidates:";
6426
6427 NamespaceDef *nd=nullptr;
6428 if (!namespaceName.isEmpty()) nd=getResolvedNamespace(namespaceName);
6429 FileDef *fd=root->fileDef();
6430
6431 for (const auto &md : *mn)
6432 {
6433 const ClassDef *cd=md->getClassDef();
6434 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
6435 if (tcd==nullptr && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
6436 {
6437 // don't be fooled by anonymous scopes
6438 tcd=cd;
6439 }
6440 if (cd!=nullptr && (rightScopeMatch(cd->name(),className) || (cd!=tcd)))
6441 {
6442 warnMsg+='\n';
6443 const ArgumentList &templAl = md->templateArguments();
6444 warnMsg+=" '";
6445 if (templAl.hasParameters())
6446 {
6447 warnMsg+="template ";
6448 warnMsg+=tempArgListToString(templAl,root->lang);
6449 warnMsg+='\n';
6450 warnMsg+=" ";
6451 }
6452 if (!md->typeString().isEmpty())
6453 {
6454 warnMsg+=md->typeString();
6455 warnMsg+=' ';
6456 }
6458 if (!qScope.isEmpty())
6459 warnMsg+=qScope+"::"+md->name();
6460 warnMsg+=md->argsString();
6461 warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
6462 }
6463 }
6464 }
6465 warn(root->fileName,root->startLine,"{}",warnMsg);
6466 }
6467}
6468
6469//-------------------------------------------------------------------------------------------
6470
6471static void addMemberSpecialization(const Entry *root,
6472 MemberName *mn,
6473 ClassDefMutable *cd,
6474 const QCString &funcType,
6475 const QCString &funcName,
6476 const QCString &funcArgs,
6477 const QCString &funcDecl,
6478 const QCString &exceptions,
6479 TypeSpecifier spec
6480 )
6481{
6482 AUTO_TRACE("funcType={} funcName={} funcArgs={} funcDecl={} spec={}",funcType,funcName,funcArgs,funcDecl,spec);
6483 MemberDef *declMd=nullptr;
6484 for (const auto &md : *mn)
6485 {
6486 if (md->getClassDef()==cd)
6487 {
6488 // TODO: we should probably also check for matching arguments
6489 declMd = md.get();
6490 break;
6491 }
6492 }
6494 ArgumentList tArgList;
6495 // getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6496 auto md = createMemberDef(
6497 root->fileName,root->startLine,root->startColumn,
6498 funcType,funcName,funcArgs,exceptions,
6499 declMd ? declMd->protection() : root->protection,
6500 root->virt,root->isStatic,Relationship::Member,
6501 mtype,tArgList,root->argList,root->metaData);
6502 auto mmd = toMemberDefMutable(md.get());
6503 //printf("new specialized member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6504 mmd->setTagInfo(root->tagInfo());
6505 mmd->setLanguage(root->lang);
6506 mmd->setId(root->id);
6507 mmd->setMemberClass(cd);
6508 mmd->setTemplateSpecialization(TRUE);
6509 mmd->setTypeConstraints(root->typeConstr);
6510 mmd->setDefinition(funcDecl);
6512 mmd->addQualifiers(root->qualifiers);
6513 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6514 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6515 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6516 mmd->setDocsForDefinition(!root->proto);
6517 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6518 mmd->addSectionsToDefinition(root->anchors);
6519 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6520 FileDef *fd=root->fileDef();
6521 mmd->setBodyDef(fd);
6522 mmd->setMemberSpecifiers(spec);
6523 mmd->setVhdlSpecifiers(root->vhdlSpec);
6524 mmd->setMemberGroupId(root->mGrpId);
6525 cd->insertMember(md.get());
6526 mmd->setRefItems(root->sli);
6527
6528 mn->push_back(std::move(md));
6529}
6530
6531//-------------------------------------------------------------------------------------------
6532
6533static void addOverloaded(const Entry *root,MemberName *mn,
6534 const QCString &funcType,const QCString &funcName,const QCString &funcArgs,
6535 const QCString &funcDecl,const QCString &exceptions,TypeSpecifier spec)
6536{
6537 // for unique overloaded member we allow the class to be
6538 // omitted, this is to be Qt compatible. Using this should
6539 // however be avoided, because it is error prone
6540 bool sameClass=false;
6541 if (mn->size()>0)
6542 {
6543 // check if all members with the same name are also in the same class
6544 sameClass = std::equal(mn->begin()+1,mn->end(),mn->begin(),
6545 [](const auto &md1,const auto &md2)
6546 { return md1->getClassDef()->name()==md2->getClassDef()->name(); });
6547 }
6548 if (sameClass)
6549 {
6550 MemberDefMutable *mdm = toMemberDefMutable(mn->front().get());
6551 ClassDefMutable *cd = mdm ? mdm->getClassDefMutable() : nullptr;
6552 if (cd==nullptr) return;
6553
6555 if (root->mtype==MethodTypes::Signal) mtype=MemberType::Signal;
6556 else if (root->mtype==MethodTypes::Slot) mtype=MemberType::Slot;
6557 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType::DCOP;
6558
6559 // new overloaded member function
6560 std::unique_ptr<ArgumentList> tArgList =
6561 getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6562 //printf("new related member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6563 auto md = createMemberDef(
6564 root->fileName,root->startLine,root->startColumn,
6565 funcType,funcName,funcArgs,exceptions,
6566 root->protection,root->virt,root->isStatic,Relationship::Related,
6567 mtype,tArgList ? *tArgList : ArgumentList(),root->argList,root->metaData);
6568 auto mmd = toMemberDefMutable(md.get());
6569 mmd->setTagInfo(root->tagInfo());
6570 mmd->setLanguage(root->lang);
6571 mmd->setId(root->id);
6572 mmd->setTypeConstraints(root->typeConstr);
6573 mmd->setMemberClass(cd);
6574 mmd->setDefinition(funcDecl);
6576 mmd->addQualifiers(root->qualifiers);
6578 doc+="<p>";
6579 doc+=root->doc;
6580 mmd->setDocumentation(doc,root->docFile,root->docLine);
6581 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6582 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6583 mmd->setDocsForDefinition(!root->proto);
6584 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6585 mmd->addSectionsToDefinition(root->anchors);
6586 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6587 FileDef *fd=root->fileDef();
6588 mmd->setBodyDef(fd);
6589 mmd->setMemberSpecifiers(spec);
6590 mmd->setVhdlSpecifiers(root->vhdlSpec);
6591 mmd->setMemberGroupId(root->mGrpId);
6592 cd->insertMember(md.get());
6593 cd->insertUsedFile(fd);
6594 mmd->setRefItems(root->sli);
6595
6596 mn->push_back(std::move(md));
6597 }
6598}
6599
6600static void insertMemberAlias(Definition *outerScope,const MemberDef *md)
6601{
6602 if (outerScope && outerScope!=Doxygen::globalScope)
6603 {
6604 auto aliasMd = createMemberDefAlias(outerScope,md);
6605 if (outerScope->definitionType()==Definition::TypeClass)
6606 {
6607 ClassDefMutable *cdm = toClassDefMutable(outerScope);
6608 if (cdm)
6609 {
6610 cdm->insertMember(aliasMd.get());
6611 }
6612 }
6613 else if (outerScope->definitionType()==Definition::TypeNamespace)
6614 {
6615 NamespaceDefMutable *ndm = toNamespaceDefMutable(outerScope);
6616 if (ndm)
6617 {
6618 ndm->insertMember(aliasMd.get());
6619 }
6620 }
6621 else if (outerScope->definitionType()==Definition::TypeFile)
6622 {
6623 toFileDef(outerScope)->insertMember(aliasMd.get());
6624 }
6625 if (aliasMd)
6626 {
6627 Doxygen::functionNameLinkedMap->add(md->name())->push_back(std::move(aliasMd));
6628 }
6629 }
6630}
6631
6632//-------------------------------------------------------------------------------------------
6633
6634/*! This function tries to find a member (in a documented class/file/namespace)
6635 * that corresponds to the function/variable declaration given in \a funcDecl.
6636 *
6637 * The boolean \a overloaded is used to specify whether or not a standard
6638 * overload documentation line should be generated.
6639 *
6640 * The boolean \a isFunc is a hint that indicates that this is a function
6641 * instead of a variable or typedef.
6642 */
6643static void findMember(const Entry *root,
6644 const QCString &relates,
6645 const QCString &type,
6646 const QCString &args,
6647 QCString funcDecl,
6648 bool overloaded,
6649 bool isFunc
6650 )
6651{
6652 AUTO_TRACE("root='{}' funcDecl='{}' related='{}' overload={} isFunc={} mGrpId={} #tArgList={} spec={} lang={}",
6653 root->name, funcDecl, relates, overloaded, isFunc, root->mGrpId, root->tArgLists.size(),
6654 root->spec, root->lang);
6655
6656 QCString scopeName;
6657 QCString className;
6658 QCString namespaceName;
6659 QCString funcType;
6660 QCString funcName;
6661 QCString funcArgs;
6662 QCString funcTempList;
6663 QCString exceptions;
6664 QCString funcSpec;
6665 bool isRelated=false;
6666 bool isMemberOf=false;
6667 bool isFriend=false;
6668 bool done=false;
6669 TypeSpecifier spec = root->spec;
6670 while (!done)
6671 {
6672 done=true;
6673 if (funcDecl.stripPrefix("friend ")) // treat friends as related members
6674 {
6675 isFriend=true;
6676 done=false;
6677 }
6678 if (funcDecl.stripPrefix("inline "))
6679 {
6680 spec.setInline(true);
6681 done=false;
6682 }
6683 if (funcDecl.stripPrefix("explicit "))
6684 {
6685 spec.setExplicit(true);
6686 done=false;
6687 }
6688 if (funcDecl.stripPrefix("mutable "))
6689 {
6690 spec.setMutable(true);
6691 done=false;
6692 }
6693 if (funcDecl.stripPrefix("virtual "))
6694 {
6695 done=false;
6696 }
6697 }
6698
6699 // delete any ; from the function declaration
6700 int sep=0;
6701 while ((sep=funcDecl.find(';'))!=-1)
6702 {
6703 funcDecl=(funcDecl.left(sep)+funcDecl.right(funcDecl.length()-sep-1)).stripWhiteSpace();
6704 }
6705
6706 // make sure the first character is a space to simplify searching.
6707 if (!funcDecl.isEmpty() && funcDecl[0]!=' ') funcDecl.prepend(" ");
6708
6709 // remove some superfluous spaces
6710 funcDecl= substitute(
6711 substitute(
6712 substitute(funcDecl,"~ ","~"),
6713 ":: ","::"
6714 ),
6715 " ::","::"
6716 ).stripWhiteSpace();
6717
6718 //printf("funcDecl='%s'\n",qPrint(funcDecl));
6719 if (isFriend && funcDecl.startsWith("class "))
6720 {
6721 //printf("friend class\n");
6722 funcDecl=funcDecl.right(funcDecl.length()-6);
6723 funcName = funcDecl;
6724 }
6725 else if (isFriend && funcDecl.startsWith("struct "))
6726 {
6727 funcDecl=funcDecl.right(funcDecl.length()-7);
6728 funcName = funcDecl;
6729 }
6730 else
6731 {
6732 // extract information from the declarations
6733 parseFuncDecl(funcDecl,root->lang,scopeName,funcType,funcName,
6734 funcArgs,funcTempList,exceptions
6735 );
6736 }
6737
6738 // the class name can also be a namespace name, we decide this later.
6739 // if a related class name is specified and the class name could
6740 // not be derived from the function declaration, then use the
6741 // related field.
6742 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}' funcType='{}' funcName='{}' funcArgs='{}'",
6743 scopeName,className,namespaceName,funcType,funcName,funcArgs);
6744 if (!relates.isEmpty())
6745 { // related member, prefix user specified scope
6746 isRelated=TRUE;
6747 isMemberOf=(root->relatesType == RelatesType::MemberOf);
6748 if (getClass(relates)==nullptr && !scopeName.isEmpty())
6749 {
6750 scopeName= mergeScopes(scopeName,relates);
6751 }
6752 else
6753 {
6754 scopeName = relates;
6755 }
6756 }
6757
6758 if (relates.isEmpty() && root->parent() &&
6759 (root->parent()->section.isScope() || root->parent()->section.isObjcImpl()) &&
6760 !root->parent()->name.isEmpty()) // see if we can combine scopeName
6761 // with the scope in which it was found
6762 {
6763 QCString joinedName = root->parent()->name+"::"+scopeName;
6764 if (!scopeName.isEmpty() &&
6765 (getClass(joinedName) || Doxygen::namespaceLinkedMap->find(joinedName)))
6766 {
6767 scopeName = joinedName;
6768 }
6769 else
6770 {
6771 scopeName = mergeScopes(root->parent()->name,scopeName);
6772 }
6773 }
6774 else // see if we can prefix a namespace or class that is used from the file
6775 {
6776 FileDef *fd=root->fileDef();
6777 if (fd)
6778 {
6779 for (const auto &fnd : fd->getUsedNamespaces())
6780 {
6781 QCString joinedName = fnd->name()+"::"+scopeName;
6782 if (Doxygen::namespaceLinkedMap->find(joinedName))
6783 {
6784 scopeName=joinedName;
6785 break;
6786 }
6787 }
6788 }
6789 }
6791 removeRedundantWhiteSpace(scopeName),false,&funcSpec,QCString(),false);
6792
6793 // funcSpec contains the last template specifiers of the given scope.
6794 // If this method does not have any template arguments or they are
6795 // empty while funcSpec is not empty we assume this is a
6796 // specialization of a method. If not, we clear the funcSpec and treat
6797 // this as a normal method of a template class.
6798 if (!(root->tArgLists.size()>0 &&
6799 root->tArgLists.front().size()==0
6800 )
6801 )
6802 {
6803 funcSpec.clear();
6804 }
6805
6806 //namespaceName=removeAnonymousScopes(namespaceName);
6807 if (!Config_getBool(EXTRACT_ANON_NSPACES) && scopeName.find('@')!=-1) return; // skip stuff in anonymous namespace...
6808
6809 // split scope into a namespace and a class part
6810 extractNamespaceName(scopeName,className,namespaceName,TRUE);
6811 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}'",scopeName,className,namespaceName);
6812
6813 //printf("namespaceName='%s' className='%s'\n",qPrint(namespaceName),qPrint(className));
6814 // merge class and namespace scopes again
6815 scopeName.clear();
6816 if (!namespaceName.isEmpty())
6817 {
6818 if (className.isEmpty())
6819 {
6820 scopeName=namespaceName;
6821 }
6822 else if (!relates.isEmpty() || // relates command with explicit scope
6823 !getClass(className)) // class name only exists in a namespace
6824 {
6825 scopeName=namespaceName+"::"+className;
6826 }
6827 else
6828 {
6829 scopeName=className;
6830 }
6831 }
6832 else if (!className.isEmpty())
6833 {
6834 scopeName=className;
6835 }
6836 //printf("new scope='%s'\n",qPrint(scopeName));
6837
6838 QCString tempScopeName=scopeName;
6839 ClassDefMutable *cd=getClassMutable(scopeName);
6840 if (cd)
6841 {
6842 if (funcSpec.isEmpty())
6843 {
6844 uint32_t argListIndex=0;
6845 tempScopeName=cd->qualifiedNameWithTemplateParameters(&root->tArgLists,&argListIndex);
6846 }
6847 else
6848 {
6849 tempScopeName=scopeName+funcSpec;
6850 }
6851 }
6852 //printf("scopeName=%s cd=%p root->tArgLists=%p result=%s\n",
6853 // qPrint(scopeName),cd,root->tArgLists,qPrint(tempScopeName));
6854
6855 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
6856 // rebuild the function declaration (needed to get the scope right).
6857 if (!scopeName.isEmpty() && !isRelated && !isFriend && !Config_getBool(HIDE_SCOPE_NAMES))
6858 {
6859 if (!funcType.isEmpty())
6860 {
6861 if (isFunc) // a function -> we use argList for the arguments
6862 {
6863 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcTempList;
6864 }
6865 else
6866 {
6867 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcArgs;
6868 }
6869 }
6870 else
6871 {
6872 if (isFunc) // a function => we use argList for the arguments
6873 {
6874 funcDecl=tempScopeName+"::"+funcName+funcTempList;
6875 }
6876 else // variable => add 'argument' list
6877 {
6878 funcDecl=tempScopeName+"::"+funcName+funcArgs;
6879 }
6880 }
6881 }
6882 else // build declaration without scope
6883 {
6884 if (!funcType.isEmpty()) // but with a type
6885 {
6886 if (isFunc) // function => omit argument list
6887 {
6888 funcDecl=funcType+" "+funcName+funcTempList;
6889 }
6890 else // variable => add 'argument' list
6891 {
6892 funcDecl=funcType+" "+funcName+funcArgs;
6893 }
6894 }
6895 else // no type
6896 {
6897 if (isFunc)
6898 {
6899 funcDecl=funcName+funcTempList;
6900 }
6901 else
6902 {
6903 funcDecl=funcName+funcArgs;
6904 }
6905 }
6906 }
6907
6908 if (funcType=="template class" && !funcTempList.isEmpty())
6909 return; // ignore explicit template instantiations
6910
6911 AUTO_TRACE_ADD("Parse results: namespaceName='{}' className=`{}` funcType='{}' funcSpec='{}' "
6912 " funcName='{}' funcArgs='{}' funcTempList='{}' funcDecl='{}' relates='{}'"
6913 " exceptions='{}' isRelated={} isMemberOf={} isFriend={} isFunc={}",
6914 namespaceName, className, funcType, funcSpec,
6915 funcName, funcArgs, funcTempList, funcDecl, relates,
6916 exceptions, isRelated, isMemberOf, isFriend, isFunc);
6917
6918 if (!funcName.isEmpty()) // function name is valid
6919 {
6920 // check if 'className' is actually a scoped enum, in which case we need to
6921 // process it as a global, see issue #6471
6922 bool strongEnum = false;
6923 MemberName *mn=nullptr;
6924 if (!className.isEmpty() && (mn=Doxygen::functionNameLinkedMap->find(className)))
6925 {
6926 for (const auto &imd : *mn)
6927 {
6928 MemberDefMutable *md = toMemberDefMutable(imd.get());
6929 Definition *mdScope = nullptr;
6930 if (md && md->isEnumerate() && md->isStrong() && (mdScope=md->getOuterScope()) &&
6931 // need filter for the correct scope, see issue #9668
6932 ((namespaceName.isEmpty() && mdScope==Doxygen::globalScope) || (mdScope->name()==namespaceName)))
6933 {
6934 AUTO_TRACE_ADD("'{}' is a strong enum! (namespace={} md->getOuterScope()->name()={})",md->name(),namespaceName,md->getOuterScope()->name());
6935 strongEnum = true;
6936 // pass the scope name name as a 'namespace' to the findGlobalMember function
6937 if (!namespaceName.isEmpty())
6938 {
6939 namespaceName+="::"+className;
6940 }
6941 else
6942 {
6943 namespaceName=className;
6944 }
6945 }
6946 }
6947 }
6948
6949 if (funcName.startsWith("operator ")) // strip class scope from cast operator
6950 {
6951 funcName = substitute(funcName,className+"::","");
6952 }
6953 mn = nullptr;
6954 if (!funcTempList.isEmpty()) // try with member specialization
6955 {
6956 mn=Doxygen::memberNameLinkedMap->find(funcName+funcTempList);
6957 }
6958 if (mn==nullptr) // try without specialization
6959 {
6960 mn=Doxygen::memberNameLinkedMap->find(funcName);
6961 }
6962 if (!isRelated && !strongEnum && mn) // function name already found
6963 {
6964 AUTO_TRACE_ADD("member name exists ({} members with this name)",mn->size());
6965 if (!className.isEmpty()) // class name is valid
6966 {
6967 if (funcSpec.isEmpty()) // not a member specialization
6968 {
6969 addMemberFunction(root,mn,scopeName,namespaceName,className,funcType,funcName,
6970 funcArgs,funcTempList,exceptions,
6971 type,args,isFriend,spec,relates,funcDecl,overloaded,isFunc);
6972 }
6973 else if (cd) // member specialization
6974 {
6975 addMemberSpecialization(root,mn,cd,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
6976 }
6977 else
6978 {
6979 //printf("*** Specialized member %s of unknown scope %s%s found!\n",
6980 // qPrint(scopeName),qPrint(funcName),qPrint(funcArgs));
6981 }
6982 }
6983 else if (overloaded) // check if the function belongs to only one class
6984 {
6985 addOverloaded(root,mn,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
6986 }
6987 else // unrelated function with the same name as a member
6988 {
6989 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
6990 {
6991 QCString fullFuncDecl=funcDecl;
6992 if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
6993 warn(root->fileName,root->startLine,
6994 "Cannot determine class for function\n{}",
6995 fullFuncDecl
6996 );
6997 }
6998 }
6999 }
7000 else if (isRelated && !relates.isEmpty())
7001 {
7002 AUTO_TRACE_ADD("related function scopeName='{}' className='{}'",scopeName,className);
7003 if (className.isEmpty()) className=relates;
7004 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
7005 if ((cd=getClassMutable(scopeName)))
7006 {
7007 bool newMember=TRUE; // assume we have a new member
7008 MemberDefMutable *mdDefine=nullptr;
7009 {
7010 mn = Doxygen::functionNameLinkedMap->find(funcName);
7011 if (mn)
7012 {
7013 for (const auto &imd : *mn)
7014 {
7015 MemberDefMutable *md = toMemberDefMutable(imd.get());
7016 if (md && md->isDefine())
7017 {
7018 mdDefine = md;
7019 break;
7020 }
7021 }
7022 }
7023 }
7024
7025 if (mdDefine) // macro definition is already created by the preprocessor and inserted as a file member
7026 {
7027 //printf("moving #define %s into class %s\n",qPrint(mdDefine->name()),qPrint(cd->name()));
7028
7029 // take mdDefine from the Doxygen::functionNameLinkedMap (without deleting the data)
7030 auto mdDefineTaken = Doxygen::functionNameLinkedMap->take(funcName,mdDefine);
7031 // insert it as a class member
7032 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==nullptr)
7033 {
7034 mn=Doxygen::memberNameLinkedMap->add(funcName);
7035 }
7036
7037 if (mdDefine->getFileDef())
7038 {
7039 mdDefine->getFileDef()->removeMember(mdDefine);
7040 }
7041 mdDefine->makeRelated();
7042 mdDefine->setMemberClass(cd);
7043 mdDefine->moveTo(cd);
7044 cd->insertMember(mdDefine);
7045 // also insert the member as an alias in the parent's scope, so it can be referenced also without cd's scope
7046 insertMemberAlias(cd->getOuterScope(),mdDefine);
7047 mn->push_back(std::move(mdDefineTaken));
7048 }
7049 else // normal member, needs to be created and added to the class
7050 {
7051 FileDef *fd=root->fileDef();
7052
7053 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==nullptr)
7054 {
7055 mn=Doxygen::memberNameLinkedMap->add(funcName);
7056 }
7057 else
7058 {
7059 // see if we got another member with matching arguments
7060 MemberDefMutable *rmd_found = nullptr;
7061 for (const auto &irmd : *mn)
7062 {
7063 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
7064 if (rmd)
7065 {
7066 const ArgumentList &rmdAl = rmd->argumentList();
7067
7068 newMember=
7069 className!=rmd->getOuterScope()->name() ||
7070 !matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),&rmdAl,
7071 cd,fd,&root->argList,
7072 TRUE,root->lang);
7073 if (!newMember)
7074 {
7075 rmd_found = rmd;
7076 }
7077 }
7078 }
7079 if (rmd_found) // member already exists as rmd -> add docs
7080 {
7081 AUTO_TRACE_ADD("addMemberDocs for related member {}",root->name);
7082 addMemberDocs(root,rmd_found,funcDecl,nullptr,overloaded,spec);
7083 newMember=false;
7084 }
7085 }
7086
7087 if (newMember) // need to create a new member
7088 {
7090 switch (root->mtype)
7091 {
7092 case MethodTypes::Method: mtype = MemberType::Function; break;
7093 case MethodTypes::Signal: mtype = MemberType::Signal; break;
7094 case MethodTypes::Slot: mtype = MemberType::Slot; break;
7095 case MethodTypes::DCOP: mtype = MemberType::DCOP; break;
7096 case MethodTypes::Property: mtype = MemberType::Property; break;
7097 case MethodTypes::Event: mtype = MemberType::Event; break;
7098 }
7099
7100 //printf("New related name '%s' '%d'\n",qPrint(funcName),
7101 // root->argList ? (int)root->argList->count() : -1);
7102
7103 // first note that we pass:
7104 // (root->tArgLists ? root->tArgLists->last() : nullptr)
7105 // for the template arguments for the new "member."
7106 // this accurately reflects the template arguments of
7107 // the related function, which don't have to do with
7108 // those of the related class.
7109 auto md = createMemberDef(
7110 root->fileName,root->startLine,root->startColumn,
7111 funcType,funcName,funcArgs,exceptions,
7112 root->protection,root->virt,
7113 root->isStatic,
7114 isMemberOf ? Relationship::Foreign : Relationship::Related,
7115 mtype,
7116 (!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList()),
7117 funcArgs.isEmpty() ? ArgumentList() : root->argList,
7118 root->metaData);
7119 auto mmd = toMemberDefMutable(md.get());
7120
7121 // also insert the member as an alias in the parent's scope, so it can be referenced also without cd's scope
7122 insertMemberAlias(cd->getOuterScope(),md.get());
7123
7124 // we still have the problem that
7125 // MemberDef::writeDocumentation() in memberdef.cpp
7126 // writes the template argument list for the class,
7127 // as if this member is a member of the class.
7128 // fortunately, MemberDef::writeDocumentation() has
7129 // a special mechanism that allows us to totally
7130 // override the set of template argument lists that
7131 // are printed. We use that and set it to the
7132 // template argument lists of the related function.
7133 //
7134 mmd->setDefinitionTemplateParameterLists(root->tArgLists);
7135
7136 mmd->setTagInfo(root->tagInfo());
7137
7138 //printf("Related member name='%s' decl='%s' bodyLine='%d'\n",
7139 // qPrint(funcName),qPrint(funcDecl),root->bodyLine);
7140
7141 // try to find the matching line number of the body from the
7142 // global function list
7143 bool found=FALSE;
7144 if (root->bodyLine==-1)
7145 {
7146 MemberName *rmn=Doxygen::functionNameLinkedMap->find(funcName);
7147 if (rmn)
7148 {
7149 const MemberDefMutable *rmd_found=nullptr;
7150 for (const auto &irmd : *rmn)
7151 {
7152 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
7153 if (rmd)
7154 {
7155 const ArgumentList &rmdAl = rmd->argumentList();
7156 // check for matching argument lists
7157 if (
7158 matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),&rmdAl,
7159 cd,fd,&root->argList,
7160 TRUE,root->lang)
7161 )
7162 {
7163 found=TRUE;
7164 rmd_found = rmd;
7165 break;
7166 }
7167 }
7168 }
7169 if (rmd_found) // member found -> copy line number info
7170 {
7171 mmd->setBodySegment(rmd_found->getDefLine(),rmd_found->getStartBodyLine(),rmd_found->getEndBodyLine());
7172 mmd->setBodyDef(rmd_found->getBodyDef());
7173 //md->setBodyMember(rmd);
7174 }
7175 }
7176 }
7177 if (!found) // line number could not be found or is available in this
7178 // entry
7179 {
7180 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7181 mmd->setBodyDef(fd);
7182 }
7183
7184 //if (root->mGrpId!=-1)
7185 //{
7186 // md->setMemberGroup(memberGroupDict[root->mGrpId]);
7187 //}
7188 mmd->setMemberClass(cd);
7189 mmd->setMemberSpecifiers(spec);
7190 mmd->setVhdlSpecifiers(root->vhdlSpec);
7191 mmd->setDefinition(funcDecl);
7193 mmd->addQualifiers(root->qualifiers);
7194 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7195 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7196 mmd->setDocsForDefinition(!root->proto);
7197 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
7198 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7199 mmd->addSectionsToDefinition(root->anchors);
7200 mmd->setMemberGroupId(root->mGrpId);
7201 mmd->setLanguage(root->lang);
7202 mmd->setId(root->id);
7203 //md->setMemberDefTemplateArguments(root->mtArgList);
7204 cd->insertMember(md.get());
7205 cd->insertUsedFile(fd);
7206 mmd->setRefItems(root->sli);
7207 if (root->relatesType==RelatesType::Duplicate) mmd->setRelatedAlso(cd);
7208 addMemberToGroups(root,md.get());
7210 //printf("Adding member=%s\n",qPrint(md->name()));
7211 mn->push_back(std::move(md));
7212 }
7213 if (root->relatesType==RelatesType::Duplicate)
7214 {
7215 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
7216 {
7217 QCString fullFuncDecl=funcDecl;
7218 if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
7219 warn(root->fileName,root->startLine,
7220 "Cannot determine file/namespace for relatedalso function\n{}",
7221 fullFuncDecl
7222 );
7223 }
7224 }
7225 }
7226 }
7227 else
7228 {
7229 warn_undoc(root->fileName,root->startLine, "class '{}' for related function '{}' is not documented.", className,funcName);
7230 }
7231 }
7232 else if (root->parent() && root->parent()->section.isObjcImpl())
7233 {
7234 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
7235 }
7236 else // unrelated not overloaded member found
7237 {
7238 bool globMem = findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec);
7239 if (className.isEmpty() && !globMem)
7240 {
7241 warn(root->fileName,root->startLine, "class for member '{}' cannot be found.", funcName);
7242 }
7243 else if (!className.isEmpty() && !globMem)
7244 {
7245 warn(root->fileName,root->startLine,
7246 "member '{}' of class '{}' cannot be found",
7247 funcName,className);
7248 }
7249 }
7250 }
7251 else
7252 {
7253 // this should not be called
7254 warn(root->fileName,root->startLine,"member with no name found.");
7255 }
7256 return;
7257}
7258
7259//----------------------------------------------------------------------
7260// find the members corresponding to the different documentation blocks
7261// that are extracted from the sources.
7262
7263static void filterMemberDocumentation(const Entry *root,const QCString &relates)
7264{
7265 AUTO_TRACE("root->type='{}' root->inside='{}' root->name='{}' root->args='{}' section={} root->spec={} root->mGrpId={}",
7266 root->type,root->inside,root->name,root->args,root->section,root->spec,root->mGrpId);
7267 //printf("root->parent()->name=%s\n",qPrint(root->parent()->name));
7268 bool isFunc=TRUE;
7269
7270 QCString type = root->type;
7271 QCString args = root->args;
7272 int i=-1, l=0;
7273 if ( // detect func variable/typedef to func ptr
7274 (i=findFunctionPtr(type.str(),root->lang,&l))!=-1
7275 )
7276 {
7277 //printf("Fixing function pointer!\n");
7278 // fix type and argument
7279 args.prepend(type.right(type.length()-i-l));
7280 type=type.left(i+l);
7281 //printf("Results type=%s,name=%s,args=%s\n",qPrint(type),qPrint(root->name),qPrint(args));
7282 isFunc=FALSE;
7283 }
7284 else if ((type.startsWith("typedef ") && args.find('(')!=-1))
7285 // detect function types marked as functions
7286 {
7287 isFunc=FALSE;
7288 }
7289
7290 //printf("Member %s isFunc=%d\n",qPrint(root->name),isFunc);
7291 if (root->section.isMemberDoc())
7292 {
7293 //printf("Documentation for inline member '%s' found args='%s'\n",
7294 // qPrint(root->name),qPrint(args));
7295 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
7296 if (type.isEmpty())
7297 {
7298 findMember(root,
7299 relates,
7300 type,
7301 args,
7302 root->name + args + root->exception,
7303 FALSE,
7304 isFunc);
7305 }
7306 else
7307 {
7308 findMember(root,
7309 relates,
7310 type,
7311 args,
7312 type + " " + root->name + args + root->exception,
7313 FALSE,
7314 isFunc);
7315 }
7316 }
7317 else if (root->section.isOverloadDoc())
7318 {
7319 //printf("Overloaded member %s found\n",qPrint(root->name));
7320 findMember(root,
7321 relates,
7322 type,
7323 args,
7324 root->name,
7325 TRUE,
7326 isFunc);
7327 }
7328 else if
7329 ((root->section.isFunction() // function
7330 ||
7331 (root->section.isVariable() && // variable
7332 !type.isEmpty() && // with a type
7333 g_compoundKeywords.find(type.str())==g_compoundKeywords.end() // that is not a keyword
7334 // (to skip forward declaration of class etc.)
7335 )
7336 )
7337 )
7338 {
7339 //printf("Documentation for member '%s' found args='%s' excp='%s'\n",
7340 // qPrint(root->name),qPrint(args),qPrint(root->exception));
7341 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
7342 //printf("Inside=%s\n Relates=%s\n",qPrint(root->inside),qPrint(relates));
7343 if (type=="friend class" || type=="friend struct" ||
7344 type=="friend union")
7345 {
7346 findMember(root,
7347 relates,
7348 type,
7349 args,
7350 type+" "+root->name,
7351 FALSE,FALSE);
7352
7353 }
7354 else if (!type.isEmpty())
7355 {
7356 findMember(root,
7357 relates,
7358 type,
7359 args,
7360 type+" "+ root->inside + root->name + args + root->exception,
7361 FALSE,isFunc);
7362 }
7363 else
7364 {
7365 findMember(root,
7366 relates,
7367 type,
7368 args,
7369 root->inside + root->name + args + root->exception,
7370 FALSE,isFunc);
7371 }
7372 }
7373 else if (root->section.isDefine() && !relates.isEmpty())
7374 {
7375 findMember(root,
7376 relates,
7377 type,
7378 args,
7379 root->name + args,
7380 FALSE,
7381 !args.isEmpty());
7382 }
7383 else if (root->section.isVariableDoc())
7384 {
7385 //printf("Documentation for variable %s found\n",qPrint(root->name));
7386 //if (!relates.isEmpty()) printf(" Relates %s\n",qPrint(relates));
7387 findMember(root,
7388 relates,
7389 type,
7390 args,
7391 root->name,
7392 FALSE,
7393 FALSE);
7394 }
7395 else if (root->section.isExportedInterface() ||
7396 root->section.isIncludedService())
7397 {
7398 findMember(root,
7399 relates,
7400 type,
7401 args,
7402 type + " " + root->name,
7403 FALSE,
7404 FALSE);
7405 }
7406 else
7407 {
7408 // skip section
7409 //printf("skip section\n");
7410 }
7411}
7412
7413static void findMemberDocumentation(const Entry *root)
7414{
7415 if (root->section.isMemberDoc() ||
7416 root->section.isOverloadDoc() ||
7417 root->section.isFunction() ||
7418 root->section.isVariable() ||
7419 root->section.isVariableDoc() ||
7420 root->section.isDefine() ||
7421 root->section.isIncludedService() ||
7422 root->section.isExportedInterface()
7423 )
7424 {
7425 AUTO_TRACE();
7426 if (root->relatesType==RelatesType::Duplicate && !root->relates.isEmpty())
7427 {
7429 }
7431 }
7432 for (const auto &e : root->children())
7433 {
7434 if (!e->section.isEnum())
7435 {
7436 findMemberDocumentation(e.get());
7437 }
7438 }
7439}
7440
7441//----------------------------------------------------------------------
7442
7443static void findObjCMethodDefinitions(const Entry *root)
7444{
7445 AUTO_TRACE();
7446 for (const auto &objCImpl : root->children())
7447 {
7448 if (objCImpl->section.isObjcImpl())
7449 {
7450 for (const auto &objCMethod : objCImpl->children())
7451 {
7452 if (objCMethod->section.isFunction())
7453 {
7454 //printf(" Found ObjC method definition %s\n",qPrint(objCMethod->name));
7455 findMember(objCMethod.get(),
7456 objCMethod->relates,
7457 objCMethod->type,
7458 objCMethod->args,
7459 objCMethod->type+" "+objCImpl->name+"::"+objCMethod->name+" "+objCMethod->args,
7460 FALSE,TRUE);
7461 objCMethod->section=EntryType::makeEmpty();
7462 }
7463 }
7464 }
7465 }
7466}
7467
7468//----------------------------------------------------------------------
7469// find and add the enumeration to their classes, namespaces or files
7470
7471static void findEnums(const Entry *root)
7472{
7473 if (root->section.isEnum())
7474 {
7475 AUTO_TRACE("name={}",root->name);
7476 ClassDefMutable *cd = nullptr;
7477 FileDef *fd = nullptr;
7478 NamespaceDefMutable *nd = nullptr;
7479 MemberNameLinkedMap *mnsd = nullptr;
7480 bool isGlobal = false;
7481 bool isRelated = false;
7482 bool isMemberOf = false;
7483 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7484
7485 QCString name;
7486 QCString scope;
7487
7488 int i = root->name.findRev("::");
7489 if (i!=-1) // scope is specified
7490 {
7491 scope=root->name.left(i); // extract scope
7492 if (root->lang==SrcLangExt::CSharp)
7493 {
7494 scope = mangleCSharpGenericName(scope);
7495 }
7496 name=root->name.right(root->name.length()-i-2); // extract name
7497 if ((cd=getClassMutable(scope))==nullptr)
7498 {
7500 }
7501 }
7502 else // no scope, check the scope in which the docs where found
7503 {
7504 if (root->parent()->section.isScope() && !root->parent()->name.isEmpty()) // found enum docs inside a compound
7505 {
7506 scope=root->parent()->name;
7507 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7508 }
7509 name=root->name;
7510 }
7511
7512 if (!root->relates.isEmpty())
7513 { // related member, prefix user specified scope
7514 isRelated=TRUE;
7515 isMemberOf=(root->relatesType==RelatesType::MemberOf);
7516 if (getClass(root->relates)==nullptr && !scope.isEmpty())
7517 scope=mergeScopes(scope,root->relates);
7518 else
7519 scope=root->relates;
7520 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7521 }
7522
7523 if (cd && !name.isEmpty()) // found a enum inside a compound
7524 {
7525 //printf("Enum '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7526 fd=nullptr;
7528 isGlobal=false;
7529 }
7530 else if (nd) // found enum inside namespace
7531 {
7533 isGlobal=true;
7534 }
7535 else // found a global enum
7536 {
7537 fd=root->fileDef();
7539 isGlobal=true;
7540 }
7541
7542 if (!name.isEmpty())
7543 {
7544 // new enum type
7545 AUTO_TRACE_ADD("new enum {} at line {} of {}",name,root->bodyLine,root->fileName);
7546 auto md = createMemberDef(
7547 root->fileName,root->startLine,root->startColumn,
7548 QCString(),name,QCString(),QCString(),
7549 root->protection,Specifier::Normal,FALSE,
7550 isMemberOf ? Relationship::Foreign : isRelated ? Relationship::Related : Relationship::Member,
7553 auto mmd = toMemberDefMutable(md.get());
7554 mmd->setTagInfo(root->tagInfo());
7555 mmd->setLanguage(root->lang);
7556 mmd->setId(root->id);
7557 if (!isGlobal) mmd->setMemberClass(cd); else mmd->setFileDef(fd);
7558 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7559 mmd->setBodyDef(root->fileDef());
7560 mmd->setMemberSpecifiers(root->spec);
7561 mmd->setVhdlSpecifiers(root->vhdlSpec);
7562 mmd->setEnumBaseType(root->args);
7563 //printf("Enum %s definition at line %d of %s: protection=%d scope=%s\n",
7564 // qPrint(root->name),root->bodyLine,qPrint(root->fileName),root->protection,cd?qPrint(cd->name()):"<none>");
7565 mmd->addSectionsToDefinition(root->anchors);
7566 mmd->setMemberGroupId(root->mGrpId);
7568 mmd->addQualifiers(root->qualifiers);
7569 //printf("%s::setRefItems(%zu)\n",qPrint(md->name()),root->sli.size());
7570 mmd->setRefItems(root->sli);
7571 //printf("found enum %s nd=%p\n",qPrint(md->name()),nd);
7572 bool defSet=FALSE;
7573
7574 QCString baseType = root->args;
7575 if (!baseType.isEmpty())
7576 {
7577 baseType.prepend(" : ");
7578 }
7579
7580 if (nd)
7581 {
7582 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES))
7583 {
7584 mmd->setDefinition(name+baseType);
7585 }
7586 else
7587 {
7588 mmd->setDefinition(nd->name()+"::"+name+baseType);
7589 }
7590 //printf("definition=%s\n",md->definition());
7591 defSet=TRUE;
7592 mmd->setNamespace(nd);
7593 nd->insertMember(md.get());
7594 }
7595
7596 // even if we have already added the enum to a namespace, we still
7597 // also want to add it to other appropriate places such as file
7598 // or class.
7599 if (isGlobal && (nd==nullptr || !nd->isAnonymous()))
7600 {
7601 if (!defSet) mmd->setDefinition(name+baseType);
7602 if (fd==nullptr && root->parent())
7603 {
7604 fd=root->parent()->fileDef();
7605 }
7606 if (fd)
7607 {
7608 mmd->setFileDef(fd);
7609 fd->insertMember(md.get());
7610 }
7611 }
7612 else if (cd)
7613 {
7614 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES))
7615 {
7616 mmd->setDefinition(name+baseType);
7617 }
7618 else
7619 {
7620 mmd->setDefinition(cd->name()+"::"+name+baseType);
7621 }
7622 cd->insertMember(md.get());
7623 cd->insertUsedFile(fd);
7624 }
7625 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7626 mmd->setDocsForDefinition(!root->proto);
7627 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7628 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7629
7630 //printf("Adding member=%s\n",qPrint(md->name()));
7631 addMemberToGroups(root,md.get());
7633
7634 MemberName *mn = mnsd->add(name);
7635 mn->push_back(std::move(md));
7636 }
7637 }
7638 else
7639 {
7640 for (const auto &e : root->children()) findEnums(e.get());
7641 }
7642}
7643
7644//----------------------------------------------------------------------
7645
7646static void addEnumValuesToEnums(const Entry *root)
7647{
7648 if (root->section.isEnum())
7649 // non anonymous enumeration
7650 {
7651 AUTO_TRACE("name={}",root->name);
7652 ClassDefMutable *cd = nullptr;
7653 FileDef *fd = nullptr;
7654 NamespaceDefMutable *nd = nullptr;
7655 MemberNameLinkedMap *mnsd = nullptr;
7656 bool isGlobal = false;
7657 bool isRelated = false;
7658 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7659
7660 QCString name;
7661 QCString scope;
7662
7663 int i = root->name.findRev("::");
7664 if (i!=-1) // scope is specified
7665 {
7666 scope=root->name.left(i); // extract scope
7667 if (root->lang==SrcLangExt::CSharp)
7668 {
7669 scope = mangleCSharpGenericName(scope);
7670 }
7671 name=root->name.right(root->name.length()-i-2); // extract name
7672 if ((cd=getClassMutable(scope))==nullptr)
7673 {
7675 }
7676 }
7677 else // no scope, check the scope in which the docs where found
7678 {
7679 if (root->parent()->section.isScope() && !root->parent()->name.isEmpty()) // found enum docs inside a compound
7680 {
7681 scope=root->parent()->name;
7682 if (root->lang==SrcLangExt::CSharp)
7683 {
7684 scope = mangleCSharpGenericName(scope);
7685 }
7686 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7687 }
7688 name=root->name;
7689 }
7690
7691 if (!root->relates.isEmpty())
7692 { // related member, prefix user specified scope
7693 isRelated=TRUE;
7694 if (getClassMutable(root->relates)==nullptr && !scope.isEmpty())
7695 scope=mergeScopes(scope,root->relates);
7696 else
7697 scope=root->relates;
7698 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7699 }
7700
7701 if (cd && !name.isEmpty()) // found a enum inside a compound
7702 {
7703 //printf("Enum in class '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7704 fd=nullptr;
7706 isGlobal=false;
7707 }
7708 else if (nd && !nd->isAnonymous()) // found enum inside namespace
7709 {
7710 //printf("Enum in namespace '%s'::'%s'\n",qPrint(nd->name()),qPrint(name));
7712 isGlobal=true;
7713 }
7714 else // found a global enum
7715 {
7716 fd=root->fileDef();
7717 //printf("Enum in file '%s': '%s'\n",qPrint(fd->name()),qPrint(name));
7719 isGlobal=true;
7720 }
7721
7722 if (!name.isEmpty())
7723 {
7724 //printf("** name=%s\n",qPrint(name));
7725 MemberName *mn = mnsd->find(name); // for all members with this name
7726 if (mn)
7727 {
7728 struct EnumValueInfo
7729 {
7730 EnumValueInfo(const QCString &n,std::unique_ptr<MemberDef> &&md) :
7731 name(n), member(std::move(md)) {}
7732 QCString name;
7733 std::unique_ptr<MemberDef> member;
7734 };
7735 std::vector< EnumValueInfo > extraMembers;
7736 // for each enum in this list
7737 for (const auto &imd : *mn)
7738 {
7739 MemberDefMutable *md = toMemberDefMutable(imd.get());
7740 // use raw pointer in this loop, since we modify mn and can then invalidate mdp.
7741 if (md && md->isEnumerate() && !root->children().empty())
7742 {
7743 AUTO_TRACE_ADD("enum {} with {} children",md->name(),root->children().size());
7744 for (const auto &e : root->children())
7745 {
7746 SrcLangExt sle = root->lang;
7747 bool isJavaLike = sle==SrcLangExt::CSharp || sle==SrcLangExt::Java || sle==SrcLangExt::XML;
7748 if ( isJavaLike || root->spec.isStrong())
7749 {
7750 if (sle == SrcLangExt::Cpp && e->section.isDefine()) continue;
7751 // Unlike classic C/C++ enums, for C++11, C# & Java enum
7752 // values are only visible inside the enum scope, so we must create
7753 // them here and only add them to the enum
7754 //printf("md->qualifiedName()=%s e->name=%s tagInfo=%p name=%s\n",
7755 // qPrint(md->qualifiedName()),qPrint(e->name),(void*)e->tagInfo(),qPrint(e->name));
7756 QCString qualifiedName = root->name;
7757 i = qualifiedName.findRev("::");
7758 if (i!=-1 && sle==SrcLangExt::CSharp)
7759 {
7760 qualifiedName = mangleCSharpGenericName(qualifiedName.left(i))+qualifiedName.mid(i);
7761 }
7762 if (isJavaLike)
7763 {
7764 qualifiedName=substitute(qualifiedName,"::",".");
7765 }
7766 if (md->qualifiedName()==qualifiedName) // enum value scope matches that of the enum
7767 {
7768 QCString fileName = e->fileName;
7769 if (fileName.isEmpty() && e->tagInfo())
7770 {
7771 fileName = e->tagInfo()->tagName;
7772 }
7773 AUTO_TRACE_ADD("strong enum value {}",e->name);
7774 auto fmd = createMemberDef(
7775 fileName,e->startLine,e->startColumn,
7776 e->type,e->name,e->args,QCString(),
7777 e->protection, Specifier::Normal,e->isStatic,Relationship::Member,
7779 auto fmmd = toMemberDefMutable(fmd.get());
7780 NamespaceDef *mnd = md->getNamespaceDef();
7781 if (md->getClassDef())
7782 fmmd->setMemberClass(md->getClassDef());
7783 else if (mnd && (mnd->isLinkable() || mnd->isAnonymous()))
7784 fmmd->setNamespace(mnd);
7785 else if (md->getFileDef())
7786 fmmd->setFileDef(md->getFileDef());
7787 fmmd->setOuterScope(md->getOuterScope());
7788 fmmd->setTagInfo(e->tagInfo());
7789 fmmd->setLanguage(e->lang);
7790 fmmd->setBodySegment(e->startLine,e->bodyLine,e->endBodyLine);
7791 fmmd->setBodyDef(e->fileDef());
7792 fmmd->setId(e->id);
7793 fmmd->setDocumentation(e->doc,e->docFile,e->docLine);
7794 fmmd->setBriefDescription(e->brief,e->briefFile,e->briefLine);
7795 fmmd->addSectionsToDefinition(e->anchors);
7796 fmmd->setInitializer(e->initializer.str());
7797 fmmd->setMaxInitLines(e->initLines);
7798 fmmd->setMemberGroupId(e->mGrpId);
7799 fmmd->setExplicitExternal(e->explicitExternal,fileName,e->startLine,e->startColumn);
7800 fmmd->setRefItems(e->sli);
7801 fmmd->setAnchor();
7802 md->insertEnumField(fmd.get());
7803 fmmd->setEnumScope(md,TRUE);
7804 extraMembers.emplace_back(e->name,std::move(fmd));
7805 }
7806 }
7807 else
7808 {
7809 AUTO_TRACE_ADD("enum value {}",e->name);
7810 //printf("e->name=%s isRelated=%d\n",qPrint(e->name),isRelated);
7811 MemberName *fmn=nullptr;
7812 MemberNameLinkedMap *emnsd = isRelated ? Doxygen::functionNameLinkedMap : mnsd;
7813 if (!e->name.isEmpty() && (fmn=emnsd->find(e->name)))
7814 // get list of members with the same name as the field
7815 {
7816 for (const auto &ifmd : *fmn)
7817 {
7818 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
7819 if (fmd && fmd->isEnumValue() && fmd->getOuterScope()==md->getOuterScope()) // in same scope
7820 {
7821 //printf("found enum value with same name %s in scope %s\n",
7822 // qPrint(fmd->name()),qPrint(fmd->getOuterScope()->name()));
7823 if (nd && !nd->isAnonymous())
7824 {
7825 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
7826 {
7827 const NamespaceDef *fnd=fmd->getNamespaceDef();
7828 if (fnd==nd) // enum value is inside a namespace
7829 {
7830 md->insertEnumField(fmd);
7831 fmd->setEnumScope(md);
7832 }
7833 }
7834 }
7835 else if (isGlobal)
7836 {
7837 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
7838 {
7839 const FileDef *ffd=fmd->getFileDef();
7840 if (ffd==fd && ffd==md->getFileDef()) // enum value has file scope
7841 {
7842 md->insertEnumField(fmd);
7843 fmd->setEnumScope(md);
7844 }
7845 }
7846 }
7847 else if (isRelated && cd) // reparent enum value to
7848 // match the enum's scope
7849 {
7850 md->insertEnumField(fmd); // add field def to list
7851 fmd->setEnumScope(md); // cross ref with enum name
7852 fmd->setEnumClassScope(cd); // cross ref with enum name
7853 fmd->setOuterScope(cd);
7854 fmd->makeRelated();
7855 cd->insertMember(fmd);
7856 }
7857 else
7858 {
7859 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
7860 {
7861 const ClassDef *fcd=fmd->getClassDef();
7862 if (fcd==cd) // enum value is inside a class
7863 {
7864 //printf("Inserting enum field %s in enum scope %s\n",
7865 // qPrint(fmd->name()),qPrint(md->name()));
7866 md->insertEnumField(fmd); // add field def to list
7867 fmd->setEnumScope(md); // cross ref with enum name
7868 }
7869 }
7870 }
7871 }
7872 }
7873 }
7874 }
7875 }
7876 }
7877 }
7878 // move the newly added members into mn
7879 for (auto &e : extraMembers)
7880 {
7881 MemberName *emn=mnsd->add(e.name);
7882 emn->push_back(std::move(e.member));
7883 }
7884 }
7885 }
7886 }
7887 else
7888 {
7889 for (const auto &e : root->children()) addEnumValuesToEnums(e.get());
7890 }
7891}
7892
7893//----------------------------------------------------------------------
7894
7895static void addEnumDocs(const Entry *root,MemberDefMutable *md)
7896{
7897 AUTO_TRACE();
7898 // documentation outside a compound overrides the documentation inside it
7899 {
7900 md->setDocumentation(root->doc,root->docFile,root->docLine);
7901 md->setDocsForDefinition(!root->proto);
7902 }
7903
7904 // brief descriptions inside a compound override the documentation
7905 // outside it
7906 {
7907 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7908 }
7909
7910 if (md->inbodyDocumentation().isEmpty() || !root->parent()->name.isEmpty())
7911 {
7913 }
7914
7915 if (root->mGrpId!=-1 && md->getMemberGroupId()==-1)
7916 {
7917 md->setMemberGroupId(root->mGrpId);
7918 }
7919
7921 md->setRefItems(root->sli);
7922
7923 const GroupDef *gd=md->getGroupDef();
7924 if (gd==nullptr && !root->groups.empty()) // member not grouped but out-of-line documentation is
7925 {
7926 addMemberToGroups(root,md);
7927 }
7929}
7930
7931//----------------------------------------------------------------------
7932// Search for the name in the associated groups. If a matching member
7933// definition exists, then add the documentation to it and return TRUE,
7934// otherwise FALSE.
7935
7936static bool tryAddEnumDocsToGroupMember(const Entry *root,const QCString &name)
7937{
7938 for (const auto &g : root->groups)
7939 {
7940 const GroupDef *gd = Doxygen::groupLinkedMap->find(g.groupname);
7941 if (gd)
7942 {
7943 MemberList *ml = gd->getMemberList(MemberListType::DecEnumMembers());
7944 if (ml)
7945 {
7946 MemberDefMutable *md = toMemberDefMutable(ml->find(name));
7947 if (md)
7948 {
7949 addEnumDocs(root,md);
7950 return TRUE;
7951 }
7952 }
7953 }
7954 else if (!gd && g.pri == Grouping::GROUPING_INGROUP)
7955 {
7956 warn(root->fileName, root->startLine,
7957 "Found non-existing group '{}' for the command '{}', ignoring command",
7958 g.groupname, Grouping::getGroupPriName( g.pri )
7959 );
7960 }
7961 }
7962
7963 return FALSE;
7964}
7965
7966//----------------------------------------------------------------------
7967// find the documentation blocks for the enumerations
7968
7969static void findEnumDocumentation(const Entry *root)
7970{
7971 if (root->section.isEnumDoc() &&
7972 !root->name.isEmpty() &&
7973 root->name.at(0)!='@' // skip anonymous enums
7974 )
7975 {
7976 QCString name;
7977 QCString scope;
7978 int i = root->name.findRev("::");
7979 if (i!=-1) // scope is specified as part of the name
7980 {
7981 name=root->name.right(root->name.length()-i-2); // extract name
7982 scope=root->name.left(i); // extract scope
7983 //printf("Scope='%s' Name='%s'\n",qPrint(scope),qPrint(name));
7984 }
7985 else // just the name
7986 {
7987 name=root->name;
7988 }
7989 if (root->parent()->section.isScope() && !root->parent()->name.isEmpty()) // found enum docs inside a compound
7990 {
7991 if (!scope.isEmpty()) scope.prepend("::");
7992 scope.prepend(root->parent()->name);
7993 }
7994 const ClassDef *cd = getClass(scope);
7995 const NamespaceDef *nd=Doxygen::namespaceLinkedMap->find(scope);
7996 const FileDef *fd = root->fileDef();
7997 AUTO_TRACE("Found docs for enum with name '{}' and scope '{}' in context '{}' cd='{}', nd='{}' fd='{}'",
7998 name,scope,root->parent()->name,
7999 cd ? cd->name() : QCString("<none>"),
8000 nd ? nd->name() : QCString("<none>"),
8001 fd ? fd->name() : QCString("<none>"));
8002
8003 if (!name.isEmpty())
8004 {
8005 bool found = tryAddEnumDocsToGroupMember(root, name);
8006 if (!found)
8007 {
8008 MemberName *mn = cd ? Doxygen::memberNameLinkedMap->find(name) : Doxygen::functionNameLinkedMap->find(name);
8009 if (mn)
8010 {
8011 for (const auto &imd : *mn)
8012 {
8013 MemberDefMutable *md = toMemberDefMutable(imd.get());
8014 if (md && md->isEnumerate())
8015 {
8016 const ClassDef *mcd = md->getClassDef();
8017 const NamespaceDef *mnd = md->getNamespaceDef();
8018 const FileDef *mfd = md->getFileDef();
8019 if (cd && mcd==cd)
8020 {
8021 AUTO_TRACE_ADD("Match found for class scope");
8022 addEnumDocs(root,md);
8023 found = TRUE;
8024 break;
8025 }
8026 else if (cd==nullptr && mcd==nullptr && nd!=nullptr && mnd==nd)
8027 {
8028 AUTO_TRACE_ADD("Match found for namespace scope");
8029 addEnumDocs(root,md);
8030 found = TRUE;
8031 break;
8032 }
8033 else if (cd==nullptr && nd==nullptr && mcd==nullptr && mnd==nullptr && fd==mfd)
8034 {
8035 AUTO_TRACE_ADD("Match found for global scope");
8036 addEnumDocs(root,md);
8037 found = TRUE;
8038 break;
8039 }
8040 }
8041 }
8042 }
8043 }
8044 if (!found)
8045 {
8046 warn(root->fileName,root->startLine, "Documentation for undefined enum '{}' found.", name);
8047 }
8048 }
8049 }
8050 for (const auto &e : root->children()) findEnumDocumentation(e.get());
8051}
8052
8053// search for each enum (member or function) in mnl if it has documented
8054// enum values.
8055static void findDEV(const MemberNameLinkedMap &mnsd)
8056{
8057 // for each member name
8058 for (const auto &mn : mnsd)
8059 {
8060 // for each member definition
8061 for (const auto &imd : *mn)
8062 {
8063 MemberDefMutable *md = toMemberDefMutable(imd.get());
8064 if (md && md->isEnumerate()) // member is an enum
8065 {
8066 int documentedEnumValues=0;
8067 // for each enum value
8068 for (const auto &fmd : md->enumFieldList())
8069 {
8070 if (fmd->isLinkableInProject()) documentedEnumValues++;
8071 }
8072 // at least one enum value is documented
8073 if (documentedEnumValues>0) md->setDocumentedEnumValues(TRUE);
8074 }
8075 }
8076 }
8077}
8078
8079// search for each enum (member or function) if it has documented enum
8080// values.
8086
8087//----------------------------------------------------------------------
8088
8090{
8091 auto &index = Index::instance();
8092 // for each class member name
8093 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8094 {
8095 // for each member definition
8096 for (const auto &md : *mn)
8097 {
8098 index.addClassMemberNameToIndex(md.get());
8099 if (md->getModuleDef())
8100 {
8101 index.addModuleMemberNameToIndex(md.get());
8102 }
8103 }
8104 }
8105 // for each file/namespace function name
8106 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8107 {
8108 // for each member definition
8109 for (const auto &md : *mn)
8110 {
8111 if (md->getNamespaceDef())
8112 {
8113 index.addNamespaceMemberNameToIndex(md.get());
8114 }
8115 else
8116 {
8117 index.addFileMemberNameToIndex(md.get());
8118 }
8119 if (md->getModuleDef())
8120 {
8121 index.addModuleMemberNameToIndex(md.get());
8122 }
8123 }
8124 }
8125
8126 index.sortMemberIndexLists();
8127}
8128
8129//----------------------------------------------------------------------
8130
8131static void addToIndices()
8132{
8133 for (const auto &cd : *Doxygen::classLinkedMap)
8134 {
8135 if (cd->isLinkableInProject())
8136 {
8137 Doxygen::indexList->addIndexItem(cd.get(),nullptr);
8138 if (Doxygen::searchIndex.enabled())
8139 {
8140 Doxygen::searchIndex.setCurrentDoc(cd.get(),cd->anchor(),FALSE);
8141 Doxygen::searchIndex.addWord(cd->localName(),TRUE);
8142 }
8143 }
8144 }
8145
8146 for (const auto &cd : *Doxygen::conceptLinkedMap)
8147 {
8148 if (cd->isLinkableInProject())
8149 {
8150 Doxygen::indexList->addIndexItem(cd.get(),nullptr);
8151 if (Doxygen::searchIndex.enabled())
8152 {
8153 Doxygen::searchIndex.setCurrentDoc(cd.get(),cd->anchor(),FALSE);
8154 Doxygen::searchIndex.addWord(cd->localName(),TRUE);
8155 }
8156 }
8157 }
8158
8159 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8160 {
8161 if (nd->isLinkableInProject())
8162 {
8163 Doxygen::indexList->addIndexItem(nd.get(),nullptr);
8164 if (Doxygen::searchIndex.enabled())
8165 {
8166 Doxygen::searchIndex.setCurrentDoc(nd.get(),nd->anchor(),FALSE);
8167 Doxygen::searchIndex.addWord(nd->localName(),TRUE);
8168 }
8169 }
8170 }
8171
8172 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8173 {
8174 for (const auto &fd : *fn)
8175 {
8176 if (Doxygen::searchIndex.enabled() && fd->isLinkableInProject())
8177 {
8178 Doxygen::searchIndex.setCurrentDoc(fd.get(),fd->anchor(),FALSE);
8179 Doxygen::searchIndex.addWord(fd->localName(),TRUE);
8180 }
8181 }
8182 }
8183
8184 auto addWordsForTitle = [](const Definition *d,const QCString &anchor,const QCString &title)
8185 {
8186 Doxygen::indexList->addIndexItem(d,nullptr,QCString(),filterTitle(title));
8187 if (Doxygen::searchIndex.enabled())
8188 {
8189 Doxygen::searchIndex.setCurrentDoc(d,anchor,false);
8190 std::string s = title.str();
8191 static const reg::Ex re(R"(\a[\w-]*)");
8192 reg::Iterator it(s,re);
8194 for (; it!=end ; ++it)
8195 {
8196 const auto &match = *it;
8197 std::string matchStr = match.str();
8198 Doxygen::searchIndex.addWord(matchStr,true);
8199 }
8200 }
8201 };
8202
8203 for (const auto &gd : *Doxygen::groupLinkedMap)
8204 {
8205 if (gd->isLinkableInProject())
8206 {
8207 addWordsForTitle(gd.get(),gd->anchor(),gd->groupTitle());
8208 }
8209 }
8210
8211 for (const auto &pd : *Doxygen::pageLinkedMap)
8212 {
8213 if (pd->isLinkableInProject())
8214 {
8215 addWordsForTitle(pd.get(),pd->anchor(),pd->title());
8216 }
8217 }
8218
8220 {
8221 addWordsForTitle(Doxygen::mainPage.get(),Doxygen::mainPage->anchor(),Doxygen::mainPage->title());
8222 }
8223
8224 auto addMemberToSearchIndex = [](const MemberDef *md)
8225 {
8226 if (Doxygen::searchIndex.enabled())
8227 {
8228 Doxygen::searchIndex.setCurrentDoc(md,md->anchor(),FALSE);
8229 QCString ln=md->localName();
8230 QCString qn=md->qualifiedName();
8231 Doxygen::searchIndex.addWord(ln,TRUE);
8232 if (ln!=qn)
8233 {
8234 Doxygen::searchIndex.addWord(qn,TRUE);
8235 if (md->getClassDef())
8236 {
8237 Doxygen::searchIndex.addWord(md->getClassDef()->displayName(),TRUE);
8238 }
8239 if (md->getNamespaceDef())
8240 {
8241 Doxygen::searchIndex.addWord(md->getNamespaceDef()->displayName(),TRUE);
8242 }
8243 }
8244 }
8245 };
8246
8247 auto getScope = [](const MemberDef *md)
8248 {
8249 const Definition *scope = nullptr;
8250 if (md->getGroupDef()) scope = md->getGroupDef();
8251 else if (md->getClassDef()) scope = md->getClassDef();
8252 else if (md->getNamespaceDef()) scope = md->getNamespaceDef();
8253 else if (md->getFileDef()) scope = md->getFileDef();
8254 return scope;
8255 };
8256
8257 auto addMemberToIndices = [addMemberToSearchIndex,getScope](const MemberDef *md)
8258 {
8259 if (md->isLinkableInProject())
8260 {
8261 if (!(md->isEnumerate() && md->isAnonymous()))
8262 {
8263 Doxygen::indexList->addIndexItem(getScope(md),md);
8265 }
8266 if (md->isEnumerate())
8267 {
8268 for (const auto &fmd : md->enumFieldList())
8269 {
8270 Doxygen::indexList->addIndexItem(getScope(fmd),fmd);
8272 }
8273 }
8274 }
8275 };
8276
8277 // for each class member name
8278 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8279 {
8280 // for each member definition
8281 for (const auto &md : *mn)
8282 {
8283 addMemberToIndices(md.get());
8284 }
8285 }
8286 // for each file/namespace function name
8287 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8288 {
8289 // for each member definition
8290 for (const auto &md : *mn)
8291 {
8292 addMemberToIndices(md.get());
8293 }
8294 }
8295}
8296
8297//----------------------------------------------------------------------
8298
8300{
8301 // for each member name
8302 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8303 {
8304 // for each member definition
8305 for (const auto &imd : *mn)
8306 {
8307 MemberDefMutable *md = toMemberDefMutable(imd.get());
8308 if (md)
8309 {
8311 }
8312 }
8313 }
8314 // for each member name
8315 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8316 {
8317 // for each member definition
8318 for (const auto &imd : *mn)
8319 {
8320 MemberDefMutable *md = toMemberDefMutable(imd.get());
8321 if (md)
8322 {
8324 }
8325 }
8326 }
8327}
8328
8329// recursive helper function looking for reimplements/implemented
8330// by relations between class cd and direct or indirect base class bcd
8332{
8333 for (const auto &mn : cd->memberNameInfoLinkedMap()) // for each member in class cd with a unique name
8334 {
8335 for (const auto &imd : *mn) // for each member with a given name
8336 {
8337 MemberDefMutable *md = toMemberDefMutable(imd->memberDef());
8338 if (md && (md->isFunction() || md->isCSharpProperty())) // filter on reimplementable members
8339 {
8340 ClassDef *mbcd = bcd->classDef;
8341 if (mbcd && mbcd->isLinkable()) // filter on linkable classes
8342 {
8343 const auto &bmn = mbcd->memberNameInfoLinkedMap();
8344 const auto &bmni = bmn.find(mn->memberName());
8345 if (bmni) // there are base class members with the same name
8346 {
8347 for (const auto &ibmd : *bmni) // for base class member with that name
8348 {
8349 MemberDefMutable *bmd = toMemberDefMutable(ibmd->memberDef());
8350 if (bmd) // not part of an inline namespace
8351 {
8352 auto lang = bmd->getLanguage();
8353 auto compType = mbcd->compoundType();
8354 if (bmd->virtualness()!=Specifier::Normal ||
8355 lang==SrcLangExt::Python ||
8356 lang==SrcLangExt::Java ||
8357 lang==SrcLangExt::PHP ||
8358 compType==ClassDef::Interface ||
8359 compType==ClassDef::Protocol)
8360 {
8361 const ArgumentList &bmdAl = bmd->argumentList();
8362 const ArgumentList &mdAl = md->argumentList();
8363 //printf(" Base argList='%s'\n Super argList='%s'\n",
8364 // qPrint(argListToString(bmdAl)),
8365 // qPrint(argListToString(mdAl))
8366 // );
8367 if (
8368 lang==SrcLangExt::Python ||
8369 matchArguments2(bmd->getOuterScope(),bmd->getFileDef(),&bmdAl,
8370 md->getOuterScope(), md->getFileDef(), &mdAl,
8371 TRUE,lang
8372 )
8373 )
8374 {
8375 if (lang==SrcLangExt::Python && md->name().startsWith("__")) continue; // private members do not reimplement
8376 //printf("match!\n");
8377 const MemberDef *rmd = md->reimplements();
8378 if (rmd==nullptr) // not already assigned
8379 {
8380 //printf("%s: setting (new) reimplements member %s\n",qPrint(md->qualifiedName()),qPrint(bmd->qualifiedName()));
8381 md->setReimplements(bmd);
8382 }
8383 //printf("%s: add reimplementedBy member %s\n",qPrint(bmd->qualifiedName()),qPrint(md->qualifiedName()));
8384 bmd->insertReimplementedBy(md);
8385 }
8386 else
8387 {
8388 //printf("no match!\n");
8389 }
8390 }
8391 }
8392 }
8393 }
8394 }
8395 }
8396 }
8397 }
8398
8399 // do also for indirect base classes
8400 for (const auto &bbcd : bcd->classDef->baseClasses())
8401 {
8403 }
8404}
8405
8406//----------------------------------------------------------------------
8407// computes the relation between all members. For each member 'm'
8408// the members that override the implementation of 'm' are searched and
8409// the member that 'm' overrides is searched.
8410
8412{
8413 for (const auto &cd : *Doxygen::classLinkedMap)
8414 {
8415 if (cd->isLinkable())
8416 {
8417 for (const auto &bcd : cd->baseClasses())
8418 {
8420 }
8421 }
8422 }
8423}
8424
8425//----------------------------------------------------------------------------
8426
8428{
8429 // for each class
8430 for (const auto &cd : *Doxygen::classLinkedMap)
8431 {
8432 // that is a template
8433 for (const auto &ti : cd->getTemplateInstances())
8434 {
8435 ClassDefMutable *tcdm = toClassDefMutable(ti.classDef);
8436 if (tcdm)
8437 {
8438 tcdm->addMembersToTemplateInstance(cd.get(),cd->templateArguments(),ti.templSpec);
8439 }
8440 }
8441 }
8442}
8443
8444//----------------------------------------------------------------------------
8445
8446static void mergeCategories()
8447{
8448 AUTO_TRACE();
8449 // merge members of categories into the class they extend
8450 for (const auto &cd : *Doxygen::classLinkedMap)
8451 {
8452 int i=cd->name().find('(');
8453 if (i!=-1) // it is an Objective-C category
8454 {
8455 QCString baseName=cd->name().left(i);
8456 ClassDefMutable *baseClass=toClassDefMutable(Doxygen::classLinkedMap->find(baseName));
8457 if (baseClass)
8458 {
8459 AUTO_TRACE_ADD("merging members of category {} into {}",cd->name(),baseClass->name());
8460 baseClass->mergeCategory(cd.get());
8461 }
8462 }
8463 }
8464}
8465
8466// builds the list of all members for each class
8467
8469{
8470 // merge the member list of base classes into the inherited classes.
8471 for (const auto &cd : *Doxygen::classLinkedMap)
8472 {
8473 if (// !cd->isReference() && // not an external class
8474 cd->subClasses().empty() && // is a root of the hierarchy
8475 !cd->baseClasses().empty()) // and has at least one base class
8476 {
8477 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8478 if (cdm)
8479 {
8480 //printf("*** merging members for %s\n",qPrint(cd->name()));
8481 cdm->mergeMembers();
8482 }
8483 }
8484 }
8485 // now sort the member list of all members for all classes.
8486 for (const auto &cd : *Doxygen::classLinkedMap)
8487 {
8488 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8489 if (cdm)
8490 {
8491 cdm->sortAllMembersList();
8492 }
8493 }
8494}
8495
8496//----------------------------------------------------------------------------
8497
8499{
8500 auto processSourceFile = [](FileDef *fd,OutputList &ol,ClangTUParser *parser)
8501 {
8502 bool showSources = fd->generateSourceFile() && !Htags::useHtags; // sources need to be shown in the output
8503 bool parseSources = !fd->isReference() && Doxygen::parseSourcesNeeded; // we needed to parse the sources even if we do not show them
8504 if (showSources)
8505 {
8506 msg("Generating code for file {}...\n",fd->docName());
8507 fd->writeSourceHeader(ol);
8508 fd->writeSourceBody(ol,parser);
8509 fd->writeSourceFooter(ol);
8510 }
8511 else if (parseSources)
8512 {
8513 msg("Parsing code for file {}...\n",fd->docName());
8514 fd->parseSource(parser);
8515 }
8516 };
8517 if (!Doxygen::inputNameLinkedMap->empty())
8518 {
8519#if USE_LIBCLANG
8521 {
8522 StringUnorderedSet processedFiles;
8523
8524 // create a dictionary with files to process
8525 StringUnorderedSet filesToProcess;
8526
8527 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8528 {
8529 for (const auto &fd : *fn)
8530 {
8531 filesToProcess.insert(fd->absFilePath().str());
8532 }
8533 }
8534 // process source files (and their include dependencies)
8535 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8536 {
8537 for (const auto &fd : *fn)
8538 {
8539 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt::Cpp &&
8540 (fd->generateSourceFile() ||
8542 )
8543 )
8544 {
8545 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8546 clangParser->parse();
8547 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8548
8549 for (auto incFile : clangParser->filesInSameTU())
8550 {
8551 if (filesToProcess.find(incFile)!=filesToProcess.end() && // part of input
8552 fd->absFilePath()!=incFile && // not same file
8553 processedFiles.find(incFile)==processedFiles.end()) // not yet marked as processed
8554 {
8555 StringVector moreFiles;
8556 bool ambig = false;
8558 if (ifd && !ifd->isReference())
8559 {
8560 processSourceFile(ifd,*g_outputList,clangParser.get());
8561 processedFiles.insert(incFile);
8562 }
8563 }
8564 }
8565 processedFiles.insert(fd->absFilePath().str());
8566 }
8567 }
8568 }
8569 // process remaining files
8570 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8571 {
8572 for (const auto &fd : *fn)
8573 {
8574 if (processedFiles.find(fd->absFilePath().str())==processedFiles.end()) // not yet processed
8575 {
8576 if (fd->getLanguage()==SrcLangExt::Cpp) // C/C++ file, use clang parser
8577 {
8578 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8579 clangParser->parse();
8580 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8581 }
8582 else // non C/C++ file, use built-in parser
8583 {
8584 processSourceFile(fd.get(),*g_outputList,nullptr);
8585 }
8586 }
8587 }
8588 }
8589 }
8590 else
8591#endif
8592 {
8593 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8594 if (numThreads>1)
8595 {
8596 msg("Generating code files using {} threads.\n",numThreads);
8597 struct SourceContext
8598 {
8599 SourceContext(FileDef *fd_,bool gen_,const OutputList &ol_)
8600 : fd(fd_), generateSourceFile(gen_), ol(ol_) {}
8601 FileDef *fd;
8602 bool generateSourceFile;
8603 OutputList ol;
8604 };
8605 ThreadPool threadPool(numThreads);
8606 std::vector< std::future< std::shared_ptr<SourceContext> > > results;
8607 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8608 {
8609 for (const auto &fd : *fn)
8610 {
8611 bool generateSourceFile = fd->generateSourceFile() && !Htags::useHtags;
8612 auto ctx = std::make_shared<SourceContext>(fd.get(),generateSourceFile,*g_outputList);
8613 auto processFile = [ctx]()
8614 {
8615 if (ctx->generateSourceFile)
8616 {
8617 msg("Generating code for file {}...\n",ctx->fd->docName());
8618 }
8619 else
8620 {
8621 msg("Parsing code for file {}...\n",ctx->fd->docName());
8622 }
8623 StringVector filesInSameTu;
8624 ctx->fd->getAllIncludeFilesRecursively(filesInSameTu);
8625 if (ctx->generateSourceFile) // sources need to be shown in the output
8626 {
8627 ctx->fd->writeSourceHeader(ctx->ol);
8628 ctx->fd->writeSourceBody(ctx->ol,nullptr);
8629 ctx->fd->writeSourceFooter(ctx->ol);
8630 }
8631 else if (!ctx->fd->isReference() && Doxygen::parseSourcesNeeded)
8632 // we needed to parse the sources even if we do not show them
8633 {
8634 ctx->fd->parseSource(nullptr);
8635 }
8636 return ctx;
8637 };
8638 results.emplace_back(threadPool.queue(processFile));
8639 }
8640 }
8641 for (auto &f : results)
8642 {
8643 auto ctx = f.get();
8644 }
8645 }
8646 else // single threaded version
8647 {
8648 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8649 {
8650 for (const auto &fd : *fn)
8651 {
8652 StringVector filesInSameTu;
8653 fd->getAllIncludeFilesRecursively(filesInSameTu);
8654 processSourceFile(fd.get(),*g_outputList,nullptr);
8655 }
8656 }
8657 }
8658 }
8659 }
8660}
8661
8662//----------------------------------------------------------------------------
8663
8664static void generateFileDocs()
8665{
8666 if (Index::instance().numDocumentedFiles()==0) return;
8667
8668 if (!Doxygen::inputNameLinkedMap->empty())
8669 {
8670 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8671 if (numThreads>1) // multi threaded processing
8672 {
8673 struct DocContext
8674 {
8675 DocContext(FileDef *fd_,const OutputList &ol_)
8676 : fd(fd_), ol(ol_) {}
8677 FileDef *fd;
8678 OutputList ol;
8679 };
8680 ThreadPool threadPool(numThreads);
8681 std::vector< std::future< std::shared_ptr<DocContext> > > results;
8682 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8683 {
8684 for (const auto &fd : *fn)
8685 {
8686 bool doc = fd->isLinkableInProject();
8687 if (doc)
8688 {
8689 auto ctx = std::make_shared<DocContext>(fd.get(),*g_outputList);
8690 auto processFile = [ctx]() {
8691 msg("Generating docs for file {}...\n",ctx->fd->docName());
8692 ctx->fd->writeDocumentation(ctx->ol);
8693 return ctx;
8694 };
8695 results.emplace_back(threadPool.queue(processFile));
8696 }
8697 }
8698 }
8699 for (auto &f : results)
8700 {
8701 auto ctx = f.get();
8702 }
8703 }
8704 else // single threaded processing
8705 {
8706 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8707 {
8708 for (const auto &fd : *fn)
8709 {
8710 bool doc = fd->isLinkableInProject();
8711 if (doc)
8712 {
8713 msg("Generating docs for file {}...\n",fd->docName());
8714 fd->writeDocumentation(*g_outputList);
8715 }
8716 }
8717 }
8718 }
8719 }
8720}
8721
8722//----------------------------------------------------------------------------
8723
8725{
8726 // add source references for class definitions
8727 for (const auto &cd : *Doxygen::classLinkedMap)
8728 {
8729 const FileDef *fd=cd->getBodyDef();
8730 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8731 {
8732 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),nullptr);
8733 }
8734 }
8735 // add source references for concept definitions
8736 for (const auto &cd : *Doxygen::conceptLinkedMap)
8737 {
8738 const FileDef *fd=cd->getBodyDef();
8739 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8740 {
8741 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),nullptr);
8742 }
8743 }
8744 // add source references for namespace definitions
8745 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8746 {
8747 const FileDef *fd=nd->getBodyDef();
8748 if (fd && nd->isLinkableInProject() && nd->getStartDefLine()!=-1)
8749 {
8750 const_cast<FileDef*>(fd)->addSourceRef(nd->getStartDefLine(),nd.get(),nullptr);
8751 }
8752 }
8753
8754 // add source references for member names
8755 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8756 {
8757 for (const auto &md : *mn)
8758 {
8759 //printf("class member %s: def=%s body=%d link?=%d\n",
8760 // qPrint(md->name()),
8761 // md->getBodyDef()?qPrint(md->getBodyDef()->name()):"<none>",
8762 // md->getStartBodyLine(),md->isLinkableInProject());
8763 const FileDef *fd=md->getBodyDef();
8764 if (fd &&
8765 md->getStartDefLine()!=-1 &&
8766 md->isLinkableInProject() &&
8768 )
8769 {
8770 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8771 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8772 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8773 }
8774 }
8775 }
8776 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8777 {
8778 for (const auto &md : *mn)
8779 {
8780 const FileDef *fd=md->getBodyDef();
8781 //printf("member %s body=[%d,%d] fd=%p link=%d parseSources=%d\n",
8782 // qPrint(md->name()),
8783 // md->getStartBodyLine(),md->getEndBodyLine(),fd,
8784 // md->isLinkableInProject(),
8785 // Doxygen::parseSourcesNeeded);
8786 if (fd &&
8787 md->getStartDefLine()!=-1 &&
8788 md->isLinkableInProject() &&
8790 )
8791 {
8792 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8793 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8794 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8795 }
8796 }
8797 }
8798}
8799
8800//----------------------------------------------------------------------------
8801
8802// add the macro definitions found during preprocessing as file members
8803static void buildDefineList()
8804{
8805 AUTO_TRACE();
8806 for (const auto &s : g_inputFiles)
8807 {
8808 auto it = Doxygen::macroDefinitions.find(s);
8810 {
8811 for (const auto &def : it->second)
8812 {
8813 auto md = createMemberDef(
8814 def.fileName,def.lineNr,def.columnNr,
8815 "#define",def.name,def.args,QCString(),
8816 Protection::Public,Specifier::Normal,FALSE,Relationship::Member,MemberType::Define,
8817 ArgumentList(),ArgumentList(),"");
8818 auto mmd = toMemberDefMutable(md.get());
8819
8820 if (!def.args.isEmpty())
8821 {
8822 mmd->moveArgumentList(stringToArgumentList(SrcLangExt::Cpp, def.args));
8823 }
8824 mmd->setInitializer(def.definition);
8825 mmd->setFileDef(def.fileDef);
8826 mmd->setDefinition("#define "+def.name);
8827
8828 MemberName *mn=Doxygen::functionNameLinkedMap->add(def.name);
8829 if (def.fileDef)
8830 {
8831 const MemberList *defMl = def.fileDef->getMemberList(MemberListType::DocDefineMembers());
8832 if (defMl)
8833 {
8834 const MemberDef *defMd = defMl->findRev(def.name);
8835 if (defMd) // definition already stored
8836 {
8837 mmd->setRedefineCount(defMd->redefineCount()+1);
8838 }
8839 }
8840 def.fileDef->insertMember(md.get());
8841 }
8842 AUTO_TRACE_ADD("adding macro {} with definition {}",def.name,def.definition);
8843 mn->push_back(std::move(md));
8844 }
8845 }
8846 }
8847}
8848
8849//----------------------------------------------------------------------------
8850
8851static void sortMemberLists()
8852{
8853 // sort class member lists
8854 for (const auto &cd : *Doxygen::classLinkedMap)
8855 {
8856 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8857 if (cdm)
8858 {
8859 cdm->sortMemberLists();
8860 }
8861 }
8862
8863 // sort namespace member lists
8864 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8865 {
8867 if (ndm)
8868 {
8869 ndm->sortMemberLists();
8870 }
8871 }
8872
8873 // sort file member lists
8874 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8875 {
8876 for (const auto &fd : *fn)
8877 {
8878 fd->sortMemberLists();
8879 }
8880 }
8881
8882 // sort group member lists
8883 for (const auto &gd : *Doxygen::groupLinkedMap)
8884 {
8885 gd->sortMemberLists();
8886 }
8887
8889}
8890
8891//----------------------------------------------------------------------------
8892
8893static bool isSymbolHidden(const Definition *d)
8894{
8895 bool hidden = d->isHidden();
8896 const Definition *parent = d->getOuterScope();
8897 return parent ? hidden || isSymbolHidden(parent) : hidden;
8898}
8899
8901{
8902 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8903 if (numThreads>1)
8904 {
8905 ThreadPool threadPool(numThreads);
8906 std::vector < std::future< void > > results;
8907 // queue the work
8908 for (const auto &[name,symList] : *Doxygen::symbolMap)
8909 {
8910 for (const auto &def : symList)
8911 {
8913 if (dm && !isSymbolHidden(def) && !def->isArtificial() && def->isLinkableInProject())
8914 {
8915 auto processTooltip = [dm]() {
8916 dm->computeTooltip();
8917 };
8918 results.emplace_back(threadPool.queue(processTooltip));
8919 }
8920 }
8921 }
8922 // wait for the results
8923 for (auto &f : results)
8924 {
8925 f.get();
8926 }
8927 }
8928 else
8929 {
8930 for (const auto &[name,symList] : *Doxygen::symbolMap)
8931 {
8932 for (const auto &def : symList)
8933 {
8935 if (dm && !isSymbolHidden(def) && !def->isArtificial() && def->isLinkableInProject())
8936 {
8937 dm->computeTooltip();
8938 }
8939 }
8940 }
8941 }
8942}
8943
8944//----------------------------------------------------------------------------
8945
8947{
8948 for (const auto &cd : *Doxygen::classLinkedMap)
8949 {
8950 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8951 if (cdm)
8952 {
8953 cdm->setAnonymousEnumType();
8954 }
8955 }
8956}
8957
8958//----------------------------------------------------------------------------
8959
8960static void countMembers()
8961{
8962 for (const auto &cd : *Doxygen::classLinkedMap)
8963 {
8964 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8965 if (cdm)
8966 {
8967 cdm->countMembers();
8968 }
8969 }
8970
8971 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8972 {
8974 if (ndm)
8975 {
8976 ndm->countMembers();
8977 }
8978 }
8979
8980 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8981 {
8982 for (const auto &fd : *fn)
8983 {
8984 fd->countMembers();
8985 }
8986 }
8987
8988 for (const auto &gd : *Doxygen::groupLinkedMap)
8989 {
8990 gd->countMembers();
8991 }
8992
8993 auto &mm = ModuleManager::instance();
8994 mm.countMembers();
8995}
8996
8997
8998//----------------------------------------------------------------------------
8999// generate the documentation for all classes
9000
9001static void generateDocsForClassList(const std::vector<ClassDefMutable*> &classList)
9002{
9003 AUTO_TRACE();
9004 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9005 if (numThreads>1) // multi threaded processing
9006 {
9007 struct DocContext
9008 {
9009 DocContext(ClassDefMutable *cd_,const OutputList &ol_)
9010 : cd(cd_), ol(ol_) {}
9011 ClassDefMutable *cd;
9012 OutputList ol;
9013 };
9014 ThreadPool threadPool(numThreads);
9015 std::vector< std::future< std::shared_ptr<DocContext> > > results;
9016 for (const auto &cd : classList)
9017 {
9018 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
9019 if (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9020 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
9021 )
9022 {
9023 auto ctx = std::make_shared<DocContext>(cd,*g_outputList);
9024 auto processFile = [ctx]()
9025 {
9026 msg("Generating docs for compound {}...\n",ctx->cd->displayName());
9027
9028 // skip external references, anonymous compounds and
9029 // template instances
9030 if (!ctx->cd->isHidden() && !ctx->cd->isEmbeddedInOuterScope() &&
9031 ctx->cd->isLinkableInProject() && !ctx->cd->isImplicitTemplateInstance())
9032 {
9033 ctx->cd->writeDocumentation(ctx->ol);
9034 ctx->cd->writeMemberList(ctx->ol);
9035 }
9036
9037 // even for undocumented classes, the inner classes can be documented.
9038 ctx->cd->writeDocumentationForInnerClasses(ctx->ol);
9039 return ctx;
9040 };
9041 results.emplace_back(threadPool.queue(processFile));
9042 }
9043 }
9044 for (auto &f : results)
9045 {
9046 auto ctx = f.get();
9047 }
9048 }
9049 else // single threaded processing
9050 {
9051 for (const auto &cd : classList)
9052 {
9053 //printf("cd=%s getOuterScope=%p global=%p hidden=%d embeddedInOuterScope=%d\n",
9054 // qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope,cd->isHidden(),cd->isEmbeddedInOuterScope());
9055 if (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9056 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
9057 )
9058 {
9059 // skip external references, anonymous compounds and
9060 // template instances
9061 if ( !cd->isHidden() && !cd->isEmbeddedInOuterScope() &&
9062 cd->isLinkableInProject() && !cd->isImplicitTemplateInstance())
9063 {
9064 msg("Generating docs for compound {}...\n",cd->displayName());
9065
9066 cd->writeDocumentation(*g_outputList);
9067 cd->writeMemberList(*g_outputList);
9068 }
9069 // even for undocumented classes, the inner classes can be documented.
9070 cd->writeDocumentationForInnerClasses(*g_outputList);
9071 }
9072 }
9073 }
9074}
9075
9076static void addClassAndNestedClasses(std::vector<ClassDefMutable*> &list,ClassDefMutable *cd)
9077{
9078 list.push_back(cd);
9079 for (const auto &innerCdi : cd->getClasses())
9080 {
9081 ClassDefMutable *innerCd = toClassDefMutable(innerCdi);
9082 if (innerCd)
9083 {
9084 AUTO_TRACE("innerCd={} isLinkable={} isImplicitTemplateInstance={} protectLevelVisible={} embeddedInOuterScope={}",
9085 innerCd->name(),innerCd->isLinkableInProject(),innerCd->isImplicitTemplateInstance(),protectionLevelVisible(innerCd->protection()),
9086 innerCd->isEmbeddedInOuterScope());
9087 }
9088 if (innerCd && innerCd->isLinkableInProject() && !innerCd->isImplicitTemplateInstance() &&
9089 protectionLevelVisible(innerCd->protection()) &&
9090 !innerCd->isEmbeddedInOuterScope()
9091 )
9092 {
9093 list.push_back(innerCd);
9094 addClassAndNestedClasses(list,innerCd);
9095 }
9096 }
9097}
9098
9100{
9101 std::vector<ClassDefMutable*> classList;
9102 for (const auto &cdi : *Doxygen::classLinkedMap)
9103 {
9104 ClassDefMutable *cd = toClassDefMutable(cdi.get());
9105 if (cd && (cd->getOuterScope()==nullptr ||
9107 {
9108 addClassAndNestedClasses(classList,cd);
9109 }
9110 }
9111 for (const auto &cdi : *Doxygen::hiddenClassLinkedMap)
9112 {
9113 ClassDefMutable *cd = toClassDefMutable(cdi.get());
9114 if (cd && (cd->getOuterScope()==nullptr ||
9116 {
9117 addClassAndNestedClasses(classList,cd);
9118 }
9119 }
9120 generateDocsForClassList(classList);
9121}
9122
9123//----------------------------------------------------------------------------
9124
9126{
9127 for (const auto &cdi : *Doxygen::conceptLinkedMap)
9128 {
9130
9131 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
9132 if (cd &&
9133 (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9134 cd->getOuterScope()==Doxygen::globalScope // only look at global concepts
9135 ) && !cd->isHidden() && cd->isLinkableInProject()
9136 )
9137 {
9138 msg("Generating docs for concept {}...\n",cd->displayName());
9140 }
9141 }
9142}
9143
9144//----------------------------------------------------------------------------
9145
9147{
9148 for (const auto &mn : *Doxygen::memberNameLinkedMap)
9149 {
9150 for (const auto &imd : *mn)
9151 {
9152 MemberDefMutable *md = toMemberDefMutable(imd.get());
9153 //static int count=0;
9154 //printf("%04d Member '%s'\n",count++,qPrint(md->qualifiedName()));
9155 if (md && md->documentation().isEmpty() && md->briefDescription().isEmpty())
9156 { // no documentation yet
9157 const MemberDef *bmd = md->reimplements();
9158 while (bmd && bmd->documentation().isEmpty() &&
9159 bmd->briefDescription().isEmpty()
9160 )
9161 { // search up the inheritance tree for a documentation member
9162 //printf("bmd=%s class=%s\n",qPrint(bmd->name()),qPrint(bmd->getClassDef()->name()));
9163 bmd = bmd->reimplements();
9164 }
9165 if (bmd) // copy the documentation from the reimplemented member
9166 {
9167 md->setInheritsDocsFrom(bmd);
9168 md->setDocumentation(bmd->documentation(),bmd->docFile(),bmd->docLine());
9170 md->setBriefDescription(bmd->briefDescription(),bmd->briefFile(),bmd->briefLine());
9171 md->copyArgumentNames(bmd);
9173 }
9174 }
9175 }
9176 }
9177}
9178
9179//----------------------------------------------------------------------------
9180
9182{
9183 // for each file
9184 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9185 {
9186 for (const auto &fd : *fn)
9187 {
9188 fd->combineUsingRelations();
9189 }
9190 }
9191
9192 // for each namespace
9193 NamespaceDefSet visitedNamespaces;
9194 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9195 {
9197 if (ndm)
9198 {
9199 ndm->combineUsingRelations(visitedNamespaces);
9200 }
9201 }
9202}
9203
9204//----------------------------------------------------------------------------
9205
9207{
9208 // for each class
9209 for (const auto &cd : *Doxygen::classLinkedMap)
9210 {
9211 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9212 if (cdm)
9213 {
9215 }
9216 }
9217 // for each file
9218 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9219 {
9220 for (const auto &fd : *fn)
9221 {
9222 fd->addMembersToMemberGroup();
9223 }
9224 }
9225 // for each namespace
9226 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9227 {
9229 if (ndm)
9230 {
9232 }
9233 }
9234 // for each group
9235 for (const auto &gd : *Doxygen::groupLinkedMap)
9236 {
9237 gd->addMembersToMemberGroup();
9238 }
9240}
9241
9242//----------------------------------------------------------------------------
9243
9245{
9246 // for each class
9247 for (const auto &cd : *Doxygen::classLinkedMap)
9248 {
9249 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9250 if (cdm)
9251 {
9253 }
9254 }
9255 // for each file
9256 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9257 {
9258 for (const auto &fd : *fn)
9259 {
9260 fd->distributeMemberGroupDocumentation();
9261 }
9262 }
9263 // for each namespace
9264 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9265 {
9267 if (ndm)
9268 {
9270 }
9271 }
9272 // for each group
9273 for (const auto &gd : *Doxygen::groupLinkedMap)
9274 {
9275 gd->distributeMemberGroupDocumentation();
9276 }
9278}
9279
9280//----------------------------------------------------------------------------
9281
9283{
9284 // for each class
9285 for (const auto &cd : *Doxygen::classLinkedMap)
9286 {
9287 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9288 if (cdm)
9289 {
9291 }
9292 }
9293 // for each concept
9294 for (const auto &cd : *Doxygen::conceptLinkedMap)
9295 {
9296 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
9297 if (cdm)
9298 {
9300 }
9301 }
9302 // for each file
9303 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9304 {
9305 for (const auto &fd : *fn)
9306 {
9307 fd->findSectionsInDocumentation();
9308 }
9309 }
9310 // for each namespace
9311 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9312 {
9314 if (ndm)
9315 {
9317 }
9318 }
9319 // for each group
9320 for (const auto &gd : *Doxygen::groupLinkedMap)
9321 {
9322 gd->findSectionsInDocumentation();
9323 }
9324 // for each page
9325 for (const auto &pd : *Doxygen::pageLinkedMap)
9326 {
9327 pd->findSectionsInDocumentation();
9328 }
9329 // for each directory
9330 for (const auto &dd : *Doxygen::dirLinkedMap)
9331 {
9332 dd->findSectionsInDocumentation();
9333 }
9335 if (Doxygen::mainPage) Doxygen::mainPage->findSectionsInDocumentation();
9336}
9337
9338//----------------------------------------------------------------------
9339
9340
9342{
9343 // remove all references to classes from the cache
9344 // as there can be new template instances in the inheritance path
9345 // to this class. Optimization: only remove those classes that
9346 // have inheritance instances as direct or indirect sub classes.
9347 StringVector elementsToRemove;
9348 for (const auto &ci : *Doxygen::typeLookupCache)
9349 {
9350 const LookupInfo &li = ci.second;
9351 if (li.definition)
9352 {
9353 elementsToRemove.push_back(ci.first);
9354 }
9355 }
9356 for (const auto &k : elementsToRemove)
9357 {
9358 Doxygen::typeLookupCache->remove(k);
9359 }
9360
9361 // remove all cached typedef resolutions whose target is a
9362 // template class as this may now be a template instance
9363 // for each global function name
9364 for (const auto &fn : *Doxygen::functionNameLinkedMap)
9365 {
9366 // for each function with that name
9367 for (const auto &ifmd : *fn)
9368 {
9369 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
9370 if (fmd && fmd->isTypedefValCached())
9371 {
9372 const ClassDef *cd = fmd->getCachedTypedefVal();
9373 if (cd->isTemplate()) fmd->invalidateTypedefValCache();
9374 }
9375 }
9376 }
9377 // for each class method name
9378 for (const auto &nm : *Doxygen::memberNameLinkedMap)
9379 {
9380 // for each function with that name
9381 for (const auto &imd : *nm)
9382 {
9383 MemberDefMutable *md = toMemberDefMutable(imd.get());
9384 if (md && md->isTypedefValCached())
9385 {
9386 const ClassDef *cd = md->getCachedTypedefVal();
9387 if (cd->isTemplate()) md->invalidateTypedefValCache();
9388 }
9389 }
9390 }
9391}
9392
9393//----------------------------------------------------------------------------
9394
9396{
9397 // Remove all unresolved references to classes from the cache.
9398 // This is needed before resolving the inheritance relations, since
9399 // it would otherwise not find the inheritance relation
9400 // for C in the example below, as B::I was already found to be unresolvable
9401 // (which is correct if you ignore the inheritance relation between A and B).
9402 //
9403 // class A { class I {} };
9404 // class B : public A {};
9405 // class C : public B::I {};
9406
9407 StringVector elementsToRemove;
9408 for (const auto &ci : *Doxygen::typeLookupCache)
9409 {
9410 const LookupInfo &li = ci.second;
9411 if (li.definition==nullptr && li.typeDef==nullptr)
9412 {
9413 elementsToRemove.push_back(ci.first);
9414 }
9415 }
9416 for (const auto &k : elementsToRemove)
9417 {
9418 Doxygen::typeLookupCache->remove(k);
9419 }
9420
9421 // for each global function name
9422 for (const auto &fn : *Doxygen::functionNameLinkedMap)
9423 {
9424 // for each function with that name
9425 for (const auto &ifmd : *fn)
9426 {
9427 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
9428 if (fmd)
9429 {
9431 }
9432 }
9433 }
9434 // for each class method name
9435 for (const auto &nm : *Doxygen::memberNameLinkedMap)
9436 {
9437 // for each function with that name
9438 for (const auto &imd : *nm)
9439 {
9440 MemberDefMutable *md = toMemberDefMutable(imd.get());
9441 if (md)
9442 {
9444 }
9445 }
9446 }
9447
9448}
9449
9450//----------------------------------------------------------------------------
9451// Returns TRUE if the entry and member definition have equal file names,
9452// otherwise FALSE.
9453
9454static bool haveEqualFileNames(const Entry *root,const MemberDef *md)
9455{
9456 const FileDef *fd = md->getFileDef();
9457 if (!fd)
9458 {
9459 return FALSE;
9460 }
9461
9462 return fd->absFilePath() == root->fileName;
9463}
9464
9465//----------------------------------------------------------------------------
9466
9467static void addDefineDoc(const Entry *root, MemberDefMutable *md)
9468{
9469 md->setDocumentation(root->doc,root->docFile,root->docLine);
9470 md->setDocsForDefinition(!root->proto);
9471 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9472 if (md->inbodyDocumentation().isEmpty())
9473 {
9475 }
9476 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
9477 {
9478 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
9479 md->setBodyDef(root->fileDef());
9480 }
9482 md->setMaxInitLines(root->initLines);
9484 md->setRefItems(root->sli);
9485 md->addQualifiers(root->qualifiers);
9486 if (root->mGrpId!=-1) md->setMemberGroupId(root->mGrpId);
9487 addMemberToGroups(root,md);
9489}
9490
9491//----------------------------------------------------------------------------
9492
9494{
9495 if ((root->section.isDefineDoc() || root->section.isDefine()) && !root->name.isEmpty())
9496 {
9497 //printf("found define '%s' '%s' brief='%s' doc='%s'\n",
9498 // qPrint(root->name),qPrint(root->args),qPrint(root->brief),qPrint(root->doc));
9499
9500 if (root->tagInfo() && !root->name.isEmpty()) // define read from a tag file
9501 {
9502 auto md = createMemberDef(root->tagInfo()->tagName,1,1,
9503 "#define",root->name,root->args,QCString(),
9504 Protection::Public,Specifier::Normal,FALSE,Relationship::Member,MemberType::Define,
9505 ArgumentList(),ArgumentList(),"");
9506 auto mmd = toMemberDefMutable(md.get());
9507 mmd->setTagInfo(root->tagInfo());
9508 mmd->setLanguage(root->lang);
9509 mmd->addQualifiers(root->qualifiers);
9510 //printf("Searching for '%s' fd=%p\n",qPrint(filePathName),fd);
9511 mmd->setFileDef(root->parent()->fileDef());
9512 //printf("Adding member=%s\n",qPrint(md->name()));
9514 mn->push_back(std::move(md));
9515 }
9517 if (mn)
9518 {
9519 int count=0;
9520 for (const auto &md : *mn)
9521 {
9522 if (md->memberType()==MemberType::Define) count++;
9523 }
9524 if (count==1)
9525 {
9526 for (const auto &imd : *mn)
9527 {
9528 MemberDefMutable *md = toMemberDefMutable(imd.get());
9529 if (md && md->memberType()==MemberType::Define)
9530 {
9531 addDefineDoc(root,md);
9532 }
9533 }
9534 }
9535 else if (count>1 &&
9536 (!root->doc.isEmpty() ||
9537 !root->brief.isEmpty() ||
9538 root->bodyLine!=-1
9539 )
9540 )
9541 // multiple defines don't know where to add docs
9542 // but maybe they are in different files together with their documentation
9543 {
9544 for (const auto &imd : *mn)
9545 {
9546 MemberDefMutable *md = toMemberDefMutable(imd.get());
9547 if (md && md->memberType()==MemberType::Define)
9548 {
9549 if (haveEqualFileNames(root, md) || isEntryInGroupOfMember(root, md))
9550 // doc and define in the same file or group assume they belong together.
9551 {
9552 addDefineDoc(root,md);
9553 }
9554 }
9555 }
9556 //warn("define {} found in the following files:\n",root->name);
9557 //warn("Cannot determine where to add the documentation found "
9558 // "at line {} of file {}. \n",
9559 // root->startLine,root->fileName);
9560 }
9561 }
9562 else if (!root->doc.isEmpty() || !root->brief.isEmpty()) // define not found
9563 {
9564 bool preEnabled = Config_getBool(ENABLE_PREPROCESSING);
9565 if (preEnabled)
9566 {
9567 warn(root->fileName,root->startLine,"documentation for unknown define {} found.",root->name);
9568 }
9569 else
9570 {
9571 warn(root->fileName,root->startLine, "found documented #define {} but ignoring it because ENABLE_PREPROCESSING is NO.", root->name);
9572 }
9573 }
9574 }
9575 for (const auto &e : root->children()) findDefineDocumentation(e.get());
9576}
9577
9578//----------------------------------------------------------------------------
9579
9580static void findDirDocumentation(const Entry *root)
9581{
9582 if (root->section.isDirDoc())
9583 {
9584 QCString normalizedName = root->name;
9585 normalizedName = substitute(normalizedName,"\\","/");
9586 //printf("root->docFile=%s normalizedName=%s\n",
9587 // qPrint(root->docFile),qPrint(normalizedName));
9588 if (root->docFile==normalizedName) // current dir?
9589 {
9590 int lastSlashPos=normalizedName.findRev('/');
9591 if (lastSlashPos!=-1) // strip file name
9592 {
9593 normalizedName=normalizedName.left(lastSlashPos);
9594 }
9595 }
9596 if (normalizedName.at(normalizedName.length()-1)!='/')
9597 {
9598 normalizedName+='/';
9599 }
9600 DirDef *matchingDir=nullptr;
9601 for (const auto &dir : *Doxygen::dirLinkedMap)
9602 {
9603 //printf("Dir: %s<->%s\n",qPrint(dir->name()),qPrint(normalizedName));
9604 if (dir->name().right(normalizedName.length())==normalizedName)
9605 {
9606 if (matchingDir)
9607 {
9608 warn(root->fileName,root->startLine,
9609 "\\dir command matches multiple directories.\n"
9610 " Applying the command for directory {}\n"
9611 " Ignoring the command for directory {}",
9612 matchingDir->name(),dir->name()
9613 );
9614 }
9615 else
9616 {
9617 matchingDir=dir.get();
9618 }
9619 }
9620 }
9621 if (matchingDir)
9622 {
9623 //printf("Match for with dir %s #anchor=%zu\n",qPrint(matchingDir->name()),root->anchors.size());
9624 matchingDir->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9625 matchingDir->setDocumentation(root->doc,root->docFile,root->docLine);
9626 matchingDir->setRefItems(root->sli);
9627 matchingDir->addSectionsToDefinition(root->anchors);
9628 root->commandOverrides.apply_directoryGraph([&](bool b) { matchingDir->overrideDirectoryGraph(b); });
9629 addDirToGroups(root,matchingDir);
9630 }
9631 else
9632 {
9633 warn(root->fileName,root->startLine,"No matching directory found for command \\dir {}",normalizedName);
9634 }
9635 }
9636 for (const auto &e : root->children()) findDirDocumentation(e.get());
9637}
9638
9639
9640//----------------------------------------------------------------------------
9641// create a (sorted) list of separate documentation pages
9642
9643static void buildPageList(Entry *root)
9644{
9645 if (root->section.isPageDoc())
9646 {
9647 if (!root->name.isEmpty())
9648 {
9649 addRelatedPage(root);
9650 }
9651 }
9652 else if (root->section.isMainpageDoc())
9653 {
9654 QCString title=root->args.stripWhiteSpace();
9655 if (title.isEmpty()) title=theTranslator->trMainPage();
9656 //QCString name = Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9657 QCString name = "index";
9658 addRefItem(root->sli,
9659 name,
9660 "page",
9661 name,
9662 title,
9663 QCString(),nullptr
9664 );
9665 }
9666 for (const auto &e : root->children()) buildPageList(e.get());
9667}
9668
9669// search for the main page defined in this project
9670static void findMainPage(Entry *root)
9671{
9672 if (root->section.isMainpageDoc())
9673 {
9674 if (Doxygen::mainPage==nullptr && root->tagInfo()==nullptr)
9675 {
9676 //printf("mainpage: docLine=%d startLine=%d\n",root->docLine,root->startLine);
9677 //printf("Found main page! \n======\n%s\n=======\n",qPrint(root->doc));
9678 QCString title=root->args.stripWhiteSpace();
9679 if (title.isEmpty()) title = Config_getString(PROJECT_NAME);
9680 //QCString indexName=Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9681 QCString indexName="index";
9683 indexName, root->brief+root->doc+root->inbodyDocs,title);
9684 //setFileNameForSections(root->anchors,"index",Doxygen::mainPage);
9685 Doxygen::mainPage->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9686 Doxygen::mainPage->setBodySegment(root->startLine,root->startLine,-1);
9687 Doxygen::mainPage->setFileName(indexName);
9688 Doxygen::mainPage->setLocalToc(root->localToc);
9690
9692 if (si)
9693 {
9694 if (!si->ref().isEmpty()) // we are from a tag file
9695 {
9696 // a page name is a label as well! but should no be double either
9698 Doxygen::mainPage->name(),
9699 indexName,
9700 root->startLine,
9701 Doxygen::mainPage->title(),
9703 0); // level 0
9704 }
9705 else if (si->lineNr() != -1)
9706 {
9707 warn(root->fileName,root->startLine,"multiple use of section label '{}' for main page, (first occurrence: {}, line {})",
9708 Doxygen::mainPage->name(),si->fileName(),si->lineNr());
9709 }
9710 else
9711 {
9712 warn(root->fileName,root->startLine,"multiple use of section label '{}' for main page, (first occurrence: {})",
9713 Doxygen::mainPage->name(),si->fileName());
9714 }
9715 }
9716 else
9717 {
9718 // a page name is a label as well! but should no be double either
9720 Doxygen::mainPage->name(),
9721 indexName,
9722 root->startLine,
9723 Doxygen::mainPage->title(),
9725 0); // level 0
9726 }
9727 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9728 }
9729 else if (root->tagInfo()==nullptr)
9730 {
9731 warn(root->fileName,root->startLine,
9732 "found more than one \\mainpage comment block! (first occurrence: {}, line {}), Skipping current block!",
9733 Doxygen::mainPage->docFile(),Doxygen::mainPage->getStartBodyLine());
9734 }
9735 }
9736 for (const auto &e : root->children()) findMainPage(e.get());
9737}
9738
9739// search for the main page imported via tag files and add only the section labels
9740static void findMainPageTagFiles(Entry *root)
9741{
9742 if (root->section.isMainpageDoc())
9743 {
9744 if (Doxygen::mainPage && root->tagInfo())
9745 {
9746 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9747 }
9748 }
9749 for (const auto &e : root->children()) findMainPageTagFiles(e.get());
9750}
9751
9752static void computePageRelations(Entry *root)
9753{
9754 if ((root->section.isPageDoc() || root->section.isMainpageDoc()) && !root->name.isEmpty())
9755 {
9756 PageDef *pd = root->section.isPageDoc() ?
9757 Doxygen::pageLinkedMap->find(root->name) :
9758 Doxygen::mainPage.get();
9759 if (pd)
9760 {
9761 for (const BaseInfo &bi : root->extends)
9762 {
9763 PageDef *subPd = Doxygen::pageLinkedMap->find(bi.name);
9764 if (pd==subPd)
9765 {
9766 term("page defined {} with label {} is a direct "
9767 "subpage of itself! Please remove this cyclic dependency.\n",
9768 warn_line(pd->docFile(),pd->docLine()),pd->name());
9769 }
9770 else if (subPd)
9771 {
9772 pd->addInnerCompound(subPd);
9773 //printf("*** Added subpage relation: %s->%s\n",
9774 // qPrint(pd->name()),qPrint(subPd->name()));
9775 }
9776 }
9777 }
9778 }
9779 for (const auto &e : root->children()) computePageRelations(e.get());
9780}
9781
9783{
9784 for (const auto &pd : *Doxygen::pageLinkedMap)
9785 {
9786 Definition *ppd = pd->getOuterScope();
9787 while (ppd)
9788 {
9789 if (ppd==pd.get())
9790 {
9791 term("page defined {} with label {} is a subpage "
9792 "of itself! Please remove this cyclic dependency.\n",
9793 warn_line(pd->docFile(),pd->docLine()),pd->name());
9794 }
9795 ppd=ppd->getOuterScope();
9796 }
9797 }
9798}
9799
9800//----------------------------------------------------------------------------
9801
9803{
9804 for (const auto &si : SectionManager::instance())
9805 {
9806 //printf("si->label='%s' si->definition=%s si->fileName='%s'\n",
9807 // qPrint(si->label),si->definition?qPrint(si->definition->name()):"<none>",
9808 // qPrint(si->fileName));
9809 PageDef *pd=nullptr;
9810
9811 // hack: the items of a todo/test/bug/deprecated list are all fragments from
9812 // different files, so the resulting section's all have the wrong file
9813 // name (not from the todo/test/bug/deprecated list, but from the file in
9814 // which they are defined). We correct this here by looking at the
9815 // generated section labels!
9817 {
9818 QCString label="_"+rl->listName(); // "_todo", "_test", ...
9819 if (si->label().left(label.length())==label)
9820 {
9821 si->setFileName(rl->listName());
9822 si->setGenerated(TRUE);
9823 break;
9824 }
9825 }
9826
9827 //printf("start: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
9828 if (!si->generated())
9829 {
9830 // if this section is in a page and the page is in a group, then we
9831 // have to adjust the link file name to point to the group.
9832 if (!si->fileName().isEmpty() &&
9833 (pd=Doxygen::pageLinkedMap->find(si->fileName())) &&
9834 pd->getGroupDef())
9835 {
9836 si->setFileName(pd->getGroupDef()->getOutputFileBase());
9837 }
9838
9839 if (si->definition())
9840 {
9841 // TODO: there should be one function in Definition that returns
9842 // the file to link to, so we can avoid the following tests.
9843 const GroupDef *gd=nullptr;
9844 if (si->definition()->definitionType()==Definition::TypeMember)
9845 {
9846 gd = (toMemberDef(si->definition()))->getGroupDef();
9847 }
9848
9849 if (gd)
9850 {
9851 si->setFileName(gd->getOutputFileBase());
9852 }
9853 else
9854 {
9855 //si->fileName=si->definition->getOutputFileBase();
9856 //printf("Setting si->fileName to %s\n",qPrint(si->fileName));
9857 }
9858 }
9859 }
9860 //printf("end: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
9861 }
9862}
9863
9864
9865
9866//----------------------------------------------------------------------------
9867// generate all separate documentation pages
9868
9869
9870static void generatePageDocs()
9871{
9872 //printf("documentedPages=%d real=%d\n",documentedPages,Doxygen::pageLinkedMap->count());
9873 if (Index::instance().numDocumentedPages()==0) return;
9874 for (const auto &pd : *Doxygen::pageLinkedMap)
9875 {
9876 if (!pd->getGroupDef() && !pd->isReference())
9877 {
9878 msg("Generating docs for page {}...\n",pd->name());
9879 pd->writeDocumentation(*g_outputList);
9880 }
9881 }
9882}
9883
9884//----------------------------------------------------------------------------
9885// create a (sorted) list & dictionary of example pages
9886
9887static void buildExampleList(Entry *root)
9888{
9889 if ((root->section.isExample() || root->section.isExampleLineno()) && !root->name.isEmpty())
9890 {
9891 if (Doxygen::exampleLinkedMap->find(root->name))
9892 {
9893 warn(root->fileName,root->startLine,"Example {} was already documented. Ignoring documentation found here.",root->name);
9894 }
9895 else
9896 {
9897 PageDef *pd = Doxygen::exampleLinkedMap->add(root->name,
9898 createPageDef(root->fileName,root->startLine,
9899 root->name,root->brief+root->doc+root->inbodyDocs,root->args));
9900 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9901 pd->setFileName(convertNameToFile(pd->name()+"-example",FALSE,TRUE));
9903 pd->setLanguage(root->lang);
9904 pd->setShowLineNo(root->section.isExampleLineno());
9905
9906 //we don't add example to groups
9907 //addExampleToGroups(root,pd);
9908 }
9909 }
9910 for (const auto &e : root->children()) buildExampleList(e.get());
9911}
9912
9913//----------------------------------------------------------------------------
9914// prints the Entry tree (for debugging)
9915
9916void printNavTree(Entry *root,int indent)
9917{
9919 {
9920 QCString indentStr;
9921 indentStr.fill(' ',indent);
9922 Debug::print(Debug::Entries,0,"{}{} at {}:{} (sec={}, spec={})\n",
9923 indentStr.isEmpty()?"":indentStr,
9924 root->name.isEmpty()?"<empty>":root->name,
9925 root->fileName,root->startLine,
9926 root->section.to_string(),
9927 root->spec.to_string());
9928 for (const auto &e : root->children())
9929 {
9930 printNavTree(e.get(),indent+2);
9931 }
9932 }
9933}
9934
9935
9936//----------------------------------------------------------------------------
9937// prints the Sections tree (for debugging)
9938
9940{
9942 {
9943 for (const auto &si : SectionManager::instance())
9944 {
9945 Debug::print(Debug::Sections,0,"Section = {}, file = {}, title = {}, type = {}, ref = {}\n",
9946 si->label(),si->fileName(),si->title(),si->type().level(),si->ref());
9947 }
9948 }
9949}
9950
9951
9952//----------------------------------------------------------------------------
9953// generate the example documentation
9954
9956{
9957 g_outputList->disable(OutputType::Man);
9958 for (const auto &pd : *Doxygen::exampleLinkedMap)
9959 {
9960 msg("Generating docs for example {}...\n",pd->name());
9961 SrcLangExt lang = getLanguageFromFileName(pd->name(), SrcLangExt::Unknown);
9962 if (lang != SrcLangExt::Unknown)
9963 {
9964 QCString ext = getFileNameExtension(pd->name());
9965 auto intf = Doxygen::parserManager->getCodeParser(ext);
9966 intf->resetCodeParserState();
9967 }
9968 QCString n=pd->getOutputFileBase();
9969 startFile(*g_outputList,n,n,pd->name());
9971 g_outputList->docify(pd->name());
9973 g_outputList->startContents();
9974 QCString lineNoOptStr;
9975 if (pd->showLineNo())
9976 {
9977 lineNoOptStr="{lineno}";
9978 }
9979 g_outputList->generateDoc(pd->docFile(), // file
9980 pd->docLine(), // startLine
9981 pd.get(), // context
9982 nullptr, // memberDef
9983 (pd->briefDescription().isEmpty()?"":pd->briefDescription()+"\n\n")+
9984 pd->documentation()+"\n\n\\include"+lineNoOptStr+" "+pd->name(), // docs
9985 TRUE, // index words
9986 TRUE, // is example
9987 pd->name(),
9988 FALSE,
9989 FALSE
9990 );
9991 endFile(*g_outputList); // contains g_outputList->endContents()
9992 }
9994}
9995
9996//----------------------------------------------------------------------------
9997// generate module pages
9998
10000{
10001 for (const auto &gd : *Doxygen::groupLinkedMap)
10002 {
10003 if (!gd->isReference())
10004 {
10005 gd->writeDocumentation(*g_outputList);
10006 }
10007 }
10008}
10009
10010//----------------------------------------------------------------------------
10011// generate module pages
10012
10014{
10015 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10016 if (numThreads>1) // multi threaded processing
10017 {
10018 struct DocContext
10019 {
10020 DocContext(ClassDefMutable *cdm_,const OutputList &ol_)
10021 : cdm(cdm_), ol(ol_) {}
10022 ClassDefMutable *cdm;
10023 OutputList ol;
10024 };
10025 ThreadPool threadPool(numThreads);
10026 std::vector< std::future< std::shared_ptr<DocContext> > > results;
10027 // for each class in the namespace...
10028 for (const auto &cd : classList)
10029 {
10031 if (cdm)
10032 {
10033 auto ctx = std::make_shared<DocContext>(cdm,*g_outputList);
10034 auto processFile = [ctx]()
10035 {
10036 if ( ( ctx->cdm->isLinkableInProject() &&
10037 !ctx->cdm->isImplicitTemplateInstance()
10038 ) // skip external references, anonymous compounds and
10039 // template instances and nested classes
10040 && !ctx->cdm->isHidden() && !ctx->cdm->isEmbeddedInOuterScope()
10041 )
10042 {
10043 msg("Generating docs for compound {}...\n",ctx->cdm->displayName());
10044 ctx->cdm->writeDocumentation(ctx->ol);
10045 ctx->cdm->writeMemberList(ctx->ol);
10046 }
10047 ctx->cdm->writeDocumentationForInnerClasses(ctx->ol);
10048 return ctx;
10049 };
10050 results.emplace_back(threadPool.queue(processFile));
10051 }
10052 }
10053 // wait for the results
10054 for (auto &f : results)
10055 {
10056 auto ctx = f.get();
10057 }
10058 }
10059 else // single threaded processing
10060 {
10061 // for each class in the namespace...
10062 for (const auto &cd : classList)
10063 {
10065 if (cdm)
10066 {
10067 if ( ( cd->isLinkableInProject() &&
10068 !cd->isImplicitTemplateInstance()
10069 ) // skip external references, anonymous compounds and
10070 // template instances and nested classes
10071 && !cd->isHidden() && !cd->isEmbeddedInOuterScope()
10072 )
10073 {
10074 msg("Generating docs for compound {}...\n",cd->displayName());
10075
10078 }
10080 }
10081 }
10082 }
10083}
10084
10086{
10087 // for each concept in the namespace...
10088 for (const auto &cd : conceptList)
10089 {
10091 if ( cdm && cd->isLinkableInProject() && !cd->isHidden())
10092 {
10093 msg("Generating docs for concept {}...\n",cd->name());
10095 }
10096 }
10097}
10098
10100{
10101 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
10102
10103 //writeNamespaceIndex(*g_outputList);
10104
10105 // for each namespace...
10106 for (const auto &nd : *Doxygen::namespaceLinkedMap)
10107 {
10108 if (nd->isLinkableInProject())
10109 {
10111 if (ndm)
10112 {
10113 msg("Generating docs for namespace {}\n",nd->displayName());
10115 }
10116 }
10117
10118 generateNamespaceClassDocs(nd->getClasses());
10119 if (sliceOpt)
10120 {
10121 generateNamespaceClassDocs(nd->getInterfaces());
10122 generateNamespaceClassDocs(nd->getStructs());
10123 generateNamespaceClassDocs(nd->getExceptions());
10124 }
10125 generateNamespaceConceptDocs(nd->getConcepts());
10126 }
10127}
10128
10130{
10131 std::string oldDir = Dir::currentDirPath();
10132 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
10135 {
10136 err("failed to run html help compiler on {}\n", HtmlHelp::hhpFileName);
10137 }
10138 Dir::setCurrent(oldDir);
10139}
10140
10142{
10143 QCString args = Qhp::qhpFileName + " -o \"" + Qhp::getQchFileName() + "\"";
10144 std::string oldDir = Dir::currentDirPath();
10145 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
10146
10147 QCString qhgLocation=Config_getString(QHG_LOCATION);
10148 if (Debug::isFlagSet(Debug::Qhp)) // produce info for debugging
10149 {
10150 // run qhelpgenerator -v and extract the Qt version used
10151 QCString cmd=qhgLocation+ " -v 2>&1";
10152 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
10153 FILE *f=Portable::popen(cmd,"r");
10154 if (!f)
10155 {
10156 err("could not execute {}\n",qhgLocation);
10157 }
10158 else
10159 {
10160 const size_t bufSize = 1024;
10161 char inBuf[bufSize+1];
10162 size_t numRead=fread(inBuf,1,bufSize,f);
10163 inBuf[numRead] = '\0';
10164 Debug::print(Debug::Qhp,0,"{}",inBuf);
10166
10167 int qtVersion=0;
10168 static const reg::Ex versionReg(R"(Qt (\d+)\.(\d+)\.(\d+))");
10169 reg::Match match;
10170 std::string s = inBuf;
10171 if (reg::search(inBuf,match,versionReg))
10172 {
10173 qtVersion = 10000*QCString(match[1].str()).toInt() +
10174 100*QCString(match[2].str()).toInt() +
10175 QCString(match[3].str()).toInt();
10176 }
10177 if (qtVersion>0 && (qtVersion<60000 || qtVersion >= 60205))
10178 {
10179 // dump the output of qhelpgenerator -c file.qhp
10180 // Qt<6 or Qt>=6.2.5 or higher, see https://bugreports.qt.io/browse/QTBUG-101070
10181 cmd=qhgLocation+ " -c " + Qhp::qhpFileName + " 2>&1";
10182 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
10183 f=Portable::popen(cmd,"r");
10184 if (!f)
10185 {
10186 err("could not execute {}\n",qhgLocation);
10187 }
10188 else
10189 {
10190 std::string output;
10191 while ((numRead=fread(inBuf,1,bufSize,f))>0)
10192 {
10193 inBuf[numRead] = '\0';
10194 output += inBuf;
10195 }
10197 Debug::print(Debug::Qhp,0,"{}",output);
10198 }
10199 }
10200 }
10201 }
10202
10203 if (Portable::system(qhgLocation, args, FALSE))
10204 {
10205 err("failed to run qhelpgenerator on {}\n",Qhp::qhpFileName);
10206 }
10207 Dir::setCurrent(oldDir);
10208}
10209
10210//----------------------------------------------------------------------------
10211
10213{
10214 // check dot path
10215 QCString dotPath = Config_getString(DOT_PATH);
10216 if (!dotPath.isEmpty())
10217 {
10218 FileInfo fi(dotPath.str());
10219 if (!(fi.exists() && fi.isFile()) )// not an existing user specified path + exec
10220 {
10221 dotPath = dotPath+"/dot"+Portable::commandExtension();
10222 FileInfo dp(dotPath.str());
10223 if (!dp.exists() || !dp.isFile())
10224 {
10225 warn_uncond("the dot tool could not be found as '{}'\n",dotPath);
10226 dotPath = "dot";
10227 dotPath += Portable::commandExtension();
10228 }
10229 }
10230#if defined(_WIN32) // convert slashes
10231 size_t l=dotPath.length();
10232 for (size_t i=0;i<l;i++) if (dotPath.at(i)=='/') dotPath.at(i)='\\';
10233#endif
10234 }
10235 else
10236 {
10237 dotPath = "dot";
10238 dotPath += Portable::commandExtension();
10239 }
10240 Doxygen::verifiedDotPath = dotPath;
10242}
10243
10244//----------------------------------------------------------------------------
10245
10246/*! Generate a template version of the configuration file.
10247 * If the \a shortList parameter is TRUE a configuration file without
10248 * comments will be generated.
10249 */
10250static void generateConfigFile(const QCString &configFile,bool shortList,
10251 bool updateOnly=FALSE)
10252{
10253 std::ofstream f;
10254 bool fileOpened=openOutputFile(configFile,f);
10255 bool writeToStdout=configFile=="-";
10256 if (fileOpened)
10257 {
10258 TextStream t(&f);
10259 Config::writeTemplate(t,shortList,updateOnly);
10260 if (!writeToStdout)
10261 {
10262 if (!updateOnly)
10263 {
10264 msg("\n\nConfiguration file '{}' created.\n\n",configFile);
10265 msg("Now edit the configuration file and enter\n\n");
10266 if (configFile!="Doxyfile" && configFile!="doxyfile")
10267 msg(" doxygen {}\n\n",configFile);
10268 else
10269 msg(" doxygen\n\n");
10270 msg("to generate the documentation for your project\n\n");
10271 }
10272 else
10273 {
10274 msg("\n\nConfiguration file '{}' updated.\n\n",configFile);
10275 }
10276 }
10277 }
10278 else
10279 {
10280 term("Cannot open file {} for writing\n",configFile);
10281 }
10282}
10283
10285{
10286 std::ofstream f;
10287 bool fileOpened=openOutputFile("-",f);
10288 if (fileOpened)
10289 {
10290 TextStream t(&f);
10291 Config::compareDoxyfile(t,diffList);
10292 }
10293 else
10294 {
10295 term("Cannot open stdout for writing\n");
10296 }
10297}
10298
10299//----------------------------------------------------------------------------
10300// read and parse a tag file
10301
10302static void readTagFile(const std::shared_ptr<Entry> &root,const QCString &tagLine)
10303{
10304 QCString fileName;
10305 QCString destName;
10306 int eqPos = tagLine.find('=');
10307 if (eqPos!=-1) // tag command contains a destination
10308 {
10309 fileName = tagLine.left(eqPos).stripWhiteSpace();
10310 destName = tagLine.right(tagLine.length()-eqPos-1).stripWhiteSpace();
10311 if (fileName.isEmpty() || destName.isEmpty()) return;
10312 //printf("insert tagDestination %s->%s\n",qPrint(fi.fileName()),qPrint(destName));
10313 }
10314 else
10315 {
10316 fileName = tagLine;
10317 }
10318
10319 FileInfo fi(fileName.str());
10320 if (!fi.exists() || !fi.isFile())
10321 {
10322 err("Tag file '{}' does not exist or is not a file. Skipping it...\n",fileName);
10323 return;
10324 }
10325
10326 if (Doxygen::tagFileSet.find(fi.absFilePath()) != Doxygen::tagFileSet.end()) return;
10327
10328 Doxygen::tagFileSet.emplace(fi.absFilePath());
10329
10330 if (!destName.isEmpty())
10331 {
10332 Doxygen::tagDestinationMap.emplace(fi.absFilePath(), destName.str());
10333 msg("Reading tag file '{}', location '{}'...\n",fileName,destName);
10334 }
10335 else
10336 {
10337 msg("Reading tag file '{}'...\n",fileName);
10338 }
10339
10340 parseTagFile(root,fi.absFilePath().c_str());
10341}
10342
10343//----------------------------------------------------------------------------
10345{
10346 const StringVector &latexExtraStyleSheet = Config_getList(LATEX_EXTRA_STYLESHEET);
10347 for (const auto &sheet : latexExtraStyleSheet)
10348 {
10349 std::string fileName = sheet;
10350 if (!fileName.empty())
10351 {
10352 FileInfo fi(fileName);
10353 if (!fi.exists())
10354 {
10355 err("Style sheet '{}' specified by LATEX_EXTRA_STYLESHEET does not exist!\n",fileName);
10356 }
10357 else if (fi.isDir())
10358 {
10359 err("Style sheet '{}' specified by LATEX_EXTRA_STYLESHEET is a directory, it has to be a file!\n", fileName);
10360 }
10361 else
10362 {
10363 QCString destFileName = Config_getString(LATEX_OUTPUT)+"/"+fi.fileName();
10365 {
10366 destFileName += LATEX_STYLE_EXTENSION;
10367 }
10368 copyFile(fileName, destFileName);
10369 }
10370 }
10371 }
10372}
10373
10374//----------------------------------------------------------------------------
10375static void copyStyleSheet()
10376{
10377 QCString htmlStyleSheet = Config_getString(HTML_STYLESHEET);
10378 if (!htmlStyleSheet.isEmpty())
10379 {
10380 if (!htmlStyleSheet.startsWith("http:") && !htmlStyleSheet.startsWith("https:"))
10381 {
10382 FileInfo fi(htmlStyleSheet.str());
10383 if (!fi.exists())
10384 {
10385 err("Style sheet '{}' specified by HTML_STYLESHEET does not exist!\n",htmlStyleSheet);
10386 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
10387 }
10388 else if (fi.isDir())
10389 {
10390 err("Style sheet '{}' specified by HTML_STYLESHEET is a directory, it has to be a file!\n",htmlStyleSheet);
10391 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
10392 }
10393 else
10394 {
10395 QCString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
10396 copyFile(htmlStyleSheet,destFileName);
10397 }
10398 }
10399 }
10400 const StringVector &htmlExtraStyleSheet = Config_getList(HTML_EXTRA_STYLESHEET);
10401 for (const auto &sheet : htmlExtraStyleSheet)
10402 {
10403 QCString fileName(sheet);
10404 if (!fileName.isEmpty() && !fileName.startsWith("http:") && !fileName.startsWith("https:"))
10405 {
10406 FileInfo fi(fileName.str());
10407 if (!fi.exists())
10408 {
10409 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET does not exist!\n",fileName);
10410 }
10411 else if (fi.fileName()=="doxygen.css" || fi.fileName()=="tabs.css" || fi.fileName()=="navtree.css")
10412 {
10413 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET is already a built-in stylesheet. Please use a different name\n",fi.fileName());
10414 }
10415 else if (fi.isDir())
10416 {
10417 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET is a directory, it has to be a file!\n",fileName);
10418 }
10419 else
10420 {
10421 QCString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
10422 copyFile(fileName, destFileName);
10423 }
10424 }
10425 }
10426}
10427
10428static void copyLogo(const QCString &outputOption)
10429{
10430 QCString projectLogo = projectLogoFile();
10431 if (!projectLogo.isEmpty())
10432 {
10433 FileInfo fi(projectLogo.str());
10434 if (!fi.exists())
10435 {
10436 err("Project logo '{}' specified by PROJECT_LOGO does not exist!\n",projectLogo);
10437 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
10438 }
10439 else if (fi.isDir())
10440 {
10441 err("Project logo '{}' specified by PROJECT_LOGO is a directory, it has to be a file!\n",projectLogo);
10442 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
10443 }
10444 else
10445 {
10446 QCString destFileName = outputOption+"/"+fi.fileName();
10447 copyFile(projectLogo,destFileName);
10448 Doxygen::indexList->addImageFile(fi.fileName());
10449 }
10450 }
10451}
10452
10453static void copyIcon(const QCString &outputOption)
10454{
10455 QCString projectIcon = Config_getString(PROJECT_ICON);
10456 if (!projectIcon.isEmpty())
10457 {
10458 FileInfo fi(projectIcon.str());
10459 if (!fi.exists())
10460 {
10461 err("Project icon '{}' specified by PROJECT_ICON does not exist!\n",projectIcon);
10462 projectIcon = Config_updateString(PROJECT_ICON,""); // revert to the default
10463 }
10464 else if (fi.isDir())
10465 {
10466 err("Project icon '{}' specified by PROJECT_ICON is a directory, it has to be a file!\n",projectIcon);
10467 projectIcon = Config_updateString(PROJECT_ICON,""); // revert to the default
10468 }
10469 else
10470 {
10471 QCString destFileName = outputOption+"/"+fi.fileName();
10472 copyFile(projectIcon,destFileName);
10473 Doxygen::indexList->addImageFile(fi.fileName());
10474 }
10475 }
10476}
10477
10478static void copyExtraFiles(const StringVector &files,const QCString &filesOption,const QCString &outputOption)
10479{
10480 for (const auto &fileName : files)
10481 {
10482 if (!fileName.empty())
10483 {
10484 FileInfo fi(fileName);
10485 if (!fi.exists())
10486 {
10487 err("Extra file '{}' specified in {} does not exist!\n", fileName,filesOption);
10488 }
10489 else if (fi.isDir())
10490 {
10491 err("Extra file '{}' specified in {} is a directory, it has to be a file!\n", fileName,filesOption);
10492 }
10493 else
10494 {
10495 QCString destFileName = outputOption+"/"+fi.fileName();
10496 Doxygen::indexList->addImageFile(fi.fileName());
10497 copyFile(fileName, destFileName);
10498 }
10499 }
10500 }
10501}
10502
10503//----------------------------------------------------------------------------
10504
10506{
10507 for (const auto &fn : *Doxygen::inputNameLinkedMap)
10508 {
10509 struct FileEntry
10510 {
10511 FileEntry(const QCString &p,FileDef *fd) : path(p), fileDef(fd) {}
10512 QCString path;
10513 FileDef *fileDef;
10514 };
10515
10516 // collect the entry for which to compute the longest common prefix (LCP) of the path
10517 std::vector<FileEntry> fileEntries;
10518 for (const auto &fd : *fn)
10519 {
10520 if (!fd->isReference()) // skip external references
10521 {
10522 fileEntries.emplace_back(fd->getPath(),fd.get());
10523 }
10524 }
10525
10526 size_t size = fileEntries.size();
10527
10528 if (size==1) // name if unique, so diskname is simply the name
10529 {
10530 FileDef *fd = fileEntries[0].fileDef;
10531 fd->setDiskName(fn->fileName());
10532 }
10533 else if (size>1) // multiple occurrences of the same file name
10534 {
10535 // sort the array
10536 std::stable_sort(fileEntries.begin(),
10537 fileEntries.end(),
10538 [](const FileEntry &fe1,const FileEntry &fe2)
10539 { return qstricmp_sort(fe1.path,fe2.path)<0; }
10540 );
10541
10542 // since the entries are sorted, the common prefix of the whole array is same
10543 // as the common prefix between the first and last entry
10544 const FileEntry &first = fileEntries[0];
10545 const FileEntry &last = fileEntries[size-1];
10546 int first_path_size = static_cast<int>(first.path.size())-1; // -1 to skip trailing slash
10547 int last_path_size = static_cast<int>(last.path.size())-1; // -1 to skip trailing slash
10548 int j=0;
10549 int i=0;
10550 for (i=0;i<first_path_size && i<last_path_size;i++)
10551 {
10552 if (first.path[i]=='/') j=i;
10553 if (first.path[i]!=last.path[i]) break;
10554 }
10555 if (i==first_path_size && i<last_path_size && last.path[i]=='/')
10556 {
10557 // case first='some/path' and last='some/path/more' => match is 'some/path'
10558 j=first_path_size;
10559 }
10560 else if (i==last_path_size && i<first_path_size && first.path[i]=='/')
10561 {
10562 // case first='some/path/more' and last='some/path' => match is 'some/path'
10563 j=last_path_size;
10564 }
10565
10566 // add non-common part of the path to the name
10567 for (auto &fileEntry : fileEntries)
10568 {
10569 QCString prefix = fileEntry.path.right(fileEntry.path.length()-j-1);
10570 fileEntry.fileDef->setName(prefix+fn->fileName());
10571 //printf("!!!!!!!! non unique disk name=%s:%s\n",qPrint(prefix),fn->fileName());
10572 fileEntry.fileDef->setDiskName(prefix+fn->fileName());
10573 }
10574 }
10575 }
10576}
10577
10578
10579
10580//----------------------------------------------------------------------------
10581
10582static std::unique_ptr<OutlineParserInterface> getParserForFile(const QCString &fn)
10583{
10584 QCString fileName=fn;
10585 QCString extension;
10586 int sep = fileName.findRev('/');
10587 int ei = fileName.findRev('.');
10588 if (ei!=-1 && (sep==-1 || ei>sep)) // matches dir/file.ext but not dir.1/file
10589 {
10590 extension=fileName.right(fileName.length()-ei);
10591 }
10592 else
10593 {
10594 extension = ".no_extension";
10595 }
10596
10597 return Doxygen::parserManager->getOutlineParser(extension);
10598}
10599
10600static std::shared_ptr<Entry> parseFile(OutlineParserInterface &parser,
10601 FileDef *fd,const QCString &fn,
10602 ClangTUParser *clangParser,bool newTU)
10603{
10604 QCString fileName=fn;
10605 AUTO_TRACE("fileName={}",fileName);
10606 QCString extension;
10607 int ei = fileName.findRev('.');
10608 if (ei!=-1)
10609 {
10610 extension=fileName.right(fileName.length()-ei);
10611 }
10612 else
10613 {
10614 extension = ".no_extension";
10615 }
10616
10617 FileInfo fi(fileName.str());
10618 std::string preBuf;
10619
10620 if (Config_getBool(ENABLE_PREPROCESSING) &&
10621 parser.needsPreprocessing(extension))
10622 {
10623 Preprocessor preprocessor;
10624 const StringVector &includePath = Config_getList(INCLUDE_PATH);
10625 for (const auto &s : includePath)
10626 {
10627 std::string absPath = FileInfo(s).absFilePath();
10628 preprocessor.addSearchDir(absPath);
10629 }
10630 std::string inBuf;
10631 msg("Preprocessing {}...\n",fn);
10632 readInputFile(fileName,inBuf);
10633 addTerminalCharIfMissing(inBuf,'\n');
10634 preprocessor.processFile(fileName,inBuf,preBuf);
10635 }
10636 else // no preprocessing
10637 {
10638 msg("Reading {}...\n",fn);
10639 readInputFile(fileName,preBuf);
10640 addTerminalCharIfMissing(preBuf,'\n');
10641 }
10642
10643 std::string convBuf;
10644 convBuf.reserve(preBuf.size()+1024);
10645
10646 // convert multi-line C++ comments to C style comments
10647 convertCppComments(preBuf,convBuf,fileName.str());
10648
10649 std::shared_ptr<Entry> fileRoot = std::make_shared<Entry>();
10650 // use language parse to parse the file
10651 if (clangParser)
10652 {
10653 if (newTU) clangParser->parse();
10654 clangParser->switchToFile(fd);
10655 }
10656 parser.parseInput(fileName,convBuf.data(),fileRoot,clangParser);
10657 fileRoot->setFileDef(fd);
10658 return fileRoot;
10659}
10660
10661//! parse the list of input files
10662static void parseFilesMultiThreading(const std::shared_ptr<Entry> &root)
10663{
10664 AUTO_TRACE();
10665#if USE_LIBCLANG
10667 {
10668 StringUnorderedSet processedFiles;
10669
10670 // create a dictionary with files to process
10671 StringUnorderedSet filesToProcess;
10672 for (const auto &s : g_inputFiles)
10673 {
10674 filesToProcess.insert(s);
10675 }
10676
10677 std::mutex processedFilesLock;
10678 // process source files (and their include dependencies)
10679 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10680 msg("Processing input using {} threads.\n",numThreads);
10681 ThreadPool threadPool(numThreads);
10682 using FutureType = std::vector< std::shared_ptr<Entry> >;
10683 std::vector< std::future< FutureType > > results;
10684 for (const auto &s : g_inputFiles)
10685 {
10686 bool ambig = false;
10687 QCString qs = s;
10689 ASSERT(fd!=nullptr);
10690 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt::Cpp) // this is a source file
10691 {
10692 // lambda representing the work to executed by a thread
10693 auto processFile = [qs,&filesToProcess,&processedFilesLock,&processedFiles]() {
10694 bool ambig_l = false;
10695 std::vector< std::shared_ptr<Entry> > roots;
10697 auto clangParser = ClangParser::instance()->createTUParser(fd_l);
10698 auto parser = getParserForFile(qs);
10699 auto fileRoot { parseFile(*parser.get(),fd_l,qs,clangParser.get(),true) };
10700 roots.push_back(fileRoot);
10701
10702 // Now process any include files in the same translation unit
10703 // first. When libclang is used this is much more efficient.
10704 for (auto incFile : clangParser->filesInSameTU())
10705 {
10706 QCString qincFile = incFile;
10707 if (filesToProcess.find(incFile)!=filesToProcess.end())
10708 {
10709 bool needsToBeProcessed = false;
10710 {
10711 std::lock_guard<std::mutex> lock(processedFilesLock);
10712 needsToBeProcessed = processedFiles.find(incFile)==processedFiles.end();
10713 if (needsToBeProcessed) processedFiles.insert(incFile);
10714 }
10715 if (qincFile!=qs && needsToBeProcessed)
10716 {
10717 FileDef *ifd=findFileDef(Doxygen::inputNameLinkedMap,qincFile,ambig_l);
10718 if (ifd && !ifd->isReference())
10719 {
10720 //printf(" Processing %s in same translation unit as %s\n",incFile,qPrint(s));
10721 fileRoot = parseFile(*parser.get(),ifd,qincFile,clangParser.get(),false);
10722 roots.push_back(fileRoot);
10723 }
10724 }
10725 }
10726 }
10727 return roots;
10728 };
10729 // dispatch the work and collect the future results
10730 results.emplace_back(threadPool.queue(processFile));
10731 }
10732 }
10733 // synchronize with the Entry result lists produced and add them to the root
10734 for (auto &f : results)
10735 {
10736 auto l = f.get();
10737 for (auto &e : l)
10738 {
10739 root->moveToSubEntryAndKeep(e);
10740 }
10741 }
10742 // process remaining files
10743 results.clear();
10744 for (const auto &s : g_inputFiles)
10745 {
10746 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
10747 {
10748 // lambda representing the work to executed by a thread
10749 auto processFile = [s]() {
10750 bool ambig = false;
10751 QCString qs = s;
10752 std::vector< std::shared_ptr<Entry> > roots;
10754 auto parser { getParserForFile(qs) };
10755 bool useClang = getLanguageFromFileName(qs)==SrcLangExt::Cpp;
10756 if (useClang)
10757 {
10758 auto clangParser = ClangParser::instance()->createTUParser(fd);
10759 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
10760 roots.push_back(fileRoot);
10761 }
10762 else
10763 {
10764 auto fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10765 roots.push_back(fileRoot);
10766 }
10767 return roots;
10768 };
10769 results.emplace_back(threadPool.queue(processFile));
10770 }
10771 }
10772 // synchronize with the Entry result lists produced and add them to the root
10773 for (auto &f : results)
10774 {
10775 auto l = f.get();
10776 for (auto &e : l)
10777 {
10778 root->moveToSubEntryAndKeep(e);
10779 }
10780 }
10781 }
10782 else // normal processing
10783#endif
10784 {
10785 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10786 msg("Processing input using {} threads.\n",numThreads);
10787 ThreadPool threadPool(numThreads);
10788 using FutureType = std::shared_ptr<Entry>;
10789 std::vector< std::future< FutureType > > results;
10790 for (const auto &s : g_inputFiles)
10791 {
10792 // lambda representing the work to executed by a thread
10793 auto processFile = [s]() {
10794 bool ambig = false;
10795 QCString qs = s;
10797 auto parser = getParserForFile(qs);
10798 auto fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10799 return fileRoot;
10800 };
10801 // dispatch the work and collect the future results
10802 results.emplace_back(threadPool.queue(processFile));
10803 }
10804 // synchronize with the Entry results produced and add them to the root
10805 for (auto &f : results)
10806 {
10807 root->moveToSubEntryAndKeep(f.get());
10808 }
10809 }
10810}
10811
10812//! parse the list of input files
10813static void parseFilesSingleThreading(const std::shared_ptr<Entry> &root)
10814{
10815 AUTO_TRACE();
10816#if USE_LIBCLANG
10818 {
10819 StringUnorderedSet processedFiles;
10820
10821 // create a dictionary with files to process
10822 StringUnorderedSet filesToProcess;
10823 for (const auto &s : g_inputFiles)
10824 {
10825 filesToProcess.insert(s);
10826 }
10827
10828 // process source files (and their include dependencies)
10829 for (const auto &s : g_inputFiles)
10830 {
10831 bool ambig = false;
10832 QCString qs =s;
10834 ASSERT(fd!=nullptr);
10835 if (fd->isSource() && !fd->isReference() && getLanguageFromFileName(qs)==SrcLangExt::Cpp) // this is a source file
10836 {
10837 auto clangParser = ClangParser::instance()->createTUParser(fd);
10838 auto parser { getParserForFile(qs) };
10839 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
10840 root->moveToSubEntryAndKeep(fileRoot);
10841 processedFiles.insert(s);
10842
10843 // Now process any include files in the same translation unit
10844 // first. When libclang is used this is much more efficient.
10845 for (auto incFile : clangParser->filesInSameTU())
10846 {
10847 //printf(" file %s\n",qPrint(incFile));
10848 if (filesToProcess.find(incFile)!=filesToProcess.end() && // file need to be processed
10849 processedFiles.find(incFile)==processedFiles.end()) // and is not processed already
10850 {
10852 if (ifd && !ifd->isReference())
10853 {
10854 //printf(" Processing %s in same translation unit as %s\n",qPrint(incFile),qPrint(qs));
10855 fileRoot = parseFile(*parser.get(),ifd,incFile,clangParser.get(),false);
10856 root->moveToSubEntryAndKeep(fileRoot);
10857 processedFiles.insert(incFile);
10858 }
10859 }
10860 }
10861 }
10862 }
10863 // process remaining files
10864 for (const auto &s : g_inputFiles)
10865 {
10866 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
10867 {
10868 bool ambig = false;
10869 QCString qs = s;
10871 if (getLanguageFromFileName(qs)==SrcLangExt::Cpp) // not yet processed
10872 {
10873 auto clangParser = ClangParser::instance()->createTUParser(fd);
10874 auto parser { getParserForFile(qs) };
10875 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
10876 root->moveToSubEntryAndKeep(fileRoot);
10877 }
10878 else
10879 {
10880 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(qs) };
10881 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10882 root->moveToSubEntryAndKeep(fileRoot);
10883 }
10884 processedFiles.insert(s);
10885 }
10886 }
10887 }
10888 else // normal processing
10889#endif
10890 {
10891 for (const auto &s : g_inputFiles)
10892 {
10893 bool ambig = false;
10894 QCString qs = s;
10896 ASSERT(fd!=nullptr);
10897 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(qs) };
10898 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10899 root->moveToSubEntryAndKeep(std::move(fileRoot));
10900 }
10901 }
10902}
10903
10904// resolves a path that may include symlinks, if a recursive symlink is
10905// found an empty string is returned.
10906static std::string resolveSymlink(const std::string &path)
10907{
10908 int sepPos=0;
10909 int oldPos=0;
10910 StringUnorderedSet nonSymlinks;
10911 StringUnorderedSet known;
10912 QCString result(path);
10913 QCString oldPrefix = "/";
10914 do
10915 {
10916#if defined(_WIN32)
10917 // UNC path, skip server and share name
10918 if (sepPos==0 && (result.startsWith("//") || result.startsWith("\\\\")))
10919 sepPos = result.find('/',2);
10920 if (sepPos!=-1)
10921 sepPos = result.find('/',sepPos+1);
10922#else
10923 sepPos = result.find('/',sepPos+1);
10924#endif
10925 QCString prefix = sepPos==-1 ? result : result.left(sepPos);
10926 if (nonSymlinks.find(prefix.str())==nonSymlinks.end())
10927 {
10928 FileInfo fi(prefix.str());
10929 if (fi.isSymLink())
10930 {
10931 QCString target = fi.readLink();
10932 bool isRelative = FileInfo(target.str()).isRelative();
10933 if (isRelative)
10934 {
10935 target = Dir::cleanDirPath(oldPrefix.str()+"/"+target.str());
10936 }
10937 if (sepPos!=-1)
10938 {
10939 if (fi.isDir() && target.length()>0 && target.at(target.length()-1)!='/')
10940 {
10941 target+='/';
10942 }
10943 target+=result.mid(sepPos);
10944 }
10945 result = Dir::cleanDirPath(target.str());
10946 if (known.find(result.str())!=known.end()) return std::string(); // recursive symlink!
10947 known.insert(result.str());
10948 if (isRelative)
10949 {
10950 sepPos = oldPos;
10951 }
10952 else // link to absolute path
10953 {
10954 sepPos = 0;
10955 oldPrefix = "/";
10956 }
10957 }
10958 else
10959 {
10960 nonSymlinks.insert(prefix.str());
10961 oldPrefix = prefix;
10962 }
10963 oldPos = sepPos;
10964 }
10965 }
10966 while (sepPos!=-1);
10967 return Dir::cleanDirPath(result.str());
10968}
10969
10971
10972//----------------------------------------------------------------------------
10973// Read all files matching at least one pattern in 'patList' in the
10974// directory represented by 'fi'.
10975// The directory is read iff the recursiveFlag is set.
10976// The contents of all files is append to the input string
10977
10978static void readDir(FileInfo *fi,
10979 FileNameLinkedMap *fnMap,
10980 StringUnorderedSet *exclSet,
10981 const StringVector *patList,
10982 const StringVector *exclPatList,
10983 StringVector *resultList,
10984 StringUnorderedSet *resultSet,
10985 bool errorIfNotExist,
10986 bool recursive,
10987 StringUnorderedSet *killSet,
10988 StringUnorderedSet *paths
10989 )
10990{
10991 std::string dirName = fi->absFilePath();
10992 if (paths && !dirName.empty())
10993 {
10994 paths->insert(dirName);
10995 }
10996 //printf("%s isSymLink()=%d\n",qPrint(dirName),fi->isSymLink());
10997 if (fi->isSymLink())
10998 {
10999 dirName = resolveSymlink(dirName);
11000 if (dirName.empty())
11001 {
11002 //printf("RECURSIVE SYMLINK: %s\n",qPrint(dirName));
11003 return; // recursive symlink
11004 }
11005 }
11006
11007 if (g_pathsVisited.find(dirName)!=g_pathsVisited.end())
11008 {
11009 //printf("PATH ALREADY VISITED: %s\n",qPrint(dirName));
11010 return; // already visited path
11011 }
11012 g_pathsVisited.insert(dirName);
11013
11014 Dir dir(dirName);
11015 msg("Searching for files in directory {}\n", fi->absFilePath());
11016 //printf("killSet=%p count=%d\n",killSet,killSet ? (int)killSet->count() : -1);
11017
11018 StringVector dirResultList;
11019
11020 for (const auto &dirEntry : dir.iterator())
11021 {
11022 FileInfo cfi(dirEntry.path());
11023 auto checkPatterns = [&]() -> bool
11024 {
11025 return (patList==nullptr || patternMatch(cfi,*patList)) &&
11026 (exclPatList==nullptr || !patternMatch(cfi,*exclPatList)) &&
11027 (killSet==nullptr || killSet->find(cfi.absFilePath())==killSet->end());
11028 };
11029
11030 if (exclSet==nullptr || exclSet->find(cfi.absFilePath())==exclSet->end())
11031 { // file should not be excluded
11032 //printf("killSet->find(%s)\n",qPrint(cfi->absFilePath()));
11033 if (Config_getBool(EXCLUDE_SYMLINKS) && cfi.isSymLink())
11034 {
11035 }
11036 else if (!cfi.exists() || !cfi.isReadable())
11037 {
11038 if (errorIfNotExist && checkPatterns())
11039 {
11040 warn_uncond("source '{}' is not a readable file or directory... skipping.\n",cfi.absFilePath());
11041 }
11042 }
11043 else if (cfi.isFile() && checkPatterns())
11044 {
11045 std::string name=cfi.fileName();
11046 std::string path=cfi.dirPath()+"/";
11047 std::string fullName=path+name;
11048 if (fnMap)
11049 {
11050 auto fd = createFileDef(path,name);
11051 FileName *fn=nullptr;
11052 if (!name.empty())
11053 {
11054 fn = fnMap->add(name,fullName);
11055 fn->push_back(std::move(fd));
11056 }
11057 }
11058 dirResultList.push_back(fullName);
11059 if (resultSet) resultSet->insert(fullName);
11060 if (killSet) killSet->insert(fullName);
11061 }
11062 else if (recursive &&
11063 cfi.isDir() &&
11064 (exclPatList==nullptr || !patternMatch(cfi,*exclPatList)) &&
11065 cfi.fileName().at(0)!='.') // skip "." ".." and ".dir"
11066 {
11067 FileInfo acfi(cfi.absFilePath());
11068 readDir(&acfi,fnMap,exclSet,
11069 patList,exclPatList,&dirResultList,resultSet,errorIfNotExist,
11070 recursive,killSet,paths);
11071 }
11072 }
11073 }
11074 if (resultList && !dirResultList.empty())
11075 {
11076 // sort the resulting list to make the order platform independent.
11077 std::stable_sort(dirResultList.begin(),
11078 dirResultList.end(),
11079 [](const auto &f1,const auto &f2) { return qstricmp_sort(f1.c_str(),f2.c_str())<0; });
11080
11081 // append the sorted results to resultList
11082 resultList->insert(resultList->end(), dirResultList.begin(), dirResultList.end());
11083 }
11084}
11085
11086
11087//----------------------------------------------------------------------------
11088// read a file or all files in a directory and append their contents to the
11089// input string. The names of the files are appended to the 'fiList' list.
11090
11092 FileNameLinkedMap *fnMap,
11093 StringUnorderedSet *exclSet,
11094 const StringVector *patList,
11095 const StringVector *exclPatList,
11096 StringVector *resultList,
11097 StringUnorderedSet *resultSet,
11098 bool recursive,
11099 bool errorIfNotExist,
11100 StringUnorderedSet *killSet,
11101 StringUnorderedSet *paths
11102 )
11103{
11104 //printf("killSet count=%d\n",killSet ? (int)killSet->size() : -1);
11105 // strip trailing slashes
11106 if (s.isEmpty()) return;
11107
11108 g_pathsVisited.clear();
11109
11110 FileInfo fi(s.str());
11111 //printf("readFileOrDirectory(%s)\n",s);
11112 {
11113 if (exclSet==nullptr || exclSet->find(fi.absFilePath())==exclSet->end())
11114 {
11115 if (Config_getBool(EXCLUDE_SYMLINKS) && fi.isSymLink())
11116 {
11117 }
11118 else if (!fi.exists() || !fi.isReadable())
11119 {
11120 if (errorIfNotExist)
11121 {
11122 warn_uncond("source '{}' is not a readable file or directory... skipping.\n",s);
11123 }
11124 }
11125 else if (fi.isFile())
11126 {
11127 std::string dirPath = fi.dirPath(true);
11128 std::string filePath = fi.absFilePath();
11129 if (paths && !dirPath.empty())
11130 {
11131 paths->insert(dirPath);
11132 }
11133 //printf("killSet.find(%s)=%d\n",qPrint(fi.absFilePath()),killSet.find(fi.absFilePath())!=killSet.end());
11134 if (killSet==nullptr || killSet->find(filePath)==killSet->end())
11135 {
11136 std::string name=fi.fileName();
11137 if (fnMap)
11138 {
11139 auto fd = createFileDef(dirPath+"/",name);
11140 if (!name.empty())
11141 {
11142 FileName *fn = fnMap->add(name,filePath);
11143 fn->push_back(std::move(fd));
11144 }
11145 }
11146 if (resultList || resultSet)
11147 {
11148 if (resultList) resultList->push_back(filePath);
11149 if (resultSet) resultSet->insert(filePath);
11150 }
11151
11152 if (killSet) killSet->insert(fi.absFilePath());
11153 }
11154 }
11155 else if (fi.isDir()) // readable dir
11156 {
11157 readDir(&fi,fnMap,exclSet,patList,
11158 exclPatList,resultList,resultSet,errorIfNotExist,
11159 recursive,killSet,paths);
11160 }
11161 }
11162 }
11163}
11164
11165//----------------------------------------------------------------------------
11166
11168{
11169 QCString anchor;
11171 {
11172 MemberDef *md = toMemberDef(d);
11173 anchor=":"+md->anchor();
11174 }
11175 QCString scope;
11176 QCString fn = d->getOutputFileBase();
11179 {
11180 scope = fn;
11181 }
11182 t << "REPLACE INTO symbols (symbol_id,scope_id,name,file,line) VALUES('"
11183 << fn+anchor << "','"
11184 << scope << "','"
11185 << d->name() << "','"
11186 << d->getDefFileName() << "','"
11187 << d->getDefLine()
11188 << "');\n";
11189}
11190
11191static void dumpSymbolMap()
11192{
11193 std::ofstream f = Portable::openOutputStream("symbols.sql");
11194 if (f.is_open())
11195 {
11196 TextStream t(&f);
11197 for (const auto &[name,symList] : *Doxygen::symbolMap)
11198 {
11199 for (const auto &def : symList)
11200 {
11201 dumpSymbol(t,def);
11202 }
11203 }
11204 }
11205}
11206
11207// print developer options of Doxygen
11208static void devUsage()
11209{
11211 msg("Developer parameters:\n");
11212 msg(" -m dump symbol map\n");
11213 msg(" -b making messages output unbuffered\n");
11214 msg(" -c <file> process input file as a comment block and produce HTML output\n");
11215#if ENABLE_TRACING
11216 msg(" -t [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout)\n");
11217 msg(" -t_time [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout),\n"
11218 " and include time and thread information\n");
11219#endif
11220 msg(" -d <level> enable a debug level, such as (multiple invocations of -d are possible):\n");
11222}
11223
11224
11225//----------------------------------------------------------------------------
11226// print the version of Doxygen
11227
11228static void version(const bool extended)
11229{
11231 QCString versionString = getFullVersion();
11232 msg("{}\n",versionString);
11233 if (extended)
11234 {
11235 QCString extVers;
11236 if (!extVers.isEmpty()) extVers+= ", ";
11237 extVers += "sqlite3 ";
11238 extVers += sqlite3_libversion();
11239#if USE_LIBCLANG
11240 if (!extVers.isEmpty()) extVers+= ", ";
11241 extVers += "clang support ";
11242 extVers += CLANG_VERSION_STRING;
11243#endif
11244 if (!extVers.isEmpty())
11245 {
11246 int lastComma = extVers.findRev(',');
11247 if (lastComma != -1) extVers = extVers.replace(lastComma,1," and");
11248 msg(" with {}.\n",extVers);
11249 }
11250 }
11251}
11252
11253//----------------------------------------------------------------------------
11254// print the usage of Doxygen
11255
11256static void usage(const QCString &name,const QCString &versionString)
11257{
11259 msg("Doxygen version {0}\nCopyright Dimitri van Heesch 1997-2025\n\n"
11260 "You can use Doxygen in a number of ways:\n\n"
11261 "1) Use Doxygen to generate a template configuration file*:\n"
11262 " {1} [-s] -g [configName]\n\n"
11263 "2) Use Doxygen to update an old configuration file*:\n"
11264 " {1} [-s] -u [configName]\n\n"
11265 "3) Use Doxygen to generate documentation using an existing "
11266 "configuration file*:\n"
11267 " {1} [configName]\n\n"
11268 "4) Use Doxygen to generate a template file controlling the layout of the\n"
11269 " generated documentation:\n"
11270 " {1} -l [layoutFileName]\n\n"
11271 " In case layoutFileName is omitted DoxygenLayout.xml will be used as filename.\n"
11272 " If - is used for layoutFileName Doxygen will write to standard output.\n\n"
11273 "5) Use Doxygen to generate a template style sheet file for RTF, HTML or Latex.\n"
11274 " RTF: {1} -w rtf styleSheetFile\n"
11275 " HTML: {1}-w html headerFile footerFile styleSheetFile [configFile]\n"
11276 " LaTeX: {1} -w latex headerFile footerFile styleSheetFile [configFile]\n\n"
11277 "6) Use Doxygen to generate a rtf extensions file\n"
11278 " {1} -e rtf extensionsFile\n\n"
11279 " If - is used for extensionsFile Doxygen will write to standard output.\n\n"
11280 "7) Use Doxygen to compare the used configuration file with the template configuration file\n"
11281 " {1} -x [configFile]\n\n"
11282 " Use Doxygen to compare the used configuration file with the template configuration file\n"
11283 " without replacing the environment variables or CMake type replacement variables\n"
11284 " {1} -x_noenv [configFile]\n\n"
11285 "8) Use Doxygen to show a list of built-in emojis.\n"
11286 " {1} -f emoji outputFileName\n\n"
11287 " If - is used for outputFileName Doxygen will write to standard output.\n\n"
11288 "*) If -s is specified the comments of the configuration items in the config file will be omitted.\n"
11289 " If configName is omitted 'Doxyfile' will be used as a default.\n"
11290 " If - is used for configFile Doxygen will write / read the configuration to /from standard output / input.\n\n"
11291 "If -q is used for a Doxygen documentation run, Doxygen will see this as if QUIET=YES has been set.\n\n"
11292 "-v print version string, -V print extended version information\n"
11293 "-h,-? prints usage help information\n"
11294 "{1} -d prints additional usage flags for debugging purposes\n",versionString,name);
11295}
11296
11297//----------------------------------------------------------------------------
11298// read the argument of option 'c' from the comment argument list and
11299// update the option index 'optInd'.
11300
11301static const char *getArg(int argc,char **argv,int &optInd)
11302{
11303 char *s=nullptr;
11304 if (qstrlen(&argv[optInd][2])>0)
11305 s=&argv[optInd][2];
11306 else if (optInd+1<argc && argv[optInd+1][0]!='-')
11307 s=argv[++optInd];
11308 return s;
11309}
11310
11311//----------------------------------------------------------------------------
11312
11313/** @brief /dev/null outline parser */
11315{
11316 public:
11317 void parseInput(const QCString &/* file */, const char * /* buf */,const std::shared_ptr<Entry> &, ClangTUParser*) override {}
11318 bool needsPreprocessing(const QCString &) const override { return FALSE; }
11319 void parsePrototype(const QCString &) override {}
11320};
11321
11322
11323template<class T> std::function< std::unique_ptr<T>() > make_parser_factory()
11324{
11325 return []() { return std::make_unique<T>(); };
11326}
11327
11329{
11330 initResources();
11331 QCString lang = Portable::getenv("LC_ALL");
11332 if (!lang.isEmpty()) Portable::setenv("LANG",lang);
11333 std::setlocale(LC_ALL,"");
11334 std::setlocale(LC_CTYPE,"C"); // to get isspace(0xA0)==0, needed for UTF-8
11335 std::setlocale(LC_NUMERIC,"C");
11336
11338
11362
11363 // register any additional parsers here...
11364
11366
11367#if USE_LIBCLANG
11369#endif
11378 Doxygen::pageLinkedMap = new PageLinkedMap; // all doc pages
11379 Doxygen::exampleLinkedMap = new PageLinkedMap; // all examples
11380 //Doxygen::tagDestinationDict.setAutoDelete(TRUE);
11382
11383 // initialization of these globals depends on
11384 // configuration switches so we need to postpone these
11385 Doxygen::globalScope = nullptr;
11394
11395}
11396
11428
11429static int computeIdealCacheParam(size_t v)
11430{
11431 //printf("computeIdealCacheParam(v=%u)\n",v);
11432
11433 int r=0;
11434 while (v!=0) v>>=1,r++;
11435 // r = log2(v)
11436
11437 // convert to a valid cache size value
11438 return std::max(0,std::min(r-16,9));
11439}
11440
11441void readConfiguration(int argc, char **argv)
11442{
11443 QCString versionString = getFullVersion();
11444
11445 // helper that calls \a func to write to file \a fileName via a TextStream
11446 auto writeFile = [](const char *fileName,std::function<void(TextStream&)> func) -> bool
11447 {
11448 std::ofstream f;
11449 if (openOutputFile(fileName,f))
11450 {
11451 TextStream t(&f);
11452 func(t);
11453 return true;
11454 }
11455 return false;
11456 };
11457
11458
11459 /**************************************************************************
11460 * Handle arguments *
11461 **************************************************************************/
11462
11463 int optInd=1;
11464 QCString configName;
11465 QCString traceName;
11466 bool genConfig=false;
11467 bool shortList=false;
11468 bool traceTiming=false;
11470 bool updateConfig=false;
11471 bool quiet = false;
11472 while (optInd<argc && argv[optInd][0]=='-' &&
11473 (isalpha(argv[optInd][1]) || argv[optInd][1]=='?' ||
11474 argv[optInd][1]=='-')
11475 )
11476 {
11477 switch(argv[optInd][1])
11478 {
11479 case 'g':
11480 {
11481 genConfig=TRUE;
11482 }
11483 break;
11484 case 'l':
11485 {
11486 QCString layoutName;
11487 if (optInd+1>=argc)
11488 {
11489 layoutName="DoxygenLayout.xml";
11490 }
11491 else
11492 {
11493 layoutName=argv[optInd+1];
11494 }
11495 writeDefaultLayoutFile(layoutName);
11497 exit(0);
11498 }
11499 break;
11500 case 'c':
11501 if (optInd+1>=argc) // no file name given
11502 {
11503 msg("option \"-c\" is missing the file name to read\n");
11504 devUsage();
11506 exit(1);
11507 }
11508 else
11509 {
11510 g_commentFileName=argv[optInd+1];
11511 optInd++;
11512 }
11513 g_singleComment=true;
11514 quiet=true;
11515 break;
11516 case 'd':
11517 {
11518 QCString debugLabel=getArg(argc,argv,optInd);
11519 if (debugLabel.isEmpty())
11520 {
11521 devUsage();
11523 exit(0);
11524 }
11525 int retVal = Debug::setFlagStr(debugLabel);
11526 if (!retVal)
11527 {
11528 msg("option \"-d\" has unknown debug specifier: \"{}\".\n",debugLabel);
11529 devUsage();
11531 exit(1);
11532 }
11533 }
11534 break;
11535 case 't':
11536 {
11537#if ENABLE_TRACING
11538 if (!strcmp(argv[optInd]+1,"t_time"))
11539 {
11540 traceTiming = true;
11541 }
11542 else if (!strcmp(argv[optInd]+1,"t"))
11543 {
11544 traceTiming = false;
11545 }
11546 else
11547 {
11548 err("option should be \"-t\" or \"-t_time\", found: \"{}\".\n",argv[optInd]);
11550 exit(1);
11551 }
11552 if (optInd+1>=argc || argv[optInd+1][0] == '-') // no file name given
11553 {
11554 traceName="stdout";
11555 }
11556 else
11557 {
11558 traceName=argv[optInd+1];
11559 optInd++;
11560 }
11561#else
11562 err("support for option \"-t\" has not been compiled in (use a debug build or a release build with tracing enabled).\n");
11564 exit(1);
11565#endif
11566 }
11567 break;
11568 case 'x':
11569 if (!strcmp(argv[optInd]+1,"x_noenv")) diffList=Config::CompareMode::CompressedNoEnv;
11570 else if (!strcmp(argv[optInd]+1,"x")) diffList=Config::CompareMode::Compressed;
11571 else
11572 {
11573 err("option should be \"-x\" or \"-x_noenv\", found: \"{}\".\n",argv[optInd]);
11575 exit(1);
11576 }
11577 break;
11578 case 's':
11579 shortList=TRUE;
11580 break;
11581 case 'u':
11582 updateConfig=TRUE;
11583 break;
11584 case 'e':
11585 {
11586 QCString formatName=getArg(argc,argv,optInd);
11587 if (formatName.isEmpty())
11588 {
11589 err("option \"-e\" is missing format specifier rtf.\n");
11591 exit(1);
11592 }
11593 if (qstricmp(formatName.data(),"rtf")==0)
11594 {
11595 if (optInd+1>=argc)
11596 {
11597 err("option \"-e rtf\" is missing an extensions file name\n");
11599 exit(1);
11600 }
11601 writeFile(argv[optInd+1],RTFGenerator::writeExtensionsFile);
11603 exit(0);
11604 }
11605 err("option \"-e\" has invalid format specifier.\n");
11607 exit(1);
11608 }
11609 break;
11610 case 'f':
11611 {
11612 QCString listName=getArg(argc,argv,optInd);
11613 if (listName.isEmpty())
11614 {
11615 err("option \"-f\" is missing list specifier.\n");
11617 exit(1);
11618 }
11619 if (qstricmp(listName.data(),"emoji")==0)
11620 {
11621 if (optInd+1>=argc)
11622 {
11623 err("option \"-f emoji\" is missing an output file name\n");
11625 exit(1);
11626 }
11627 writeFile(argv[optInd+1],[](TextStream &t) { EmojiEntityMapper::instance().writeEmojiFile(t); });
11629 exit(0);
11630 }
11631 err("option \"-f\" has invalid list specifier.\n");
11633 exit(1);
11634 }
11635 break;
11636 case 'w':
11637 {
11638 QCString formatName=getArg(argc,argv,optInd);
11639 if (formatName.isEmpty())
11640 {
11641 err("option \"-w\" is missing format specifier rtf, html or latex\n");
11643 exit(1);
11644 }
11645 if (qstricmp(formatName.data(),"rtf")==0)
11646 {
11647 if (optInd+1>=argc)
11648 {
11649 err("option \"-w rtf\" is missing a style sheet file name\n");
11651 exit(1);
11652 }
11653 if (!writeFile(argv[optInd+1],RTFGenerator::writeStyleSheetFile))
11654 {
11655 err("error opening RTF style sheet file {}!\n",argv[optInd+1]);
11657 exit(1);
11658 }
11660 exit(0);
11661 }
11662 else if (qstricmp(formatName.data(),"html")==0)
11663 {
11664 Config::init();
11665 if (optInd+4<argc || FileInfo("Doxyfile").exists() || FileInfo("doxyfile").exists())
11666 // explicit config file mentioned or default found on disk
11667 {
11668 QCString df = optInd+4<argc ? argv[optInd+4] : (FileInfo("Doxyfile").exists() ? QCString("Doxyfile") : QCString("doxyfile"));
11669 if (!Config::parse(df)) // parse the config file
11670 {
11671 err("error opening or reading configuration file {}!\n",argv[optInd+4]);
11673 exit(1);
11674 }
11675 }
11676 if (optInd+3>=argc)
11677 {
11678 err("option \"-w html\" does not have enough arguments\n");
11680 exit(1);
11681 }
11685 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11686 writeFile(argv[optInd+1],[&](TextStream &t) { HtmlGenerator::writeHeaderFile(t,argv[optInd+3]); });
11687 writeFile(argv[optInd+2],HtmlGenerator::writeFooterFile);
11688 writeFile(argv[optInd+3],HtmlGenerator::writeStyleSheetFile);
11690 exit(0);
11691 }
11692 else if (qstricmp(formatName.data(),"latex")==0)
11693 {
11694 Config::init();
11695 if (optInd+4<argc || FileInfo("Doxyfile").exists() || FileInfo("doxyfile").exists())
11696 {
11697 QCString df = optInd+4<argc ? argv[optInd+4] : (FileInfo("Doxyfile").exists() ? QCString("Doxyfile") : QCString("doxyfile"));
11698 if (!Config::parse(df))
11699 {
11700 err("error opening or reading configuration file {}!\n",argv[optInd+4]);
11702 exit(1);
11703 }
11704 }
11705 if (optInd+3>=argc)
11706 {
11707 err("option \"-w latex\" does not have enough arguments\n");
11709 exit(1);
11710 }
11714 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11715 writeFile(argv[optInd+1],LatexGenerator::writeHeaderFile);
11716 writeFile(argv[optInd+2],LatexGenerator::writeFooterFile);
11717 writeFile(argv[optInd+3],LatexGenerator::writeStyleSheetFile);
11719 exit(0);
11720 }
11721 else
11722 {
11723 err("Illegal format specifier \"{}\": should be one of rtf, html or latex\n",formatName);
11725 exit(1);
11726 }
11727 }
11728 break;
11729 case 'm':
11731 break;
11732 case 'v':
11733 version(false);
11735 exit(0);
11736 break;
11737 case 'V':
11738 version(true);
11740 exit(0);
11741 break;
11742 case '-':
11743 if (qstrcmp(&argv[optInd][2],"help")==0)
11744 {
11745 usage(argv[0],versionString);
11746 exit(0);
11747 }
11748 else if (qstrcmp(&argv[optInd][2],"version")==0)
11749 {
11750 version(false);
11752 exit(0);
11753 }
11754 else if ((qstrcmp(&argv[optInd][2],"Version")==0) ||
11755 (qstrcmp(&argv[optInd][2],"VERSION")==0))
11756 {
11757 version(true);
11759 exit(0);
11760 }
11761 else
11762 {
11763 err("Unknown option \"-{}\"\n",&argv[optInd][1]);
11764 usage(argv[0],versionString);
11765 exit(1);
11766 }
11767 break;
11768 case 'b':
11769 setvbuf(stdout,nullptr,_IONBF,0);
11770 break;
11771 case 'q':
11772 quiet = true;
11773 break;
11774 case 'h':
11775 case '?':
11776 usage(argv[0],versionString);
11777 exit(0);
11778 break;
11779 default:
11780 err("Unknown option \"-{:c}\"\n",argv[optInd][1]);
11781 usage(argv[0],versionString);
11782 exit(1);
11783 }
11784 optInd++;
11785 }
11786
11787 /**************************************************************************
11788 * Parse or generate the config file *
11789 **************************************************************************/
11790
11791 initTracing(traceName.data(),traceTiming);
11792 TRACE("Doxygen version used: {}",getFullVersion());
11793 Config::init();
11794
11795 FileInfo configFileInfo1("Doxyfile"),configFileInfo2("doxyfile");
11796 if (optInd>=argc)
11797 {
11798 if (configFileInfo1.exists())
11799 {
11800 configName="Doxyfile";
11801 }
11802 else if (configFileInfo2.exists())
11803 {
11804 configName="doxyfile";
11805 }
11806 else if (genConfig)
11807 {
11808 configName="Doxyfile";
11809 }
11810 else
11811 {
11812 err("Doxyfile not found and no input file specified!\n");
11813 usage(argv[0],versionString);
11814 exit(1);
11815 }
11816 }
11817 else
11818 {
11819 FileInfo fi(argv[optInd]);
11820 if (fi.exists() || qstrcmp(argv[optInd],"-")==0 || genConfig)
11821 {
11822 configName=argv[optInd];
11823 }
11824 else
11825 {
11826 err("configuration file {} not found!\n",argv[optInd]);
11827 usage(argv[0],versionString);
11828 exit(1);
11829 }
11830 }
11831
11832 if (genConfig)
11833 {
11834 generateConfigFile(configName,shortList);
11836 exit(0);
11837 }
11838
11839 if (!Config::parse(configName,updateConfig,diffList))
11840 {
11841 err("could not open or read configuration file {}!\n",configName);
11843 exit(1);
11844 }
11845
11846 if (diffList!=Config::CompareMode::Full)
11847 {
11849 compareDoxyfile(diffList);
11851 exit(0);
11852 }
11853
11854 if (updateConfig)
11855 {
11857 generateConfigFile(configName,shortList,TRUE);
11859 exit(0);
11860 }
11861
11862 /* Perlmod wants to know the path to the config file.*/
11863 FileInfo configFileInfo(configName.str());
11864 setPerlModDoxyfile(configFileInfo.absFilePath());
11865
11866 /* handle -q option */
11867 if (quiet) Config_updateBool(QUIET,TRUE);
11868}
11869
11870/** check and resolve config options */
11872{
11873 AUTO_TRACE();
11874
11879}
11880
11881/** adjust globals that depend on configuration settings. */
11883{
11884 AUTO_TRACE();
11885 Doxygen::globalNamespaceDef = createNamespaceDef("<globalScope>",1,1,"<globalScope>");
11895
11896 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11897
11898 /* Set the global html file extension. */
11899 Doxygen::htmlFileExtension = Config_getString(HTML_FILE_EXTENSION);
11900
11901
11903 Config_getBool(CALLER_GRAPH) ||
11904 Config_getBool(REFERENCES_RELATION) ||
11905 Config_getBool(REFERENCED_BY_RELATION);
11906
11907 /**************************************************************************
11908 * Add custom extension mappings
11909 **************************************************************************/
11910
11911 const StringVector &extMaps = Config_getList(EXTENSION_MAPPING);
11912 for (const auto &mapping : extMaps)
11913 {
11914 QCString mapStr = mapping;
11915 int i=mapStr.find('=');
11916 if (i==-1)
11917 {
11918 continue;
11919 }
11920 else
11921 {
11922 QCString ext = mapStr.left(i).stripWhiteSpace().lower();
11923 QCString language = mapStr.mid(i+1).stripWhiteSpace().lower();
11924 if (ext.isEmpty() || language.isEmpty())
11925 {
11926 continue;
11927 }
11928
11929 if (!updateLanguageMapping(ext,language))
11930 {
11931 err("Failed to map file extension '{}' to unsupported language '{}'.\n"
11932 "Check the EXTENSION_MAPPING setting in the config file.\n",
11933 ext,language);
11934 }
11935 else
11936 {
11937 msg("Adding custom extension mapping: '{}' will be treated as language '{}'\n",
11938 ext,language);
11939 }
11940 }
11941 }
11942 // create input file exncodings
11943
11944 // check INPUT_ENCODING
11945 void *cd = portable_iconv_open("UTF-8",Config_getString(INPUT_ENCODING).data());
11946 if (cd==reinterpret_cast<void *>(-1))
11947 {
11948 term("unsupported character conversion: '{}'->'UTF-8': {}\n"
11949 "Check the 'INPUT_ENCODING' setting in the config file!\n",
11950 Config_getString(INPUT_ENCODING),strerror(errno));
11951 }
11952 else
11953 {
11955 }
11956
11957 // check and split INPUT_FILE_ENCODING
11958 const StringVector &fileEncod = Config_getList(INPUT_FILE_ENCODING);
11959 for (const auto &mapping : fileEncod)
11960 {
11961 QCString mapStr = mapping;
11962 int i=mapStr.find('=');
11963 if (i==-1)
11964 {
11965 continue;
11966 }
11967 else
11968 {
11969 QCString pattern = mapStr.left(i).stripWhiteSpace().lower();
11970 QCString encoding = mapStr.mid(i+1).stripWhiteSpace().lower();
11971 if (pattern.isEmpty() || encoding.isEmpty())
11972 {
11973 continue;
11974 }
11975 cd = portable_iconv_open("UTF-8",encoding.data());
11976 if (cd==reinterpret_cast<void *>(-1))
11977 {
11978 term("unsupported character conversion: '{}'->'UTF-8': {}\n"
11979 "Check the 'INPUT_FILE_ENCODING' setting in the config file!\n",
11980 encoding,strerror(errno));
11981 }
11982 else
11983 {
11985 }
11986
11987 Doxygen::inputFileEncodingList.emplace_back(pattern, encoding);
11988 }
11989 }
11990
11991 // add predefined macro name to a dictionary
11992 const StringVector &expandAsDefinedList =Config_getList(EXPAND_AS_DEFINED);
11993 for (const auto &s : expandAsDefinedList)
11994 {
11996 }
11997
11998 // read aliases and store them in a dictionary
11999 readAliases();
12000
12001 // store number of spaces in a tab into Doxygen::spaces
12002 int tabSize = Config_getInt(TAB_SIZE);
12003 Doxygen::spaces.resize(tabSize);
12004 for (int sp=0; sp<tabSize; sp++) Doxygen::spaces.at(sp)=' ';
12005 Doxygen::spaces.at(tabSize)='\0';
12006}
12007
12008#ifdef HAS_SIGNALS
12009static void stopDoxygen(int)
12010{
12011 signal(SIGINT,SIG_DFL); // Re-register signal handler for default action
12012 Dir thisDir;
12013 msg("Cleaning up...\n");
12014 if (!Doxygen::filterDBFileName.isEmpty())
12015 {
12016 thisDir.remove(Doxygen::filterDBFileName.str());
12017 }
12018 killpg(0,SIGINT);
12020 exitTracing();
12021 exit(1);
12022}
12023#endif
12024
12025static void writeTagFile()
12026{
12027 QCString generateTagFile = Config_getString(GENERATE_TAGFILE);
12028 if (generateTagFile.isEmpty()) return;
12029
12030 std::ofstream f = Portable::openOutputStream(generateTagFile);
12031 if (!f.is_open())
12032 {
12033 err("cannot open tag file {} for writing\n", generateTagFile);
12034 return;
12035 }
12036 TextStream tagFile(&f);
12037 tagFile << "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>\n";
12038 tagFile << "<tagfile doxygen_version=\"" << getDoxygenVersion() << "\"";
12039 std::string gitVersion = getGitVersion();
12040 if (!gitVersion.empty())
12041 {
12042 tagFile << " doxygen_gitid=\"" << gitVersion << "\"";
12043 }
12044 tagFile << ">\n";
12045
12046 // for each file
12047 for (const auto &fn : *Doxygen::inputNameLinkedMap)
12048 {
12049 for (const auto &fd : *fn)
12050 {
12051 if (fd->isLinkableInProject()) fd->writeTagFile(tagFile);
12052 }
12053 }
12054 // for each class
12055 for (const auto &cd : *Doxygen::classLinkedMap)
12056 {
12057 ClassDefMutable *cdm = toClassDefMutable(cd.get());
12058 if (cdm && cdm->isLinkableInProject())
12059 {
12060 cdm->writeTagFile(tagFile);
12061 }
12062 }
12063 // for each concept
12064 for (const auto &cd : *Doxygen::conceptLinkedMap)
12065 {
12066 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
12067 if (cdm && cdm->isLinkableInProject())
12068 {
12069 cdm->writeTagFile(tagFile);
12070 }
12071 }
12072 // for each namespace
12073 for (const auto &nd : *Doxygen::namespaceLinkedMap)
12074 {
12076 if (ndm && nd->isLinkableInProject())
12077 {
12078 ndm->writeTagFile(tagFile);
12079 }
12080 }
12081 // for each group
12082 for (const auto &gd : *Doxygen::groupLinkedMap)
12083 {
12084 if (gd->isLinkableInProject()) gd->writeTagFile(tagFile);
12085 }
12086 // for each module
12087 for (const auto &mod : ModuleManager::instance().modules())
12088 {
12089 if (mod->isLinkableInProject()) mod->writeTagFile(tagFile);
12090 }
12091 // for each page
12092 for (const auto &pd : *Doxygen::pageLinkedMap)
12093 {
12094 if (pd->isLinkableInProject()) pd->writeTagFile(tagFile);
12095 }
12096 // for each directory
12097 for (const auto &dd : *Doxygen::dirLinkedMap)
12098 {
12099 if (dd->isLinkableInProject()) dd->writeTagFile(tagFile);
12100 }
12101 if (Doxygen::mainPage) Doxygen::mainPage->writeTagFile(tagFile);
12102
12103 tagFile << "</tagfile>\n";
12104}
12105
12106static void exitDoxygen() noexcept
12107{
12108 if (!g_successfulRun) // premature exit
12109 {
12110 Dir thisDir;
12111 msg("Exiting...\n");
12112 if (!Doxygen::filterDBFileName.isEmpty())
12113 {
12114 thisDir.remove(Doxygen::filterDBFileName.str());
12115 }
12116 }
12117}
12118
12119static QCString createOutputDirectory(const QCString &baseDirName,
12120 const QCString &formatDirName,
12121 const char *defaultDirName)
12122{
12123 QCString result = formatDirName;
12124 if (result.isEmpty())
12125 {
12126 result = baseDirName + defaultDirName;
12127 }
12128 else if (formatDirName[0]!='/' && (formatDirName.length()==1 || formatDirName[1]!=':'))
12129 {
12130 result.prepend(baseDirName+"/");
12131 }
12132 Dir formatDir(result.str());
12133 if (!formatDir.exists() && !formatDir.mkdir(result.str()))
12134 {
12135 term("Could not create output directory {}\n", result);
12136 }
12137 return result;
12138}
12139
12141{
12142 StringUnorderedSet killSet;
12143
12144 const StringVector &exclPatterns = Config_getList(EXCLUDE_PATTERNS);
12145 bool alwaysRecursive = Config_getBool(RECURSIVE);
12146 StringUnorderedSet excludeNameSet;
12147
12148 // gather names of all files in the include path
12149 g_s.begin("Searching for include files...\n");
12150 killSet.clear();
12151 const StringVector &includePathList = Config_getList(INCLUDE_PATH);
12152 for (const auto &s : includePathList)
12153 {
12154 size_t plSize = Config_getList(INCLUDE_FILE_PATTERNS).size();
12155 const StringVector &pl = plSize==0 ? Config_getList(FILE_PATTERNS) :
12156 Config_getList(INCLUDE_FILE_PATTERNS);
12157 readFileOrDirectory(s, // s
12159 nullptr, // exclSet
12160 &pl, // patList
12161 &exclPatterns, // exclPatList
12162 nullptr, // resultList
12163 nullptr, // resultSet
12164 false, // INCLUDE_PATH isn't recursive
12165 TRUE, // errorIfNotExist
12166 &killSet); // killSet
12167 }
12168 g_s.end();
12169
12170 g_s.begin("Searching for example files...\n");
12171 killSet.clear();
12172 const StringVector &examplePathList = Config_getList(EXAMPLE_PATH);
12173 for (const auto &s : examplePathList)
12174 {
12175 readFileOrDirectory(s, // s
12177 nullptr, // exclSet
12178 &Config_getList(EXAMPLE_PATTERNS), // patList
12179 nullptr, // exclPatList
12180 nullptr, // resultList
12181 nullptr, // resultSet
12182 (alwaysRecursive || Config_getBool(EXAMPLE_RECURSIVE)), // recursive
12183 TRUE, // errorIfNotExist
12184 &killSet); // killSet
12185 }
12186 g_s.end();
12187
12188 g_s.begin("Searching for images...\n");
12189 killSet.clear();
12190 const StringVector &imagePathList=Config_getList(IMAGE_PATH);
12191 for (const auto &s : imagePathList)
12192 {
12193 readFileOrDirectory(s, // s
12195 nullptr, // exclSet
12196 nullptr, // patList
12197 nullptr, // exclPatList
12198 nullptr, // resultList
12199 nullptr, // resultSet
12200 alwaysRecursive, // recursive
12201 TRUE, // errorIfNotExist
12202 &killSet); // killSet
12203 }
12204 g_s.end();
12205
12206 g_s.begin("Searching for dot files...\n");
12207 killSet.clear();
12208 const StringVector &dotFileList=Config_getList(DOTFILE_DIRS);
12209 for (const auto &s : dotFileList)
12210 {
12211 readFileOrDirectory(s, // s
12213 nullptr, // exclSet
12214 nullptr, // patList
12215 nullptr, // exclPatList
12216 nullptr, // resultList
12217 nullptr, // resultSet
12218 alwaysRecursive, // recursive
12219 TRUE, // errorIfNotExist
12220 &killSet); // killSet
12221 }
12222 g_s.end();
12223
12224 g_s.begin("Searching for msc files...\n");
12225 killSet.clear();
12226 const StringVector &mscFileList=Config_getList(MSCFILE_DIRS);
12227 for (const auto &s : mscFileList)
12228 {
12229 readFileOrDirectory(s, // s
12231 nullptr, // exclSet
12232 nullptr, // patList
12233 nullptr, // exclPatList
12234 nullptr, // resultList
12235 nullptr, // resultSet
12236 alwaysRecursive, // recursive
12237 TRUE, // errorIfNotExist
12238 &killSet); // killSet
12239 }
12240 g_s.end();
12241
12242 g_s.begin("Searching for dia files...\n");
12243 killSet.clear();
12244 const StringVector &diaFileList=Config_getList(DIAFILE_DIRS);
12245 for (const auto &s : diaFileList)
12246 {
12247 readFileOrDirectory(s, // s
12249 nullptr, // exclSet
12250 nullptr, // patList
12251 nullptr, // exclPatList
12252 nullptr, // resultList
12253 nullptr, // resultSet
12254 alwaysRecursive, // recursive
12255 TRUE, // errorIfNotExist
12256 &killSet); // killSet
12257 }
12258 g_s.end();
12259
12260 g_s.begin("Searching for plantuml files...\n");
12261 killSet.clear();
12262 const StringVector &plantUmlFileList=Config_getList(PLANTUMLFILE_DIRS);
12263 for (const auto &s : plantUmlFileList)
12264 {
12265 readFileOrDirectory(s, // s
12267 nullptr, // exclSet
12268 nullptr, // patList
12269 nullptr, // exclPatList
12270 nullptr, // resultList
12271 nullptr, // resultSet
12272 alwaysRecursive, // recursive
12273 TRUE, // errorIfNotExist
12274 &killSet); // killSet
12275 }
12276 g_s.end();
12277
12278 g_s.begin("Searching for files to exclude\n");
12279 const StringVector &excludeList = Config_getList(EXCLUDE);
12280 for (const auto &s : excludeList)
12281 {
12282 readFileOrDirectory(s, // s
12283 nullptr, // fnDict
12284 nullptr, // exclSet
12285 &Config_getList(FILE_PATTERNS), // patList
12286 nullptr, // exclPatList
12287 nullptr, // resultList
12288 &excludeNameSet, // resultSet
12289 alwaysRecursive, // recursive
12290 FALSE); // errorIfNotExist
12291 }
12292 g_s.end();
12293
12294 /**************************************************************************
12295 * Determine Input Files *
12296 **************************************************************************/
12297
12298 g_s.begin("Searching INPUT for files to process...\n");
12299 killSet.clear();
12300 Doxygen::inputPaths.clear();
12301 const StringVector &inputList=Config_getList(INPUT);
12302 for (const auto &s : inputList)
12303 {
12304 QCString path = s;
12305 size_t l = path.length();
12306 if (l>0)
12307 {
12308 // strip trailing slashes
12309 if (path.at(l-1)=='\\' || path.at(l-1)=='/') path=path.left(l-1);
12310
12312 path, // s
12314 &excludeNameSet, // exclSet
12315 &Config_getList(FILE_PATTERNS), // patList
12316 &exclPatterns, // exclPatList
12317 &g_inputFiles, // resultList
12318 nullptr, // resultSet
12319 alwaysRecursive, // recursive
12320 TRUE, // errorIfNotExist
12321 &killSet, // killSet
12322 &Doxygen::inputPaths); // paths
12323 }
12324 }
12325
12326 // Sort the FileDef objects by full path to get a predictable ordering over multiple runs
12327 std::stable_sort(Doxygen::inputNameLinkedMap->begin(),
12329 [](const auto &f1,const auto &f2)
12330 {
12331 return qstricmp_sort(f1->fullName(),f2->fullName())<0;
12332 });
12333 for (auto &fileName : *Doxygen::inputNameLinkedMap)
12334 {
12335 if (fileName->size()>1)
12336 {
12337 std::stable_sort(fileName->begin(),fileName->end(),[](const auto &f1,const auto &f2)
12338 {
12339 return qstricmp_sort(f1->absFilePath(),f2->absFilePath())<0;
12340 });
12341 }
12342 }
12343 if (Doxygen::inputNameLinkedMap->empty())
12344 {
12345 warn_uncond("No files to be processed, please check your settings, in particular INPUT, FILE_PATTERNS, and RECURSIVE\n");
12346 }
12347 g_s.end();
12348}
12349
12350
12352{
12353 if (Config_getBool(MARKDOWN_SUPPORT))
12354 {
12355 QCString mdfileAsMainPage = Config_getString(USE_MDFILE_AS_MAINPAGE);
12356 if (mdfileAsMainPage.isEmpty()) return;
12357 FileInfo fi(mdfileAsMainPage.data());
12358 if (!fi.exists())
12359 {
12360 warn_uncond("Specified markdown mainpage '{}' does not exist\n",mdfileAsMainPage);
12361 return;
12362 }
12363 bool ambig = false;
12364 if (findFileDef(Doxygen::inputNameLinkedMap,fi.absFilePath(),ambig)==nullptr)
12365 {
12366 warn_uncond("Specified markdown mainpage '{}' has not been defined as input file\n",mdfileAsMainPage);
12367 return;
12368 }
12369 }
12370}
12371
12373{
12374 AUTO_TRACE();
12375 std::atexit(exitDoxygen);
12376
12377 Portable::correctPath(Config_getList(EXTERNAL_TOOL_PATH));
12378
12379#if USE_LIBCLANG
12380 Doxygen::clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING);
12381#endif
12382
12383 // we would like to show the versionString earlier, but we first have to handle the configuration file
12384 // to know the value of the QUIET setting.
12385 QCString versionString = getFullVersion();
12386 msg("Doxygen version used: {}\n",versionString);
12387
12389
12390 /**************************************************************************
12391 * Make sure the output directory exists
12392 **************************************************************************/
12393 QCString outputDirectory = Config_getString(OUTPUT_DIRECTORY);
12394 if (!g_singleComment)
12395 {
12396 if (outputDirectory.isEmpty())
12397 {
12398 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,Dir::currentDirPath());
12399 }
12400 else
12401 {
12402 Dir dir(outputDirectory.str());
12403 if (!dir.exists())
12404 {
12406 if (!dir.mkdir(outputDirectory.str()))
12407 {
12408 term("tag OUTPUT_DIRECTORY: Output directory '{}' does not "
12409 "exist and cannot be created\n",outputDirectory);
12410 }
12411 else
12412 {
12413 msg("Notice: Output directory '{}' does not exist. "
12414 "I have created it for you.\n", outputDirectory);
12415 }
12416 dir.setPath(outputDirectory.str());
12417 }
12418 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,dir.absPath());
12419 }
12420 }
12421 AUTO_TRACE_ADD("outputDirectory={}",outputDirectory);
12422
12423 /**************************************************************************
12424 * Initialize global lists and dictionaries
12425 **************************************************************************/
12426
12427 // also scale lookup cache with SYMBOL_CACHE_SIZE
12428 int cacheSize = Config_getInt(LOOKUP_CACHE_SIZE);
12429 if (cacheSize<0) cacheSize=0;
12430 if (cacheSize>9) cacheSize=9;
12431 uint32_t lookupSize = 65536 << cacheSize;
12434
12435#ifdef HAS_SIGNALS
12436 signal(SIGINT, stopDoxygen);
12437#endif
12438
12439 uint32_t pid = Portable::pid();
12440 Doxygen::filterDBFileName.sprintf("doxygen_filterdb_%d.tmp",pid);
12441 Doxygen::filterDBFileName.prepend(outputDirectory+"/");
12442
12443 /**************************************************************************
12444 * Check/create output directories *
12445 **************************************************************************/
12446
12447 bool generateHtml = Config_getBool(GENERATE_HTML);
12448 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
12449 bool generateXml = Config_getBool(GENERATE_XML);
12450 bool generateLatex = Config_getBool(GENERATE_LATEX);
12451 bool generateRtf = Config_getBool(GENERATE_RTF);
12452 bool generateMan = Config_getBool(GENERATE_MAN);
12453 bool generateSql = Config_getBool(GENERATE_SQLITE3);
12454 QCString htmlOutput;
12455 QCString docbookOutput;
12456 QCString xmlOutput;
12457 QCString latexOutput;
12458 QCString rtfOutput;
12459 QCString manOutput;
12460 QCString sqlOutput;
12461
12462 if (!g_singleComment)
12463 {
12464 if (generateHtml)
12465 {
12466 htmlOutput = createOutputDirectory(outputDirectory,Config_getString(HTML_OUTPUT),"/html");
12467 Config_updateString(HTML_OUTPUT,htmlOutput);
12468
12469 QCString sitemapUrl = Config_getString(SITEMAP_URL);
12470 bool generateSitemap = !sitemapUrl.isEmpty();
12471 if (generateSitemap && !sitemapUrl.endsWith("/"))
12472 {
12473 Config_updateString(SITEMAP_URL,sitemapUrl+"/");
12474 }
12475
12476 // add HTML indexers that are enabled
12477 bool generateHtmlHelp = Config_getBool(GENERATE_HTMLHELP);
12478 bool generateEclipseHelp = Config_getBool(GENERATE_ECLIPSEHELP);
12479 bool generateQhp = Config_getBool(GENERATE_QHP);
12480 bool generateTreeView = Config_getBool(GENERATE_TREEVIEW);
12481 bool generateDocSet = Config_getBool(GENERATE_DOCSET);
12482 if (generateEclipseHelp) Doxygen::indexList->addIndex<EclipseHelp>();
12483 if (generateHtmlHelp) Doxygen::indexList->addIndex<HtmlHelp>();
12484 if (generateQhp) Doxygen::indexList->addIndex<Qhp>();
12485 if (generateSitemap) Doxygen::indexList->addIndex<Sitemap>();
12486 if (generateTreeView) Doxygen::indexList->addIndex<FTVHelp>(TRUE);
12487 if (generateDocSet) Doxygen::indexList->addIndex<DocSets>();
12488 Doxygen::indexList->addIndex<Crawlmap>();
12489 Doxygen::indexList->initialize();
12490 }
12491
12492 if (generateDocbook)
12493 {
12494 docbookOutput = createOutputDirectory(outputDirectory,Config_getString(DOCBOOK_OUTPUT),"/docbook");
12495 Config_updateString(DOCBOOK_OUTPUT,docbookOutput);
12496 }
12497
12498 if (generateXml)
12499 {
12500 xmlOutput = createOutputDirectory(outputDirectory,Config_getString(XML_OUTPUT),"/xml");
12501 Config_updateString(XML_OUTPUT,xmlOutput);
12502 }
12503
12504 if (generateLatex)
12505 {
12506 latexOutput = createOutputDirectory(outputDirectory,Config_getString(LATEX_OUTPUT), "/latex");
12507 Config_updateString(LATEX_OUTPUT,latexOutput);
12508 }
12509
12510 if (generateRtf)
12511 {
12512 rtfOutput = createOutputDirectory(outputDirectory,Config_getString(RTF_OUTPUT),"/rtf");
12513 Config_updateString(RTF_OUTPUT,rtfOutput);
12514 }
12515
12516 if (generateMan)
12517 {
12518 manOutput = createOutputDirectory(outputDirectory,Config_getString(MAN_OUTPUT),"/man");
12519 Config_updateString(MAN_OUTPUT,manOutput);
12520 }
12521
12522 if (generateSql)
12523 {
12524 sqlOutput = createOutputDirectory(outputDirectory,Config_getString(SQLITE3_OUTPUT),"/sqlite3");
12525 Config_updateString(SQLITE3_OUTPUT,sqlOutput);
12526 }
12527 }
12528
12529 if (Config_getBool(HAVE_DOT))
12530 {
12531 QCString curFontPath = Config_getString(DOT_FONTPATH);
12532 if (curFontPath.isEmpty())
12533 {
12534 Portable::getenv("DOTFONTPATH");
12535 QCString newFontPath = ".";
12536 if (!curFontPath.isEmpty())
12537 {
12538 newFontPath+=Portable::pathListSeparator();
12539 newFontPath+=curFontPath;
12540 }
12541 Portable::setenv("DOTFONTPATH",qPrint(newFontPath));
12542 }
12543 else
12544 {
12545 Portable::setenv("DOTFONTPATH",qPrint(curFontPath));
12546 }
12547 }
12548
12549 /**************************************************************************
12550 * Handle layout file *
12551 **************************************************************************/
12552
12554 QCString layoutFileName = Config_getString(LAYOUT_FILE);
12555 bool defaultLayoutUsed = FALSE;
12556 if (layoutFileName.isEmpty())
12557 {
12558 layoutFileName = Config_updateString(LAYOUT_FILE,"DoxygenLayout.xml");
12559 defaultLayoutUsed = TRUE;
12560 }
12561 AUTO_TRACE_ADD("defaultLayoutUsed={}, layoutFileName={}",defaultLayoutUsed,layoutFileName);
12562
12563 FileInfo fi(layoutFileName.str());
12564 if (fi.exists())
12565 {
12566 msg("Parsing layout file {}...\n",layoutFileName);
12567 LayoutDocManager::instance().parse(layoutFileName);
12568 }
12569 else if (!defaultLayoutUsed)
12570 {
12571 warn_uncond("failed to open layout file '{}' for reading! Using default settings.\n",layoutFileName);
12572 }
12573 printLayout();
12574
12575 /**************************************************************************
12576 * Read and preprocess input *
12577 **************************************************************************/
12578
12579 // prevent search in the output directories
12580 StringVector exclPatterns = Config_getList(EXCLUDE_PATTERNS);
12581 if (generateHtml) exclPatterns.push_back(htmlOutput.str());
12582 if (generateDocbook) exclPatterns.push_back(docbookOutput.str());
12583 if (generateXml) exclPatterns.push_back(xmlOutput.str());
12584 if (generateLatex) exclPatterns.push_back(latexOutput.str());
12585 if (generateRtf) exclPatterns.push_back(rtfOutput.str());
12586 if (generateMan) exclPatterns.push_back(manOutput.str());
12587 Config_updateList(EXCLUDE_PATTERNS,exclPatterns);
12588
12589 if (!g_singleComment)
12590 {
12592
12594 }
12595
12596 // Notice: the order of the function calls below is very important!
12597
12598 if (generateHtml && !Config_getBool(USE_MATHJAX))
12599 {
12601 }
12602 if (generateRtf)
12603 {
12605 }
12606 if (generateDocbook)
12607 {
12609 }
12610
12612
12613 /**************************************************************************
12614 * Handle Tag Files *
12615 **************************************************************************/
12616
12617 std::shared_ptr<Entry> root = std::make_shared<Entry>();
12618
12619 if (!g_singleComment)
12620 {
12621 msg("Reading and parsing tag files\n");
12622 const StringVector &tagFileList = Config_getList(TAGFILES);
12623 for (const auto &s : tagFileList)
12624 {
12625 readTagFile(root,s.c_str());
12626 }
12627 }
12628
12629 /**************************************************************************
12630 * Parse source files *
12631 **************************************************************************/
12632
12633 addSTLSupport(root);
12634
12635 g_s.begin("Parsing files\n");
12636 if (g_singleComment)
12637 {
12638 //printf("Parsing comment %s\n",qPrint(g_commentFileName));
12639 if (g_commentFileName=="-")
12640 {
12641 std::string text = fileToString(g_commentFileName).str();
12642 addTerminalCharIfMissing(text,'\n');
12643 generateHtmlForComment("stdin.md",text);
12644 }
12645 else if (FileInfo(g_commentFileName.str()).isFile())
12646 {
12647 std::string text;
12649 addTerminalCharIfMissing(text,'\n');
12651 }
12652 else
12653 {
12654 }
12656 exit(0);
12657 }
12658 else
12659 {
12660 if (Config_getInt(NUM_PROC_THREADS)==1)
12661 {
12663 }
12664 else
12665 {
12667 }
12668 }
12669 g_s.end();
12670
12671 /**************************************************************************
12672 * Gather information *
12673 **************************************************************************/
12674
12675 g_s.begin("Building macro definition list...\n");
12677 g_s.end();
12678
12679 g_s.begin("Building group list...\n");
12680 buildGroupList(root.get());
12681 organizeSubGroups(root.get());
12682 g_s.end();
12683
12684 g_s.begin("Building directory list...\n");
12686 findDirDocumentation(root.get());
12687 g_s.end();
12688
12689 g_s.begin("Building namespace list...\n");
12690 buildNamespaceList(root.get());
12691 findUsingDirectives(root.get());
12692 g_s.end();
12693
12694 g_s.begin("Building file list...\n");
12695 buildFileList(root.get());
12696 g_s.end();
12697
12698 g_s.begin("Building class list...\n");
12699 buildClassList(root.get());
12700 g_s.end();
12701
12702 g_s.begin("Building concept list...\n");
12703 buildConceptList(root.get());
12704 g_s.end();
12705
12706 // build list of using declarations here (global list)
12707 buildListOfUsingDecls(root.get());
12708 g_s.end();
12709
12710 g_s.begin("Computing nesting relations for classes...\n");
12712 g_s.end();
12713 // 1.8.2-20121111: no longer add nested classes to the group as well
12714 //distributeClassGroupRelations();
12715
12716 // calling buildClassList may result in cached relations that
12717 // become invalid after resolveClassNestingRelations(), that's why
12718 // we need to clear the cache here
12719 Doxygen::typeLookupCache->clear();
12720 // we don't need the list of using declaration anymore
12721 g_usingDeclarations.clear();
12722
12723 g_s.begin("Associating documentation with classes...\n");
12724 buildClassDocList(root.get());
12725 g_s.end();
12726
12727 g_s.begin("Associating documentation with concepts...\n");
12728 buildConceptDocList(root.get());
12730 g_s.end();
12731
12732 g_s.begin("Associating documentation with modules...\n");
12733 findModuleDocumentation(root.get());
12734 g_s.end();
12735
12736 g_s.begin("Building example list...\n");
12737 buildExampleList(root.get());
12738 g_s.end();
12739
12740 g_s.begin("Searching for enumerations...\n");
12741 findEnums(root.get());
12742 g_s.end();
12743
12744 // Since buildVarList calls isVarWithConstructor
12745 // and this calls getResolvedClass we need to process
12746 // typedefs first so the relations between classes via typedefs
12747 // are properly resolved. See bug 536385 for an example.
12748 g_s.begin("Searching for documented typedefs...\n");
12749 buildTypedefList(root.get());
12750 g_s.end();
12751
12752 if (Config_getBool(OPTIMIZE_OUTPUT_SLICE))
12753 {
12754 g_s.begin("Searching for documented sequences...\n");
12755 buildSequenceList(root.get());
12756 g_s.end();
12757
12758 g_s.begin("Searching for documented dictionaries...\n");
12759 buildDictionaryList(root.get());
12760 g_s.end();
12761 }
12762
12763 g_s.begin("Searching for members imported via using declarations...\n");
12764 // this should be after buildTypedefList in order to properly import
12765 // used typedefs
12766 findUsingDeclarations(root.get(),TRUE); // do for python packages first
12767 findUsingDeclarations(root.get(),FALSE); // then the rest
12768 g_s.end();
12769
12770 g_s.begin("Searching for included using directives...\n");
12772 g_s.end();
12773
12774 g_s.begin("Searching for documented variables...\n");
12775 buildVarList(root.get());
12776 g_s.end();
12777
12778 g_s.begin("Building interface member list...\n");
12779 buildInterfaceAndServiceList(root.get()); // UNO IDL
12780
12781 g_s.begin("Building member list...\n"); // using class info only !
12782 buildFunctionList(root.get());
12783 g_s.end();
12784
12785 g_s.begin("Searching for friends...\n");
12786 findFriends();
12787 g_s.end();
12788
12789 g_s.begin("Searching for documented defines...\n");
12790 findDefineDocumentation(root.get());
12791 g_s.end();
12792
12793 g_s.begin("Computing class inheritance relations...\n");
12794 findClassEntries(root.get());
12796 g_s.end();
12797
12798 g_s.begin("Computing class usage relations...\n");
12800 g_s.end();
12801
12802 if (Config_getBool(INLINE_SIMPLE_STRUCTS))
12803 {
12804 g_s.begin("Searching for tag less structs...\n");
12806 g_s.end();
12807 }
12808
12809 g_s.begin("Flushing cached template relations that have become invalid...\n");
12811 g_s.end();
12812
12813 g_s.begin("Warn for undocumented namespaces...\n");
12815 g_s.end();
12816
12817 g_s.begin("Computing class relations...\n");
12820 if (Config_getBool(OPTIMIZE_OUTPUT_VHDL))
12821 {
12823 }
12825 g_classEntries.clear();
12826 g_s.end();
12827
12828 g_s.begin("Add enum values to enums...\n");
12829 addEnumValuesToEnums(root.get());
12830 findEnumDocumentation(root.get());
12831 g_s.end();
12832
12833 g_s.begin("Searching for member function documentation...\n");
12834 findObjCMethodDefinitions(root.get());
12835 findMemberDocumentation(root.get()); // may introduce new members !
12836 findUsingDeclImports(root.get()); // may introduce new members !
12837 g_usingClassMap.clear();
12841 g_s.end();
12842
12843 // moved to after finding and copying documentation,
12844 // as this introduces new members see bug 722654
12845 g_s.begin("Creating members for template instances...\n");
12847 g_s.end();
12848
12849 g_s.begin("Building page list...\n");
12850 buildPageList(root.get());
12851 g_s.end();
12852
12853 g_s.begin("Search for main page...\n");
12854 findMainPage(root.get());
12855 findMainPageTagFiles(root.get());
12856 g_s.end();
12857
12858 g_s.begin("Computing page relations...\n");
12859 computePageRelations(root.get());
12861 g_s.end();
12862
12863 g_s.begin("Determining the scope of groups...\n");
12864 findGroupScope(root.get());
12865 g_s.end();
12866
12867 g_s.begin("Computing module relations...\n");
12868 auto &mm = ModuleManager::instance();
12869 mm.resolvePartitions();
12870 mm.resolveImports();
12871 mm.collectExportedSymbols();
12872 g_s.end();
12873
12874 auto memberNameComp = [](const MemberNameLinkedMap::Ptr &n1,const MemberNameLinkedMap::Ptr &n2)
12875 {
12876 return qstricmp_sort(n1->memberName().data()+getPrefixIndex(n1->memberName()),
12877 n2->memberName().data()+getPrefixIndex(n2->memberName())
12878 )<0;
12879 };
12880
12881 auto classComp = [](const ClassLinkedMap::Ptr &c1,const ClassLinkedMap::Ptr &c2)
12882 {
12883 if (Config_getBool(SORT_BY_SCOPE_NAME))
12884 {
12885 return qstricmp_sort(c1->name(), c2->name())<0;
12886 }
12887 else
12888 {
12889 int i = qstricmp_sort(c1->className(), c2->className());
12890 return i==0 ? qstricmp_sort(c1->name(), c2->name())<0 : i<0;
12891 }
12892 };
12893
12894 auto namespaceComp = [](const NamespaceLinkedMap::Ptr &n1,const NamespaceLinkedMap::Ptr &n2)
12895 {
12896 return qstricmp_sort(n1->name(),n2->name())<0;
12897 };
12898
12899 auto conceptComp = [](const ConceptLinkedMap::Ptr &c1,const ConceptLinkedMap::Ptr &c2)
12900 {
12901 return qstricmp_sort(c1->name(),c2->name())<0;
12902 };
12903
12904 g_s.begin("Sorting lists...\n");
12905 std::stable_sort(Doxygen::memberNameLinkedMap->begin(),
12907 memberNameComp);
12908 std::stable_sort(Doxygen::functionNameLinkedMap->begin(),
12910 memberNameComp);
12911 std::stable_sort(Doxygen::hiddenClassLinkedMap->begin(),
12913 classComp);
12914 std::stable_sort(Doxygen::classLinkedMap->begin(),
12916 classComp);
12917 std::stable_sort(Doxygen::conceptLinkedMap->begin(),
12919 conceptComp);
12920 std::stable_sort(Doxygen::namespaceLinkedMap->begin(),
12922 namespaceComp);
12923 g_s.end();
12924
12925 g_s.begin("Determining which enums are documented\n");
12927 g_s.end();
12928
12929 g_s.begin("Computing member relations...\n");
12932 g_s.end();
12933
12934 g_s.begin("Building full member lists recursively...\n");
12936 g_s.end();
12937
12938 g_s.begin("Adding members to member groups.\n");
12940 g_s.end();
12941
12942 if (Config_getBool(DISTRIBUTE_GROUP_DOC))
12943 {
12944 g_s.begin("Distributing member group documentation.\n");
12946 g_s.end();
12947 }
12948
12949 g_s.begin("Computing member references...\n");
12951 g_s.end();
12952
12953 if (Config_getBool(INHERIT_DOCS))
12954 {
12955 g_s.begin("Inheriting documentation...\n");
12957 g_s.end();
12958 }
12959
12960
12961 // compute the shortest possible names of all files
12962 // without losing the uniqueness of the file names.
12963 g_s.begin("Generating disk names...\n");
12965 g_s.end();
12966
12967 g_s.begin("Adding source references...\n");
12969 g_s.end();
12970
12971 g_s.begin("Adding xrefitems...\n");
12974 g_s.end();
12975
12976 g_s.begin("Sorting member lists...\n");
12978 g_s.end();
12979
12980 g_s.begin("Setting anonymous enum type...\n");
12982 g_s.end();
12983
12984 g_s.begin("Computing dependencies between directories...\n");
12986 g_s.end();
12987
12988 g_s.begin("Generating citations page...\n");
12990 g_s.end();
12991
12992 g_s.begin("Counting members...\n");
12993 countMembers();
12994 g_s.end();
12995
12996 g_s.begin("Counting data structures...\n");
12998 g_s.end();
12999
13000 g_s.begin("Resolving user defined references...\n");
13002 g_s.end();
13003
13004 g_s.begin("Finding anchors and sections in the documentation...\n");
13006 g_s.end();
13007
13008 g_s.begin("Transferring function references...\n");
13010 g_s.end();
13011
13012 g_s.begin("Combining using relations...\n");
13014 g_s.end();
13015
13017 g_s.begin("Adding members to index pages...\n");
13019 addToIndices();
13020 g_s.end();
13021
13022 g_s.begin("Correcting members for VHDL...\n");
13024 g_s.end();
13025
13026 g_s.begin("Computing tooltip texts...\n");
13028 g_s.end();
13029
13030 if (Config_getBool(SORT_GROUP_NAMES))
13031 {
13032 std::stable_sort(Doxygen::groupLinkedMap->begin(),
13034 [](const auto &g1,const auto &g2)
13035 { return g1->groupTitle() < g2->groupTitle(); });
13036
13037 for (const auto &gd : *Doxygen::groupLinkedMap)
13038 {
13039 gd->sortSubGroups();
13040 }
13041 }
13042
13043 printNavTree(root.get(),0);
13045}
13046
13048{
13049 AUTO_TRACE();
13050 /**************************************************************************
13051 * Initialize output generators *
13052 **************************************************************************/
13053
13054 /// add extra languages for which we can only produce syntax highlighted code
13056
13057 //// dump all symbols
13058 if (g_dumpSymbolMap)
13059 {
13060 dumpSymbolMap();
13061 exit(0);
13062 }
13063
13064 bool generateHtml = Config_getBool(GENERATE_HTML);
13065 bool generateLatex = Config_getBool(GENERATE_LATEX);
13066 bool generateMan = Config_getBool(GENERATE_MAN);
13067 bool generateRtf = Config_getBool(GENERATE_RTF);
13068 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
13069
13070
13072 if (generateHtml)
13073 {
13077 }
13078 if (generateLatex)
13079 {
13082 }
13083 if (generateDocbook)
13084 {
13087 }
13088 if (generateMan)
13089 {
13090 g_outputList->add<ManGenerator>();
13092 }
13093 if (generateRtf)
13094 {
13095 g_outputList->add<RTFGenerator>();
13097 }
13098 if (Config_getBool(USE_HTAGS))
13099 {
13101 QCString htmldir = Config_getString(HTML_OUTPUT);
13102 if (!Htags::execute(htmldir))
13103 err("USE_HTAGS is YES but htags(1) failed. \n");
13104 else if (!Htags::loadFilemap(htmldir))
13105 err("htags(1) ended normally but failed to load the filemap. \n");
13106 }
13107
13108 /**************************************************************************
13109 * Generate documentation *
13110 **************************************************************************/
13111
13112 g_s.begin("Generating style sheet...\n");
13113 //printf("writing style info\n");
13114 g_outputList->writeStyleInfo(0); // write first part
13115 g_s.end();
13116
13117 bool searchEngine = Config_getBool(SEARCHENGINE);
13118 bool serverBasedSearch = Config_getBool(SERVER_BASED_SEARCH);
13119
13120 g_s.begin("Generating search indices...\n");
13121 if (searchEngine && !serverBasedSearch && generateHtml)
13122 {
13124 }
13125
13126 // generate search indices (need to do this before writing other HTML
13127 // pages as these contain a drop down menu with options depending on
13128 // what categories we find in this function.
13129 if (generateHtml && searchEngine)
13130 {
13131 QCString searchDirName = Config_getString(HTML_OUTPUT)+"/search";
13132 Dir searchDir(searchDirName.str());
13133 if (!searchDir.exists() && !searchDir.mkdir(searchDirName.str()))
13134 {
13135 term("Could not create search results directory '{}' $PWD='{}'\n",
13136 searchDirName,Dir::currentDirPath());
13137 }
13138 HtmlGenerator::writeSearchData(searchDirName);
13139 if (!serverBasedSearch) // client side search index
13140 {
13142 }
13143 }
13144 g_s.end();
13145
13146 // copy static stuff
13147 if (generateHtml)
13148 {
13150 copyLogo(Config_getString(HTML_OUTPUT));
13151 copyIcon(Config_getString(HTML_OUTPUT));
13152 copyExtraFiles(Config_getList(HTML_EXTRA_FILES),"HTML_EXTRA_FILES",Config_getString(HTML_OUTPUT));
13153 }
13154 if (generateLatex)
13155 {
13157 copyLogo(Config_getString(LATEX_OUTPUT));
13158 copyIcon(Config_getString(LATEX_OUTPUT));
13159 copyExtraFiles(Config_getList(LATEX_EXTRA_FILES),"LATEX_EXTRA_FILES",Config_getString(LATEX_OUTPUT));
13160 }
13161 if (generateDocbook)
13162 {
13163 copyLogo(Config_getString(DOCBOOK_OUTPUT));
13164 copyIcon(Config_getString(DOCBOOK_OUTPUT));
13165 }
13166 if (generateRtf)
13167 {
13168 copyLogo(Config_getString(RTF_OUTPUT));
13169 copyIcon(Config_getString(RTF_OUTPUT));
13170 copyExtraFiles(Config_getList(RTF_EXTRA_FILES),"RTF_EXTRA_FILES",Config_getString(RTF_OUTPUT));
13171 }
13172
13174 if (fm.hasFormulas() && generateHtml
13175 && !Config_getBool(USE_MATHJAX))
13176 {
13177 g_s.begin("Generating images for formulas in HTML...\n");
13178 fm.generateImages(Config_getString(HTML_OUTPUT), Config_getEnum(HTML_FORMULA_FORMAT)==HTML_FORMULA_FORMAT_t::svg ?
13180 g_s.end();
13181 }
13182 if (fm.hasFormulas() && generateRtf)
13183 {
13184 g_s.begin("Generating images for formulas in RTF...\n");
13186 g_s.end();
13187 }
13188
13189 if (fm.hasFormulas() && generateDocbook)
13190 {
13191 g_s.begin("Generating images for formulas in Docbook...\n");
13193 g_s.end();
13194 }
13195
13196 g_s.begin("Generating example documentation...\n");
13198 g_s.end();
13199
13200 g_s.begin("Generating file sources...\n");
13202 g_s.end();
13203
13204 g_s.begin("Generating file documentation...\n");
13206 g_s.end();
13207
13208 g_s.begin("Generating page documentation...\n");
13210 g_s.end();
13211
13212 g_s.begin("Generating group documentation...\n");
13214 g_s.end();
13215
13216 g_s.begin("Generating class documentation...\n");
13218 g_s.end();
13219
13220 g_s.begin("Generating concept documentation...\n");
13222 g_s.end();
13223
13224 g_s.begin("Generating module documentation...\n");
13226 g_s.end();
13227
13228 g_s.begin("Generating namespace documentation...\n");
13230 g_s.end();
13231
13232 if (Config_getBool(GENERATE_LEGEND))
13233 {
13234 g_s.begin("Generating graph info page...\n");
13236 g_s.end();
13237 }
13238
13239 g_s.begin("Generating directory documentation...\n");
13241 g_s.end();
13242
13243 if (g_outputList->size()>0)
13244 {
13246 }
13247
13248 g_s.begin("finalizing index lists...\n");
13249 Doxygen::indexList->finalize();
13250 g_s.end();
13251
13252 g_s.begin("writing tag file...\n");
13253 writeTagFile();
13254 g_s.end();
13255
13256 if (Config_getBool(GENERATE_XML))
13257 {
13258 g_s.begin("Generating XML output...\n");
13260 generateXML();
13262 g_s.end();
13263 }
13264 if (Config_getBool(GENERATE_SQLITE3))
13265 {
13266 g_s.begin("Generating SQLITE3 output...\n");
13268 g_s.end();
13269 }
13270
13271 if (Config_getBool(GENERATE_AUTOGEN_DEF))
13272 {
13273 g_s.begin("Generating AutoGen DEF output...\n");
13274 generateDEF();
13275 g_s.end();
13276 }
13277 if (Config_getBool(GENERATE_PERLMOD))
13278 {
13279 g_s.begin("Generating Perl module output...\n");
13281 g_s.end();
13282 }
13283 if (generateHtml && searchEngine && serverBasedSearch)
13284 {
13285 g_s.begin("Generating search index\n");
13286 if (Doxygen::searchIndex.kind()==SearchIndexIntf::Internal) // write own search index
13287 {
13289 Doxygen::searchIndex.write(Config_getString(HTML_OUTPUT)+"/search/search.idx");
13290 }
13291 else // write data for external search index
13292 {
13294 QCString searchDataFile = Config_getString(SEARCHDATA_FILE);
13295 if (searchDataFile.isEmpty())
13296 {
13297 searchDataFile="searchdata.xml";
13298 }
13299 if (!Portable::isAbsolutePath(searchDataFile.data()))
13300 {
13301 searchDataFile.prepend(Config_getString(OUTPUT_DIRECTORY)+"/");
13302 }
13303 Doxygen::searchIndex.write(searchDataFile);
13304 }
13305 g_s.end();
13306 }
13307
13308 if (generateRtf)
13309 {
13310 g_s.begin("Combining RTF output...\n");
13311 if (!RTFGenerator::preProcessFileInplace(Config_getString(RTF_OUTPUT),"refman.rtf"))
13312 {
13313 err("An error occurred during post-processing the RTF files!\n");
13314 }
13315 g_s.end();
13316 }
13317
13318 g_s.begin("Running plantuml with JAVA...\n");
13320 g_s.end();
13321
13322 if (Config_getBool(HAVE_DOT))
13323 {
13324 g_s.begin("Running dot...\n");
13326 g_s.end();
13327 }
13328
13329 if (generateHtml &&
13330 Config_getBool(GENERATE_HTMLHELP) &&
13331 !Config_getString(HHC_LOCATION).isEmpty())
13332 {
13333 g_s.begin("Running html help compiler...\n");
13335 g_s.end();
13336 }
13337
13338 if ( generateHtml &&
13339 Config_getBool(GENERATE_QHP) &&
13340 !Config_getString(QHG_LOCATION).isEmpty())
13341 {
13342 g_s.begin("Running qhelpgenerator...\n");
13344 g_s.end();
13345 }
13346
13347 g_outputList->cleanup();
13348
13349 msg("type lookup cache used {}/{} hits={} misses={}\n",
13351 Doxygen::typeLookupCache->capacity(),
13353 Doxygen::typeLookupCache->misses());
13354 msg("symbol lookup cache used {}/{} hits={} misses={}\n",
13356 Doxygen::symbolLookupCache->capacity(),
13358 Doxygen::symbolLookupCache->misses());
13359 int typeCacheParam = computeIdealCacheParam(static_cast<size_t>(Doxygen::typeLookupCache->misses()*2/3)); // part of the cache is flushed, hence the 2/3 correction factor
13360 int symbolCacheParam = computeIdealCacheParam(static_cast<size_t>(Doxygen::symbolLookupCache->misses()));
13361 int cacheParam = std::max(typeCacheParam,symbolCacheParam);
13362 if (cacheParam>Config_getInt(LOOKUP_CACHE_SIZE))
13363 {
13364 msg("Note: based on cache misses the ideal setting for LOOKUP_CACHE_SIZE is {} at the cost of higher memory usage.\n",cacheParam);
13365 }
13366
13368 {
13369
13370 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
13371 if (numThreads<1) numThreads=1;
13372 msg("Total elapsed time: {:.6f} seconds\n(of which an average of {:.6f} seconds per thread waiting for external tools to finish)\n",
13373 (static_cast<double>(Debug::elapsedTime())),
13374 Portable::getSysElapsedTime()/static_cast<double>(numThreads)
13375 );
13376 g_s.print();
13377
13379 msg("finished...\n");
13381 }
13382 else
13383 {
13384 msg("finished...\n");
13385 }
13386
13387
13388 /**************************************************************************
13389 * Start cleaning up *
13390 **************************************************************************/
13391
13393
13395 Dir thisDir;
13396 thisDir.remove(Doxygen::filterDBFileName.str());
13398 exitTracing();
13400 delete Doxygen::clangUsrMap;
13402
13403 //dumpDocNodeSizes();
13404}
void readAliases()
Definition aliases.cpp:170
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:112
void generatePage()
Generate the citations page.
Definition cite.cpp:333
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:201
static void startTimer()
Definition debug.cpp:196
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:1202
static void writeSearchPage()
Definition htmlgen.cpp:3169
static void writeFooterFile(TextStream &t)
Definition htmlgen.cpp:1536
static void writeTabData()
Additional initialization after indices have been created.
Definition htmlgen.cpp:1353
static void writeSearchData(const QCString &dir)
Definition htmlgen.cpp:1362
static void writeExternalSearchPage()
Definition htmlgen.cpp:3268
static void writeStyleSheetFile(TextStream &t)
Definition htmlgen.cpp:1524
static void writeHeaderFile(TextStream &t, const QCString &cssname)
Definition htmlgen.cpp:1530
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:314
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:232
Object representing the matching results.
Definition regex.h:153
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:809
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:1085
void computeDirDependencies()
Definition dirdef.cpp:1159
void generateDirDocs(OutputList &ol)
Definition dirdef.cpp:1176
#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:9916
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:3029
static void buildGroupList(const Entry *root)
Definition doxygen.cpp:425
static void insertMemberAlias(Definition *outerScope, const MemberDef *md)
Definition doxygen.cpp:6600
static void findUsingDeclarations(const Entry *root, bool filterPythonPackages)
Definition doxygen.cpp:2005
static void flushCachedTemplateRelations()
Definition doxygen.cpp:9341
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:9001
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:2825
static bool isSpecialization(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists)
Definition doxygen.cpp:5916
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:2425
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:8055
static void runQHelpGenerator()
static void addConceptToContext(const Entry *root)
Definition doxygen.cpp:1151
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:9939
class Statistics g_s
static void createUsingMemberImportForClass(const Entry *root, ClassDefMutable *cd, const MemberDef *md, const QCString &fileName, const QCString &memName)
Definition doxygen.cpp:2109
static void generateXRefPages()
Definition doxygen.cpp:5509
static void findUsingDeclImports(const Entry *root)
Definition doxygen.cpp:2158
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:1446
static void generateGroupDocs()
Definition doxygen.cpp:9999
static void findDirDocumentation(const Entry *root)
Definition doxygen.cpp:9580
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:1837
static void buildConceptList(const Entry *root)
Definition doxygen.cpp:1276
static void resolveClassNestingRelations()
Definition doxygen.cpp:1331
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:6643
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:1565
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:6127
static void vhdlCorrectMemberProperties()
Definition doxygen.cpp:8299
static void generateExampleDocs()
Definition doxygen.cpp:9955
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:6030
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:9206
static bool tryAddEnumDocsToGroupMember(const Entry *root, const QCString &name)
Definition doxygen.cpp:7936
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:6533
static void findMainPageTagFiles(Entry *root)
Definition doxygen.cpp:9740
static void distributeConceptGroups()
Definition doxygen.cpp:1298
static void transferFunctionDocumentation()
Definition doxygen.cpp:4281
static void setAnonymousEnumType()
Definition doxygen.cpp:8946
static void sortMemberLists()
Definition doxygen.cpp:8851
static void createTemplateInstanceMembers()
Definition doxygen.cpp:8427
void transferStaticInstanceInitializers()
Definition doxygen.cpp:4411
static void findObjCMethodDefinitions(const Entry *root)
Definition doxygen.cpp:7443
static void dumpSymbolMap()
static void buildTypedefList(const Entry *root)
Definition doxygen.cpp:3352
static void findGroupScope(const Entry *root)
Definition doxygen.cpp:440
static void generateFileDocs()
Definition doxygen.cpp:8664
static void findDefineDocumentation(Entry *root)
Definition doxygen.cpp:9493
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:7413
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:6074
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:5709
static void distributeMemberGroupDocumentation()
Definition doxygen.cpp:9244
static void generateNamespaceClassDocs(const ClassLinkedRefMap &classList)
static void addEnumValuesToEnums(const Entry *root)
Definition doxygen.cpp:7646
static void generatePageDocs()
Definition doxygen.cpp:9870
static void resolveUserReferences()
Definition doxygen.cpp:9802
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:3488
static void buildSequenceList(const Entry *root)
Definition doxygen.cpp:3451
static void generateFileSources()
Definition doxygen.cpp:8498
static QCString substituteTemplatesInString(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists, const std::string &src)
Definition doxygen.cpp:5946
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:2585
static void generateClassDocs()
Definition doxygen.cpp:9099
static void buildNamespaceList(const Entry *root)
Definition doxygen.cpp:1670
static int computeIdealCacheParam(size_t v)
static void readTagFile(const std::shared_ptr< Entry > &root, const QCString &tagLine)
static void findIncludedUsingDirectives()
Definition doxygen.cpp:2408
static void addDefineDoc(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:9467
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:8960
void clearAll()
Definition doxygen.cpp:201
static void devUsage()
static void addInterfaceOrServiceToServiceOrSingleton(const Entry *root, ClassDefMutable *cd, QCString const &rname)
Definition doxygen.cpp:3520
static void organizeSubGroups(const Entry *root)
Definition doxygen.cpp:484
static void applyMemberOverrideOptions(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:2097
void searchInputFiles()
static void findFriends()
Definition doxygen.cpp:4184
static void findEnums(const Entry *root)
Definition doxygen.cpp:7471
static void dumpSymbol(TextStream &t, Definition *d)
static void addClassAndNestedClasses(std::vector< ClassDefMutable * > &list, ClassDefMutable *cd)
Definition doxygen.cpp:9076
static void addEnumDocs(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:7895
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:8089
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:9670
static ClassDef::CompoundType convertToCompoundType(EntryType section, TypeSpecifier specifier)
Definition doxygen.cpp:889
static void findUsingDirectives(const Entry *root)
Definition doxygen.cpp:1850
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:8724
std::function< std::unique_ptr< T >() > make_parser_factory()
static void buildExampleList(Entry *root)
Definition doxygen.cpp:9887
static void inheritDocumentation()
Definition doxygen.cpp:9146
static void flushUnresolvedRelations()
Definition doxygen.cpp:9395
static bool isSymbolHidden(const Definition *d)
Definition doxygen.cpp:8893
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:8081
static void findTagLessClasses()
Definition doxygen.cpp:1646
static void generateDiskNames()
static void addToIndices()
Definition doxygen.cpp:8131
static void computeClassRelations()
Definition doxygen.cpp:5312
static void buildFunctionList(const Entry *root)
Definition doxygen.cpp:3878
static void checkPageRelations()
Definition doxygen.cpp:9782
static void findModuleDocumentation(const Entry *root)
Definition doxygen.cpp:1266
static void findEnumDocumentation(const Entry *root)
Definition doxygen.cpp:7969
static void computePageRelations(Entry *root)
Definition doxygen.cpp:9752
static void filterMemberDocumentation(const Entry *root, const QCString &relates)
Definition doxygen.cpp:7263
static QCString createOutputDirectory(const QCString &baseDirName, const QCString &formatDirName, const char *defaultDirName)
void initResources()
static bool isVarWithConstructor(const Entry *root)
Definition doxygen.cpp:2885
static StringSet g_usingDeclarations
Definition doxygen.cpp:189
static void buildDictionaryList(const Entry *root)
Definition doxygen.cpp:3469
static bool haveEqualFileNames(const Entry *root, const MemberDef *md)
Definition doxygen.cpp:9454
static void generateNamespaceDocs()
static void buildClassDocList(const Entry *root)
Definition doxygen.cpp:1137
static void buildPageList(Entry *root)
Definition doxygen.cpp:9643
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:9282
static void mergeCategories()
Definition doxygen.cpp:8446
static const StringUnorderedSet g_compoundKeywords
Definition doxygen.cpp:198
static bool scopeIsTemplate(const Definition *d)
Definition doxygen.cpp:5932
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:1479
static void buildClassList(const Entry *root)
Definition doxygen.cpp:1127
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:3634
static std::unique_ptr< OutlineParserInterface > getParserForFile(const QCString &fn)
static void findUsedTemplateInstances()
Definition doxygen.cpp:5276
static void computeTooltipTexts()
Definition doxygen.cpp:8900
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:3097
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:6471
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:8468
static void generateConceptDocs()
Definition doxygen.cpp:9125
static void combineUsingRelations()
Definition doxygen.cpp:9181
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:2156
static void buildConceptDocList(const Entry *root)
Definition doxygen.cpp:1286
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:5685
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:8803
static void buildInterfaceAndServiceList(const Entry *root)
Definition doxygen.cpp:3582
static void computeMemberRelations()
Definition doxygen.cpp:8411
static void buildListOfUsingDecls(const Entry *root)
Definition doxygen.cpp:1992
static void computeMemberRelationsForBaseClass(const ClassDef *cd, const BaseClassDef *bcd)
Definition doxygen.cpp:8331
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:1939
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:4063
void endTitle(OutputList &ol, const QCString &fileName, const QCString &name)
Definition index.cpp:394
void startFile(OutputList &ol, const QCString &name, 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:5758
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:748
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:579
QCString mergeScopes(const QCString &leftScope, const QCString &rightScope)
Definition util.cpp:5099
QCString normalizeNonTemplateArgumentsInString(const QCString &name, const Definition *context, const ArgumentList &formalArgs)
Definition util.cpp:4805
SrcLangExt getLanguageFromFileName(const QCString &fileName, SrcLangExt defLang)
Definition util.cpp:5724
bool protectionLevelVisible(Protection prot)
Definition util.cpp:6456
bool matchTemplateArguments(const ArgumentList &srcAl, const ArgumentList &dstAl)
Definition util.cpp:2197
void addCodeOnlyMappings()
Definition util.cpp:5718
QCString substituteTemplateArgumentsInString(const QCString &nm, const ArgumentList &formalArgs, const ArgumentList *actualArgs)
Definition util.cpp:4870
int extractClassNameFromType(const QCString &type, int &pos, QCString &name, QCString &templSpec, SrcLangExt lang)
Definition util.cpp:4731
bool leftScopeMatch(const QCString &scope, const QCString &name)
Definition util.cpp:893
QCString stripAnonymousNamespaceScope(const QCString &s)
Definition util.cpp:242
QCString stripFromIncludePath(const QCString &path)
Definition util.cpp:341
bool checkIfTypedef(const Definition *scope, const FileDef *fileScope, const QCString &n)
Definition util.cpp:5828
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:6053
bool patternMatch(const FileInfo &fi, const StringVector &patList)
Definition util.cpp:6207
bool openOutputFile(const QCString &outFile, std::ofstream &f)
Definition util.cpp:6813
QCString tempArgListToString(const ArgumentList &al, SrcLangExt lang, bool includeDefault)
Definition util.cpp:1247
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:5321
QCString showFileDefMatches(const FileNameLinkedMap *fnMap, const QCString &n)
Definition util.cpp:3545
QCString fileToString(const QCString &name, bool filter, bool isSourceCode)
Definition util.cpp:1442
QCString filterTitle(const QCString &title)
Definition util.cpp:6133
QCString removeAnonymousScopes(const QCString &str)
Definition util.cpp:173
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:385
bool checkExtension(const QCString &fName, const QCString &ext)
Definition util.cpp:5413
int computeQualifiedIndex(const QCString &name)
Return the index of the last :: in the string name that is still before the first <.
Definition util.cpp:7341
void initDefaultExtensionMapping()
Definition util.cpp:5651
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:5500
QCString convertNameToFile(const QCString &name, bool allowDots, bool allowUnderscore)
Definition util.cpp:4020
QCString langToString(SrcLangExt lang)
Returns a string representation of lang.
Definition util.cpp:6410
EntryType guessSection(const QCString &name)
Definition util.cpp:350
void extractNamespaceName(const QCString &scopeName, QCString &className, QCString &namespaceName, bool allowEmptyClass)
Definition util.cpp:4213
QCString argListToString(const ArgumentList &al, bool useCanonicalType, bool showDefVals)
Definition util.cpp:1202
QCString getLanguageSpecificSeparator(SrcLangExt lang, bool classScope)
Returns the scope separator to use given the programming language lang.
Definition util.cpp:6416
void mergeMemberOverrideOptions(MemberDefMutable *md1, MemberDefMutable *md2)
Definition util.cpp:7378
QCString mangleCSharpGenericName(const QCString &name)
Definition util.cpp:7430
QCString projectLogoFile()
Definition util.cpp:3661
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:6376
QCString stripTemplateSpecifiersFromScope(const QCString &fullName, bool parentOnly, QCString *pLastScopeStripped, QCString scopeName, bool allowArtificial)
Definition util.cpp:5032
QCString getOverloadDocs()
Definition util.cpp:4605
int getPrefixIndex(const QCString &name)
Definition util.cpp:3752
bool rightScopeMatch(const QCString &scope, const QCString &name)
Definition util.cpp:882
bool updateLanguageMapping(const QCString &extension, const QCString &language)
Definition util.cpp:5619
QCString getFileNameExtension(const QCString &fn)
Definition util.cpp:5766
QCString replaceAnonymousScopes(const QCString &s, const QCString &replacement)
Definition util.cpp:230
FileDef * findFileDef(const FileNameLinkedMap *fnMap, const QCString &n, bool &ambig)
Definition util.cpp:3417
int getScopeFragment(const QCString &s, int p, int *l)
Definition util.cpp:5144
void addHtmlExtensionIfMissing(QCString &fName)
Definition util.cpp:5418
A bunch of utility functions.
void generateXML()
Definition xmlgen.cpp:2204