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)
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",usedName.c_str());
832 if (rightScopeMatch(usedName.c_str(),nestedNameSpecifier))
833 {
834 // ui.currentKey() is the fully qualified name of nestedNameSpecifier
835 // so use this instead.
836 QCString fqn = QCString(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().c_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 //printf("processing unresolved=%s, iteration=%d\n",qPrint(cd->name()),iteration);
1424 /// create the scope artificially
1425 // anyway, so we can at least relate scopes properly.
1426 Definition *d = buildScopeFromQualifiedName(name,cd->getLanguage(),nullptr);
1427 if (d && d!=cd && !cd->getDefFileName().isEmpty())
1428 // avoid recursion in case of redundant scopes, i.e: namespace N { class N::C {}; }
1429 // for this case doxygen assumes the existence of a namespace N::N in which C is to be found!
1430 // also avoid warning for stuff imported via a tagfile.
1431 {
1433 if (dm)
1434 {
1435 dm->addInnerCompound(cd);
1436 }
1437 cd->setOuterScope(d);
1438 warn(cd->getDefFileName(),cd->getDefLine(),
1439 "Internal inconsistency: scope for class {} not "
1440 "found!",name
1441 );
1442 }
1443 }
1444 }
1445}
1446
1448{
1449 //bool inlineGroupedClasses = Config_getBool(INLINE_GROUPED_CLASSES);
1450 //if (!inlineGroupedClasses) return;
1451 //printf("** distributeClassGroupRelations()\n");
1452
1453 ClassDefSet visitedClasses;
1454 for (const auto &cd : *Doxygen::classLinkedMap)
1455 {
1456 //printf("Checking %s\n",qPrint(cd->name()));
1457 // distribute the group to nested classes as well
1458 if (visitedClasses.find(cd.get())==visitedClasses.end() && !cd->partOfGroups().empty())
1459 {
1460 //printf(" Candidate for merging\n");
1461 GroupDef *gd = cd->partOfGroups().front();
1462 for (auto &ncd : cd->getClasses())
1463 {
1465 if (ncdm && ncdm->partOfGroups().empty())
1466 {
1467 //printf(" Adding %s to group '%s'\n",qPrint(ncd->name()),
1468 // gd->groupTitle());
1469 ncdm->makePartOfGroup(gd);
1470 gd->addClass(ncdm);
1471 }
1472 }
1473 visitedClasses.insert(cd.get()); // only visit every class once
1474 }
1475 }
1476}
1477
1478//----------------------------
1479
1480static ClassDefMutable *createTagLessInstance(const ClassDef *rootCd,const ClassDef *templ,const QCString &fieldName)
1481{
1482 QCString fullName = removeAnonymousScopes(templ->name());
1483 if (fullName.endsWith("::")) fullName=fullName.left(fullName.length()-2);
1484 fullName+="."+fieldName;
1485
1486 //printf("** adding class %s based on %s\n",qPrint(fullName),qPrint(templ->name()));
1488 Doxygen::classLinkedMap->add(fullName,
1490 templ->getDefLine(),
1491 templ->getDefColumn(),
1492 fullName,
1493 templ->compoundType())));
1494 if (cd)
1495 {
1496 cd->setDocumentation(templ->documentation(),templ->docFile(),templ->docLine()); // copy docs to definition
1497 cd->setBriefDescription(templ->briefDescription(),templ->briefFile(),templ->briefLine());
1498 cd->setLanguage(templ->getLanguage());
1499 cd->setBodySegment(templ->getDefLine(),templ->getStartBodyLine(),templ->getEndBodyLine());
1500 cd->setBodyDef(templ->getBodyDef());
1501
1502 cd->setOuterScope(rootCd->getOuterScope());
1503 if (rootCd->getOuterScope()!=Doxygen::globalScope)
1504 {
1505 DefinitionMutable *outerScope = toDefinitionMutable(rootCd->getOuterScope());
1506 if (outerScope)
1507 {
1508 outerScope->addInnerCompound(cd);
1509 }
1510 }
1511
1512 FileDef *fd = templ->getFileDef();
1513 if (fd)
1514 {
1515 cd->setFileDef(fd);
1516 fd->insertClass(cd);
1517 }
1518 for (auto &gd : rootCd->partOfGroups())
1519 {
1520 cd->makePartOfGroup(gd);
1521 gd->addClass(cd);
1522 }
1523
1524 MemberList *ml = templ->getMemberList(MemberListType::PubAttribs());
1525 if (ml)
1526 {
1527 for (const auto &md : *ml)
1528 {
1529 //printf(" Member %s type=%s\n",qPrint(md->name()),md->typeString());
1530 auto newMd = createMemberDef(md->getDefFileName(),md->getDefLine(),md->getDefColumn(),
1531 md->typeString(),md->name(),md->argsString(),md->excpString(),
1532 md->protection(),md->virtualness(),md->isStatic(),Relationship::Member,
1533 md->memberType(),
1534 ArgumentList(),ArgumentList(),"");
1535 MemberDefMutable *imd = toMemberDefMutable(newMd.get());
1536 imd->setMemberClass(cd);
1537 imd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
1538 imd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
1539 imd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
1540 imd->setMemberSpecifiers(md->getMemberSpecifiers());
1541 imd->setVhdlSpecifiers(md->getVhdlSpecifiers());
1542 imd->setMemberGroupId(md->getMemberGroupId());
1543 imd->setInitializer(md->initializer());
1544 imd->setRequiresClause(md->requiresClause());
1545 imd->setMaxInitLines(md->initializerLines());
1546 imd->setBitfields(md->bitfieldString());
1547 imd->setLanguage(md->getLanguage());
1548 cd->insertMember(imd);
1549 MemberName *mn = Doxygen::memberNameLinkedMap->add(md->name());
1550 mn->push_back(std::move(newMd));
1551 }
1552 }
1553 }
1554 return cd;
1555}
1556
1557/** Look through the members of class \a cd and its public members.
1558 * If there is a member m of a tag less struct/union,
1559 * then we create a duplicate of the struct/union with the name of the
1560 * member to identify it.
1561 * So if cd has name S, then the tag less struct/union will get name S.m
1562 * Since tag less structs can be nested we need to call this function
1563 * recursively. Later on we need to patch the member types so we keep
1564 * track of the hierarchy of classes we create.
1565 */
1566static void processTagLessClasses(const ClassDef *rootCd,
1567 const ClassDef *cd,
1568 ClassDefMutable *tagParentCd,
1569 const QCString &prefix,int count)
1570{
1571 //printf("%d: processTagLessClasses %s\n",count,qPrint(cd->name()));
1572 //printf("checking members for %s\n",qPrint(cd->name()));
1573 if (tagParentCd && !cd->getClasses().empty())
1574 {
1575 MemberList *ml = cd->getMemberList(MemberListType::PubAttribs());
1576 if (ml)
1577 {
1578 int pos=0;
1579 for (const auto &md : *ml)
1580 {
1581 QCString type = md->typeString();
1582 if (type.find("::@")!=-1) // member of tag less struct/union
1583 {
1584 for (const auto &icd : cd->getClasses())
1585 {
1586 //printf(" member %s: type='%s'\n",qPrint(md->name()),qPrint(type));
1587 //printf(" comparing '%s'<->'%s'\n",qPrint(type),qPrint(icd->name()));
1588 if (type.find(icd->name())!=-1) // matching tag less struct/union
1589 {
1590 QCString name = md->name();
1591 if (md->isAnonymous()) name = "__unnamed" + QCString().setNum(pos++)+"__";
1592 if (!prefix.isEmpty()) name.prepend(prefix+".");
1593 //printf(" found %s for class %s\n",qPrint(name),qPrint(cd->name()));
1594 ClassDefMutable *ncd = createTagLessInstance(rootCd,icd,name);
1595 if (ncd)
1596 {
1597 processTagLessClasses(rootCd,icd,ncd,name,count+1);
1598 //printf(" addTagged %s to %s\n",qPrint(ncd->name()),qPrint(tagParentCd->name()));
1599 ncd->setTagLessReference(icd);
1600
1601 // replace tag-less type for generated/original member
1602 // by newly created class name.
1603 // note the difference between changing cd and tagParentCd.
1604 // for the initial call this is the same pointer, but for
1605 // recursive calls cd is the original tag-less struct (of which
1606 // there is only one instance) and tagParentCd is the newly
1607 // generated tagged struct of which there can be multiple instances!
1608 MemberList *pml = tagParentCd->getMemberList(MemberListType::PubAttribs());
1609 if (pml)
1610 {
1611 for (const auto &pmd : *pml)
1612 {
1614 if (pmdm && pmd->name()==md->name())
1615 {
1616 pmdm->setAccessorType(ncd,substitute(pmd->typeString(),icd->name(),ncd->name()));
1617 //pmd->setType(substitute(pmd->typeString(),icd->name(),ncd->name()));
1618 }
1619 }
1620 }
1621 }
1622 }
1623 }
1624 }
1625 }
1626 }
1627 }
1628}
1629
1630static void findTagLessClasses(std::vector<ClassDefMutable*> &candidates,ClassDef *cd)
1631{
1632 for (const auto &icd : cd->getClasses())
1633 {
1634 if (icd->name().find("@")==-1) // process all non-anonymous inner classes
1635 {
1636 findTagLessClasses(candidates,icd);
1637 }
1638 }
1639
1641 if (cdm)
1642 {
1643 candidates.push_back(cdm);
1644 }
1645}
1646
1648{
1649 std::vector<ClassDefMutable *> candidates;
1650 for (auto &cd : *Doxygen::classLinkedMap)
1651 {
1652 Definition *scope = cd->getOuterScope();
1653 if (scope && scope->definitionType()!=Definition::TypeClass) // that is not nested
1654 {
1655 findTagLessClasses(candidates,cd.get());
1656 }
1657 }
1658
1659 // since processTagLessClasses is potentially adding classes to Doxygen::classLinkedMap
1660 // we need to call it outside of the loop above, otherwise the iterator gets invalidated!
1661 for (auto &cd : candidates)
1662 {
1663 processTagLessClasses(cd,cd,cd,"",0); // process tag less inner struct/classes
1664 }
1665}
1666
1667
1668//----------------------------------------------------------------------
1669// build a list of all namespaces mentioned in the documentation
1670// and all namespaces that have a documentation block before their definition.
1671static void buildNamespaceList(const Entry *root)
1672{
1673 if (
1674 (root->section.isNamespace() ||
1675 root->section.isNamespaceDoc() ||
1676 root->section.isPackageDoc()
1677 ) &&
1678 !root->name.isEmpty()
1679 )
1680 {
1681 AUTO_TRACE("name={}",root->name);
1682
1683 QCString fName = root->name;
1684 if (root->section.isPackageDoc())
1685 {
1686 fName=substitute(fName,".","::");
1687 }
1688
1689 QCString fullName = stripAnonymousNamespaceScope(fName);
1690 if (!fullName.isEmpty())
1691 {
1692 AUTO_TRACE_ADD("Found namespace {} in {} at line {}",root->name,root->fileName,root->startLine);
1693 NamespaceDef *ndi = Doxygen::namespaceLinkedMap->find(fullName);
1694 if (ndi) // existing namespace
1695 {
1697 if (nd) // non-inline namespace
1698 {
1699 AUTO_TRACE_ADD("Existing namespace");
1700 nd->setDocumentation(root->doc,root->docFile,root->docLine);
1701 nd->setName(fullName); // change name to match docs
1703 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1705 {
1706 nd->setLanguage(root->lang);
1707 }
1708 if (root->tagInfo()==nullptr && nd->isReference() && !(root->doc.isEmpty() && root->brief.isEmpty()))
1709 // if we previously found namespace nd in a tag file and now we find a
1710 // documented namespace with the same name in the project, then remove
1711 // the tag file reference
1712 {
1713 nd->setReference("");
1714 nd->setFileName(fullName);
1715 }
1716 nd->setMetaData(root->metaData);
1717
1718 // file definition containing the namespace nd
1719 FileDef *fd=root->fileDef();
1720 if (nd->isArtificial())
1721 {
1722 nd->setArtificial(FALSE); // found namespace explicitly, so cannot be artificial
1723 nd->setDefFile(root->fileName,root->startLine,root->startColumn);
1724 }
1725 // insert the namespace in the file definition
1726 if (fd) fd->insertNamespace(nd);
1727 addNamespaceToGroups(root,nd);
1728 nd->setRefItems(root->sli);
1729 }
1730 }
1731 else // fresh namespace
1732 {
1733 QCString tagName;
1734 QCString tagFileName;
1735 const TagInfo *tagInfo = root->tagInfo();
1736 if (tagInfo)
1737 {
1738 tagName = tagInfo->tagName;
1739 tagFileName = tagInfo->fileName;
1740 }
1741 AUTO_TRACE_ADD("new namespace {} lang={} tagName={}",fullName,langToString(root->lang),tagName);
1742 // add namespace to the list
1744 Doxygen::namespaceLinkedMap->add(fullName,
1745 createNamespaceDef(tagInfo?tagName:root->fileName,root->startLine,
1746 root->startColumn,fullName,tagName,tagFileName,
1747 root->type,root->spec.isPublished())));
1748 if (nd)
1749 {
1750 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1751 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1753 nd->setHidden(root->hidden);
1754 nd->setArtificial(root->artificial);
1755 nd->setLanguage(root->lang);
1756 nd->setId(root->id);
1757 nd->setMetaData(root->metaData);
1758 nd->setInline(root->spec.isInline());
1759 nd->setExported(root->exported);
1760
1761 addNamespaceToGroups(root,nd);
1762 nd->setRefItems(root->sli);
1763
1764 // file definition containing the namespace nd
1765 FileDef *fd=root->fileDef();
1766 // insert the namespace in the file definition
1767 if (fd) fd->insertNamespace(nd);
1768
1769 // the empty string test is needed for extract all case
1770 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1771 nd->insertUsedFile(fd);
1772 nd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1773 nd->setBodyDef(fd);
1774
1775 // also add namespace to the correct structural context
1776 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,fullName,nullptr,tagInfo);
1777 AUTO_TRACE_ADD("adding namespace {} to context {}",nd->name(),d ? d->name() : QCString("<none>"));
1778 if (d==nullptr) // we didn't find anything, create the scope artificially
1779 // anyway, so we can at least relate scopes properly.
1780 {
1781 d = buildScopeFromQualifiedName(fullName,nd->getLanguage(),tagInfo);
1783 if (dm)
1784 {
1785 dm->addInnerCompound(nd);
1786 }
1787 nd->setOuterScope(d);
1788 // TODO: Due to the order in which the tag file is written
1789 // a nested class can be found before its parent!
1790 }
1791 else
1792 {
1794 if (dm)
1795 {
1796 dm->addInnerCompound(nd);
1797 }
1798 nd->setOuterScope(d);
1799 // in case of d is an inline namespace, alias insert nd in the part scope of d.
1801 {
1802 NamespaceDef *pnd = toNamespaceDef(d);
1803 if (pnd && pnd->isInline())
1804 {
1805 d = d->getOuterScope();
1806 if (d)
1807 {
1808 dm = toDefinitionMutable(d);
1809 if (dm)
1810 {
1811 auto aliasNd = createNamespaceDefAlias(d,nd);
1812 dm->addInnerCompound(aliasNd.get());
1813 QCString aliasName = aliasNd->name();
1814 AUTO_TRACE_ADD("adding alias {} to {}",aliasName,d->name());
1815 Doxygen::namespaceLinkedMap->add(aliasName,std::move(aliasNd));
1816 }
1817 }
1818 else
1819 {
1820 break;
1821 }
1822 }
1823 else
1824 {
1825 break;
1826 }
1827 }
1828 }
1829 }
1830 }
1831 }
1832 }
1833 for (const auto &e : root->children()) buildNamespaceList(e.get());
1834}
1835
1836//----------------------------------------------------------------------
1837
1839 const QCString &name)
1840{
1841 NamespaceDef *usingNd =nullptr;
1842 for (auto &und : unl)
1843 {
1844 QCString uScope=und->name()+"::";
1845 usingNd = getResolvedNamespace(uScope+name);
1846 if (usingNd!=nullptr) break;
1847 }
1848 return usingNd;
1849}
1850
1851static void findUsingDirectives(const Entry *root)
1852{
1853 if (root->section.isUsingDir())
1854 {
1855 AUTO_TRACE("Found using directive {} at line {} of {}",root->name,root->startLine,root->fileName);
1856 QCString name=substitute(root->name,".","::");
1857 if (name.endsWith("::"))
1858 {
1859 name=name.left(name.length()-2);
1860 }
1861 if (!name.isEmpty())
1862 {
1863 NamespaceDef *usingNd = nullptr;
1864 NamespaceDefMutable *nd = nullptr;
1865 FileDef *fd = root->fileDef();
1866 QCString nsName;
1867
1868 // see if the using statement was found inside a namespace or inside
1869 // the global file scope.
1870 if (root->parent() && root->parent()->section.isNamespace() &&
1871 (fd==nullptr || fd->getLanguage()!=SrcLangExt::Java) // not a .java file
1872 )
1873 {
1874 nsName=stripAnonymousNamespaceScope(root->parent()->name);
1875 if (!nsName.isEmpty())
1876 {
1877 nd = getResolvedNamespaceMutable(nsName);
1878 }
1879 }
1880
1881 // find the scope in which the 'using' namespace is defined by prepending
1882 // the possible scopes in which the using statement was found, starting
1883 // with the most inner scope and going to the most outer scope (i.e.
1884 // file scope).
1885 int scopeOffset = static_cast<int>(nsName.length());
1886 do
1887 {
1888 QCString scope=scopeOffset>0 ?
1889 nsName.left(scopeOffset)+"::" : QCString();
1890 usingNd = getResolvedNamespace(scope+name);
1891 //printf("Trying with scope='%s' usingNd=%p\n",(scope+qPrint(name)),usingNd);
1892 if (scopeOffset==0)
1893 {
1894 scopeOffset=-1;
1895 }
1896 else if ((scopeOffset=nsName.findRev("::",scopeOffset-1))==-1)
1897 {
1898 scopeOffset=0;
1899 }
1900 } while (scopeOffset>=0 && usingNd==nullptr);
1901
1902 if (usingNd==nullptr && nd) // not found, try used namespaces in this scope
1903 // or in one of the parent namespace scopes
1904 {
1905 const NamespaceDefMutable *pnd = nd;
1906 while (pnd && usingNd==nullptr)
1907 {
1908 // also try with one of the used namespaces found earlier
1910
1911 // goto the parent
1912 Definition *s = pnd->getOuterScope();
1914 {
1916 }
1917 else
1918 {
1919 pnd = nullptr;
1920 }
1921 }
1922 }
1923 if (usingNd==nullptr && fd) // still nothing, also try used namespace in the
1924 // global scope
1925 {
1926 usingNd = findUsedNamespace(fd->getUsedNamespaces(),name);
1927 }
1928
1929 //printf("%s -> %s\n",qPrint(name),usingNd?qPrint(usingNd->name()):"<none>");
1930
1931 // add the namespace the correct scope
1932 if (usingNd)
1933 {
1934 //printf("using fd=%p nd=%p\n",fd,nd);
1935 if (nd)
1936 {
1937 //printf("Inside namespace %s\n",qPrint(nd->name()));
1938 nd->addUsingDirective(usingNd);
1939 }
1940 else if (fd)
1941 {
1942 //printf("Inside file %s\n",qPrint(fd->name()));
1943 fd->addUsingDirective(usingNd);
1944 }
1945 }
1946 else // unknown namespace, but add it anyway.
1947 {
1948 AUTO_TRACE_ADD("new unknown namespace {} lang={} hidden={}",name,langToString(root->lang),root->hidden);
1949 // add namespace to the list
1952 createNamespaceDef(root->fileName,root->startLine,root->startColumn,name)));
1953 if (nd)
1954 {
1955 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1956 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1958 nd->setHidden(root->hidden);
1959 nd->setArtificial(TRUE);
1960 nd->setLanguage(root->lang);
1961 nd->setId(root->id);
1962 nd->setMetaData(root->metaData);
1963 nd->setInline(root->spec.isInline());
1964 nd->setExported(root->exported);
1965
1966 for (const Grouping &g : root->groups)
1967 {
1968 GroupDef *gd=nullptr;
1969 if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname)))
1970 gd->addNamespace(nd);
1971 }
1972
1973 // insert the namespace in the file definition
1974 if (fd)
1975 {
1976 fd->insertNamespace(nd);
1977 fd->addUsingDirective(nd);
1978 }
1979
1980 // the empty string test is needed for extract all case
1981 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1982 nd->insertUsedFile(fd);
1983 nd->setRefItems(root->sli);
1984 }
1985 }
1986 }
1987 }
1988 for (const auto &e : root->children()) findUsingDirectives(e.get());
1989}
1990
1991//----------------------------------------------------------------------
1992
1993static void buildListOfUsingDecls(const Entry *root)
1994{
1995 if (root->section.isUsingDecl() &&
1996 !root->parent()->section.isCompound() // not a class/struct member
1997 )
1998 {
1999 QCString name = substitute(root->name,".","::");
2000 g_usingDeclarations.insert(name.str());
2001 }
2002 for (const auto &e : root->children()) buildListOfUsingDecls(e.get());
2003}
2004
2005
2006static void findUsingDeclarations(const Entry *root,bool filterPythonPackages)
2007{
2008 if (root->section.isUsingDecl() &&
2009 !root->parent()->section.isCompound() && // not a class/struct member
2010 (!filterPythonPackages || (root->lang==SrcLangExt::Python && root->fileName.endsWith("__init__.py")))
2011 )
2012 {
2013 AUTO_TRACE("Found using declaration '{}' at line {} of {} inside section {}",
2014 root->name,root->startLine,root->fileName,root->parent()->section);
2015 if (!root->name.isEmpty())
2016 {
2017 const Definition *usingDef = nullptr;
2018 NamespaceDefMutable *nd = nullptr;
2019 FileDef *fd = root->fileDef();
2020 QCString scName;
2021
2022 // see if the using statement was found inside a namespace or inside
2023 // the global file scope.
2024 if (root->parent()->section.isNamespace())
2025 {
2026 scName=root->parent()->name;
2027 if (!scName.isEmpty())
2028 {
2029 nd = getResolvedNamespaceMutable(scName);
2030 }
2031 }
2032
2033 // Assume the using statement was used to import a class.
2034 // Find the scope in which the 'using' namespace is defined by prepending
2035 // the possible scopes in which the using statement was found, starting
2036 // with the most inner scope and going to the most outer scope (i.e.
2037 // file scope).
2038
2039 QCString name = substitute(root->name,".","::"); //Java/C# scope->internal
2040
2041 SymbolResolver resolver;
2042 const Definition *scope = nd;
2043 if (nd==nullptr) scope = fd;
2044 usingDef = resolver.resolveSymbol(scope,name);
2045
2046 //printf("usingDef(scope=%s,name=%s)=%s\n",qPrint(nd?nd->qualifiedName():""),qPrint(name),usingDef?qPrint(usingDef->qualifiedName()):"nullptr");
2047
2048 if (!usingDef)
2049 {
2050 usingDef = getClass(name); // try direct lookup, this is needed to get
2051 // builtin STL classes to properly resolve, e.g.
2052 // vector -> std::vector
2053 }
2054 if (!usingDef)
2055 {
2056 usingDef = Doxygen::hiddenClassLinkedMap->find(name); // check if it is already hidden
2057 }
2058#if 0
2059 if (!usingDef)
2060 {
2061 AUTO_TRACE_ADD("New using class '{}' (sec={})! #tArgLists={}",
2062 name,root->section,root->tArgLists.size());
2065 createClassDef( "<using>",1,1, name, ClassDef::Class)));
2066 if (usingCd)
2067 {
2068 usingCd->setArtificial(TRUE);
2069 usingCd->setLanguage(root->lang);
2070 usingDef = usingCd;
2071 }
2072 }
2073#endif
2074 else
2075 {
2076 AUTO_TRACE_ADD("Found used type '{}' in scope='{}'",
2077 usingDef->name(), nd ? nd->name(): fd ? fd->name() : QCString("<unknown>"));
2078 }
2079
2080 if (usingDef)
2081 {
2082 if (nd)
2083 {
2084 nd->addUsingDeclaration(usingDef);
2085 }
2086 else if (fd)
2087 {
2088 fd->addUsingDeclaration(usingDef);
2089 }
2090 }
2091 }
2092 }
2093 for (const auto &e : root->children()) findUsingDeclarations(e.get(),filterPythonPackages);
2094}
2095
2096//----------------------------------------------------------------------
2097
2099{
2100 root->commandOverrides.apply_callGraph ([&](bool b) { md->overrideCallGraph(b); });
2101 root->commandOverrides.apply_callerGraph ([&](bool b) { md->overrideCallerGraph(b); });
2102 root->commandOverrides.apply_referencedByRelation([&](bool b) { md->overrideReferencedByRelation(b); });
2103 root->commandOverrides.apply_referencesRelation ([&](bool b) { md->overrideReferencesRelation(b); });
2104 root->commandOverrides.apply_inlineSource ([&](bool b) { md->overrideInlineSource(b); });
2105 root->commandOverrides.apply_enumValues ([&](bool b) { md->overrideEnumValues(b); });
2106}
2107
2108//----------------------------------------------------------------------
2109
2111 const QCString &fileName,const QCString &memName)
2112{
2113 AUTO_TRACE("creating new member {} for class {}",memName,cd->name());
2114 const ArgumentList &templAl = md->templateArguments();
2115 const ArgumentList &al = md->argumentList();
2116 auto newMd = createMemberDef(
2117 fileName,root->startLine,root->startColumn,
2118 md->typeString(),memName,md->argsString(),
2119 md->excpString(),root->protection,root->virt,
2121 templAl,al,root->metaData
2122 );
2123 auto newMmd = toMemberDefMutable(newMd.get());
2124 newMmd->setMemberClass(cd);
2125 cd->insertMember(newMd.get());
2126 if (!root->doc.isEmpty() || !root->brief.isEmpty())
2127 {
2128 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2129 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2130 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2131 }
2132 else
2133 {
2134 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2135 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2136 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2137 }
2138 newMmd->setDefinition(md->definition());
2139 applyMemberOverrideOptions(root,newMmd);
2140 newMmd->addQualifiers(root->qualifiers);
2141 newMmd->setBitfields(md->bitfieldString());
2142 newMmd->addSectionsToDefinition(root->anchors);
2143 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2144 newMmd->setBodyDef(md->getBodyDef());
2145 newMmd->setInitializer(md->initializer());
2146 newMmd->setRequiresClause(md->requiresClause());
2147 newMmd->setMaxInitLines(md->initializerLines());
2148 newMmd->setMemberGroupId(root->mGrpId);
2149 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2150 newMmd->setVhdlSpecifiers(md->getVhdlSpecifiers());
2151 newMmd->setLanguage(root->lang);
2152 newMmd->setId(root->id);
2153 MemberName *mn = Doxygen::memberNameLinkedMap->add(memName);
2154 mn->push_back(std::move(newMd));
2155}
2156
2157static std::unordered_map<std::string,std::vector<ClassDefMutable*>> g_usingClassMap;
2158
2159static void findUsingDeclImports(const Entry *root)
2160{
2161 if (root->section.isUsingDecl() &&
2162 root->parent()->section.isCompound() // in a class/struct member
2163 )
2164 {
2165 AUTO_TRACE("Found using declaration '{}' inside section {}", root->name, root->parent()->section);
2166 QCString fullName=removeRedundantWhiteSpace(root->parent()->name);
2167 fullName=stripAnonymousNamespaceScope(fullName);
2168 fullName=stripTemplateSpecifiersFromScope(fullName);
2169 ClassDefMutable *cd = getClassMutable(fullName);
2170 if (cd)
2171 {
2172 AUTO_TRACE_ADD("found class '{}'",cd->name());
2173 int i=root->name.findRev("::");
2174 if (i!=-1)
2175 {
2176 QCString scope=root->name.left(i);
2177 QCString memName=root->name.right(root->name.length()-i-2);
2178 SymbolResolver resolver;
2179 const ClassDef *bcd = resolver.resolveClass(cd,scope); // todo: file in fileScope parameter
2180 AUTO_TRACE_ADD("name={} scope={} bcd={}",scope,cd?cd->name():"<none>",bcd?bcd->name():"<none>");
2181 if (bcd && bcd!=cd)
2182 {
2183 AUTO_TRACE_ADD("found class '{}' memName='{}'",bcd->name(),memName);
2185 const MemberNameInfo *mni = mnlm.find(memName);
2186 if (mni)
2187 {
2188 for (auto &mi : *mni)
2189 {
2190 const MemberDef *md = mi->memberDef();
2191 if (md && md->protection()!=Protection::Private)
2192 {
2193 AUTO_TRACE_ADD("found member '{}'",mni->memberName());
2194 QCString fileName = root->fileName;
2195 if (fileName.isEmpty() && root->tagInfo())
2196 {
2197 fileName = root->tagInfo()->tagName;
2198 }
2199 if (!cd->containsOverload(md))
2200 {
2201 createUsingMemberImportForClass(root,cd,md,fileName,memName);
2202 // also insert the member into copies of the class
2203 auto it = g_usingClassMap.find(cd->qualifiedName().str());
2204 if (it != g_usingClassMap.end())
2205 {
2206 for (const auto &copyCd : it->second)
2207 {
2208 createUsingMemberImportForClass(root,copyCd,md,fileName,memName);
2209 }
2210 }
2211 }
2212 }
2213 }
2214 }
2215 }
2216 }
2217 }
2218 }
2219 else if (root->section.isUsingDecl() &&
2220 (root->parent()->section.isNamespace() || root->parent()->section.isEmpty()) && // namespace or global member
2221 root->lang==SrcLangExt::Cpp // do we also want this for e.g. Fortran? (see test case 095)
2222 )
2223 {
2224 AUTO_TRACE("Found using declaration '{}' inside section {}", root->name, root->parent()->section);
2225 Definition *scope = nullptr;
2226 NamespaceDefMutable *nd = nullptr;
2227 FileDef *fd = root->parent()->fileDef();
2228 if (!root->parent()->name.isEmpty())
2229 {
2230 QCString fullName=removeRedundantWhiteSpace(root->parent()->name);
2231 fullName=stripAnonymousNamespaceScope(fullName);
2233 scope = nd;
2234 }
2235 else
2236 {
2237 scope = fd;
2238 }
2239 if (scope)
2240 {
2241 AUTO_TRACE_ADD("found scope '{}'",scope->name());
2242 SymbolResolver resolver;
2243 const Definition *def = resolver.resolveSymbol(root->name.startsWith("::") ? nullptr : scope,root->name);
2244 if (def && def->definitionType()==Definition::TypeMember)
2245 {
2246 int i=root->name.findRev("::");
2247 QCString memName;
2248 if (i!=-1)
2249 {
2250 memName = root->name.right(root->name.length()-i-2);
2251 }
2252 else
2253 {
2254 memName = root->name;
2255 }
2256 const MemberDef *md = toMemberDef(def);
2257 AUTO_TRACE_ADD("found member '{}' for name '{}'",md->qualifiedName(),root->name);
2258 QCString fileName = root->fileName;
2259 if (fileName.isEmpty() && root->tagInfo())
2260 {
2261 fileName = root->tagInfo()->tagName;
2262 }
2263 const ArgumentList &templAl = md->templateArguments();
2264 const ArgumentList &al = md->argumentList();
2265
2266 auto newMd = createMemberDef(
2267 fileName,root->startLine,root->startColumn,
2268 md->typeString(),memName,md->argsString(),
2269 md->excpString(),root->protection,root->virt,
2271 templAl,al,root->metaData
2272 );
2273 auto newMmd = toMemberDefMutable(newMd.get());
2274 if (nd)
2275 {
2276 newMmd->setNamespace(nd);
2277 nd->insertMember(newMd.get());
2278 }
2279 if (fd)
2280 {
2281 newMmd->setFileDef(fd);
2282 fd->insertMember(newMd.get());
2283 }
2284 if (!root->doc.isEmpty() || !root->brief.isEmpty())
2285 {
2286 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2287 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2288 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2289 }
2290 else
2291 {
2292 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2293 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2294 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2295 }
2296 newMmd->setDefinition(md->definition());
2297 applyMemberOverrideOptions(root,newMmd);
2298 newMmd->addQualifiers(root->qualifiers);
2299 newMmd->setBitfields(md->bitfieldString());
2300 newMmd->addSectionsToDefinition(root->anchors);
2301 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2302 newMmd->setBodyDef(md->getBodyDef());
2303 newMmd->setInitializer(md->initializer());
2304 newMmd->setRequiresClause(md->requiresClause());
2305 newMmd->setMaxInitLines(md->initializerLines());
2306 newMmd->setMemberGroupId(root->mGrpId);
2307 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2308 newMmd->setVhdlSpecifiers(md->getVhdlSpecifiers());
2309 newMmd->setLanguage(root->lang);
2310 newMmd->setId(root->id);
2311 MemberName *mn = Doxygen::functionNameLinkedMap->add(memName);
2312 mn->push_back(std::move(newMd));
2313#if 0 // insert an alias instead of a copy
2314 const MemberDef *md = toMemberDef(def);
2315 AUTO_TRACE_ADD("found member '{}' for name '{}'",md->qualifiedName(),root->name);
2316 auto aliasMd = createMemberDefAlias(nd,md);
2317 QCString aliasFullName = nd->qualifiedName()+"::"+aliasMd->localName();
2318 if (nd && aliasMd.get())
2319 {
2320 nd->insertMember(aliasMd.get());
2321 }
2322 if (fd && aliasMd.get())
2323 {
2324 fd->insertMember(aliasMd.get());
2325 }
2326 MemberName *mn = Doxygen::memberNameLinkedMap->add(aliasFullName);
2327 mn->push_back(std::move(aliasMd));
2328#endif
2329 }
2330 else if (def && def->definitionType()==Definition::TypeClass)
2331 {
2332 const ClassDef *cd = toClassDef(def);
2333 QCString copyFullName;
2334 if (nd==nullptr)
2335 {
2336 copyFullName = cd->localName();
2337 }
2338 else
2339 {
2340 copyFullName = nd->qualifiedName()+"::"+cd->localName();
2341 }
2342 if (Doxygen::classLinkedMap->find(copyFullName)==nullptr)
2343 {
2345 Doxygen::classLinkedMap->add(copyFullName,
2346 cd->deepCopy(copyFullName)));
2347 AUTO_TRACE_ADD("found class '{}' for name '{}' copy '{}' obj={}",cd->qualifiedName(),root->name,copyFullName,(void*)ncdm);
2348 g_usingClassMap[cd->qualifiedName().str()].push_back(ncdm);
2349 if (ncdm)
2350 {
2351 if (nd) ncdm->moveTo(nd);
2352 if ((!root->doc.isEmpty() || !root->brief.isEmpty())) // use docs at using statement
2353 {
2354 ncdm->setDocumentation(root->doc,root->docFile,root->docLine);
2355 ncdm->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2356 }
2357 else // use docs from used class
2358 {
2359 ncdm->setDocumentation(cd->documentation(),cd->docFile(),cd->docLine());
2361 }
2362 if (nd)
2363 {
2364 nd->addInnerCompound(ncdm);
2365 nd->addUsingDeclaration(ncdm);
2366 }
2367 if (fd)
2368 {
2369 if (ncdm) ncdm->setFileDef(fd);
2370 fd->insertClass(ncdm);
2371 fd->addUsingDeclaration(ncdm);
2372 }
2373 }
2374 }
2375#if 0 // insert an alias instead of a copy
2376 auto aliasCd = createClassDefAlias(nd,cd);
2377 QCString aliasFullName;
2378 if (nd==nullptr)
2379 {
2380 aliasFullName = aliasCd->localName();
2381 }
2382 else
2383 {
2384 aliasFullName = nd->qualifiedName()+"::"+aliasCd->localName();
2385 }
2386 AUTO_TRACE_ADD("found class '{}' for name '{}' aliasFullName='{}'",cd->qualifiedName(),root->name,aliasFullName);
2387 auto acd = Doxygen::classLinkedMap->add(aliasFullName,std::move(aliasCd));
2388 if (nd && acd)
2389 {
2390 nd->addInnerCompound(acd);
2391 }
2392 if (fd && acd)
2393 {
2394 fd->insertClass(acd);
2395 }
2396#endif
2397 }
2398 else if (scope)
2399 {
2400 AUTO_TRACE_ADD("no symbol with name '{}' in scope {}",root->name,scope->name());
2401 }
2402 }
2403 }
2404 for (const auto &e : root->children()) findUsingDeclImports(e.get());
2405}
2406
2407//----------------------------------------------------------------------
2408
2410{
2411 FileDefSet visitedFiles;
2412 // then recursively add using directives found in #include files
2413 // to files that have not been visited.
2414 for (const auto &fn : *Doxygen::inputNameLinkedMap)
2415 {
2416 for (const auto &fd : *fn)
2417 {
2418 //printf("----- adding using directives for file %s\n",qPrint(fd->name()));
2419 fd->addIncludedUsingDirectives(visitedFiles);
2420 }
2421 }
2422}
2423
2424//----------------------------------------------------------------------
2425
2427 const Entry *root,
2428 ClassDefMutable *cd,
2429 MemberType mtype,
2430 const QCString &type,
2431 const QCString &name,
2432 const QCString &args,
2433 bool fromAnnScope,
2434 MemberDef *fromAnnMemb,
2435 Protection prot,
2436 Relationship related)
2437{
2439 QCString scopeSeparator="::";
2440 SrcLangExt lang = cd->getLanguage();
2441 if (lang==SrcLangExt::Java || lang==SrcLangExt::CSharp)
2442 {
2443 qualScope = substitute(qualScope,"::",".");
2444 scopeSeparator=".";
2445 }
2446 AUTO_TRACE("class variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} ann={} init='{}'",
2447 root->fileName, type, qualScope, name, args, root->protection, mtype, lang, fromAnnScope, root->initializer.str());
2448
2449 QCString def;
2450 if (!type.isEmpty())
2451 {
2452 if (related!=Relationship::Member || mtype==MemberType::Friend || Config_getBool(HIDE_SCOPE_NAMES))
2453 {
2454 if (root->spec.isAlias()) // turn 'typedef B A' into 'using A'
2455 {
2456 def="using "+name;
2457 }
2458 else
2459 {
2460 def=type+" "+name+args;
2461 }
2462 }
2463 else
2464 {
2465 if (root->spec.isAlias()) // turn 'typedef B C::A' into 'using C::A'
2466 {
2467 def="using "+qualScope+scopeSeparator+name;
2468 }
2469 else
2470 {
2471 def=type+" "+qualScope+scopeSeparator+name+args;
2472 }
2473 }
2474 }
2475 else
2476 {
2477 if (Config_getBool(HIDE_SCOPE_NAMES))
2478 {
2479 def=name+args;
2480 }
2481 else
2482 {
2483 def=qualScope+scopeSeparator+name+args;
2484 }
2485 }
2486 def.stripPrefix("static ");
2487
2488 // see if the member is already found in the same scope
2489 // (this may be the case for a static member that is initialized
2490 // outside the class)
2492 if (mn)
2493 {
2494 for (const auto &imd : *mn)
2495 {
2496 //printf("md->getClassDef()=%p cd=%p type=[%s] md->typeString()=[%s]\n",
2497 // md->getClassDef(),cd,qPrint(type),md->typeString());
2498 MemberDefMutable *md = toMemberDefMutable(imd.get());
2499 if (md &&
2500 md->getClassDef()==cd &&
2501 ((lang==SrcLangExt::Python && type.isEmpty() && !md->typeString().isEmpty()) ||
2503 // member already in the scope
2504 {
2505
2506 if (root->lang==SrcLangExt::ObjC &&
2509 { // Objective-C 2.0 property
2510 // turn variable into a property
2511 md->setProtection(root->protection);
2513 }
2514 addMemberDocs(root,md,def,nullptr,FALSE,root->spec);
2515 AUTO_TRACE_ADD("Member already found!");
2516 return md;
2517 }
2518 }
2519 }
2520
2521 QCString fileName = root->fileName;
2522 if (fileName.isEmpty() && root->tagInfo())
2523 {
2524 fileName = root->tagInfo()->tagName;
2525 }
2526
2527 // new member variable, typedef or enum value
2528 auto md = createMemberDef(
2529 fileName,root->startLine,root->startColumn,
2530 type,name,args,root->exception,
2531 prot,Specifier::Normal,root->isStatic,related,
2532 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2533 ArgumentList(), root->metaData);
2534 auto mmd = toMemberDefMutable(md.get());
2535 mmd->setTagInfo(root->tagInfo());
2536 mmd->setMemberClass(cd); // also sets outer scope (i.e. getOuterScope())
2537 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2538 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2539 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2540 mmd->setDefinition(def);
2541 mmd->setBitfields(root->bitfields);
2542 mmd->addSectionsToDefinition(root->anchors);
2543 mmd->setFromAnonymousScope(fromAnnScope);
2544 mmd->setFromAnonymousMember(fromAnnMemb);
2545 //md->setIndentDepth(indentDepth);
2546 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2547 std::string init = root->initializer.str();
2548 mmd->setInitializer(init.c_str());
2549 mmd->setMaxInitLines(root->initLines);
2550 mmd->setMemberGroupId(root->mGrpId);
2551 mmd->setMemberSpecifiers(root->spec);
2552 mmd->setVhdlSpecifiers(root->vhdlSpec);
2553 mmd->setReadAccessor(root->read);
2554 mmd->setWriteAccessor(root->write);
2556 mmd->setHidden(root->hidden);
2557 mmd->setArtificial(root->artificial);
2558 mmd->setLanguage(root->lang);
2559 mmd->setId(root->id);
2560 addMemberToGroups(root,md.get());
2562 mmd->setBodyDef(root->fileDef());
2563 mmd->addQualifiers(root->qualifiers);
2564
2565 AUTO_TRACE_ADD("Adding new member to class '{}'",cd->name());
2566 cd->insertMember(md.get());
2567 mmd->setRefItems(root->sli);
2568
2569 cd->insertUsedFile(root->fileDef());
2570 root->markAsProcessed();
2571
2572 if (mtype==MemberType::Typedef)
2573 {
2574 resolveTemplateInstanceInType(root,cd,md.get());
2575 }
2576
2577 // add the member to the global list
2578 MemberDef *result = md.get();
2579 mn = Doxygen::memberNameLinkedMap->add(name);
2580 mn->push_back(std::move(md));
2581
2582 return result;
2583}
2584
2585//----------------------------------------------------------------------
2586
2588 const Entry *root,
2589 MemberType mtype,
2590 const QCString &scope,
2591 const QCString &type,
2592 const QCString &name,
2593 const QCString &args,
2594 bool fromAnnScope,
2595 MemberDef *fromAnnMemb)
2596{
2597 AUTO_TRACE("global variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} init='{}'",
2598 root->fileName, type, scope, name, args, root->protection, mtype, root->lang, root->initializer.str());
2599
2600 FileDef *fd = root->fileDef();
2601
2602 // see if we have a typedef that should hide a struct or union
2603 if (mtype==MemberType::Typedef && Config_getBool(TYPEDEF_HIDES_STRUCT))
2604 {
2605 QCString ttype = type;
2606 ttype.stripPrefix("typedef ");
2607 if (ttype.stripPrefix("struct ") || ttype.stripPrefix("union "))
2608 {
2609 static const reg::Ex re(R"(\a\w*)");
2610 reg::Match match;
2611 std::string typ = ttype.str();
2612 if (reg::search(typ,match,re))
2613 {
2614 QCString typeValue = match.str();
2615 ClassDefMutable *cd = getClassMutable(typeValue);
2616 if (cd)
2617 {
2618 // this typedef should hide compound name cd, so we
2619 // change the name that is displayed from cd.
2620 cd->setClassName(name);
2621 cd->setDocumentation(root->doc,root->docFile,root->docLine);
2622 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2623 return nullptr;
2624 }
2625 }
2626 }
2627 }
2628
2629 // see if the function is inside a namespace
2630 NamespaceDefMutable *nd = nullptr;
2631 if (!scope.isEmpty())
2632 {
2633 if (scope.find('@')!=-1) return nullptr; // anonymous scope!
2634 nd = getResolvedNamespaceMutable(scope);
2635 }
2636 QCString def;
2637
2638 // determine the definition of the global variable
2639 if (nd && !nd->isAnonymous() &&
2640 !Config_getBool(HIDE_SCOPE_NAMES)
2641 )
2642 // variable is inside a namespace, so put the scope before the name
2643 {
2644 SrcLangExt lang = nd->getLanguage();
2646
2647 if (!type.isEmpty())
2648 {
2649 if (root->spec.isAlias()) // turn 'typedef B NS::A' into 'using NS::A'
2650 {
2651 def="using "+nd->name()+sep+name;
2652 }
2653 else // normal member
2654 {
2655 def=type+" "+nd->name()+sep+name+args;
2656 }
2657 }
2658 else
2659 {
2660 def=nd->name()+sep+name+args;
2661 }
2662 }
2663 else
2664 {
2665 if (!type.isEmpty() && !root->name.isEmpty())
2666 {
2667 if (name.at(0)=='@') // dummy variable representing anonymous union
2668 {
2669 def=type;
2670 }
2671 else
2672 {
2673 if (root->spec.isAlias()) // turn 'typedef B A' into 'using A'
2674 {
2675 def="using "+root->name;
2676 }
2677 else // normal member
2678 {
2679 def=type+" "+name+args;
2680 }
2681 }
2682 }
2683 else
2684 {
2685 def=name+args;
2686 }
2687 }
2688 def.stripPrefix("static ");
2689
2691 if (mn)
2692 {
2693 //QCString nscope=removeAnonymousScopes(scope);
2694 //NamespaceDef *nd=nullptr;
2695 //if (!nscope.isEmpty())
2696 if (!scope.isEmpty())
2697 {
2698 nd = getResolvedNamespaceMutable(scope);
2699 }
2700 for (const auto &imd : *mn)
2701 {
2702 MemberDefMutable *md = toMemberDefMutable(imd.get());
2703 if (md &&
2704 ((nd==nullptr && md->getNamespaceDef()==nullptr && md->getFileDef() &&
2705 root->fileName==md->getFileDef()->absFilePath()
2706 ) // both variable names in the same file
2707 || (nd!=nullptr && md->getNamespaceDef()==nd) // both in same namespace
2708 )
2709 && !md->isDefine() // function style #define's can be "overloaded" by typedefs or variables
2710 && !md->isEnumerate() // in C# an enum value and enum can have the same name
2711 )
2712 // variable already in the scope
2713 {
2714 bool isPHPArray = md->getLanguage()==SrcLangExt::PHP &&
2715 md->argsString()!=args &&
2716 args.find('[')!=-1;
2717 bool staticsInDifferentFiles =
2718 root->isStatic && md->isStatic() &&
2719 root->fileName!=md->getDefFileName();
2720
2721 if (md->getFileDef() &&
2722 !isPHPArray && // not a php array
2723 !staticsInDifferentFiles
2724 )
2725 // not a php array variable
2726 {
2727 AUTO_TRACE_ADD("variable already found: scope='{}'",md->getOuterScope()->name());
2728 addMemberDocs(root,md,def,nullptr,FALSE,root->spec);
2729 md->setRefItems(root->sli);
2730 // if md is a variable forward declaration and root is the definition that
2731 // turn md into the definition
2732 if (!root->explicitExternal && md->isExternal())
2733 {
2734 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
2736 }
2737 // if md is the definition and root point at a declaration, then add the
2738 // declaration info
2739 else if (root->explicitExternal && !md->isExternal())
2740 {
2741 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
2742 }
2743 return md;
2744 }
2745 }
2746 }
2747 }
2748
2749 QCString fileName = root->fileName;
2750 if (fileName.isEmpty() && root->tagInfo())
2751 {
2752 fileName = root->tagInfo()->tagName;
2753 }
2754
2755 AUTO_TRACE_ADD("new variable, namespace='{}'",nd?nd->name():QCString("<global>"));
2756 // new global variable, enum value or typedef
2757 auto md = createMemberDef(
2758 fileName,root->startLine,root->startColumn,
2759 type,name,args,QCString(),
2761 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2762 root->argList, root->metaData);
2763 auto mmd = toMemberDefMutable(md.get());
2764 mmd->setTagInfo(root->tagInfo());
2765 mmd->setMemberSpecifiers(root->spec);
2766 mmd->setVhdlSpecifiers(root->vhdlSpec);
2767 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2768 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2769 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2770 mmd->addSectionsToDefinition(root->anchors);
2771 mmd->setFromAnonymousScope(fromAnnScope);
2772 mmd->setFromAnonymousMember(fromAnnMemb);
2773 std::string init = root->initializer.str();
2774 mmd->setInitializer(init.c_str());
2775 mmd->setMaxInitLines(root->initLines);
2776 mmd->setMemberGroupId(root->mGrpId);
2777 mmd->setDefinition(def);
2778 mmd->setLanguage(root->lang);
2779 mmd->setId(root->id);
2781 mmd->setExplicitExternal(root->explicitExternal,fileName,root->startLine,root->startColumn);
2782 mmd->addQualifiers(root->qualifiers);
2783 //md->setOuterScope(fd);
2784 if (!root->explicitExternal)
2785 {
2786 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2787 mmd->setBodyDef(fd);
2788 }
2789 addMemberToGroups(root,md.get());
2791
2792 mmd->setRefItems(root->sli);
2793 if (nd && !nd->isAnonymous())
2794 {
2795 mmd->setNamespace(nd);
2796 nd->insertMember(md.get());
2797 }
2798
2799 // add member to the file (we do this even if we have already inserted
2800 // it into the namespace.
2801 if (fd)
2802 {
2803 mmd->setFileDef(fd);
2804 fd->insertMember(md.get());
2805 }
2806
2807 root->markAsProcessed();
2808
2809 if (mtype==MemberType::Typedef)
2810 {
2811 resolveTemplateInstanceInType(root,nd,md.get());
2812 }
2813
2814 // add member definition to the list of globals
2815 MemberDef *result = md.get();
2816 mn = Doxygen::functionNameLinkedMap->add(name);
2817 mn->push_back(std::move(md));
2818
2819
2820
2821 return result;
2822}
2823
2824/*! See if the return type string \a type is that of a function pointer
2825 * \returns -1 if this is not a function pointer variable or
2826 * the index at which the closing brace of (...*name) was found.
2827 */
2828static int findFunctionPtr(const std::string &type,SrcLangExt lang, int *pLength=nullptr)
2829{
2830 AUTO_TRACE("type='{}' lang={}",type,lang);
2831 if (lang == SrcLangExt::Fortran || lang == SrcLangExt::VHDL)
2832 {
2833 return -1; // Fortran and VHDL do not have function pointers
2834 }
2835
2836 static const reg::Ex re(R"(\‍([^)]*[*&^][^)]*\))");
2837 reg::Match match;
2838 size_t i=std::string::npos;
2839 size_t l=0;
2840 if (reg::search(type,match,re)) // contains (...*...) or (...&...) or (...^...)
2841 {
2842 i = match.position();
2843 l = match.length();
2844 }
2845 if (i!=std::string::npos)
2846 {
2847 size_t di = type.find("decltype(");
2848 if (di!=std::string::npos && di<i)
2849 {
2850 i = std::string::npos;
2851 }
2852 }
2853 size_t bb=type.find('<');
2854 size_t be=type.rfind('>');
2855 bool templFp = false;
2856 if (be!=std::string::npos) {
2857 size_t cc_ast = type.find("::*");
2858 size_t cc_amp = type.find("::&");
2859 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>::*)'
2860 }
2861
2862 if (!type.empty() && // return type is non-empty
2863 i!=std::string::npos && // contains (...*...)
2864 type.find("operator")==std::string::npos && // not an operator
2865 (type.find(")(")==std::string::npos || type.find("typedef ")!=std::string::npos) &&
2866 // not a function pointer return type
2867 (!(bb<i && i<be) || templFp) // bug665855: avoid treating "typedef A<void (T*)> type" as a function pointer
2868 )
2869 {
2870 if (pLength) *pLength=static_cast<int>(l);
2871 //printf("findFunctionPtr=%d\n",(int)i);
2872 AUTO_TRACE_EXIT("result={}",i);
2873 return static_cast<int>(i);
2874 }
2875 else
2876 {
2877 //printf("findFunctionPtr=%d\n",-1);
2878 AUTO_TRACE_EXIT("result=-1");
2879 return -1;
2880 }
2881}
2882
2883//--------------------------------------------------------------------------------------
2884
2885/*! Returns TRUE iff \a type is a class within scope \a context.
2886 * Used to detect variable declarations that look like function prototypes.
2887 */
2888static bool isVarWithConstructor(const Entry *root)
2889{
2890 bool result = false;
2891 bool typeIsClass = false;
2892 bool typePtrType = false;
2893 QCString type;
2894 Definition *ctx = nullptr;
2895 FileDef *fd = root->fileDef();
2896 SymbolResolver resolver(fd);
2897
2898 AUTO_TRACE("isVarWithConstructor({})",root->name);
2899 if (root->parent()->section.isCompound())
2900 { // inside a class
2901 result=FALSE;
2902 AUTO_TRACE_EXIT("inside class: result={}",result);
2903 return result;
2904 }
2905 else if ((fd != nullptr) && (fd->name().endsWith(".c") || fd->name().endsWith(".h")))
2906 { // inside a .c file
2907 result=FALSE;
2908 AUTO_TRACE_EXIT("inside C file: result={}",result);
2909 return result;
2910 }
2911 if (root->type.isEmpty())
2912 {
2913 result=FALSE;
2914 AUTO_TRACE_EXIT("no type: result={}",result);
2915 return result;
2916 }
2917 if (!root->parent()->name.isEmpty())
2918 {
2919 ctx=Doxygen::namespaceLinkedMap->find(root->parent()->name);
2920 }
2921 type = root->type;
2922 // remove qualifiers
2923 findAndRemoveWord(type,"const");
2924 findAndRemoveWord(type,"static");
2925 findAndRemoveWord(type,"volatile");
2926 typePtrType = type.find('*')!=-1 || type.find('&')!=-1;
2927 if (!typePtrType)
2928 {
2929 typeIsClass = resolver.resolveClass(ctx,type)!=nullptr;
2930 int ti=0;
2931 if (!typeIsClass && (ti=type.find('<'))!=-1)
2932 {
2933 typeIsClass=resolver.resolveClass(ctx,type.left(ti))!=nullptr;
2934 }
2935 }
2936 if (typeIsClass) // now we still have to check if the arguments are
2937 // types or values. Since we do not have complete type info
2938 // we need to rely on heuristics :-(
2939 {
2940 if (root->argList.empty())
2941 {
2942 result=FALSE; // empty arg list -> function prototype.
2943 AUTO_TRACE_EXIT("empty arg list: result={}",result);
2944 return result;
2945 }
2946 for (const Argument &a : root->argList)
2947 {
2948 static const reg::Ex initChars(R"([\d"'&*!^]+)");
2949 reg::Match match;
2950 if (!a.name.isEmpty() || !a.defval.isEmpty())
2951 {
2952 std::string name = a.name.str();
2953 if (reg::search(name,match,initChars) && match.position()==0)
2954 {
2955 result=TRUE;
2956 }
2957 else
2958 {
2959 result=FALSE; // arg has (type,name) pair -> function prototype
2960 }
2961 AUTO_TRACE_EXIT("function prototype: result={}",result);
2962 return result;
2963 }
2964 if (!a.type.isEmpty() &&
2965 (a.type.at(a.type.length()-1)=='*' ||
2966 a.type.at(a.type.length()-1)=='&'))
2967 // type ends with * or & => pointer or reference
2968 {
2969 result=FALSE;
2970 AUTO_TRACE_EXIT("pointer or reference: result={}",result);
2971 return result;
2972 }
2973 if (a.type.isEmpty() || resolver.resolveClass(ctx,a.type)!=nullptr)
2974 {
2975 result=FALSE; // arg type is a known type
2976 AUTO_TRACE_EXIT("known type: result={}",result);
2977 return result;
2978 }
2979 if (checkIfTypedef(ctx,fd,a.type))
2980 {
2981 result=FALSE; // argument is a typedef
2982 AUTO_TRACE_EXIT("typedef: result={}",result);
2983 return result;
2984 }
2985 std::string atype = a.type.str();
2986 if (reg::search(atype,match,initChars) && match.position()==0)
2987 {
2988 result=TRUE; // argument type starts with typical initializer char
2989 AUTO_TRACE_EXIT("argument with init char: result={}",result);
2990 return result;
2991 }
2992 std::string resType=resolveTypeDef(ctx,a.type).str();
2993 if (resType.empty()) resType=atype;
2994 static const reg::Ex idChars(R"(\a\w*)");
2995 if (reg::search(resType,match,idChars) && match.position()==0) // resType starts with identifier
2996 {
2997 resType=match.str();
2998 if (resType=="int" || resType=="long" ||
2999 resType=="float" || resType=="double" ||
3000 resType=="char" || resType=="void" ||
3001 resType=="signed" || resType=="unsigned" ||
3002 resType=="const" || resType=="volatile" )
3003 {
3004 result=FALSE; // type keyword -> function prototype
3005 AUTO_TRACE_EXIT("type keyword: result={}",result);
3006 return result;
3007 }
3008 }
3009 }
3010 result=TRUE;
3011 }
3012
3013 AUTO_TRACE_EXIT("end: result={}",result);
3014 return result;
3015}
3016
3017//--------------------------------------------------------------------------------------
3018
3019/*! Searches for the end of a template in prototype \a s starting from
3020 * character position \a startPos. If the end was found the position
3021 * of the closing > is returned, otherwise -1 is returned.
3022 *
3023 * Handles exotic cases such as
3024 * \code
3025 * Class<(id<0)>
3026 * Class<bits<<2>
3027 * Class<"<">
3028 * Class<'<'>
3029 * Class<(")<")>
3030 * \endcode
3031 */
3032static int findEndOfTemplate(const QCString &s,size_t startPos)
3033{
3034 // locate end of template
3035 size_t e=startPos;
3036 int brCount=1;
3037 int roundCount=0;
3038 size_t len = s.length();
3039 bool insideString=FALSE;
3040 bool insideChar=FALSE;
3041 char pc = 0;
3042 while (e<len && brCount!=0)
3043 {
3044 char c=s.at(e);
3045 switch(c)
3046 {
3047 case '<':
3048 if (!insideString && !insideChar)
3049 {
3050 if (e<len-1 && s.at(e+1)=='<')
3051 e++;
3052 else if (roundCount==0)
3053 brCount++;
3054 }
3055 break;
3056 case '>':
3057 if (!insideString && !insideChar)
3058 {
3059 if (e<len-1 && s.at(e+1)=='>')
3060 e++;
3061 else if (roundCount==0)
3062 brCount--;
3063 }
3064 break;
3065 case '(':
3066 if (!insideString && !insideChar)
3067 roundCount++;
3068 break;
3069 case ')':
3070 if (!insideString && !insideChar)
3071 roundCount--;
3072 break;
3073 case '"':
3074 if (!insideChar)
3075 {
3076 if (insideString && pc!='\\')
3077 insideString=FALSE;
3078 else
3079 insideString=TRUE;
3080 }
3081 break;
3082 case '\'':
3083 if (!insideString)
3084 {
3085 if (insideChar && pc!='\\')
3086 insideChar=FALSE;
3087 else
3088 insideChar=TRUE;
3089 }
3090 break;
3091 }
3092 pc = c;
3093 e++;
3094 }
3095 return brCount==0 ? static_cast<int>(e) : -1;
3096}
3097
3098//--------------------------------------------------------------------------------------
3099
3100static void addVariable(const Entry *root,int isFuncPtr=-1)
3101{
3102 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
3103
3104 AUTO_TRACE("VARIABLE_SEC: type='{}' name='{}' args='{}' bodyLine={} endBodyLine={} mGrpId={} relates='{}'",
3105 root->type, root->name, root->args, root->bodyLine, root->endBodyLine, root->mGrpId, root->relates);
3106 //printf("root->parent->name=%s\n",qPrint(root->parent->name));
3107
3108 QCString type = root->type;
3109 QCString name = root->name;
3110 QCString args = root->args;
3111 if (type.isEmpty() && name.find("operator")==-1 &&
3112 (name.find('*')!=-1 || name.find('&')!=-1))
3113 {
3114 // recover from parse error caused by redundant braces
3115 // like in "int *(var[10]);", which is parsed as
3116 // type="" name="int *" args="(var[10])"
3117
3118 type=name;
3119 std::string sargs = args.str();
3120 static const reg::Ex reName(R"(\a\w*)");
3121 reg::Match match;
3122 if (reg::search(sargs,match,reName))
3123 {
3124 name = match.str(); // e.g. 'var' in '(var[10])'
3125 sargs = match.suffix().str(); // e.g. '[10]) in '(var[10])'
3126 size_t j = sargs.find(')');
3127 if (j!=std::string::npos) args=sargs.substr(0,j); // extract, e.g '[10]' from '[10])'
3128 }
3129 }
3130 else
3131 {
3132 int i=isFuncPtr;
3133 if (i==-1 && (root->spec.isAlias())==0) i=findFunctionPtr(type.str(),root->lang); // for typedefs isFuncPtr is not yet set
3134 AUTO_TRACE_ADD("functionPtr={}",i!=-1?"yes":"no");
3135 if (i>=0) // function pointer
3136 {
3137 int ai = type.find('[',i);
3138 if (ai>i) // function pointer array
3139 {
3140 args.prepend(type.right(type.length()-ai));
3141 type=type.left(ai);
3142 }
3143 else if (type.find(')',i)!=-1) // function ptr, not variable like "int (*bla)[10]"
3144 {
3145 type=type.left(type.length()-1);
3146 args.prepend(") ");
3147 }
3148 }
3149 }
3150 AUTO_TRACE_ADD("after correction: type='{}' name='{}' args='{}'",type,name,args);
3151
3152 QCString scope;
3153 name=removeRedundantWhiteSpace(name);
3154
3155 // find the scope of this variable
3156 int index = computeQualifiedIndex(name);
3157 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3158 // grouped members are stored with full scope
3159 {
3160 buildScopeFromQualifiedName(name.left(index+2),root->lang,root->tagInfo());
3161 scope=name.left(index);
3162 name=name.mid(index+2);
3163 }
3164 else
3165 {
3166 Entry *p = root->parent();
3167 while (p->section.isScope())
3168 {
3169 QCString scopeName = p->name;
3170 if (!scopeName.isEmpty())
3171 {
3172 scope.prepend(scopeName);
3173 break;
3174 }
3175 p=p->parent();
3176 }
3177 }
3178
3179 type=type.stripWhiteSpace();
3180 ClassDefMutable *cd=nullptr;
3181 bool isRelated=FALSE;
3182 bool isMemberOf=FALSE;
3183
3184 QCString classScope=stripAnonymousNamespaceScope(scope);
3185 if (root->lang==SrcLangExt::CSharp)
3186 {
3187 classScope=mangleCSharpGenericName(classScope);
3188 }
3189 else
3190 {
3191 classScope=stripTemplateSpecifiersFromScope(classScope,FALSE);
3192 }
3193 QCString annScopePrefix=scope.left(scope.length()-classScope.length());
3194
3195
3196 // Look for last :: not part of template specifier
3197 int p=-1;
3198 for (size_t i=0;i<name.length()-1;i++)
3199 {
3200 if (name[i]==':' && name[i+1]==':')
3201 {
3202 p=static_cast<int>(i);
3203 }
3204 else if (name[i]=='<') // skip over template parts,
3205 // i.e. A::B<C::D> => p=1 and
3206 // A<B::C>::D => p=8
3207 {
3208 int e = findEndOfTemplate(name,i+1);
3209 if (e!=-1) i=static_cast<int>(e);
3210 }
3211 }
3212
3213 if (p!=-1) // found it
3214 {
3215 if (type=="friend class" || type=="friend struct" ||
3216 type=="friend union")
3217 {
3218 cd=getClassMutable(scope);
3219 if (cd)
3220 {
3221 addVariableToClass(root, // entry
3222 cd, // class to add member to
3223 MemberType::Friend, // type of member
3224 type, // type value as string
3225 name, // name of the member
3226 args, // arguments as string
3227 FALSE, // from Anonymous scope
3228 nullptr, // anonymous member
3229 Protection::Public, // protection
3230 Relationship::Member // related to a class
3231 );
3232 }
3233 }
3234 if (root->bodyLine!=-1 && root->endBodyLine!=-1) // store the body location for later use
3235 {
3236 Doxygen::staticInitMap.emplace(name.str(),BodyInfo{root->startLine,root->bodyLine,root->endBodyLine});
3237 }
3238
3239
3240 AUTO_TRACE_ADD("static variable {} body=[{}..{}]",name,root->bodyLine,root->endBodyLine);
3241 return; /* skip this member, because it is a
3242 * static variable definition (always?), which will be
3243 * found in a class scope as well, but then we know the
3244 * correct protection level, so only then it will be
3245 * inserted in the correct list!
3246 */
3247 }
3248
3250 if (type=="@")
3252 else if (type.startsWith("typedef "))
3253 mtype=MemberType::Typedef;
3254 else if (type.startsWith("friend "))
3255 mtype=MemberType::Friend;
3256 else if (root->mtype==MethodTypes::Property)
3258 else if (root->mtype==MethodTypes::Event)
3259 mtype=MemberType::Event;
3260 else if (type.find("sequence<") != -1)
3261 mtype=sliceOpt ? MemberType::Sequence : MemberType::Typedef;
3262 else if (type.find("dictionary<") != -1)
3264
3265 if (!root->relates.isEmpty()) // related variable
3266 {
3267 isRelated=TRUE;
3268 isMemberOf=(root->relatesType==RelatesType::MemberOf);
3269 if (getClass(root->relates)==nullptr && !scope.isEmpty())
3270 scope=mergeScopes(scope,root->relates);
3271 else
3272 scope=root->relates;
3273 }
3274
3275 cd=getClassMutable(scope);
3276 if (cd==nullptr && classScope!=scope) cd=getClassMutable(classScope);
3277 if (cd)
3278 {
3279 MemberDef *md=nullptr;
3280
3281 // if cd is an anonymous (=tag less) scope we insert the member
3282 // into a non-anonymous parent scope as well. This is needed to
3283 // be able to refer to it using \var or \fn
3284
3285 //int indentDepth=0;
3286 int si=scope.find('@');
3287 //int anonyScopes = 0;
3288 //bool added=FALSE;
3289
3290 bool inlineSimpleStructs = Config_getBool(INLINE_SIMPLE_STRUCTS);
3291 Relationship relationship = isMemberOf ? Relationship::Foreign :
3292 isRelated ? Relationship::Related :
3294 if (si!=-1 && !inlineSimpleStructs) // anonymous scope or type
3295 {
3296 QCString pScope;
3297 ClassDefMutable *pcd=nullptr;
3298 pScope = scope.left(std::max(si-2,0)); // scope without tag less parts
3299 if (!pScope.isEmpty())
3300 pScope.prepend(annScopePrefix);
3301 else if (annScopePrefix.length()>2)
3302 pScope=annScopePrefix.left(annScopePrefix.length()-2);
3303 if (name.at(0)!='@')
3304 {
3305 if (!pScope.isEmpty() && (pcd=getClassMutable(pScope)))
3306 {
3307 AUTO_TRACE_ADD("Adding anonymous member to scope '{}'",pScope);
3308 md=addVariableToClass(root, // entry
3309 pcd, // class to add member to
3310 mtype, // member type
3311 type, // type value as string
3312 name, // member name
3313 args, // arguments as string
3314 TRUE, // from anonymous scope
3315 nullptr, // from anonymous member
3316 root->protection,
3317 relationship
3318 );
3319 //added=TRUE;
3320 }
3321 else // anonymous scope inside namespace or file => put variable in the global scope
3322 {
3323 if (mtype==MemberType::Variable)
3324 {
3325 AUTO_TRACE_ADD("Adding anonymous member to global scope '{}'");
3326 md=addVariableToFile(root,mtype,pScope,type,name,args,TRUE,nullptr);
3327 }
3328 //added=TRUE;
3329 }
3330 }
3331 }
3332
3333 addVariableToClass(root, // entry
3334 cd, // class to add member to
3335 mtype, // member type
3336 type, // type value as string
3337 name, // name of the member
3338 args, // arguments as string
3339 FALSE, // from anonymous scope
3340 md, // from anonymous member
3341 root->protection,
3342 relationship
3343 );
3344 }
3345 else if (!name.isEmpty()) // global variable
3346 {
3347 addVariableToFile(root,mtype,scope,type,name,args,FALSE,/*nullptr,*/nullptr);
3348 }
3349
3350}
3351
3352//----------------------------------------------------------------------
3353// Searches the Entry tree for typedef documentation sections.
3354// If found they are stored in their class or in the global list.
3355static void buildTypedefList(const Entry *root)
3356{
3357 //printf("buildVarList(%s)\n",qPrint(rootNav->name()));
3358 if (!root->name.isEmpty() &&
3359 root->section.isVariable() &&
3360 root->type.find("typedef ")!=-1 // its a typedef
3361 )
3362 {
3363 AUTO_TRACE();
3365 QCString scope;
3366 int index = computeQualifiedIndex(rname);
3367 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3368 // grouped members are stored with full scope
3369 {
3370 buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
3371 scope=rname.left(index);
3372 rname=rname.mid(index+2);
3373 }
3374 else
3375 {
3376 scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
3377 }
3380 MemberName *mn = Doxygen::functionNameLinkedMap->find(rname);
3381 bool found=false;
3382 if (mn) // symbol with the same name already found
3383 {
3384 for (auto &imd : *mn)
3385 {
3386 if (!imd->isTypedef())
3387 continue;
3388
3389 QCString rtype = root->type;
3390 rtype.stripPrefix("typedef ");
3391
3392 // merge the typedefs only if they're not both grouped, and both are
3393 // either part of the same class, part of the same namespace, or both
3394 // are global (i.e., neither in a class or a namespace)
3395 bool notBothGrouped = root->groups.empty() || imd->getGroupDef()==nullptr; // see example #100
3396 bool bothSameScope = (!cd && !nd) || (cd && imd->getClassDef() == cd) || (nd && imd->getNamespaceDef() == nd);
3397 //printf("imd->isTypedef()=%d imd->typeString()=%s root->type=%s\n",imd->isTypedef(),
3398 // qPrint(imd->typeString()),qPrint(root->type));
3399 if (notBothGrouped && bothSameScope && imd->typeString()==rtype)
3400 {
3401 MemberDefMutable *md = toMemberDefMutable(imd.get());
3402 if (md)
3403 {
3404 md->setDocumentation(root->doc,root->docFile,root->docLine);
3406 md->setDocsForDefinition(!root->proto);
3407 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3409 md->setRefItems(root->sli);
3410 md->addQualifiers(root->qualifiers);
3411
3412 // merge ingroup specifiers
3413 if (md->getGroupDef()==nullptr && !root->groups.empty())
3414 {
3415 addMemberToGroups(root,md);
3416 }
3417 else if (md->getGroupDef()!=nullptr && root->groups.empty())
3418 {
3419 //printf("existing member is grouped, new member not\n");
3420 }
3421 else if (md->getGroupDef()!=nullptr && !root->groups.empty())
3422 {
3423 //printf("both members are grouped\n");
3424 }
3425 found=true;
3426 break;
3427 }
3428 }
3429 }
3430 }
3431 if (found)
3432 {
3433 AUTO_TRACE_ADD("typedef '{}' already found",rname);
3434 // mark the entry as processed, as we copied everything from it elsewhere
3435 // also, otherwise, due to containing `typedef` it may later get treated
3436 // as a function typedef in filterMemberDocumentation, which is incorrect
3437 root->markAsProcessed();
3438 }
3439 else
3440 {
3441 AUTO_TRACE_ADD("new typedef '{}'",rname);
3442 addVariable(root);
3443 }
3444
3445 }
3446 for (const auto &e : root->children())
3447 if (!e->section.isEnum())
3448 buildTypedefList(e.get());
3449}
3450
3451//----------------------------------------------------------------------
3452// Searches the Entry tree for sequence documentation sections.
3453// If found they are stored in the global list.
3454static void buildSequenceList(const Entry *root)
3455{
3456 if (!root->name.isEmpty() &&
3457 root->section.isVariable() &&
3458 root->type.find("sequence<")!=-1 // it's a sequence
3459 )
3460 {
3461 AUTO_TRACE();
3462 addVariable(root);
3463 }
3464 for (const auto &e : root->children())
3465 if (!e->section.isEnum())
3466 buildSequenceList(e.get());
3467}
3468
3469//----------------------------------------------------------------------
3470// Searches the Entry tree for dictionary documentation sections.
3471// If found they are stored in the global list.
3472static void buildDictionaryList(const Entry *root)
3473{
3474 if (!root->name.isEmpty() &&
3475 root->section.isVariable() &&
3476 root->type.find("dictionary<")!=-1 // it's a dictionary
3477 )
3478 {
3479 AUTO_TRACE();
3480 addVariable(root);
3481 }
3482 for (const auto &e : root->children())
3483 if (!e->section.isEnum())
3484 buildDictionaryList(e.get());
3485}
3486
3487//----------------------------------------------------------------------
3488// Searches the Entry tree for Variable documentation sections.
3489// If found they are stored in their class or in the global list.
3490
3491static void buildVarList(const Entry *root)
3492{
3493 //printf("buildVarList(%s) section=%08x\n",qPrint(rootNav->name()),rootNav->section());
3494 int isFuncPtr=-1;
3495 if (!root->name.isEmpty() &&
3496 (root->type.isEmpty() || g_compoundKeywords.find(root->type.str())==g_compoundKeywords.end()) &&
3497 (
3498 (root->section.isVariable() && // it's a variable
3499 root->type.find("typedef ")==-1 // and not a typedef
3500 ) ||
3501 (root->section.isFunction() && // or maybe a function pointer variable
3502 (isFuncPtr=findFunctionPtr(root->type.str(),root->lang))!=-1
3503 ) ||
3504 (root->section.isFunction() && // class variable initialized by constructor
3506 )
3507 )
3508 ) // documented variable
3509 {
3510 AUTO_TRACE();
3511 addVariable(root,isFuncPtr);
3512 }
3513 for (const auto &e : root->children())
3514 if (!e->section.isEnum())
3515 buildVarList(e.get());
3516}
3517
3518//----------------------------------------------------------------------
3519// Searches the Entry tree for Interface sections (UNO IDL only).
3520// If found they are stored in their service or in the global list.
3521//
3522
3524 const Entry *root,
3525 ClassDefMutable *cd,
3526 QCString const& rname)
3527{
3528 FileDef *fd = root->fileDef();
3529 enum MemberType type = root->section.isExportedInterface() ? MemberType::Interface : MemberType::Service;
3530 QCString fileName = root->fileName;
3531 if (fileName.isEmpty() && root->tagInfo())
3532 {
3533 fileName = root->tagInfo()->tagName;
3534 }
3535 auto md = createMemberDef(
3536 fileName, root->startLine, root->startColumn, root->type, rname,
3537 "", "", root->protection, root->virt, root->isStatic, Relationship::Member,
3538 type, ArgumentList(), root->argList, root->metaData);
3539 auto mmd = toMemberDefMutable(md.get());
3540 mmd->setTagInfo(root->tagInfo());
3541 mmd->setMemberClass(cd);
3542 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3543 mmd->setDocsForDefinition(false);
3544 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3545 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3546 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3547 mmd->setMemberSpecifiers(root->spec);
3548 mmd->setVhdlSpecifiers(root->vhdlSpec);
3549 mmd->setMemberGroupId(root->mGrpId);
3550 mmd->setTypeConstraints(root->typeConstr);
3551 mmd->setLanguage(root->lang);
3552 mmd->setBodyDef(fd);
3553 mmd->setFileDef(fd);
3554 mmd->addSectionsToDefinition(root->anchors);
3555 QCString const def = root->type + " " + rname;
3556 mmd->setDefinition(def);
3558 mmd->addQualifiers(root->qualifiers);
3559
3560 AUTO_TRACE("Interface member: fileName='{}' type='{}' name='{}' mtype='{}' prot={} virt={} state={} proto={} def='{}'",
3561 fileName,root->type,rname,type,root->protection,root->virt,root->isStatic,root->proto,def);
3562
3563 // add member to the class cd
3564 cd->insertMember(md.get());
3565 // also add the member as a "base" (to get nicer diagrams)
3566 // "optional" interface/service get Protected which turns into dashed line
3567 BaseInfo base(rname,
3569 TemplateNameMap templateNames;
3570 findClassRelation(root,cd,cd,&base,templateNames,DocumentedOnly,true) ||
3571 findClassRelation(root,cd,cd,&base,templateNames,Undocumented,true);
3572 // add file to list of used files
3573 cd->insertUsedFile(fd);
3574
3575 addMemberToGroups(root,md.get());
3577 root->markAsProcessed();
3578 mmd->setRefItems(root->sli);
3579
3580 // add member to the global list of all members
3581 MemberName *mn = Doxygen::memberNameLinkedMap->add(rname);
3582 mn->push_back(std::move(md));
3583}
3584
3585static void buildInterfaceAndServiceList(const Entry *root)
3586{
3587 if (root->section.isExportedInterface() || root->section.isIncludedService())
3588 {
3589 AUTO_TRACE("Exported interface/included service: type='{}' scope='{}' name='{}' args='{}'"
3590 " relates='{}' relatesType='{}' file='{}' line={} bodyLine={} #tArgLists={}"
3591 " mGrpId={} spec={} proto={} docFile='{}'",
3592 root->type, root->parent()->name, root->name, root->args,
3593 root->relates, root->relatesType, root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(),
3594 root->mGrpId, root->spec, root->proto, root->docFile);
3595
3597
3598 if (!rname.isEmpty())
3599 {
3600 QCString scope = root->parent()->name;
3601 ClassDefMutable *cd = getClassMutable(scope);
3602 assert(cd);
3603 if (cd && ((ClassDef::Interface == cd->compoundType()) ||
3604 (ClassDef::Service == cd->compoundType()) ||
3606 {
3608 }
3609 else
3610 {
3611 assert(false); // was checked by scanner.l
3612 }
3613 }
3614 else if (rname.isEmpty())
3615 {
3616 warn(root->fileName,root->startLine,
3617 "Illegal member name found.");
3618 }
3619 }
3620 // can only have these in IDL anyway
3621 switch (root->lang)
3622 {
3623 case SrcLangExt::Unknown: // fall through (root node always is Unknown)
3624 case SrcLangExt::IDL:
3625 for (const auto &e : root->children()) buildInterfaceAndServiceList(e.get());
3626 break;
3627 default:
3628 return; // nothing to do here
3629 }
3630}
3631
3632
3633//----------------------------------------------------------------------
3634// Searches the Entry tree for Function sections.
3635// If found they are stored in their class or in the global list.
3636
3637static void addMethodToClass(const Entry *root,ClassDefMutable *cd,
3638 const QCString &rtype,const QCString &rname,const QCString &rargs,
3639 bool isFriend,
3640 Protection protection,bool stat,Specifier virt,TypeSpecifier spec,
3641 const QCString &relates
3642 )
3643{
3644 FileDef *fd=root->fileDef();
3645
3646 QCString type = rtype;
3647 QCString args = rargs;
3648
3650 name.stripPrefix("::");
3651
3653 if (isFriend) mtype=MemberType::Friend;
3654 else if (root->mtype==MethodTypes::Signal) mtype=MemberType::Signal;
3655 else if (root->mtype==MethodTypes::Slot) mtype=MemberType::Slot;
3656 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType::DCOP;
3657
3658 // strip redundant template specifier for constructors
3659 int i = -1;
3660 int j = -1;
3661 if ((fd==nullptr || fd->getLanguage()==SrcLangExt::Cpp) &&
3662 !name.startsWith("operator ") && // not operator
3663 (i=name.find('<'))!=-1 && // containing <
3664 (j=name.find('>'))!=-1 && // or >
3665 (j!=i+2 || name.at(i+1)!='=') // but not the C++20 spaceship operator <=>
3666 )
3667 {
3668 name=name.left(i);
3669 }
3670
3671 QCString fileName = root->fileName;
3672 if (fileName.isEmpty() && root->tagInfo())
3673 {
3674 fileName = root->tagInfo()->tagName;
3675 }
3676
3677 //printf("root->name='%s; args='%s' root->argList='%s'\n",
3678 // qPrint(root->name),qPrint(args),qPrint(argListToString(root->argList))
3679 // );
3680
3681 // adding class member
3682 Relationship relationship = relates.isEmpty() ? Relationship::Member :
3685 auto md = createMemberDef(
3686 fileName,root->startLine,root->startColumn,
3687 type,name,args,root->exception,
3688 protection,virt,
3689 stat && root->relatesType!=RelatesType::MemberOf,
3690 relationship,
3691 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3692 root->argList, root->metaData);
3693 auto mmd = toMemberDefMutable(md.get());
3694 mmd->setTagInfo(root->tagInfo());
3695 mmd->setMemberClass(cd);
3696 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3697 mmd->setDocsForDefinition(!root->proto);
3698 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3699 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3700 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3701 mmd->setMemberSpecifiers(spec);
3702 mmd->setVhdlSpecifiers(root->vhdlSpec);
3703 mmd->setMemberGroupId(root->mGrpId);
3704 mmd->setTypeConstraints(root->typeConstr);
3705 mmd->setLanguage(root->lang);
3706 mmd->setRequiresClause(root->req);
3707 mmd->setId(root->id);
3708 mmd->setBodyDef(fd);
3709 mmd->setFileDef(fd);
3710 mmd->addSectionsToDefinition(root->anchors);
3711 QCString def;
3713 SrcLangExt lang = cd->getLanguage();
3714 QCString scopeSeparator=getLanguageSpecificSeparator(lang);
3715 if (scopeSeparator!="::")
3716 {
3717 qualScope = substitute(qualScope,"::",scopeSeparator);
3718 }
3719 if (lang==SrcLangExt::PHP)
3720 {
3721 // for PHP we use Class::method and Namespace\method
3722 scopeSeparator="::";
3723 }
3724// QCString optArgs = root->argList.empty() ? args : QCString();
3725 if (!relates.isEmpty() || isFriend || Config_getBool(HIDE_SCOPE_NAMES))
3726 {
3727 if (!type.isEmpty())
3728 {
3729 def=type+" "+name; //+optArgs;
3730 }
3731 else
3732 {
3733 def=name; //+optArgs;
3734 }
3735 }
3736 else
3737 {
3738 if (!type.isEmpty())
3739 {
3740 def=type+" "+qualScope+scopeSeparator+name; //+optArgs;
3741 }
3742 else
3743 {
3744 def=qualScope+scopeSeparator+name; //+optArgs;
3745 }
3746 }
3747 def.stripPrefix("friend ");
3748 mmd->setDefinition(def);
3750 mmd->addQualifiers(root->qualifiers);
3751
3752 AUTO_TRACE("function member: type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
3753 type, qualScope, rname, args, root->proto, def);
3754
3755 // add member to the class cd
3756 cd->insertMember(md.get());
3757 // add file to list of used files
3758 cd->insertUsedFile(fd);
3759
3760 addMemberToGroups(root,md.get());
3762 root->markAsProcessed();
3763 mmd->setRefItems(root->sli);
3764
3765 // add member to the global list of all members
3766 //printf("Adding member=%s class=%s\n",qPrint(md->name()),qPrint(cd->name()));
3768 mn->push_back(std::move(md));
3769}
3770
3771//------------------------------------------------------------------------------------------
3772
3773static void addGlobalFunction(const Entry *root,const QCString &rname,const QCString &sc)
3774{
3775 QCString scope = sc;
3776
3777 // new global function
3779 auto md = createMemberDef(
3780 root->fileName,root->startLine,root->startColumn,
3781 root->type,name,root->args,root->exception,
3782 root->protection,root->virt,root->isStatic,Relationship::Member,
3784 !root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3785 root->argList,root->metaData);
3786 auto mmd = toMemberDefMutable(md.get());
3787 mmd->setTagInfo(root->tagInfo());
3788 mmd->setLanguage(root->lang);
3789 mmd->setId(root->id);
3790 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3791 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3792 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3793 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
3794 mmd->setDocsForDefinition(!root->proto);
3795 mmd->setTypeConstraints(root->typeConstr);
3796 //md->setBody(root->body);
3797 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3798 FileDef *fd=root->fileDef();
3799 mmd->setBodyDef(fd);
3800 mmd->addSectionsToDefinition(root->anchors);
3801 mmd->setMemberSpecifiers(root->spec);
3802 mmd->setVhdlSpecifiers(root->vhdlSpec);
3803 mmd->setMemberGroupId(root->mGrpId);
3804 mmd->setRequiresClause(root->req);
3805 mmd->setExplicitExternal(root->explicitExternal,root->fileName,root->startLine,root->startColumn);
3806
3807 NamespaceDefMutable *nd = nullptr;
3808 // see if the function is inside a namespace that was not part of
3809 // the name already (in that case nd should be non-zero already)
3810 if (root->parent()->section.isNamespace())
3811 {
3812 //QCString nscope=removeAnonymousScopes(root->parent()->name);
3813 QCString nscope=root->parent()->name;
3814 if (!nscope.isEmpty())
3815 {
3816 nd = getResolvedNamespaceMutable(nscope);
3817 }
3818 }
3819 else if (root->parent()->section.isGroupDoc() && !scope.isEmpty())
3820 {
3822 }
3823
3824 if (!scope.isEmpty())
3825 {
3827 if (sep!="::")
3828 {
3829 scope = substitute(scope,"::",sep);
3830 }
3831 scope+=sep;
3832 }
3833
3834 if (Config_getBool(HIDE_SCOPE_NAMES)) scope = "";
3835 QCString def;
3836 //QCString optArgs = root->argList.empty() ? QCString() : root->args;
3837 if (!root->type.isEmpty())
3838 {
3839 def=root->type+" "+scope+name; //+optArgs;
3840 }
3841 else
3842 {
3843 def=scope+name; //+optArgs;
3844 }
3845 AUTO_TRACE("new non-member function type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
3846 root->type,scope,rname,root->args,root->proto,def);
3847 mmd->setDefinition(def);
3849 mmd->addQualifiers(root->qualifiers);
3850
3851 mmd->setRefItems(root->sli);
3852 if (nd && !nd->name().isEmpty() && nd->name().at(0)!='@')
3853 {
3854 // add member to namespace
3855 mmd->setNamespace(nd);
3856 nd->insertMember(md.get());
3857 }
3858 if (fd)
3859 {
3860 // add member to the file (we do this even if we have already
3861 // inserted it into the namespace)
3862 mmd->setFileDef(fd);
3863 fd->insertMember(md.get());
3864 }
3865
3866 addMemberToGroups(root,md.get());
3868 if (root->relatesType == RelatesType::Simple) // if this is a relatesalso command,
3869 // allow find Member to pick it up
3870 {
3871 root->markAsProcessed(); // Otherwise we have finished with this entry.
3872 }
3873
3874 // add member to the list of file members
3876 mn->push_back(std::move(md));
3877}
3878
3879//------------------------------------------------------------------------------------------
3880
3881static void buildFunctionList(const Entry *root)
3882{
3883 if (root->section.isFunction())
3884 {
3885 AUTO_TRACE("member function: type='{}' scope='{}' name='{}' args='{}' relates='{}' relatesType='{}'"
3886 " file='{}' line={} bodyLine={} #tArgLists={} mGrpId={}"
3887 " spec={} proto={} docFile='{}'",
3888 root->type, root->parent()->name, root->name, root->args, root->relates, root->relatesType,
3889 root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(), root->mGrpId,
3890 root->spec, root->proto, root->docFile);
3891
3892 bool isFriend=root->type.find("friend ")!=-1;
3894 //printf("rname=%s\n",qPrint(rname));
3895
3896 QCString scope;
3897 int index = computeQualifiedIndex(rname);
3898 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3899 // grouped members are stored with full scope
3900 {
3901 buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
3902 scope=rname.left(index);
3903 rname=rname.mid(index+2);
3904 }
3905 else
3906 {
3907 scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
3908 }
3909 if (!rname.isEmpty() && scope.find('@')==-1)
3910 {
3911 // check if this function's parent is a class
3912 if (root->lang==SrcLangExt::CSharp)
3913 {
3914 scope=mangleCSharpGenericName(scope);
3915 }
3916 else
3917 {
3919 }
3920
3921 FileDef *rfd=root->fileDef();
3922
3923 int memIndex=rname.findRev("::");
3924
3926 if (cd && scope+"::"==rname.left(scope.length()+2)) // found A::f inside A
3927 {
3928 // strip scope from name
3929 rname=rname.right(rname.length()-root->parent()->name.length()-2);
3930 }
3931
3932 bool isMember=FALSE;
3933 if (memIndex!=-1)
3934 {
3935 int ts=rname.find('<');
3936 int te=rname.find('>');
3937 if (memIndex>0 && (ts==-1 || te==-1))
3938 {
3939 // note: the following code was replaced by inMember=TRUE to deal with a
3940 // function rname='X::foo' of class X inside a namespace also called X...
3941 // bug id 548175
3942 //nd = Doxygen::namespaceLinkedMap->find(rname.left(memIndex));
3943 //isMember = nd==nullptr;
3944 //if (nd)
3945 //{
3946 // // strip namespace scope from name
3947 // scope=rname.left(memIndex);
3948 // rname=rname.right(rname.length()-memIndex-2);
3949 //}
3950 isMember = TRUE;
3951 }
3952 else
3953 {
3954 isMember=memIndex<ts || memIndex>te;
3955 }
3956 }
3957
3958 if (!root->parent()->name.isEmpty() && root->parent()->section.isCompound() && cd)
3959 {
3960 AUTO_TRACE_ADD("member '{}' of class '{}'", rname,cd->name());
3961 addMethodToClass(root,cd,root->type,rname,root->args,isFriend,
3962 root->protection,root->isStatic,root->virt,root->spec,root->relates);
3963 }
3964 else if (root->parent()->section.isObjcImpl() && cd)
3965 {
3966 const MemberDef *md = cd->getMemberByName(rname);
3967 if (md)
3968 {
3969 MemberDefMutable *mdm = toMemberDefMutable(const_cast<MemberDef*>(md));
3970 if (mdm)
3971 {
3972 mdm->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3973 mdm->setBodyDef(root->fileDef());
3974 }
3975 }
3976 }
3977 else if (!root->parent()->section.isCompound() && !root->parent()->section.isObjcImpl() &&
3978 !isMember &&
3979 (root->relates.isEmpty() || root->relatesType==RelatesType::Duplicate) &&
3980 !root->type.startsWith("extern ") && !root->type.startsWith("typedef ")
3981 )
3982 // no member => unrelated function
3983 {
3984 /* check the uniqueness of the function name in the file.
3985 * A file could contain a function prototype and a function definition
3986 * or even multiple function prototypes.
3987 */
3988 bool found=FALSE;
3989 MemberDef *md_found=nullptr;
3990 MemberName *mn = Doxygen::functionNameLinkedMap->find(rname);
3991 if (mn)
3992 {
3993 AUTO_TRACE_ADD("function '{}' already found",rname);
3994 for (const auto &imd : *mn)
3995 {
3996 MemberDefMutable *md = toMemberDefMutable(imd.get());
3997 if (md)
3998 {
3999 const NamespaceDef *mnd = md->getNamespaceDef();
4000 NamespaceDef *rnd = nullptr;
4001 //printf("root namespace=%s\n",qPrint(rootNav->parent()->name()));
4002 QCString fullScope = scope;
4003 QCString parentScope = root->parent()->name;
4004 if (!parentScope.isEmpty() && !leftScopeMatch(parentScope,scope))
4005 {
4006 if (!scope.isEmpty()) fullScope.prepend("::");
4007 fullScope.prepend(parentScope);
4008 }
4009 //printf("fullScope=%s\n",qPrint(fullScope));
4010 rnd = getResolvedNamespace(fullScope);
4011 const FileDef *mfd = md->getFileDef();
4012 QCString nsName,rnsName;
4013 if (mnd) nsName = mnd->name();
4014 if (rnd) rnsName = rnd->name();
4015 //printf("matching arguments for %s%s %s%s\n",
4016 // qPrint(md->name()),md->argsString(),qPrint(rname),qPrint(argListToString(root->argList)));
4017 const ArgumentList &mdAl = md->argumentList();
4018 const ArgumentList &mdTempl = md->templateArguments();
4019
4020 // in case of template functions, we need to check if the
4021 // functions have the same number of template parameters
4022 bool sameNumTemplateArgs = TRUE;
4023 bool matchingReturnTypes = TRUE;
4024 bool sameRequiresClause = TRUE;
4025 if (!mdTempl.empty() && !root->tArgLists.empty())
4026 {
4027 if (mdTempl.size()!=root->tArgLists.back().size())
4028 {
4029 sameNumTemplateArgs = FALSE;
4030 }
4031 if (md->typeString()!=removeRedundantWhiteSpace(root->type))
4032 {
4033 matchingReturnTypes = FALSE;
4034 }
4035 if (md->requiresClause()!=root->req)
4036 {
4037 sameRequiresClause = FALSE;
4038 }
4039 }
4040 else if (!mdTempl.empty() || !root->tArgLists.empty())
4041 { // if one has template parameters and the other doesn't then that also counts as a
4042 // difference
4043 sameNumTemplateArgs = FALSE;
4044 }
4045
4046 bool staticsInDifferentFiles =
4047 root->isStatic && md->isStatic() && root->fileName!=md->getDefFileName();
4048
4049 if (
4050 matchArguments2(md->getOuterScope(),mfd,&mdAl,
4051 rnd ? rnd : Doxygen::globalScope,rfd,&root->argList,
4052 FALSE,root->lang) &&
4053 sameNumTemplateArgs &&
4054 matchingReturnTypes &&
4055 sameRequiresClause &&
4056 !staticsInDifferentFiles
4057 )
4058 {
4059 GroupDef *gd=nullptr;
4060 if (!root->groups.empty() && !root->groups.front().groupname.isEmpty())
4061 {
4062 gd = Doxygen::groupLinkedMap->find(root->groups.front().groupname);
4063 }
4064 //printf("match!\n");
4065 //printf("mnd=%p rnd=%p nsName=%s rnsName=%s\n",mnd,rnd,qPrint(nsName),qPrint(rnsName));
4066 // see if we need to create a new member
4067 found=(mnd && rnd && nsName==rnsName) || // members are in the same namespace
4068 ((mnd==nullptr && rnd==nullptr && mfd!=nullptr && // no external reference and
4069 mfd->absFilePath()==root->fileName // prototype in the same file
4070 )
4071 );
4072 // otherwise, allow a duplicate global member with the same argument list
4073 if (!found && gd && gd==md->getGroupDef() && nsName==rnsName)
4074 {
4075 // member is already in the group, so we don't want to add it again.
4076 found=TRUE;
4077 }
4078
4079 AUTO_TRACE_ADD("combining function with prototype found={} in namespace '{}'",found,nsName);
4080
4081 if (found)
4082 {
4083 // merge argument lists
4084 ArgumentList mergedArgList = root->argList;
4085 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.isEmpty());
4086 // merge documentation
4087 if (md->documentation().isEmpty() && !root->doc.isEmpty())
4088 {
4089 if (root->proto)
4090 {
4092 }
4093 else
4094 {
4096 }
4097 }
4098
4099 md->setDocumentation(root->doc,root->docFile,root->docLine);
4101 md->setDocsForDefinition(!root->proto);
4102 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
4103 {
4104 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
4105 md->setBodyDef(rfd);
4106 }
4107
4108 if (md->briefDescription().isEmpty() && !root->brief.isEmpty())
4109 {
4110 md->setArgsString(root->args);
4111 }
4112 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
4113
4115
4117 md->addQualifiers(root->qualifiers);
4118
4119 // merge ingroup specifiers
4120 if (md->getGroupDef()==nullptr && !root->groups.empty())
4121 {
4122 addMemberToGroups(root,md);
4123 }
4124 else if (md->getGroupDef()!=nullptr && root->groups.empty())
4125 {
4126 //printf("existing member is grouped, new member not\n");
4127 }
4128 else if (md->getGroupDef()!=nullptr && !root->groups.empty())
4129 {
4130 //printf("both members are grouped\n");
4131 }
4133
4134 // if md is a declaration and root is the corresponding
4135 // definition, then turn md into a definition.
4136 if (md->isPrototype() && !root->proto)
4137 {
4138 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
4139 md->setPrototype(FALSE,root->fileName,root->startLine,root->startColumn);
4140 }
4141 // if md is already the definition, then add the declaration info
4142 else if (!md->isPrototype() && root->proto)
4143 {
4144 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
4145 }
4146 }
4147 }
4148 }
4149 if (found)
4150 {
4151 md_found = md;
4152 break;
4153 }
4154 }
4155 }
4156 if (!found) /* global function is unique with respect to the file */
4157 {
4158 addGlobalFunction(root,rname,scope);
4159 }
4160 else
4161 {
4162 FileDef *fd=root->fileDef();
4163 if (fd)
4164 {
4165 // add member to the file (we do this even if we have already
4166 // inserted it into the namespace)
4167 fd->insertMember(md_found);
4168 }
4169 }
4170
4171 AUTO_TRACE_ADD("unrelated function type='{}' name='{}' args='{}'",root->type,rname,root->args);
4172 }
4173 else
4174 {
4175 AUTO_TRACE_ADD("function '{}' is not processed",rname);
4176 }
4177 }
4178 else if (rname.isEmpty())
4179 {
4180 warn(root->fileName,root->startLine,
4181 "Illegal member name found."
4182 );
4183 }
4184 }
4185 for (const auto &e : root->children()) buildFunctionList(e.get());
4186}
4187
4188//----------------------------------------------------------------------
4189
4190static void findFriends()
4191{
4192 AUTO_TRACE();
4193 for (const auto &fn : *Doxygen::functionNameLinkedMap) // for each global function name
4194 {
4195 MemberName *mn = Doxygen::memberNameLinkedMap->find(fn->memberName());
4196 if (mn)
4197 { // there are members with the same name
4198 // for each function with that name
4199 for (const auto &ifmd : *fn)
4200 {
4201 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
4202 // for each member with that name
4203 for (const auto &immd : *mn)
4204 {
4205 MemberDefMutable *mmd = toMemberDefMutable(immd.get());
4206 //printf("Checking for matching arguments
4207 // mmd->isRelated()=%d mmd->isFriend()=%d mmd->isFunction()=%d\n",
4208 // mmd->isRelated(),mmd->isFriend(),mmd->isFunction());
4209 if (fmd && mmd &&
4210 (mmd->isFriend() || (mmd->isRelated() && mmd->isFunction())) &&
4211 matchArguments2(mmd->getOuterScope(), mmd->getFileDef(), &mmd->argumentList(),
4212 fmd->getOuterScope(), fmd->getFileDef(), &fmd->argumentList(),
4213 TRUE,mmd->getLanguage()
4214 )
4215
4216 ) // if the member is related and the arguments match then the
4217 // function is actually a friend.
4218 {
4219 AUTO_TRACE_ADD("Merging related global and member '{}' isFriend={} isRelated={} isFunction={}",
4220 mmd->name(),mmd->isFriend(),mmd->isRelated(),mmd->isFunction());
4221 const ArgumentList &mmdAl = mmd->argumentList();
4222 const ArgumentList &fmdAl = fmd->argumentList();
4223 mergeArguments(const_cast<ArgumentList&>(fmdAl),const_cast<ArgumentList&>(mmdAl));
4224
4225 // reset argument lists to add missing default parameters
4226 QCString mmdAlStr = argListToString(mmdAl);
4227 QCString fmdAlStr = argListToString(fmdAl);
4228 mmd->setArgsString(mmdAlStr);
4229 fmd->setArgsString(fmdAlStr);
4230 mmd->moveDeclArgumentList(std::make_unique<ArgumentList>(mmdAl));
4231 fmd->moveDeclArgumentList(std::make_unique<ArgumentList>(fmdAl));
4232 AUTO_TRACE_ADD("friend args='{}' member args='{}'",argListToString(fmd->argumentList()),argListToString(mmd->argumentList()));
4233
4234 if (!fmd->documentation().isEmpty())
4235 {
4236 mmd->setDocumentation(fmd->documentation(),fmd->docFile(),fmd->docLine());
4237 }
4238 else if (!mmd->documentation().isEmpty())
4239 {
4240 fmd->setDocumentation(mmd->documentation(),mmd->docFile(),mmd->docLine());
4241 }
4242 if (mmd->briefDescription().isEmpty() && !fmd->briefDescription().isEmpty())
4243 {
4244 mmd->setBriefDescription(fmd->briefDescription(),fmd->briefFile(),fmd->briefLine());
4245 }
4246 else if (!mmd->briefDescription().isEmpty() && !fmd->briefDescription().isEmpty())
4247 {
4248 fmd->setBriefDescription(mmd->briefDescription(),mmd->briefFile(),mmd->briefLine());
4249 }
4250 if (!fmd->inbodyDocumentation().isEmpty())
4251 {
4253 }
4254 else if (!mmd->inbodyDocumentation().isEmpty())
4255 {
4257 }
4258 //printf("body mmd %d fmd %d\n",mmd->getStartBodyLine(),fmd->getStartBodyLine());
4259 if (mmd->getStartBodyLine()==-1 && fmd->getStartBodyLine()!=-1)
4260 {
4261 mmd->setBodySegment(fmd->getDefLine(),fmd->getStartBodyLine(),fmd->getEndBodyLine());
4262 mmd->setBodyDef(fmd->getBodyDef());
4263 //mmd->setBodyMember(fmd);
4264 }
4265 else if (mmd->getStartBodyLine()!=-1 && fmd->getStartBodyLine()==-1)
4266 {
4267 fmd->setBodySegment(mmd->getDefLine(),mmd->getStartBodyLine(),mmd->getEndBodyLine());
4268 fmd->setBodyDef(mmd->getBodyDef());
4269 //fmd->setBodyMember(mmd);
4270 }
4272
4274
4275 mmd->addQualifiers(fmd->getQualifiers());
4276 fmd->addQualifiers(mmd->getQualifiers());
4277
4278 }
4279 }
4280 }
4281 }
4282 }
4283}
4284
4285//----------------------------------------------------------------------
4286
4288{
4289 AUTO_TRACE();
4290
4291 // find matching function declaration and definitions.
4292 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4293 {
4294 //printf("memberName=%s count=%zu\n",qPrint(mn->memberName()),mn->size());
4295 /* find a matching function declaration and definition for this function */
4296 for (const auto &imdec : *mn)
4297 {
4298 MemberDefMutable *mdec = toMemberDefMutable(imdec.get());
4299 if (mdec &&
4300 (mdec->isPrototype() ||
4301 (mdec->isVariable() && mdec->isExternal())
4302 ))
4303 {
4304 for (const auto &imdef : *mn)
4305 {
4306 MemberDefMutable *mdef = toMemberDefMutable(imdef.get());
4307 if (mdef && mdec!=mdef &&
4308 mdec->getNamespaceDef()==mdef->getNamespaceDef())
4309 {
4311 }
4312 }
4313 }
4314 }
4315 }
4316}
4317
4318//----------------------------------------------------------------------
4319
4321{
4322 AUTO_TRACE();
4323 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4324 {
4325 MemberDefMutable *mdef=nullptr,*mdec=nullptr;
4326 /* find a matching function declaration and definition for this function */
4327 for (const auto &imd : *mn)
4328 {
4329 MemberDefMutable *md = toMemberDefMutable(imd.get());
4330 if (md)
4331 {
4332 if (md->isPrototype())
4333 mdec=md;
4334 else if (md->isVariable() && md->isExternal())
4335 mdec=md;
4336
4337 if (md->isFunction() && !md->isStatic() && !md->isPrototype())
4338 mdef=md;
4339 else if (md->isVariable() && !md->isExternal() && !md->isStatic())
4340 mdef=md;
4341 }
4342
4343 if (mdef && mdec) break;
4344 }
4345 if (mdef && mdec)
4346 {
4347 const ArgumentList &mdefAl = mdef->argumentList();
4348 const ArgumentList &mdecAl = mdec->argumentList();
4349 if (
4350 matchArguments2(mdef->getOuterScope(),mdef->getFileDef(),const_cast<ArgumentList*>(&mdefAl),
4351 mdec->getOuterScope(),mdec->getFileDef(),const_cast<ArgumentList*>(&mdecAl),
4352 TRUE,mdef->getLanguage()
4353 )
4354 ) /* match found */
4355 {
4356 AUTO_TRACE_ADD("merging references for mdec={} mdef={}",mdec->name(),mdef->name());
4357 mdef->mergeReferences(mdec);
4358 mdec->mergeReferences(mdef);
4359 mdef->mergeReferencedBy(mdec);
4360 mdec->mergeReferencedBy(mdef);
4361 }
4362 }
4363 }
4364}
4365
4366//----------------------------------------------------------------------
4367
4369{
4370 AUTO_TRACE();
4371 // find match between function declaration and definition for
4372 // related functions
4373 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4374 {
4375 /* find a matching function declaration and definition for this function */
4376 // for each global function
4377 for (const auto &imd : *mn)
4378 {
4379 MemberDefMutable *md = toMemberDefMutable(imd.get());
4380 if (md)
4381 {
4382 //printf(" Function '%s'\n",qPrint(md->name()));
4383 MemberName *rmn = Doxygen::memberNameLinkedMap->find(md->name());
4384 if (rmn) // check if there is a member with the same name
4385 {
4386 //printf(" Member name found\n");
4387 // for each member with the same name
4388 for (const auto &irmd : *rmn)
4389 {
4390 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
4391 //printf(" Member found: related='%d'\n",rmd->isRelated());
4392 if (rmd &&
4393 (rmd->isRelated() || rmd->isForeign()) && // related function
4395 rmd->getOuterScope(),rmd->getFileDef(),&rmd->argumentList(),
4396 TRUE,md->getLanguage()
4397 )
4398 )
4399 {
4400 AUTO_TRACE_ADD("Found related member '{}'",md->name());
4401 if (rmd->relatedAlso())
4402 md->setRelatedAlso(rmd->relatedAlso());
4403 else if (rmd->isForeign())
4404 md->makeForeign();
4405 else
4406 md->makeRelated();
4407 }
4408 }
4409 }
4410 }
4411 }
4412 }
4413}
4414
4415//----------------------------------------------------------------------
4416
4418{
4419 AUTO_TRACE();
4420 for (const auto &[qualifiedName,bodyInfo] : Doxygen::staticInitMap)
4421 {
4422 size_t i=qualifiedName.rfind("::");
4423 if (i!=std::string::npos)
4424 {
4425 QCString scope = qualifiedName.substr(0,i);
4426 QCString name = qualifiedName.substr(i+2);
4427 MemberName *mn = Doxygen::memberNameLinkedMap->find(name);
4428 if (mn)
4429 {
4430 for (const auto &imd : *mn)
4431 {
4432 MemberDefMutable *md = toMemberDefMutable(imd.get());
4433 if (md && md->qualifiedName().str()==qualifiedName && md->isVariable())
4434 {
4435 AUTO_TRACE_ADD("found static member {} body [{}..{}]\n",
4436 md->qualifiedName(),bodyInfo.startLine,bodyInfo.endLine);
4437 md->setBodySegment(bodyInfo.defLine,
4438 bodyInfo.startLine,
4439 bodyInfo.endLine);
4440 }
4441 }
4442 }
4443 }
4444 }
4445}
4446
4447//----------------------------------------------------------------------
4448
4449/*! make a dictionary of all template arguments of class cd
4450 * that are part of the base class name.
4451 * Example: A template class A with template arguments <R,S,T>
4452 * that inherits from B<T,T,S> will have T and S in the dictionary.
4453 */
4454static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments,const std::string &name)
4455{
4456 std::map<std::string,int> templateNames;
4457 int count=0;
4458 for (const Argument &arg : templateArguments)
4459 {
4460 static const reg::Ex re(R"(\a[\w:]*)");
4461 reg::Iterator it(name,re);
4463 for (; it!=end ; ++it)
4464 {
4465 const auto &match = *it;
4466 std::string n = match.str();
4467 if (n==arg.name.str())
4468 {
4469 if (templateNames.find(n)==templateNames.end())
4470 {
4471 templateNames.emplace(n,count);
4472 }
4473 }
4474 }
4475 }
4476 return templateNames;
4477}
4478
4479/*! Searches a class from within \a context and \a cd and returns its
4480 * definition if found (otherwise nullptr is returned).
4481 */
4483{
4484 ClassDef *result=nullptr;
4485 if (cd==nullptr)
4486 {
4487 return result;
4488 }
4489 FileDef *fd=cd->getFileDef();
4490 SymbolResolver resolver(fd);
4491 if (context && cd!=context)
4492 {
4493 result = const_cast<ClassDef*>(resolver.resolveClass(context,name,true,true));
4494 }
4495 //printf("1. result=%p\n",result);
4496 if (result==nullptr)
4497 {
4498 result = const_cast<ClassDef*>(resolver.resolveClass(cd,name,true,true));
4499 }
4500 //printf("2. result=%p\n",result);
4501 if (result==nullptr) // try direct class, needed for namespaced classes imported via tag files (see bug624095)
4502 {
4503 result = getClass(name);
4504 }
4505 //printf("3. result=%p\n",result);
4506 //printf("** Trying to find %s within context %s class %s result=%s lookup=%p\n",
4507 // qPrint(name),
4508 // context ? qPrint(context->name()) : "<none>",
4509 // cd ? qPrint(cd->name()) : "<none>",
4510 // result ? qPrint(result->name()) : "<none>",
4511 // Doxygen::classLinkedMap->find(name)
4512 // );
4513 return result;
4514}
4515
4516
4517static void findUsedClassesForClass(const Entry *root,
4518 Definition *context,
4519 ClassDefMutable *masterCd,
4520 ClassDefMutable *instanceCd,
4521 bool isArtificial,
4522 const ArgumentList *actualArgs = nullptr,
4523 const TemplateNameMap &templateNames = TemplateNameMap()
4524 )
4525{
4526 AUTO_TRACE();
4527 const ArgumentList &formalArgs = masterCd->templateArguments();
4528 for (auto &mni : masterCd->memberNameInfoLinkedMap())
4529 {
4530 for (auto &mi : *mni)
4531 {
4532 const MemberDef *md=mi->memberDef();
4533 if (md->isVariable() || md->isObjCProperty()) // for each member variable in this class
4534 {
4535 AUTO_TRACE_ADD("Found variable '{}' in class '{}'",md->name(),masterCd->name());
4536 QCString type = normalizeNonTemplateArgumentsInString(md->typeString(),masterCd,formalArgs);
4537 QCString typedefValue = md->getLanguage()==SrcLangExt::Java ? type : resolveTypeDef(masterCd,type);
4538 if (!typedefValue.isEmpty())
4539 {
4540 type = typedefValue;
4541 }
4542 int pos=0;
4543 QCString usedClassName;
4544 QCString templSpec;
4545 bool found=FALSE;
4546 // the type can contain template variables, replace them if present
4547 type = substituteTemplateArgumentsInString(type,formalArgs,actualArgs);
4548
4549 //printf(" template substitution gives=%s\n",qPrint(type));
4550 while (!found && extractClassNameFromType(type,pos,usedClassName,templSpec,root->lang)!=-1)
4551 {
4552 // find the type (if any) that matches usedClassName
4553 SymbolResolver resolver(masterCd->getFileDef());
4554 const ClassDefMutable *typeCd = resolver.resolveClassMutable(masterCd,usedClassName,false,true);
4555 //printf("====> usedClassName=%s -> typeCd=%s\n",
4556 // qPrint(usedClassName),typeCd?qPrint(typeCd->name()):"<none>");
4557 if (typeCd)
4558 {
4559 usedClassName = typeCd->name();
4560 }
4561
4562 int sp=usedClassName.find('<');
4563 if (sp==-1) sp=0;
4564 // replace any namespace aliases
4565 replaceNamespaceAliases(usedClassName);
4566 // add any template arguments to the class
4567 QCString usedName = removeRedundantWhiteSpace(usedClassName+templSpec);
4568 //printf(" usedName=%s usedClassName=%s templSpec=%s\n",qPrint(usedName),qPrint(usedClassName),qPrint(templSpec));
4569
4570 TemplateNameMap formTemplateNames;
4571 if (templateNames.empty())
4572 {
4573 formTemplateNames = getTemplateArgumentsInName(formalArgs,usedName.str());
4574 }
4576 findClassRelation(root,context,instanceCd,&bi,formTemplateNames,TemplateInstances,isArtificial);
4577
4578 for (const Argument &arg : masterCd->templateArguments())
4579 {
4580 if (arg.name==usedName) // type is a template argument
4581 {
4582 ClassDef *usedCd = Doxygen::hiddenClassLinkedMap->find(usedName);
4583 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4584 if (usedCd==nullptr)
4585 {
4586 usedCdm = toClassDefMutable(
4587 Doxygen::hiddenClassLinkedMap->add(usedName,
4589 masterCd->getDefFileName(),masterCd->getDefLine(),
4590 masterCd->getDefColumn(),
4591 usedName,
4592 ClassDef::Class)));
4593 if (usedCdm)
4594 {
4595 //printf("making %s a template argument!!!\n",qPrint(usedCd->name()));
4596 usedCdm->makeTemplateArgument();
4597 usedCdm->setUsedOnly(TRUE);
4598 usedCdm->setLanguage(masterCd->getLanguage());
4599 usedCd = usedCdm;
4600 }
4601 }
4602 if (usedCd)
4603 {
4604 found=TRUE;
4605 AUTO_TRACE_ADD("case 1: adding used class '{}'", usedCd->name());
4606 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4607 if (usedCdm)
4608 {
4609 if (isArtificial) usedCdm->setArtificial(TRUE);
4610 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4611 }
4612 }
4613 }
4614 }
4615
4616 if (!found)
4617 {
4618 ClassDef *usedCd=findClassWithinClassContext(context,masterCd,usedName);
4619 //printf("Looking for used class %s: result=%s master=%s\n",
4620 // qPrint(usedName),usedCd?qPrint(usedCd->name()):"<none>",masterCd?qPrint(masterCd->name()):"<none>");
4621
4622 if (usedCd)
4623 {
4624 found=TRUE;
4625 AUTO_TRACE_ADD("case 2: adding used class '{}'", usedCd->name());
4626 instanceCd->addUsedClass(usedCd,md->name(),md->protection()); // class exists
4627 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4628 if (usedCdm)
4629 {
4630 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4631 }
4632 }
4633 }
4634 }
4635 if (!found && !type.isEmpty()) // used class is not documented in any scope
4636 {
4637 ClassDef *usedCd = Doxygen::hiddenClassLinkedMap->find(type);
4638 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4639 if (usedCd==nullptr && !Config_getBool(HIDE_UNDOC_RELATIONS))
4640 {
4641 if (type.endsWith("(*") || type.endsWith("(^")) // type is a function pointer
4642 {
4643 type+=md->argsString();
4644 }
4645 AUTO_TRACE_ADD("New undocumented used class '{}'", type);
4646 usedCdm = toClassDefMutable(
4649 masterCd->getDefFileName(),masterCd->getDefLine(),
4650 masterCd->getDefColumn(),
4651 type,ClassDef::Class)));
4652 if (usedCdm)
4653 {
4654 usedCdm->setUsedOnly(TRUE);
4655 usedCdm->setLanguage(masterCd->getLanguage());
4656 usedCd = usedCdm;
4657 }
4658 }
4659 if (usedCd)
4660 {
4661 AUTO_TRACE_ADD("case 3: adding used class '{}'", usedCd->name());
4662 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4663 if (usedCdm)
4664 {
4665 if (isArtificial) usedCdm->setArtificial(TRUE);
4666 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4667 }
4668 }
4669 }
4670 }
4671 }
4672 }
4673}
4674
4676 const Entry *root,
4677 Definition *context,
4678 ClassDefMutable *masterCd,
4679 ClassDefMutable *instanceCd,
4681 bool isArtificial,
4682 const ArgumentList *actualArgs = nullptr,
4683 const TemplateNameMap &templateNames=TemplateNameMap()
4684 )
4685{
4686 AUTO_TRACE("name={}",root->name);
4687 // The base class could ofcouse also be a non-nested class
4688 const ArgumentList &formalArgs = masterCd->templateArguments();
4689 for (const BaseInfo &bi : root->extends)
4690 {
4691 //printf("masterCd=%s bi.name='%s' #actualArgs=%d\n",
4692 // qPrint(masterCd->localName()),qPrint(bi.name),actualArgs ? (int)actualArgs->size() : -1);
4693 TemplateNameMap formTemplateNames;
4694 if (templateNames.empty())
4695 {
4696 formTemplateNames = getTemplateArgumentsInName(formalArgs,bi.name.str());
4697 }
4698 BaseInfo tbi = bi;
4699 tbi.name = substituteTemplateArgumentsInString(bi.name,formalArgs,actualArgs);
4700 //printf("masterCd=%p instanceCd=%p bi->name=%s tbi.name=%s\n",(void*)masterCd,(void*)instanceCd,qPrint(bi.name),qPrint(tbi.name));
4701
4702 if (mode==DocumentedOnly)
4703 {
4704 // find a documented base class in the correct scope
4705 if (!findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,DocumentedOnly,isArtificial))
4706 {
4707 // 1.8.2: decided to show inheritance relations even if not documented,
4708 // we do make them artificial, so they do not appear in the index
4709 //if (!Config_getBool(HIDE_UNDOC_RELATIONS))
4710 bool b = Config_getBool(HIDE_UNDOC_RELATIONS) ? TRUE : isArtificial;
4711 //{
4712 // no documented base class -> try to find an undocumented one
4713 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,Undocumented,b);
4714 //}
4715 }
4716 }
4717 else if (mode==TemplateInstances)
4718 {
4719 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,TemplateInstances,isArtificial);
4720 }
4721 }
4722}
4723
4724//----------------------------------------------------------------------
4725
4726static void findTemplateInstanceRelation(const Entry *root,
4727 Definition *context,
4728 ClassDefMutable *templateClass,const QCString &templSpec,
4729 const TemplateNameMap &templateNames,
4730 bool isArtificial)
4731{
4732 AUTO_TRACE("Derived from template '{}' with parameters '{}' isArtificial={}",
4733 templateClass->name(),templSpec,isArtificial);
4734
4735 QCString tempArgsStr = tempArgListToString(templateClass->templateArguments(),root->lang,false);
4736 bool existingClass = templSpec==tempArgsStr;
4737 if (existingClass) return; // avoid recursion
4738
4739 bool freshInstance=FALSE;
4740 ClassDefMutable *instanceClass = toClassDefMutable(
4741 templateClass->insertTemplateInstance(
4742 root->fileName,root->startLine,root->startColumn,templSpec,freshInstance));
4743 if (instanceClass)
4744 {
4745 instanceClass->setArtificial(TRUE);
4746 instanceClass->setLanguage(root->lang);
4747
4748 if (freshInstance)
4749 {
4750 AUTO_TRACE_ADD("found fresh instance '{}'",instanceClass->name());
4751 instanceClass->setTemplateBaseClassNames(templateNames);
4752
4753 // search for new template instances caused by base classes of
4754 // instanceClass
4755 auto it_pair = g_classEntries.equal_range(templateClass->name().str());
4756 for (auto it=it_pair.first ; it!=it_pair.second ; ++it)
4757 {
4758 const Entry *templateRoot = it->second;
4759 AUTO_TRACE_ADD("template root found '{}' templSpec='{}'",templateRoot->name,templSpec);
4760 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(root->lang,templSpec);
4761 findBaseClassesForClass(templateRoot,context,templateClass,instanceClass,
4762 TemplateInstances,isArtificial,templArgs.get(),templateNames);
4763
4764 findUsedClassesForClass(templateRoot,context,templateClass,instanceClass,
4765 isArtificial,templArgs.get(),templateNames);
4766 }
4767 }
4768 else
4769 {
4770 AUTO_TRACE_ADD("instance already exists");
4771 }
4772 }
4773}
4774
4775//----------------------------------------------------------------------
4776
4777static void resolveTemplateInstanceInType(const Entry *root,const Definition *scope,const MemberDef *md)
4778{
4779 // For a statement like 'using X = T<A>', add a template instance 'T<A>' as a symbol, so it can
4780 // be used to match arguments (see issue #11111)
4781 AUTO_TRACE();
4782 QCString ttype = md->typeString();
4783 ttype.stripPrefix("typedef ");
4784 int ti=ttype.find('<');
4785 if (ti!=-1)
4786 {
4787 QCString templateClassName = ttype.left(ti);
4788 SymbolResolver resolver(root->fileDef());
4789 ClassDefMutable *baseClass = resolver.resolveClassMutable(scope ? scope : Doxygen::globalScope,
4790 templateClassName, true, true);
4791 AUTO_TRACE_ADD("templateClassName={} baseClass={}",templateClassName,baseClass?baseClass->name():"<none>");
4792 if (baseClass)
4793 {
4794 const ArgumentList &tl = baseClass->templateArguments();
4795 TemplateNameMap templateNames = getTemplateArgumentsInName(tl,templateClassName.str());
4797 baseClass,
4798 ttype.mid(ti),
4799 templateNames,
4800 baseClass->isArtificial());
4801 }
4802 }
4803}
4804
4805//----------------------------------------------------------------------
4806
4807static bool isRecursiveBaseClass(const QCString &scope,const QCString &name)
4808{
4809 QCString n=name;
4810 int index=n.find('<');
4811 if (index!=-1)
4812 {
4813 n=n.left(index);
4814 }
4815 bool result = rightScopeMatch(scope,n);
4816 return result;
4817}
4818
4820{
4821 if (name.isEmpty()) return 0;
4822 int l = static_cast<int>(name.length());
4823 if (name[l-1]=='>') // search backward to find the matching <, allowing nested <...> and strings.
4824 {
4825 int count=1;
4826 int i=l-2;
4827 char insideQuote=0;
4828 while (count>0 && i>=0)
4829 {
4830 char c = name[i--];
4831 switch (c)
4832 {
4833 case '>': if (!insideQuote) count++; break;
4834 case '<': if (!insideQuote) count--; break;
4835 case '\'': if (!insideQuote) insideQuote=c;
4836 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
4837 break;
4838 case '"': if (!insideQuote) insideQuote=c;
4839 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
4840 break;
4841 default: break;
4842 }
4843 }
4844 if (i>=0) l=i+1;
4845 }
4846 return l;
4847}
4848
4850 const Entry *root,
4851 Definition *context,
4852 ClassDefMutable *cd,
4853 const BaseInfo *bi,
4854 const TemplateNameMap &templateNames,
4856 bool isArtificial
4857 )
4858{
4859 AUTO_TRACE("name={} base={} isArtificial={} mode={}",cd->name(),bi->name,isArtificial,(int)mode);
4860
4861 QCString biName=bi->name;
4862 bool explicitGlobalScope=FALSE;
4863 if (biName.startsWith("::")) // explicit global scope
4864 {
4865 biName=biName.right(biName.length()-2);
4866 explicitGlobalScope=TRUE;
4867 }
4868
4869 Entry *parentNode=root->parent();
4870 bool lastParent=FALSE;
4871 do // for each parent scope, starting with the largest scope
4872 // (in case of nested classes)
4873 {
4874 QCString scopeName= parentNode ? parentNode->name : QCString();
4875 int scopeOffset=explicitGlobalScope ? 0 : static_cast<int>(scopeName.length());
4876 do // try all parent scope prefixes, starting with the largest scope
4877 {
4878 //printf("scopePrefix='%s' biName='%s'\n",
4879 // qPrint(scopeName.left(scopeOffset)),qPrint(biName));
4880
4881 QCString baseClassName=biName;
4882 if (scopeOffset>0)
4883 {
4884 baseClassName.prepend(scopeName.left(scopeOffset)+"::");
4885 }
4886 if (root->lang==SrcLangExt::CSharp)
4887 {
4888 baseClassName = mangleCSharpGenericName(baseClassName);
4889 }
4890 AUTO_TRACE_ADD("cd='{}' baseClassName='{}'",cd->name(),baseClassName);
4891 SymbolResolver resolver(cd->getFileDef());
4892 ClassDefMutable *baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
4893 baseClassName,
4894 mode==Undocumented,
4895 true
4896 );
4897 const MemberDef *baseClassTypeDef = resolver.getTypedef();
4898 QCString templSpec = resolver.getTemplateSpec();
4899 //printf("baseClassName=%s baseClass=%p cd=%p explicitGlobalScope=%d\n",
4900 // qPrint(baseClassName),baseClass,cd,explicitGlobalScope);
4901 //printf(" scope='%s' baseClassName='%s' baseClass=%s templSpec=%s\n",
4902 // cd ? qPrint(cd->name()):"<none>",
4903 // qPrint(baseClassName),
4904 // baseClass?qPrint(baseClass->name()):"<none>",
4905 // qPrint(templSpec)
4906 // );
4907 //if (baseClassName.left(root->name.length())!=root->name ||
4908 // baseClassName.at(root->name.length())!='<'
4909 // ) // Check for base class with the same name.
4910 // // If found then look in the outer scope for a match
4911 // // and prevent recursion.
4912 if (!isRecursiveBaseClass(root->name,baseClassName)
4913 || explicitGlobalScope
4914 // sadly isRecursiveBaseClass always true for UNO IDL ifc/svc members
4915 // (i.e. this is needed for addInterfaceOrServiceToServiceOrSingleton)
4916 || (root->lang==SrcLangExt::IDL &&
4917 (root->section.isExportedInterface() ||
4918 root->section.isIncludedService()))
4919 )
4920 {
4921 AUTO_TRACE_ADD("class relation '{}' inherited/used by '{}' found prot={} virt={} templSpec='{}'",
4922 baseClassName, root->name, bi->prot, bi->virt, templSpec);
4923
4924 int i=findTemplateSpecializationPosition(baseClassName);
4925 int si=baseClassName.findRev("::",i);
4926 if (si==-1) si=0;
4927 if (baseClass==nullptr && static_cast<size_t>(i)!=baseClassName.length())
4928 // base class has template specifiers
4929 {
4930 // TODO: here we should try to find the correct template specialization
4931 // but for now, we only look for the unspecialized base class.
4932 int e=findEndOfTemplate(baseClassName,i+1);
4933 //printf("baseClass==0 i=%d e=%d\n",i,e);
4934 if (e!=-1) // end of template was found at e
4935 {
4936 templSpec = removeRedundantWhiteSpace(baseClassName.mid(i,e-i));
4937 baseClassName = baseClassName.left(i)+baseClassName.right(baseClassName.length()-e);
4938 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
4939 baseClassName,
4940 mode==Undocumented,
4941 true
4942 );
4943 baseClassTypeDef = resolver.getTypedef();
4944 //printf("baseClass=%p -> baseClass=%s templSpec=%s\n",
4945 // baseClass,qPrint(baseClassName),qPrint(templSpec));
4946 }
4947 }
4948 else if (baseClass && !templSpec.isEmpty()) // we have a known class, but also
4949 // know it is a template, so see if
4950 // we can also link to the explicit
4951 // instance (for instance if a class
4952 // derived from a template argument)
4953 {
4954 //printf("baseClass=%s templSpec=%s\n",qPrint(baseClass->name()),qPrint(templSpec));
4955 ClassDefMutable *templClass=getClassMutable(baseClass->name()+templSpec);
4956 if (templClass)
4957 {
4958 // use the template instance instead of the template base.
4959 baseClass = templClass;
4960 templSpec.clear();
4961 }
4962 }
4963
4964 //printf("cd=%p baseClass=%p\n",cd,baseClass);
4965 bool found=baseClass!=nullptr && (baseClass!=cd || mode==TemplateInstances);
4966 AUTO_TRACE_ADD("1. found={}",found);
4967 if (!found && si!=-1)
4968 {
4969 // replace any namespace aliases
4970 replaceNamespaceAliases(baseClassName);
4971 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
4972 baseClassName,
4973 mode==Undocumented,
4974 true
4975 );
4976 baseClassTypeDef = resolver.getTypedef();
4977 found=baseClass!=nullptr && baseClass!=cd;
4978 if (found) templSpec = resolver.getTemplateSpec();
4979 }
4980 AUTO_TRACE_ADD("2. found={}",found);
4981
4982 if (!found)
4983 {
4984 baseClass=toClassDefMutable(findClassWithinClassContext(context,cd,baseClassName));
4985 //printf("findClassWithinClassContext(%s,%s)=%p\n",
4986 // qPrint(cd->name()),qPrint(baseClassName),baseClass);
4987 found = baseClass!=nullptr && baseClass!=cd;
4988
4989 }
4990 AUTO_TRACE_ADD("3. found={}",found);
4991 if (!found)
4992 {
4993 // for PHP the "use A\B as C" construct map class C to A::B, so we lookup
4994 // the class name also in the alias mapping.
4995 auto it = Doxygen::namespaceAliasMap.find(baseClassName.str());
4996 if (it!=Doxygen::namespaceAliasMap.end()) // see if it is indeed a class.
4997 {
4998 baseClass=getClassMutable(QCString(it->second.alias));
4999 found = baseClass!=nullptr && baseClass!=cd;
5000 }
5001 }
5002 bool isATemplateArgument = templateNames.find(biName.str())!=templateNames.end();
5003
5004 AUTO_TRACE_ADD("4. found={}",found);
5005 if (found)
5006 {
5007 AUTO_TRACE_ADD("Documented base class '{}' templSpec='{}'",biName,templSpec);
5008 // add base class to this class
5009
5010 // if templSpec is not empty then we should "instantiate"
5011 // the template baseClass. A new ClassDef should be created
5012 // to represent the instance. To be able to add the (instantiated)
5013 // members and documentation of a template class
5014 // (inserted in that template class at a later stage),
5015 // the template should know about its instances.
5016 // the instantiation process, should be done in a recursive way,
5017 // since instantiating a template may introduce new inheritance
5018 // relations.
5019 if (!templSpec.isEmpty() && mode==TemplateInstances)
5020 {
5021 // if baseClass is actually a typedef then we should not
5022 // instantiate it, since typedefs are in a different namespace
5023 // see bug531637 for an example where this would otherwise hang
5024 // Doxygen
5025 if (baseClassTypeDef==nullptr)
5026 {
5027 //printf(" => findTemplateInstanceRelation: %s\n",qPrint(baseClass->name()));
5028 findTemplateInstanceRelation(root,context,baseClass,templSpec,templateNames,baseClass->isArtificial());
5029 }
5030 }
5031 else if (mode==DocumentedOnly || mode==Undocumented)
5032 {
5033 //printf(" => insert base class\n");
5034 QCString usedName;
5035 if (baseClassTypeDef)
5036 {
5037 usedName=biName;
5038 //printf("***** usedName=%s templSpec=%s\n",qPrint(usedName),qPrint(templSpec));
5039 }
5040 Protection prot = bi->prot;
5041 if (Config_getBool(SIP_SUPPORT)) prot=Protection::Public;
5042 if (cd!=baseClass && !cd->isSubClass(baseClass) && baseClass->isBaseClass(cd,true,templSpec)==0) // check for recursion, see bug690787
5043 {
5044 AUTO_TRACE_ADD("insertBaseClass name={} prot={} virt={} templSpec={}",usedName,prot,bi->virt,templSpec);
5045 cd->insertBaseClass(baseClass,usedName,prot,bi->virt,templSpec);
5046 // add this class as super class to the base class
5047 baseClass->insertSubClass(cd,prot,bi->virt,templSpec);
5048 }
5049 else
5050 {
5051 warn(root->fileName,root->startLine,
5052 "Detected potential recursive class relation "
5053 "between class {} and base class {}!",
5054 cd->name(),baseClass->name()
5055 );
5056 }
5057 }
5058 return TRUE;
5059 }
5060 else if (mode==Undocumented && (scopeOffset==0 || isATemplateArgument))
5061 {
5062 AUTO_TRACE_ADD("New undocumented base class '{}' baseClassName='{}' templSpec='{}' isArtificial={}",
5063 biName,baseClassName,templSpec,isArtificial);
5064 baseClass=nullptr;
5065 if (isATemplateArgument)
5066 {
5067 baseClass = toClassDefMutable(Doxygen::hiddenClassLinkedMap->find(baseClassName));
5068 if (baseClass==nullptr) // not found (or alias)
5069 {
5070 baseClass= toClassDefMutable(
5071 Doxygen::hiddenClassLinkedMap->add(baseClassName,
5072 createClassDef(root->fileName,root->startLine,root->startColumn,
5073 baseClassName,
5074 ClassDef::Class)));
5075 if (baseClass) // really added (not alias)
5076 {
5077 if (isArtificial) baseClass->setArtificial(TRUE);
5078 baseClass->setLanguage(root->lang);
5079 }
5080 }
5081 }
5082 else
5083 {
5084 baseClass = toClassDefMutable(Doxygen::classLinkedMap->find(baseClassName));
5085 //printf("*** classDDict->find(%s)=%p biName=%s templSpec=%s\n",
5086 // qPrint(baseClassName),baseClass,qPrint(biName),qPrint(templSpec));
5087 if (baseClass==nullptr) // not found (or alias)
5088 {
5089 baseClass = toClassDefMutable(
5090 Doxygen::classLinkedMap->add(baseClassName,
5091 createClassDef(root->fileName,root->startLine,root->startColumn,
5092 baseClassName,
5093 ClassDef::Class)));
5094 if (baseClass) // really added (not alias)
5095 {
5096 if (isArtificial) baseClass->setArtificial(TRUE);
5097 baseClass->setLanguage(root->lang);
5098 si = baseClassName.findRev("::");
5099 if (si!=-1) // class is nested
5100 {
5101 Definition *sd = findScopeFromQualifiedName(Doxygen::globalScope,baseClassName.left(si),nullptr,root->tagInfo());
5102 if (sd==nullptr || sd==Doxygen::globalScope) // outer scope not found
5103 {
5104 baseClass->setArtificial(TRUE); // see bug678139
5105 }
5106 }
5107 }
5108 }
5109 }
5110 if (baseClass)
5111 {
5112 if (biName.endsWith("-p"))
5113 {
5114 biName="<"+biName.left(biName.length()-2)+">";
5115 }
5116 if (!cd->isSubClass(baseClass) && cd!=baseClass && cd->isBaseClass(baseClass,true,templSpec)==0) // check for recursion
5117 {
5118 AUTO_TRACE_ADD("insertBaseClass name={} prot={} virt={} templSpec={}",biName,bi->prot,bi->virt,templSpec);
5119 // add base class to this class
5120 cd->insertBaseClass(baseClass,biName,bi->prot,bi->virt,templSpec);
5121 // add this class as super class to the base class
5122 baseClass->insertSubClass(cd,bi->prot,bi->virt,templSpec);
5123 }
5124 // the undocumented base was found in this file
5125 baseClass->insertUsedFile(root->fileDef());
5126
5127 Definition *scope = buildScopeFromQualifiedName(baseClass->name(),root->lang,nullptr);
5128 if (scope!=baseClass)
5129 {
5130 baseClass->setOuterScope(scope);
5131 }
5132
5133 if (baseClassName.endsWith("-p"))
5134 {
5136 }
5137 return TRUE;
5138 }
5139 else
5140 {
5141 AUTO_TRACE_ADD("Base class '{}' not created (alias?)",biName);
5142 }
5143 }
5144 else
5145 {
5146 AUTO_TRACE_ADD("Base class '{}' not found",biName);
5147 }
5148 }
5149 else
5150 {
5151 if (mode!=TemplateInstances)
5152 {
5153 warn(root->fileName,root->startLine,
5154 "Detected potential recursive class relation "
5155 "between class {} and base class {}!",
5156 root->name,baseClassName
5157 );
5158 }
5159 // for mode==TemplateInstance this case is quite common and
5160 // indicates a relation between a template class and a template
5161 // instance with the same name.
5162 }
5163 if (scopeOffset==0)
5164 {
5165 scopeOffset=-1;
5166 }
5167 else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1)
5168 {
5169 scopeOffset=0;
5170 }
5171 //printf("new scopeOffset='%d'",scopeOffset);
5172 } while (scopeOffset>=0);
5173
5174 if (parentNode==nullptr)
5175 {
5176 lastParent=TRUE;
5177 }
5178 else
5179 {
5180 parentNode=parentNode->parent();
5181 }
5182 } while (lastParent);
5183
5184 return FALSE;
5185}
5186
5187//----------------------------------------------------------------------
5188// Computes the base and super classes for each class in the tree
5189
5190static bool isClassSection(const Entry *root)
5191{
5192 if ( !root->name.isEmpty() )
5193 {
5194 if (root->section.isCompound())
5195 // is it a compound (class, struct, union, interface ...)
5196 {
5197 return TRUE;
5198 }
5199 else if (root->section.isCompoundDoc())
5200 // is it a documentation block with inheritance info.
5201 {
5202 bool hasExtends = !root->extends.empty();
5203 if (hasExtends) return TRUE;
5204 }
5205 }
5206 return FALSE;
5207}
5208
5209
5210/*! Builds a dictionary of all entry nodes in the tree starting with \a root
5211 */
5212static void findClassEntries(const Entry *root)
5213{
5214 if (isClassSection(root))
5215 {
5216 g_classEntries.emplace(root->name.str(),root);
5217 }
5218 for (const auto &e : root->children()) findClassEntries(e.get());
5219}
5220
5222{
5223 // strip any anonymous scopes first
5226 int i=0;
5227 if ((root->lang==SrcLangExt::CSharp || root->lang==SrcLangExt::Java) &&
5228 (i=bName.find('<'))!=-1)
5229 {
5230 // a Java/C# generic class looks like a C++ specialization, so we need to strip the
5231 // template part before looking for matches
5232 if (root->lang==SrcLangExt::CSharp)
5233 {
5234 bName = mangleCSharpGenericName(root->name);
5235 }
5236 else
5237 {
5238 bName = bName.left(i);
5239 }
5240 }
5241 return bName;
5242}
5243
5244/*! Using the dictionary build by findClassEntries(), this
5245 * function will look for additional template specialization that
5246 * exists as inheritance relations only. These instances will be
5247 * added to the template they are derived from.
5248 */
5250{
5251 AUTO_TRACE();
5252 ClassDefSet visitedClasses;
5253 for (const auto &[name,root] : g_classEntries)
5254 {
5255 QCString bName = extractClassName(root);
5256 ClassDefMutable *cdm = getClassMutable(bName);
5257 if (cdm)
5258 {
5260 }
5261 }
5262}
5263
5265{
5266 AUTO_TRACE("root->name={} cd={}",root->name,cd->name());
5267 int i = root->name.find('<');
5268 if (i!=-1 && root->lang!=SrcLangExt::CSharp && root->lang!=SrcLangExt::Java)
5269 {
5270 ClassDefMutable *master = getClassMutable(root->name.left(i));
5271 if (master && master!=cd && !cd->templateMaster())
5272 {
5273 AUTO_TRACE_ADD("class={} master={}",cd->name(),cd->templateMaster()?cd->templateMaster()->name():"<none>",master->name());
5274 cd->setTemplateMaster(master);
5275 master->insertExplicitTemplateInstance(cd,root->name.mid(i));
5276 }
5277 }
5278}
5279
5281{
5282 AUTO_TRACE();
5283 for (const auto &[name,root] : g_classEntries)
5284 {
5285 QCString bName = extractClassName(root);
5286 ClassDefMutable *cdm = getClassMutable(bName);
5287 if (cdm)
5288 {
5289 findUsedClassesForClass(root,cdm,cdm,cdm,TRUE);
5291 cdm->addTypeConstraints();
5292 }
5293 }
5294}
5295
5297{
5298 AUTO_TRACE();
5299 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5300 {
5301 if (!nd->hasDocumentation())
5302 {
5303 if ((guessSection(nd->getDefFileName()).isHeader() ||
5304 nd->getLanguage() == SrcLangExt::Fortran) && // Fortran doesn't have header files.
5305 !Config_getBool(HIDE_UNDOC_NAMESPACES) // undocumented namespaces are visible
5306 )
5307 {
5308 warn_undoc(nd->getDefFileName(),nd->getDefLine(), "{} {} is not documented.",
5309 nd->getLanguage() == SrcLangExt::Fortran ? "Module" : "Namespace",
5310 nd->name());
5311 }
5312 }
5313 }
5314}
5315
5317{
5318 AUTO_TRACE();
5319 for (const auto &[name,root] : g_classEntries)
5320 {
5321 QCString bName = extractClassName(root);
5322 ClassDefMutable *cd = getClassMutable(bName);
5323 if (cd)
5324 {
5326 }
5327 size_t numMembers = cd ? cd->memberNameInfoLinkedMap().size() : 0;
5328 if ((cd==nullptr || (!cd->hasDocumentation() && !cd->isReference())) && numMembers>0 && !bName.endsWith("::"))
5329 {
5330 if (!root->name.isEmpty() && root->name.find('@')==-1 && // normal name
5331 (guessSection(root->fileName).isHeader() ||
5332 Config_getBool(EXTRACT_LOCAL_CLASSES)) && // not defined in source file
5333 protectionLevelVisible(root->protection) && // hidden by protection
5334 !Config_getBool(HIDE_UNDOC_CLASSES) // undocumented class are visible
5335 )
5336 warn_undoc(root->fileName,root->startLine, "Compound {} is not documented.", root->name);
5337 }
5338 }
5339}
5340
5342{
5343 AUTO_TRACE();
5344 for (const auto &[name,root] : g_classEntries)
5345 {
5349 // strip any anonymous scopes first
5350 if (cd && !cd->getTemplateInstances().empty())
5351 {
5352 AUTO_TRACE_ADD("Template class '{}'",cd->name());
5353 for (const auto &ti : cd->getTemplateInstances()) // for each template instance
5354 {
5355 ClassDefMutable *tcd=toClassDefMutable(ti.classDef);
5356 if (tcd)
5357 {
5358 AUTO_TRACE_ADD("Template instance '{}'",tcd->name());
5359 QCString templSpec = ti.templSpec;
5360 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(tcd->getLanguage(),templSpec);
5361 for (const BaseInfo &bi : root->extends)
5362 {
5363 // check if the base class is a template argument
5364 BaseInfo tbi = bi;
5365 const ArgumentList &tl = cd->templateArguments();
5366 if (!tl.empty())
5367 {
5368 TemplateNameMap baseClassNames = tcd->getTemplateBaseClassNames();
5369 TemplateNameMap templateNames = getTemplateArgumentsInName(tl,bi.name.str());
5370 // for each template name that we inherit from we need to
5371 // substitute the formal with the actual arguments
5372 TemplateNameMap actualTemplateNames;
5373 for (const auto &tn_kv : templateNames)
5374 {
5375 size_t templIndex = tn_kv.second;
5376 Argument actArg;
5377 bool hasActArg=FALSE;
5378 if (templIndex<templArgs->size())
5379 {
5380 actArg=templArgs->at(templIndex);
5381 hasActArg=TRUE;
5382 }
5383 if (hasActArg &&
5384 baseClassNames.find(actArg.type.str())!=baseClassNames.end() &&
5385 actualTemplateNames.find(actArg.type.str())==actualTemplateNames.end()
5386 )
5387 {
5388 actualTemplateNames.emplace(actArg.type.str(),static_cast<int>(templIndex));
5389 }
5390 }
5391
5392 tbi.name = substituteTemplateArgumentsInString(bi.name,tl,templArgs.get());
5393 // find a documented base class in the correct scope
5394 if (!findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,DocumentedOnly,FALSE))
5395 {
5396 // no documented base class -> try to find an undocumented one
5397 findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,Undocumented,TRUE);
5398 }
5399 }
5400 }
5401 }
5402 }
5403 }
5404 }
5405}
5406
5407//-----------------------------------------------------------------------
5408// compute the references (anchors in HTML) for each function in the file
5409
5411{
5412 AUTO_TRACE();
5413 for (const auto &cd : *Doxygen::classLinkedMap)
5414 {
5415 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5416 if (cdm)
5417 {
5418 cdm->computeAnchors();
5419 }
5420 }
5421 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5422 {
5423 for (const auto &fd : *fn)
5424 {
5425 fd->computeAnchors();
5426 }
5427 }
5428 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5429 {
5431 if (ndm)
5432 {
5433 ndm->computeAnchors();
5434 }
5435 }
5436 for (const auto &gd : *Doxygen::groupLinkedMap)
5437 {
5438 gd->computeAnchors();
5439 }
5440}
5441
5442//----------------------------------------------------------------------
5443
5445{
5446 AUTO_TRACE();
5447 for (const auto &cd : *Doxygen::classLinkedMap)
5448 {
5449 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5450 if (cdm)
5451 {
5452 cdm->addListReferences();
5453 }
5454 }
5455
5456 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5457 {
5458 for (const auto &fd : *fn)
5459 {
5460 fd->addListReferences();
5461 }
5462 }
5463
5464 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5465 {
5467 if (ndm)
5468 {
5469 ndm->addListReferences();
5470 }
5471 }
5472
5473 for (const auto &gd : *Doxygen::groupLinkedMap)
5474 {
5475 gd->addListReferences();
5476 }
5477
5478 for (const auto &pd : *Doxygen::pageLinkedMap)
5479 {
5480 QCString name = pd->getOutputFileBase();
5481 if (pd->getGroupDef())
5482 {
5483 name = pd->getGroupDef()->getOutputFileBase();
5484 }
5485 {
5486 const RefItemVector &xrefItems = pd->xrefListItems();
5487 addRefItem(xrefItems,
5488 name,
5489 theTranslator->trPage(TRUE,TRUE),
5490 name,pd->title(),QCString(),nullptr);
5491 }
5492 }
5493
5494 for (const auto &dd : *Doxygen::dirLinkedMap)
5495 {
5496 QCString name = dd->getOutputFileBase();
5497 //if (dd->getGroupDef())
5498 //{
5499 // name = dd->getGroupDef()->getOutputFileBase();
5500 //}
5501 const RefItemVector &xrefItems = dd->xrefListItems();
5502 addRefItem(xrefItems,
5503 name,
5504 theTranslator->trDir(TRUE,TRUE),
5505 name,dd->displayName(),QCString(),nullptr);
5506 }
5507
5509}
5510
5511//----------------------------------------------------------------------
5512
5514{
5515 AUTO_TRACE();
5517 {
5518 rl->generatePage();
5519 }
5520}
5521
5522//----------------------------------------------------------------------
5523// Copy the documentation in entry 'root' to member definition 'md' and
5524// set the function declaration of the member to 'funcDecl'. If the boolean
5525// over_load is set the standard overload text is added.
5526
5527static void addMemberDocs(const Entry *root,
5528 MemberDefMutable *md, const QCString &funcDecl,
5529 const ArgumentList *al,
5530 bool over_load,
5531 TypeSpecifier spec
5532 )
5533{
5534 if (md==nullptr) return;
5535 AUTO_TRACE("scope='{}' name='{}' args='{}' funcDecl='{}' mSpec={}",
5536 root->parent()->name,md->name(),md->argsString(),funcDecl,spec);
5537 if (!root->section.isDoc()) // @fn or @var does not need to specify the complete definition, so don't overwrite it
5538 {
5539 QCString fDecl=funcDecl;
5540 // strip extern specifier
5541 fDecl.stripPrefix("extern ");
5542 md->setDefinition(fDecl);
5543 }
5545 md->addQualifiers(root->qualifiers);
5547 const NamespaceDef *nd=md->getNamespaceDef();
5548 QCString fullName;
5549 if (cd)
5550 fullName = cd->name();
5551 else if (nd)
5552 fullName = nd->name();
5553
5554 if (!fullName.isEmpty()) fullName+="::";
5555 fullName+=md->name();
5556 FileDef *rfd=root->fileDef();
5557
5558 // TODO determine scope based on root not md
5559 Definition *rscope = md->getOuterScope();
5560
5561 const ArgumentList &mdAl = md->argumentList();
5562 if (al)
5563 {
5564 ArgumentList mergedAl = *al;
5565 //printf("merging arguments (1) docs=%d\n",root->doc.isEmpty());
5566 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedAl,!root->doc.isEmpty());
5567 }
5568 else
5569 {
5570 if (
5571 matchArguments2( md->getOuterScope(), md->getFileDef(),const_cast<ArgumentList*>(&mdAl),
5572 rscope,rfd,&root->argList,
5573 TRUE, root->lang
5574 )
5575 )
5576 {
5577 //printf("merging arguments (2)\n");
5578 ArgumentList mergedArgList = root->argList;
5579 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.isEmpty());
5580 }
5581 }
5582 if (over_load) // the \overload keyword was used
5583 {
5585 if (!root->doc.isEmpty())
5586 {
5587 doc+="<p>";
5588 doc+=root->doc;
5589 }
5590 md->setDocumentation(doc,root->docFile,root->docLine);
5592 md->setDocsForDefinition(!root->proto);
5593 }
5594 else
5595 {
5596 //printf("overwrite!\n");
5597 md->setDocumentation(root->doc,root->docFile,root->docLine);
5598 md->setDocsForDefinition(!root->proto);
5599
5600 //printf("overwrite!\n");
5601 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
5602
5603 if (
5604 (md->inbodyDocumentation().isEmpty() ||
5605 !root->parent()->name.isEmpty()
5606 ) && !root->inbodyDocs.isEmpty()
5607 )
5608 {
5610 }
5611 }
5612
5613 //printf("initializer: '%s'(isEmpty=%d) '%s'(isEmpty=%d)\n",
5614 // qPrint(md->initializer()),md->initializer().isEmpty(),
5615 // qPrint(root->initializer),root->initializer.isEmpty()
5616 // );
5617 std::string rootInit = root->initializer.str();
5618 if (md->initializer().isEmpty() && !rootInit.empty())
5619 {
5620 //printf("setInitializer\n");
5621 md->setInitializer(rootInit.c_str());
5622 }
5623 if (md->requiresClause().isEmpty() && !root->req.isEmpty())
5624 {
5625 md->setRequiresClause(root->req);
5626 }
5627
5628 md->setMaxInitLines(root->initLines);
5629
5630 if (rfd)
5631 {
5632 if ((md->getStartBodyLine()==-1 && root->bodyLine!=-1)
5633 )
5634 {
5635 //printf("Setting new body segment [%d,%d]\n",root->bodyLine,root->endBodyLine);
5636 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
5637 md->setBodyDef(rfd);
5638 }
5639
5640 md->setRefItems(root->sli);
5641 }
5642
5644 md->addQualifiers(root->qualifiers);
5645
5646 md->mergeMemberSpecifiers(spec);
5648 addMemberToGroups(root,md);
5650 if (cd) cd->insertUsedFile(rfd);
5651 //printf("root->mGrpId=%d\n",root->mGrpId);
5652 if (root->mGrpId!=-1)
5653 {
5654 if (md->getMemberGroupId()!=-1)
5655 {
5656 if (md->getMemberGroupId()!=root->mGrpId)
5657 {
5658 warn(root->fileName,root->startLine,
5659 "member {} belongs to two different groups. The second one found here will be ignored.",
5660 md->name()
5661 );
5662 }
5663 }
5664 else // set group id
5665 {
5666 //printf("setMemberGroupId=%d md=%s\n",root->mGrpId,qPrint(md->name()));
5667 md->setMemberGroupId(root->mGrpId);
5668 }
5669 }
5670 md->addQualifiers(root->qualifiers);
5671}
5672
5673//----------------------------------------------------------------------
5674// find a class definition given the scope name and (optionally) a
5675// template list specifier
5676
5678 const QCString &scopeName)
5679{
5680 SymbolResolver resolver(fd);
5681 const ClassDef *tcd = resolver.resolveClass(nd,scopeName,true,true);
5682 return tcd;
5683}
5684
5685//----------------------------------------------------------------------------
5686// Returns TRUE, if the entry belongs to the group of the member definition,
5687// otherwise FALSE.
5688
5689static bool isEntryInGroupOfMember(const Entry *root,const MemberDef *md,bool allowNoGroup=false)
5690{
5691 const GroupDef *gd = md->getGroupDef();
5692 if (!gd)
5693 {
5694 return allowNoGroup;
5695 }
5696
5697 for (const auto &g : root->groups)
5698 {
5699 if (g.groupname == gd->name())
5700 {
5701 return true; // matching group
5702 }
5703 }
5704
5705 return false;
5706}
5707
5708//----------------------------------------------------------------------
5709// Adds the documentation contained in 'root' to a global function
5710// with name 'name' and argument list 'args' (for overloading) and
5711// function declaration 'decl' to the corresponding member definition.
5712
5713static bool findGlobalMember(const Entry *root,
5714 const QCString &namespaceName,
5715 const QCString &type,
5716 const QCString &name,
5717 const QCString &tempArg,
5718 const QCString &,
5719 const QCString &decl,
5720 TypeSpecifier /* spec */)
5721{
5722 AUTO_TRACE("namespace='{}' type='{}' name='{}' tempArg='{}' decl='{}'",namespaceName,type,name,tempArg,decl);
5723 QCString n=name;
5724 if (n.isEmpty()) return FALSE;
5725 if (n.find("::")!=-1) return FALSE; // skip undefined class members
5726 MemberName *mn=Doxygen::functionNameLinkedMap->find(n+tempArg); // look in function dictionary
5727 if (mn==nullptr)
5728 {
5729 mn=Doxygen::functionNameLinkedMap->find(n); // try without template arguments
5730 }
5731 if (mn) // function name defined
5732 {
5733 AUTO_TRACE_ADD("Found symbol name");
5734 //int count=0;
5735 bool found=FALSE;
5736 for (const auto &md : *mn)
5737 {
5738 // If the entry has groups, then restrict the search to members which are
5739 // in one of the groups of the entry. If md is not associated with a group yet,
5740 // allow this documentation entry to add the group info.
5741 if (!root->groups.empty() && !isEntryInGroupOfMember(root, md.get(), true))
5742 {
5743 continue;
5744 }
5745
5746 const NamespaceDef *nd=nullptr;
5747 if (md->isAlias() && md->getOuterScope() &&
5748 md->getOuterScope()->definitionType()==Definition::TypeNamespace)
5749 {
5750 nd = toNamespaceDef(md->getOuterScope());
5751 }
5752 else
5753 {
5754 nd = md->getNamespaceDef();
5755 }
5756
5757 // special case for strong enums
5758 int enumNamePos=0;
5759 if (nd && md->isEnumValue() && (enumNamePos=namespaceName.findRev("::"))!=-1)
5760 { // md part of a strong enum in a namespace?
5761 QCString enumName = namespaceName.mid(enumNamePos+2);
5762 if (namespaceName.left(enumNamePos)==nd->name())
5763 {
5764 MemberName *enumMn=Doxygen::functionNameLinkedMap->find(enumName);
5765 if (enumMn)
5766 {
5767 for (const auto &emd : *enumMn)
5768 {
5769 found = emd->isStrong() && md->getEnumScope()==emd.get();
5770 if (found)
5771 {
5772 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,nullptr,FALSE,root->spec);
5773 break;
5774 }
5775 }
5776 }
5777 }
5778 if (found)
5779 {
5780 break;
5781 }
5782 }
5783 else if (nd==nullptr && md->isEnumValue()) // md part of global strong enum?
5784 {
5785 MemberName *enumMn=Doxygen::functionNameLinkedMap->find(namespaceName);
5786 if (enumMn)
5787 {
5788 for (const auto &emd : *enumMn)
5789 {
5790 found = emd->isStrong() && md->getEnumScope()==emd.get();
5791 if (found)
5792 {
5793 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,nullptr,FALSE,root->spec);
5794 break;
5795 }
5796 }
5797 }
5798 }
5799
5800 const FileDef *fd=root->fileDef();
5801 //printf("File %s\n",fd ? qPrint(fd->name()) : "<none>");
5803 if (fd)
5804 {
5805 nl = fd->getUsedNamespaces();
5806 }
5807 //printf("NamespaceList %p\n",nl);
5808
5809 // search in the list of namespaces that are imported via a
5810 // using declaration
5811 bool viaUsingDirective = nd && nl.find(nd->qualifiedName())!=nullptr;
5812
5813 if ((namespaceName.isEmpty() && nd==nullptr) || // not in a namespace
5814 (nd && nd->name()==namespaceName) || // or in the same namespace
5815 viaUsingDirective // member in 'using' namespace
5816 )
5817 {
5818 AUTO_TRACE_ADD("Try to add member '{}' to scope '{}'",md->name(),namespaceName);
5819
5820 NamespaceDef *rnd = nullptr;
5821 if (!namespaceName.isEmpty()) rnd = Doxygen::namespaceLinkedMap->find(namespaceName);
5822
5823 const ArgumentList &mdAl = md.get()->argumentList();
5824 bool matching=
5825 (mdAl.empty() && root->argList.empty()) ||
5826 md->isVariable() || md->isTypedef() || /* in case of function pointers */
5827 matchArguments2(md->getOuterScope(),md.get()->getFileDef(),&mdAl,
5828 rnd ? rnd : Doxygen::globalScope,fd,&root->argList,
5829 FALSE,root->lang);
5830
5831 // for template members we need to check if the number of
5832 // template arguments is the same, otherwise we are dealing with
5833 // different functions.
5834 if (matching && !root->tArgLists.empty())
5835 {
5836 const ArgumentList &mdTempl = md->templateArguments();
5837 if (root->tArgLists.back().size()!=mdTempl.size())
5838 {
5839 matching=FALSE;
5840 }
5841 }
5842
5843 //printf("%s<->%s\n",
5844 // qPrint(argListToString(md->argumentList())),
5845 // qPrint(argListToString(root->argList)));
5846
5847 // For static members we also check if the comment block was found in
5848 // the same file. This is needed because static members with the same
5849 // name can be in different files. Thus it would be wrong to just
5850 // put the comment block at the first syntactically matching member. If
5851 // the comment block belongs to a group of the static member, then add
5852 // the documentation even if it is in a different file.
5853 if (matching && md->isStatic() &&
5854 md->getDefFileName()!=root->fileName &&
5855 mn->size()>1 &&
5856 !isEntryInGroupOfMember(root,md.get()))
5857 {
5858 matching = FALSE;
5859 }
5860
5861 // for template member we also need to check the return type and requires
5862 if (!md->templateArguments().empty() && !root->tArgLists.empty())
5863 {
5864 //printf("Comparing return types '%s'<->'%s'\n",
5865 // md->typeString(),type);
5866 if (md->templateArguments().size()!=root->tArgLists.back().size() ||
5867 md->typeString()!=type ||
5868 md->requiresClause()!=root->req)
5869 {
5870 //printf(" ---> no matching\n");
5871 matching = FALSE;
5872 }
5873 }
5874
5875 if (matching) // add docs to the member
5876 {
5877 AUTO_TRACE_ADD("Match found");
5878 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,&root->argList,FALSE,root->spec);
5879 found=TRUE;
5880 break;
5881 }
5882 }
5883 }
5884 if (!found && root->relatesType!=RelatesType::Duplicate && root->section.isFunction()) // no match
5885 {
5886 QCString fullFuncDecl=decl;
5887 if (!root->argList.empty()) fullFuncDecl+=argListToString(root->argList,TRUE);
5888 QCString warnMsg = "no matching file member found for \n"+fullFuncDecl;
5889 if (mn->size()>0)
5890 {
5891 warnMsg+="\nPossible candidates:";
5892 for (const auto &md : *mn)
5893 {
5894 warnMsg+="\n '";
5895 warnMsg+=replaceAnonymousScopes(md->declaration());
5896 warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
5897 }
5898 }
5899 warn(root->fileName,root->startLine, "{}", qPrint(warnMsg));
5900 }
5901 }
5902 else // got docs for an undefined member!
5903 {
5904 if (root->type!="friend class" &&
5905 root->type!="friend struct" &&
5906 root->type!="friend union" &&
5907 root->type!="friend" &&
5908 (!Config_getBool(TYPEDEF_HIDES_STRUCT) ||
5909 root->type.find("typedef ")==-1)
5910 )
5911 {
5912 warn(root->fileName,root->startLine,
5913 "documented symbol '{}' was not declared or defined.",qPrint(decl)
5914 );
5915 }
5916 }
5917 return TRUE;
5918}
5919
5921 const ArgumentLists &srcTempArgLists,
5922 const ArgumentLists &dstTempArgLists
5923 )
5924{
5925 auto srcIt = srcTempArgLists.begin();
5926 auto dstIt = dstTempArgLists.begin();
5927 while (srcIt!=srcTempArgLists.end() && dstIt!=dstTempArgLists.end())
5928 {
5929 if ((*srcIt).size()!=(*dstIt).size()) return TRUE;
5930 ++srcIt;
5931 ++dstIt;
5932 }
5933 return FALSE;
5934}
5935
5936static bool scopeIsTemplate(const Definition *d)
5937{
5938 bool result=FALSE;
5939 //printf("> scopeIsTemplate(%s)\n",qPrint(d?d->name():"null"));
5941 {
5942 auto cd = toClassDef(d);
5943 result = cd->templateArguments().hasParameters() || cd->templateMaster()!=nullptr ||
5945 }
5946 //printf("< scopeIsTemplate=%d\n",result);
5947 return result;
5948}
5949
5951 const ArgumentLists &srcTempArgLists,
5952 const ArgumentLists &dstTempArgLists,
5953 const std::string &src
5954 )
5955{
5956 std::string dst;
5957 static const reg::Ex re(R"(\a\w*)");
5958 reg::Iterator it(src,re);
5960 //printf("type=%s\n",qPrint(sa->type));
5961 size_t p=0;
5962 for (; it!=end ; ++it) // for each word in srcType
5963 {
5964 const auto &match = *it;
5965 size_t i = match.position();
5966 size_t l = match.length();
5967 bool found=FALSE;
5968 dst+=src.substr(p,i-p);
5969 std::string name=match.str();
5970
5971 auto srcIt = srcTempArgLists.begin();
5972 auto dstIt = dstTempArgLists.begin();
5973 while (srcIt!=srcTempArgLists.end() && !found)
5974 {
5975 const ArgumentList *tdAli = nullptr;
5976 std::vector<Argument>::const_iterator tdaIt;
5977 if (dstIt!=dstTempArgLists.end())
5978 {
5979 tdAli = &(*dstIt);
5980 tdaIt = tdAli->begin();
5981 ++dstIt;
5982 }
5983
5984 const ArgumentList &tsaLi = *srcIt;
5985 for (auto tsaIt = tsaLi.begin(); tsaIt!=tsaLi.end() && !found; ++tsaIt)
5986 {
5987 Argument tsa = *tsaIt;
5988 const Argument *tda = nullptr;
5989 if (tdAli && tdaIt!=tdAli->end())
5990 {
5991 tda = &(*tdaIt);
5992 ++tdaIt;
5993 }
5994 //if (tda) printf("tsa=%s|%s tda=%s|%s\n",
5995 // qPrint(tsa.type),qPrint(tsa.name),
5996 // qPrint(tda->type),qPrint(tda->name));
5997 if (name==tsa.name.str())
5998 {
5999 if (tda && tda->name.isEmpty())
6000 {
6001 QCString tdaName = tda->name;
6002 QCString tdaType = tda->type;
6003 int vc=0;
6004 if (tdaType.startsWith("class ")) vc=6;
6005 else if (tdaType.startsWith("typename ")) vc=9;
6006 if (vc>0) // convert type=="class T" to type=="class" name=="T"
6007 {
6008 tdaName = tdaType.mid(vc);
6009 }
6010 if (!tdaName.isEmpty())
6011 {
6012 name=tdaName.str(); // substitute
6013 found=TRUE;
6014 }
6015 }
6016 }
6017 }
6018
6019 //printf(" srcList='%s' dstList='%s faList='%s'\n",
6020 // qPrint(argListToString(srclali.current())),
6021 // qPrint(argListToString(dstlali.current())),
6022 // funcTempArgList ? qPrint(argListToString(funcTempArgList)) : "<none>");
6023 ++srcIt;
6024 }
6025 dst+=name;
6026 p=i+l;
6027 }
6028 dst+=src.substr(p);
6029 //printf(" substituteTemplatesInString(%s)=%s\n",
6030 // qPrint(src),qPrint(dst));
6031 return QCString(dst);
6032}
6033
6035 const ArgumentLists &srcTempArgLists,
6036 const ArgumentLists &dstTempArgLists,
6037 const ArgumentList &src,
6038 ArgumentList &dst
6039 )
6040{
6041 auto dstIt = dst.begin();
6042 for (const Argument &sa : src)
6043 {
6044 QCString dstType = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.type.str());
6045 QCString dstArray = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.array.str());
6046 if (dstIt == dst.end())
6047 {
6048 Argument da = sa;
6049 da.type = dstType;
6050 da.array = dstArray;
6051 dst.push_back(da);
6052 dstIt = dst.end();
6053 }
6054 else
6055 {
6056 Argument da = *dstIt;
6057 da.type = dstType;
6058 da.array = dstArray;
6059 ++dstIt;
6060 }
6061 }
6066 srcTempArgLists,dstTempArgLists,
6067 src.trailingReturnType().str()));
6068 dst.setIsDeleted(src.isDeleted());
6069 dst.setRefQualifier(src.refQualifier());
6070 dst.setNoParameters(src.noParameters());
6071 //printf("substituteTemplatesInArgList: replacing %s with %s\n",
6072 // qPrint(argListToString(src)),qPrint(argListToString(dst))
6073 // );
6074}
6075
6076//-------------------------------------------------------------------------------------------
6077
6078static void addLocalObjCMethod(const Entry *root,
6079 const QCString &scopeName,
6080 const QCString &funcType,const QCString &funcName,const QCString &funcArgs,
6081 const QCString &exceptions,const QCString &funcDecl,
6082 TypeSpecifier spec)
6083{
6084 AUTO_TRACE();
6085 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
6086 ClassDefMutable *cd=nullptr;
6087 if (Config_getBool(EXTRACT_LOCAL_METHODS) && (cd=getClassMutable(scopeName)))
6088 {
6089 AUTO_TRACE_ADD("Local objective C method '{}' scopeName='{}'",root->name,scopeName);
6090 auto md = createMemberDef(
6091 root->fileName,root->startLine,root->startColumn,
6092 funcType,funcName,funcArgs,exceptions,
6093 root->protection,root->virt,root->isStatic,Relationship::Member,
6095 auto mmd = toMemberDefMutable(md.get());
6096 mmd->setTagInfo(root->tagInfo());
6097 mmd->setLanguage(root->lang);
6098 mmd->setId(root->id);
6099 mmd->makeImplementationDetail();
6100 mmd->setMemberClass(cd);
6101 mmd->setDefinition(funcDecl);
6103 mmd->addQualifiers(root->qualifiers);
6104 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6105 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6106 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6107 mmd->setDocsForDefinition(!root->proto);
6108 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6109 mmd->addSectionsToDefinition(root->anchors);
6110 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6111 FileDef *fd=root->fileDef();
6112 mmd->setBodyDef(fd);
6113 mmd->setMemberSpecifiers(spec);
6114 mmd->setVhdlSpecifiers(root->vhdlSpec);
6115 mmd->setMemberGroupId(root->mGrpId);
6116 cd->insertMember(md.get());
6117 cd->insertUsedFile(fd);
6118 mmd->setRefItems(root->sli);
6119
6121 mn->push_back(std::move(md));
6122 }
6123 else
6124 {
6125 // local objective C method found for class without interface
6126 }
6127}
6128
6129//-------------------------------------------------------------------------------------------
6130
6131static void addMemberFunction(const Entry *root,
6132 MemberName *mn,
6133 const QCString &scopeName,
6134 const QCString &namespaceName,
6135 const QCString &className,
6136 const QCString &funcTyp,
6137 const QCString &funcName,
6138 const QCString &funcArgs,
6139 const QCString &funcTempList,
6140 const QCString &exceptions,
6141 const QCString &type,
6142 const QCString &args,
6143 bool isFriend,
6144 TypeSpecifier spec,
6145 const QCString &relates,
6146 const QCString &funcDecl,
6147 bool overloaded,
6148 bool isFunc)
6149{
6150 AUTO_TRACE();
6151 QCString funcType = funcTyp;
6152 int count=0;
6153 int noMatchCount=0;
6154 bool memFound=FALSE;
6155 for (const auto &imd : *mn)
6156 {
6157 MemberDefMutable *md = toMemberDefMutable(imd.get());
6158 if (md==nullptr) continue;
6160 if (cd==nullptr) continue;
6161 //AUTO_TRACE_ADD("member definition found, scope needed='{}' scope='{}' args='{}' fileName='{}'",
6162 // scopeName, cd->name(), md->argsString(), root->fileName);
6163 FileDef *fd=root->fileDef();
6164 NamespaceDef *nd=nullptr;
6165 if (!namespaceName.isEmpty()) nd=getResolvedNamespace(namespaceName);
6166
6167 //printf("scopeName %s->%s\n",qPrint(scopeName),
6168 // qPrint(stripTemplateSpecifiersFromScope(scopeName,FALSE)));
6169
6170 // if the member we are searching for is an enum value that is part of
6171 // a "strong" enum, we need to look into the fields of the enum for a match
6172 int enumNamePos=0;
6173 if (md->isEnumValue() && (enumNamePos=className.findRev("::"))!=-1)
6174 {
6175 QCString enumName = className.mid(enumNamePos+2);
6176 QCString fullScope = className.left(enumNamePos);
6177 if (!namespaceName.isEmpty()) fullScope.prepend(namespaceName+"::");
6178 if (fullScope==cd->name())
6179 {
6180 MemberName *enumMn=Doxygen::memberNameLinkedMap->find(enumName);
6181 //printf("enumMn(%s)=%p\n",qPrint(className),(void*)enumMn);
6182 if (enumMn)
6183 {
6184 for (const auto &emd : *enumMn)
6185 {
6186 memFound = emd->isStrong() && md->getEnumScope()==emd.get();
6187 if (memFound)
6188 {
6189 addMemberDocs(root,md,funcDecl,nullptr,overloaded,spec);
6190 count++;
6191 }
6192 if (memFound) break;
6193 }
6194 }
6195 }
6196 }
6197 if (memFound) break;
6198
6199 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
6200 if (tcd==nullptr && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
6201 {
6202 // don't be fooled by anonymous scopes
6203 tcd=cd;
6204 }
6205 //printf("Looking for %s inside nd=%s result=%p (%s) cd=%p\n",
6206 // qPrint(scopeName),nd?qPrint(nd->name()):"<none>",tcd,tcd?qPrint(tcd->name()):"",cd);
6207
6208 if (cd && tcd==cd) // member's classes match
6209 {
6210 AUTO_TRACE_ADD("class definition '{}' found",cd->name());
6211
6212 // get the template parameter lists found at the member declaration
6213 ArgumentLists declTemplArgs = cd->getTemplateParameterLists();
6214 const ArgumentList &templAl = md->templateArguments();
6215 if (!templAl.empty())
6216 {
6217 declTemplArgs.push_back(templAl);
6218 }
6219
6220 // get the template parameter lists found at the member definition
6221 const ArgumentLists &defTemplArgs = root->tArgLists;
6222 //printf("defTemplArgs=%p\n",defTemplArgs);
6223
6224 // do we replace the decl argument lists with the def argument lists?
6225 bool substDone=FALSE;
6226 ArgumentList argList;
6227
6228 /* substitute the occurrences of class template names in the
6229 * argument list before matching
6230 */
6231 const ArgumentList &mdAl = md->argumentList();
6232 if (declTemplArgs.size()>0 && declTemplArgs.size()==defTemplArgs.size())
6233 {
6234 /* the function definition has template arguments
6235 * and the class definition also has template arguments, so
6236 * we must substitute the template names of the class by that
6237 * of the function definition before matching.
6238 */
6239 substituteTemplatesInArgList(declTemplArgs,defTemplArgs,mdAl,argList);
6240
6241 substDone=TRUE;
6242 }
6243 else /* no template arguments, compare argument lists directly */
6244 {
6245 argList = mdAl;
6246 }
6247
6248 AUTO_TRACE_ADD("matching '{}'<=>'{}' className='{}' namespaceName='{}'",
6249 argListToString(argList,TRUE),argListToString(root->argList,TRUE),className,namespaceName);
6250
6251 bool matching=
6252 md->isVariable() || md->isTypedef() || // needed for function pointers
6254 md->getClassDef(),md->getFileDef(),&argList,
6255 cd,fd,&root->argList,
6256 TRUE,root->lang);
6257
6258 if (md->getLanguage()==SrcLangExt::ObjC && md->isVariable() && root->section.isFunction())
6259 {
6260 matching = FALSE; // don't match methods and attributes with the same name
6261 }
6262
6263 // for template member we also need to check the return type
6264 if (!md->templateArguments().empty() && !root->tArgLists.empty())
6265 {
6266 QCString memType = md->typeString();
6267 memType.stripPrefix("static "); // see bug700696
6269 className+"::",""); // see bug700693 & bug732594
6271 className+"::",""); // see bug758900
6272 AUTO_TRACE_ADD("Comparing return types '{}'<->'{}' #args {}<->{}",
6273 md->typeString(),funcType,md->templateArguments().size(),root->tArgLists.back().size());
6274 if (md->templateArguments().size()!=root->tArgLists.back().size() || memType!=funcType)
6275 {
6276 //printf(" ---> no matching\n");
6277 matching = FALSE;
6278 }
6279 }
6280 else if (defTemplArgs.size()>declTemplArgs.size())
6281 {
6282 AUTO_TRACE_ADD("Different number of template arguments {} vs {}",defTemplArgs.size(),declTemplArgs.size());
6283 // avoid matching a non-template function in a template class against a
6284 // template function with the same name and parameters, see issue #10184
6285 substDone = false;
6286 matching = false;
6287 }
6288 bool rootIsUserDoc = root->section.isMemberDoc();
6289 bool classIsTemplate = scopeIsTemplate(md->getClassDef());
6290 bool mdIsTemplate = md->templateArguments().hasParameters();
6291 bool classOrMdIsTemplate = mdIsTemplate || classIsTemplate;
6292 bool rootIsTemplate = !root->tArgLists.empty();
6293 //printf("classIsTemplate=%d mdIsTemplate=%d rootIsTemplate=%d\n",classIsTemplate,mdIsTemplate,rootIsTemplate);
6294 if (!rootIsUserDoc && // don't check out-of-line @fn references, see bug722457
6295 (mdIsTemplate || rootIsTemplate) && // either md or root is a template
6296 ((classOrMdIsTemplate && !rootIsTemplate) || (!classOrMdIsTemplate && rootIsTemplate))
6297 )
6298 {
6299 // Method with template return type does not match method without return type
6300 // even if the parameters are the same. See also bug709052
6301 AUTO_TRACE_ADD("Comparing return types: template v.s. non-template");
6302 matching = FALSE;
6303 }
6304
6305 AUTO_TRACE_ADD("Match results of matchArguments2='{}' substDone='{}'",matching,substDone);
6306
6307 if (substDone) // found a new argument list
6308 {
6309 if (matching) // replace member's argument list
6310 {
6312 md->moveArgumentList(std::make_unique<ArgumentList>(argList));
6313 }
6314 else // no match
6315 {
6316 if (!funcTempList.isEmpty() &&
6317 isSpecialization(declTemplArgs,defTemplArgs))
6318 {
6319 // check if we are dealing with a partial template
6320 // specialization. In this case we add it to the class
6321 // even though the member arguments do not match.
6322
6323 addMethodToClass(root,cd,type,md->name(),args,isFriend,
6324 md->protection(),md->isStatic(),md->virtualness(),spec,relates);
6325 return;
6326 }
6327 }
6328 }
6329 if (matching)
6330 {
6331 addMemberDocs(root,md,funcDecl,nullptr,overloaded,spec);
6332 count++;
6333 memFound=TRUE;
6334 }
6335 }
6336 else if (cd && cd!=tcd) // we did find a class with the same name as cd
6337 // but in a different namespace
6338 {
6339 noMatchCount++;
6340 }
6341
6342 if (memFound) break;
6343 }
6344 if (count==0 && root->parent() && root->parent()->section.isObjcImpl())
6345 {
6346 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
6347 return;
6348 }
6349 if (count==0 && !(isFriend && funcType=="class"))
6350 {
6351 int candidates=0;
6352 const ClassDef *ecd = nullptr, *ucd = nullptr;
6353 MemberDef *emd = nullptr, *umd = nullptr;
6354 //printf("Assume template class\n");
6355 for (const auto &md : *mn)
6356 {
6357 MemberDef *cmd=md.get();
6359 ClassDefMutable *ccd=cdmdm ? cdmdm->getClassDefMutable() : nullptr;
6360 //printf("ccd->name()==%s className=%s\n",qPrint(ccd->name()),qPrint(className));
6361 if (ccd!=nullptr && rightScopeMatch(ccd->name(),className))
6362 {
6363 const ArgumentList &templAl = md->templateArguments();
6364 if (!root->tArgLists.empty() && !templAl.empty() &&
6365 root->tArgLists.back().size()<=templAl.size())
6366 {
6367 AUTO_TRACE_ADD("add template specialization");
6368 addMethodToClass(root,ccd,type,md->name(),args,isFriend,
6369 root->protection,root->isStatic,root->virt,spec,relates);
6370 return;
6371 }
6372 if (argListToString(md->argumentList(),FALSE,FALSE) ==
6374 { // exact argument list match -> remember
6375 ucd = ecd = ccd;
6376 umd = emd = cmd;
6377 AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': exact match",
6378 className,ccd->name(),md->argsString());
6379 }
6380 else // arguments do not match, but member name and scope do -> remember
6381 {
6382 ucd = ccd;
6383 umd = cmd;
6384 AUTO_TRACE_ADD("new candidate className='{}' scope='{}' args='{}': no match",
6385 className,ccd->name(),md->argsString());
6386 }
6387 candidates++;
6388 }
6389 }
6390 bool strictProtoMatching = Config_getBool(STRICT_PROTO_MATCHING);
6391 if (!strictProtoMatching)
6392 {
6393 if (candidates==1 && ucd && umd)
6394 {
6395 // we didn't find an actual match on argument lists, but there is only 1 member with this
6396 // name in the same scope, so that has to be the one.
6397 addMemberDocs(root,toMemberDefMutable(umd),funcDecl,nullptr,overloaded,spec);
6398 return;
6399 }
6400 else if (candidates>1 && ecd && emd)
6401 {
6402 // we didn't find a unique match using type resolution,
6403 // but one of the matches has the exact same signature so
6404 // we take that one.
6405 addMemberDocs(root,toMemberDefMutable(emd),funcDecl,nullptr,overloaded,spec);
6406 return;
6407 }
6408 }
6409
6410 QCString warnMsg = "no ";
6411 if (noMatchCount>1) warnMsg+="uniquely ";
6412 warnMsg+="matching class member found for \n";
6413
6414 for (const ArgumentList &al : root->tArgLists)
6415 {
6416 warnMsg+=" template ";
6417 warnMsg+=tempArgListToString(al,root->lang);
6418 warnMsg+='\n';
6419 }
6420
6421 QCString fullFuncDecl=funcDecl;
6422 if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
6423
6424 warnMsg+=" ";
6425 warnMsg+=fullFuncDecl;
6426
6427 if (candidates>0 || noMatchCount>=1)
6428 {
6429 warnMsg+="\nPossible candidates:";
6430
6431 NamespaceDef *nd=nullptr;
6432 if (!namespaceName.isEmpty()) nd=getResolvedNamespace(namespaceName);
6433 FileDef *fd=root->fileDef();
6434
6435 for (const auto &md : *mn)
6436 {
6437 const ClassDef *cd=md->getClassDef();
6438 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
6439 if (tcd==nullptr && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
6440 {
6441 // don't be fooled by anonymous scopes
6442 tcd=cd;
6443 }
6444 if (cd!=nullptr && (rightScopeMatch(cd->name(),className) || (cd!=tcd)))
6445 {
6446 warnMsg+='\n';
6447 const ArgumentList &templAl = md->templateArguments();
6448 warnMsg+=" '";
6449 if (templAl.hasParameters())
6450 {
6451 warnMsg+="template ";
6452 warnMsg+=tempArgListToString(templAl,root->lang);
6453 warnMsg+='\n';
6454 warnMsg+=" ";
6455 }
6456 if (!md->typeString().isEmpty())
6457 {
6458 warnMsg+=md->typeString();
6459 warnMsg+=' ';
6460 }
6462 if (!qScope.isEmpty())
6463 warnMsg+=qScope+"::"+md->name();
6464 warnMsg+=md->argsString();
6465 warnMsg+="' " + warn_line(md->getDefFileName(),md->getDefLine());
6466 }
6467 }
6468 }
6469 warn(root->fileName,root->startLine,"{}",warnMsg);
6470 }
6471}
6472
6473//-------------------------------------------------------------------------------------------
6474
6475static void addMemberSpecialization(const Entry *root,
6476 MemberName *mn,
6477 ClassDefMutable *cd,
6478 const QCString &funcType,
6479 const QCString &funcName,
6480 const QCString &funcArgs,
6481 const QCString &funcDecl,
6482 const QCString &exceptions,
6483 TypeSpecifier spec
6484 )
6485{
6486 AUTO_TRACE("funcType={} funcName={} funcArgs={} funcDecl={} spec={}",funcType,funcName,funcArgs,funcDecl,spec);
6487 MemberDef *declMd=nullptr;
6488 for (const auto &md : *mn)
6489 {
6490 if (md->getClassDef()==cd)
6491 {
6492 // TODO: we should probably also check for matching arguments
6493 declMd = md.get();
6494 break;
6495 }
6496 }
6498 ArgumentList tArgList;
6499 // getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6500 auto md = createMemberDef(
6501 root->fileName,root->startLine,root->startColumn,
6502 funcType,funcName,funcArgs,exceptions,
6503 declMd ? declMd->protection() : root->protection,
6505 mtype,tArgList,root->argList,root->metaData);
6506 auto mmd = toMemberDefMutable(md.get());
6507 //printf("new specialized member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6508 mmd->setTagInfo(root->tagInfo());
6509 mmd->setLanguage(root->lang);
6510 mmd->setId(root->id);
6511 mmd->setMemberClass(cd);
6512 mmd->setTemplateSpecialization(TRUE);
6513 mmd->setTypeConstraints(root->typeConstr);
6514 mmd->setDefinition(funcDecl);
6516 mmd->addQualifiers(root->qualifiers);
6517 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
6518 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6519 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6520 mmd->setDocsForDefinition(!root->proto);
6521 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6522 mmd->addSectionsToDefinition(root->anchors);
6523 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6524 FileDef *fd=root->fileDef();
6525 mmd->setBodyDef(fd);
6526 mmd->setMemberSpecifiers(spec);
6527 mmd->setVhdlSpecifiers(root->vhdlSpec);
6528 mmd->setMemberGroupId(root->mGrpId);
6529 cd->insertMember(md.get());
6530 mmd->setRefItems(root->sli);
6531
6532 mn->push_back(std::move(md));
6533}
6534
6535//-------------------------------------------------------------------------------------------
6536
6537static void addOverloaded(const Entry *root,MemberName *mn,
6538 const QCString &funcType,const QCString &funcName,const QCString &funcArgs,
6539 const QCString &funcDecl,const QCString &exceptions,TypeSpecifier spec)
6540{
6541 // for unique overloaded member we allow the class to be
6542 // omitted, this is to be Qt compatible. Using this should
6543 // however be avoided, because it is error prone
6544 bool sameClass=false;
6545 if (mn->size()>0)
6546 {
6547 // check if all members with the same name are also in the same class
6548 sameClass = std::equal(mn->begin()+1,mn->end(),mn->begin(),
6549 [](const auto &md1,const auto &md2)
6550 { return md1->getClassDef()->name()==md2->getClassDef()->name(); });
6551 }
6552 if (sameClass)
6553 {
6554 MemberDefMutable *mdm = toMemberDefMutable(mn->front().get());
6555 ClassDefMutable *cd = mdm ? mdm->getClassDefMutable() : nullptr;
6556 if (cd==nullptr) return;
6557
6560 else if (root->mtype==MethodTypes::Slot) mtype=MemberType::Slot;
6561 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType::DCOP;
6562
6563 // new overloaded member function
6564 std::unique_ptr<ArgumentList> tArgList =
6565 getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6566 //printf("new related member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6567 auto md = createMemberDef(
6568 root->fileName,root->startLine,root->startColumn,
6569 funcType,funcName,funcArgs,exceptions,
6571 mtype,tArgList ? *tArgList : ArgumentList(),root->argList,root->metaData);
6572 auto mmd = toMemberDefMutable(md.get());
6573 mmd->setTagInfo(root->tagInfo());
6574 mmd->setLanguage(root->lang);
6575 mmd->setId(root->id);
6576 mmd->setTypeConstraints(root->typeConstr);
6577 mmd->setMemberClass(cd);
6578 mmd->setDefinition(funcDecl);
6580 mmd->addQualifiers(root->qualifiers);
6582 doc+="<p>";
6583 doc+=root->doc;
6584 mmd->setDocumentation(doc,root->docFile,root->docLine);
6585 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6586 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6587 mmd->setDocsForDefinition(!root->proto);
6588 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6589 mmd->addSectionsToDefinition(root->anchors);
6590 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6591 FileDef *fd=root->fileDef();
6592 mmd->setBodyDef(fd);
6593 mmd->setMemberSpecifiers(spec);
6594 mmd->setVhdlSpecifiers(root->vhdlSpec);
6595 mmd->setMemberGroupId(root->mGrpId);
6596 cd->insertMember(md.get());
6597 cd->insertUsedFile(fd);
6598 mmd->setRefItems(root->sli);
6599
6600 mn->push_back(std::move(md));
6601 }
6602}
6603
6604static void insertMemberAlias(Definition *outerScope,const MemberDef *md)
6605{
6606 if (outerScope && outerScope!=Doxygen::globalScope)
6607 {
6608 auto aliasMd = createMemberDefAlias(outerScope,md);
6609 if (outerScope->definitionType()==Definition::TypeClass)
6610 {
6611 ClassDefMutable *cdm = toClassDefMutable(outerScope);
6612 if (cdm)
6613 {
6614 cdm->insertMember(aliasMd.get());
6615 }
6616 }
6617 else if (outerScope->definitionType()==Definition::TypeNamespace)
6618 {
6619 NamespaceDefMutable *ndm = toNamespaceDefMutable(outerScope);
6620 if (ndm)
6621 {
6622 ndm->insertMember(aliasMd.get());
6623 }
6624 }
6625 else if (outerScope->definitionType()==Definition::TypeFile)
6626 {
6627 toFileDef(outerScope)->insertMember(aliasMd.get());
6628 }
6629 if (aliasMd)
6630 {
6631 Doxygen::functionNameLinkedMap->add(md->name())->push_back(std::move(aliasMd));
6632 }
6633 }
6634}
6635
6636//-------------------------------------------------------------------------------------------
6637
6638/*! This function tries to find a member (in a documented class/file/namespace)
6639 * that corresponds to the function/variable declaration given in \a funcDecl.
6640 *
6641 * The boolean \a overloaded is used to specify whether or not a standard
6642 * overload documentation line should be generated.
6643 *
6644 * The boolean \a isFunc is a hint that indicates that this is a function
6645 * instead of a variable or typedef.
6646 */
6647static void findMember(const Entry *root,
6648 const QCString &relates,
6649 const QCString &type,
6650 const QCString &args,
6651 QCString funcDecl,
6652 bool overloaded,
6653 bool isFunc
6654 )
6655{
6656 AUTO_TRACE("root='{}' funcDecl='{}' related='{}' overload={} isFunc={} mGrpId={} #tArgList={} spec={} lang={}",
6657 root->name, funcDecl, relates, overloaded, isFunc, root->mGrpId, root->tArgLists.size(),
6658 root->spec, root->lang);
6659
6660 QCString scopeName;
6661 QCString className;
6662 QCString namespaceName;
6663 QCString funcType;
6664 QCString funcName;
6665 QCString funcArgs;
6666 QCString funcTempList;
6667 QCString exceptions;
6668 QCString funcSpec;
6669 bool isRelated=false;
6670 bool isMemberOf=false;
6671 bool isFriend=false;
6672 bool done=false;
6673 TypeSpecifier spec = root->spec;
6674 while (!done)
6675 {
6676 done=true;
6677 if (funcDecl.stripPrefix("friend ")) // treat friends as related members
6678 {
6679 isFriend=true;
6680 done=false;
6681 }
6682 if (funcDecl.stripPrefix("inline "))
6683 {
6684 spec.setInline(true);
6685 done=false;
6686 }
6687 if (funcDecl.stripPrefix("explicit "))
6688 {
6689 spec.setExplicit(true);
6690 done=false;
6691 }
6692 if (funcDecl.stripPrefix("mutable "))
6693 {
6694 spec.setMutable(true);
6695 done=false;
6696 }
6697 if (funcDecl.stripPrefix("virtual "))
6698 {
6699 done=false;
6700 }
6701 }
6702
6703 // delete any ; from the function declaration
6704 int sep=0;
6705 while ((sep=funcDecl.find(';'))!=-1)
6706 {
6707 funcDecl=(funcDecl.left(sep)+funcDecl.right(funcDecl.length()-sep-1)).stripWhiteSpace();
6708 }
6709
6710 // make sure the first character is a space to simplify searching.
6711 if (!funcDecl.isEmpty() && funcDecl[0]!=' ') funcDecl.prepend(" ");
6712
6713 // remove some superfluous spaces
6714 funcDecl= substitute(
6715 substitute(
6716 substitute(funcDecl,"~ ","~"),
6717 ":: ","::"
6718 ),
6719 " ::","::"
6720 ).stripWhiteSpace();
6721
6722 //printf("funcDecl='%s'\n",qPrint(funcDecl));
6723 if (isFriend && funcDecl.startsWith("class "))
6724 {
6725 //printf("friend class\n");
6726 funcDecl=funcDecl.right(funcDecl.length()-6);
6727 funcName = funcDecl;
6728 }
6729 else if (isFriend && funcDecl.startsWith("struct "))
6730 {
6731 funcDecl=funcDecl.right(funcDecl.length()-7);
6732 funcName = funcDecl;
6733 }
6734 else
6735 {
6736 // extract information from the declarations
6737 parseFuncDecl(funcDecl,root->lang,scopeName,funcType,funcName,
6738 funcArgs,funcTempList,exceptions
6739 );
6740 }
6741
6742 // the class name can also be a namespace name, we decide this later.
6743 // if a related class name is specified and the class name could
6744 // not be derived from the function declaration, then use the
6745 // related field.
6746 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}' funcType='{}' funcName='{}' funcArgs='{}'",
6747 scopeName,className,namespaceName,funcType,funcName,funcArgs);
6748 if (!relates.isEmpty())
6749 { // related member, prefix user specified scope
6750 isRelated=TRUE;
6751 isMemberOf=(root->relatesType == RelatesType::MemberOf);
6752 if (getClass(relates)==nullptr && !scopeName.isEmpty())
6753 {
6754 scopeName= mergeScopes(scopeName,relates);
6755 }
6756 else
6757 {
6758 scopeName = relates;
6759 }
6760 }
6761
6762 if (relates.isEmpty() && root->parent() &&
6763 (root->parent()->section.isScope() || root->parent()->section.isObjcImpl()) &&
6764 !root->parent()->name.isEmpty()) // see if we can combine scopeName
6765 // with the scope in which it was found
6766 {
6767 QCString joinedName = root->parent()->name+"::"+scopeName;
6768 if (!scopeName.isEmpty() &&
6769 (getClass(joinedName) || Doxygen::namespaceLinkedMap->find(joinedName)))
6770 {
6771 scopeName = joinedName;
6772 }
6773 else
6774 {
6775 scopeName = mergeScopes(root->parent()->name,scopeName);
6776 }
6777 }
6778 else // see if we can prefix a namespace or class that is used from the file
6779 {
6780 FileDef *fd=root->fileDef();
6781 if (fd)
6782 {
6783 for (const auto &fnd : fd->getUsedNamespaces())
6784 {
6785 QCString joinedName = fnd->name()+"::"+scopeName;
6786 if (Doxygen::namespaceLinkedMap->find(joinedName))
6787 {
6788 scopeName=joinedName;
6789 break;
6790 }
6791 }
6792 }
6793 }
6795 removeRedundantWhiteSpace(scopeName),false,&funcSpec,QCString(),false);
6796
6797 // funcSpec contains the last template specifiers of the given scope.
6798 // If this method does not have any template arguments or they are
6799 // empty while funcSpec is not empty we assume this is a
6800 // specialization of a method. If not, we clear the funcSpec and treat
6801 // this as a normal method of a template class.
6802 if (!(root->tArgLists.size()>0 &&
6803 root->tArgLists.front().size()==0
6804 )
6805 )
6806 {
6807 funcSpec.clear();
6808 }
6809
6810 //namespaceName=removeAnonymousScopes(namespaceName);
6811 if (!Config_getBool(EXTRACT_ANON_NSPACES) && scopeName.find('@')!=-1) return; // skip stuff in anonymous namespace...
6812
6813 // split scope into a namespace and a class part
6814 extractNamespaceName(scopeName,className,namespaceName,TRUE);
6815 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}'",scopeName,className,namespaceName);
6816
6817 //printf("namespaceName='%s' className='%s'\n",qPrint(namespaceName),qPrint(className));
6818 // merge class and namespace scopes again
6819 scopeName.clear();
6820 if (!namespaceName.isEmpty())
6821 {
6822 if (className.isEmpty())
6823 {
6824 scopeName=namespaceName;
6825 }
6826 else if (!relates.isEmpty() || // relates command with explicit scope
6827 !getClass(className)) // class name only exists in a namespace
6828 {
6829 scopeName=namespaceName+"::"+className;
6830 }
6831 else
6832 {
6833 scopeName=className;
6834 }
6835 }
6836 else if (!className.isEmpty())
6837 {
6838 scopeName=className;
6839 }
6840 //printf("new scope='%s'\n",qPrint(scopeName));
6841
6842 QCString tempScopeName=scopeName;
6843 ClassDefMutable *cd=getClassMutable(scopeName);
6844 if (cd)
6845 {
6846 if (funcSpec.isEmpty())
6847 {
6848 uint32_t argListIndex=0;
6849 tempScopeName=cd->qualifiedNameWithTemplateParameters(&root->tArgLists,&argListIndex);
6850 }
6851 else
6852 {
6853 tempScopeName=scopeName+funcSpec;
6854 }
6855 }
6856 //printf("scopeName=%s cd=%p root->tArgLists=%p result=%s\n",
6857 // qPrint(scopeName),cd,root->tArgLists,qPrint(tempScopeName));
6858
6859 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
6860 // rebuild the function declaration (needed to get the scope right).
6861 if (!scopeName.isEmpty() && !isRelated && !isFriend && !Config_getBool(HIDE_SCOPE_NAMES))
6862 {
6863 if (!funcType.isEmpty())
6864 {
6865 if (isFunc) // a function -> we use argList for the arguments
6866 {
6867 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcTempList;
6868 }
6869 else
6870 {
6871 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcArgs;
6872 }
6873 }
6874 else
6875 {
6876 if (isFunc) // a function => we use argList for the arguments
6877 {
6878 funcDecl=tempScopeName+"::"+funcName+funcTempList;
6879 }
6880 else // variable => add 'argument' list
6881 {
6882 funcDecl=tempScopeName+"::"+funcName+funcArgs;
6883 }
6884 }
6885 }
6886 else // build declaration without scope
6887 {
6888 if (!funcType.isEmpty()) // but with a type
6889 {
6890 if (isFunc) // function => omit argument list
6891 {
6892 funcDecl=funcType+" "+funcName+funcTempList;
6893 }
6894 else // variable => add 'argument' list
6895 {
6896 funcDecl=funcType+" "+funcName+funcArgs;
6897 }
6898 }
6899 else // no type
6900 {
6901 if (isFunc)
6902 {
6903 funcDecl=funcName+funcTempList;
6904 }
6905 else
6906 {
6907 funcDecl=funcName+funcArgs;
6908 }
6909 }
6910 }
6911
6912 if (funcType=="template class" && !funcTempList.isEmpty())
6913 return; // ignore explicit template instantiations
6914
6915 AUTO_TRACE_ADD("Parse results: namespaceName='{}' className=`{}` funcType='{}' funcSpec='{}' "
6916 " funcName='{}' funcArgs='{}' funcTempList='{}' funcDecl='{}' relates='{}'"
6917 " exceptions='{}' isRelated={} isMemberOf={} isFriend={} isFunc={}",
6918 namespaceName, className, funcType, funcSpec,
6919 funcName, funcArgs, funcTempList, funcDecl, relates,
6920 exceptions, isRelated, isMemberOf, isFriend, isFunc);
6921
6922 if (!funcName.isEmpty()) // function name is valid
6923 {
6924 // check if 'className' is actually a scoped enum, in which case we need to
6925 // process it as a global, see issue #6471
6926 bool strongEnum = false;
6927 MemberName *mn=nullptr;
6928 if (!className.isEmpty() && (mn=Doxygen::functionNameLinkedMap->find(className)))
6929 {
6930 for (const auto &imd : *mn)
6931 {
6932 MemberDefMutable *md = toMemberDefMutable(imd.get());
6933 Definition *mdScope = nullptr;
6934 if (md && md->isEnumerate() && md->isStrong() && (mdScope=md->getOuterScope()) &&
6935 // need filter for the correct scope, see issue #9668
6936 ((namespaceName.isEmpty() && mdScope==Doxygen::globalScope) || (mdScope->name()==namespaceName)))
6937 {
6938 AUTO_TRACE_ADD("'{}' is a strong enum! (namespace={} md->getOuterScope()->name()={})",md->name(),namespaceName,md->getOuterScope()->name());
6939 strongEnum = true;
6940 // pass the scope name name as a 'namespace' to the findGlobalMember function
6941 if (!namespaceName.isEmpty())
6942 {
6943 namespaceName+="::"+className;
6944 }
6945 else
6946 {
6947 namespaceName=className;
6948 }
6949 }
6950 }
6951 }
6952
6953 if (funcName.startsWith("operator ")) // strip class scope from cast operator
6954 {
6955 funcName = substitute(funcName,className+"::","");
6956 }
6957 mn = nullptr;
6958 if (!funcTempList.isEmpty()) // try with member specialization
6959 {
6960 mn=Doxygen::memberNameLinkedMap->find(funcName+funcTempList);
6961 }
6962 if (mn==nullptr) // try without specialization
6963 {
6964 mn=Doxygen::memberNameLinkedMap->find(funcName);
6965 }
6966 if (!isRelated && !strongEnum && mn) // function name already found
6967 {
6968 AUTO_TRACE_ADD("member name exists ({} members with this name)",mn->size());
6969 if (!className.isEmpty()) // class name is valid
6970 {
6971 if (funcSpec.isEmpty()) // not a member specialization
6972 {
6973 addMemberFunction(root,mn,scopeName,namespaceName,className,funcType,funcName,
6974 funcArgs,funcTempList,exceptions,
6975 type,args,isFriend,spec,relates,funcDecl,overloaded,isFunc);
6976 }
6977 else if (cd) // member specialization
6978 {
6979 addMemberSpecialization(root,mn,cd,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
6980 }
6981 else
6982 {
6983 //printf("*** Specialized member %s of unknown scope %s%s found!\n",
6984 // qPrint(scopeName),qPrint(funcName),qPrint(funcArgs));
6985 }
6986 }
6987 else if (overloaded) // check if the function belongs to only one class
6988 {
6989 addOverloaded(root,mn,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
6990 }
6991 else // unrelated function with the same name as a member
6992 {
6993 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
6994 {
6995 QCString fullFuncDecl=funcDecl;
6996 if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
6997 warn(root->fileName,root->startLine,
6998 "Cannot determine class for function\n{}",
6999 fullFuncDecl
7000 );
7001 }
7002 }
7003 }
7004 else if (isRelated && !relates.isEmpty())
7005 {
7006 AUTO_TRACE_ADD("related function scopeName='{}' className='{}'",scopeName,className);
7007 if (className.isEmpty()) className=relates;
7008 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
7009 if ((cd=getClassMutable(scopeName)))
7010 {
7011 bool newMember=TRUE; // assume we have a new member
7012 MemberDefMutable *mdDefine=nullptr;
7013 {
7014 mn = Doxygen::functionNameLinkedMap->find(funcName);
7015 if (mn)
7016 {
7017 for (const auto &imd : *mn)
7018 {
7019 MemberDefMutable *md = toMemberDefMutable(imd.get());
7020 if (md && md->isDefine())
7021 {
7022 mdDefine = md;
7023 break;
7024 }
7025 }
7026 }
7027 }
7028
7029 if (mdDefine) // macro definition is already created by the preprocessor and inserted as a file member
7030 {
7031 //printf("moving #define %s into class %s\n",qPrint(mdDefine->name()),qPrint(cd->name()));
7032
7033 // take mdDefine from the Doxygen::functionNameLinkedMap (without deleting the data)
7034 auto mdDefineTaken = Doxygen::functionNameLinkedMap->take(funcName,mdDefine);
7035 // insert it as a class member
7036 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==nullptr)
7037 {
7038 mn=Doxygen::memberNameLinkedMap->add(funcName);
7039 }
7040
7041 if (mdDefine->getFileDef())
7042 {
7043 mdDefine->getFileDef()->removeMember(mdDefine);
7044 }
7045 mdDefine->makeRelated();
7046 mdDefine->setMemberClass(cd);
7047 mdDefine->moveTo(cd);
7048 cd->insertMember(mdDefine);
7049 // also insert the member as an alias in the parent's scope, so it can be referenced also without cd's scope
7050 insertMemberAlias(cd->getOuterScope(),mdDefine);
7051 mn->push_back(std::move(mdDefineTaken));
7052 }
7053 else // normal member, needs to be created and added to the class
7054 {
7055 FileDef *fd=root->fileDef();
7056
7057 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==nullptr)
7058 {
7059 mn=Doxygen::memberNameLinkedMap->add(funcName);
7060 }
7061 else
7062 {
7063 // see if we got another member with matching arguments
7064 MemberDefMutable *rmd_found = nullptr;
7065 for (const auto &irmd : *mn)
7066 {
7067 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
7068 if (rmd)
7069 {
7070 const ArgumentList &rmdAl = rmd->argumentList();
7071
7072 newMember=
7073 className!=rmd->getOuterScope()->name() ||
7074 !matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),&rmdAl,
7075 cd,fd,&root->argList,
7076 TRUE,root->lang);
7077 if (!newMember)
7078 {
7079 rmd_found = rmd;
7080 }
7081 }
7082 }
7083 if (rmd_found) // member already exists as rmd -> add docs
7084 {
7085 AUTO_TRACE_ADD("addMemberDocs for related member {}",root->name);
7086 addMemberDocs(root,rmd_found,funcDecl,nullptr,overloaded,spec);
7087 newMember=false;
7088 }
7089 }
7090
7091 if (newMember) // need to create a new member
7092 {
7094 switch (root->mtype)
7095 {
7096 case MethodTypes::Method: mtype = MemberType::Function; break;
7097 case MethodTypes::Signal: mtype = MemberType::Signal; break;
7098 case MethodTypes::Slot: mtype = MemberType::Slot; break;
7099 case MethodTypes::DCOP: mtype = MemberType::DCOP; break;
7100 case MethodTypes::Property: mtype = MemberType::Property; break;
7101 case MethodTypes::Event: mtype = MemberType::Event; break;
7102 }
7103
7104 //printf("New related name '%s' '%d'\n",qPrint(funcName),
7105 // root->argList ? (int)root->argList->count() : -1);
7106
7107 // first note that we pass:
7108 // (root->tArgLists ? root->tArgLists->last() : nullptr)
7109 // for the template arguments for the new "member."
7110 // this accurately reflects the template arguments of
7111 // the related function, which don't have to do with
7112 // those of the related class.
7113 auto md = createMemberDef(
7114 root->fileName,root->startLine,root->startColumn,
7115 funcType,funcName,funcArgs,exceptions,
7116 root->protection,root->virt,
7117 root->isStatic,
7119 mtype,
7120 (!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList()),
7121 funcArgs.isEmpty() ? ArgumentList() : root->argList,
7122 root->metaData);
7123 auto mmd = toMemberDefMutable(md.get());
7124
7125 // also insert the member as an alias in the parent's scope, so it can be referenced also without cd's scope
7126 insertMemberAlias(cd->getOuterScope(),md.get());
7127
7128 // we still have the problem that
7129 // MemberDef::writeDocumentation() in memberdef.cpp
7130 // writes the template argument list for the class,
7131 // as if this member is a member of the class.
7132 // fortunately, MemberDef::writeDocumentation() has
7133 // a special mechanism that allows us to totally
7134 // override the set of template argument lists that
7135 // are printed. We use that and set it to the
7136 // template argument lists of the related function.
7137 //
7138 mmd->setDefinitionTemplateParameterLists(root->tArgLists);
7139
7140 mmd->setTagInfo(root->tagInfo());
7141
7142 //printf("Related member name='%s' decl='%s' bodyLine='%d'\n",
7143 // qPrint(funcName),qPrint(funcDecl),root->bodyLine);
7144
7145 // try to find the matching line number of the body from the
7146 // global function list
7147 bool found=FALSE;
7148 if (root->bodyLine==-1)
7149 {
7150 MemberName *rmn=Doxygen::functionNameLinkedMap->find(funcName);
7151 if (rmn)
7152 {
7153 const MemberDefMutable *rmd_found=nullptr;
7154 for (const auto &irmd : *rmn)
7155 {
7156 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
7157 if (rmd)
7158 {
7159 const ArgumentList &rmdAl = rmd->argumentList();
7160 // check for matching argument lists
7161 if (
7162 matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),&rmdAl,
7163 cd,fd,&root->argList,
7164 TRUE,root->lang)
7165 )
7166 {
7167 found=TRUE;
7168 rmd_found = rmd;
7169 break;
7170 }
7171 }
7172 }
7173 if (rmd_found) // member found -> copy line number info
7174 {
7175 mmd->setBodySegment(rmd_found->getDefLine(),rmd_found->getStartBodyLine(),rmd_found->getEndBodyLine());
7176 mmd->setBodyDef(rmd_found->getBodyDef());
7177 //md->setBodyMember(rmd);
7178 }
7179 }
7180 }
7181 if (!found) // line number could not be found or is available in this
7182 // entry
7183 {
7184 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7185 mmd->setBodyDef(fd);
7186 }
7187
7188 //if (root->mGrpId!=-1)
7189 //{
7190 // md->setMemberGroup(memberGroupDict[root->mGrpId]);
7191 //}
7192 mmd->setMemberClass(cd);
7193 mmd->setMemberSpecifiers(spec);
7194 mmd->setVhdlSpecifiers(root->vhdlSpec);
7195 mmd->setDefinition(funcDecl);
7197 mmd->addQualifiers(root->qualifiers);
7198 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7199 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7200 mmd->setDocsForDefinition(!root->proto);
7201 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
7202 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7203 mmd->addSectionsToDefinition(root->anchors);
7204 mmd->setMemberGroupId(root->mGrpId);
7205 mmd->setLanguage(root->lang);
7206 mmd->setId(root->id);
7207 //md->setMemberDefTemplateArguments(root->mtArgList);
7208 cd->insertMember(md.get());
7209 cd->insertUsedFile(fd);
7210 mmd->setRefItems(root->sli);
7211 if (root->relatesType==RelatesType::Duplicate) mmd->setRelatedAlso(cd);
7212 addMemberToGroups(root,md.get());
7214 //printf("Adding member=%s\n",qPrint(md->name()));
7215 mn->push_back(std::move(md));
7216 }
7218 {
7219 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
7220 {
7221 QCString fullFuncDecl=funcDecl;
7222 if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
7223 warn(root->fileName,root->startLine,
7224 "Cannot determine file/namespace for relatedalso function\n{}",
7225 fullFuncDecl
7226 );
7227 }
7228 }
7229 }
7230 }
7231 else
7232 {
7233 warn_undoc(root->fileName,root->startLine, "class '{}' for related function '{}' is not documented.", className,funcName);
7234 }
7235 }
7236 else if (root->parent() && root->parent()->section.isObjcImpl())
7237 {
7238 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
7239 }
7240 else // unrelated not overloaded member found
7241 {
7242 bool globMem = findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec);
7243 if (className.isEmpty() && !globMem)
7244 {
7245 warn(root->fileName,root->startLine, "class for member '{}' cannot be found.", funcName);
7246 }
7247 else if (!className.isEmpty() && !globMem)
7248 {
7249 warn(root->fileName,root->startLine,
7250 "member '{}' of class '{}' cannot be found",
7251 funcName,className);
7252 }
7253 }
7254 }
7255 else
7256 {
7257 // this should not be called
7258 warn(root->fileName,root->startLine,"member with no name found.");
7259 }
7260 return;
7261}
7262
7263//----------------------------------------------------------------------
7264// find the members corresponding to the different documentation blocks
7265// that are extracted from the sources.
7266
7267static void filterMemberDocumentation(const Entry *root,const QCString &relates)
7268{
7269 AUTO_TRACE("root->type='{}' root->inside='{}' root->name='{}' root->args='{}' section={} root->spec={} root->mGrpId={}",
7270 root->type,root->inside,root->name,root->args,root->section,root->spec,root->mGrpId);
7271 //printf("root->parent()->name=%s\n",qPrint(root->parent()->name));
7272 bool isFunc=TRUE;
7273
7274 QCString type = root->type;
7275 QCString args = root->args;
7276 int i=-1, l=0;
7277 if ( // detect func variable/typedef to func ptr
7278 (i=findFunctionPtr(type.str(),root->lang,&l))!=-1
7279 )
7280 {
7281 //printf("Fixing function pointer!\n");
7282 // fix type and argument
7283 args.prepend(type.right(type.length()-i-l));
7284 type=type.left(i+l);
7285 //printf("Results type=%s,name=%s,args=%s\n",qPrint(type),qPrint(root->name),qPrint(args));
7286 isFunc=FALSE;
7287 }
7288 else if ((type.startsWith("typedef ") && args.find('(')!=-1))
7289 // detect function types marked as functions
7290 {
7291 isFunc=FALSE;
7292 }
7293
7294 //printf("Member %s isFunc=%d\n",qPrint(root->name),isFunc);
7295 if (root->section.isMemberDoc())
7296 {
7297 //printf("Documentation for inline member '%s' found args='%s'\n",
7298 // qPrint(root->name),qPrint(args));
7299 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
7300 if (type.isEmpty())
7301 {
7302 findMember(root,
7303 relates,
7304 type,
7305 args,
7306 root->name + args + root->exception,
7307 FALSE,
7308 isFunc);
7309 }
7310 else
7311 {
7312 findMember(root,
7313 relates,
7314 type,
7315 args,
7316 type + " " + root->name + args + root->exception,
7317 FALSE,
7318 isFunc);
7319 }
7320 }
7321 else if (root->section.isOverloadDoc())
7322 {
7323 //printf("Overloaded member %s found\n",qPrint(root->name));
7324 findMember(root,
7325 relates,
7326 type,
7327 args,
7328 root->name,
7329 TRUE,
7330 isFunc);
7331 }
7332 else if
7333 ((root->section.isFunction() // function
7334 ||
7335 (root->section.isVariable() && // variable
7336 !type.isEmpty() && // with a type
7337 g_compoundKeywords.find(type.str())==g_compoundKeywords.end() // that is not a keyword
7338 // (to skip forward declaration of class etc.)
7339 )
7340 )
7341 )
7342 {
7343 //printf("Documentation for member '%s' found args='%s' excp='%s'\n",
7344 // qPrint(root->name),qPrint(args),qPrint(root->exception));
7345 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
7346 //printf("Inside=%s\n Relates=%s\n",qPrint(root->inside),qPrint(relates));
7347 if (type=="friend class" || type=="friend struct" ||
7348 type=="friend union")
7349 {
7350 findMember(root,
7351 relates,
7352 type,
7353 args,
7354 type+" "+root->name,
7355 FALSE,FALSE);
7356
7357 }
7358 else if (!type.isEmpty())
7359 {
7360 findMember(root,
7361 relates,
7362 type,
7363 args,
7364 type+" "+ root->inside + root->name + args + root->exception,
7365 FALSE,isFunc);
7366 }
7367 else
7368 {
7369 findMember(root,
7370 relates,
7371 type,
7372 args,
7373 root->inside + root->name + args + root->exception,
7374 FALSE,isFunc);
7375 }
7376 }
7377 else if (root->section.isDefine() && !relates.isEmpty())
7378 {
7379 findMember(root,
7380 relates,
7381 type,
7382 args,
7383 root->name + args,
7384 FALSE,
7385 !args.isEmpty());
7386 }
7387 else if (root->section.isVariableDoc())
7388 {
7389 //printf("Documentation for variable %s found\n",qPrint(root->name));
7390 //if (!relates.isEmpty()) printf(" Relates %s\n",qPrint(relates));
7391 findMember(root,
7392 relates,
7393 type,
7394 args,
7395 root->name,
7396 FALSE,
7397 FALSE);
7398 }
7399 else if (root->section.isExportedInterface() ||
7400 root->section.isIncludedService())
7401 {
7402 findMember(root,
7403 relates,
7404 type,
7405 args,
7406 type + " " + root->name,
7407 FALSE,
7408 FALSE);
7409 }
7410 else
7411 {
7412 // skip section
7413 //printf("skip section\n");
7414 }
7415}
7416
7417static void findMemberDocumentation(const Entry *root)
7418{
7419 if (root->section.isMemberDoc() ||
7420 root->section.isOverloadDoc() ||
7421 root->section.isFunction() ||
7422 root->section.isVariable() ||
7423 root->section.isVariableDoc() ||
7424 root->section.isDefine() ||
7425 root->section.isIncludedService() ||
7426 root->section.isExportedInterface()
7427 )
7428 {
7429 AUTO_TRACE();
7430 if (root->relatesType==RelatesType::Duplicate && !root->relates.isEmpty())
7431 {
7433 }
7435 }
7436 for (const auto &e : root->children())
7437 {
7438 if (!e->section.isEnum())
7439 {
7440 findMemberDocumentation(e.get());
7441 }
7442 }
7443}
7444
7445//----------------------------------------------------------------------
7446
7447static void findObjCMethodDefinitions(const Entry *root)
7448{
7449 AUTO_TRACE();
7450 for (const auto &objCImpl : root->children())
7451 {
7452 if (objCImpl->section.isObjcImpl())
7453 {
7454 for (const auto &objCMethod : objCImpl->children())
7455 {
7456 if (objCMethod->section.isFunction())
7457 {
7458 //printf(" Found ObjC method definition %s\n",qPrint(objCMethod->name));
7459 findMember(objCMethod.get(),
7460 objCMethod->relates,
7461 objCMethod->type,
7462 objCMethod->args,
7463 objCMethod->type+" "+objCImpl->name+"::"+objCMethod->name+" "+objCMethod->args,
7464 FALSE,TRUE);
7465 objCMethod->section=EntryType::makeEmpty();
7466 }
7467 }
7468 }
7469 }
7470}
7471
7472//----------------------------------------------------------------------
7473// find and add the enumeration to their classes, namespaces or files
7474
7475static void findEnums(const Entry *root)
7476{
7477 if (root->section.isEnum())
7478 {
7479 AUTO_TRACE("name={}",root->name);
7480 ClassDefMutable *cd = nullptr;
7481 FileDef *fd = nullptr;
7482 NamespaceDefMutable *nd = nullptr;
7483 MemberNameLinkedMap *mnsd = nullptr;
7484 bool isGlobal = false;
7485 bool isRelated = false;
7486 bool isMemberOf = false;
7487 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7488
7489 QCString name;
7490 QCString scope;
7491
7492 int i = root->name.findRev("::");
7493 if (i!=-1) // scope is specified
7494 {
7495 scope=root->name.left(i); // extract scope
7496 if (root->lang==SrcLangExt::CSharp)
7497 {
7498 scope = mangleCSharpGenericName(scope);
7499 }
7500 name=root->name.right(root->name.length()-i-2); // extract name
7501 if ((cd=getClassMutable(scope))==nullptr)
7502 {
7504 }
7505 }
7506 else // no scope, check the scope in which the docs where found
7507 {
7508 if (root->parent()->section.isScope() && !root->parent()->name.isEmpty()) // found enum docs inside a compound
7509 {
7510 scope=root->parent()->name;
7511 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7512 }
7513 name=root->name;
7514 }
7515
7516 if (!root->relates.isEmpty())
7517 { // related member, prefix user specified scope
7518 isRelated=TRUE;
7519 isMemberOf=(root->relatesType==RelatesType::MemberOf);
7520 if (getClass(root->relates)==nullptr && !scope.isEmpty())
7521 scope=mergeScopes(scope,root->relates);
7522 else
7523 scope=root->relates;
7524 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7525 }
7526
7527 if (cd && !name.isEmpty()) // found a enum inside a compound
7528 {
7529 //printf("Enum '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7530 fd=nullptr;
7532 isGlobal=false;
7533 }
7534 else if (nd) // found enum inside namespace
7535 {
7537 isGlobal=true;
7538 }
7539 else // found a global enum
7540 {
7541 fd=root->fileDef();
7543 isGlobal=true;
7544 }
7545
7546 if (!name.isEmpty())
7547 {
7548 // new enum type
7549 AUTO_TRACE_ADD("new enum {} at line {} of {}",name,root->bodyLine,root->fileName);
7550 auto md = createMemberDef(
7551 root->fileName,root->startLine,root->startColumn,
7552 QCString(),name,QCString(),QCString(),
7557 auto mmd = toMemberDefMutable(md.get());
7558 mmd->setTagInfo(root->tagInfo());
7559 mmd->setLanguage(root->lang);
7560 mmd->setId(root->id);
7561 if (!isGlobal) mmd->setMemberClass(cd); else mmd->setFileDef(fd);
7562 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7563 mmd->setBodyDef(root->fileDef());
7564 mmd->setMemberSpecifiers(root->spec);
7565 mmd->setVhdlSpecifiers(root->vhdlSpec);
7566 mmd->setEnumBaseType(root->args);
7567 //printf("Enum %s definition at line %d of %s: protection=%d scope=%s\n",
7568 // qPrint(root->name),root->bodyLine,qPrint(root->fileName),root->protection,cd?qPrint(cd->name()):"<none>");
7569 mmd->addSectionsToDefinition(root->anchors);
7570 mmd->setMemberGroupId(root->mGrpId);
7572 mmd->addQualifiers(root->qualifiers);
7573 //printf("%s::setRefItems(%zu)\n",qPrint(md->name()),root->sli.size());
7574 mmd->setRefItems(root->sli);
7575 //printf("found enum %s nd=%p\n",qPrint(md->name()),nd);
7576 bool defSet=FALSE;
7577
7578 QCString baseType = root->args;
7579 if (!baseType.isEmpty())
7580 {
7581 baseType.prepend(" : ");
7582 }
7583
7584 if (nd)
7585 {
7586 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES))
7587 {
7588 mmd->setDefinition(name+baseType);
7589 }
7590 else
7591 {
7592 mmd->setDefinition(nd->name()+"::"+name+baseType);
7593 }
7594 //printf("definition=%s\n",md->definition());
7595 defSet=TRUE;
7596 mmd->setNamespace(nd);
7597 nd->insertMember(md.get());
7598 }
7599
7600 // even if we have already added the enum to a namespace, we still
7601 // also want to add it to other appropriate places such as file
7602 // or class.
7603 if (isGlobal && (nd==nullptr || !nd->isAnonymous()))
7604 {
7605 if (!defSet) mmd->setDefinition(name+baseType);
7606 if (fd==nullptr && root->parent())
7607 {
7608 fd=root->parent()->fileDef();
7609 }
7610 if (fd)
7611 {
7612 mmd->setFileDef(fd);
7613 fd->insertMember(md.get());
7614 }
7615 }
7616 else if (cd)
7617 {
7618 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES))
7619 {
7620 mmd->setDefinition(name+baseType);
7621 }
7622 else
7623 {
7624 mmd->setDefinition(cd->name()+"::"+name+baseType);
7625 }
7626 cd->insertMember(md.get());
7627 cd->insertUsedFile(fd);
7628 }
7629 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7630 mmd->setDocsForDefinition(!root->proto);
7631 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7632 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7633
7634 //printf("Adding member=%s\n",qPrint(md->name()));
7635 addMemberToGroups(root,md.get());
7637
7638 MemberName *mn = mnsd->add(name);
7639 mn->push_back(std::move(md));
7640 }
7641 }
7642 else
7643 {
7644 for (const auto &e : root->children()) findEnums(e.get());
7645 }
7646}
7647
7648//----------------------------------------------------------------------
7649
7650static void addEnumValuesToEnums(const Entry *root)
7651{
7652 if (root->section.isEnum())
7653 // non anonymous enumeration
7654 {
7655 AUTO_TRACE("name={}",root->name);
7656 ClassDefMutable *cd = nullptr;
7657 FileDef *fd = nullptr;
7658 NamespaceDefMutable *nd = nullptr;
7659 MemberNameLinkedMap *mnsd = nullptr;
7660 bool isGlobal = false;
7661 bool isRelated = false;
7662 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7663
7664 QCString name;
7665 QCString scope;
7666
7667 int i = root->name.findRev("::");
7668 if (i!=-1) // scope is specified
7669 {
7670 scope=root->name.left(i); // extract scope
7671 if (root->lang==SrcLangExt::CSharp)
7672 {
7673 scope = mangleCSharpGenericName(scope);
7674 }
7675 name=root->name.right(root->name.length()-i-2); // extract name
7676 if ((cd=getClassMutable(scope))==nullptr)
7677 {
7679 }
7680 }
7681 else // no scope, check the scope in which the docs where found
7682 {
7683 if (root->parent()->section.isScope() && !root->parent()->name.isEmpty()) // found enum docs inside a compound
7684 {
7685 scope=root->parent()->name;
7686 if (root->lang==SrcLangExt::CSharp)
7687 {
7688 scope = mangleCSharpGenericName(scope);
7689 }
7690 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7691 }
7692 name=root->name;
7693 }
7694
7695 if (!root->relates.isEmpty())
7696 { // related member, prefix user specified scope
7697 isRelated=TRUE;
7698 if (getClassMutable(root->relates)==nullptr && !scope.isEmpty())
7699 scope=mergeScopes(scope,root->relates);
7700 else
7701 scope=root->relates;
7702 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7703 }
7704
7705 if (cd && !name.isEmpty()) // found a enum inside a compound
7706 {
7707 //printf("Enum in class '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7708 fd=nullptr;
7710 isGlobal=false;
7711 }
7712 else if (nd && !nd->isAnonymous()) // found enum inside namespace
7713 {
7714 //printf("Enum in namespace '%s'::'%s'\n",qPrint(nd->name()),qPrint(name));
7716 isGlobal=true;
7717 }
7718 else // found a global enum
7719 {
7720 fd=root->fileDef();
7721 //printf("Enum in file '%s': '%s'\n",qPrint(fd->name()),qPrint(name));
7723 isGlobal=true;
7724 }
7725
7726 if (!name.isEmpty())
7727 {
7728 //printf("** name=%s\n",qPrint(name));
7729 MemberName *mn = mnsd->find(name); // for all members with this name
7730 if (mn)
7731 {
7732 struct EnumValueInfo
7733 {
7734 EnumValueInfo(const QCString &n,std::unique_ptr<MemberDef> &&md) :
7735 name(n), member(std::move(md)) {}
7736 QCString name;
7737 std::unique_ptr<MemberDef> member;
7738 };
7739 std::vector< EnumValueInfo > extraMembers;
7740 // for each enum in this list
7741 for (const auto &imd : *mn)
7742 {
7743 MemberDefMutable *md = toMemberDefMutable(imd.get());
7744 // use raw pointer in this loop, since we modify mn and can then invalidate mdp.
7745 if (md && md->isEnumerate() && !root->children().empty())
7746 {
7747 AUTO_TRACE_ADD("enum {} with {} children",md->name(),root->children().size());
7748 for (const auto &e : root->children())
7749 {
7750 SrcLangExt sle = root->lang;
7751 bool isJavaLike = sle==SrcLangExt::CSharp || sle==SrcLangExt::Java || sle==SrcLangExt::XML;
7752 if ( isJavaLike || root->spec.isStrong())
7753 {
7754 // Unlike classic C/C++ enums, for C++11, C# & Java enum
7755 // values are only visible inside the enum scope, so we must create
7756 // them here and only add them to the enum
7757 //printf("md->qualifiedName()=%s e->name=%s tagInfo=%p name=%s\n",
7758 // qPrint(md->qualifiedName()),qPrint(e->name),(void*)e->tagInfo(),qPrint(e->name));
7759 QCString qualifiedName = root->name;
7760 i = qualifiedName.findRev("::");
7761 if (i!=-1 && sle==SrcLangExt::CSharp)
7762 {
7763 qualifiedName = mangleCSharpGenericName(qualifiedName.left(i))+qualifiedName.mid(i);
7764 }
7765 if (isJavaLike)
7766 {
7767 qualifiedName=substitute(qualifiedName,"::",".");
7768 }
7769 if (md->qualifiedName()==qualifiedName) // enum value scope matches that of the enum
7770 {
7771 QCString fileName = e->fileName;
7772 if (fileName.isEmpty() && e->tagInfo())
7773 {
7774 fileName = e->tagInfo()->tagName;
7775 }
7776 AUTO_TRACE_ADD("strong enum value {}",e->name);
7777 auto fmd = createMemberDef(
7778 fileName,e->startLine,e->startColumn,
7779 e->type,e->name,e->args,QCString(),
7780 e->protection, Specifier::Normal,e->isStatic,Relationship::Member,
7782 auto fmmd = toMemberDefMutable(fmd.get());
7783 NamespaceDef *mnd = md->getNamespaceDef();
7784 if (md->getClassDef())
7785 fmmd->setMemberClass(md->getClassDef());
7786 else if (mnd && (mnd->isLinkable() || mnd->isAnonymous()))
7787 fmmd->setNamespace(mnd);
7788 else if (md->getFileDef())
7789 fmmd->setFileDef(md->getFileDef());
7790 fmmd->setOuterScope(md->getOuterScope());
7791 fmmd->setTagInfo(e->tagInfo());
7792 fmmd->setLanguage(e->lang);
7793 fmmd->setBodySegment(e->startLine,e->bodyLine,e->endBodyLine);
7794 fmmd->setBodyDef(e->fileDef());
7795 fmmd->setId(e->id);
7796 fmmd->setDocumentation(e->doc,e->docFile,e->docLine);
7797 fmmd->setBriefDescription(e->brief,e->briefFile,e->briefLine);
7798 fmmd->addSectionsToDefinition(e->anchors);
7799 std::string init = e->initializer.str();
7800 fmmd->setInitializer(init.c_str());
7801 fmmd->setMaxInitLines(e->initLines);
7802 fmmd->setMemberGroupId(e->mGrpId);
7803 fmmd->setExplicitExternal(e->explicitExternal,fileName,e->startLine,e->startColumn);
7804 fmmd->setRefItems(e->sli);
7805 fmmd->setAnchor();
7806 md->insertEnumField(fmd.get());
7807 fmmd->setEnumScope(md,TRUE);
7808 extraMembers.emplace_back(e->name,std::move(fmd));
7809 }
7810 }
7811 else
7812 {
7813 AUTO_TRACE_ADD("enum value {}",e->name);
7814 //printf("e->name=%s isRelated=%d\n",qPrint(e->name),isRelated);
7815 MemberName *fmn=nullptr;
7816 MemberNameLinkedMap *emnsd = isRelated ? Doxygen::functionNameLinkedMap : mnsd;
7817 if (!e->name.isEmpty() && (fmn=emnsd->find(e->name)))
7818 // get list of members with the same name as the field
7819 {
7820 for (const auto &ifmd : *fmn)
7821 {
7822 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
7823 if (fmd && fmd->isEnumValue() && fmd->getOuterScope()==md->getOuterScope()) // in same scope
7824 {
7825 //printf("found enum value with same name %s in scope %s\n",
7826 // qPrint(fmd->name()),qPrint(fmd->getOuterScope()->name()));
7827 if (nd && !nd->isAnonymous())
7828 {
7829 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
7830 {
7831 const NamespaceDef *fnd=fmd->getNamespaceDef();
7832 if (fnd==nd) // enum value is inside a namespace
7833 {
7834 md->insertEnumField(fmd);
7835 fmd->setEnumScope(md);
7836 }
7837 }
7838 }
7839 else if (isGlobal)
7840 {
7841 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
7842 {
7843 const FileDef *ffd=fmd->getFileDef();
7844 if (ffd==fd && ffd==md->getFileDef()) // enum value has file scope
7845 {
7846 md->insertEnumField(fmd);
7847 fmd->setEnumScope(md);
7848 }
7849 }
7850 }
7851 else if (isRelated && cd) // reparent enum value to
7852 // match the enum's scope
7853 {
7854 md->insertEnumField(fmd); // add field def to list
7855 fmd->setEnumScope(md); // cross ref with enum name
7856 fmd->setEnumClassScope(cd); // cross ref with enum name
7857 fmd->setOuterScope(cd);
7858 fmd->makeRelated();
7859 cd->insertMember(fmd);
7860 }
7861 else
7862 {
7863 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
7864 {
7865 const ClassDef *fcd=fmd->getClassDef();
7866 if (fcd==cd) // enum value is inside a class
7867 {
7868 //printf("Inserting enum field %s in enum scope %s\n",
7869 // qPrint(fmd->name()),qPrint(md->name()));
7870 md->insertEnumField(fmd); // add field def to list
7871 fmd->setEnumScope(md); // cross ref with enum name
7872 }
7873 }
7874 }
7875 }
7876 }
7877 }
7878 }
7879 }
7880 }
7881 }
7882 // move the newly added members into mn
7883 for (auto &e : extraMembers)
7884 {
7885 MemberName *emn=mnsd->add(e.name);
7886 emn->push_back(std::move(e.member));
7887 }
7888 }
7889 }
7890 }
7891 else
7892 {
7893 for (const auto &e : root->children()) addEnumValuesToEnums(e.get());
7894 }
7895}
7896
7897//----------------------------------------------------------------------
7898
7899static void addEnumDocs(const Entry *root,MemberDefMutable *md)
7900{
7901 AUTO_TRACE();
7902 // documentation outside a compound overrides the documentation inside it
7903 {
7904 md->setDocumentation(root->doc,root->docFile,root->docLine);
7905 md->setDocsForDefinition(!root->proto);
7906 }
7907
7908 // brief descriptions inside a compound override the documentation
7909 // outside it
7910 {
7911 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7912 }
7913
7914 if (md->inbodyDocumentation().isEmpty() || !root->parent()->name.isEmpty())
7915 {
7917 }
7918
7919 if (root->mGrpId!=-1 && md->getMemberGroupId()==-1)
7920 {
7921 md->setMemberGroupId(root->mGrpId);
7922 }
7923
7925 md->setRefItems(root->sli);
7926
7927 const GroupDef *gd=md->getGroupDef();
7928 if (gd==nullptr && !root->groups.empty()) // member not grouped but out-of-line documentation is
7929 {
7930 addMemberToGroups(root,md);
7931 }
7933}
7934
7935//----------------------------------------------------------------------
7936// Search for the name in the associated groups. If a matching member
7937// definition exists, then add the documentation to it and return TRUE,
7938// otherwise FALSE.
7939
7940static bool tryAddEnumDocsToGroupMember(const Entry *root,const QCString &name)
7941{
7942 for (const auto &g : root->groups)
7943 {
7944 const GroupDef *gd = Doxygen::groupLinkedMap->find(g.groupname);
7945 if (gd)
7946 {
7947 MemberList *ml = gd->getMemberList(MemberListType::DecEnumMembers());
7948 if (ml)
7949 {
7950 MemberDefMutable *md = toMemberDefMutable(ml->find(name));
7951 if (md)
7952 {
7953 addEnumDocs(root,md);
7954 return TRUE;
7955 }
7956 }
7957 }
7958 else if (!gd && g.pri == Grouping::GROUPING_INGROUP)
7959 {
7960 warn(root->fileName, root->startLine,
7961 "Found non-existing group '{}' for the command '{}', ignoring command",
7962 g.groupname, Grouping::getGroupPriName( g.pri )
7963 );
7964 }
7965 }
7966
7967 return FALSE;
7968}
7969
7970//----------------------------------------------------------------------
7971// find the documentation blocks for the enumerations
7972
7973static void findEnumDocumentation(const Entry *root)
7974{
7975 if (root->section.isEnumDoc() &&
7976 !root->name.isEmpty() &&
7977 root->name.at(0)!='@' // skip anonymous enums
7978 )
7979 {
7980 QCString name;
7981 QCString scope;
7982 int i = root->name.findRev("::");
7983 if (i!=-1) // scope is specified as part of the name
7984 {
7985 name=root->name.right(root->name.length()-i-2); // extract name
7986 scope=root->name.left(i); // extract scope
7987 //printf("Scope='%s' Name='%s'\n",qPrint(scope),qPrint(name));
7988 }
7989 else // just the name
7990 {
7991 name=root->name;
7992 }
7993 if (root->parent()->section.isScope() && !root->parent()->name.isEmpty()) // found enum docs inside a compound
7994 {
7995 if (!scope.isEmpty()) scope.prepend("::");
7996 scope.prepend(root->parent()->name);
7997 }
7998 const ClassDef *cd = getClass(scope);
7999 const NamespaceDef *nd=Doxygen::namespaceLinkedMap->find(scope);
8000 const FileDef *fd = root->fileDef();
8001 AUTO_TRACE("Found docs for enum with name '{}' and scope '{}' in context '{}' cd='{}', nd='{}' fd='{}'",
8002 name,scope,root->parent()->name,
8003 cd ? cd->name() : QCString("<none>"),
8004 nd ? nd->name() : QCString("<none>"),
8005 fd ? fd->name() : QCString("<none>"));
8006
8007 if (!name.isEmpty())
8008 {
8009 bool found = tryAddEnumDocsToGroupMember(root, name);
8010 if (!found)
8011 {
8012 MemberName *mn = cd ? Doxygen::memberNameLinkedMap->find(name) : Doxygen::functionNameLinkedMap->find(name);
8013 if (mn)
8014 {
8015 for (const auto &imd : *mn)
8016 {
8017 MemberDefMutable *md = toMemberDefMutable(imd.get());
8018 if (md && md->isEnumerate())
8019 {
8020 const ClassDef *mcd = md->getClassDef();
8021 const NamespaceDef *mnd = md->getNamespaceDef();
8022 const FileDef *mfd = md->getFileDef();
8023 if (cd && mcd==cd)
8024 {
8025 AUTO_TRACE_ADD("Match found for class scope");
8026 addEnumDocs(root,md);
8027 found = TRUE;
8028 break;
8029 }
8030 else if (cd==nullptr && mcd==nullptr && nd!=nullptr && mnd==nd)
8031 {
8032 AUTO_TRACE_ADD("Match found for namespace scope");
8033 addEnumDocs(root,md);
8034 found = TRUE;
8035 break;
8036 }
8037 else if (cd==nullptr && nd==nullptr && mcd==nullptr && mnd==nullptr && fd==mfd)
8038 {
8039 AUTO_TRACE_ADD("Match found for global scope");
8040 addEnumDocs(root,md);
8041 found = TRUE;
8042 break;
8043 }
8044 }
8045 }
8046 }
8047 }
8048 if (!found)
8049 {
8050 warn(root->fileName,root->startLine, "Documentation for undefined enum '{}' found.", name);
8051 }
8052 }
8053 }
8054 for (const auto &e : root->children()) findEnumDocumentation(e.get());
8055}
8056
8057// search for each enum (member or function) in mnl if it has documented
8058// enum values.
8059static void findDEV(const MemberNameLinkedMap &mnsd)
8060{
8061 // for each member name
8062 for (const auto &mn : mnsd)
8063 {
8064 // for each member definition
8065 for (const auto &imd : *mn)
8066 {
8067 MemberDefMutable *md = toMemberDefMutable(imd.get());
8068 if (md && md->isEnumerate()) // member is an enum
8069 {
8070 int documentedEnumValues=0;
8071 // for each enum value
8072 for (const auto &fmd : md->enumFieldList())
8073 {
8074 if (fmd->isLinkableInProject()) documentedEnumValues++;
8075 }
8076 // at least one enum value is documented
8077 if (documentedEnumValues>0) md->setDocumentedEnumValues(TRUE);
8078 }
8079 }
8080 }
8081}
8082
8083// search for each enum (member or function) if it has documented enum
8084// values.
8090
8091//----------------------------------------------------------------------
8092
8094{
8095 auto &index = Index::instance();
8096 // for each class member name
8097 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8098 {
8099 // for each member definition
8100 for (const auto &md : *mn)
8101 {
8102 index.addClassMemberNameToIndex(md.get());
8103 if (md->getModuleDef())
8104 {
8105 index.addModuleMemberNameToIndex(md.get());
8106 }
8107 }
8108 }
8109 // for each file/namespace function name
8110 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8111 {
8112 // for each member definition
8113 for (const auto &md : *mn)
8114 {
8115 if (md->getNamespaceDef())
8116 {
8117 index.addNamespaceMemberNameToIndex(md.get());
8118 }
8119 else
8120 {
8121 index.addFileMemberNameToIndex(md.get());
8122 }
8123 if (md->getModuleDef())
8124 {
8125 index.addModuleMemberNameToIndex(md.get());
8126 }
8127 }
8128 }
8129
8130 index.sortMemberIndexLists();
8131}
8132
8133//----------------------------------------------------------------------
8134
8135static void addToIndices()
8136{
8137 for (const auto &cd : *Doxygen::classLinkedMap)
8138 {
8139 if (cd->isLinkableInProject())
8140 {
8141 Doxygen::indexList->addIndexItem(cd.get(),nullptr);
8142 if (Doxygen::searchIndex.enabled())
8143 {
8144 Doxygen::searchIndex.setCurrentDoc(cd.get(),cd->anchor(),FALSE);
8145 Doxygen::searchIndex.addWord(cd->localName(),TRUE);
8146 }
8147 }
8148 }
8149
8150 for (const auto &cd : *Doxygen::conceptLinkedMap)
8151 {
8152 if (cd->isLinkableInProject())
8153 {
8154 Doxygen::indexList->addIndexItem(cd.get(),nullptr);
8155 if (Doxygen::searchIndex.enabled())
8156 {
8157 Doxygen::searchIndex.setCurrentDoc(cd.get(),cd->anchor(),FALSE);
8158 Doxygen::searchIndex.addWord(cd->localName(),TRUE);
8159 }
8160 }
8161 }
8162
8163 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8164 {
8165 if (nd->isLinkableInProject())
8166 {
8167 Doxygen::indexList->addIndexItem(nd.get(),nullptr);
8168 if (Doxygen::searchIndex.enabled())
8169 {
8170 Doxygen::searchIndex.setCurrentDoc(nd.get(),nd->anchor(),FALSE);
8171 Doxygen::searchIndex.addWord(nd->localName(),TRUE);
8172 }
8173 }
8174 }
8175
8176 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8177 {
8178 for (const auto &fd : *fn)
8179 {
8180 if (Doxygen::searchIndex.enabled() && fd->isLinkableInProject())
8181 {
8182 Doxygen::searchIndex.setCurrentDoc(fd.get(),fd->anchor(),FALSE);
8183 Doxygen::searchIndex.addWord(fd->localName(),TRUE);
8184 }
8185 }
8186 }
8187
8188 auto addWordsForTitle = [](const Definition *d,const QCString &anchor,const QCString &title)
8189 {
8190 Doxygen::indexList->addIndexItem(d,nullptr,QCString(),filterTitle(title));
8191 if (Doxygen::searchIndex.enabled())
8192 {
8193 Doxygen::searchIndex.setCurrentDoc(d,anchor,false);
8194 std::string s = title.str();
8195 static const reg::Ex re(R"(\a[\w-]*)");
8196 reg::Iterator it(s,re);
8198 for (; it!=end ; ++it)
8199 {
8200 const auto &match = *it;
8201 std::string matchStr = match.str();
8202 Doxygen::searchIndex.addWord(matchStr.c_str(),true);
8203 }
8204 }
8205 };
8206
8207 for (const auto &gd : *Doxygen::groupLinkedMap)
8208 {
8209 if (gd->isLinkableInProject())
8210 {
8211 addWordsForTitle(gd.get(),gd->anchor(),gd->groupTitle());
8212 }
8213 }
8214
8215 for (const auto &pd : *Doxygen::pageLinkedMap)
8216 {
8217 if (pd->isLinkableInProject())
8218 {
8219 addWordsForTitle(pd.get(),pd->anchor(),pd->title());
8220 }
8221 }
8222
8224 {
8225 addWordsForTitle(Doxygen::mainPage.get(),Doxygen::mainPage->anchor(),Doxygen::mainPage->title());
8226 }
8227
8228 auto addMemberToSearchIndex = [](const MemberDef *md)
8229 {
8230 if (Doxygen::searchIndex.enabled())
8231 {
8232 Doxygen::searchIndex.setCurrentDoc(md,md->anchor(),FALSE);
8233 QCString ln=md->localName();
8234 QCString qn=md->qualifiedName();
8235 Doxygen::searchIndex.addWord(ln,TRUE);
8236 if (ln!=qn)
8237 {
8238 Doxygen::searchIndex.addWord(qn,TRUE);
8239 if (md->getClassDef())
8240 {
8241 Doxygen::searchIndex.addWord(md->getClassDef()->displayName(),TRUE);
8242 }
8243 if (md->getNamespaceDef())
8244 {
8245 Doxygen::searchIndex.addWord(md->getNamespaceDef()->displayName(),TRUE);
8246 }
8247 }
8248 }
8249 };
8250
8251 auto getScope = [](const MemberDef *md)
8252 {
8253 const Definition *scope = nullptr;
8254 if (md->getGroupDef()) scope = md->getGroupDef();
8255 else if (md->getClassDef()) scope = md->getClassDef();
8256 else if (md->getNamespaceDef()) scope = md->getNamespaceDef();
8257 else if (md->getFileDef()) scope = md->getFileDef();
8258 return scope;
8259 };
8260
8261 auto addMemberToIndices = [addMemberToSearchIndex,getScope](const MemberDef *md)
8262 {
8263 if (md->isLinkableInProject())
8264 {
8265 if (!(md->isEnumerate() && md->isAnonymous()))
8266 {
8267 Doxygen::indexList->addIndexItem(getScope(md),md);
8269 }
8270 if (md->isEnumerate())
8271 {
8272 for (const auto &fmd : md->enumFieldList())
8273 {
8274 Doxygen::indexList->addIndexItem(getScope(fmd),fmd);
8276 }
8277 }
8278 }
8279 };
8280
8281 // for each class member name
8282 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8283 {
8284 // for each member definition
8285 for (const auto &md : *mn)
8286 {
8287 addMemberToIndices(md.get());
8288 }
8289 }
8290 // for each file/namespace function name
8291 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8292 {
8293 // for each member definition
8294 for (const auto &md : *mn)
8295 {
8296 addMemberToIndices(md.get());
8297 }
8298 }
8299}
8300
8301//----------------------------------------------------------------------
8302
8304{
8305 // for each member name
8306 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8307 {
8308 // for each member definition
8309 for (const auto &imd : *mn)
8310 {
8311 MemberDefMutable *md = toMemberDefMutable(imd.get());
8312 if (md)
8313 {
8315 }
8316 }
8317 }
8318 // for each member name
8319 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8320 {
8321 // for each member definition
8322 for (const auto &imd : *mn)
8323 {
8324 MemberDefMutable *md = toMemberDefMutable(imd.get());
8325 if (md)
8326 {
8328 }
8329 }
8330 }
8331}
8332
8333// recursive helper function looking for reimplements/implemented
8334// by relations between class cd and direct or indirect base class bcd
8336{
8337 for (const auto &mn : cd->memberNameInfoLinkedMap()) // for each member in class cd with a unique name
8338 {
8339 for (const auto &imd : *mn) // for each member with a given name
8340 {
8341 MemberDefMutable *md = toMemberDefMutable(imd->memberDef());
8342 if (md && (md->isFunction() || md->isCSharpProperty())) // filter on reimplementable members
8343 {
8344 ClassDef *mbcd = bcd->classDef;
8345 if (mbcd && mbcd->isLinkable()) // filter on linkable classes
8346 {
8347 const auto &bmn = mbcd->memberNameInfoLinkedMap();
8348 const auto &bmni = bmn.find(mn->memberName());
8349 if (bmni) // there are base class members with the same name
8350 {
8351 for (const auto &ibmd : *bmni) // for base class member with that name
8352 {
8353 MemberDefMutable *bmd = toMemberDefMutable(ibmd->memberDef());
8354 if (bmd) // not part of an inline namespace
8355 {
8356 auto lang = bmd->getLanguage();
8357 auto compType = mbcd->compoundType();
8358 if (bmd->virtualness()!=Specifier::Normal ||
8359 lang==SrcLangExt::Python ||
8360 lang==SrcLangExt::Java ||
8361 lang==SrcLangExt::PHP ||
8362 compType==ClassDef::Interface ||
8363 compType==ClassDef::Protocol)
8364 {
8365 const ArgumentList &bmdAl = bmd->argumentList();
8366 const ArgumentList &mdAl = md->argumentList();
8367 //printf(" Base argList='%s'\n Super argList='%s'\n",
8368 // qPrint(argListToString(bmdAl)),
8369 // qPrint(argListToString(mdAl))
8370 // );
8371 if (
8372 lang==SrcLangExt::Python ||
8373 matchArguments2(bmd->getOuterScope(),bmd->getFileDef(),&bmdAl,
8374 md->getOuterScope(), md->getFileDef(), &mdAl,
8375 TRUE,lang
8376 )
8377 )
8378 {
8379 if (lang==SrcLangExt::Python && md->name().startsWith("__")) continue; // private members do not reimplement
8380 //printf("match!\n");
8381 const MemberDef *rmd = md->reimplements();
8382 if (rmd==nullptr) // not already assigned
8383 {
8384 //printf("%s: setting (new) reimplements member %s\n",qPrint(md->qualifiedName()),qPrint(bmd->qualifiedName()));
8385 md->setReimplements(bmd);
8386 }
8387 //printf("%s: add reimplementedBy member %s\n",qPrint(bmd->qualifiedName()),qPrint(md->qualifiedName()));
8388 bmd->insertReimplementedBy(md);
8389 }
8390 else
8391 {
8392 //printf("no match!\n");
8393 }
8394 }
8395 }
8396 }
8397 }
8398 }
8399 }
8400 }
8401 }
8402
8403 // do also for indirect base classes
8404 for (const auto &bbcd : bcd->classDef->baseClasses())
8405 {
8407 }
8408}
8409
8410//----------------------------------------------------------------------
8411// computes the relation between all members. For each member 'm'
8412// the members that override the implementation of 'm' are searched and
8413// the member that 'm' overrides is searched.
8414
8416{
8417 for (const auto &cd : *Doxygen::classLinkedMap)
8418 {
8419 if (cd->isLinkable())
8420 {
8421 for (const auto &bcd : cd->baseClasses())
8422 {
8424 }
8425 }
8426 }
8427}
8428
8429//----------------------------------------------------------------------------
8430
8432{
8433 // for each class
8434 for (const auto &cd : *Doxygen::classLinkedMap)
8435 {
8436 // that is a template
8437 for (const auto &ti : cd->getTemplateInstances())
8438 {
8439 ClassDefMutable *tcdm = toClassDefMutable(ti.classDef);
8440 if (tcdm)
8441 {
8442 tcdm->addMembersToTemplateInstance(cd.get(),cd->templateArguments(),ti.templSpec);
8443 }
8444 }
8445 }
8446}
8447
8448//----------------------------------------------------------------------------
8449
8450static void mergeCategories()
8451{
8452 AUTO_TRACE();
8453 // merge members of categories into the class they extend
8454 for (const auto &cd : *Doxygen::classLinkedMap)
8455 {
8456 int i=cd->name().find('(');
8457 if (i!=-1) // it is an Objective-C category
8458 {
8459 QCString baseName=cd->name().left(i);
8460 ClassDefMutable *baseClass=toClassDefMutable(Doxygen::classLinkedMap->find(baseName));
8461 if (baseClass)
8462 {
8463 AUTO_TRACE_ADD("merging members of category {} into {}",cd->name(),baseClass->name());
8464 baseClass->mergeCategory(cd.get());
8465 }
8466 }
8467 }
8468}
8469
8470// builds the list of all members for each class
8471
8473{
8474 // merge the member list of base classes into the inherited classes.
8475 for (const auto &cd : *Doxygen::classLinkedMap)
8476 {
8477 if (// !cd->isReference() && // not an external class
8478 cd->subClasses().empty() && // is a root of the hierarchy
8479 !cd->baseClasses().empty()) // and has at least one base class
8480 {
8481 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8482 if (cdm)
8483 {
8484 //printf("*** merging members for %s\n",qPrint(cd->name()));
8485 cdm->mergeMembers();
8486 }
8487 }
8488 }
8489 // now sort the member list of all members for all classes.
8490 for (const auto &cd : *Doxygen::classLinkedMap)
8491 {
8492 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8493 if (cdm)
8494 {
8495 cdm->sortAllMembersList();
8496 }
8497 }
8498}
8499
8500//----------------------------------------------------------------------------
8501
8503{
8504 auto processSourceFile = [](FileDef *fd,OutputList &ol,ClangTUParser *parser)
8505 {
8506 bool showSources = fd->generateSourceFile() && !Htags::useHtags; // sources need to be shown in the output
8507 bool parseSources = !fd->isReference() && Doxygen::parseSourcesNeeded; // we needed to parse the sources even if we do not show them
8508 if (showSources)
8509 {
8510 msg("Generating code for file {}...\n",fd->docName());
8511 fd->writeSourceHeader(ol);
8512 fd->writeSourceBody(ol,parser);
8513 fd->writeSourceFooter(ol);
8514 }
8515 else if (parseSources)
8516 {
8517 msg("Parsing code for file {}...\n",fd->docName());
8518 fd->parseSource(parser);
8519 }
8520 };
8521 if (!Doxygen::inputNameLinkedMap->empty())
8522 {
8523#if USE_LIBCLANG
8525 {
8526 StringUnorderedSet processedFiles;
8527
8528 // create a dictionary with files to process
8529 StringUnorderedSet filesToProcess;
8530
8531 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8532 {
8533 for (const auto &fd : *fn)
8534 {
8535 filesToProcess.insert(fd->absFilePath().str());
8536 }
8537 }
8538 // process source files (and their include dependencies)
8539 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8540 {
8541 for (const auto &fd : *fn)
8542 {
8543 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt::Cpp &&
8544 (fd->generateSourceFile() ||
8546 )
8547 )
8548 {
8549 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8550 clangParser->parse();
8551 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8552
8553 for (auto incFile : clangParser->filesInSameTU())
8554 {
8555 if (filesToProcess.find(incFile)!=filesToProcess.end() && // part of input
8556 fd->absFilePath()!=QCString(incFile) && // not same file
8557 processedFiles.find(incFile)==processedFiles.end()) // not yet marked as processed
8558 {
8559 StringVector moreFiles;
8560 bool ambig = false;
8561 FileDef *ifd=findFileDef(Doxygen::inputNameLinkedMap,incFile.c_str(),ambig);
8562 if (ifd && !ifd->isReference())
8563 {
8564 processSourceFile(ifd,*g_outputList,clangParser.get());
8565 processedFiles.insert(incFile);
8566 }
8567 }
8568 }
8569 processedFiles.insert(fd->absFilePath().str());
8570 }
8571 }
8572 }
8573 // process remaining files
8574 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8575 {
8576 for (const auto &fd : *fn)
8577 {
8578 if (processedFiles.find(fd->absFilePath().str())==processedFiles.end()) // not yet processed
8579 {
8580 if (fd->getLanguage()==SrcLangExt::Cpp) // C/C++ file, use clang parser
8581 {
8582 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8583 clangParser->parse();
8584 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8585 }
8586 else // non C/C++ file, use built-in parser
8587 {
8588 processSourceFile(fd.get(),*g_outputList,nullptr);
8589 }
8590 }
8591 }
8592 }
8593 }
8594 else
8595#endif
8596 {
8597 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8598 if (numThreads>1)
8599 {
8600 msg("Generating code files using {} threads.\n",numThreads);
8601 struct SourceContext
8602 {
8603 SourceContext(FileDef *fd_,bool gen_,const OutputList &ol_)
8604 : fd(fd_), generateSourceFile(gen_), ol(ol_) {}
8605 FileDef *fd;
8606 bool generateSourceFile;
8607 OutputList ol;
8608 };
8609 ThreadPool threadPool(numThreads);
8610 std::vector< std::future< std::shared_ptr<SourceContext> > > results;
8611 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8612 {
8613 for (const auto &fd : *fn)
8614 {
8615 bool generateSourceFile = fd->generateSourceFile() && !Htags::useHtags;
8616 auto ctx = std::make_shared<SourceContext>(fd.get(),generateSourceFile,*g_outputList);
8617 auto processFile = [ctx]()
8618 {
8619 if (ctx->generateSourceFile)
8620 {
8621 msg("Generating code for file {}...\n",ctx->fd->docName());
8622 }
8623 else
8624 {
8625 msg("Parsing code for file {}...\n",ctx->fd->docName());
8626 }
8627 StringVector filesInSameTu;
8628 ctx->fd->getAllIncludeFilesRecursively(filesInSameTu);
8629 if (ctx->generateSourceFile) // sources need to be shown in the output
8630 {
8631 ctx->fd->writeSourceHeader(ctx->ol);
8632 ctx->fd->writeSourceBody(ctx->ol,nullptr);
8633 ctx->fd->writeSourceFooter(ctx->ol);
8634 }
8635 else if (!ctx->fd->isReference() && Doxygen::parseSourcesNeeded)
8636 // we needed to parse the sources even if we do not show them
8637 {
8638 ctx->fd->parseSource(nullptr);
8639 }
8640 return ctx;
8641 };
8642 results.emplace_back(threadPool.queue(processFile));
8643 }
8644 }
8645 for (auto &f : results)
8646 {
8647 auto ctx = f.get();
8648 }
8649 }
8650 else // single threaded version
8651 {
8652 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8653 {
8654 for (const auto &fd : *fn)
8655 {
8656 StringVector filesInSameTu;
8657 fd->getAllIncludeFilesRecursively(filesInSameTu);
8658 processSourceFile(fd.get(),*g_outputList,nullptr);
8659 }
8660 }
8661 }
8662 }
8663 }
8664}
8665
8666//----------------------------------------------------------------------------
8667
8668static void generateFileDocs()
8669{
8670 if (Index::instance().numDocumentedFiles()==0) return;
8671
8672 if (!Doxygen::inputNameLinkedMap->empty())
8673 {
8674 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8675 if (numThreads>1) // multi threaded processing
8676 {
8677 struct DocContext
8678 {
8679 DocContext(FileDef *fd_,const OutputList &ol_)
8680 : fd(fd_), ol(ol_) {}
8681 FileDef *fd;
8682 OutputList ol;
8683 };
8684 ThreadPool threadPool(numThreads);
8685 std::vector< std::future< std::shared_ptr<DocContext> > > results;
8686 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8687 {
8688 for (const auto &fd : *fn)
8689 {
8690 bool doc = fd->isLinkableInProject();
8691 if (doc)
8692 {
8693 auto ctx = std::make_shared<DocContext>(fd.get(),*g_outputList);
8694 auto processFile = [ctx]() {
8695 msg("Generating docs for file {}...\n",ctx->fd->docName());
8696 ctx->fd->writeDocumentation(ctx->ol);
8697 return ctx;
8698 };
8699 results.emplace_back(threadPool.queue(processFile));
8700 }
8701 }
8702 }
8703 for (auto &f : results)
8704 {
8705 auto ctx = f.get();
8706 }
8707 }
8708 else // single threaded processing
8709 {
8710 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8711 {
8712 for (const auto &fd : *fn)
8713 {
8714 bool doc = fd->isLinkableInProject();
8715 if (doc)
8716 {
8717 msg("Generating docs for file {}...\n",fd->docName());
8718 fd->writeDocumentation(*g_outputList);
8719 }
8720 }
8721 }
8722 }
8723 }
8724}
8725
8726//----------------------------------------------------------------------------
8727
8729{
8730 // add source references for class definitions
8731 for (const auto &cd : *Doxygen::classLinkedMap)
8732 {
8733 const FileDef *fd=cd->getBodyDef();
8734 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8735 {
8736 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),nullptr);
8737 }
8738 }
8739 // add source references for concept definitions
8740 for (const auto &cd : *Doxygen::conceptLinkedMap)
8741 {
8742 const FileDef *fd=cd->getBodyDef();
8743 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8744 {
8745 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),nullptr);
8746 }
8747 }
8748 // add source references for namespace definitions
8749 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8750 {
8751 const FileDef *fd=nd->getBodyDef();
8752 if (fd && nd->isLinkableInProject() && nd->getStartDefLine()!=-1)
8753 {
8754 const_cast<FileDef*>(fd)->addSourceRef(nd->getStartDefLine(),nd.get(),nullptr);
8755 }
8756 }
8757
8758 // add source references for member names
8759 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8760 {
8761 for (const auto &md : *mn)
8762 {
8763 //printf("class member %s: def=%s body=%d link?=%d\n",
8764 // qPrint(md->name()),
8765 // md->getBodyDef()?qPrint(md->getBodyDef()->name()):"<none>",
8766 // md->getStartBodyLine(),md->isLinkableInProject());
8767 const FileDef *fd=md->getBodyDef();
8768 if (fd &&
8769 md->getStartDefLine()!=-1 &&
8770 md->isLinkableInProject() &&
8772 )
8773 {
8774 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8775 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8776 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8777 }
8778 }
8779 }
8780 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8781 {
8782 for (const auto &md : *mn)
8783 {
8784 const FileDef *fd=md->getBodyDef();
8785 //printf("member %s body=[%d,%d] fd=%p link=%d parseSources=%d\n",
8786 // qPrint(md->name()),
8787 // md->getStartBodyLine(),md->getEndBodyLine(),fd,
8788 // md->isLinkableInProject(),
8789 // Doxygen::parseSourcesNeeded);
8790 if (fd &&
8791 md->getStartDefLine()!=-1 &&
8792 md->isLinkableInProject() &&
8794 )
8795 {
8796 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8797 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8798 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8799 }
8800 }
8801 }
8802}
8803
8804//----------------------------------------------------------------------------
8805
8806// add the macro definitions found during preprocessing as file members
8807static void buildDefineList()
8808{
8809 AUTO_TRACE();
8810 for (const auto &s : g_inputFiles)
8811 {
8812 auto it = Doxygen::macroDefinitions.find(s);
8814 {
8815 for (const auto &def : it->second)
8816 {
8817 auto md = createMemberDef(
8818 def.fileName,def.lineNr,def.columnNr,
8819 "#define",def.name,def.args,QCString(),
8821 ArgumentList(),ArgumentList(),"");
8822 auto mmd = toMemberDefMutable(md.get());
8823
8824 if (!def.args.isEmpty())
8825 {
8826 mmd->moveArgumentList(stringToArgumentList(SrcLangExt::Cpp, def.args));
8827 }
8828 mmd->setInitializer(def.definition);
8829 mmd->setFileDef(def.fileDef);
8830 mmd->setDefinition("#define "+def.name);
8831
8832 MemberName *mn=Doxygen::functionNameLinkedMap->add(def.name);
8833 if (def.fileDef)
8834 {
8835 def.fileDef->insertMember(md.get());
8836 }
8837 AUTO_TRACE_ADD("adding macro {} with definition {}",def.name,def.definition);
8838 mn->push_back(std::move(md));
8839 }
8840 }
8841 }
8842}
8843
8844//----------------------------------------------------------------------------
8845
8846static void sortMemberLists()
8847{
8848 // sort class member lists
8849 for (const auto &cd : *Doxygen::classLinkedMap)
8850 {
8851 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8852 if (cdm)
8853 {
8854 cdm->sortMemberLists();
8855 }
8856 }
8857
8858 // sort namespace member lists
8859 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8860 {
8862 if (ndm)
8863 {
8864 ndm->sortMemberLists();
8865 }
8866 }
8867
8868 // sort file member lists
8869 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8870 {
8871 for (const auto &fd : *fn)
8872 {
8873 fd->sortMemberLists();
8874 }
8875 }
8876
8877 // sort group member lists
8878 for (const auto &gd : *Doxygen::groupLinkedMap)
8879 {
8880 gd->sortMemberLists();
8881 }
8882
8884}
8885
8886//----------------------------------------------------------------------------
8887
8888static bool isSymbolHidden(const Definition *d)
8889{
8890 bool hidden = d->isHidden();
8891 const Definition *parent = d->getOuterScope();
8892 return parent ? hidden || isSymbolHidden(parent) : hidden;
8893}
8894
8896{
8897 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8898 if (numThreads>1)
8899 {
8900 ThreadPool threadPool(numThreads);
8901 std::vector < std::future< void > > results;
8902 // queue the work
8903 for (const auto &[name,symList] : *Doxygen::symbolMap)
8904 {
8905 for (const auto &def : symList)
8906 {
8908 if (dm && !isSymbolHidden(def) && !def->isArtificial() && def->isLinkableInProject())
8909 {
8910 auto processTooltip = [dm]() {
8911 dm->computeTooltip();
8912 };
8913 results.emplace_back(threadPool.queue(processTooltip));
8914 }
8915 }
8916 }
8917 // wait for the results
8918 for (auto &f : results)
8919 {
8920 f.get();
8921 }
8922 }
8923 else
8924 {
8925 for (const auto &[name,symList] : *Doxygen::symbolMap)
8926 {
8927 for (const auto &def : symList)
8928 {
8930 if (dm && !isSymbolHidden(def) && !def->isArtificial() && def->isLinkableInProject())
8931 {
8932 dm->computeTooltip();
8933 }
8934 }
8935 }
8936 }
8937}
8938
8939//----------------------------------------------------------------------------
8940
8942{
8943 for (const auto &cd : *Doxygen::classLinkedMap)
8944 {
8945 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8946 if (cdm)
8947 {
8948 cdm->setAnonymousEnumType();
8949 }
8950 }
8951}
8952
8953//----------------------------------------------------------------------------
8954
8955static void countMembers()
8956{
8957 for (const auto &cd : *Doxygen::classLinkedMap)
8958 {
8959 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8960 if (cdm)
8961 {
8962 cdm->countMembers();
8963 }
8964 }
8965
8966 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8967 {
8969 if (ndm)
8970 {
8971 ndm->countMembers();
8972 }
8973 }
8974
8975 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8976 {
8977 for (const auto &fd : *fn)
8978 {
8979 fd->countMembers();
8980 }
8981 }
8982
8983 for (const auto &gd : *Doxygen::groupLinkedMap)
8984 {
8985 gd->countMembers();
8986 }
8987
8988 auto &mm = ModuleManager::instance();
8989 mm.countMembers();
8990}
8991
8992
8993//----------------------------------------------------------------------------
8994// generate the documentation for all classes
8995
8996static void generateDocsForClassList(const std::vector<ClassDefMutable*> &classList)
8997{
8998 AUTO_TRACE();
8999 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9000 if (numThreads>1) // multi threaded processing
9001 {
9002 struct DocContext
9003 {
9004 DocContext(ClassDefMutable *cd_,const OutputList &ol_)
9005 : cd(cd_), ol(ol_) {}
9006 ClassDefMutable *cd;
9007 OutputList ol;
9008 };
9009 ThreadPool threadPool(numThreads);
9010 std::vector< std::future< std::shared_ptr<DocContext> > > results;
9011 for (const auto &cd : classList)
9012 {
9013 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
9014 if (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9015 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
9016 )
9017 {
9018 auto ctx = std::make_shared<DocContext>(cd,*g_outputList);
9019 auto processFile = [ctx]()
9020 {
9021 msg("Generating docs for compound {}...\n",ctx->cd->displayName());
9022
9023 // skip external references, anonymous compounds and
9024 // template instances
9025 if (!ctx->cd->isHidden() && !ctx->cd->isEmbeddedInOuterScope() &&
9026 ctx->cd->isLinkableInProject() && !ctx->cd->isImplicitTemplateInstance())
9027 {
9028 ctx->cd->writeDocumentation(ctx->ol);
9029 ctx->cd->writeMemberList(ctx->ol);
9030 }
9031
9032 // even for undocumented classes, the inner classes can be documented.
9033 ctx->cd->writeDocumentationForInnerClasses(ctx->ol);
9034 return ctx;
9035 };
9036 results.emplace_back(threadPool.queue(processFile));
9037 }
9038 }
9039 for (auto &f : results)
9040 {
9041 auto ctx = f.get();
9042 }
9043 }
9044 else // single threaded processing
9045 {
9046 for (const auto &cd : classList)
9047 {
9048 //printf("cd=%s getOuterScope=%p global=%p hidden=%d embeddedInOuterScope=%d\n",
9049 // qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope,cd->isHidden(),cd->isEmbeddedInOuterScope());
9050 if (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9051 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
9052 )
9053 {
9054 // skip external references, anonymous compounds and
9055 // template instances
9056 if ( !cd->isHidden() && !cd->isEmbeddedInOuterScope() &&
9057 cd->isLinkableInProject() && !cd->isImplicitTemplateInstance())
9058 {
9059 msg("Generating docs for compound {}...\n",cd->displayName());
9060
9061 cd->writeDocumentation(*g_outputList);
9062 cd->writeMemberList(*g_outputList);
9063 }
9064 // even for undocumented classes, the inner classes can be documented.
9065 cd->writeDocumentationForInnerClasses(*g_outputList);
9066 }
9067 }
9068 }
9069}
9070
9071static void addClassAndNestedClasses(std::vector<ClassDefMutable*> &list,ClassDefMutable *cd)
9072{
9073 list.push_back(cd);
9074 for (const auto &innerCdi : cd->getClasses())
9075 {
9076 ClassDefMutable *innerCd = toClassDefMutable(innerCdi);
9077 if (innerCd)
9078 {
9079 AUTO_TRACE("innerCd={} isLinkable={} isImplicitTemplateInstance={} protectLevelVisible={} embeddedInOuterScope={}",
9080 innerCd->name(),innerCd->isLinkableInProject(),innerCd->isImplicitTemplateInstance(),protectionLevelVisible(innerCd->protection()),
9081 innerCd->isEmbeddedInOuterScope());
9082 }
9083 if (innerCd && innerCd->isLinkableInProject() && !innerCd->isImplicitTemplateInstance() &&
9084 protectionLevelVisible(innerCd->protection()) &&
9085 !innerCd->isEmbeddedInOuterScope()
9086 )
9087 {
9088 list.push_back(innerCd);
9089 addClassAndNestedClasses(list,innerCd);
9090 }
9091 }
9092}
9093
9095{
9096 std::vector<ClassDefMutable*> classList;
9097 for (const auto &cdi : *Doxygen::classLinkedMap)
9098 {
9099 ClassDefMutable *cd = toClassDefMutable(cdi.get());
9100 if (cd && (cd->getOuterScope()==nullptr ||
9102 {
9103 addClassAndNestedClasses(classList,cd);
9104 }
9105 }
9106 for (const auto &cdi : *Doxygen::hiddenClassLinkedMap)
9107 {
9108 ClassDefMutable *cd = toClassDefMutable(cdi.get());
9109 if (cd && (cd->getOuterScope()==nullptr ||
9111 {
9112 addClassAndNestedClasses(classList,cd);
9113 }
9114 }
9115 generateDocsForClassList(classList);
9116}
9117
9118//----------------------------------------------------------------------------
9119
9121{
9122 for (const auto &cdi : *Doxygen::conceptLinkedMap)
9123 {
9125
9126 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
9127 if (cd &&
9128 (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9129 cd->getOuterScope()==Doxygen::globalScope // only look at global concepts
9130 ) && !cd->isHidden() && cd->isLinkableInProject()
9131 )
9132 {
9133 msg("Generating docs for concept {}...\n",cd->displayName());
9135 }
9136 }
9137}
9138
9139//----------------------------------------------------------------------------
9140
9142{
9143 for (const auto &mn : *Doxygen::memberNameLinkedMap)
9144 {
9145 for (const auto &imd : *mn)
9146 {
9147 MemberDefMutable *md = toMemberDefMutable(imd.get());
9148 //static int count=0;
9149 //printf("%04d Member '%s'\n",count++,qPrint(md->qualifiedName()));
9150 if (md && md->documentation().isEmpty() && md->briefDescription().isEmpty())
9151 { // no documentation yet
9152 const MemberDef *bmd = md->reimplements();
9153 while (bmd && bmd->documentation().isEmpty() &&
9154 bmd->briefDescription().isEmpty()
9155 )
9156 { // search up the inheritance tree for a documentation member
9157 //printf("bmd=%s class=%s\n",qPrint(bmd->name()),qPrint(bmd->getClassDef()->name()));
9158 bmd = bmd->reimplements();
9159 }
9160 if (bmd) // copy the documentation from the reimplemented member
9161 {
9162 md->setInheritsDocsFrom(bmd);
9163 md->setDocumentation(bmd->documentation(),bmd->docFile(),bmd->docLine());
9165 md->setBriefDescription(bmd->briefDescription(),bmd->briefFile(),bmd->briefLine());
9166 md->copyArgumentNames(bmd);
9168 }
9169 }
9170 }
9171 }
9172}
9173
9174//----------------------------------------------------------------------------
9175
9177{
9178 // for each file
9179 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9180 {
9181 for (const auto &fd : *fn)
9182 {
9183 fd->combineUsingRelations();
9184 }
9185 }
9186
9187 // for each namespace
9188 NamespaceDefSet visitedNamespaces;
9189 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9190 {
9192 if (ndm)
9193 {
9194 ndm->combineUsingRelations(visitedNamespaces);
9195 }
9196 }
9197}
9198
9199//----------------------------------------------------------------------------
9200
9202{
9203 // for each class
9204 for (const auto &cd : *Doxygen::classLinkedMap)
9205 {
9206 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9207 if (cdm)
9208 {
9210 }
9211 }
9212 // for each file
9213 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9214 {
9215 for (const auto &fd : *fn)
9216 {
9217 fd->addMembersToMemberGroup();
9218 }
9219 }
9220 // for each namespace
9221 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9222 {
9224 if (ndm)
9225 {
9227 }
9228 }
9229 // for each group
9230 for (const auto &gd : *Doxygen::groupLinkedMap)
9231 {
9232 gd->addMembersToMemberGroup();
9233 }
9235}
9236
9237//----------------------------------------------------------------------------
9238
9240{
9241 // for each class
9242 for (const auto &cd : *Doxygen::classLinkedMap)
9243 {
9244 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9245 if (cdm)
9246 {
9248 }
9249 }
9250 // for each file
9251 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9252 {
9253 for (const auto &fd : *fn)
9254 {
9255 fd->distributeMemberGroupDocumentation();
9256 }
9257 }
9258 // for each namespace
9259 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9260 {
9262 if (ndm)
9263 {
9265 }
9266 }
9267 // for each group
9268 for (const auto &gd : *Doxygen::groupLinkedMap)
9269 {
9270 gd->distributeMemberGroupDocumentation();
9271 }
9273}
9274
9275//----------------------------------------------------------------------------
9276
9278{
9279 // for each class
9280 for (const auto &cd : *Doxygen::classLinkedMap)
9281 {
9282 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9283 if (cdm)
9284 {
9286 }
9287 }
9288 // for each concept
9289 for (const auto &cd : *Doxygen::conceptLinkedMap)
9290 {
9291 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
9292 if (cdm)
9293 {
9295 }
9296 }
9297 // for each file
9298 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9299 {
9300 for (const auto &fd : *fn)
9301 {
9302 fd->findSectionsInDocumentation();
9303 }
9304 }
9305 // for each namespace
9306 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9307 {
9309 if (ndm)
9310 {
9312 }
9313 }
9314 // for each group
9315 for (const auto &gd : *Doxygen::groupLinkedMap)
9316 {
9317 gd->findSectionsInDocumentation();
9318 }
9319 // for each page
9320 for (const auto &pd : *Doxygen::pageLinkedMap)
9321 {
9322 pd->findSectionsInDocumentation();
9323 }
9325 if (Doxygen::mainPage) Doxygen::mainPage->findSectionsInDocumentation();
9326}
9327
9328//----------------------------------------------------------------------
9329
9330
9332{
9333 // remove all references to classes from the cache
9334 // as there can be new template instances in the inheritance path
9335 // to this class. Optimization: only remove those classes that
9336 // have inheritance instances as direct or indirect sub classes.
9337 StringVector elementsToRemove;
9338 for (const auto &ci : *Doxygen::typeLookupCache)
9339 {
9340 const LookupInfo &li = ci.second;
9341 if (li.definition)
9342 {
9343 elementsToRemove.push_back(ci.first);
9344 }
9345 }
9346 for (const auto &k : elementsToRemove)
9347 {
9348 Doxygen::typeLookupCache->remove(k);
9349 }
9350
9351 // remove all cached typedef resolutions whose target is a
9352 // template class as this may now be a template instance
9353 // for each global function name
9354 for (const auto &fn : *Doxygen::functionNameLinkedMap)
9355 {
9356 // for each function with that name
9357 for (const auto &ifmd : *fn)
9358 {
9359 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
9360 if (fmd && fmd->isTypedefValCached())
9361 {
9362 const ClassDef *cd = fmd->getCachedTypedefVal();
9363 if (cd->isTemplate()) fmd->invalidateTypedefValCache();
9364 }
9365 }
9366 }
9367 // for each class method name
9368 for (const auto &nm : *Doxygen::memberNameLinkedMap)
9369 {
9370 // for each function with that name
9371 for (const auto &imd : *nm)
9372 {
9373 MemberDefMutable *md = toMemberDefMutable(imd.get());
9374 if (md && md->isTypedefValCached())
9375 {
9376 const ClassDef *cd = md->getCachedTypedefVal();
9377 if (cd->isTemplate()) md->invalidateTypedefValCache();
9378 }
9379 }
9380 }
9381}
9382
9383//----------------------------------------------------------------------------
9384
9386{
9387 // Remove all unresolved references to classes from the cache.
9388 // This is needed before resolving the inheritance relations, since
9389 // it would otherwise not find the inheritance relation
9390 // for C in the example below, as B::I was already found to be unresolvable
9391 // (which is correct if you ignore the inheritance relation between A and B).
9392 //
9393 // class A { class I {} };
9394 // class B : public A {};
9395 // class C : public B::I {};
9396
9397 StringVector elementsToRemove;
9398 for (const auto &ci : *Doxygen::typeLookupCache)
9399 {
9400 const LookupInfo &li = ci.second;
9401 if (li.definition==nullptr && li.typeDef==nullptr)
9402 {
9403 elementsToRemove.push_back(ci.first);
9404 }
9405 }
9406 for (const auto &k : elementsToRemove)
9407 {
9408 Doxygen::typeLookupCache->remove(k);
9409 }
9410
9411 // for each global function name
9412 for (const auto &fn : *Doxygen::functionNameLinkedMap)
9413 {
9414 // for each function with that name
9415 for (const auto &ifmd : *fn)
9416 {
9417 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
9418 if (fmd)
9419 {
9421 }
9422 }
9423 }
9424 // for each class method name
9425 for (const auto &nm : *Doxygen::memberNameLinkedMap)
9426 {
9427 // for each function with that name
9428 for (const auto &imd : *nm)
9429 {
9430 MemberDefMutable *md = toMemberDefMutable(imd.get());
9431 if (md)
9432 {
9434 }
9435 }
9436 }
9437
9438}
9439
9440//----------------------------------------------------------------------------
9441// Returns TRUE if the entry and member definition have equal file names,
9442// otherwise FALSE.
9443
9444static bool haveEqualFileNames(const Entry *root,const MemberDef *md)
9445{
9446 const FileDef *fd = md->getFileDef();
9447 if (!fd)
9448 {
9449 return FALSE;
9450 }
9451
9452 return fd->absFilePath() == root->fileName;
9453}
9454
9455//----------------------------------------------------------------------------
9456
9457static void addDefineDoc(const Entry *root, MemberDefMutable *md)
9458{
9459 md->setDocumentation(root->doc,root->docFile,root->docLine);
9460 md->setDocsForDefinition(!root->proto);
9461 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9462 if (md->inbodyDocumentation().isEmpty())
9463 {
9465 }
9466 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
9467 {
9468 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
9469 md->setBodyDef(root->fileDef());
9470 }
9472 md->setMaxInitLines(root->initLines);
9474 md->setRefItems(root->sli);
9475 if (root->mGrpId!=-1) md->setMemberGroupId(root->mGrpId);
9476 addMemberToGroups(root,md);
9478}
9479
9480//----------------------------------------------------------------------------
9481
9483{
9484 if ((root->section.isDefineDoc() || root->section.isDefine()) && !root->name.isEmpty())
9485 {
9486 //printf("found define '%s' '%s' brief='%s' doc='%s'\n",
9487 // qPrint(root->name),qPrint(root->args),qPrint(root->brief),qPrint(root->doc));
9488
9489 if (root->tagInfo() && !root->name.isEmpty()) // define read from a tag file
9490 {
9491 auto md = createMemberDef(root->tagInfo()->tagName,1,1,
9492 "#define",root->name,root->args,QCString(),
9494 ArgumentList(),ArgumentList(),"");
9495 auto mmd = toMemberDefMutable(md.get());
9496 mmd->setTagInfo(root->tagInfo());
9497 mmd->setLanguage(root->lang);
9498 //printf("Searching for '%s' fd=%p\n",qPrint(filePathName),fd);
9499 mmd->setFileDef(root->parent()->fileDef());
9500 //printf("Adding member=%s\n",qPrint(md->name()));
9502 mn->push_back(std::move(md));
9503 }
9505 if (mn)
9506 {
9507 int count=0;
9508 for (const auto &md : *mn)
9509 {
9510 if (md->memberType()==MemberType::Define) count++;
9511 }
9512 if (count==1)
9513 {
9514 for (const auto &imd : *mn)
9515 {
9516 MemberDefMutable *md = toMemberDefMutable(imd.get());
9517 if (md && md->memberType()==MemberType::Define)
9518 {
9519 addDefineDoc(root,md);
9520 }
9521 }
9522 }
9523 else if (count>1 &&
9524 (!root->doc.isEmpty() ||
9525 !root->brief.isEmpty() ||
9526 root->bodyLine!=-1
9527 )
9528 )
9529 // multiple defines don't know where to add docs
9530 // but maybe they are in different files together with their documentation
9531 {
9532 for (const auto &imd : *mn)
9533 {
9534 MemberDefMutable *md = toMemberDefMutable(imd.get());
9535 if (md && md->memberType()==MemberType::Define)
9536 {
9537 if (haveEqualFileNames(root, md) || isEntryInGroupOfMember(root, md))
9538 // doc and define in the same file or group assume they belong together.
9539 {
9540 addDefineDoc(root,md);
9541 }
9542 }
9543 }
9544 //warn("define {} found in the following files:\n",root->name);
9545 //warn("Cannot determine where to add the documentation found "
9546 // "at line {} of file {}. \n",
9547 // root->startLine,root->fileName);
9548 }
9549 }
9550 else if (!root->doc.isEmpty() || !root->brief.isEmpty()) // define not found
9551 {
9552 bool preEnabled = Config_getBool(ENABLE_PREPROCESSING);
9553 if (preEnabled)
9554 {
9555 warn(root->fileName,root->startLine,"documentation for unknown define {} found.",root->name);
9556 }
9557 else
9558 {
9559 warn(root->fileName,root->startLine, "found documented #define {} but ignoring it because ENABLE_PREPROCESSING is NO.", root->name);
9560 }
9561 }
9562 }
9563 for (const auto &e : root->children()) findDefineDocumentation(e.get());
9564}
9565
9566//----------------------------------------------------------------------------
9567
9568static void findDirDocumentation(const Entry *root)
9569{
9570 if (root->section.isDirDoc())
9571 {
9572 QCString normalizedName = root->name;
9573 normalizedName = substitute(normalizedName,"\\","/");
9574 //printf("root->docFile=%s normalizedName=%s\n",
9575 // qPrint(root->docFile),qPrint(normalizedName));
9576 if (root->docFile==normalizedName) // current dir?
9577 {
9578 int lastSlashPos=normalizedName.findRev('/');
9579 if (lastSlashPos!=-1) // strip file name
9580 {
9581 normalizedName=normalizedName.left(lastSlashPos);
9582 }
9583 }
9584 if (normalizedName.at(normalizedName.length()-1)!='/')
9585 {
9586 normalizedName+='/';
9587 }
9588 DirDef *matchingDir=nullptr;
9589 for (const auto &dir : *Doxygen::dirLinkedMap)
9590 {
9591 //printf("Dir: %s<->%s\n",qPrint(dir->name()),qPrint(normalizedName));
9592 if (dir->name().right(normalizedName.length())==normalizedName)
9593 {
9594 if (matchingDir)
9595 {
9596 warn(root->fileName,root->startLine,
9597 "\\dir command matches multiple directories.\n"
9598 " Applying the command for directory {}\n"
9599 " Ignoring the command for directory {}",
9600 matchingDir->name(),dir->name()
9601 );
9602 }
9603 else
9604 {
9605 matchingDir=dir.get();
9606 }
9607 }
9608 }
9609 if (matchingDir)
9610 {
9611 //printf("Match for with dir %s\n",qPrint(matchingDir->name()));
9612 matchingDir->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9613 matchingDir->setDocumentation(root->doc,root->docFile,root->docLine);
9614 matchingDir->setRefItems(root->sli);
9615 root->commandOverrides.apply_directoryGraph([&](bool b) { matchingDir->overrideDirectoryGraph(b); });
9616 addDirToGroups(root,matchingDir);
9617 }
9618 else
9619 {
9620 warn(root->fileName,root->startLine,"No matching directory found for command \\dir {}",normalizedName);
9621 }
9622 }
9623 for (const auto &e : root->children()) findDirDocumentation(e.get());
9624}
9625
9626
9627//----------------------------------------------------------------------------
9628// create a (sorted) list of separate documentation pages
9629
9630static void buildPageList(Entry *root)
9631{
9632 if (root->section.isPageDoc())
9633 {
9634 if (!root->name.isEmpty())
9635 {
9636 addRelatedPage(root);
9637 }
9638 }
9639 else if (root->section.isMainpageDoc())
9640 {
9641 QCString title=root->args.stripWhiteSpace();
9642 if (title.isEmpty()) title=theTranslator->trMainPage();
9643 //QCString name = Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9644 QCString name = "index";
9645 addRefItem(root->sli,
9646 name,
9647 "page",
9648 name,
9649 title,
9650 QCString(),nullptr
9651 );
9652 }
9653 for (const auto &e : root->children()) buildPageList(e.get());
9654}
9655
9656// search for the main page defined in this project
9657static void findMainPage(Entry *root)
9658{
9659 if (root->section.isMainpageDoc())
9660 {
9661 if (Doxygen::mainPage==nullptr && root->tagInfo()==nullptr)
9662 {
9663 //printf("mainpage: docLine=%d startLine=%d\n",root->docLine,root->startLine);
9664 //printf("Found main page! \n======\n%s\n=======\n",qPrint(root->doc));
9665 QCString title=root->args.stripWhiteSpace();
9666 if (title.isEmpty()) title = Config_getString(PROJECT_NAME);
9667 //QCString indexName=Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9668 QCString indexName="index";
9670 indexName, root->brief+root->doc+root->inbodyDocs,title);
9671 //setFileNameForSections(root->anchors,"index",Doxygen::mainPage);
9672 Doxygen::mainPage->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9673 Doxygen::mainPage->setBodySegment(root->startLine,root->startLine,-1);
9674 Doxygen::mainPage->setFileName(indexName);
9675 Doxygen::mainPage->setLocalToc(root->localToc);
9677
9679 if (si)
9680 {
9681 if (!si->ref().isEmpty()) // we are from a tag file
9682 {
9683 // a page name is a label as well! but should no be double either
9685 Doxygen::mainPage->name(),
9686 indexName,
9687 root->startLine,
9688 Doxygen::mainPage->title(),
9690 0); // level 0
9691 }
9692 else if (si->lineNr() != -1)
9693 {
9694 warn(root->fileName,root->startLine,"multiple use of section label '{}' for main page, (first occurrence: {}, line {})",
9695 Doxygen::mainPage->name(),si->fileName(),si->lineNr());
9696 }
9697 else
9698 {
9699 warn(root->fileName,root->startLine,"multiple use of section label '{}' for main page, (first occurrence: {})",
9700 Doxygen::mainPage->name(),si->fileName());
9701 }
9702 }
9703 else
9704 {
9705 // a page name is a label as well! but should no be double either
9707 Doxygen::mainPage->name(),
9708 indexName,
9709 root->startLine,
9710 Doxygen::mainPage->title(),
9712 0); // level 0
9713 }
9714 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9715 }
9716 else if (root->tagInfo()==nullptr)
9717 {
9718 warn(root->fileName,root->startLine,
9719 "found more than one \\mainpage comment block! (first occurrence: {}, line {}), Skipping current block!",
9720 Doxygen::mainPage->docFile(),Doxygen::mainPage->getStartBodyLine());
9721 }
9722 }
9723 for (const auto &e : root->children()) findMainPage(e.get());
9724}
9725
9726// search for the main page imported via tag files and add only the section labels
9727static void findMainPageTagFiles(Entry *root)
9728{
9729 if (root->section.isMainpageDoc())
9730 {
9731 if (Doxygen::mainPage && root->tagInfo())
9732 {
9733 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9734 }
9735 }
9736 for (const auto &e : root->children()) findMainPageTagFiles(e.get());
9737}
9738
9739static void computePageRelations(Entry *root)
9740{
9741 if ((root->section.isPageDoc() || root->section.isMainpageDoc()) && !root->name.isEmpty())
9742 {
9743 PageDef *pd = root->section.isPageDoc() ?
9744 Doxygen::pageLinkedMap->find(root->name) :
9745 Doxygen::mainPage.get();
9746 if (pd)
9747 {
9748 for (const BaseInfo &bi : root->extends)
9749 {
9750 PageDef *subPd = Doxygen::pageLinkedMap->find(bi.name);
9751 if (pd==subPd)
9752 {
9753 term("page defined {} with label {} is a direct "
9754 "subpage of itself! Please remove this cyclic dependency.\n",
9755 warn_line(pd->docFile(),pd->docLine()),pd->name());
9756 }
9757 else if (subPd)
9758 {
9759 pd->addInnerCompound(subPd);
9760 //printf("*** Added subpage relation: %s->%s\n",
9761 // qPrint(pd->name()),qPrint(subPd->name()));
9762 }
9763 }
9764 }
9765 }
9766 for (const auto &e : root->children()) computePageRelations(e.get());
9767}
9768
9770{
9771 for (const auto &pd : *Doxygen::pageLinkedMap)
9772 {
9773 Definition *ppd = pd->getOuterScope();
9774 while (ppd)
9775 {
9776 if (ppd==pd.get())
9777 {
9778 term("page defined {} with label {} is a subpage "
9779 "of itself! Please remove this cyclic dependency.\n",
9780 warn_line(pd->docFile(),pd->docLine()),pd->name());
9781 }
9782 ppd=ppd->getOuterScope();
9783 }
9784 }
9785}
9786
9787//----------------------------------------------------------------------------
9788
9790{
9791 for (const auto &si : SectionManager::instance())
9792 {
9793 //printf("si->label='%s' si->definition=%s si->fileName='%s'\n",
9794 // qPrint(si->label),si->definition?qPrint(si->definition->name()):"<none>",
9795 // qPrint(si->fileName));
9796 PageDef *pd=nullptr;
9797
9798 // hack: the items of a todo/test/bug/deprecated list are all fragments from
9799 // different files, so the resulting section's all have the wrong file
9800 // name (not from the todo/test/bug/deprecated list, but from the file in
9801 // which they are defined). We correct this here by looking at the
9802 // generated section labels!
9804 {
9805 QCString label="_"+rl->listName(); // "_todo", "_test", ...
9806 if (si->label().left(label.length())==label)
9807 {
9808 si->setFileName(rl->listName());
9809 si->setGenerated(TRUE);
9810 break;
9811 }
9812 }
9813
9814 //printf("start: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
9815 if (!si->generated())
9816 {
9817 // if this section is in a page and the page is in a group, then we
9818 // have to adjust the link file name to point to the group.
9819 if (!si->fileName().isEmpty() &&
9820 (pd=Doxygen::pageLinkedMap->find(si->fileName())) &&
9821 pd->getGroupDef())
9822 {
9823 si->setFileName(pd->getGroupDef()->getOutputFileBase());
9824 }
9825
9826 if (si->definition())
9827 {
9828 // TODO: there should be one function in Definition that returns
9829 // the file to link to, so we can avoid the following tests.
9830 const GroupDef *gd=nullptr;
9831 if (si->definition()->definitionType()==Definition::TypeMember)
9832 {
9833 gd = (toMemberDef(si->definition()))->getGroupDef();
9834 }
9835
9836 if (gd)
9837 {
9838 si->setFileName(gd->getOutputFileBase());
9839 }
9840 else
9841 {
9842 //si->fileName=si->definition->getOutputFileBase();
9843 //printf("Setting si->fileName to %s\n",qPrint(si->fileName));
9844 }
9845 }
9846 }
9847 //printf("end: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
9848 }
9849}
9850
9851
9852
9853//----------------------------------------------------------------------------
9854// generate all separate documentation pages
9855
9856
9857static void generatePageDocs()
9858{
9859 //printf("documentedPages=%d real=%d\n",documentedPages,Doxygen::pageLinkedMap->count());
9860 if (Index::instance().numDocumentedPages()==0) return;
9861 for (const auto &pd : *Doxygen::pageLinkedMap)
9862 {
9863 if (!pd->getGroupDef() && !pd->isReference())
9864 {
9865 msg("Generating docs for page {}...\n",pd->name());
9866 pd->writeDocumentation(*g_outputList);
9867 }
9868 }
9869}
9870
9871//----------------------------------------------------------------------------
9872// create a (sorted) list & dictionary of example pages
9873
9874static void buildExampleList(Entry *root)
9875{
9876 if ((root->section.isExample() || root->section.isExampleLineno()) && !root->name.isEmpty())
9877 {
9878 if (Doxygen::exampleLinkedMap->find(root->name))
9879 {
9880 warn(root->fileName,root->startLine,"Example {} was already documented. Ignoring documentation found here.",root->name);
9881 }
9882 else
9883 {
9884 PageDef *pd = Doxygen::exampleLinkedMap->add(root->name,
9885 createPageDef(root->fileName,root->startLine,
9886 root->name,root->brief+root->doc+root->inbodyDocs,root->args));
9887 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9888 pd->setFileName(convertNameToFile(pd->name()+"-example",FALSE,TRUE));
9890 pd->setLanguage(root->lang);
9891 pd->setShowLineNo(root->section.isExampleLineno());
9892
9893 //we don't add example to groups
9894 //addExampleToGroups(root,pd);
9895 }
9896 }
9897 for (const auto &e : root->children()) buildExampleList(e.get());
9898}
9899
9900//----------------------------------------------------------------------------
9901// prints the Entry tree (for debugging)
9902
9903void printNavTree(Entry *root,int indent)
9904{
9906 {
9907 QCString indentStr;
9908 indentStr.fill(' ',indent);
9909 Debug::print(Debug::Entries,0,"{}{} at {}:{} (sec={}, spec={})\n",
9910 indentStr.isEmpty()?"":indentStr,
9911 root->name.isEmpty()?"<empty>":root->name,
9912 root->fileName,root->startLine,
9913 root->section.to_string(),
9914 root->spec.to_string());
9915 for (const auto &e : root->children())
9916 {
9917 printNavTree(e.get(),indent+2);
9918 }
9919 }
9920}
9921
9922
9923//----------------------------------------------------------------------------
9924// prints the Sections tree (for debugging)
9925
9927{
9929 {
9930 for (const auto &si : SectionManager::instance())
9931 {
9932 Debug::print(Debug::Sections,0,"Section = {}, file = {}, title = {}, type = {}, ref = {}\n",
9933 si->label(),si->fileName(),si->title(),si->type().level(),si->ref());
9934 }
9935 }
9936}
9937
9938
9939//----------------------------------------------------------------------------
9940// generate the example documentation
9941
9943{
9944 g_outputList->disable(OutputType::Man);
9945 for (const auto &pd : *Doxygen::exampleLinkedMap)
9946 {
9947 msg("Generating docs for example {}...\n",pd->name());
9949 if (lang != SrcLangExt::Unknown)
9950 {
9951 QCString ext = getFileNameExtension(pd->name());
9952 auto intf = Doxygen::parserManager->getCodeParser(ext);
9953 intf->resetCodeParserState();
9954 }
9955 QCString n=pd->getOutputFileBase();
9956 startFile(*g_outputList,n,n,pd->name());
9958 g_outputList->docify(pd->name());
9960 g_outputList->startContents();
9961 QCString lineNoOptStr;
9962 if (pd->showLineNo())
9963 {
9964 lineNoOptStr="{lineno}";
9965 }
9966 g_outputList->generateDoc(pd->docFile(), // file
9967 pd->docLine(), // startLine
9968 pd.get(), // context
9969 nullptr, // memberDef
9970 (pd->briefDescription().isEmpty()?"":pd->briefDescription()+"\n\n")+
9971 pd->documentation()+"\n\n\\include"+lineNoOptStr+" "+pd->name(), // docs
9972 TRUE, // index words
9973 TRUE, // is example
9974 pd->name(),
9975 FALSE,
9976 FALSE,
9977 Config_getBool(MARKDOWN_SUPPORT)
9978 );
9979 endFile(*g_outputList); // contains g_outputList->endContents()
9980 }
9982}
9983
9984//----------------------------------------------------------------------------
9985// generate module pages
9986
9988{
9989 for (const auto &gd : *Doxygen::groupLinkedMap)
9990 {
9991 if (!gd->isReference())
9992 {
9993 gd->writeDocumentation(*g_outputList);
9994 }
9995 }
9996}
9997
9998//----------------------------------------------------------------------------
9999// generate module pages
10000
10002{
10003 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10004 if (numThreads>1) // multi threaded processing
10005 {
10006 struct DocContext
10007 {
10008 DocContext(ClassDefMutable *cdm_,const OutputList &ol_)
10009 : cdm(cdm_), ol(ol_) {}
10010 ClassDefMutable *cdm;
10011 OutputList ol;
10012 };
10013 ThreadPool threadPool(numThreads);
10014 std::vector< std::future< std::shared_ptr<DocContext> > > results;
10015 // for each class in the namespace...
10016 for (const auto &cd : classList)
10017 {
10019 if (cdm)
10020 {
10021 auto ctx = std::make_shared<DocContext>(cdm,*g_outputList);
10022 auto processFile = [ctx]()
10023 {
10024 if ( ( ctx->cdm->isLinkableInProject() &&
10025 !ctx->cdm->isImplicitTemplateInstance()
10026 ) // skip external references, anonymous compounds and
10027 // template instances and nested classes
10028 && !ctx->cdm->isHidden() && !ctx->cdm->isEmbeddedInOuterScope()
10029 )
10030 {
10031 msg("Generating docs for compound {}...\n",ctx->cdm->displayName());
10032 ctx->cdm->writeDocumentation(ctx->ol);
10033 ctx->cdm->writeMemberList(ctx->ol);
10034 }
10035 ctx->cdm->writeDocumentationForInnerClasses(ctx->ol);
10036 return ctx;
10037 };
10038 results.emplace_back(threadPool.queue(processFile));
10039 }
10040 }
10041 // wait for the results
10042 for (auto &f : results)
10043 {
10044 auto ctx = f.get();
10045 }
10046 }
10047 else // single threaded processing
10048 {
10049 // for each class in the namespace...
10050 for (const auto &cd : classList)
10051 {
10053 if (cdm)
10054 {
10055 if ( ( cd->isLinkableInProject() &&
10056 !cd->isImplicitTemplateInstance()
10057 ) // skip external references, anonymous compounds and
10058 // template instances and nested classes
10059 && !cd->isHidden() && !cd->isEmbeddedInOuterScope()
10060 )
10061 {
10062 msg("Generating docs for compound {}...\n",cd->displayName());
10063
10066 }
10068 }
10069 }
10070 }
10071}
10072
10074{
10075 // for each concept in the namespace...
10076 for (const auto &cd : conceptList)
10077 {
10079 if ( cdm && cd->isLinkableInProject() && !cd->isHidden())
10080 {
10081 msg("Generating docs for concept {}...\n",cd->name());
10083 }
10084 }
10085}
10086
10088{
10089 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
10090
10091 //writeNamespaceIndex(*g_outputList);
10092
10093 // for each namespace...
10094 for (const auto &nd : *Doxygen::namespaceLinkedMap)
10095 {
10096 if (nd->isLinkableInProject())
10097 {
10099 if (ndm)
10100 {
10101 msg("Generating docs for namespace {}\n",nd->displayName());
10103 }
10104 }
10105
10106 generateNamespaceClassDocs(nd->getClasses());
10107 if (sliceOpt)
10108 {
10109 generateNamespaceClassDocs(nd->getInterfaces());
10110 generateNamespaceClassDocs(nd->getStructs());
10111 generateNamespaceClassDocs(nd->getExceptions());
10112 }
10113 generateNamespaceConceptDocs(nd->getConcepts());
10114 }
10115}
10116
10118{
10119 std::string oldDir = Dir::currentDirPath();
10120 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
10123 {
10124 err("failed to run html help compiler on {}\n", HtmlHelp::hhpFileName);
10125 }
10126 Dir::setCurrent(oldDir);
10127}
10128
10130{
10131 QCString args = Qhp::qhpFileName + " -o \"" + Qhp::getQchFileName() + "\"";
10132 std::string oldDir = Dir::currentDirPath();
10133 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
10134
10135 QCString qhgLocation=Config_getString(QHG_LOCATION);
10136 if (Debug::isFlagSet(Debug::Qhp)) // produce info for debugging
10137 {
10138 // run qhelpgenerator -v and extract the Qt version used
10139 QCString cmd=qhgLocation+ " -v 2>&1";
10140 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
10141 FILE *f=Portable::popen(cmd,"r");
10142 if (!f)
10143 {
10144 err("could not execute {}\n",qhgLocation);
10145 }
10146 else
10147 {
10148 const size_t bufSize = 1024;
10149 char inBuf[bufSize+1];
10150 size_t numRead=fread(inBuf,1,bufSize,f);
10151 inBuf[numRead] = '\0';
10152 Debug::print(Debug::Qhp,0,"{}",inBuf);
10154
10155 int qtVersion=0;
10156 static const reg::Ex versionReg(R"(Qt (\d+)\.(\d+)\.(\d+))");
10157 reg::Match match;
10158 std::string s = inBuf;
10159 if (reg::search(inBuf,match,versionReg))
10160 {
10161 qtVersion = 10000*QCString(match[1].str()).toInt() +
10162 100*QCString(match[2].str()).toInt() +
10163 QCString(match[3].str()).toInt();
10164 }
10165 if (qtVersion>0 && (qtVersion<60000 || qtVersion >= 60205))
10166 {
10167 // dump the output of qhelpgenerator -c file.qhp
10168 // Qt<6 or Qt>=6.2.5 or higher, see https://bugreports.qt.io/browse/QTBUG-101070
10169 cmd=qhgLocation+ " -c " + Qhp::qhpFileName + " 2>&1";
10170 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
10171 f=Portable::popen(cmd,"r");
10172 if (!f)
10173 {
10174 err("could not execute {}\n",qhgLocation);
10175 }
10176 else
10177 {
10178 std::string output;
10179 while ((numRead=fread(inBuf,1,bufSize,f))>0)
10180 {
10181 inBuf[numRead] = '\0';
10182 output += inBuf;
10183 }
10185 Debug::print(Debug::Qhp,0,"{}",output);
10186 }
10187 }
10188 }
10189 }
10190
10191 if (Portable::system(qhgLocation, args, FALSE))
10192 {
10193 err("failed to run qhelpgenerator on {}\n",Qhp::qhpFileName);
10194 }
10195 Dir::setCurrent(oldDir);
10196}
10197
10198//----------------------------------------------------------------------------
10199
10201{
10202 // check dot path
10203 QCString dotPath = Config_getString(DOT_PATH);
10204 if (!dotPath.isEmpty())
10205 {
10206 FileInfo fi(dotPath.str());
10207 if (!(fi.exists() && fi.isFile()) )// not an existing user specified path + exec
10208 {
10209 dotPath = dotPath+"/dot"+Portable::commandExtension();
10210 FileInfo dp(dotPath.str());
10211 if (!dp.exists() || !dp.isFile())
10212 {
10213 warn_uncond("the dot tool could not be found as '{}'\n",dotPath);
10214 dotPath = "dot";
10215 dotPath += Portable::commandExtension();
10216 }
10217 }
10218#if defined(_WIN32) // convert slashes
10219 size_t l=dotPath.length();
10220 for (size_t i=0;i<l;i++) if (dotPath.at(i)=='/') dotPath.at(i)='\\';
10221#endif
10222 }
10223 else
10224 {
10225 dotPath = "dot";
10226 dotPath += Portable::commandExtension();
10227 }
10228 Doxygen::verifiedDotPath = dotPath;
10230}
10231
10232//----------------------------------------------------------------------------
10233
10234/*! Generate a template version of the configuration file.
10235 * If the \a shortList parameter is TRUE a configuration file without
10236 * comments will be generated.
10237 */
10238static void generateConfigFile(const QCString &configFile,bool shortList,
10239 bool updateOnly=FALSE)
10240{
10241 std::ofstream f;
10242 bool fileOpened=openOutputFile(configFile,f);
10243 bool writeToStdout=configFile=="-";
10244 if (fileOpened)
10245 {
10246 TextStream t(&f);
10247 Config::writeTemplate(t,shortList,updateOnly);
10248 if (!writeToStdout)
10249 {
10250 if (!updateOnly)
10251 {
10252 msg("\n\nConfiguration file '{}' created.\n\n",configFile);
10253 msg("Now edit the configuration file and enter\n\n");
10254 if (configFile!="Doxyfile" && configFile!="doxyfile")
10255 msg(" doxygen {}\n\n",configFile);
10256 else
10257 msg(" doxygen\n\n");
10258 msg("to generate the documentation for your project\n\n");
10259 }
10260 else
10261 {
10262 msg("\n\nConfiguration file '{}' updated.\n\n",configFile);
10263 }
10264 }
10265 }
10266 else
10267 {
10268 term("Cannot open file {} for writing\n",configFile);
10269 }
10270}
10271
10273{
10274 std::ofstream f;
10275 bool fileOpened=openOutputFile("-",f);
10276 if (fileOpened)
10277 {
10278 TextStream t(&f);
10279 Config::compareDoxyfile(t,diffList);
10280 }
10281 else
10282 {
10283 term("Cannot open stdout for writing\n");
10284 }
10285}
10286
10287//----------------------------------------------------------------------------
10288// read and parse a tag file
10289
10290static void readTagFile(const std::shared_ptr<Entry> &root,const QCString &tagLine)
10291{
10292 QCString fileName;
10293 QCString destName;
10294 int eqPos = tagLine.find('=');
10295 if (eqPos!=-1) // tag command contains a destination
10296 {
10297 fileName = tagLine.left(eqPos).stripWhiteSpace();
10298 destName = tagLine.right(tagLine.length()-eqPos-1).stripWhiteSpace();
10299 if (fileName.isEmpty() || destName.isEmpty()) return;
10300 //printf("insert tagDestination %s->%s\n",qPrint(fi.fileName()),qPrint(destName));
10301 }
10302 else
10303 {
10304 fileName = tagLine;
10305 }
10306
10307 FileInfo fi(fileName.str());
10308 if (!fi.exists() || !fi.isFile())
10309 {
10310 err("Tag file '{}' does not exist or is not a file. Skipping it...\n",fileName);
10311 return;
10312 }
10313
10314 if (Doxygen::tagFileSet.find(fi.absFilePath().c_str()) != Doxygen::tagFileSet.end()) return;
10315
10316 Doxygen::tagFileSet.emplace(fi.absFilePath());
10317
10318 if (!destName.isEmpty())
10319 {
10320 Doxygen::tagDestinationMap.emplace(fi.absFilePath(), destName.str());
10321 msg("Reading tag file '{}', location '{}'...\n",fileName,destName);
10322 }
10323 else
10324 {
10325 msg("Reading tag file '{}'...\n",fileName);
10326 }
10327
10328 parseTagFile(root,fi.absFilePath().c_str());
10329}
10330
10331//----------------------------------------------------------------------------
10333{
10334 const StringVector &latexExtraStyleSheet = Config_getList(LATEX_EXTRA_STYLESHEET);
10335 for (const auto &sheet : latexExtraStyleSheet)
10336 {
10337 std::string fileName = sheet;
10338 if (!fileName.empty())
10339 {
10340 FileInfo fi(fileName);
10341 if (!fi.exists())
10342 {
10343 err("Style sheet '{}' specified by LATEX_EXTRA_STYLESHEET does not exist!\n",fileName);
10344 }
10345 else if (fi.isDir())
10346 {
10347 err("Style sheet '{}' specified by LATEX_EXTRA_STYLESHEET is a directory, it has to be a file!\n", fileName);
10348 }
10349 else
10350 {
10351 QCString destFileName = Config_getString(LATEX_OUTPUT)+"/"+fi.fileName();
10352 if (!checkExtension(fi.fileName().c_str(), LATEX_STYLE_EXTENSION))
10353 {
10354 destFileName += LATEX_STYLE_EXTENSION;
10355 }
10356 copyFile(QCString(fileName), destFileName);
10357 }
10358 }
10359 }
10360}
10361
10362//----------------------------------------------------------------------------
10363static void copyStyleSheet()
10364{
10365 QCString htmlStyleSheet = Config_getString(HTML_STYLESHEET);
10366 if (!htmlStyleSheet.isEmpty())
10367 {
10368 if (!htmlStyleSheet.startsWith("http:") && !htmlStyleSheet.startsWith("https:"))
10369 {
10370 FileInfo fi(htmlStyleSheet.str());
10371 if (!fi.exists())
10372 {
10373 err("Style sheet '{}' specified by HTML_STYLESHEET does not exist!\n",htmlStyleSheet);
10374 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
10375 }
10376 else if (fi.isDir())
10377 {
10378 err("Style sheet '{}' specified by HTML_STYLESHEET is a directory, it has to be a file!\n",htmlStyleSheet);
10379 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
10380 }
10381 else
10382 {
10383 QCString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
10384 copyFile(htmlStyleSheet,destFileName);
10385 }
10386 }
10387 }
10388 const StringVector &htmlExtraStyleSheet = Config_getList(HTML_EXTRA_STYLESHEET);
10389 for (const auto &sheet : htmlExtraStyleSheet)
10390 {
10391 QCString fileName(sheet);
10392 if (!fileName.isEmpty() && !fileName.startsWith("http:") && !fileName.startsWith("https:"))
10393 {
10394 FileInfo fi(fileName.str());
10395 if (!fi.exists())
10396 {
10397 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET does not exist!\n",fileName);
10398 }
10399 else if (fi.fileName()=="doxygen.css" || fi.fileName()=="tabs.css" || fi.fileName()=="navtree.css")
10400 {
10401 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET is already a built-in stylesheet. Please use a different name\n",fi.fileName());
10402 }
10403 else if (fi.isDir())
10404 {
10405 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET is a directory, it has to be a file!\n",fileName);
10406 }
10407 else
10408 {
10409 QCString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
10410 copyFile(QCString(fileName), destFileName);
10411 }
10412 }
10413 }
10414}
10415
10416static void copyLogo(const QCString &outputOption)
10417{
10418 QCString projectLogo = projectLogoFile();
10419 if (!projectLogo.isEmpty())
10420 {
10421 FileInfo fi(projectLogo.str());
10422 if (!fi.exists())
10423 {
10424 err("Project logo '{}' specified by PROJECT_LOGO does not exist!\n",projectLogo);
10425 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
10426 }
10427 else if (fi.isDir())
10428 {
10429 err("Project logo '{}' specified by PROJECT_LOGO is a directory, it has to be a file!\n",projectLogo);
10430 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
10431 }
10432 else
10433 {
10434 QCString destFileName = outputOption+"/"+fi.fileName();
10435 copyFile(projectLogo,destFileName);
10436 Doxygen::indexList->addImageFile(fi.fileName().c_str());
10437 }
10438 }
10439}
10440
10441static void copyIcon(const QCString &outputOption)
10442{
10443 QCString projectIcon = Config_getString(PROJECT_ICON);
10444 if (!projectIcon.isEmpty())
10445 {
10446 FileInfo fi(projectIcon.str());
10447 if (!fi.exists())
10448 {
10449 err("Project icon '{}' specified by PROJECT_ICON does not exist!\n",projectIcon);
10450 projectIcon = Config_updateString(PROJECT_ICON,""); // revert to the default
10451 }
10452 else if (fi.isDir())
10453 {
10454 err("Project icon '{}' specified by PROJECT_ICON is a directory, it has to be a file!\n",projectIcon);
10455 projectIcon = Config_updateString(PROJECT_ICON,""); // revert to the default
10456 }
10457 else
10458 {
10459 QCString destFileName = outputOption+"/"+fi.fileName();
10460 copyFile(projectIcon,destFileName);
10461 Doxygen::indexList->addImageFile(fi.fileName().c_str());
10462 }
10463 }
10464}
10465
10466static void copyExtraFiles(const StringVector &files,const QCString &filesOption,const QCString &outputOption)
10467{
10468 for (const auto &fileName : files)
10469 {
10470 if (!fileName.empty())
10471 {
10472 FileInfo fi(fileName);
10473 if (!fi.exists())
10474 {
10475 err("Extra file '{}' specified in {} does not exist!\n", fileName,filesOption);
10476 }
10477 else if (fi.isDir())
10478 {
10479 err("Extra file '{}' specified in {} is a directory, it has to be a file!\n", fileName,filesOption);
10480 }
10481 else
10482 {
10483 QCString destFileName = outputOption+"/"+fi.fileName();
10484 Doxygen::indexList->addImageFile(fi.fileName().c_str());
10485 copyFile(QCString(fileName), destFileName);
10486 }
10487 }
10488 }
10489}
10490
10491//----------------------------------------------------------------------------
10492
10494{
10495 for (const auto &fn : *Doxygen::inputNameLinkedMap)
10496 {
10497 struct FileEntry
10498 {
10499 FileEntry(const QCString &p,FileDef *fd) : path(p), fileDef(fd) {}
10500 QCString path;
10501 FileDef *fileDef;
10502 };
10503
10504 // collect the entry for which to compute the longest common prefix (LCP) of the path
10505 std::vector<FileEntry> fileEntries;
10506 for (const auto &fd : *fn)
10507 {
10508 if (!fd->isReference()) // skip external references
10509 {
10510 fileEntries.emplace_back(fd->getPath(),fd.get());
10511 }
10512 }
10513
10514 size_t size = fileEntries.size();
10515
10516 if (size==1) // name if unique, so diskname is simply the name
10517 {
10518 FileDef *fd = fileEntries[0].fileDef;
10519 fd->setDiskName(fn->fileName());
10520 }
10521 else if (size>1) // multiple occurrences of the same file name
10522 {
10523 // sort the array
10524 std::stable_sort(fileEntries.begin(),
10525 fileEntries.end(),
10526 [](const FileEntry &fe1,const FileEntry &fe2)
10527 { return qstricmp_sort(fe1.path,fe2.path)<0; }
10528 );
10529
10530 // since the entries are sorted, the common prefix of the whole array is same
10531 // as the common prefix between the first and last entry
10532 const FileEntry &first = fileEntries[0];
10533 const FileEntry &last = fileEntries[size-1];
10534 int first_path_size = static_cast<int>(first.path.size())-1; // -1 to skip trailing slash
10535 int last_path_size = static_cast<int>(last.path.size())-1; // -1 to skip trailing slash
10536 int j=0;
10537 int i=0;
10538 for (i=0;i<first_path_size && i<last_path_size;i++)
10539 {
10540 if (first.path[i]=='/') j=i;
10541 if (first.path[i]!=last.path[i]) break;
10542 }
10543 if (i==first_path_size && i<last_path_size && last.path[i]=='/')
10544 {
10545 // case first='some/path' and last='some/path/more' => match is 'some/path'
10546 j=first_path_size;
10547 }
10548 else if (i==last_path_size && i<first_path_size && first.path[i]=='/')
10549 {
10550 // case first='some/path/more' and last='some/path' => match is 'some/path'
10551 j=last_path_size;
10552 }
10553
10554 // add non-common part of the path to the name
10555 for (auto &fileEntry : fileEntries)
10556 {
10557 QCString prefix = fileEntry.path.right(fileEntry.path.length()-j-1);
10558 fileEntry.fileDef->setName(prefix+fn->fileName());
10559 //printf("!!!!!!!! non unique disk name=%s:%s\n",qPrint(prefix),fn->fileName());
10560 fileEntry.fileDef->setDiskName(prefix+fn->fileName());
10561 }
10562 }
10563 }
10564}
10565
10566
10567
10568//----------------------------------------------------------------------------
10569
10570static std::unique_ptr<OutlineParserInterface> getParserForFile(const QCString &fn)
10571{
10572 QCString fileName=fn;
10573 QCString extension;
10574 int sep = fileName.findRev('/');
10575 int ei = fileName.findRev('.');
10576 if (ei!=-1 && (sep==-1 || ei>sep)) // matches dir/file.ext but not dir.1/file
10577 {
10578 extension=fileName.right(fileName.length()-ei);
10579 }
10580 else
10581 {
10582 extension = ".no_extension";
10583 }
10584
10585 return Doxygen::parserManager->getOutlineParser(extension);
10586}
10587
10588static std::shared_ptr<Entry> parseFile(OutlineParserInterface &parser,
10589 FileDef *fd,const QCString &fn,
10590 ClangTUParser *clangParser,bool newTU)
10591{
10592 QCString fileName=fn;
10593 AUTO_TRACE("fileName={}",fileName);
10594 QCString extension;
10595 int ei = fileName.findRev('.');
10596 if (ei!=-1)
10597 {
10598 extension=fileName.right(fileName.length()-ei);
10599 }
10600 else
10601 {
10602 extension = ".no_extension";
10603 }
10604
10605 FileInfo fi(fileName.str());
10606 std::string preBuf;
10607
10608 if (Config_getBool(ENABLE_PREPROCESSING) &&
10609 parser.needsPreprocessing(extension))
10610 {
10611 Preprocessor preprocessor;
10612 const StringVector &includePath = Config_getList(INCLUDE_PATH);
10613 for (const auto &s : includePath)
10614 {
10615 std::string absPath = FileInfo(s).absFilePath();
10616 preprocessor.addSearchDir(absPath.c_str());
10617 }
10618 std::string inBuf;
10619 msg("Preprocessing {}...\n",fn);
10620 readInputFile(fileName,inBuf);
10621 addTerminalCharIfMissing(inBuf,'\n');
10622 preprocessor.processFile(fileName,inBuf,preBuf);
10623 }
10624 else // no preprocessing
10625 {
10626 msg("Reading {}...\n",fn);
10627 readInputFile(fileName,preBuf);
10628 addTerminalCharIfMissing(preBuf,'\n');
10629 }
10630
10631 std::string convBuf;
10632 convBuf.reserve(preBuf.size()+1024);
10633
10634 // convert multi-line C++ comments to C style comments
10635 convertCppComments(preBuf,convBuf,fileName.str());
10636
10637 std::shared_ptr<Entry> fileRoot = std::make_shared<Entry>();
10638 // use language parse to parse the file
10639 if (clangParser)
10640 {
10641 if (newTU) clangParser->parse();
10642 clangParser->switchToFile(fd);
10643 }
10644 parser.parseInput(fileName,convBuf.data(),fileRoot,clangParser);
10645 fileRoot->setFileDef(fd);
10646 return fileRoot;
10647}
10648
10649//! parse the list of input files
10650static void parseFilesMultiThreading(const std::shared_ptr<Entry> &root)
10651{
10652 AUTO_TRACE();
10653#if USE_LIBCLANG
10655 {
10656 StringUnorderedSet processedFiles;
10657
10658 // create a dictionary with files to process
10659 StringUnorderedSet filesToProcess;
10660 for (const auto &s : g_inputFiles)
10661 {
10662 filesToProcess.insert(s);
10663 }
10664
10665 std::mutex processedFilesLock;
10666 // process source files (and their include dependencies)
10667 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10668 msg("Processing input using {} threads.\n",numThreads);
10669 ThreadPool threadPool(numThreads);
10670 using FutureType = std::vector< std::shared_ptr<Entry> >;
10671 std::vector< std::future< FutureType > > results;
10672 for (const auto &s : g_inputFiles)
10673 {
10674 bool ambig = false;
10675 FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
10676 ASSERT(fd!=nullptr);
10677 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt::Cpp) // this is a source file
10678 {
10679 // lambda representing the work to executed by a thread
10680 auto processFile = [s,&filesToProcess,&processedFilesLock,&processedFiles]() {
10681 bool ambig_l = false;
10682 std::vector< std::shared_ptr<Entry> > roots;
10683 FileDef *fd_l = findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig_l);
10684 auto clangParser = ClangParser::instance()->createTUParser(fd_l);
10685 auto parser = getParserForFile(s.c_str());
10686 auto fileRoot { parseFile(*parser.get(),fd_l,s.c_str(),clangParser.get(),true) };
10687 roots.push_back(fileRoot);
10688
10689 // Now process any include files in the same translation unit
10690 // first. When libclang is used this is much more efficient.
10691 for (auto incFile : clangParser->filesInSameTU())
10692 {
10693 if (filesToProcess.find(incFile)!=filesToProcess.end())
10694 {
10695 bool needsToBeProcessed = false;
10696 {
10697 std::lock_guard<std::mutex> lock(processedFilesLock);
10698 needsToBeProcessed = processedFiles.find(incFile)==processedFiles.end();
10699 if (needsToBeProcessed) processedFiles.insert(incFile);
10700 }
10701 if (incFile!=s && needsToBeProcessed)
10702 {
10703 FileDef *ifd=findFileDef(Doxygen::inputNameLinkedMap,incFile.c_str(),ambig_l);
10704 if (ifd && !ifd->isReference())
10705 {
10706 //printf(" Processing %s in same translation unit as %s\n",incFile,s->c_str());
10707 fileRoot = parseFile(*parser.get(),ifd,incFile.c_str(),clangParser.get(),false);
10708 roots.push_back(fileRoot);
10709 }
10710 }
10711 }
10712 }
10713 return roots;
10714 };
10715 // dispatch the work and collect the future results
10716 results.emplace_back(threadPool.queue(processFile));
10717 }
10718 }
10719 // synchronize with the Entry result lists produced and add them to the root
10720 for (auto &f : results)
10721 {
10722 auto l = f.get();
10723 for (auto &e : l)
10724 {
10725 root->moveToSubEntryAndKeep(e);
10726 }
10727 }
10728 // process remaining files
10729 results.clear();
10730 for (const auto &s : g_inputFiles)
10731 {
10732 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
10733 {
10734 // lambda representing the work to executed by a thread
10735 auto processFile = [s]() {
10736 bool ambig = false;
10737 std::vector< std::shared_ptr<Entry> > roots;
10738 FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
10739 auto parser { getParserForFile(s.c_str()) };
10740 bool useClang = getLanguageFromFileName(s.c_str())==SrcLangExt::Cpp;
10741 if (useClang)
10742 {
10743 auto clangParser = ClangParser::instance()->createTUParser(fd);
10744 auto fileRoot = parseFile(*parser.get(),fd,s.c_str(),clangParser.get(),true);
10745 roots.push_back(fileRoot);
10746 }
10747 else
10748 {
10749 auto fileRoot = parseFile(*parser.get(),fd,s.c_str(),nullptr,true);
10750 roots.push_back(fileRoot);
10751 }
10752 return roots;
10753 };
10754 results.emplace_back(threadPool.queue(processFile));
10755 }
10756 }
10757 // synchronize with the Entry result lists produced and add them to the root
10758 for (auto &f : results)
10759 {
10760 auto l = f.get();
10761 for (auto &e : l)
10762 {
10763 root->moveToSubEntryAndKeep(e);
10764 }
10765 }
10766 }
10767 else // normal processing
10768#endif
10769 {
10770 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10771 msg("Processing input using {} threads.\n",numThreads);
10772 ThreadPool threadPool(numThreads);
10773 using FutureType = std::shared_ptr<Entry>;
10774 std::vector< std::future< FutureType > > results;
10775 for (const auto &s : g_inputFiles)
10776 {
10777 // lambda representing the work to executed by a thread
10778 auto processFile = [s]() {
10779 bool ambig = false;
10780 FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
10781 auto parser = getParserForFile(s.c_str());
10782 auto fileRoot = parseFile(*parser.get(),fd,s.c_str(),nullptr,true);
10783 return fileRoot;
10784 };
10785 // dispatch the work and collect the future results
10786 results.emplace_back(threadPool.queue(processFile));
10787 }
10788 // synchronize with the Entry results produced and add them to the root
10789 for (auto &f : results)
10790 {
10791 root->moveToSubEntryAndKeep(f.get());
10792 }
10793 }
10794}
10795
10796//! parse the list of input files
10797static void parseFilesSingleThreading(const std::shared_ptr<Entry> &root)
10798{
10799 AUTO_TRACE();
10800#if USE_LIBCLANG
10802 {
10803 StringUnorderedSet processedFiles;
10804
10805 // create a dictionary with files to process
10806 StringUnorderedSet filesToProcess;
10807 for (const auto &s : g_inputFiles)
10808 {
10809 filesToProcess.insert(s);
10810 }
10811
10812 // process source files (and their include dependencies)
10813 for (const auto &s : g_inputFiles)
10814 {
10815 bool ambig = false;
10816 FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
10817 ASSERT(fd!=nullptr);
10818 if (fd->isSource() && !fd->isReference() && getLanguageFromFileName(s.c_str())==SrcLangExt::Cpp) // this is a source file
10819 {
10820 auto clangParser = ClangParser::instance()->createTUParser(fd);
10821 auto parser { getParserForFile(s.c_str()) };
10822 auto fileRoot = parseFile(*parser.get(),fd,s.c_str(),clangParser.get(),true);
10823 root->moveToSubEntryAndKeep(fileRoot);
10824 processedFiles.insert(s);
10825
10826 // Now process any include files in the same translation unit
10827 // first. When libclang is used this is much more efficient.
10828 for (auto incFile : clangParser->filesInSameTU())
10829 {
10830 //printf(" file %s\n",incFile.c_str());
10831 if (filesToProcess.find(incFile)!=filesToProcess.end() && // file need to be processed
10832 processedFiles.find(incFile)==processedFiles.end()) // and is not processed already
10833 {
10834 FileDef *ifd=findFileDef(Doxygen::inputNameLinkedMap,incFile.c_str(),ambig);
10835 if (ifd && !ifd->isReference())
10836 {
10837 //printf(" Processing %s in same translation unit as %s\n",incFile.c_str(),s.c_str());
10838 fileRoot = parseFile(*parser.get(),ifd,incFile.c_str(),clangParser.get(),false);
10839 root->moveToSubEntryAndKeep(fileRoot);
10840 processedFiles.insert(incFile);
10841 }
10842 }
10843 }
10844 }
10845 }
10846 // process remaining files
10847 for (const auto &s : g_inputFiles)
10848 {
10849 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
10850 {
10851 bool ambig = false;
10852 FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
10853 if (getLanguageFromFileName(s.c_str())==SrcLangExt::Cpp) // not yet processed
10854 {
10855 auto clangParser = ClangParser::instance()->createTUParser(fd);
10856 auto parser { getParserForFile(s.c_str()) };
10857 auto fileRoot = parseFile(*parser.get(),fd,s.c_str(),clangParser.get(),true);
10858 root->moveToSubEntryAndKeep(fileRoot);
10859 }
10860 else
10861 {
10862 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(s.c_str()) };
10863 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,s.c_str(),nullptr,true);
10864 root->moveToSubEntryAndKeep(fileRoot);
10865 }
10866 processedFiles.insert(s);
10867 }
10868 }
10869 }
10870 else // normal processing
10871#endif
10872 {
10873 for (const auto &s : g_inputFiles)
10874 {
10875 bool ambig = false;
10876 FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
10877 ASSERT(fd!=nullptr);
10878 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(s.c_str()) };
10879 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,s.c_str(),nullptr,true);
10880 root->moveToSubEntryAndKeep(std::move(fileRoot));
10881 }
10882 }
10883}
10884
10885// resolves a path that may include symlinks, if a recursive symlink is
10886// found an empty string is returned.
10887static std::string resolveSymlink(const std::string &path)
10888{
10889 int sepPos=0;
10890 int oldPos=0;
10891 StringUnorderedSet nonSymlinks;
10892 StringUnorderedSet known;
10893 QCString result(path);
10894 QCString oldPrefix = "/";
10895 do
10896 {
10897#if defined(_WIN32)
10898 // UNC path, skip server and share name
10899 if (sepPos==0 && (result.startsWith("//") || result.startsWith("\\\\")))
10900 sepPos = result.find('/',2);
10901 if (sepPos!=-1)
10902 sepPos = result.find('/',sepPos+1);
10903#else
10904 sepPos = result.find('/',sepPos+1);
10905#endif
10906 QCString prefix = sepPos==-1 ? result : result.left(sepPos);
10907 if (nonSymlinks.find(prefix.str())==nonSymlinks.end())
10908 {
10909 FileInfo fi(prefix.str());
10910 if (fi.isSymLink())
10911 {
10912 QCString target = fi.readLink();
10913 bool isRelative = FileInfo(target.str()).isRelative();
10914 if (isRelative)
10915 {
10916 target = Dir::cleanDirPath(oldPrefix.str()+"/"+target.str());
10917 }
10918 if (sepPos!=-1)
10919 {
10920 if (fi.isDir() && target.length()>0 && target.at(target.length()-1)!='/')
10921 {
10922 target+='/';
10923 }
10924 target+=result.mid(sepPos);
10925 }
10926 result = Dir::cleanDirPath(target.str());
10927 if (known.find(result.str())!=known.end()) return std::string(); // recursive symlink!
10928 known.insert(result.str());
10929 if (isRelative)
10930 {
10931 sepPos = oldPos;
10932 }
10933 else // link to absolute path
10934 {
10935 sepPos = 0;
10936 oldPrefix = "/";
10937 }
10938 }
10939 else
10940 {
10941 nonSymlinks.insert(prefix.str());
10942 oldPrefix = prefix;
10943 }
10944 oldPos = sepPos;
10945 }
10946 }
10947 while (sepPos!=-1);
10948 return Dir::cleanDirPath(result.str());
10949}
10950
10952
10953//----------------------------------------------------------------------------
10954// Read all files matching at least one pattern in 'patList' in the
10955// directory represented by 'fi'.
10956// The directory is read iff the recursiveFlag is set.
10957// The contents of all files is append to the input string
10958
10959static void readDir(FileInfo *fi,
10960 FileNameLinkedMap *fnMap,
10961 StringUnorderedSet *exclSet,
10962 const StringVector *patList,
10963 const StringVector *exclPatList,
10964 StringVector *resultList,
10965 StringUnorderedSet *resultSet,
10966 bool errorIfNotExist,
10967 bool recursive,
10968 StringUnorderedSet *killSet,
10969 StringUnorderedSet *paths
10970 )
10971{
10972 std::string dirName = fi->absFilePath();
10973 if (paths && !dirName.empty())
10974 {
10975 paths->insert(dirName);
10976 }
10977 //printf("%s isSymLink()=%d\n",qPrint(dirName),fi->isSymLink());
10978 if (fi->isSymLink())
10979 {
10980 dirName = resolveSymlink(dirName);
10981 if (dirName.empty())
10982 {
10983 //printf("RECURSIVE SYMLINK: %s\n",qPrint(dirName));
10984 return; // recursive symlink
10985 }
10986 }
10987
10988 if (g_pathsVisited.find(dirName)!=g_pathsVisited.end())
10989 {
10990 //printf("PATH ALREADY VISITED: %s\n",qPrint(dirName));
10991 return; // already visited path
10992 }
10993 g_pathsVisited.insert(dirName);
10994
10995 Dir dir(dirName);
10996 msg("Searching for files in directory {}\n", fi->absFilePath());
10997 //printf("killSet=%p count=%d\n",killSet,killSet ? (int)killSet->count() : -1);
10998
10999 StringVector dirResultList;
11000
11001 for (const auto &dirEntry : dir.iterator())
11002 {
11003 FileInfo cfi(dirEntry.path());
11004 if (exclSet==nullptr || exclSet->find(cfi.absFilePath())==exclSet->end())
11005 { // file should not be excluded
11006 //printf("killSet->find(%s)\n",qPrint(cfi->absFilePath()));
11007 if (Config_getBool(EXCLUDE_SYMLINKS) && cfi.isSymLink())
11008 {
11009 }
11010 else if (!cfi.exists() || !cfi.isReadable())
11011 {
11012 if (errorIfNotExist)
11013 {
11014 warn_uncond("source '{}' is not a readable file or directory... skipping.\n",cfi.absFilePath());
11015 }
11016 }
11017 else if (cfi.isFile() &&
11018 (patList==nullptr || patternMatch(cfi,*patList)) &&
11019 (exclPatList==nullptr || !patternMatch(cfi,*exclPatList)) &&
11020 (killSet==nullptr || killSet->find(cfi.absFilePath())==killSet->end())
11021 )
11022 {
11023 std::string name=cfi.fileName();
11024 std::string path=cfi.dirPath()+"/";
11025 std::string fullName=path+name;
11026 if (fnMap)
11027 {
11028 auto fd = createFileDef(QCString(path),QCString(name));
11029 FileName *fn=nullptr;
11030 if (!name.empty())
11031 {
11032 fn = fnMap->add(QCString(name),QCString(fullName));
11033 fn->push_back(std::move(fd));
11034 }
11035 }
11036 dirResultList.push_back(fullName);
11037 if (resultSet) resultSet->insert(fullName);
11038 if (killSet) killSet->insert(fullName);
11039 }
11040 else if (recursive &&
11041 cfi.isDir() &&
11042 (exclPatList==nullptr || !patternMatch(cfi,*exclPatList)) &&
11043 cfi.fileName().at(0)!='.') // skip "." ".." and ".dir"
11044 {
11045 FileInfo acfi(cfi.absFilePath());
11046 readDir(&acfi,fnMap,exclSet,
11047 patList,exclPatList,&dirResultList,resultSet,errorIfNotExist,
11048 recursive,killSet,paths);
11049 }
11050 }
11051 }
11052 if (resultList && !dirResultList.empty())
11053 {
11054 // sort the resulting list to make the order platform independent.
11055 std::stable_sort(dirResultList.begin(),
11056 dirResultList.end(),
11057 [](const auto &f1,const auto &f2) { return qstricmp_sort(f1.c_str(),f2.c_str())<0; });
11058
11059 // append the sorted results to resultList
11060 resultList->insert(resultList->end(), dirResultList.begin(), dirResultList.end());
11061 }
11062}
11063
11064
11065//----------------------------------------------------------------------------
11066// read a file or all files in a directory and append their contents to the
11067// input string. The names of the files are appended to the 'fiList' list.
11068
11070 FileNameLinkedMap *fnMap,
11071 StringUnorderedSet *exclSet,
11072 const StringVector *patList,
11073 const StringVector *exclPatList,
11074 StringVector *resultList,
11075 StringUnorderedSet *resultSet,
11076 bool recursive,
11077 bool errorIfNotExist,
11078 StringUnorderedSet *killSet,
11079 StringUnorderedSet *paths
11080 )
11081{
11082 //printf("killSet count=%d\n",killSet ? (int)killSet->size() : -1);
11083 // strip trailing slashes
11084 if (s.isEmpty()) return;
11085
11086 g_pathsVisited.clear();
11087
11088 FileInfo fi(s.str());
11089 //printf("readFileOrDirectory(%s)\n",s);
11090 {
11091 if (exclSet==nullptr || exclSet->find(fi.absFilePath())==exclSet->end())
11092 {
11093 if (Config_getBool(EXCLUDE_SYMLINKS) && fi.isSymLink())
11094 {
11095 }
11096 else if (!fi.exists() || !fi.isReadable())
11097 {
11098 if (errorIfNotExist)
11099 {
11100 warn_uncond("source '{}' is not a readable file or directory... skipping.\n",s);
11101 }
11102 }
11103 else if (fi.isFile())
11104 {
11105 std::string dirPath = fi.dirPath(true);
11106 std::string filePath = fi.absFilePath();
11107 if (paths && !dirPath.empty())
11108 {
11109 paths->insert(dirPath);
11110 }
11111 //printf("killSet.find(%s)=%d\n",qPrint(fi.absFilePath()),killSet.find(fi.absFilePath())!=killSet.end());
11112 if (killSet==nullptr || killSet->find(filePath)==killSet->end())
11113 {
11114 std::string name=fi.fileName();
11115 if (fnMap)
11116 {
11117 auto fd = createFileDef(QCString(dirPath+"/"),QCString(name));
11118 if (!name.empty())
11119 {
11120 FileName *fn = fnMap->add(QCString(name),QCString(filePath));
11121 fn->push_back(std::move(fd));
11122 }
11123 }
11124 if (resultList || resultSet)
11125 {
11126 if (resultList) resultList->push_back(filePath);
11127 if (resultSet) resultSet->insert(filePath);
11128 }
11129
11130 if (killSet) killSet->insert(fi.absFilePath());
11131 }
11132 }
11133 else if (fi.isDir()) // readable dir
11134 {
11135 readDir(&fi,fnMap,exclSet,patList,
11136 exclPatList,resultList,resultSet,errorIfNotExist,
11137 recursive,killSet,paths);
11138 }
11139 }
11140 }
11141}
11142
11143//----------------------------------------------------------------------------
11144
11146{
11147 QCString anchor;
11149 {
11150 MemberDef *md = toMemberDef(d);
11151 anchor=":"+md->anchor();
11152 }
11153 QCString scope;
11154 QCString fn = d->getOutputFileBase();
11157 {
11158 scope = fn;
11159 }
11160 t << "REPLACE INTO symbols (symbol_id,scope_id,name,file,line) VALUES('"
11161 << fn+anchor << "','"
11162 << scope << "','"
11163 << d->name() << "','"
11164 << d->getDefFileName() << "','"
11165 << d->getDefLine()
11166 << "');\n";
11167}
11168
11169static void dumpSymbolMap()
11170{
11171 std::ofstream f = Portable::openOutputStream("symbols.sql");
11172 if (f.is_open())
11173 {
11174 TextStream t(&f);
11175 for (const auto &[name,symList] : *Doxygen::symbolMap)
11176 {
11177 for (const auto &def : symList)
11178 {
11179 dumpSymbol(t,def);
11180 }
11181 }
11182 }
11183}
11184
11185// print developer options of Doxygen
11186static void devUsage()
11187{
11189 msg("Developer parameters:\n");
11190 msg(" -m dump symbol map\n");
11191 msg(" -b making messages output unbuffered\n");
11192 msg(" -c <file> process input file as a comment block and produce HTML output\n");
11193#if ENABLE_TRACING
11194 msg(" -t [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout)\n");
11195 msg(" -t_time [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout),\n"
11196 " and include time and thread information\n");
11197#endif
11198 msg(" -d <level> enable a debug level, such as (multiple invocations of -d are possible):\n");
11200}
11201
11202
11203//----------------------------------------------------------------------------
11204// print the version of Doxygen
11205
11206static void version(const bool extended)
11207{
11209 QCString versionString = getFullVersion();
11210 msg("{}\n",versionString);
11211 if (extended)
11212 {
11213 QCString extVers;
11214 if (!extVers.isEmpty()) extVers+= ", ";
11215 extVers += "sqlite3 ";
11216 extVers += sqlite3_libversion();
11217#if USE_LIBCLANG
11218 if (!extVers.isEmpty()) extVers+= ", ";
11219 extVers += "clang support ";
11220 extVers += CLANG_VERSION_STRING;
11221#endif
11222 if (!extVers.isEmpty())
11223 {
11224 int lastComma = extVers.findRev(',');
11225 if (lastComma != -1) extVers = extVers.replace(lastComma,1," and");
11226 msg(" with {}.\n",extVers);
11227 }
11228 }
11229}
11230
11231//----------------------------------------------------------------------------
11232// print the usage of Doxygen
11233
11234static void usage(const QCString &name,const QCString &versionString)
11235{
11237 msg("Doxygen version {0}\nCopyright Dimitri van Heesch 1997-2025\n\n"
11238 "You can use Doxygen in a number of ways:\n\n"
11239 "1) Use Doxygen to generate a template configuration file*:\n"
11240 " {1} [-s] -g [configName]\n\n"
11241 "2) Use Doxygen to update an old configuration file*:\n"
11242 " {1} [-s] -u [configName]\n\n"
11243 "3) Use Doxygen to generate documentation using an existing "
11244 "configuration file*:\n"
11245 " {1} [configName]\n\n"
11246 "4) Use Doxygen to generate a template file controlling the layout of the\n"
11247 " generated documentation:\n"
11248 " {1} -l [layoutFileName]\n\n"
11249 " In case layoutFileName is omitted DoxygenLayout.xml will be used as filename.\n"
11250 " If - is used for layoutFileName Doxygen will write to standard output.\n\n"
11251 "5) Use Doxygen to generate a template style sheet file for RTF, HTML or Latex.\n"
11252 " RTF: {1} -w rtf styleSheetFile\n"
11253 " HTML: {1}-w html headerFile footerFile styleSheetFile [configFile]\n"
11254 " LaTeX: {1} -w latex headerFile footerFile styleSheetFile [configFile]\n\n"
11255 "6) Use Doxygen to generate a rtf extensions file\n"
11256 " {1} -e rtf extensionsFile\n\n"
11257 " If - is used for extensionsFile Doxygen will write to standard output.\n\n"
11258 "7) Use Doxygen to compare the used configuration file with the template configuration file\n"
11259 " {1} -x [configFile]\n\n"
11260 " Use Doxygen to compare the used configuration file with the template configuration file\n"
11261 " without replacing the environment variables or CMake type replacement variables\n"
11262 " {1} -x_noenv [configFile]\n\n"
11263 "8) Use Doxygen to show a list of built-in emojis.\n"
11264 " {1} -f emoji outputFileName\n\n"
11265 " If - is used for outputFileName Doxygen will write to standard output.\n\n"
11266 "*) If -s is specified the comments of the configuration items in the config file will be omitted.\n"
11267 " If configName is omitted 'Doxyfile' will be used as a default.\n"
11268 " If - is used for configFile Doxygen will write / read the configuration to /from standard output / input.\n\n"
11269 "If -q is used for a Doxygen documentation run, Doxygen will see this as if QUIET=YES has been set.\n\n"
11270 "-v print version string, -V print extended version information\n"
11271 "-h,-? prints usage help information\n"
11272 "{1} -d prints additional usage flags for debugging purposes\n",versionString,name);
11273}
11274
11275//----------------------------------------------------------------------------
11276// read the argument of option 'c' from the comment argument list and
11277// update the option index 'optInd'.
11278
11279static const char *getArg(int argc,char **argv,int &optInd)
11280{
11281 char *s=nullptr;
11282 if (qstrlen(&argv[optInd][2])>0)
11283 s=&argv[optInd][2];
11284 else if (optInd+1<argc && argv[optInd+1][0]!='-')
11285 s=argv[++optInd];
11286 return s;
11287}
11288
11289//----------------------------------------------------------------------------
11290
11291/** @brief /dev/null outline parser */
11293{
11294 public:
11295 void parseInput(const QCString &/* file */, const char * /* buf */,const std::shared_ptr<Entry> &, ClangTUParser*) override {}
11296 bool needsPreprocessing(const QCString &) const override { return FALSE; }
11297 void parsePrototype(const QCString &) override {}
11298};
11299
11300
11301template<class T> std::function< std::unique_ptr<T>() > make_parser_factory()
11302{
11303 return []() { return std::make_unique<T>(); };
11304}
11305
11307{
11308 initResources();
11309 QCString lang = Portable::getenv("LC_ALL");
11310 if (!lang.isEmpty()) Portable::setenv("LANG",lang);
11311 std::setlocale(LC_ALL,"");
11312 std::setlocale(LC_CTYPE,"C"); // to get isspace(0xA0)==0, needed for UTF-8
11313 std::setlocale(LC_NUMERIC,"C");
11314
11316
11340
11341 // register any additional parsers here...
11342
11344
11345#if USE_LIBCLANG
11347#endif
11356 Doxygen::pageLinkedMap = new PageLinkedMap; // all doc pages
11357 Doxygen::exampleLinkedMap = new PageLinkedMap; // all examples
11358 //Doxygen::tagDestinationDict.setAutoDelete(TRUE);
11360
11361 // initialization of these globals depends on
11362 // configuration switches so we need to postpone these
11363 Doxygen::globalScope = nullptr;
11372
11373}
11374
11406
11407static int computeIdealCacheParam(size_t v)
11408{
11409 //printf("computeIdealCacheParam(v=%u)\n",v);
11410
11411 int r=0;
11412 while (v!=0) v>>=1,r++;
11413 // r = log2(v)
11414
11415 // convert to a valid cache size value
11416 return std::max(0,std::min(r-16,9));
11417}
11418
11419void readConfiguration(int argc, char **argv)
11420{
11421 QCString versionString = getFullVersion();
11422
11423 // helper that calls \a func to write to file \a fileName via a TextStream
11424 auto writeFile = [](const char *fileName,std::function<void(TextStream&)> func) -> bool
11425 {
11426 std::ofstream f;
11427 if (openOutputFile(fileName,f))
11428 {
11429 TextStream t(&f);
11430 func(t);
11431 return true;
11432 }
11433 return false;
11434 };
11435
11436
11437 /**************************************************************************
11438 * Handle arguments *
11439 **************************************************************************/
11440
11441 int optInd=1;
11442 QCString configName;
11443 QCString traceName;
11444 bool genConfig=false;
11445 bool shortList=false;
11446 bool traceTiming=false;
11448 bool updateConfig=false;
11449 bool quiet = false;
11450 while (optInd<argc && argv[optInd][0]=='-' &&
11451 (isalpha(argv[optInd][1]) || argv[optInd][1]=='?' ||
11452 argv[optInd][1]=='-')
11453 )
11454 {
11455 switch(argv[optInd][1])
11456 {
11457 case 'g':
11458 {
11459 genConfig=TRUE;
11460 }
11461 break;
11462 case 'l':
11463 {
11464 QCString layoutName;
11465 if (optInd+1>=argc)
11466 {
11467 layoutName="DoxygenLayout.xml";
11468 }
11469 else
11470 {
11471 layoutName=argv[optInd+1];
11472 }
11473 writeDefaultLayoutFile(layoutName);
11475 exit(0);
11476 }
11477 break;
11478 case 'c':
11479 if (optInd+1>=argc) // no file name given
11480 {
11481 err("option \"-c\" is missing the file name to read\n");
11482 devUsage();
11484 exit(1);
11485 }
11486 else
11487 {
11488 g_commentFileName=argv[optInd+1];
11489 optInd++;
11490 }
11491 g_singleComment=true;
11492 quiet=true;
11493 break;
11494 case 'd':
11495 {
11496 QCString debugLabel=getArg(argc,argv,optInd);
11497 if (debugLabel.isEmpty())
11498 {
11499 devUsage();
11501 exit(0);
11502 }
11503 int retVal = Debug::setFlagStr(debugLabel);
11504 if (!retVal)
11505 {
11506 err("option \"-d\" has unknown debug specifier: \"{}\".\n",debugLabel);
11507 devUsage();
11509 exit(1);
11510 }
11511 }
11512 break;
11513 case 't':
11514 {
11515#if ENABLE_TRACING
11516 if (!strcmp(argv[optInd]+1,"t_time"))
11517 {
11518 traceTiming = true;
11519 }
11520 else if (!strcmp(argv[optInd]+1,"t"))
11521 {
11522 traceTiming = false;
11523 }
11524 else
11525 {
11526 err("option should be \"-t\" or \"-t_time\", found: \"{}\".\n",argv[optInd]);
11528 exit(1);
11529 }
11530 if (optInd+1>=argc || argv[optInd+1][0] == '-') // no file name given
11531 {
11532 traceName="stdout";
11533 }
11534 else
11535 {
11536 traceName=argv[optInd+1];
11537 optInd++;
11538 }
11539#else
11540 err("support for option \"-t\" has not been compiled in (use a debug build or a release build with tracing enabled).\n");
11542 exit(1);
11543#endif
11544 }
11545 break;
11546 case 'x':
11547 if (!strcmp(argv[optInd]+1,"x_noenv")) diffList=Config::CompareMode::CompressedNoEnv;
11548 else if (!strcmp(argv[optInd]+1,"x")) diffList=Config::CompareMode::Compressed;
11549 else
11550 {
11551 err("option should be \"-x\" or \"-x_noenv\", found: \"{}\".\n",argv[optInd]);
11553 exit(1);
11554 }
11555 break;
11556 case 's':
11557 shortList=TRUE;
11558 break;
11559 case 'u':
11560 updateConfig=TRUE;
11561 break;
11562 case 'e':
11563 {
11564 QCString formatName=getArg(argc,argv,optInd);
11565 if (formatName.isEmpty())
11566 {
11567 err("option \"-e\" is missing format specifier rtf.\n");
11569 exit(1);
11570 }
11571 if (qstricmp(formatName.data(),"rtf")==0)
11572 {
11573 if (optInd+1>=argc)
11574 {
11575 err("option \"-e rtf\" is missing an extensions file name\n");
11577 exit(1);
11578 }
11579 writeFile(argv[optInd+1],RTFGenerator::writeExtensionsFile);
11581 exit(0);
11582 }
11583 err("option \"-e\" has invalid format specifier.\n");
11585 exit(1);
11586 }
11587 break;
11588 case 'f':
11589 {
11590 QCString listName=getArg(argc,argv,optInd);
11591 if (listName.isEmpty())
11592 {
11593 err("option \"-f\" is missing list specifier.\n");
11595 exit(1);
11596 }
11597 if (qstricmp(listName.data(),"emoji")==0)
11598 {
11599 if (optInd+1>=argc)
11600 {
11601 err("option \"-f emoji\" is missing an output file name\n");
11603 exit(1);
11604 }
11605 writeFile(argv[optInd+1],[](TextStream &t) { EmojiEntityMapper::instance().writeEmojiFile(t); });
11607 exit(0);
11608 }
11609 err("option \"-f\" has invalid list specifier.\n");
11611 exit(1);
11612 }
11613 break;
11614 case 'w':
11615 {
11616 QCString formatName=getArg(argc,argv,optInd);
11617 if (formatName.isEmpty())
11618 {
11619 err("option \"-w\" is missing format specifier rtf, html or latex\n");
11621 exit(1);
11622 }
11623 if (qstricmp(formatName.data(),"rtf")==0)
11624 {
11625 if (optInd+1>=argc)
11626 {
11627 err("option \"-w rtf\" is missing a style sheet file name\n");
11629 exit(1);
11630 }
11631 if (!writeFile(argv[optInd+1],RTFGenerator::writeStyleSheetFile))
11632 {
11633 err("error opening RTF style sheet file {}!\n",argv[optInd+1]);
11635 exit(1);
11636 }
11638 exit(0);
11639 }
11640 else if (qstricmp(formatName.data(),"html")==0)
11641 {
11642 Config::init();
11643 if (optInd+4<argc || FileInfo("Doxyfile").exists() || FileInfo("doxyfile").exists())
11644 // explicit config file mentioned or default found on disk
11645 {
11646 QCString df = optInd+4<argc ? argv[optInd+4] : (FileInfo("Doxyfile").exists() ? QCString("Doxyfile") : QCString("doxyfile"));
11647 if (!Config::parse(df)) // parse the config file
11648 {
11649 err("error opening or reading configuration file {}!\n",argv[optInd+4]);
11651 exit(1);
11652 }
11653 }
11654 if (optInd+3>=argc)
11655 {
11656 err("option \"-w html\" does not have enough arguments\n");
11658 exit(1);
11659 }
11663 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11664 writeFile(argv[optInd+1],[&](TextStream &t) { HtmlGenerator::writeHeaderFile(t,argv[optInd+3]); });
11665 writeFile(argv[optInd+2],HtmlGenerator::writeFooterFile);
11666 writeFile(argv[optInd+3],HtmlGenerator::writeStyleSheetFile);
11668 exit(0);
11669 }
11670 else if (qstricmp(formatName.data(),"latex")==0)
11671 {
11672 Config::init();
11673 if (optInd+4<argc || FileInfo("Doxyfile").exists() || FileInfo("doxyfile").exists())
11674 {
11675 QCString df = optInd+4<argc ? argv[optInd+4] : (FileInfo("Doxyfile").exists() ? QCString("Doxyfile") : QCString("doxyfile"));
11676 if (!Config::parse(df))
11677 {
11678 err("error opening or reading configuration file {}!\n",argv[optInd+4]);
11680 exit(1);
11681 }
11682 }
11683 if (optInd+3>=argc)
11684 {
11685 err("option \"-w latex\" does not have enough arguments\n");
11687 exit(1);
11688 }
11692 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11693 writeFile(argv[optInd+1],LatexGenerator::writeHeaderFile);
11694 writeFile(argv[optInd+2],LatexGenerator::writeFooterFile);
11695 writeFile(argv[optInd+3],LatexGenerator::writeStyleSheetFile);
11697 exit(0);
11698 }
11699 else
11700 {
11701 err("Illegal format specifier \"{}\": should be one of rtf, html or latex\n",formatName);
11703 exit(1);
11704 }
11705 }
11706 break;
11707 case 'm':
11709 break;
11710 case 'v':
11711 version(false);
11713 exit(0);
11714 break;
11715 case 'V':
11716 version(true);
11718 exit(0);
11719 break;
11720 case '-':
11721 if (qstrcmp(&argv[optInd][2],"help")==0)
11722 {
11723 usage(argv[0],versionString);
11724 exit(0);
11725 }
11726 else if (qstrcmp(&argv[optInd][2],"version")==0)
11727 {
11728 version(false);
11730 exit(0);
11731 }
11732 else if ((qstrcmp(&argv[optInd][2],"Version")==0) ||
11733 (qstrcmp(&argv[optInd][2],"VERSION")==0))
11734 {
11735 version(true);
11737 exit(0);
11738 }
11739 else
11740 {
11741 err("Unknown option \"-{}\"\n",&argv[optInd][1]);
11742 usage(argv[0],versionString);
11743 exit(1);
11744 }
11745 break;
11746 case 'b':
11747 setvbuf(stdout,nullptr,_IONBF,0);
11748 break;
11749 case 'q':
11750 quiet = true;
11751 break;
11752 case 'h':
11753 case '?':
11754 usage(argv[0],versionString);
11755 exit(0);
11756 break;
11757 default:
11758 err("Unknown option \"-{:c}\"\n",argv[optInd][1]);
11759 usage(argv[0],versionString);
11760 exit(1);
11761 }
11762 optInd++;
11763 }
11764
11765 /**************************************************************************
11766 * Parse or generate the config file *
11767 **************************************************************************/
11768
11769 initTracing(traceName.data(),traceTiming);
11770 TRACE("Doxygen version used: {}",getFullVersion());
11771 Config::init();
11772
11773 FileInfo configFileInfo1("Doxyfile"),configFileInfo2("doxyfile");
11774 if (optInd>=argc)
11775 {
11776 if (configFileInfo1.exists())
11777 {
11778 configName="Doxyfile";
11779 }
11780 else if (configFileInfo2.exists())
11781 {
11782 configName="doxyfile";
11783 }
11784 else if (genConfig)
11785 {
11786 configName="Doxyfile";
11787 }
11788 else
11789 {
11790 err("Doxyfile not found and no input file specified!\n");
11791 usage(argv[0],versionString);
11792 exit(1);
11793 }
11794 }
11795 else
11796 {
11797 FileInfo fi(argv[optInd]);
11798 if (fi.exists() || qstrcmp(argv[optInd],"-")==0 || genConfig)
11799 {
11800 configName=argv[optInd];
11801 }
11802 else
11803 {
11804 err("configuration file {} not found!\n",argv[optInd]);
11805 usage(argv[0],versionString);
11806 exit(1);
11807 }
11808 }
11809
11810 if (genConfig)
11811 {
11812 generateConfigFile(configName,shortList);
11814 exit(0);
11815 }
11816
11817 if (!Config::parse(configName,updateConfig,diffList))
11818 {
11819 err("could not open or read configuration file {}!\n",configName);
11821 exit(1);
11822 }
11823
11824 if (diffList!=Config::CompareMode::Full)
11825 {
11827 compareDoxyfile(diffList);
11829 exit(0);
11830 }
11831
11832 if (updateConfig)
11833 {
11835 generateConfigFile(configName,shortList,TRUE);
11837 exit(0);
11838 }
11839
11840 /* Perlmod wants to know the path to the config file.*/
11841 FileInfo configFileInfo(configName.str());
11842 setPerlModDoxyfile(configFileInfo.absFilePath());
11843
11844 /* handle -q option */
11845 if (quiet) Config_updateBool(QUIET,TRUE);
11846}
11847
11848/** check and resolve config options */
11850{
11851 AUTO_TRACE();
11852
11857}
11858
11859/** adjust globals that depend on configuration settings. */
11861{
11862 AUTO_TRACE();
11863 Doxygen::globalNamespaceDef = createNamespaceDef("<globalScope>",1,1,"<globalScope>");
11873
11874 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11875
11876 /* Set the global html file extension. */
11877 Doxygen::htmlFileExtension = Config_getString(HTML_FILE_EXTENSION);
11878
11879
11881 Config_getBool(CALLER_GRAPH) ||
11882 Config_getBool(REFERENCES_RELATION) ||
11883 Config_getBool(REFERENCED_BY_RELATION);
11884
11885 /**************************************************************************
11886 * Add custom extension mappings
11887 **************************************************************************/
11888
11889 const StringVector &extMaps = Config_getList(EXTENSION_MAPPING);
11890 for (const auto &mapping : extMaps)
11891 {
11892 QCString mapStr = mapping.c_str();
11893 int i=mapStr.find('=');
11894 if (i==-1)
11895 {
11896 continue;
11897 }
11898 else
11899 {
11900 QCString ext = mapStr.left(i).stripWhiteSpace().lower();
11901 QCString language = mapStr.mid(i+1).stripWhiteSpace().lower();
11902 if (ext.isEmpty() || language.isEmpty())
11903 {
11904 continue;
11905 }
11906
11907 if (!updateLanguageMapping(ext,language))
11908 {
11909 err("Failed to map file extension '{}' to unsupported language '{}'.\n"
11910 "Check the EXTENSION_MAPPING setting in the config file.\n",
11911 ext,language);
11912 }
11913 else
11914 {
11915 msg("Adding custom extension mapping: '{}' will be treated as language '{}'\n",
11916 ext,language);
11917 }
11918 }
11919 }
11920 // create input file exncodings
11921
11922 // check INPUT_ENCODING
11923 void *cd = portable_iconv_open("UTF-8",Config_getString(INPUT_ENCODING).data());
11924 if (cd==reinterpret_cast<void *>(-1))
11925 {
11926 term("unsupported character conversion: '{}'->'UTF-8': {}\n"
11927 "Check the 'INPUT_ENCODING' setting in the config file!\n",
11928 Config_getString(INPUT_ENCODING),strerror(errno));
11929 }
11930 else
11931 {
11933 }
11934
11935 // check and split INPUT_FILE_ENCODING
11936 const StringVector &fileEncod = Config_getList(INPUT_FILE_ENCODING);
11937 for (const auto &mapping : fileEncod)
11938 {
11939 QCString mapStr = mapping.c_str();
11940 int i=mapStr.find('=');
11941 if (i==-1)
11942 {
11943 continue;
11944 }
11945 else
11946 {
11947 QCString pattern = mapStr.left(i).stripWhiteSpace().lower();
11948 QCString encoding = mapStr.mid(i+1).stripWhiteSpace().lower();
11949 if (pattern.isEmpty() || encoding.isEmpty())
11950 {
11951 continue;
11952 }
11953 cd = portable_iconv_open("UTF-8",encoding.data());
11954 if (cd==reinterpret_cast<void *>(-1))
11955 {
11956 term("unsupported character conversion: '{}'->'UTF-8': {}\n"
11957 "Check the 'INPUT_FILE_ENCODING' setting in the config file!\n",
11958 encoding,strerror(errno));
11959 }
11960 else
11961 {
11963 }
11964
11965 Doxygen::inputFileEncodingList.emplace_back(pattern, encoding);
11966 }
11967 }
11968
11969 // add predefined macro name to a dictionary
11970 const StringVector &expandAsDefinedList =Config_getList(EXPAND_AS_DEFINED);
11971 for (const auto &s : expandAsDefinedList)
11972 {
11973 Doxygen::expandAsDefinedSet.insert(s.c_str());
11974 }
11975
11976 // read aliases and store them in a dictionary
11977 readAliases();
11978
11979 // store number of spaces in a tab into Doxygen::spaces
11980 int tabSize = Config_getInt(TAB_SIZE);
11981 Doxygen::spaces.resize(tabSize);
11982 for (int sp=0; sp<tabSize; sp++) Doxygen::spaces.at(sp)=' ';
11983 Doxygen::spaces.at(tabSize)='\0';
11984}
11985
11986#ifdef HAS_SIGNALS
11987static void stopDoxygen(int)
11988{
11989 signal(SIGINT,SIG_DFL); // Re-register signal handler for default action
11990 Dir thisDir;
11991 msg("Cleaning up...\n");
11992 if (!Doxygen::filterDBFileName.isEmpty())
11993 {
11994 thisDir.remove(Doxygen::filterDBFileName.str());
11995 }
11996 killpg(0,SIGINT);
11998 exitTracing();
11999 exit(1);
12000}
12001#endif
12002
12003static void writeTagFile()
12004{
12005 QCString generateTagFile = Config_getString(GENERATE_TAGFILE);
12006 if (generateTagFile.isEmpty()) return;
12007
12008 std::ofstream f = Portable::openOutputStream(generateTagFile);
12009 if (!f.is_open())
12010 {
12011 err("cannot open tag file {} for writing\n", generateTagFile);
12012 return;
12013 }
12014 TextStream tagFile(&f);
12015 tagFile << "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>\n";
12016 tagFile << "<tagfile doxygen_version=\"" << getDoxygenVersion() << "\"";
12017 std::string gitVersion = getGitVersion();
12018 if (!gitVersion.empty())
12019 {
12020 tagFile << " doxygen_gitid=\"" << gitVersion << "\"";
12021 }
12022 tagFile << ">\n";
12023
12024 // for each file
12025 for (const auto &fn : *Doxygen::inputNameLinkedMap)
12026 {
12027 for (const auto &fd : *fn)
12028 {
12029 if (fd->isLinkableInProject()) fd->writeTagFile(tagFile);
12030 }
12031 }
12032 // for each class
12033 for (const auto &cd : *Doxygen::classLinkedMap)
12034 {
12035 ClassDefMutable *cdm = toClassDefMutable(cd.get());
12036 if (cdm && cdm->isLinkableInProject())
12037 {
12038 cdm->writeTagFile(tagFile);
12039 }
12040 }
12041 // for each concept
12042 for (const auto &cd : *Doxygen::conceptLinkedMap)
12043 {
12044 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
12045 if (cdm && cdm->isLinkableInProject())
12046 {
12047 cdm->writeTagFile(tagFile);
12048 }
12049 }
12050 // for each namespace
12051 for (const auto &nd : *Doxygen::namespaceLinkedMap)
12052 {
12054 if (ndm && nd->isLinkableInProject())
12055 {
12056 ndm->writeTagFile(tagFile);
12057 }
12058 }
12059 // for each group
12060 for (const auto &gd : *Doxygen::groupLinkedMap)
12061 {
12062 if (gd->isLinkableInProject()) gd->writeTagFile(tagFile);
12063 }
12064 // for each module
12065 for (const auto &mod : ModuleManager::instance().modules())
12066 {
12067 if (mod->isLinkableInProject()) mod->writeTagFile(tagFile);
12068 }
12069 // for each page
12070 for (const auto &pd : *Doxygen::pageLinkedMap)
12071 {
12072 if (pd->isLinkableInProject()) pd->writeTagFile(tagFile);
12073 }
12074 if (Doxygen::mainPage) Doxygen::mainPage->writeTagFile(tagFile);
12075
12076 tagFile << "</tagfile>\n";
12077}
12078
12079static void exitDoxygen() noexcept
12080{
12081 if (!g_successfulRun) // premature exit
12082 {
12083 Dir thisDir;
12084 msg("Exiting...\n");
12085 if (!Doxygen::filterDBFileName.isEmpty())
12086 {
12087 thisDir.remove(Doxygen::filterDBFileName.str());
12088 }
12089 }
12090}
12091
12092static QCString createOutputDirectory(const QCString &baseDirName,
12093 const QCString &formatDirName,
12094 const char *defaultDirName)
12095{
12096 QCString result = formatDirName;
12097 if (result.isEmpty())
12098 {
12099 result = baseDirName + defaultDirName;
12100 }
12101 else if (formatDirName[0]!='/' && (formatDirName.length()==1 || formatDirName[1]!=':'))
12102 {
12103 result.prepend(baseDirName+"/");
12104 }
12105 Dir formatDir(result.str());
12106 if (!formatDir.exists() && !formatDir.mkdir(result.str()))
12107 {
12108 term("Could not create output directory {}\n", result);
12109 }
12110 return result;
12111}
12112
12114{
12115 StringUnorderedSet killSet;
12116
12117 const StringVector &exclPatterns = Config_getList(EXCLUDE_PATTERNS);
12118 bool alwaysRecursive = Config_getBool(RECURSIVE);
12119 StringUnorderedSet excludeNameSet;
12120
12121 // gather names of all files in the include path
12122 g_s.begin("Searching for include files...\n");
12123 killSet.clear();
12124 const StringVector &includePathList = Config_getList(INCLUDE_PATH);
12125 for (const auto &s : includePathList)
12126 {
12127 size_t plSize = Config_getList(INCLUDE_FILE_PATTERNS).size();
12128 const StringVector &pl = plSize==0 ? Config_getList(FILE_PATTERNS) :
12129 Config_getList(INCLUDE_FILE_PATTERNS);
12130 readFileOrDirectory(s.c_str(), // s
12132 nullptr, // exclSet
12133 &pl, // patList
12134 &exclPatterns, // exclPatList
12135 nullptr, // resultList
12136 nullptr, // resultSet
12137 false, // INCLUDE_PATH isn't recursive
12138 TRUE, // errorIfNotExist
12139 &killSet); // killSet
12140 }
12141 g_s.end();
12142
12143 g_s.begin("Searching for example files...\n");
12144 killSet.clear();
12145 const StringVector &examplePathList = Config_getList(EXAMPLE_PATH);
12146 for (const auto &s : examplePathList)
12147 {
12148 readFileOrDirectory(s.c_str(), // s
12150 nullptr, // exclSet
12151 &Config_getList(EXAMPLE_PATTERNS), // patList
12152 nullptr, // exclPatList
12153 nullptr, // resultList
12154 nullptr, // resultSet
12155 (alwaysRecursive || Config_getBool(EXAMPLE_RECURSIVE)), // recursive
12156 TRUE, // errorIfNotExist
12157 &killSet); // killSet
12158 }
12159 g_s.end();
12160
12161 g_s.begin("Searching for images...\n");
12162 killSet.clear();
12163 const StringVector &imagePathList=Config_getList(IMAGE_PATH);
12164 for (const auto &s : imagePathList)
12165 {
12166 readFileOrDirectory(s.c_str(), // s
12168 nullptr, // exclSet
12169 nullptr, // patList
12170 nullptr, // exclPatList
12171 nullptr, // resultList
12172 nullptr, // resultSet
12173 alwaysRecursive, // recursive
12174 TRUE, // errorIfNotExist
12175 &killSet); // killSet
12176 }
12177 g_s.end();
12178
12179 g_s.begin("Searching for dot files...\n");
12180 killSet.clear();
12181 const StringVector &dotFileList=Config_getList(DOTFILE_DIRS);
12182 for (const auto &s : dotFileList)
12183 {
12184 readFileOrDirectory(s.c_str(), // s
12186 nullptr, // exclSet
12187 nullptr, // patList
12188 nullptr, // exclPatList
12189 nullptr, // resultList
12190 nullptr, // resultSet
12191 alwaysRecursive, // recursive
12192 TRUE, // errorIfNotExist
12193 &killSet); // killSet
12194 }
12195 g_s.end();
12196
12197 g_s.begin("Searching for msc files...\n");
12198 killSet.clear();
12199 const StringVector &mscFileList=Config_getList(MSCFILE_DIRS);
12200 for (const auto &s : mscFileList)
12201 {
12202 readFileOrDirectory(s.c_str(), // s
12204 nullptr, // exclSet
12205 nullptr, // patList
12206 nullptr, // exclPatList
12207 nullptr, // resultList
12208 nullptr, // resultSet
12209 alwaysRecursive, // recursive
12210 TRUE, // errorIfNotExist
12211 &killSet); // killSet
12212 }
12213 g_s.end();
12214
12215 g_s.begin("Searching for dia files...\n");
12216 killSet.clear();
12217 const StringVector &diaFileList=Config_getList(DIAFILE_DIRS);
12218 for (const auto &s : diaFileList)
12219 {
12220 readFileOrDirectory(s.c_str(), // s
12222 nullptr, // exclSet
12223 nullptr, // patList
12224 nullptr, // exclPatList
12225 nullptr, // resultList
12226 nullptr, // resultSet
12227 alwaysRecursive, // recursive
12228 TRUE, // errorIfNotExist
12229 &killSet); // killSet
12230 }
12231 g_s.end();
12232
12233 g_s.begin("Searching for plantuml files...\n");
12234 killSet.clear();
12235 const StringVector &plantUmlFileList=Config_getList(PLANTUMLFILE_DIRS);
12236 for (const auto &s : plantUmlFileList)
12237 {
12238 readFileOrDirectory(s.c_str(), // s
12240 nullptr, // exclSet
12241 nullptr, // patList
12242 nullptr, // exclPatList
12243 nullptr, // resultList
12244 nullptr, // resultSet
12245 alwaysRecursive, // recursive
12246 TRUE, // errorIfNotExist
12247 &killSet); // killSet
12248 }
12249 g_s.end();
12250
12251 g_s.begin("Searching for files to exclude\n");
12252 const StringVector &excludeList = Config_getList(EXCLUDE);
12253 for (const auto &s : excludeList)
12254 {
12255 readFileOrDirectory(s.c_str(), // s
12256 nullptr, // fnDict
12257 nullptr, // exclSet
12258 &Config_getList(FILE_PATTERNS), // patList
12259 nullptr, // exclPatList
12260 nullptr, // resultList
12261 &excludeNameSet, // resultSet
12262 alwaysRecursive, // recursive
12263 FALSE); // errorIfNotExist
12264 }
12265 g_s.end();
12266
12267 /**************************************************************************
12268 * Determine Input Files *
12269 **************************************************************************/
12270
12271 g_s.begin("Searching INPUT for files to process...\n");
12272 killSet.clear();
12273 Doxygen::inputPaths.clear();
12274 const StringVector &inputList=Config_getList(INPUT);
12275 for (const auto &s : inputList)
12276 {
12277 QCString path=s.c_str();
12278 size_t l = path.length();
12279 if (l>0)
12280 {
12281 // strip trailing slashes
12282 if (path.at(l-1)=='\\' || path.at(l-1)=='/') path=path.left(l-1);
12283
12285 path, // s
12287 &excludeNameSet, // exclSet
12288 &Config_getList(FILE_PATTERNS), // patList
12289 &exclPatterns, // exclPatList
12290 &g_inputFiles, // resultList
12291 nullptr, // resultSet
12292 alwaysRecursive, // recursive
12293 TRUE, // errorIfNotExist
12294 &killSet, // killSet
12295 &Doxygen::inputPaths); // paths
12296 }
12297 }
12298
12299 // Sort the FileDef objects by full path to get a predictable ordering over multiple runs
12300 std::stable_sort(Doxygen::inputNameLinkedMap->begin(),
12302 [](const auto &f1,const auto &f2)
12303 {
12304 return qstricmp_sort(f1->fullName(),f2->fullName())<0;
12305 });
12306 for (auto &fileName : *Doxygen::inputNameLinkedMap)
12307 {
12308 if (fileName->size()>1)
12309 {
12310 std::stable_sort(fileName->begin(),fileName->end(),[](const auto &f1,const auto &f2)
12311 {
12312 return qstricmp_sort(f1->absFilePath(),f2->absFilePath())<0;
12313 });
12314 }
12315 }
12316 if (Doxygen::inputNameLinkedMap->empty())
12317 {
12318 warn_uncond("No files to be processed, please check your settings, in particular INPUT, FILE_PATTERNS, and RECURSIVE\n");
12319 }
12320 g_s.end();
12321}
12322
12323
12325{
12326 if (Config_getBool(MARKDOWN_SUPPORT))
12327 {
12328 QCString mdfileAsMainPage = Config_getString(USE_MDFILE_AS_MAINPAGE);
12329 if (mdfileAsMainPage.isEmpty()) return;
12330 FileInfo fi(mdfileAsMainPage.data());
12331 if (!fi.exists())
12332 {
12333 warn_uncond("Specified markdown mainpage '{}' does not exist\n",mdfileAsMainPage);
12334 return;
12335 }
12336 bool ambig = false;
12337 if (findFileDef(Doxygen::inputNameLinkedMap,fi.absFilePath(),ambig)==nullptr)
12338 {
12339 warn_uncond("Specified markdown mainpage '{}' has not been defined as input file\n",mdfileAsMainPage);
12340 return;
12341 }
12342 }
12343}
12344
12346{
12347 AUTO_TRACE();
12348 std::atexit(exitDoxygen);
12349
12350 Portable::correctPath(Config_getList(EXTERNAL_TOOL_PATH));
12351
12352#if USE_LIBCLANG
12353 Doxygen::clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING);
12354#endif
12355
12356 // we would like to show the versionString earlier, but we first have to handle the configuration file
12357 // to know the value of the QUIET setting.
12358 QCString versionString = getFullVersion();
12359 msg("Doxygen version used: {}\n",versionString);
12360
12362
12363 /**************************************************************************
12364 * Make sure the output directory exists
12365 **************************************************************************/
12366 QCString outputDirectory = Config_getString(OUTPUT_DIRECTORY);
12367 if (!g_singleComment)
12368 {
12369 if (outputDirectory.isEmpty())
12370 {
12371 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,Dir::currentDirPath().c_str());
12372 }
12373 else
12374 {
12375 Dir dir(outputDirectory.str());
12376 if (!dir.exists())
12377 {
12379 if (!dir.mkdir(outputDirectory.str()))
12380 {
12381 term("tag OUTPUT_DIRECTORY: Output directory '{}' does not "
12382 "exist and cannot be created\n",outputDirectory);
12383 }
12384 else
12385 {
12386 msg("Notice: Output directory '{}' does not exist. "
12387 "I have created it for you.\n", outputDirectory);
12388 }
12389 dir.setPath(outputDirectory.str());
12390 }
12391 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,dir.absPath().c_str());
12392 }
12393 }
12394 AUTO_TRACE_ADD("outputDirectory={}",outputDirectory);
12395
12396 /**************************************************************************
12397 * Initialize global lists and dictionaries
12398 **************************************************************************/
12399
12400 // also scale lookup cache with SYMBOL_CACHE_SIZE
12401 int cacheSize = Config_getInt(LOOKUP_CACHE_SIZE);
12402 if (cacheSize<0) cacheSize=0;
12403 if (cacheSize>9) cacheSize=9;
12404 uint32_t lookupSize = 65536 << cacheSize;
12407
12408#ifdef HAS_SIGNALS
12409 signal(SIGINT, stopDoxygen);
12410#endif
12411
12412 uint32_t pid = Portable::pid();
12413 Doxygen::filterDBFileName.sprintf("doxygen_filterdb_%d.tmp",pid);
12414 Doxygen::filterDBFileName.prepend(outputDirectory+"/");
12415
12416 /**************************************************************************
12417 * Check/create output directories *
12418 **************************************************************************/
12419
12420 bool generateHtml = Config_getBool(GENERATE_HTML);
12421 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
12422 bool generateXml = Config_getBool(GENERATE_XML);
12423 bool generateLatex = Config_getBool(GENERATE_LATEX);
12424 bool generateRtf = Config_getBool(GENERATE_RTF);
12425 bool generateMan = Config_getBool(GENERATE_MAN);
12426 bool generateSql = Config_getBool(GENERATE_SQLITE3);
12427 QCString htmlOutput;
12428 QCString docbookOutput;
12429 QCString xmlOutput;
12430 QCString latexOutput;
12431 QCString rtfOutput;
12432 QCString manOutput;
12433 QCString sqlOutput;
12434
12435 if (!g_singleComment)
12436 {
12437 if (generateHtml)
12438 {
12439 htmlOutput = createOutputDirectory(outputDirectory,Config_getString(HTML_OUTPUT),"/html");
12440 Config_updateString(HTML_OUTPUT,htmlOutput);
12441
12442 QCString sitemapUrl = Config_getString(SITEMAP_URL);
12443 bool generateSitemap = !sitemapUrl.isEmpty();
12444 if (generateSitemap && !sitemapUrl.endsWith("/"))
12445 {
12446 Config_updateString(SITEMAP_URL,sitemapUrl+"/");
12447 }
12448
12449 // add HTML indexers that are enabled
12450 bool generateHtmlHelp = Config_getBool(GENERATE_HTMLHELP);
12451 bool generateEclipseHelp = Config_getBool(GENERATE_ECLIPSEHELP);
12452 bool generateQhp = Config_getBool(GENERATE_QHP);
12453 bool generateTreeView = Config_getBool(GENERATE_TREEVIEW);
12454 bool generateDocSet = Config_getBool(GENERATE_DOCSET);
12455 if (generateEclipseHelp) Doxygen::indexList->addIndex<EclipseHelp>();
12456 if (generateHtmlHelp) Doxygen::indexList->addIndex<HtmlHelp>();
12457 if (generateQhp) Doxygen::indexList->addIndex<Qhp>();
12458 if (generateSitemap) Doxygen::indexList->addIndex<Sitemap>();
12459 if (generateTreeView) Doxygen::indexList->addIndex<FTVHelp>(TRUE);
12460 if (generateDocSet) Doxygen::indexList->addIndex<DocSets>();
12461 Doxygen::indexList->addIndex<Crawlmap>();
12462 Doxygen::indexList->initialize();
12463 }
12464
12465 if (generateDocbook)
12466 {
12467 docbookOutput = createOutputDirectory(outputDirectory,Config_getString(DOCBOOK_OUTPUT),"/docbook");
12468 Config_updateString(DOCBOOK_OUTPUT,docbookOutput);
12469 }
12470
12471 if (generateXml)
12472 {
12473 xmlOutput = createOutputDirectory(outputDirectory,Config_getString(XML_OUTPUT),"/xml");
12474 Config_updateString(XML_OUTPUT,xmlOutput);
12475 }
12476
12477 if (generateLatex)
12478 {
12479 latexOutput = createOutputDirectory(outputDirectory,Config_getString(LATEX_OUTPUT), "/latex");
12480 Config_updateString(LATEX_OUTPUT,latexOutput);
12481 }
12482
12483 if (generateRtf)
12484 {
12485 rtfOutput = createOutputDirectory(outputDirectory,Config_getString(RTF_OUTPUT),"/rtf");
12486 Config_updateString(RTF_OUTPUT,rtfOutput);
12487 }
12488
12489 if (generateMan)
12490 {
12491 manOutput = createOutputDirectory(outputDirectory,Config_getString(MAN_OUTPUT),"/man");
12492 Config_updateString(MAN_OUTPUT,manOutput);
12493 }
12494
12495 if (generateSql)
12496 {
12497 sqlOutput = createOutputDirectory(outputDirectory,Config_getString(SQLITE3_OUTPUT),"/sqlite3");
12498 Config_updateString(SQLITE3_OUTPUT,sqlOutput);
12499 }
12500 }
12501
12502 if (Config_getBool(HAVE_DOT))
12503 {
12504 QCString curFontPath = Config_getString(DOT_FONTPATH);
12505 if (curFontPath.isEmpty())
12506 {
12507 Portable::getenv("DOTFONTPATH");
12508 QCString newFontPath = ".";
12509 if (!curFontPath.isEmpty())
12510 {
12511 newFontPath+=Portable::pathListSeparator();
12512 newFontPath+=curFontPath;
12513 }
12514 Portable::setenv("DOTFONTPATH",qPrint(newFontPath));
12515 }
12516 else
12517 {
12518 Portable::setenv("DOTFONTPATH",qPrint(curFontPath));
12519 }
12520 }
12521
12522 /**************************************************************************
12523 * Handle layout file *
12524 **************************************************************************/
12525
12527 QCString layoutFileName = Config_getString(LAYOUT_FILE);
12528 bool defaultLayoutUsed = FALSE;
12529 if (layoutFileName.isEmpty())
12530 {
12531 layoutFileName = Config_updateString(LAYOUT_FILE,"DoxygenLayout.xml");
12532 defaultLayoutUsed = TRUE;
12533 }
12534 AUTO_TRACE_ADD("defaultLayoutUsed={}, layoutFileName={}",defaultLayoutUsed,layoutFileName);
12535
12536 FileInfo fi(layoutFileName.str());
12537 if (fi.exists())
12538 {
12539 msg("Parsing layout file {}...\n",layoutFileName);
12540 LayoutDocManager::instance().parse(layoutFileName);
12541 }
12542 else if (!defaultLayoutUsed)
12543 {
12544 warn_uncond("failed to open layout file '{}' for reading! Using default settings.\n",layoutFileName);
12545 }
12546 printLayout();
12547
12548 /**************************************************************************
12549 * Read and preprocess input *
12550 **************************************************************************/
12551
12552 // prevent search in the output directories
12553 StringVector exclPatterns = Config_getList(EXCLUDE_PATTERNS);
12554 if (generateHtml) exclPatterns.push_back(htmlOutput.str());
12555 if (generateDocbook) exclPatterns.push_back(docbookOutput.str());
12556 if (generateXml) exclPatterns.push_back(xmlOutput.str());
12557 if (generateLatex) exclPatterns.push_back(latexOutput.str());
12558 if (generateRtf) exclPatterns.push_back(rtfOutput.str());
12559 if (generateMan) exclPatterns.push_back(manOutput.str());
12560 Config_updateList(EXCLUDE_PATTERNS,exclPatterns);
12561
12562 if (!g_singleComment)
12563 {
12565
12567 }
12568
12569 // Notice: the order of the function calls below is very important!
12570
12571 if (generateHtml && !Config_getBool(USE_MATHJAX))
12572 {
12574 }
12575 if (generateRtf)
12576 {
12578 }
12579 if (generateDocbook)
12580 {
12582 }
12583
12585
12586 /**************************************************************************
12587 * Handle Tag Files *
12588 **************************************************************************/
12589
12590 std::shared_ptr<Entry> root = std::make_shared<Entry>();
12591
12592 if (!g_singleComment)
12593 {
12594 msg("Reading and parsing tag files\n");
12595 const StringVector &tagFileList = Config_getList(TAGFILES);
12596 for (const auto &s : tagFileList)
12597 {
12598 readTagFile(root,s.c_str());
12599 }
12600 }
12601
12602 /**************************************************************************
12603 * Parse source files *
12604 **************************************************************************/
12605
12606 addSTLSupport(root);
12607
12608 g_s.begin("Parsing files\n");
12609 if (g_singleComment)
12610 {
12611 //printf("Parsing comment %s\n",qPrint(g_commentFileName));
12612 if (g_commentFileName=="-")
12613 {
12614 std::string text = fileToString(g_commentFileName).str();
12615 addTerminalCharIfMissing(text,'\n');
12616 generateHtmlForComment("stdin.md",text);
12617 }
12618 else if (FileInfo(g_commentFileName.str()).isFile())
12619 {
12620 std::string text;
12622 addTerminalCharIfMissing(text,'\n');
12624 }
12625 else
12626 {
12627 }
12629 exit(0);
12630 }
12631 else
12632 {
12633 if (Config_getInt(NUM_PROC_THREADS)==1)
12634 {
12636 }
12637 else
12638 {
12640 }
12641 }
12642 g_s.end();
12643
12644 /**************************************************************************
12645 * Gather information *
12646 **************************************************************************/
12647
12648 g_s.begin("Building macro definition list...\n");
12650 g_s.end();
12651
12652 g_s.begin("Building group list...\n");
12653 buildGroupList(root.get());
12654 organizeSubGroups(root.get());
12655 g_s.end();
12656
12657 g_s.begin("Building directory list...\n");
12659 findDirDocumentation(root.get());
12660 g_s.end();
12661
12662 g_s.begin("Building namespace list...\n");
12663 buildNamespaceList(root.get());
12664 findUsingDirectives(root.get());
12665 g_s.end();
12666
12667 g_s.begin("Building file list...\n");
12668 buildFileList(root.get());
12669 g_s.end();
12670
12671 g_s.begin("Building class list...\n");
12672 buildClassList(root.get());
12673 g_s.end();
12674
12675 g_s.begin("Building concept list...\n");
12676 buildConceptList(root.get());
12677 g_s.end();
12678
12679 // build list of using declarations here (global list)
12680 buildListOfUsingDecls(root.get());
12681 g_s.end();
12682
12683 g_s.begin("Computing nesting relations for classes...\n");
12685 g_s.end();
12686 // 1.8.2-20121111: no longer add nested classes to the group as well
12687 //distributeClassGroupRelations();
12688
12689 // calling buildClassList may result in cached relations that
12690 // become invalid after resolveClassNestingRelations(), that's why
12691 // we need to clear the cache here
12692 Doxygen::typeLookupCache->clear();
12693 // we don't need the list of using declaration anymore
12694 g_usingDeclarations.clear();
12695
12696 g_s.begin("Associating documentation with classes...\n");
12697 buildClassDocList(root.get());
12698 g_s.end();
12699
12700 g_s.begin("Associating documentation with concepts...\n");
12701 buildConceptDocList(root.get());
12703 g_s.end();
12704
12705 g_s.begin("Associating documentation with modules...\n");
12706 findModuleDocumentation(root.get());
12707 g_s.end();
12708
12709 g_s.begin("Building example list...\n");
12710 buildExampleList(root.get());
12711 g_s.end();
12712
12713 g_s.begin("Searching for enumerations...\n");
12714 findEnums(root.get());
12715 g_s.end();
12716
12717 // Since buildVarList calls isVarWithConstructor
12718 // and this calls getResolvedClass we need to process
12719 // typedefs first so the relations between classes via typedefs
12720 // are properly resolved. See bug 536385 for an example.
12721 g_s.begin("Searching for documented typedefs...\n");
12722 buildTypedefList(root.get());
12723 g_s.end();
12724
12725 if (Config_getBool(OPTIMIZE_OUTPUT_SLICE))
12726 {
12727 g_s.begin("Searching for documented sequences...\n");
12728 buildSequenceList(root.get());
12729 g_s.end();
12730
12731 g_s.begin("Searching for documented dictionaries...\n");
12732 buildDictionaryList(root.get());
12733 g_s.end();
12734 }
12735
12736 g_s.begin("Searching for members imported via using declarations...\n");
12737 // this should be after buildTypedefList in order to properly import
12738 // used typedefs
12739 findUsingDeclarations(root.get(),TRUE); // do for python packages first
12740 findUsingDeclarations(root.get(),FALSE); // then the rest
12741 g_s.end();
12742
12743 g_s.begin("Searching for included using directives...\n");
12745 g_s.end();
12746
12747 g_s.begin("Searching for documented variables...\n");
12748 buildVarList(root.get());
12749 g_s.end();
12750
12751 g_s.begin("Building interface member list...\n");
12752 buildInterfaceAndServiceList(root.get()); // UNO IDL
12753
12754 g_s.begin("Building member list...\n"); // using class info only !
12755 buildFunctionList(root.get());
12756 g_s.end();
12757
12758 g_s.begin("Searching for friends...\n");
12759 findFriends();
12760 g_s.end();
12761
12762 g_s.begin("Searching for documented defines...\n");
12763 findDefineDocumentation(root.get());
12764 g_s.end();
12765
12766 g_s.begin("Computing class inheritance relations...\n");
12767 findClassEntries(root.get());
12769 g_s.end();
12770
12771 g_s.begin("Computing class usage relations...\n");
12773 g_s.end();
12774
12775 if (Config_getBool(INLINE_SIMPLE_STRUCTS))
12776 {
12777 g_s.begin("Searching for tag less structs...\n");
12779 g_s.end();
12780 }
12781
12782 g_s.begin("Flushing cached template relations that have become invalid...\n");
12784 g_s.end();
12785
12786 g_s.begin("Warn for undocumented namespaces...\n");
12788 g_s.end();
12789
12790 g_s.begin("Computing class relations...\n");
12793 if (Config_getBool(OPTIMIZE_OUTPUT_VHDL))
12794 {
12796 }
12798 g_classEntries.clear();
12799 g_s.end();
12800
12801 g_s.begin("Add enum values to enums...\n");
12802 addEnumValuesToEnums(root.get());
12803 findEnumDocumentation(root.get());
12804 g_s.end();
12805
12806 g_s.begin("Searching for member function documentation...\n");
12807 findObjCMethodDefinitions(root.get());
12808 findMemberDocumentation(root.get()); // may introduce new members !
12809 findUsingDeclImports(root.get()); // may introduce new members !
12810 g_usingClassMap.clear();
12814 g_s.end();
12815
12816 // moved to after finding and copying documentation,
12817 // as this introduces new members see bug 722654
12818 g_s.begin("Creating members for template instances...\n");
12820 g_s.end();
12821
12822 g_s.begin("Building page list...\n");
12823 buildPageList(root.get());
12824 g_s.end();
12825
12826 g_s.begin("Search for main page...\n");
12827 findMainPage(root.get());
12828 findMainPageTagFiles(root.get());
12829 g_s.end();
12830
12831 g_s.begin("Computing page relations...\n");
12832 computePageRelations(root.get());
12834 g_s.end();
12835
12836 g_s.begin("Determining the scope of groups...\n");
12837 findGroupScope(root.get());
12838 g_s.end();
12839
12840 g_s.begin("Computing module relations...\n");
12841 auto &mm = ModuleManager::instance();
12842 mm.resolvePartitions();
12843 mm.resolveImports();
12844 mm.collectExportedSymbols();
12845 g_s.end();
12846
12847 auto memberNameComp = [](const MemberNameLinkedMap::Ptr &n1,const MemberNameLinkedMap::Ptr &n2)
12848 {
12849 return qstricmp_sort(n1->memberName().data()+getPrefixIndex(n1->memberName()),
12850 n2->memberName().data()+getPrefixIndex(n2->memberName())
12851 )<0;
12852 };
12853
12854 auto classComp = [](const ClassLinkedMap::Ptr &c1,const ClassLinkedMap::Ptr &c2)
12855 {
12856 if (Config_getBool(SORT_BY_SCOPE_NAME))
12857 {
12858 return qstricmp_sort(c1->name(), c2->name())<0;
12859 }
12860 else
12861 {
12862 int i = qstricmp_sort(c1->className(), c2->className());
12863 return i==0 ? qstricmp_sort(c1->name(), c2->name())<0 : i<0;
12864 }
12865 };
12866
12867 auto namespaceComp = [](const NamespaceLinkedMap::Ptr &n1,const NamespaceLinkedMap::Ptr &n2)
12868 {
12869 return qstricmp_sort(n1->name(),n2->name())<0;
12870 };
12871
12872 auto conceptComp = [](const ConceptLinkedMap::Ptr &c1,const ConceptLinkedMap::Ptr &c2)
12873 {
12874 return qstricmp_sort(c1->name(),c2->name())<0;
12875 };
12876
12877 g_s.begin("Sorting lists...\n");
12878 std::stable_sort(Doxygen::memberNameLinkedMap->begin(),
12880 memberNameComp);
12881 std::stable_sort(Doxygen::functionNameLinkedMap->begin(),
12883 memberNameComp);
12884 std::stable_sort(Doxygen::hiddenClassLinkedMap->begin(),
12886 classComp);
12887 std::stable_sort(Doxygen::classLinkedMap->begin(),
12889 classComp);
12890 std::stable_sort(Doxygen::conceptLinkedMap->begin(),
12892 conceptComp);
12893 std::stable_sort(Doxygen::namespaceLinkedMap->begin(),
12895 namespaceComp);
12896 g_s.end();
12897
12898 g_s.begin("Determining which enums are documented\n");
12900 g_s.end();
12901
12902 g_s.begin("Computing member relations...\n");
12905 g_s.end();
12906
12907 g_s.begin("Building full member lists recursively...\n");
12909 g_s.end();
12910
12911 g_s.begin("Adding members to member groups.\n");
12913 g_s.end();
12914
12915 if (Config_getBool(DISTRIBUTE_GROUP_DOC))
12916 {
12917 g_s.begin("Distributing member group documentation.\n");
12919 g_s.end();
12920 }
12921
12922 g_s.begin("Computing member references...\n");
12924 g_s.end();
12925
12926 if (Config_getBool(INHERIT_DOCS))
12927 {
12928 g_s.begin("Inheriting documentation...\n");
12930 g_s.end();
12931 }
12932
12933
12934 // compute the shortest possible names of all files
12935 // without losing the uniqueness of the file names.
12936 g_s.begin("Generating disk names...\n");
12938 g_s.end();
12939
12940 g_s.begin("Adding source references...\n");
12942 g_s.end();
12943
12944 g_s.begin("Adding xrefitems...\n");
12947 g_s.end();
12948
12949 g_s.begin("Sorting member lists...\n");
12951 g_s.end();
12952
12953 g_s.begin("Setting anonymous enum type...\n");
12955 g_s.end();
12956
12957 g_s.begin("Computing dependencies between directories...\n");
12959 g_s.end();
12960
12961 g_s.begin("Generating citations page...\n");
12963 g_s.end();
12964
12965 g_s.begin("Counting members...\n");
12966 countMembers();
12967 g_s.end();
12968
12969 g_s.begin("Counting data structures...\n");
12971 g_s.end();
12972
12973 g_s.begin("Resolving user defined references...\n");
12975 g_s.end();
12976
12977 g_s.begin("Finding anchors and sections in the documentation...\n");
12979 g_s.end();
12980
12981 g_s.begin("Transferring function references...\n");
12983 g_s.end();
12984
12985 g_s.begin("Combining using relations...\n");
12987 g_s.end();
12988
12990 g_s.begin("Adding members to index pages...\n");
12992 addToIndices();
12993 g_s.end();
12994
12995 g_s.begin("Correcting members for VHDL...\n");
12997 g_s.end();
12998
12999 g_s.begin("Computing tooltip texts...\n");
13001 g_s.end();
13002
13003 if (Config_getBool(SORT_GROUP_NAMES))
13004 {
13005 std::stable_sort(Doxygen::groupLinkedMap->begin(),
13007 [](const auto &g1,const auto &g2)
13008 { return g1->groupTitle() < g2->groupTitle(); });
13009
13010 for (const auto &gd : *Doxygen::groupLinkedMap)
13011 {
13012 gd->sortSubGroups();
13013 }
13014 }
13015
13016 printNavTree(root.get(),0);
13018}
13019
13021{
13022 AUTO_TRACE();
13023 /**************************************************************************
13024 * Initialize output generators *
13025 **************************************************************************/
13026
13027 /// add extra languages for which we can only produce syntax highlighted code
13029
13030 //// dump all symbols
13031 if (g_dumpSymbolMap)
13032 {
13033 dumpSymbolMap();
13034 exit(0);
13035 }
13036
13037 bool generateHtml = Config_getBool(GENERATE_HTML);
13038 bool generateLatex = Config_getBool(GENERATE_LATEX);
13039 bool generateMan = Config_getBool(GENERATE_MAN);
13040 bool generateRtf = Config_getBool(GENERATE_RTF);
13041 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
13042
13043
13045 if (generateHtml)
13046 {
13050 }
13051 if (generateLatex)
13052 {
13055 }
13056 if (generateDocbook)
13057 {
13060 }
13061 if (generateMan)
13062 {
13063 g_outputList->add<ManGenerator>();
13065 }
13066 if (generateRtf)
13067 {
13068 g_outputList->add<RTFGenerator>();
13070 }
13071 if (Config_getBool(USE_HTAGS))
13072 {
13074 QCString htmldir = Config_getString(HTML_OUTPUT);
13075 if (!Htags::execute(htmldir))
13076 err("USE_HTAGS is YES but htags(1) failed. \n");
13077 else if (!Htags::loadFilemap(htmldir))
13078 err("htags(1) ended normally but failed to load the filemap. \n");
13079 }
13080
13081 /**************************************************************************
13082 * Generate documentation *
13083 **************************************************************************/
13084
13085 g_s.begin("Generating style sheet...\n");
13086 //printf("writing style info\n");
13087 g_outputList->writeStyleInfo(0); // write first part
13088 g_s.end();
13089
13090 bool searchEngine = Config_getBool(SEARCHENGINE);
13091 bool serverBasedSearch = Config_getBool(SERVER_BASED_SEARCH);
13092
13093 g_s.begin("Generating search indices...\n");
13094 if (searchEngine && !serverBasedSearch && generateHtml)
13095 {
13097 }
13098
13099 // generate search indices (need to do this before writing other HTML
13100 // pages as these contain a drop down menu with options depending on
13101 // what categories we find in this function.
13102 if (generateHtml && searchEngine)
13103 {
13104 QCString searchDirName = Config_getString(HTML_OUTPUT)+"/search";
13105 Dir searchDir(searchDirName.str());
13106 if (!searchDir.exists() && !searchDir.mkdir(searchDirName.str()))
13107 {
13108 term("Could not create search results directory '{}' $PWD='{}'\n",
13109 searchDirName,Dir::currentDirPath());
13110 }
13111 HtmlGenerator::writeSearchData(searchDirName);
13112 if (!serverBasedSearch) // client side search index
13113 {
13115 }
13116 }
13117 g_s.end();
13118
13119 // copy static stuff
13120 if (generateHtml)
13121 {
13124 copyLogo(Config_getString(HTML_OUTPUT));
13125 copyIcon(Config_getString(HTML_OUTPUT));
13126 copyExtraFiles(Config_getList(HTML_EXTRA_FILES),"HTML_EXTRA_FILES",Config_getString(HTML_OUTPUT));
13127 }
13128 if (generateLatex)
13129 {
13131 copyLogo(Config_getString(LATEX_OUTPUT));
13132 copyIcon(Config_getString(LATEX_OUTPUT));
13133 copyExtraFiles(Config_getList(LATEX_EXTRA_FILES),"LATEX_EXTRA_FILES",Config_getString(LATEX_OUTPUT));
13134 }
13135 if (generateDocbook)
13136 {
13137 copyLogo(Config_getString(DOCBOOK_OUTPUT));
13138 copyIcon(Config_getString(DOCBOOK_OUTPUT));
13139 }
13140 if (generateRtf)
13141 {
13142 copyLogo(Config_getString(RTF_OUTPUT));
13143 copyIcon(Config_getString(RTF_OUTPUT));
13144 copyExtraFiles(Config_getList(RTF_EXTRA_FILES),"RTF_EXTRA_FILES",Config_getString(RTF_OUTPUT));
13145 }
13146
13148 if (fm.hasFormulas() && generateHtml
13149 && !Config_getBool(USE_MATHJAX))
13150 {
13151 g_s.begin("Generating images for formulas in HTML...\n");
13152 fm.generateImages(Config_getString(HTML_OUTPUT), Config_getEnum(HTML_FORMULA_FORMAT)==HTML_FORMULA_FORMAT_t::svg ?
13154 g_s.end();
13155 }
13156 if (fm.hasFormulas() && generateRtf)
13157 {
13158 g_s.begin("Generating images for formulas in RTF...\n");
13160 g_s.end();
13161 }
13162
13163 if (fm.hasFormulas() && generateDocbook)
13164 {
13165 g_s.begin("Generating images for formulas in Docbook...\n");
13167 g_s.end();
13168 }
13169
13170 g_s.begin("Generating example documentation...\n");
13172 g_s.end();
13173
13174 g_s.begin("Generating file sources...\n");
13176 g_s.end();
13177
13178 g_s.begin("Generating file documentation...\n");
13180 g_s.end();
13181
13182 g_s.begin("Generating page documentation...\n");
13184 g_s.end();
13185
13186 g_s.begin("Generating group documentation...\n");
13188 g_s.end();
13189
13190 g_s.begin("Generating class documentation...\n");
13192 g_s.end();
13193
13194 g_s.begin("Generating concept documentation...\n");
13196 g_s.end();
13197
13198 g_s.begin("Generating module documentation...\n");
13200 g_s.end();
13201
13202 g_s.begin("Generating namespace documentation...\n");
13204 g_s.end();
13205
13206 if (Config_getBool(GENERATE_LEGEND))
13207 {
13208 g_s.begin("Generating graph info page...\n");
13210 g_s.end();
13211 }
13212
13213 g_s.begin("Generating directory documentation...\n");
13215 g_s.end();
13216
13217 if (g_outputList->size()>0)
13218 {
13220 }
13221
13222 g_s.begin("finalizing index lists...\n");
13223 Doxygen::indexList->finalize();
13224 g_s.end();
13225
13226 g_s.begin("writing tag file...\n");
13227 writeTagFile();
13228 g_s.end();
13229
13230 if (Config_getBool(GENERATE_XML))
13231 {
13232 g_s.begin("Generating XML output...\n");
13234 generateXML();
13236 g_s.end();
13237 }
13238 if (Config_getBool(GENERATE_SQLITE3))
13239 {
13240 g_s.begin("Generating SQLITE3 output...\n");
13242 g_s.end();
13243 }
13244
13245 if (Config_getBool(GENERATE_AUTOGEN_DEF))
13246 {
13247 g_s.begin("Generating AutoGen DEF output...\n");
13248 generateDEF();
13249 g_s.end();
13250 }
13251 if (Config_getBool(GENERATE_PERLMOD))
13252 {
13253 g_s.begin("Generating Perl module output...\n");
13255 g_s.end();
13256 }
13257 if (generateHtml && searchEngine && serverBasedSearch)
13258 {
13259 g_s.begin("Generating search index\n");
13260 if (Doxygen::searchIndex.kind()==SearchIndexIntf::Internal) // write own search index
13261 {
13263 Doxygen::searchIndex.write(Config_getString(HTML_OUTPUT)+"/search/search.idx");
13264 }
13265 else // write data for external search index
13266 {
13268 QCString searchDataFile = Config_getString(SEARCHDATA_FILE);
13269 if (searchDataFile.isEmpty())
13270 {
13271 searchDataFile="searchdata.xml";
13272 }
13273 if (!Portable::isAbsolutePath(searchDataFile.data()))
13274 {
13275 searchDataFile.prepend(Config_getString(OUTPUT_DIRECTORY)+"/");
13276 }
13277 Doxygen::searchIndex.write(searchDataFile);
13278 }
13279 g_s.end();
13280 }
13281
13282 if (generateRtf)
13283 {
13284 g_s.begin("Combining RTF output...\n");
13285 if (!RTFGenerator::preProcessFileInplace(Config_getString(RTF_OUTPUT),"refman.rtf"))
13286 {
13287 err("An error occurred during post-processing the RTF files!\n");
13288 }
13289 g_s.end();
13290 }
13291
13292 g_s.begin("Running plantuml with JAVA...\n");
13294 g_s.end();
13295
13296 if (Config_getBool(HAVE_DOT))
13297 {
13298 g_s.begin("Running dot...\n");
13300 g_s.end();
13301 }
13302
13303 if (generateHtml &&
13304 Config_getBool(GENERATE_HTMLHELP) &&
13305 !Config_getString(HHC_LOCATION).isEmpty())
13306 {
13307 g_s.begin("Running html help compiler...\n");
13309 g_s.end();
13310 }
13311
13312 if ( generateHtml &&
13313 Config_getBool(GENERATE_QHP) &&
13314 !Config_getString(QHG_LOCATION).isEmpty())
13315 {
13316 g_s.begin("Running qhelpgenerator...\n");
13318 g_s.end();
13319 }
13320
13321 g_outputList->cleanup();
13322
13323 msg("type lookup cache used {}/{} hits={} misses={}\n",
13325 Doxygen::typeLookupCache->capacity(),
13327 Doxygen::typeLookupCache->misses());
13328 msg("symbol lookup cache used {}/{} hits={} misses={}\n",
13330 Doxygen::symbolLookupCache->capacity(),
13332 Doxygen::symbolLookupCache->misses());
13333 int typeCacheParam = computeIdealCacheParam(static_cast<size_t>(Doxygen::typeLookupCache->misses()*2/3)); // part of the cache is flushed, hence the 2/3 correction factor
13334 int symbolCacheParam = computeIdealCacheParam(static_cast<size_t>(Doxygen::symbolLookupCache->misses()));
13335 int cacheParam = std::max(typeCacheParam,symbolCacheParam);
13336 if (cacheParam>Config_getInt(LOOKUP_CACHE_SIZE))
13337 {
13338 msg("Note: based on cache misses the ideal setting for LOOKUP_CACHE_SIZE is {} at the cost of higher memory usage.\n",cacheParam);
13339 }
13340
13342 {
13343
13344 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
13345 if (numThreads<1) numThreads=1;
13346 msg("Total elapsed time: {:.6f} seconds\n(of which an average of {:.6f} seconds per thread waiting for external tools to finish)\n",
13347 (static_cast<double>(Debug::elapsedTime())),
13348 Portable::getSysElapsedTime()/static_cast<double>(numThreads)
13349 );
13350 g_s.print();
13351
13353 msg("finished...\n");
13355 }
13356 else
13357 {
13358 msg("finished...\n");
13359 }
13360
13361
13362 /**************************************************************************
13363 * Start cleaning up *
13364 **************************************************************************/
13365
13367
13369 Dir thisDir;
13370 thisDir.remove(Doxygen::filterDBFileName.str());
13372 exitTracing();
13374 delete Doxygen::clangUsrMap;
13376
13377 //dumpDocNodeSizes();
13378}
void readAliases()
Definition aliases.cpp:170
constexpr auto prefix
Definition anchor.cpp:44
std::vector< ArgumentList > ArgumentLists
Definition arguments.h:138
This class represents an function or template argument list.
Definition arguments.h:60
RefQualifierType refQualifier() const
Definition arguments.h:109
bool noParameters() const
Definition arguments.h:110
bool pureSpecifier() const
Definition arguments.h:106
iterator end()
Definition arguments.h:87
void setTrailingReturnType(const QCString &s)
Definition arguments.h:115
bool hasParameters() const
Definition arguments.h:69
bool isDeleted() const
Definition arguments.h:108
QCString trailingReturnType() const
Definition arguments.h:107
size_t size() const
Definition arguments.h:93
void setPureSpecifier(bool b)
Definition arguments.h:114
bool constSpecifier() const
Definition arguments.h:104
void push_back(const Argument &a)
Definition arguments.h:95
bool empty() const
Definition arguments.h:92
void setConstSpecifier(bool b)
Definition arguments.h:112
void setRefQualifier(RefQualifierType t)
Definition arguments.h:117
void setIsDeleted(bool b)
Definition arguments.h:116
iterator begin()
Definition arguments.h:86
bool volatileSpecifier() const
Definition arguments.h:105
void setNoParameters(bool b)
Definition arguments.h:118
void setVolatileSpecifier(bool b)
Definition arguments.h:113
Definition cache.h:32
static CitationManager & instance()
Definition cite.cpp:80
void clear()
clears the database
Definition cite.cpp:106
void generatePage()
Generate the citations page.
Definition cite.cpp:327
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:340
std::string absPath() const
Definition dir.cpp:363
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:355
static bool setCurrent(const std::string &path)
Definition dir.cpp:348
bool exists() const
Definition dir.cpp:257
A linked map of directories.
Definition dirdef.h:172
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:631
bool isDoc() const
Definition types.h:642
ENTRY_TYPES bool isCompound() const
Definition types.h:638
bool isFile() const
Definition types.h:640
std::string to_string() const
Definition types.h:643
bool isScope() const
Definition types.h:639
bool isCompoundDoc() const
Definition types.h:641
A class that generates a dynamic tree view side panel.
Definition ftvhelp.h:41
static void generateTreeViewImages()
Definition ftvhelp.cpp:851
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:95
static void init()
Definition htmlgen.cpp:1135
static void writeSearchPage()
Definition htmlgen.cpp:3071
static void writeFooterFile(TextStream &t)
Definition htmlgen.cpp:1457
static void writeTabData()
Additional initialization after indices have been created.
Definition htmlgen.cpp:1291
static void writeSearchData(const QCString &dir)
Definition htmlgen.cpp:1329
static void writeExternalSearchPage()
Definition htmlgen.cpp:3162
static void writeStyleSheetFile(TextStream &t)
Definition htmlgen.cpp:1445
static void writeHeaderFile(TextStream &t, const QCString &cssname)
Definition htmlgen.cpp:1451
A class that generated the HTML Help specific files.
Definition htmlhelp.h:36
static const QCString hhpFileName
Definition htmlhelp.h:88
static Index & instance()
Definition index.cpp:106
void countDataStructures()
Definition index.cpp:262
A list of index interfaces.
Definition indexlist.h:63
Generator for LaTeX output.
Definition latexgen.h:94
static void writeFooterFile(TextStream &t)
Definition latexgen.cpp:688
static void writeStyleSheetFile(TextStream &t)
Definition latexgen.cpp:694
static void writeHeaderFile(TextStream &t)
Definition latexgen.cpp:682
static void init()
Definition latexgen.cpp:628
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 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:109
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
MemberDef * find(const QCString &name)
Definition memberlist.h:93
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:156
void run()
Run plant UML tool for all images.
Definition plantuml.cpp:314
void processFile(const QCString &fileName, const std::string &input, std::string &output)
Definition pre.l:4009
void addSearchDir(const QCString &dir)
Definition pre.l:3991
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
void fill(char c, int len=-1)
Fills a string with a predefined character.
Definition qcstring.h:180
QCString & prepend(const char *s)
Definition qcstring.h:407
int toInt(bool *ok=nullptr, int base=10) const
Definition qcstring.cpp:249
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition qcstring.h:153
bool startsWith(const char *s) const
Definition qcstring.h:492
QCString mid(size_t index, size_t len=static_cast< size_t >(-1)) const
Definition qcstring.h:226
QCString lower() const
Definition qcstring.h:234
bool endsWith(const char *s) const
Definition qcstring.h:509
char & at(size_t i)
Returns a reference to the character at index i.
Definition qcstring.h:578
bool isEmpty() const
Returns TRUE iff the string is empty.
Definition qcstring.h:150
QCString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
Definition qcstring.h:245
const std::string & str() const
Definition qcstring.h:537
QCString & setNum(short n)
Definition qcstring.h:444
QCString right(size_t len) const
Definition qcstring.h:219
size_t size() const
Returns the length of the string, not counting the 0-terminator.
Definition qcstring.h:156
QCString & sprintf(const char *format,...)
Definition qcstring.cpp:29
@ ExplicitSize
Definition qcstring.h:133
int findRev(char c, int index=-1, bool cs=TRUE) const
Definition qcstring.cpp:91
QCString & replace(size_t index, size_t len, const char *s)
Definition qcstring.cpp:212
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:159
QCString left(size_t len) const
Definition qcstring.h:214
int contains(char c, bool cs=TRUE) const
Definition qcstring.cpp:143
bool stripPrefix(const QCString &prefix)
Definition qcstring.h:198
void clear()
Definition qcstring.h:169
Definition qhp.h:27
static const QCString qhpFileName
Definition qhp.h:47
static QCString getQchFileName()
Definition qhp.cpp:428
Generator for RTF output.
Definition rtfgen.h:80
static void init()
Definition rtfgen.cpp:458
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:2458
static void writeStyleSheetFile(TextStream &t)
Definition rtfgen.cpp:391
static void writeExtensionsFile(TextStream &t)
Definition rtfgen.cpp:406
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:154
SectionInfo * add(const SectionInfo &si)
Definition section.h:138
static SectionManager & instance()
returns a reference to the singleton
Definition section.h:175
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)
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:492
std::string to_string() const
Definition types.h:520
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:785
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:558
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:468
Class representing a regular expression.
Definition regex.h:39
Iterator class to iterator 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:814
void generateDEF()
Definition defgen.cpp:525
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:1055
void computeDirDependencies()
Definition dirdef.cpp:1129
void generateDirDocs(OutputList &ol)
Definition dirdef.cpp:1146
#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:1324
static void findInheritedTemplateInstances()
Definition doxygen.cpp:5249
void printNavTree(Entry *root, int indent)
Definition doxygen.cpp:9903
static void addClassToContext(const Entry *root)
Definition doxygen.cpp:931
static void makeTemplateInstanceRelation(const Entry *root, ClassDefMutable *cd)
Definition doxygen.cpp:5264
static StringUnorderedSet g_pathsVisited(1009)
static void buildGroupList(const Entry *root)
Definition doxygen.cpp:425
static void insertMemberAlias(Definition *outerScope, const MemberDef *md)
Definition doxygen.cpp:6604
static void findUsingDeclarations(const Entry *root, bool filterPythonPackages)
Definition doxygen.cpp:2006
static void flushCachedTemplateRelations()
Definition doxygen.cpp:9331
static bool isRecursiveBaseClass(const QCString &scope, const QCString &name)
Definition doxygen.cpp:4807
static void copyLatexStyleSheet()
static void generateDocsForClassList(const std::vector< ClassDefMutable * > &classList)
Definition doxygen.cpp:8996
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:2828
static bool isSpecialization(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists)
Definition doxygen.cpp:5920
static MemberDef * addVariableToClass(const Entry *root, ClassDefMutable *cd, MemberType mtype, const QCString &type, const QCString &name, const QCString &args, bool fromAnnScope, MemberDef *fromAnnMemb, Protection prot, Relationship related)
Definition doxygen.cpp:2426
static void computeTemplateClassRelations()
Definition doxygen.cpp:5341
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:8059
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:9926
class Statistics g_s
static void createUsingMemberImportForClass(const Entry *root, ClassDefMutable *cd, const MemberDef *md, const QCString &fileName, const QCString &memName)
Definition doxygen.cpp:2110
static void generateXRefPages()
Definition doxygen.cpp:5513
static void findUsingDeclImports(const Entry *root)
Definition doxygen.cpp:2159
static void copyStyleSheet()
FindBaseClassRelation_Mode
Definition doxygen.cpp:285
@ Undocumented
Definition doxygen.cpp:288
@ TemplateInstances
Definition doxygen.cpp:286
@ DocumentedOnly
Definition doxygen.cpp:287
void distributeClassGroupRelations()
Definition doxygen.cpp:1447
static void generateGroupDocs()
Definition doxygen.cpp:9987
static void findDirDocumentation(const Entry *root)
Definition doxygen.cpp:9568
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:4849
static void resolveTemplateInstanceInType(const Entry *root, const Definition *scope, const MemberDef *md)
Definition doxygen.cpp:4777
static void organizeSubGroupsFiltered(const Entry *root, bool additional)
Definition doxygen.cpp:465
static void warnUndocumentedNamespaces()
Definition doxygen.cpp:5296
static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments, const std::string &name)
Definition doxygen.cpp:4454
static NamespaceDef * findUsedNamespace(const LinkedRefMap< NamespaceDef > &unl, const QCString &name)
Definition doxygen.cpp:1838
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:6647
static void findClassEntries(const Entry *root)
Definition doxygen.cpp:5212
static void processTagLessClasses(const ClassDef *rootCd, const ClassDef *cd, ClassDefMutable *tagParentCd, const QCString &prefix, int count)
Look through the members of class cd and its public members.
Definition doxygen.cpp:1566
static void addMemberFunction(const Entry *root, MemberName *mn, const QCString &scopeName, const QCString &namespaceName, const QCString &className, const QCString &funcTyp, const QCString &funcName, const QCString &funcArgs, const QCString &funcTempList, const QCString &exceptions, const QCString &type, const QCString &args, bool isFriend, TypeSpecifier spec, const QCString &relates, const QCString &funcDecl, bool overloaded, bool isFunc)
Definition doxygen.cpp:6131
static void vhdlCorrectMemberProperties()
Definition doxygen.cpp:8303
static void generateExampleDocs()
Definition doxygen.cpp:9942
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:4726
static void substituteTemplatesInArgList(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists, const ArgumentList &src, ArgumentList &dst)
Definition doxygen.cpp:6034
static void computeMemberReferences()
Definition doxygen.cpp:5410
static void generateConfigFile(const QCString &configFile, bool shortList, bool updateOnly=FALSE)
static void transferRelatedFunctionDocumentation()
Definition doxygen.cpp:4368
static void addMembersToMemberGroup()
Definition doxygen.cpp:9201
static bool tryAddEnumDocsToGroupMember(const Entry *root, const QCString &name)
Definition doxygen.cpp:7940
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:6537
static void findMainPageTagFiles(Entry *root)
Definition doxygen.cpp:9727
static void distributeConceptGroups()
Definition doxygen.cpp:1298
static void transferFunctionDocumentation()
Definition doxygen.cpp:4287
static void setAnonymousEnumType()
Definition doxygen.cpp:8941
static void sortMemberLists()
Definition doxygen.cpp:8846
static void createTemplateInstanceMembers()
Definition doxygen.cpp:8431
void transferStaticInstanceInitializers()
Definition doxygen.cpp:4417
static void findObjCMethodDefinitions(const Entry *root)
Definition doxygen.cpp:7447
static void dumpSymbolMap()
static void buildTypedefList(const Entry *root)
Definition doxygen.cpp:3355
static void findGroupScope(const Entry *root)
Definition doxygen.cpp:440
static void generateFileDocs()
Definition doxygen.cpp:8668
static void findDefineDocumentation(Entry *root)
Definition doxygen.cpp:9482
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:4517
void parseInput()
static void findMemberDocumentation(const Entry *root)
Definition doxygen.cpp:7417
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:6078
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:5713
static void distributeMemberGroupDocumentation()
Definition doxygen.cpp:9239
static void generateNamespaceClassDocs(const ClassLinkedRefMap &classList)
static void addEnumValuesToEnums(const Entry *root)
Definition doxygen.cpp:7650
static void generatePageDocs()
Definition doxygen.cpp:9857
static void resolveUserReferences()
Definition doxygen.cpp:9789
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:3491
static void buildSequenceList(const Entry *root)
Definition doxygen.cpp:3454
static void generateFileSources()
Definition doxygen.cpp:8502
static QCString substituteTemplatesInString(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists, const std::string &src)
Definition doxygen.cpp:5950
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:2587
static void generateClassDocs()
Definition doxygen.cpp:9094
static void buildNamespaceList(const Entry *root)
Definition doxygen.cpp:1671
static int computeIdealCacheParam(size_t v)
static void readTagFile(const std::shared_ptr< Entry > &root, const QCString &tagLine)
static void findIncludedUsingDirectives()
Definition doxygen.cpp:2409
static void addDefineDoc(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:9457
static void addMemberDocs(const Entry *root, MemberDefMutable *md, const QCString &funcDecl, const ArgumentList *al, bool over_load, TypeSpecifier spec)
Definition doxygen.cpp:5527
static void countMembers()
Definition doxygen.cpp:8955
void clearAll()
Definition doxygen.cpp:201
static void devUsage()
static void addInterfaceOrServiceToServiceOrSingleton(const Entry *root, ClassDefMutable *cd, QCString const &rname)
Definition doxygen.cpp:3523
static void organizeSubGroups(const Entry *root)
Definition doxygen.cpp:484
static void applyMemberOverrideOptions(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:2098
void searchInputFiles()
static void findFriends()
Definition doxygen.cpp:4190
static void findEnums(const Entry *root)
Definition doxygen.cpp:7475
static void dumpSymbol(TextStream &t, Definition *d)
static void addClassAndNestedClasses(std::vector< ClassDefMutable * > &list, ClassDefMutable *cd)
Definition doxygen.cpp:9071
static void addEnumDocs(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:7899
static void addListReferences()
Definition doxygen.cpp:5444
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:5190
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:4482
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:5221
static void addMembersToIndex()
Definition doxygen.cpp:8093
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:3773
static OutputList * g_outputList
Definition doxygen.cpp:188
static void findMainPage(Entry *root)
Definition doxygen.cpp:9657
static ClassDef::CompoundType convertToCompoundType(EntryType section, TypeSpecifier specifier)
Definition doxygen.cpp:889
static void findUsingDirectives(const Entry *root)
Definition doxygen.cpp:1851
std::unique_ptr< ArgumentList > getTemplateArgumentsFromName(const QCString &name, const ArgumentLists &tArgLists)
Definition doxygen.cpp:859
static bool g_successfulRun
Definition doxygen.cpp:190
static void addSourceReferences()
Definition doxygen.cpp:8728
std::function< std::unique_ptr< T >() > make_parser_factory()
static void buildExampleList(Entry *root)
Definition doxygen.cpp:9874
static void inheritDocumentation()
Definition doxygen.cpp:9141
static void flushUnresolvedRelations()
Definition doxygen.cpp:9385
static bool isSymbolHidden(const Definition *d)
Definition doxygen.cpp:8888
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:8085
static void findTagLessClasses()
Definition doxygen.cpp:1647
static void generateDiskNames()
static void addToIndices()
Definition doxygen.cpp:8135
static void computeClassRelations()
Definition doxygen.cpp:5316
static void buildFunctionList(const Entry *root)
Definition doxygen.cpp:3881
static void checkPageRelations()
Definition doxygen.cpp:9769
static void findModuleDocumentation(const Entry *root)
Definition doxygen.cpp:1266
static void findEnumDocumentation(const Entry *root)
Definition doxygen.cpp:7973
static void computePageRelations(Entry *root)
Definition doxygen.cpp:9739
static void filterMemberDocumentation(const Entry *root, const QCString &relates)
Definition doxygen.cpp:7267
static QCString createOutputDirectory(const QCString &baseDirName, const QCString &formatDirName, const char *defaultDirName)
void initResources()
static bool isVarWithConstructor(const Entry *root)
Definition doxygen.cpp:2888
static StringSet g_usingDeclarations
Definition doxygen.cpp:189
static void buildDictionaryList(const Entry *root)
Definition doxygen.cpp:3472
static bool haveEqualFileNames(const Entry *root, const MemberDef *md)
Definition doxygen.cpp:9444
static void generateNamespaceDocs()
static void buildClassDocList(const Entry *root)
Definition doxygen.cpp:1137
static void buildPageList(Entry *root)
Definition doxygen.cpp:9630
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:9277
static void mergeCategories()
Definition doxygen.cpp:8450
static const StringUnorderedSet g_compoundKeywords
Definition doxygen.cpp:198
static bool scopeIsTemplate(const Definition *d)
Definition doxygen.cpp:5936
static void buildFileList(const Entry *root)
Definition doxygen.cpp:496
static ClassDefMutable * createTagLessInstance(const ClassDef *rootCd, const ClassDef *templ, const QCString &fieldName)
Definition doxygen.cpp:1480
static void buildClassList(const Entry *root)
Definition doxygen.cpp: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:3637
static std::unique_ptr< OutlineParserInterface > getParserForFile(const QCString &fn)
static void findUsedTemplateInstances()
Definition doxygen.cpp:5280
static void computeTooltipTexts()
Definition doxygen.cpp:8895
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:3100
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:6475
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:4675
static void parseFilesSingleThreading(const std::shared_ptr< Entry > &root)
parse the list of input files
static void buildCompleteMemberLists()
Definition doxygen.cpp:8472
static void generateConceptDocs()
Definition doxygen.cpp:9120
static void combineUsingRelations()
Definition doxygen.cpp:9176
static const char * getArg(int argc, char **argv, int &optInd)
static const ClassDef * findClassDefinition(FileDef *fd, NamespaceDef *nd, const QCString &scopeName)
Definition doxygen.cpp:5677
static void checkMarkdownMainfile()
static std::unordered_map< std::string, std::vector< ClassDefMutable * > > g_usingClassMap
Definition doxygen.cpp:2157
static void buildConceptDocList(const Entry *root)
Definition doxygen.cpp:1286
static int findTemplateSpecializationPosition(const QCString &name)
Definition doxygen.cpp:4819
static bool isEntryInGroupOfMember(const Entry *root, const MemberDef *md, bool allowNoGroup=false)
Definition doxygen.cpp:5689
static void parseFilesMultiThreading(const std::shared_ptr< Entry > &root)
parse the list of input files
static void transferFunctionReferences()
Definition doxygen.cpp:4320
static void buildDefineList()
Definition doxygen.cpp:8807
static void buildInterfaceAndServiceList(const Entry *root)
Definition doxygen.cpp:3585
static void computeMemberRelations()
Definition doxygen.cpp:8415
static void buildListOfUsingDecls(const Entry *root)
Definition doxygen.cpp:1993
static void computeMemberRelationsForBaseClass(const ClassDef *cd, const BaseClassDef *bcd)
Definition doxygen.cpp:8335
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:267
FileDef * toFileDef(Definition *d)
Definition filedef.cpp:1894
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:174
void addConceptToGroups(const Entry *root, ConceptDef *cd)
void addMemberToGroups(const Entry *root, MemberDef *md)
void startTitle(OutputList &ol, const QCString &fileName, const DefinitionMutable *def)
Definition index.cpp:386
void endFile(OutputList &ol, bool skipNavIndex, bool skipEndContents, const QCString &navPath)
Definition index.cpp:421
void writeGraphInfo(OutputList &ol)
Definition index.cpp:3945
void endTitle(OutputList &ol, const QCString &fileName, const QCString &name)
Definition index.cpp:395
void startFile(OutputList &ol, const QCString &name, const QCString &manName, const QCString &title, HighlightedItem hli, bool additionalIndices, const QCString &altSidebarName, int hierarchyLevel)
Definition index.cpp:402
void writeIndexHierarchy(OutputList &ol)
Definition index.cpp:5585
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:196
void initWarningFormat()
Definition message.cpp:218
void warn_flush()
Definition message.cpp:211
void finishWarnExit()
Definition message.cpp:276
#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:533
bool isAbsolutePath(const QCString &fileName)
Definition portable.cpp:514
FILE * popen(const QCString &name, const QCString &type)
Definition portable.cpp:496
std::ofstream openOutputStream(const QCString &name, bool append=false)
Definition portable.cpp:665
double getSysElapsedTime()
Definition portable.cpp:98
QCString pathListSeparator()
Definition portable.cpp:400
uint32_t pid()
Definition portable.cpp:265
int pclose(FILE *stream)
Definition portable.cpp:505
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:303
const char * commandExtension()
Definition portable.cpp:478
QCString getenv(const QCString &variable)
Definition portable.cpp:338
void setShortDir()
Definition portable.cpp:570
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:79
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:442
QCString substitute(const QCString &s, const QCString &src, const QCString &dst)
substitute all occurrences of src in s by dst
Definition qcstring.cpp:477
int qstricmp_sort(const char *str1, const char *str2)
Definition qcstring.h:86
const char * qPrint(const char *s)
Definition qcstring.h:672
#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:37
QCString name
Definition arguments.h:39
QCString defval
Definition arguments.h:41
QCString array
Definition arguments.h:40
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:65
QCString groupname
name of the group
Definition types.h:95
@ GROUPING_INGROUP
membership in group was defined by @ingroup
Definition types.h:74
static const char * getGroupPriName(GroupPri_t priority)
Definition types.h:78
GroupPri_t pri
priority of this definition
Definition types.h:96
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
@ Property
Definition types.h:32
MemberType
Definition types.h:390
@ Enumeration
Definition types.h:395
@ EnumValue
Definition types.h:396
@ Dictionary
Definition types.h:406
@ Interface
Definition types.h:403
@ Sequence
Definition types.h:405
@ Variable
Definition types.h:393
@ Property
Definition types.h:401
@ Typedef
Definition types.h:394
@ Function
Definition types.h:392
@ Service
Definition types.h:404
Protection
Protection level of members.
Definition types.h:26
@ Public
Definition types.h:26
@ Private
Definition types.h:26
@ Protected
Definition types.h:26
SrcLangExt
Language as given by extension.
Definition types.h:42
@ CSharp
Definition types.h:46
@ Fortran
Definition types.h:53
@ Unknown
Definition types.h:43
@ Python
Definition types.h:52
Relationship
Kind of member relationship.
Definition types.h:38
@ MemberOf
Definition types.h:35
@ Duplicate
Definition types.h:35
Specifier
Virtualness of a member.
Definition types.h:29
@ Normal
Definition types.h:29
QCString removeRedundantWhiteSpace(const QCString &s)
Definition util.cpp:578
QCString mergeScopes(const QCString &leftScope, const QCString &rightScope)
Definition util.cpp:5020
QCString normalizeNonTemplateArgumentsInString(const QCString &name, const Definition *context, const ArgumentList &formalArgs)
Definition util.cpp:4726
SrcLangExt getLanguageFromFileName(const QCString &fileName, SrcLangExt defLang)
Definition util.cpp:5645
bool protectionLevelVisible(Protection prot)
Definition util.cpp:6366
void addCodeOnlyMappings()
Definition util.cpp:5639
QCString substituteTemplateArgumentsInString(const QCString &nm, const ArgumentList &formalArgs, const ArgumentList *actualArgs)
Definition util.cpp:4791
int extractClassNameFromType(const QCString &type, int &pos, QCString &name, QCString &templSpec, SrcLangExt lang)
Definition util.cpp:4652
bool leftScopeMatch(const QCString &scope, const QCString &name)
Definition util.cpp:892
QCString stripAnonymousNamespaceScope(const QCString &s)
Definition util.cpp:241
QCString stripFromIncludePath(const QCString &path)
Definition util.cpp:340
bool checkIfTypedef(const Definition *scope, const FileDef *fileScope, const QCString &n)
Definition util.cpp:5749
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:5937
bool patternMatch(const FileInfo &fi, const StringVector &patList)
Definition util.cpp:6097
bool openOutputFile(const QCString &outFile, std::ofstream &f)
Definition util.cpp:6720
QCString tempArgListToString(const ArgumentList &al, SrcLangExt lang, bool includeDefault)
Definition util.cpp:1219
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:5242
QCString showFileDefMatches(const FileNameLinkedMap *fnMap, const QCString &n)
Definition util.cpp:3474
bool found
Definition util.cpp:984
QCString fileToString(const QCString &name, bool filter, bool isSourceCode)
Definition util.cpp:1414
QCString filterTitle(const QCString &title)
Definition util.cpp:6017
QCString removeAnonymousScopes(const QCString &str)
Definition util.cpp:172
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:1931
QCString resolveTypeDef(const Definition *context, const QCString &qualifiedName, const Definition **typedefContext)
Definition util.cpp:384
bool checkExtension(const QCString &fName, const QCString &ext)
Definition util.cpp:5334
int computeQualifiedIndex(const QCString &name)
Return the index of the last :: in the string name that is still before the first <.
Definition util.cpp:7248
void initDefaultExtensionMapping()
Definition util.cpp:5572
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:5421
QCString convertNameToFile(const QCString &name, bool allowDots, bool allowUnderscore)
Definition util.cpp:3944
QCString langToString(SrcLangExt lang)
Returns a string representation of lang.
Definition util.cpp:6300
EntryType guessSection(const QCString &name)
Definition util.cpp:349
void extractNamespaceName(const QCString &scopeName, QCString &className, QCString &namespaceName, bool allowEmptyClass)
Definition util.cpp:4137
QCString argListToString(const ArgumentList &al, bool useCanonicalType, bool showDefVals)
Definition util.cpp:1174
QCString getLanguageSpecificSeparator(SrcLangExt lang, bool classScope)
Returns the scope separator to use given the programming language lang.
Definition util.cpp:6326
void mergeMemberOverrideOptions(MemberDefMutable *md1, MemberDefMutable *md2)
Definition util.cpp:7285
QCString mangleCSharpGenericName(const QCString &name)
Definition util.cpp:7337
QCString projectLogoFile()
Definition util.cpp:3581
void mergeArguments(ArgumentList &srcAl, ArgumentList &dstAl, bool forceNameOverwrite)
Definition util.cpp:2025
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:6266
QCString stripTemplateSpecifiersFromScope(const QCString &fullName, bool parentOnly, QCString *pLastScopeStripped, QCString scopeName, bool allowArtificial)
Definition util.cpp:4953
QCString getOverloadDocs()
Definition util.cpp:4526
int getPrefixIndex(const QCString &name)
Definition util.cpp:3676
bool rightScopeMatch(const QCString &scope, const QCString &name)
Definition util.cpp:881
bool updateLanguageMapping(const QCString &extension, const QCString &language)
Definition util.cpp:5540
QCString getFileNameExtension(const QCString &fn)
Definition util.cpp:5687
QCString replaceAnonymousScopes(const QCString &s, const QCString &replacement)
Definition util.cpp:229
FileDef * findFileDef(const FileNameLinkedMap *fnMap, const QCString &n, bool &ambig)
Definition util.cpp:3348
int getScopeFragment(const QCString &s, int p, int *l)
Definition util.cpp:5065
void addHtmlExtensionIfMissing(QCString &fName)
Definition util.cpp:5339
A bunch of utility functions.
void generateXML()
Definition xmlgen.cpp:2178