Doxygen
Loading...
Searching...
No Matches
doxygen.cpp
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * Copyright (C) 1997-2015 by Dimitri van Heesch.
4 *
5 * Permission to use, copy, modify, and distribute this software and its
6 * documentation under the terms of the GNU General Public License is hereby
7 * granted. No representations are made about the suitability of this software
8 * for any purpose. It is provided "as is" without express or implied warranty.
9 * See the GNU General Public License for more details.
10 *
11 * Documents produced by Doxygen are derivative works derived from the
12 * input used in their production; they are not affected by this license.
13 *
14 */
15
16#include <cstdio>
17#include <cstdlib>
18#include <cerrno>
19#include <sys/stat.h>
20
21#include <algorithm>
22#include <unordered_map>
23#include <memory>
24#include <cinttypes>
25#include <chrono>
26#include <clocale>
27#include <locale>
28
29#include "version.h"
30#include "doxygen.h"
31#include "scanner.h"
32#include "entry.h"
33#include "index.h"
34#include "indexlist.h"
35#include "message.h"
36#include "config.h"
37#include "util.h"
38#include "pre.h"
39#include "tagreader.h"
40#include "dot.h"
41#include "msc.h"
42#include "docparser.h"
43#include "dirdef.h"
44#include "outputlist.h"
45#include "declinfo.h"
46#include "htmlgen.h"
47#include "latexgen.h"
48#include "mangen.h"
49#include "language.h"
50#include "debug.h"
51#include "htmlhelp.h"
52#include "qhp.h"
53#include "sitemap.h"
54#include "ftvhelp.h"
55#include "defargs.h"
56#include "rtfgen.h"
57#include "sqlite3gen.h"
58#include "xmlgen.h"
59#include "docbookgen.h"
60#include "defgen.h"
61#include "perlmodgen.h"
62#include "reflist.h"
63#include "pagedef.h"
64#include "commentcnv.h"
65#include "cmdmapper.h"
66#include "searchindex_js.h"
67#include "parserintf.h"
68#include "htags.h"
69#include "pycode.h"
70#include "pyscanner.h"
71#include "fortrancode.h"
72#include "fortranscanner.h"
73#include "xmlcode.h"
74#include "sqlcode.h"
75#include "lexcode.h"
76#include "lexscanner.h"
77#include "code.h"
78#include "portable.h"
79#include "vhdljjparser.h"
80#include "vhdldocgen.h"
81#include "vhdlcode.h"
82#include "eclipsehelp.h"
83#include "cite.h"
84#include "markdown.h"
85#include "arguments.h"
86#include "memberlist.h"
87#include "layout.h"
88#include "groupdef.h"
89#include "classlist.h"
90#include "namespacedef.h"
91#include "filename.h"
92#include "membername.h"
93#include "membergroup.h"
94#include "docsets.h"
95#include "formula.h"
96#include "settings.h"
97#include "fileparser.h"
98#include "emoji.h"
99#include "plantuml.h"
100#include "stlsupport.h"
101#include "threadpool.h"
102#include "clangparser.h"
103#include "symbolresolver.h"
104#include "regex.h"
105#include "aliases.h"
106#include "fileinfo.h"
107#include "dir.h"
108#include "conceptdef.h"
109#include "trace.h"
110#include "moduledef.h"
111#include "stringutil.h"
112#include "singlecomment.h"
113
114#include <sqlite3.h>
115
116#if USE_LIBCLANG
117#if defined(__GNUC__)
118#pragma GCC diagnostic push
119#pragma GCC diagnostic ignored "-Wshadow"
120#endif
121#include <clang/Basic/Version.h>
122#if defined(__GNUC__)
123#pragma GCC diagnostic pop
124#endif
125#endif
126
127// provided by the generated file resources.cpp
128extern void initResources();
129
130#if !defined(_WIN32) || defined(__CYGWIN__)
131#include <signal.h>
132#define HAS_SIGNALS
133#endif
134
135// globally accessible variables
147FileNameLinkedMap *Doxygen::includeNameLinkedMap = nullptr; // include names
153FileNameLinkedMap *Doxygen::plantUmlFileNameLinkedMap = nullptr;// plantuml files
155StringMap Doxygen::tagDestinationMap; // all tag locations
156StringUnorderedSet Doxygen::tagFileSet; // all tag file names
157StringUnorderedSet Doxygen::expandAsDefinedSet; // all macros that should be expanded
158MemberGroupInfoMap Doxygen::memberGroupInfoMap; // dictionary of the member groups heading
159std::unique_ptr<PageDef> Doxygen::mainPage;
160std::unique_ptr<NamespaceDef> Doxygen::globalNamespaceDef;
182std::mutex Doxygen::addExampleMutex;
184
185// locally accessible globals
186static std::multimap< std::string, const Entry* > g_classEntries;
188static OutputList *g_outputList = nullptr; // list of output generating objects
189static StringSet g_usingDeclarations; // used classes
190static bool g_successfulRun = FALSE;
191static bool g_dumpSymbolMap = FALSE;
193static bool g_singleComment=false;
194
195
196
197// keywords recognized as compounds
199{ "template class", "template struct", "class", "struct", "union", "interface", "exception" };
200
202{
203 g_inputFiles.clear();
204 //g_excludeNameDict.clear();
205 //delete g_outputList; g_outputList=nullptr;
206
211 Doxygen::pageLinkedMap->clear();
224 Doxygen::mainPage.reset();
226}
227
229{
230 public:
232 void begin(const char *name)
233 {
234 msg("{}", name);
235 stats.emplace_back(name,0);
236 startTime = std::chrono::steady_clock::now();
237 }
238 void end()
239 {
240 std::chrono::steady_clock::time_point endTime = std::chrono::steady_clock::now();
241 stats.back().elapsed = static_cast<double>(std::chrono::duration_cast<
242 std::chrono::microseconds>(endTime - startTime).count())/1000000.0;
243 warn_flush();
244 }
245 void print()
246 {
247 bool restore=FALSE;
249 {
251 restore=TRUE;
252 }
253 msg("----------------------\n");
254 for (const auto &s : stats)
255 {
256 msg("Spent {:.6f} seconds in {}",s.elapsed,s.name);
257 }
258 if (restore) Debug::setFlag(Debug::Time);
259 }
260 private:
261 struct stat
262 {
263 const char *name;
264 double elapsed;
265 //stat() : name(nullptr),elapsed(0) {}
266 stat(const char *n, double el) : name(n),elapsed(el) {}
267 };
268 std::vector<stat> stats;
269 std::chrono::steady_clock::time_point startTime;
271
272
273static void addMemberDocs(const Entry *root,MemberDefMutable *md, const QCString &funcDecl,
274 const ArgumentList *al,bool over_load,TypeSpecifier spec);
275static void findMember(const Entry *root,
276 const QCString &relates,
277 const QCString &type,
278 const QCString &args,
279 QCString funcDecl,
280 bool overloaded,
281 bool isFunc
282 );
283
290
291
292static bool findClassRelation(
293 const Entry *root,
294 Definition *context,
295 ClassDefMutable *cd,
296 const BaseInfo *bi,
297 const TemplateNameMap &templateNames,
298 /*bool insertUndocumented*/
300 bool isArtificial
301 );
302
303//----------------------------------------------------------------------------
304
306 FileDef *fileScope,const TagInfo *tagInfo);
307static void resolveTemplateInstanceInType(const Entry *root,const Definition *scope,const MemberDef *md);
308
309static void addPageToContext(PageDef *pd,Entry *root)
310{
311 if (root->parent()) // add the page to it's scope
312 {
313 QCString scope = root->parent()->name;
314 if (root->parent()->section.isPackageDoc())
315 {
316 scope=substitute(scope,".","::");
317 }
318 scope = stripAnonymousNamespaceScope(scope);
319 scope+="::"+pd->name();
321 if (d)
322 {
323 pd->setPageScope(d);
324 }
325 }
326}
327
328static void addRelatedPage(Entry *root)
329{
330 GroupDef *gd=nullptr;
331 for (const Grouping &g : root->groups)
332 {
333 if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname))) break;
334 }
335 //printf("---> addRelatedPage() %s gd=%p\n",qPrint(root->name),gd);
336 QCString doc=root->doc+root->inbodyDocs;
337
338 PageDef *pd = addRelatedPage(root->name,root->args,doc,
339 root->docFile,
340 root->docLine,
341 root->startLine,
342 root->sli,
343 gd,root->tagInfo(),
344 FALSE,
345 root->lang
346 );
347 if (pd)
348 {
349 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
351 pd->setLocalToc(root->localToc);
352 addPageToContext(pd,root);
353 }
354}
355
356static void buildGroupListFiltered(const Entry *root,bool additional, bool includeExternal)
357{
358 if (root->section.isGroupDoc() && !root->name.isEmpty() &&
359 ((!includeExternal && root->tagInfo()==nullptr) ||
360 ( includeExternal && root->tagInfo()!=nullptr))
361 )
362 {
363 AUTO_TRACE("additional={} includeExternal={}",additional,includeExternal);
364 if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
365 (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
366 {
367 GroupDef *gd = Doxygen::groupLinkedMap->find(root->name);
368 AUTO_TRACE_ADD("Processing group '{}':'{}' gd={}", root->type,root->name,(void*)gd);
369
370 if (gd)
371 {
372 if ( !gd->hasGroupTitle() )
373 {
374 gd->setGroupTitle( root->type );
375 }
376 else if ( root->type.length() > 0 && root->name != root->type && gd->groupTitle() != root->type )
377 {
378 warn( root->fileName,root->startLine,
379 "group {}: ignoring title \"{}\" that does not match old title \"{}\"",
380 root->name, root->type, gd->groupTitle() );
381 }
382 gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
383 gd->setDocumentation( root->doc, root->docFile, root->docLine );
384 gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
386 gd->setRefItems(root->sli);
387 gd->setLanguage(root->lang);
389 {
390 root->commandOverrides.apply_groupGraph([&](bool b) { gd->overrideGroupGraph(b); });
391 }
392 }
393 else
394 {
395 if (root->tagInfo())
396 {
397 gd = Doxygen::groupLinkedMap->add(root->name,
398 std::unique_ptr<GroupDef>(
399 createGroupDef(root->fileName,root->startLine,root->name,root->type,root->tagInfo()->fileName)));
400 gd->setReference(root->tagInfo()->tagName);
401 }
402 else
403 {
404 gd = Doxygen::groupLinkedMap->add(root->name,
405 std::unique_ptr<GroupDef>(
406 createGroupDef(root->fileName,root->startLine,root->name,root->type)));
407 }
408 gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
409 // allow empty docs for group
410 gd->setDocumentation(!root->doc.isEmpty() ? root->doc : QCString(" "),root->docFile,root->docLine,FALSE);
411 gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
413 gd->setRefItems(root->sli);
414 gd->setLanguage(root->lang);
416 {
417 root->commandOverrides.apply_groupGraph([&](bool b) { gd->overrideGroupGraph(b); });
418 }
419 }
420 }
421 }
422 for (const auto &e : root->children()) buildGroupListFiltered(e.get(),additional,includeExternal);
423}
424
425static void buildGroupList(const Entry *root)
426{
427 // --- first process only local groups
428 // first process the @defgroups blocks
430 // then process the @addtogroup, @weakgroup blocks
432
433 // --- then also process external groups
434 // first process the @defgroups blocks
436 // then process the @addtogroup, @weakgroup blocks
438}
439
440static void findGroupScope(const Entry *root)
441{
442 if (root->section.isGroupDoc() && !root->name.isEmpty() &&
443 root->parent() && !root->parent()->name.isEmpty())
444 {
445 GroupDef *gd = Doxygen::groupLinkedMap->find(root->name);
446 if (gd)
447 {
448 QCString scope = root->parent()->name;
449 if (root->parent()->section.isPackageDoc())
450 {
451 scope=substitute(scope,".","::");
452 }
453 scope = stripAnonymousNamespaceScope(scope);
454 scope+="::"+gd->name();
456 if (d)
457 {
458 gd->setGroupScope(d);
459 }
460 }
461 }
462 for (const auto &e : root->children()) findGroupScope(e.get());
463}
464
465static void organizeSubGroupsFiltered(const Entry *root,bool additional)
466{
467 if (root->section.isGroupDoc() && !root->name.isEmpty())
468 {
469 AUTO_TRACE("additional={}",additional);
470 if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
471 (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
472 {
473 GroupDef *gd = Doxygen::groupLinkedMap->find(root->name);
474 if (gd)
475 {
476 AUTO_TRACE_ADD("adding {} to group {}",root->name,gd->name());
477 addGroupToGroups(root,gd);
478 }
479 }
480 }
481 for (const auto &e : root->children()) organizeSubGroupsFiltered(e.get(),additional);
482}
483
484static void organizeSubGroups(const Entry *root)
485{
486 //printf("Defining groups\n");
487 // first process the @defgroups blocks
489 //printf("Additional groups\n");
490 // then process the @addtogroup, @weakgroup blocks
492}
493
494//----------------------------------------------------------------------
495
496static void buildFileList(const Entry *root)
497{
498 if ((root->section.isFileDoc() || (root->section.isFile() && Config_getBool(EXTRACT_ALL))) &&
499 !root->name.isEmpty() && !root->tagInfo() // skip any file coming from tag files
500 )
501 {
502 bool ambig = false;
504 if (!fd || ambig)
505 {
506 bool save_ambig = ambig;
507 // use the directory of the file to see if the described file is in the same
508 // directory as the describing file.
509 QCString fn = root->fileName;
510 int newIndex=fn.findRev('/');
511 if (newIndex<0)
512 {
513 fn = root->name;
514 }
515 else
516 {
517 fn = fn.left(newIndex)+"/"+root->name;
518 }
520 if (!fd) ambig = save_ambig;
521 }
522 //printf("**************** root->name=%s fd=%p\n",qPrint(root->name),(void*)fd);
523 if (fd && !ambig)
524 {
525 //printf("Adding documentation!\n");
526 // using FALSE in setDocumentation is small hack to make sure a file
527 // is documented even if a \file command is used without further
528 // documentation
529 fd->setDocumentation(root->doc,root->docFile,root->docLine,FALSE);
530 fd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
532 fd->setRefItems(root->sli);
533 root->commandOverrides.apply_includeGraph ([&](bool b) { fd->overrideIncludeGraph(b); });
534 root->commandOverrides.apply_includedByGraph([&](bool b) { fd->overrideIncludedByGraph(b); });
535 for (const Grouping &g : root->groups)
536 {
537 GroupDef *gd=nullptr;
538 if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname)))
539 {
540 if (!gd->containsFile(fd))
541 {
542 gd->addFile(fd);
543 fd->makePartOfGroup(gd);
544 //printf("File %s: in group %s\n",qPrint(fd->name()),qPrint(gd->name()));
545 }
546 }
547 else if (!gd && g.pri == Grouping::GROUPING_INGROUP)
548 {
549 warn(root->fileName, root->startLine,
550 "Found non-existing group '{}' for the command '{}', ignoring command",
552 );
553 }
554 }
555 }
556 else
557 {
559 text.sprintf("the name '%s' supplied as "
560 "the argument in the \\file statement ",
561 qPrint(root->name));
562 if (ambig) // name is ambiguous
563 {
564 text+="matches the following input files:\n";
566 text+="\n";
567 text+="Please use a more specific name by "
568 "including a (larger) part of the path!";
569 }
570 else // name is not an input file
571 {
572 text+="is not an input file";
573 }
574 warn(root->fileName,root->startLine,"{}", text);
575 }
576 }
577 for (const auto &e : root->children()) buildFileList(e.get());
578}
579
580template<class DefMutable>
581static void addIncludeFile(DefMutable *cd,FileDef *ifd,const Entry *root)
582{
583 if (
584 (!root->doc.stripWhiteSpace().isEmpty() ||
585 !root->brief.stripWhiteSpace().isEmpty() ||
586 Config_getBool(EXTRACT_ALL)
587 ) && root->protection!=Protection::Private
588 )
589 {
590 //printf(">>>>>> includeFile=%s\n",qPrint(root->includeFile));
591
592 bool local=Config_getBool(FORCE_LOCAL_INCLUDES);
593 QCString includeFile = root->includeFile;
594 if (!includeFile.isEmpty() && includeFile.at(0)=='"')
595 {
596 local = TRUE;
597 includeFile=includeFile.mid(1,includeFile.length()-2);
598 }
599 else if (!includeFile.isEmpty() && includeFile.at(0)=='<')
600 {
601 local = FALSE;
602 includeFile=includeFile.mid(1,includeFile.length()-2);
603 }
604
605 bool ambig = false;
606 FileDef *fd=nullptr;
607 // see if we need to include a verbatim copy of the header file
608 //printf("root->includeFile=%s\n",qPrint(root->includeFile));
609 if (!includeFile.isEmpty() &&
610 (fd=findFileDef(Doxygen::inputNameLinkedMap,includeFile,ambig))==nullptr
611 )
612 { // explicit request
613 QCString text;
614 text.sprintf("the name '%s' supplied as "
615 "the argument of the \\class, \\struct, \\union, or \\include command ",
616 qPrint(includeFile)
617 );
618 if (ambig) // name is ambiguous
619 {
620 text+="matches the following input files:\n";
622 text+="\n";
623 text+="Please use a more specific name by "
624 "including a (larger) part of the path!";
625 }
626 else // name is not an input file
627 {
628 text+="is not an input file";
629 }
630 warn(root->fileName,root->startLine, "{}", text);
631 }
632 else if (includeFile.isEmpty() && ifd &&
633 // see if the file extension makes sense
634 guessSection(ifd->name()).isHeader())
635 { // implicit assumption
636 fd=ifd;
637 }
638
639 // if a file is found, we mark it as a source file.
640 if (fd)
641 {
642 QCString iName = !root->includeName.isEmpty() ?
643 root->includeName : includeFile;
644 if (!iName.isEmpty()) // user specified include file
645 {
646 if (iName.at(0)=='<') local=FALSE; // explicit override
647 else if (iName.at(0)=='"') local=TRUE;
648 if (iName.at(0)=='"' || iName.at(0)=='<')
649 {
650 iName=iName.mid(1,iName.length()-2); // strip quotes or brackets
651 }
652 if (iName.isEmpty())
653 {
654 iName=fd->name();
655 }
656 }
657 else if (!Config_getList(STRIP_FROM_INC_PATH).empty())
658 {
660 }
661 else // use name of the file containing the class definition
662 {
663 iName=fd->name();
664 }
665 if (fd->generateSourceFile()) // generate code for header
666 {
667 cd->setIncludeFile(fd,iName,local,!root->includeName.isEmpty());
668 }
669 else // put #include in the class documentation without link
670 {
671 cd->setIncludeFile(nullptr,iName,local,TRUE);
672 }
673 }
674 }
675}
676
677
679{
680 size_t l = s.length();
681 int count=0;
682 int round=0;
683 QCString result;
684 for (size_t i=0;i<l;i++)
685 {
686 char c=s.at(i);
687 if (c=='(') round++;
688 else if (c==')' && round>0) round--;
689 else if (c=='<' && round==0) count++;
690 if (count==0)
691 {
692 result+=c;
693 }
694 if (c=='>' && round==0 && count>0) count--;
695 }
696 //printf("stripTemplateSpecifiers(%s)=%s\n",qPrint(s),qPrint(result));
697 return result;
698}
699
700/*! returns the Definition object belonging to the first \a level levels of
701 * full qualified name \a name. Creates an artificial scope if the scope is
702 * not found and set the parent/child scope relation if the scope is found.
703 */
704[[maybe_unused]]
705static Definition *buildScopeFromQualifiedName(const QCString &name_,SrcLangExt lang,const TagInfo *tagInfo)
706{
707 QCString name = stripTemplateSpecifiers(name_);
708 name.stripPrefix("::");
709 int level = name.contains("::");
710 //printf("buildScopeFromQualifiedName(%s) level=%d\n",qPrint(name),level);
711 int i=0, p=0, l=0;
713 QCString fullScope;
714 while (i<level)
715 {
716 int idx=getScopeFragment(name,p,&l);
717 if (idx==-1) return prevScope;
718 QCString nsName = name.mid(idx,l);
719 if (nsName.isEmpty()) return prevScope;
720 if (!fullScope.isEmpty()) fullScope+="::";
721 fullScope+=nsName;
722 NamespaceDef *nd=Doxygen::namespaceLinkedMap->find(fullScope);
723 DefinitionMutable *innerScope = toDefinitionMutable(nd);
724 ClassDef *cd=nullptr;
725 if (nd==nullptr) cd = getClass(fullScope);
726 if (nd==nullptr && cd) // scope is a class
727 {
728 innerScope = toDefinitionMutable(cd);
729 }
730 else if (nd==nullptr && cd==nullptr && fullScope.find('<')==-1) // scope is not known and could be a namespace!
731 {
732 // introduce bogus namespace
733 //printf("++ adding dummy namespace %s to %s tagInfo=%p\n",qPrint(nsName),qPrint(prevScope->name()),(void*)tagInfo);
734 NamespaceDefMutable *newNd=
736 Doxygen::namespaceLinkedMap->add(fullScope,
738 "[generated]",1,1,fullScope,
739 tagInfo?tagInfo->tagName:QCString(),
740 tagInfo?tagInfo->fileName:QCString())));
741 if (newNd)
742 {
743 newNd->setLanguage(lang);
744 newNd->setArtificial(TRUE);
745 // add namespace to the list
746 innerScope = newNd;
747 }
748 }
749 else // scope is a namespace
750 {
751 }
752 if (innerScope)
753 {
754 // make the parent/child scope relation
755 DefinitionMutable *prevScopeMutable = toDefinitionMutable(prevScope);
756 if (prevScopeMutable)
757 {
758 prevScopeMutable->addInnerCompound(toDefinition(innerScope));
759 }
760 innerScope->setOuterScope(prevScope);
761 }
762 else // current scope is a class, so return only the namespace part...
763 {
764 return prevScope;
765 }
766 // proceed to the next scope fragment
767 p=idx+l+2;
768 prevScope=toDefinition(innerScope);
769 i++;
770 }
771 return prevScope;
772}
773
775 FileDef *fileScope,const TagInfo *tagInfo)
776{
777 //printf("<findScopeFromQualifiedName(%s,%s)\n",startScope ? qPrint(startScope->name()) : 0, qPrint(n));
778 Definition *resultScope=toDefinition(startScope);
779 if (resultScope==nullptr) resultScope=Doxygen::globalScope;
781 int l1 = 0;
782 int i1 = getScopeFragment(scope,0,&l1);
783 if (i1==-1)
784 {
785 //printf(">no fragments!\n");
786 return resultScope;
787 }
788 int p=i1+l1,l2=0,i2=0;
789 while ((i2=getScopeFragment(scope,p,&l2))!=-1)
790 {
791 QCString nestedNameSpecifier = scope.mid(i1,l1);
792 Definition *orgScope = resultScope;
793 //printf(" nestedNameSpecifier=%s\n",qPrint(nestedNameSpecifier));
794 resultScope = const_cast<Definition*>(resultScope->findInnerCompound(nestedNameSpecifier));
795 //printf(" resultScope=%p\n",resultScope);
796 if (resultScope==nullptr)
797 {
798 if (orgScope==Doxygen::globalScope && fileScope && !fileScope->getUsedNamespaces().empty())
799 // also search for used namespaces
800 {
801 for (const auto &nd : fileScope->getUsedNamespaces())
802 {
804 if (mnd)
805 {
806 resultScope = findScopeFromQualifiedName(toNamespaceDefMutable(nd),n,fileScope,tagInfo);
807 if (resultScope!=nullptr) break;
808 }
809 }
810 if (resultScope)
811 {
812 // for a nested class A::I in used namespace N, we get
813 // N::A::I while looking for A, so we should compare
814 // resultScope->name() against scope.left(i2+l2)
815 //printf(" -> result=%s scope=%s\n",qPrint(resultScope->name()),qPrint(scope));
816 if (rightScopeMatch(resultScope->name(),scope.left(i2+l2)))
817 {
818 break;
819 }
820 goto nextFragment;
821 }
822 }
823
824 // also search for used classes. Complication: we haven't been able
825 // to put them in the right scope yet, because we are still resolving
826 // the scope relations!
827 // Therefore loop through all used classes and see if there is a right
828 // scope match between the used class and nestedNameSpecifier.
829 for (const auto &usedName : g_usingDeclarations)
830 {
831 //printf("Checking using class %s\n",qPrint(usedName));
832 if (rightScopeMatch(usedName,nestedNameSpecifier))
833 {
834 // ui.currentKey() is the fully qualified name of nestedNameSpecifier
835 // so use this instead.
836 QCString fqn = usedName + scope.right(scope.length()-p);
837 resultScope = buildScopeFromQualifiedName(fqn,startScope->getLanguage(),nullptr);
838 //printf("Creating scope from fqn=%s result %p\n",qPrint(fqn),resultScope);
839 if (resultScope)
840 {
841 //printf("> Match! resultScope=%s\n",qPrint(resultScope->name()));
842 return resultScope;
843 }
844 }
845 }
846
847 //printf("> name %s not found in scope %s\n",qPrint(nestedNameSpecifier),qPrint(orgScope->name()));
848 return nullptr;
849 }
850 nextFragment:
851 i1=i2;
852 l1=l2;
853 p=i2+l2;
854 }
855 //printf(">findScopeFromQualifiedName scope %s\n",qPrint(resultScope->name()));
856 return resultScope;
857}
858
859std::unique_ptr<ArgumentList> getTemplateArgumentsFromName(
860 const QCString &name,
861 const ArgumentLists &tArgLists)
862{
863 // for each scope fragment, check if it is a template and advance through
864 // the list if so.
865 int i=0, p=0;
866 auto alIt = tArgLists.begin();
867 while ((i=name.find("::",p))!=-1 && alIt!=tArgLists.end())
868 {
869 NamespaceDef *nd = Doxygen::namespaceLinkedMap->find(name.left(i));
870 if (nd==nullptr)
871 {
872 ClassDef *cd = getClass(name.left(i));
873 if (cd)
874 {
875 if (!cd->templateArguments().empty())
876 {
877 ++alIt;
878 }
879 }
880 }
881 p=i+2;
882 }
883 return alIt!=tArgLists.end() ?
884 std::make_unique<ArgumentList>(*alIt) :
885 std::unique_ptr<ArgumentList>();
886}
887
888static
890{
892
893 if (specifier.isStruct())
895 else if (specifier.isUnion())
896 sec=ClassDef::Union;
897 else if (specifier.isCategory())
899 else if (specifier.isInterface())
901 else if (specifier.isProtocol())
903 else if (specifier.isException())
905 else if (specifier.isService())
907 else if (specifier.isSingleton())
909
910 if (section.isUnionDoc())
911 sec=ClassDef::Union;
912 else if (section.isStructDoc())
914 else if (section.isInterfaceDoc())
916 else if (section.isProtocolDoc())
918 else if (section.isCategoryDoc())
920 else if (section.isExceptionDoc())
922 else if (section.isServiceDoc())
924 else if (section.isSingletonDoc())
926
927 return sec;
928}
929
930
931static void addClassToContext(const Entry *root)
932{
933 AUTO_TRACE("name={}",root->name);
934 FileDef *fd = root->fileDef();
935
936 QCString scName;
937 if (root->parent()->section.isScope())
938 {
939 scName=root->parent()->name;
940 }
941 // name without parent's scope
942 QCString fullName = root->name;
943
944 // strip off any template parameters (but not those for specializations)
945 int idx=fullName.find('>');
946 if (idx!=-1 && root->lang==SrcLangExt::CSharp) // mangle A<S,T>::N as A-2-g::N
947 {
948 fullName = mangleCSharpGenericName(fullName.left(idx+1))+fullName.mid(idx+1);
949 }
950 fullName=stripTemplateSpecifiersFromScope(fullName);
951
952 // name with scope (if not present already)
953 QCString qualifiedName = fullName;
954 if (!scName.isEmpty() && !leftScopeMatch(scName,fullName))
955 {
956 qualifiedName.prepend(scName+"::");
957 }
958
959 // see if we already found the class before
960 ClassDefMutable *cd = getClassMutable(qualifiedName);
961
962 AUTO_TRACE_ADD("Found class with name '{}', qualifiedName '{}'", cd ? cd->name() : root->name, qualifiedName);
963
964 if (cd)
965 {
966 fullName=cd->name();
967 AUTO_TRACE_ADD("Existing class '{}'",cd->name());
968 //if (cd->templateArguments()==0)
969 //{
970 // //printf("existing ClassDef tempArgList=%p specScope=%s\n",root->tArgList,qPrint(root->scopeSpec));
971 // cd->setTemplateArguments(tArgList);
972 //}
973
974 cd->setDocumentation(root->doc,root->docFile,root->docLine);
975 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
976 root->commandOverrides.apply_collaborationGraph([&](bool b ) { cd->overrideCollaborationGraph(b); });
977 root->commandOverrides.apply_inheritanceGraph ([&](CLASS_GRAPH_t gt) { cd->overrideInheritanceGraph(gt); });
978
979 if (!root->spec.isForwardDecl() && cd->isForwardDeclared())
980 {
981 cd->setDefFile(root->fileName,root->startLine,root->startColumn);
982 if (root->bodyLine!=-1)
983 {
984 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
985 cd->setBodyDef(fd);
986 }
987 }
988
989 if (cd->templateArguments().empty() || (cd->isForwardDeclared() && !root->spec.isForwardDecl()))
990 {
991 // this happens if a template class declared with @class is found
992 // before the actual definition or if a forward declaration has different template
993 // parameter names.
994 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(cd->name(),root->tArgLists);
995 if (tArgList)
996 {
997 cd->setTemplateArguments(*tArgList);
998 }
999 }
1000 if (cd->requiresClause().isEmpty() && !root->req.isEmpty())
1001 {
1002 cd->setRequiresClause(root->req);
1003 }
1004
1006
1007 cd->setMetaData(root->metaData);
1008 }
1009 else // new class
1010 {
1012
1013 QCString className;
1014 QCString namespaceName;
1015 extractNamespaceName(fullName,className,namespaceName);
1016
1017 AUTO_TRACE_ADD("New class: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1018 fullName, namespaceName, className, Trace::trunc(root->brief), Trace::trunc(root->doc));
1019
1020 QCString tagName;
1021 QCString refFileName;
1022 const TagInfo *tagInfo = root->tagInfo();
1023 if (tagInfo)
1024 {
1025 tagName = tagInfo->tagName;
1026 refFileName = tagInfo->fileName;
1027 if (fullName.find("::")!=-1)
1028 // symbols imported via tag files may come without the parent scope,
1029 // so we artificially create it here
1030 {
1031 buildScopeFromQualifiedName(fullName,root->lang,tagInfo);
1032 }
1033 }
1034 std::unique_ptr<ArgumentList> tArgList;
1035 int i=0;
1036 if ((root->lang==SrcLangExt::CSharp || root->lang==SrcLangExt::Java) &&
1037 (i=fullName.find('<'))!=-1)
1038 {
1039 // a Java/C# generic class looks like a C++ specialization, so we need to split the
1040 // name and template arguments here
1041 tArgList = stringToArgumentList(root->lang,fullName.mid(i));
1042 if (i!=-1 && root->lang==SrcLangExt::CSharp) // in C# A, A<T>, and A<T,S> are different classes, so we need some way to disguish them using this name mangling
1043 // A -> A
1044 // A<T> -> A-1-g
1045 // A<T,S> -> A-2-g
1046 {
1047 fullName=mangleCSharpGenericName(fullName);
1048 }
1049 else
1050 {
1051 fullName=fullName.left(i);
1052 }
1053 }
1054 else
1055 {
1056 tArgList = getTemplateArgumentsFromName(fullName,root->tArgLists);
1057 }
1058 // add class to the list
1059 cd = toClassDefMutable(
1060 Doxygen::classLinkedMap->add(fullName,
1061 createClassDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1062 fullName,sec,tagName,refFileName,TRUE,root->spec.isEnum()) ));
1063 if (cd)
1064 {
1065 AUTO_TRACE_ADD("New class '{}' type={} #tArgLists={} tagInfo={} hidden={} artificial={}",
1066 fullName,cd->compoundTypeString(),root->tArgLists.size(),
1067 fmt::ptr(tagInfo),root->hidden,root->artificial);
1068 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1069 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1070 cd->setLanguage(root->lang);
1071 cd->setId(root->id);
1072 cd->setHidden(root->hidden);
1073 cd->setArtificial(root->artificial);
1074 cd->setClassSpecifier(root->spec);
1075 cd->addQualifiers(root->qualifiers);
1076 cd->setTypeConstraints(root->typeConstr);
1077 root->commandOverrides.apply_collaborationGraph([&](bool b ) { cd->overrideCollaborationGraph(b); });
1078 root->commandOverrides.apply_inheritanceGraph ([&](CLASS_GRAPH_t gt) { cd->overrideInheritanceGraph(gt); });
1079
1080 if (tArgList)
1081 {
1082 cd->setTemplateArguments(*tArgList);
1083 }
1084 cd->setRequiresClause(root->req);
1085 cd->setProtection(root->protection);
1086 cd->setIsStatic(root->isStatic);
1087
1088 // file definition containing the class cd
1089 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1090 cd->setBodyDef(fd);
1091
1092 cd->setMetaData(root->metaData);
1093
1094 cd->insertUsedFile(fd);
1095 }
1096 else
1097 {
1098 AUTO_TRACE_ADD("Class {} not added, already exists as alias", fullName);
1099 }
1100 }
1101
1102 if (cd)
1103 {
1105 if (!root->subGrouping) cd->setSubGrouping(FALSE);
1106 if (!root->spec.isForwardDecl())
1107 {
1108 if (cd->hasDocumentation())
1109 {
1110 addIncludeFile(cd,fd,root);
1111 }
1112 if (fd && root->section.isCompound())
1113 {
1114 AUTO_TRACE_ADD("Inserting class {} in file {} (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1115 cd->setFileDef(fd);
1116 fd->insertClass(cd);
1117 }
1118 }
1119 addClassToGroups(root,cd);
1121 cd->setRefItems(root->sli);
1122 }
1123}
1124
1125//----------------------------------------------------------------------
1126// build a list of all classes mentioned in the documentation
1127// and all classes that have a documentation block before their definition.
1128static void buildClassList(const Entry *root)
1129{
1130 if ((root->section.isCompound() || root->section.isObjcImpl()) && !root->name.isEmpty())
1131 {
1132 AUTO_TRACE();
1133 addClassToContext(root);
1134 }
1135 for (const auto &e : root->children()) buildClassList(e.get());
1136}
1137
1138static void buildClassDocList(const Entry *root)
1139{
1140 if ((root->section.isCompoundDoc()) && !root->name.isEmpty())
1141 {
1142 AUTO_TRACE();
1143 addClassToContext(root);
1144 }
1145 for (const auto &e : root->children()) buildClassDocList(e.get());
1146}
1147
1148//----------------------------------------------------------------------
1149// build a list of all classes mentioned in the documentation
1150// and all classes that have a documentation block before their definition.
1151
1152static void addConceptToContext(const Entry *root)
1153{
1154 AUTO_TRACE();
1155 FileDef *fd = root->fileDef();
1156
1157 QCString scName;
1158 if (root->parent()->section.isScope())
1159 {
1160 scName=root->parent()->name;
1161 }
1162
1163 // name with scope (if not present already)
1164 QCString qualifiedName = root->name;
1165 if (!scName.isEmpty() && !leftScopeMatch(qualifiedName,scName))
1166 {
1167 qualifiedName.prepend(scName+"::");
1168 }
1169
1170 // see if we already found the concept before
1171 ConceptDefMutable *cd = getConceptMutable(qualifiedName);
1172
1173 AUTO_TRACE_ADD("Found concept with name '{}' (qualifiedName='{}')", cd ? cd->name() : root->name, qualifiedName);
1174
1175 if (cd)
1176 {
1177 qualifiedName=cd->name();
1178 AUTO_TRACE_ADD("Existing concept '{}'",cd->name());
1179
1180 cd->setDocumentation(root->doc,root->docFile,root->docLine);
1181 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1182
1183 addIncludeFile(cd,fd,root);
1184 }
1185 else // new concept
1186 {
1187 QCString className;
1188 QCString namespaceName;
1189 extractNamespaceName(qualifiedName,className,namespaceName);
1190
1191 AUTO_TRACE_ADD("New concept: fullname '{}' namespace '{}' name='{}' brief='{}' docs='{}'",
1192 qualifiedName,namespaceName,className,root->brief,root->doc);
1193
1194 QCString tagName;
1195 QCString refFileName;
1196 const TagInfo *tagInfo = root->tagInfo();
1197 if (tagInfo)
1198 {
1199 tagName = tagInfo->tagName;
1200 refFileName = tagInfo->fileName;
1201 if (qualifiedName.find("::")!=-1)
1202 // symbols imported via tag files may come without the parent scope,
1203 // so we artificially create it here
1204 {
1205 buildScopeFromQualifiedName(qualifiedName,root->lang,tagInfo);
1206 }
1207 }
1208 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(qualifiedName,root->tArgLists);
1209 // add concept to the list
1211 Doxygen::conceptLinkedMap->add(qualifiedName,
1212 createConceptDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1213 qualifiedName,tagName,refFileName)));
1214 if (cd)
1215 {
1216 AUTO_TRACE_ADD("New concept '{}' #tArgLists={} tagInfo={}",
1217 qualifiedName,root->tArgLists.size(),fmt::ptr(tagInfo));
1218 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1219 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1220 cd->setLanguage(root->lang);
1221 cd->setId(root->id);
1222 cd->setHidden(root->hidden);
1223 cd->setGroupId(root->mGrpId);
1224 if (tArgList)
1225 {
1226 cd->setTemplateArguments(*tArgList);
1227 }
1228 cd->setInitializer(root->initializer.str());
1229 // file definition containing the class cd
1230 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1231 cd->setBodyDef(fd);
1232 addIncludeFile(cd,fd,root);
1233
1234 // also add namespace to the correct structural context
1235 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,qualifiedName,nullptr,tagInfo);
1237 {
1239 if (dm)
1240 {
1241 dm->addInnerCompound(cd);
1242 }
1243 cd->setOuterScope(d);
1244 }
1245 }
1246 else
1247 {
1248 AUTO_TRACE_ADD("Concept '{}' not added, already exists (as alias)", qualifiedName);
1249 }
1250 }
1251
1252 if (cd)
1253 {
1255 if (fd)
1256 {
1257 AUTO_TRACE_ADD("Inserting concept '{}' in file '{}' (root->fileName='{}')", cd->name(), fd->name(), root->fileName);
1258 cd->setFileDef(fd);
1259 fd->insertConcept(cd);
1260 }
1261 addConceptToGroups(root,cd);
1263 cd->setRefItems(root->sli);
1264 }
1265}
1266
1267static void findModuleDocumentation(const Entry *root)
1268{
1269 if (root->section.isModuleDoc())
1270 {
1271 AUTO_TRACE();
1273 }
1274 for (const auto &e : root->children()) findModuleDocumentation(e.get());
1275}
1276
1277static void buildConceptList(const Entry *root)
1278{
1279 if (root->section.isConcept())
1280 {
1281 AUTO_TRACE();
1282 addConceptToContext(root);
1283 }
1284 for (const auto &e : root->children()) buildConceptList(e.get());
1285}
1286
1287static void buildConceptDocList(const Entry *root)
1288{
1289 if (root->section.isConceptDoc())
1290 {
1291 AUTO_TRACE();
1292 addConceptToContext(root);
1293 }
1294 for (const auto &e : root->children()) buildConceptDocList(e.get());
1295}
1296
1297// This routine is to allow @ingroup X @{ concept A; concept B; @} to work
1298// (same also works for variable and functions because of logic in MemberGroup::insertMember)
1300{
1301 AUTO_TRACE();
1302 for (const auto &cd : *Doxygen::conceptLinkedMap)
1303 {
1304 if (cd->groupId()!=DOX_NOGROUP)
1305 {
1306 for (const auto &ocd : *Doxygen::conceptLinkedMap)
1307 {
1308 if (cd!=ocd && cd->groupId()==ocd->groupId() &&
1309 !cd->partOfGroups().empty() && ocd->partOfGroups().empty())
1310 {
1311 ConceptDefMutable *ocdm = toConceptDefMutable(ocd.get());
1312 if (ocdm)
1313 {
1314 for (const auto &gd : cd->partOfGroups())
1315 {
1316 if (gd)
1317 {
1318 AUTO_TRACE_ADD("making concept '{}' part of group '{}'",ocdm->name(),gd->name());
1319 ocdm->makePartOfGroup(gd);
1320 gd->addConcept(ocd.get());
1321 }
1322 }
1323 }
1324 }
1325 }
1326 }
1327 }
1328}
1329
1330//----------------------------------------------------------------------
1331
1333{
1334 ClassDefSet visitedClasses;
1335
1336 bool done=FALSE;
1337 //int iteration=0;
1338 while (!done)
1339 {
1340 done=TRUE;
1341 //++iteration;
1342 struct ClassAlias
1343 {
1344 ClassAlias(const QCString &name,std::unique_ptr<ClassDef> cd,DefinitionMutable *ctx) :
1345 aliasFullName(name),aliasCd(std::move(cd)), aliasContext(ctx) {}
1346 QCString aliasFullName;
1347 std::unique_ptr<ClassDef> aliasCd;
1348 DefinitionMutable *aliasContext;
1349 };
1350 std::vector<ClassAlias> aliases;
1351 for (const auto &icd : *Doxygen::classLinkedMap)
1352 {
1353 ClassDefMutable *cd = toClassDefMutable(icd.get());
1354 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1355 {
1356 QCString name = stripAnonymousNamespaceScope(icd->name());
1357 //printf("processing=%s, iteration=%d\n",qPrint(cd->name()),iteration);
1358 // also add class to the correct structural context
1360 name,icd->getFileDef(),nullptr);
1361 if (d)
1362 {
1363 //printf("****** adding %s to scope %s in iteration %d\n",qPrint(cd->name()),qPrint(d->name()),iteration);
1365 if (dm)
1366 {
1367 dm->addInnerCompound(cd);
1368 }
1369 cd->setOuterScope(d);
1370
1371 // for inline namespace add an alias of the class to the outer scope
1373 {
1375 //printf("nd->isInline()=%d\n",nd->isInline());
1376 if (nd && nd->isInline())
1377 {
1378 d = d->getOuterScope();
1379 if (d)
1380 {
1381 dm = toDefinitionMutable(d);
1382 if (dm)
1383 {
1384 auto aliasCd = createClassDefAlias(d,cd);
1385 QCString aliasFullName = d->qualifiedName()+"::"+aliasCd->localName();
1386 aliases.emplace_back(aliasFullName,std::move(aliasCd),dm);
1387 //printf("adding %s to %s as %s\n",qPrint(aliasCd->name()),qPrint(d->name()),qPrint(aliasFullName));
1388 }
1389 }
1390 }
1391 else
1392 {
1393 break;
1394 }
1395 }
1396
1397 visitedClasses.insert(icd.get());
1398 done=FALSE;
1399 }
1400 //else
1401 //{
1402 // printf("****** ignoring %s: scope not (yet) found in iteration %d\n",qPrint(cd->name()),iteration);
1403 //}
1404 }
1405 }
1406 // add aliases
1407 for (auto &alias : aliases)
1408 {
1409 ClassDef *aliasCd = Doxygen::classLinkedMap->add(alias.aliasFullName,std::move(alias.aliasCd));
1410 if (aliasCd)
1411 {
1412 alias.aliasContext->addInnerCompound(aliasCd);
1413 }
1414 }
1415 }
1416
1417 //give warnings for unresolved compounds
1418 for (const auto &icd : *Doxygen::classLinkedMap)
1419 {
1420 ClassDefMutable *cd = toClassDefMutable(icd.get());
1421 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1422 {
1424 /// create the scope artificially
1425 // anyway, so we can at least relate scopes properly.
1426 Definition *d = buildScopeFromQualifiedName(name,cd->getLanguage(),nullptr);
1427 if (d && d!=cd && !cd->getDefFileName().isEmpty())
1428 // avoid recursion in case of redundant scopes, i.e: namespace N { class N::C {}; }
1429 // for this case doxygen assumes the existence of a namespace N::N in which C is to be found!
1430 // also avoid warning for stuff imported via a tagfile.
1431 {
1433 if (dm)
1434 {
1435 dm->addInnerCompound(cd);
1436 }
1437 cd->setOuterScope(d);
1438 warn(cd->getDefFileName(),cd->getDefLine(),
1439 "Incomplete input: scope for class {} not found!{}",name,
1440 name.startsWith("std::") ? " Try enabling BUILTIN_STL_SUPPORT." : ""
1441 );
1442 }
1443 }
1444 }
1445}
1446
1448{
1449 //bool inlineGroupedClasses = Config_getBool(INLINE_GROUPED_CLASSES);
1450 //if (!inlineGroupedClasses) return;
1451 //printf("** distributeClassGroupRelations()\n");
1452
1453 ClassDefSet visitedClasses;
1454 for (const auto &cd : *Doxygen::classLinkedMap)
1455 {
1456 //printf("Checking %s\n",qPrint(cd->name()));
1457 // distribute the group to nested classes as well
1458 if (visitedClasses.find(cd.get())==visitedClasses.end() && !cd->partOfGroups().empty())
1459 {
1460 //printf(" Candidate for merging\n");
1461 GroupDef *gd = cd->partOfGroups().front();
1462 for (auto &ncd : cd->getClasses())
1463 {
1465 if (ncdm && ncdm->partOfGroups().empty())
1466 {
1467 //printf(" Adding %s to group '%s'\n",qPrint(ncd->name()),
1468 // gd->groupTitle());
1469 ncdm->makePartOfGroup(gd);
1470 gd->addClass(ncdm);
1471 }
1472 }
1473 visitedClasses.insert(cd.get()); // only visit every class once
1474 }
1475 }
1476}
1477
1478//----------------------------
1479
1480static ClassDefMutable *createTagLessInstance(const ClassDef *rootCd,const ClassDef *templ,const QCString &fieldName)
1481{
1482 QCString fullName = removeAnonymousScopes(templ->name());
1483 if (fullName.endsWith("::")) fullName=fullName.left(fullName.length()-2);
1484 fullName+="."+fieldName;
1485
1486 //printf("** adding class %s based on %s\n",qPrint(fullName),qPrint(templ->name()));
1488 Doxygen::classLinkedMap->add(fullName,
1490 templ->getDefLine(),
1491 templ->getDefColumn(),
1492 fullName,
1493 templ->compoundType())));
1494 if (cd)
1495 {
1496 cd->setDocumentation(templ->documentation(),templ->docFile(),templ->docLine()); // copy docs to definition
1497 cd->setBriefDescription(templ->briefDescription(),templ->briefFile(),templ->briefLine());
1498 cd->setLanguage(templ->getLanguage());
1499 cd->setBodySegment(templ->getDefLine(),templ->getStartBodyLine(),templ->getEndBodyLine());
1500 cd->setBodyDef(templ->getBodyDef());
1501
1502 cd->setOuterScope(rootCd->getOuterScope());
1503 if (rootCd->getOuterScope()!=Doxygen::globalScope)
1504 {
1505 DefinitionMutable *outerScope = toDefinitionMutable(rootCd->getOuterScope());
1506 if (outerScope)
1507 {
1508 outerScope->addInnerCompound(cd);
1509 }
1510 }
1511
1512 FileDef *fd = templ->getFileDef();
1513 if (fd)
1514 {
1515 cd->setFileDef(fd);
1516 fd->insertClass(cd);
1517 }
1518 for (auto &gd : rootCd->partOfGroups())
1519 {
1520 cd->makePartOfGroup(gd);
1521 gd->addClass(cd);
1522 }
1523
1524 MemberList *ml = templ->getMemberList(MemberListType::PubAttribs());
1525 if (ml)
1526 {
1527 for (const auto &md : *ml)
1528 {
1529 //printf(" Member %s type=%s\n",qPrint(md->name()),md->typeString());
1530 auto newMd = createMemberDef(md->getDefFileName(),md->getDefLine(),md->getDefColumn(),
1531 md->typeString(),md->name(),md->argsString(),md->excpString(),
1532 md->protection(),md->virtualness(),md->isStatic(),Relationship::Member,
1533 md->memberType(),
1534 ArgumentList(),ArgumentList(),"");
1535 MemberDefMutable *imd = toMemberDefMutable(newMd.get());
1536 imd->setMemberClass(cd);
1537 imd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
1538 imd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
1539 imd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
1540 imd->setMemberSpecifiers(md->getMemberSpecifiers());
1541 imd->setVhdlSpecifiers(md->getVhdlSpecifiers());
1542 imd->setMemberGroupId(md->getMemberGroupId());
1543 imd->setInitializer(md->initializer());
1544 imd->setRequiresClause(md->requiresClause());
1545 imd->setMaxInitLines(md->initializerLines());
1546 imd->setBitfields(md->bitfieldString());
1547 imd->setLanguage(md->getLanguage());
1548 cd->insertMember(imd);
1549 MemberName *mn = Doxygen::memberNameLinkedMap->add(md->name());
1550 mn->push_back(std::move(newMd));
1551 }
1552 }
1553 }
1554 return cd;
1555}
1556
1557/** Look through the members of class \a cd and its public members.
1558 * If there is a member m of a tag less struct/union,
1559 * then we create a duplicate of the struct/union with the name of the
1560 * member to identify it.
1561 * So if cd has name S, then the tag less struct/union will get name S.m
1562 * Since tag less structs can be nested we need to call this function
1563 * recursively. Later on we need to patch the member types so we keep
1564 * track of the hierarchy of classes we create.
1565 */
1566static void processTagLessClasses(const ClassDef *rootCd,
1567 const ClassDef *cd,
1568 ClassDefMutable *tagParentCd,
1569 const QCString &prefix,int count)
1570{
1571 //printf("%d: processTagLessClasses %s\n",count,qPrint(cd->name()));
1572 //printf("checking members for %s\n",qPrint(cd->name()));
1573 if (tagParentCd && !cd->getClasses().empty())
1574 {
1575 MemberList *ml = cd->getMemberList(MemberListType::PubAttribs());
1576 if (ml)
1577 {
1578 int pos=0;
1579 for (const auto &md : *ml)
1580 {
1581 QCString type = md->typeString();
1582 if (type.find("::@")!=-1) // member of tag less struct/union
1583 {
1584 for (const auto &icd : cd->getClasses())
1585 {
1586 //printf(" member %s: type='%s'\n",qPrint(md->name()),qPrint(type));
1587 //printf(" comparing '%s'<->'%s'\n",qPrint(type),qPrint(icd->name()));
1588 if (type.find(icd->name())!=-1) // matching tag less struct/union
1589 {
1590 QCString name = md->name();
1591 if (md->isAnonymous()) name = "__unnamed" + QCString().setNum(pos++)+"__";
1592 if (!prefix.isEmpty()) name.prepend(prefix+".");
1593 //printf(" found %s for class %s\n",qPrint(name),qPrint(cd->name()));
1594 ClassDefMutable *ncd = createTagLessInstance(rootCd,icd,name);
1595 if (ncd)
1596 {
1597 processTagLessClasses(rootCd,icd,ncd,name,count+1);
1598 //printf(" addTagged %s to %s\n",qPrint(ncd->name()),qPrint(tagParentCd->name()));
1599 ncd->setTagLessReference(icd);
1600
1601 // replace tag-less type for generated/original member
1602 // by newly created class name.
1603 // note the difference between changing cd and tagParentCd.
1604 // for the initial call this is the same pointer, but for
1605 // recursive calls cd is the original tag-less struct (of which
1606 // there is only one instance) and tagParentCd is the newly
1607 // generated tagged struct of which there can be multiple instances!
1608 MemberList *pml = tagParentCd->getMemberList(MemberListType::PubAttribs());
1609 if (pml)
1610 {
1611 for (const auto &pmd : *pml)
1612 {
1614 if (pmdm && pmd->name()==md->name())
1615 {
1616 pmdm->setAccessorType(ncd,substitute(pmd->typeString(),icd->name(),ncd->name()));
1617 //pmd->setType(substitute(pmd->typeString(),icd->name(),ncd->name()));
1618 }
1619 }
1620 }
1621 }
1622 }
1623 }
1624 }
1625 }
1626 }
1627 }
1628}
1629
1630static void findTagLessClasses(std::vector<ClassDefMutable*> &candidates,ClassDef *cd)
1631{
1632 for (const auto &icd : cd->getClasses())
1633 {
1634 if (icd->name().find("@")==-1) // process all non-anonymous inner classes
1635 {
1636 findTagLessClasses(candidates,icd);
1637 }
1638 }
1639
1641 if (cdm)
1642 {
1643 candidates.push_back(cdm);
1644 }
1645}
1646
1648{
1649 std::vector<ClassDefMutable *> candidates;
1650 for (auto &cd : *Doxygen::classLinkedMap)
1651 {
1652 Definition *scope = cd->getOuterScope();
1653 if (scope && scope->definitionType()!=Definition::TypeClass) // that is not nested
1654 {
1655 findTagLessClasses(candidates,cd.get());
1656 }
1657 }
1658
1659 // since processTagLessClasses is potentially adding classes to Doxygen::classLinkedMap
1660 // we need to call it outside of the loop above, otherwise the iterator gets invalidated!
1661 for (auto &cd : candidates)
1662 {
1663 processTagLessClasses(cd,cd,cd,"",0); // process tag less inner struct/classes
1664 }
1665}
1666
1667
1668//----------------------------------------------------------------------
1669// build a list of all namespaces mentioned in the documentation
1670// and all namespaces that have a documentation block before their definition.
1671static void buildNamespaceList(const Entry *root)
1672{
1673 if (
1674 (root->section.isNamespace() ||
1675 root->section.isNamespaceDoc() ||
1676 root->section.isPackageDoc()
1677 ) &&
1678 !root->name.isEmpty()
1679 )
1680 {
1681 AUTO_TRACE("name={}",root->name);
1682
1683 QCString fName = root->name;
1684 if (root->section.isPackageDoc())
1685 {
1686 fName=substitute(fName,".","::");
1687 }
1688
1689 QCString fullName = stripAnonymousNamespaceScope(fName);
1690 if (!fullName.isEmpty())
1691 {
1692 AUTO_TRACE_ADD("Found namespace {} in {} at line {}",root->name,root->fileName,root->startLine);
1693 NamespaceDef *ndi = Doxygen::namespaceLinkedMap->find(fullName);
1694 if (ndi) // existing namespace
1695 {
1697 if (nd) // non-inline namespace
1698 {
1699 AUTO_TRACE_ADD("Existing namespace");
1700 nd->setDocumentation(root->doc,root->docFile,root->docLine);
1701 nd->setName(fullName); // change name to match docs
1703 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1704 if (nd->getLanguage()==SrcLangExt::Unknown)
1705 {
1706 nd->setLanguage(root->lang);
1707 }
1708 if (root->tagInfo()==nullptr && nd->isReference() && !(root->doc.isEmpty() && root->brief.isEmpty()))
1709 // if we previously found namespace nd in a tag file and now we find a
1710 // documented namespace with the same name in the project, then remove
1711 // the tag file reference
1712 {
1713 nd->setReference("");
1714 nd->setFileName(fullName);
1715 }
1716 nd->setMetaData(root->metaData);
1717
1718 // file definition containing the namespace nd
1719 FileDef *fd=root->fileDef();
1720 if (nd->isArtificial())
1721 {
1722 nd->setArtificial(FALSE); // found namespace explicitly, so cannot be artificial
1723 nd->setDefFile(root->fileName,root->startLine,root->startColumn);
1724 }
1725 // insert the namespace in the file definition
1726 if (fd) fd->insertNamespace(nd);
1727 addNamespaceToGroups(root,nd);
1728 nd->setRefItems(root->sli);
1729 }
1730 }
1731 else // fresh namespace
1732 {
1733 QCString tagName;
1734 QCString tagFileName;
1735 const TagInfo *tagInfo = root->tagInfo();
1736 if (tagInfo)
1737 {
1738 tagName = tagInfo->tagName;
1739 tagFileName = tagInfo->fileName;
1740 }
1741 AUTO_TRACE_ADD("new namespace {} lang={} tagName={}",fullName,langToString(root->lang),tagName);
1742 // add namespace to the list
1744 Doxygen::namespaceLinkedMap->add(fullName,
1745 createNamespaceDef(tagInfo?tagName:root->fileName,root->startLine,
1746 root->startColumn,fullName,tagName,tagFileName,
1747 root->type,root->spec.isPublished())));
1748 if (nd)
1749 {
1750 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1751 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1753 nd->setHidden(root->hidden);
1754 nd->setArtificial(root->artificial);
1755 nd->setLanguage(root->lang);
1756 nd->setId(root->id);
1757 nd->setMetaData(root->metaData);
1758 nd->setInline(root->spec.isInline());
1759 nd->setExported(root->exported);
1760
1761 addNamespaceToGroups(root,nd);
1762 nd->setRefItems(root->sli);
1763
1764 // file definition containing the namespace nd
1765 FileDef *fd=root->fileDef();
1766 // insert the namespace in the file definition
1767 if (fd) fd->insertNamespace(nd);
1768
1769 // the empty string test is needed for extract all case
1770 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1771 nd->insertUsedFile(fd);
1772 nd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1773 nd->setBodyDef(fd);
1774
1775 // also add namespace to the correct structural context
1776 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,fullName,nullptr,tagInfo);
1777 AUTO_TRACE_ADD("adding namespace {} to context {}",nd->name(),d ? d->name() : QCString("<none>"));
1778 if (d==nullptr) // we didn't find anything, create the scope artificially
1779 // anyway, so we can at least relate scopes properly.
1780 {
1781 d = buildScopeFromQualifiedName(fullName,nd->getLanguage(),tagInfo);
1783 if (dm)
1784 {
1785 dm->addInnerCompound(nd);
1786 }
1787 nd->setOuterScope(d);
1788 // TODO: Due to the order in which the tag file is written
1789 // a nested class can be found before its parent!
1790 }
1791 else
1792 {
1794 if (dm)
1795 {
1796 dm->addInnerCompound(nd);
1797 }
1798 nd->setOuterScope(d);
1799 // in case of d is an inline namespace, alias insert nd in the part scope of d.
1801 {
1802 NamespaceDef *pnd = toNamespaceDef(d);
1803 if (pnd && pnd->isInline())
1804 {
1805 d = d->getOuterScope();
1806 if (d)
1807 {
1808 dm = toDefinitionMutable(d);
1809 if (dm)
1810 {
1811 auto aliasNd = createNamespaceDefAlias(d,nd);
1812 dm->addInnerCompound(aliasNd.get());
1813 QCString aliasName = aliasNd->name();
1814 AUTO_TRACE_ADD("adding alias {} to {}",aliasName,d->name());
1815 Doxygen::namespaceLinkedMap->add(aliasName,std::move(aliasNd));
1816 }
1817 }
1818 else
1819 {
1820 break;
1821 }
1822 }
1823 else
1824 {
1825 break;
1826 }
1827 }
1828 }
1829 }
1830 }
1831 }
1832 }
1833 for (const auto &e : root->children()) buildNamespaceList(e.get());
1834}
1835
1836//----------------------------------------------------------------------
1837
1839 const QCString &name)
1840{
1841 NamespaceDef *usingNd =nullptr;
1842 for (auto &und : unl)
1843 {
1844 QCString uScope=und->name()+"::";
1845 usingNd = getResolvedNamespace(uScope+name);
1846 if (usingNd!=nullptr) break;
1847 }
1848 return usingNd;
1849}
1850
1851static void findUsingDirectives(const Entry *root)
1852{
1853 if (root->section.isUsingDir())
1854 {
1855 AUTO_TRACE("Found using directive {} at line {} of {}",root->name,root->startLine,root->fileName);
1856 QCString name=substitute(root->name,".","::");
1857 if (name.endsWith("::"))
1858 {
1859 name=name.left(name.length()-2);
1860 }
1861 if (!name.isEmpty())
1862 {
1863 NamespaceDef *usingNd = nullptr;
1864 NamespaceDefMutable *nd = nullptr;
1865 FileDef *fd = root->fileDef();
1866 QCString nsName;
1867
1868 // see if the using statement was found inside a namespace or inside
1869 // the global file scope.
1870 if (root->parent() && root->parent()->section.isNamespace() &&
1871 (fd==nullptr || fd->getLanguage()!=SrcLangExt::Java) // not a .java file
1872 )
1873 {
1874 nsName=stripAnonymousNamespaceScope(root->parent()->name);
1875 if (!nsName.isEmpty())
1876 {
1877 nd = getResolvedNamespaceMutable(nsName);
1878 }
1879 }
1880
1881 // find the scope in which the 'using' namespace is defined by prepending
1882 // the possible scopes in which the using statement was found, starting
1883 // with the most inner scope and going to the most outer scope (i.e.
1884 // file scope).
1885 int scopeOffset = static_cast<int>(nsName.length());
1886 do
1887 {
1888 QCString scope=scopeOffset>0 ?
1889 nsName.left(scopeOffset)+"::" : QCString();
1890 usingNd = getResolvedNamespace(scope+name);
1891 //printf("Trying with scope='%s' usingNd=%p\n",(scope+qPrint(name)),usingNd);
1892 if (scopeOffset==0)
1893 {
1894 scopeOffset=-1;
1895 }
1896 else if ((scopeOffset=nsName.findRev("::",scopeOffset-1))==-1)
1897 {
1898 scopeOffset=0;
1899 }
1900 } while (scopeOffset>=0 && usingNd==nullptr);
1901
1902 if (usingNd==nullptr && nd) // not found, try used namespaces in this scope
1903 // or in one of the parent namespace scopes
1904 {
1905 const NamespaceDefMutable *pnd = nd;
1906 while (pnd && usingNd==nullptr)
1907 {
1908 // also try with one of the used namespaces found earlier
1910
1911 // goto the parent
1912 Definition *s = pnd->getOuterScope();
1914 {
1916 }
1917 else
1918 {
1919 pnd = nullptr;
1920 }
1921 }
1922 }
1923 if (usingNd==nullptr && fd) // still nothing, also try used namespace in the
1924 // global scope
1925 {
1926 usingNd = findUsedNamespace(fd->getUsedNamespaces(),name);
1927 }
1928
1929 //printf("%s -> %s\n",qPrint(name),usingNd?qPrint(usingNd->name()):"<none>");
1930
1931 // add the namespace the correct scope
1932 if (usingNd)
1933 {
1934 //printf("using fd=%p nd=%p\n",fd,nd);
1935 if (nd)
1936 {
1937 //printf("Inside namespace %s\n",qPrint(nd->name()));
1938 nd->addUsingDirective(usingNd);
1939 }
1940 else if (fd)
1941 {
1942 //printf("Inside file %s\n",qPrint(fd->name()));
1943 fd->addUsingDirective(usingNd);
1944 }
1945 }
1946 else // unknown namespace, but add it anyway.
1947 {
1948 AUTO_TRACE_ADD("new unknown namespace {} lang={} hidden={}",name,langToString(root->lang),root->hidden);
1949 // add namespace to the list
1952 createNamespaceDef(root->fileName,root->startLine,root->startColumn,name)));
1953 if (nd)
1954 {
1955 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1956 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1958 nd->setHidden(root->hidden);
1959 nd->setArtificial(TRUE);
1960 nd->setLanguage(root->lang);
1961 nd->setId(root->id);
1962 nd->setMetaData(root->metaData);
1963 nd->setInline(root->spec.isInline());
1964 nd->setExported(root->exported);
1965
1966 for (const Grouping &g : root->groups)
1967 {
1968 GroupDef *gd=nullptr;
1969 if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname)))
1970 gd->addNamespace(nd);
1971 }
1972
1973 // insert the namespace in the file definition
1974 if (fd)
1975 {
1976 fd->insertNamespace(nd);
1977 fd->addUsingDirective(nd);
1978 }
1979
1980 // the empty string test is needed for extract all case
1981 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1982 nd->insertUsedFile(fd);
1983 nd->setRefItems(root->sli);
1984 }
1985 }
1986 }
1987 }
1988 for (const auto &e : root->children()) findUsingDirectives(e.get());
1989}
1990
1991//----------------------------------------------------------------------
1992
1993static void buildListOfUsingDecls(const Entry *root)
1994{
1995 if (root->section.isUsingDecl() &&
1996 !root->parent()->section.isCompound() // not a class/struct member
1997 )
1998 {
1999 QCString name = substitute(root->name,".","::");
2000 g_usingDeclarations.insert(name.str());
2001 }
2002 for (const auto &e : root->children()) buildListOfUsingDecls(e.get());
2003}
2004
2005
2006static void findUsingDeclarations(const Entry *root,bool filterPythonPackages)
2007{
2008 if (root->section.isUsingDecl() &&
2009 !root->parent()->section.isCompound() && // not a class/struct member
2010 (!filterPythonPackages || (root->lang==SrcLangExt::Python && root->fileName.endsWith("__init__.py")))
2011 )
2012 {
2013 AUTO_TRACE("Found using declaration '{}' at line {} of {} inside section {}",
2014 root->name,root->startLine,root->fileName,root->parent()->section);
2015 if (!root->name.isEmpty())
2016 {
2017 const Definition *usingDef = nullptr;
2018 NamespaceDefMutable *nd = nullptr;
2019 FileDef *fd = root->fileDef();
2020 QCString scName;
2021
2022 // see if the using statement was found inside a namespace or inside
2023 // the global file scope.
2024 if (root->parent()->section.isNamespace())
2025 {
2026 scName=root->parent()->name;
2027 if (!scName.isEmpty())
2028 {
2029 nd = getResolvedNamespaceMutable(scName);
2030 }
2031 }
2032
2033 // Assume the using statement was used to import a class.
2034 // Find the scope in which the 'using' namespace is defined by prepending
2035 // the possible scopes in which the using statement was found, starting
2036 // with the most inner scope and going to the most outer scope (i.e.
2037 // file scope).
2038
2039 QCString name = substitute(root->name,".","::"); //Java/C# scope->internal
2040
2041 SymbolResolver resolver;
2042 const Definition *scope = nd;
2043 if (nd==nullptr) scope = fd;
2044 usingDef = resolver.resolveSymbol(scope,name);
2045
2046 //printf("usingDef(scope=%s,name=%s)=%s\n",qPrint(nd?nd->qualifiedName():""),qPrint(name),usingDef?qPrint(usingDef->qualifiedName()):"nullptr");
2047
2048 if (!usingDef)
2049 {
2050 usingDef = getClass(name); // try direct lookup, this is needed to get
2051 // builtin STL classes to properly resolve, e.g.
2052 // vector -> std::vector
2053 }
2054 if (!usingDef)
2055 {
2056 usingDef = Doxygen::hiddenClassLinkedMap->find(name); // check if it is already hidden
2057 }
2058#if 0
2059 if (!usingDef)
2060 {
2061 AUTO_TRACE_ADD("New using class '{}' (sec={})! #tArgLists={}",
2062 name,root->section,root->tArgLists.size());
2065 createClassDef( "<using>",1,1, name, ClassDef::Class)));
2066 if (usingCd)
2067 {
2068 usingCd->setArtificial(TRUE);
2069 usingCd->setLanguage(root->lang);
2070 usingDef = usingCd;
2071 }
2072 }
2073#endif
2074 else
2075 {
2076 AUTO_TRACE_ADD("Found used type '{}' in scope='{}'",
2077 usingDef->name(), nd ? nd->name(): fd ? fd->name() : QCString("<unknown>"));
2078 }
2079
2080 if (usingDef)
2081 {
2082 if (nd)
2083 {
2084 nd->addUsingDeclaration(usingDef);
2085 }
2086 else if (fd)
2087 {
2088 fd->addUsingDeclaration(usingDef);
2089 }
2090 }
2091 }
2092 }
2093 for (const auto &e : root->children()) findUsingDeclarations(e.get(),filterPythonPackages);
2094}
2095
2096//----------------------------------------------------------------------
2097
2099{
2100 root->commandOverrides.apply_callGraph ([&](bool b) { md->overrideCallGraph(b); });
2101 root->commandOverrides.apply_callerGraph ([&](bool b) { md->overrideCallerGraph(b); });
2102 root->commandOverrides.apply_referencedByRelation([&](bool b) { md->overrideReferencedByRelation(b); });
2103 root->commandOverrides.apply_referencesRelation ([&](bool b) { md->overrideReferencesRelation(b); });
2104 root->commandOverrides.apply_inlineSource ([&](bool b) { md->overrideInlineSource(b); });
2105 root->commandOverrides.apply_enumValues ([&](bool b) { md->overrideEnumValues(b); });
2106}
2107
2108//----------------------------------------------------------------------
2109
2111 const QCString &fileName,const QCString &memName)
2112{
2113 AUTO_TRACE("creating new member {} for class {}",memName,cd->name());
2114 const ArgumentList &templAl = md->templateArguments();
2115 const ArgumentList &al = md->argumentList();
2116 auto newMd = createMemberDef(
2117 fileName,root->startLine,root->startColumn,
2118 md->typeString(),memName,md->argsString(),
2119 md->excpString(),root->protection,root->virt,
2120 md->isStatic(),Relationship::Member,md->memberType(),
2121 templAl,al,root->metaData
2122 );
2123 auto newMmd = toMemberDefMutable(newMd.get());
2124 newMmd->setMemberClass(cd);
2125 cd->insertMember(newMd.get());
2126 if (!root->doc.isEmpty() || !root->brief.isEmpty())
2127 {
2128 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2129 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2130 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2131 }
2132 else
2133 {
2134 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2135 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2136 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2137 }
2138 newMmd->setDefinition(md->definition());
2139 applyMemberOverrideOptions(root,newMmd);
2140 newMmd->addQualifiers(root->qualifiers);
2141 newMmd->setBitfields(md->bitfieldString());
2142 newMmd->addSectionsToDefinition(root->anchors);
2143 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2144 newMmd->setBodyDef(md->getBodyDef());
2145 newMmd->setInitializer(md->initializer());
2146 newMmd->setRequiresClause(md->requiresClause());
2147 newMmd->setMaxInitLines(md->initializerLines());
2148 newMmd->setMemberGroupId(root->mGrpId);
2149 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2150 newMmd->setVhdlSpecifiers(md->getVhdlSpecifiers());
2151 newMmd->setLanguage(root->lang);
2152 newMmd->setId(root->id);
2153 MemberName *mn = Doxygen::memberNameLinkedMap->add(memName);
2154 mn->push_back(std::move(newMd));
2155}
2156
2157static std::unordered_map<std::string,std::vector<ClassDefMutable*>> g_usingClassMap;
2158
2159static void findUsingDeclImports(const Entry *root)
2160{
2161 if (root->section.isUsingDecl() &&
2162 root->parent()->section.isCompound() // in a class/struct member
2163 )
2164 {
2165 AUTO_TRACE("Found using declaration '{}' inside section {}", root->name, root->parent()->section);
2166 QCString fullName=removeRedundantWhiteSpace(root->parent()->name);
2167 fullName=stripAnonymousNamespaceScope(fullName);
2168 fullName=stripTemplateSpecifiersFromScope(fullName);
2169 ClassDefMutable *cd = getClassMutable(fullName);
2170 if (cd)
2171 {
2172 AUTO_TRACE_ADD("found class '{}'",cd->name());
2173 int i=root->name.findRev("::");
2174 if (i!=-1)
2175 {
2176 QCString scope=root->name.left(i);
2177 QCString memName=root->name.right(root->name.length()-i-2);
2178 SymbolResolver resolver;
2179 const ClassDef *bcd = resolver.resolveClass(cd,scope); // todo: file in fileScope parameter
2180 AUTO_TRACE_ADD("name={} scope={} bcd={}",scope,cd?cd->name():"<none>",bcd?bcd->name():"<none>");
2181 if (bcd && bcd!=cd)
2182 {
2183 AUTO_TRACE_ADD("found class '{}' memName='{}'",bcd->name(),memName);
2185 const MemberNameInfo *mni = mnlm.find(memName);
2186 if (mni)
2187 {
2188 for (auto &mi : *mni)
2189 {
2190 const MemberDef *md = mi->memberDef();
2191 if (md && md->protection()!=Protection::Private)
2192 {
2193 AUTO_TRACE_ADD("found member '{}'",mni->memberName());
2194 QCString fileName = root->fileName;
2195 if (fileName.isEmpty() && root->tagInfo())
2196 {
2197 fileName = root->tagInfo()->tagName;
2198 }
2199 if (!cd->containsOverload(md))
2200 {
2201 createUsingMemberImportForClass(root,cd,md,fileName,memName);
2202 // also insert the member into copies of the class
2203 auto it = g_usingClassMap.find(cd->qualifiedName().str());
2204 if (it != g_usingClassMap.end())
2205 {
2206 for (const auto &copyCd : it->second)
2207 {
2208 createUsingMemberImportForClass(root,copyCd,md,fileName,memName);
2209 }
2210 }
2211 }
2212 }
2213 }
2214 }
2215 }
2216 }
2217 }
2218 }
2219 else if (root->section.isUsingDecl() &&
2220 (root->parent()->section.isNamespace() || root->parent()->section.isEmpty()) && // namespace or global member
2221 root->lang==SrcLangExt::Cpp // do we also want this for e.g. Fortran? (see test case 095)
2222 )
2223 {
2224 AUTO_TRACE("Found using declaration '{}' inside section {}", root->name, root->parent()->section);
2225 Definition *scope = nullptr;
2226 NamespaceDefMutable *nd = nullptr;
2227 FileDef *fd = root->parent()->fileDef();
2228 if (!root->parent()->name.isEmpty())
2229 {
2230 QCString fullName=removeRedundantWhiteSpace(root->parent()->name);
2231 fullName=stripAnonymousNamespaceScope(fullName);
2233 scope = nd;
2234 }
2235 else
2236 {
2237 scope = fd;
2238 }
2239 if (scope)
2240 {
2241 AUTO_TRACE_ADD("found scope '{}'",scope->name());
2242 SymbolResolver resolver;
2243 const Definition *def = resolver.resolveSymbol(root->name.startsWith("::") ? nullptr : scope,root->name);
2244 if (def && def->definitionType()==Definition::TypeMember)
2245 {
2246 int i=root->name.findRev("::");
2247 QCString memName;
2248 if (i!=-1)
2249 {
2250 memName = root->name.right(root->name.length()-i-2);
2251 }
2252 else
2253 {
2254 memName = root->name;
2255 }
2256 const MemberDef *md = toMemberDef(def);
2257 AUTO_TRACE_ADD("found member '{}' for name '{}'",md->qualifiedName(),root->name);
2258 QCString fileName = root->fileName;
2259 if (fileName.isEmpty() && root->tagInfo())
2260 {
2261 fileName = root->tagInfo()->tagName;
2262 }
2263 const ArgumentList &templAl = md->templateArguments();
2264 const ArgumentList &al = md->argumentList();
2265
2266 auto newMd = createMemberDef(
2267 fileName,root->startLine,root->startColumn,
2268 md->typeString(),memName,md->argsString(),
2269 md->excpString(),root->protection,root->virt,
2270 md->isStatic(),Relationship::Member,md->memberType(),
2271 templAl,al,root->metaData
2272 );
2273 auto newMmd = toMemberDefMutable(newMd.get());
2274 if (nd)
2275 {
2276 newMmd->setNamespace(nd);
2277 nd->insertMember(newMd.get());
2278 }
2279 if (fd)
2280 {
2281 newMmd->setFileDef(fd);
2282 fd->insertMember(newMd.get());
2283 }
2284 if (!root->doc.isEmpty() || !root->brief.isEmpty())
2285 {
2286 newMmd->setDocumentation(root->doc,root->docFile,root->docLine);
2287 newMmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2288 newMmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2289 }
2290 else
2291 {
2292 newMmd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2293 newMmd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2294 newMmd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2295 }
2296 newMmd->setDefinition(md->definition());
2297 applyMemberOverrideOptions(root,newMmd);
2298 newMmd->addQualifiers(root->qualifiers);
2299 newMmd->setBitfields(md->bitfieldString());
2300 newMmd->addSectionsToDefinition(root->anchors);
2301 newMmd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2302 newMmd->setBodyDef(md->getBodyDef());
2303 newMmd->setInitializer(md->initializer());
2304 newMmd->setRequiresClause(md->requiresClause());
2305 newMmd->setMaxInitLines(md->initializerLines());
2306 newMmd->setMemberGroupId(root->mGrpId);
2307 newMmd->setMemberSpecifiers(md->getMemberSpecifiers());
2308 newMmd->setVhdlSpecifiers(md->getVhdlSpecifiers());
2309 newMmd->setLanguage(root->lang);
2310 newMmd->setId(root->id);
2311 MemberName *mn = Doxygen::functionNameLinkedMap->add(memName);
2312 mn->push_back(std::move(newMd));
2313#if 0 // insert an alias instead of a copy
2314 const MemberDef *md = toMemberDef(def);
2315 AUTO_TRACE_ADD("found member '{}' for name '{}'",md->qualifiedName(),root->name);
2316 auto aliasMd = createMemberDefAlias(nd,md);
2317 QCString aliasFullName = nd->qualifiedName()+"::"+aliasMd->localName();
2318 if (nd && aliasMd.get())
2319 {
2320 nd->insertMember(aliasMd.get());
2321 }
2322 if (fd && aliasMd.get())
2323 {
2324 fd->insertMember(aliasMd.get());
2325 }
2326 MemberName *mn = Doxygen::memberNameLinkedMap->add(aliasFullName);
2327 mn->push_back(std::move(aliasMd));
2328#endif
2329 }
2330 else if (def && def->definitionType()==Definition::TypeClass)
2331 {
2332 const ClassDef *cd = toClassDef(def);
2333 QCString copyFullName;
2334 if (nd==nullptr)
2335 {
2336 copyFullName = cd->localName();
2337 }
2338 else
2339 {
2340 copyFullName = nd->qualifiedName()+"::"+cd->localName();
2341 }
2342 if (Doxygen::classLinkedMap->find(copyFullName)==nullptr)
2343 {
2345 Doxygen::classLinkedMap->add(copyFullName,
2346 cd->deepCopy(copyFullName)));
2347 AUTO_TRACE_ADD("found class '{}' for name '{}' copy '{}' obj={}",cd->qualifiedName(),root->name,copyFullName,(void*)ncdm);
2348 g_usingClassMap[cd->qualifiedName().str()].push_back(ncdm);
2349 if (ncdm)
2350 {
2351 if (nd) ncdm->moveTo(nd);
2352 if ((!root->doc.isEmpty() || !root->brief.isEmpty())) // use docs at using statement
2353 {
2354 ncdm->setDocumentation(root->doc,root->docFile,root->docLine);
2355 ncdm->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2356 }
2357 else // use docs from used class
2358 {
2359 ncdm->setDocumentation(cd->documentation(),cd->docFile(),cd->docLine());
2361 }
2362 if (nd)
2363 {
2364 nd->addInnerCompound(ncdm);
2365 nd->addUsingDeclaration(ncdm);
2366 }
2367 if (fd)
2368 {
2369 if (ncdm) ncdm->setFileDef(fd);
2370 fd->insertClass(ncdm);
2371 fd->addUsingDeclaration(ncdm);
2372 }
2373 }
2374 }
2375#if 0 // insert an alias instead of a copy
2376 auto aliasCd = createClassDefAlias(nd,cd);
2377 QCString aliasFullName;
2378 if (nd==nullptr)
2379 {
2380 aliasFullName = aliasCd->localName();
2381 }
2382 else
2383 {
2384 aliasFullName = nd->qualifiedName()+"::"+aliasCd->localName();
2385 }
2386 AUTO_TRACE_ADD("found class '{}' for name '{}' aliasFullName='{}'",cd->qualifiedName(),root->name,aliasFullName);
2387 auto acd = Doxygen::classLinkedMap->add(aliasFullName,std::move(aliasCd));
2388 if (nd && acd)
2389 {
2390 nd->addInnerCompound(acd);
2391 }
2392 if (fd && acd)
2393 {
2394 fd->insertClass(acd);
2395 }
2396#endif
2397 }
2398 else if (scope)
2399 {
2400 AUTO_TRACE_ADD("no symbol with name '{}' in scope {}",root->name,scope->name());
2401 }
2402 }
2403 }
2404 for (const auto &e : root->children()) findUsingDeclImports(e.get());
2405}
2406
2407//----------------------------------------------------------------------
2408
2410{
2411 FileDefSet visitedFiles;
2412 // then recursively add using directives found in #include files
2413 // to files that have not been visited.
2414 for (const auto &fn : *Doxygen::inputNameLinkedMap)
2415 {
2416 for (const auto &fd : *fn)
2417 {
2418 //printf("----- adding using directives for file %s\n",qPrint(fd->name()));
2419 fd->addIncludedUsingDirectives(visitedFiles);
2420 }
2421 }
2422}
2423
2424//----------------------------------------------------------------------
2425
2427 const Entry *root,
2428 ClassDefMutable *cd,
2429 MemberType mtype,
2430 const QCString &type,
2431 const QCString &name,
2432 const QCString &args,
2433 bool fromAnnScope,
2434 MemberDef *fromAnnMemb,
2435 Protection prot,
2436 Relationship related)
2437{
2439 QCString scopeSeparator="::";
2440 SrcLangExt lang = cd->getLanguage();
2441 if (lang==SrcLangExt::Java || lang==SrcLangExt::CSharp)
2442 {
2443 qualScope = substitute(qualScope,"::",".");
2444 scopeSeparator=".";
2445 }
2446 AUTO_TRACE("class variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} ann={} init='{}'",
2447 root->fileName, type, qualScope, name, args, root->protection, mtype, lang, fromAnnScope, root->initializer.str());
2448
2449 QCString def;
2450 if (!type.isEmpty())
2451 {
2452 if (related!=Relationship::Member || mtype==MemberType::Friend || Config_getBool(HIDE_SCOPE_NAMES))
2453 {
2454 if (root->spec.isAlias()) // turn 'typedef B A' into 'using A'
2455 {
2456 def="using "+name;
2457 }
2458 else
2459 {
2460 def=type+" "+name+args;
2461 }
2462 }
2463 else
2464 {
2465 if (root->spec.isAlias()) // turn 'typedef B C::A' into 'using C::A'
2466 {
2467 def="using "+qualScope+scopeSeparator+name;
2468 }
2469 else
2470 {
2471 def=type+" "+qualScope+scopeSeparator+name+args;
2472 }
2473 }
2474 }
2475 else
2476 {
2477 if (Config_getBool(HIDE_SCOPE_NAMES))
2478 {
2479 def=name+args;
2480 }
2481 else
2482 {
2483 def=qualScope+scopeSeparator+name+args;
2484 }
2485 }
2486 def.stripPrefix("static ");
2487
2488 // see if the member is already found in the same scope
2489 // (this may be the case for a static member that is initialized
2490 // outside the class)
2492 if (mn)
2493 {
2494 for (const auto &imd : *mn)
2495 {
2496 //printf("md->getClassDef()=%p cd=%p type=[%s] md->typeString()=[%s]\n",
2497 // md->getClassDef(),cd,qPrint(type),md->typeString());
2498 MemberDefMutable *md = toMemberDefMutable(imd.get());
2499 if (md &&
2500 md->getClassDef()==cd &&
2501 ((lang==SrcLangExt::Python && type.isEmpty() && !md->typeString().isEmpty()) ||
2503 // member already in the scope
2504 {
2505
2506 if (root->lang==SrcLangExt::ObjC &&
2507 root->mtype==MethodTypes::Property &&
2509 { // Objective-C 2.0 property
2510 // turn variable into a property
2511 md->setProtection(root->protection);
2513 }
2514 addMemberDocs(root,md,def,nullptr,FALSE,root->spec);
2515 AUTO_TRACE_ADD("Member already found!");
2516 return md;
2517 }
2518 }
2519 }
2520
2521 QCString fileName = root->fileName;
2522 if (fileName.isEmpty() && root->tagInfo())
2523 {
2524 fileName = root->tagInfo()->tagName;
2525 }
2526
2527 // new member variable, typedef or enum value
2528 auto md = createMemberDef(
2529 fileName,root->startLine,root->startColumn,
2530 type,name,args,root->exception,
2531 prot,Specifier::Normal,root->isStatic,related,
2532 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2533 ArgumentList(), root->metaData);
2534 auto mmd = toMemberDefMutable(md.get());
2535 mmd->setTagInfo(root->tagInfo());
2536 mmd->setMemberClass(cd); // also sets outer scope (i.e. getOuterScope())
2537 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2538 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2539 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2540 mmd->setDefinition(def);
2541 mmd->setBitfields(root->bitfields);
2542 mmd->addSectionsToDefinition(root->anchors);
2543 mmd->setFromAnonymousScope(fromAnnScope);
2544 mmd->setFromAnonymousMember(fromAnnMemb);
2545 //md->setIndentDepth(indentDepth);
2546 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2547 mmd->setInitializer(root->initializer.str());
2548 mmd->setMaxInitLines(root->initLines);
2549 mmd->setMemberGroupId(root->mGrpId);
2550 mmd->setMemberSpecifiers(root->spec);
2551 mmd->setVhdlSpecifiers(root->vhdlSpec);
2552 mmd->setReadAccessor(root->read);
2553 mmd->setWriteAccessor(root->write);
2555 mmd->setHidden(root->hidden);
2556 mmd->setArtificial(root->artificial);
2557 mmd->setLanguage(root->lang);
2558 mmd->setId(root->id);
2559 addMemberToGroups(root,md.get());
2561 mmd->setBodyDef(root->fileDef());
2562 mmd->addQualifiers(root->qualifiers);
2563
2564 AUTO_TRACE_ADD("Adding new member to class '{}'",cd->name());
2565 cd->insertMember(md.get());
2566 mmd->setRefItems(root->sli);
2567
2568 cd->insertUsedFile(root->fileDef());
2569 root->markAsProcessed();
2570
2571 if (mtype==MemberType::Typedef)
2572 {
2573 resolveTemplateInstanceInType(root,cd,md.get());
2574 }
2575
2576 // add the member to the global list
2577 MemberDef *result = md.get();
2578 mn = Doxygen::memberNameLinkedMap->add(name);
2579 mn->push_back(std::move(md));
2580
2581 return result;
2582}
2583
2584//----------------------------------------------------------------------
2585
2587 const Entry *root,
2588 MemberType mtype,
2589 const QCString &scope,
2590 const QCString &type,
2591 const QCString &name,
2592 const QCString &args,
2593 bool fromAnnScope,
2594 MemberDef *fromAnnMemb)
2595{
2596 AUTO_TRACE("global variable: file='{}' type='{}' scope='{}' name='{}' args='{}' prot={} mtype={} lang={} init='{}'",
2597 root->fileName, type, scope, name, args, root->protection, mtype, root->lang, root->initializer.str());
2598
2599 FileDef *fd = root->fileDef();
2600
2601 // see if we have a typedef that should hide a struct or union
2602 if (mtype==MemberType::Typedef && Config_getBool(TYPEDEF_HIDES_STRUCT))
2603 {
2604 QCString ttype = type;
2605 ttype.stripPrefix("typedef ");
2606 if (ttype.stripPrefix("struct ") || ttype.stripPrefix("union "))
2607 {
2608 static const reg::Ex re(R"(\a\w*)");
2609 reg::Match match;
2610 std::string typ = ttype.str();
2611 if (reg::search(typ,match,re))
2612 {
2613 QCString typeValue = match.str();
2614 ClassDefMutable *cd = getClassMutable(typeValue);
2615 if (cd)
2616 {
2617 // this typedef should hide compound name cd, so we
2618 // change the name that is displayed from cd.
2619 cd->setClassName(name);
2620 cd->setDocumentation(root->doc,root->docFile,root->docLine);
2621 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2622 return nullptr;
2623 }
2624 }
2625 }
2626 }
2627
2628 // see if the function is inside a namespace
2629 NamespaceDefMutable *nd = nullptr;
2630 if (!scope.isEmpty())
2631 {
2632 if (scope.find('@')!=-1) return nullptr; // anonymous scope!
2633 nd = getResolvedNamespaceMutable(scope);
2634 }
2635 QCString def;
2636
2637 // determine the definition of the global variable
2638 if (nd && !nd->isAnonymous() &&
2639 !Config_getBool(HIDE_SCOPE_NAMES)
2640 )
2641 // variable is inside a namespace, so put the scope before the name
2642 {
2643 SrcLangExt lang = nd->getLanguage();
2645
2646 if (!type.isEmpty())
2647 {
2648 if (root->spec.isAlias()) // turn 'typedef B NS::A' into 'using NS::A'
2649 {
2650 def="using "+nd->name()+sep+name;
2651 }
2652 else // normal member
2653 {
2654 def=type+" "+nd->name()+sep+name+args;
2655 }
2656 }
2657 else
2658 {
2659 def=nd->name()+sep+name+args;
2660 }
2661 }
2662 else
2663 {
2664 if (!type.isEmpty() && !root->name.isEmpty())
2665 {
2666 if (name.at(0)=='@') // dummy variable representing anonymous union
2667 {
2668 def=type;
2669 }
2670 else
2671 {
2672 if (root->spec.isAlias()) // turn 'typedef B A' into 'using A'
2673 {
2674 def="using "+root->name;
2675 }
2676 else // normal member
2677 {
2678 def=type+" "+name+args;
2679 }
2680 }
2681 }
2682 else
2683 {
2684 def=name+args;
2685 }
2686 }
2687 def.stripPrefix("static ");
2688
2690 if (mn)
2691 {
2692 //QCString nscope=removeAnonymousScopes(scope);
2693 //NamespaceDef *nd=nullptr;
2694 //if (!nscope.isEmpty())
2695 if (!scope.isEmpty())
2696 {
2697 nd = getResolvedNamespaceMutable(scope);
2698 }
2699 for (const auto &imd : *mn)
2700 {
2701 MemberDefMutable *md = toMemberDefMutable(imd.get());
2702 if (md &&
2703 ((nd==nullptr && md->getNamespaceDef()==nullptr && md->getFileDef() &&
2704 root->fileName==md->getFileDef()->absFilePath()
2705 ) // both variable names in the same file
2706 || (nd!=nullptr && md->getNamespaceDef()==nd) // both in same namespace
2707 )
2708 && !md->isDefine() // function style #define's can be "overloaded" by typedefs or variables
2709 && !md->isEnumerate() // in C# an enum value and enum can have the same name
2710 )
2711 // variable already in the scope
2712 {
2713 bool isPHPArray = md->getLanguage()==SrcLangExt::PHP &&
2714 md->argsString()!=args &&
2715 args.find('[')!=-1;
2716 bool staticsInDifferentFiles =
2717 root->isStatic && md->isStatic() &&
2718 root->fileName!=md->getDefFileName();
2719
2720 if (md->getFileDef() &&
2721 !isPHPArray && // not a php array
2722 !staticsInDifferentFiles
2723 )
2724 // not a php array variable
2725 {
2726 AUTO_TRACE_ADD("variable already found: scope='{}'",md->getOuterScope()->name());
2727 addMemberDocs(root,md,def,nullptr,FALSE,root->spec);
2728 md->setRefItems(root->sli);
2729 // if md is a variable forward declaration and root is the definition that
2730 // turn md into the definition
2731 if (!root->explicitExternal && md->isExternal())
2732 {
2733 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
2735 }
2736 // if md is the definition and root point at a declaration, then add the
2737 // declaration info
2738 else if (root->explicitExternal && !md->isExternal())
2739 {
2740 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
2741 }
2742 return md;
2743 }
2744 }
2745 }
2746 }
2747
2748 QCString fileName = root->fileName;
2749 if (fileName.isEmpty() && root->tagInfo())
2750 {
2751 fileName = root->tagInfo()->tagName;
2752 }
2753
2754 AUTO_TRACE_ADD("new variable, namespace='{}'",nd?nd->name():QCString("<global>"));
2755 // new global variable, enum value or typedef
2756 auto md = createMemberDef(
2757 fileName,root->startLine,root->startColumn,
2758 type,name,args,QCString(),
2759 root->protection, Specifier::Normal,root->isStatic,Relationship::Member,
2760 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2761 root->argList, root->metaData);
2762 auto mmd = toMemberDefMutable(md.get());
2763 mmd->setTagInfo(root->tagInfo());
2764 mmd->setMemberSpecifiers(root->spec);
2765 mmd->setVhdlSpecifiers(root->vhdlSpec);
2766 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
2767 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2768 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2769 mmd->addSectionsToDefinition(root->anchors);
2770 mmd->setFromAnonymousScope(fromAnnScope);
2771 mmd->setFromAnonymousMember(fromAnnMemb);
2772 mmd->setInitializer(root->initializer.str());
2773 mmd->setMaxInitLines(root->initLines);
2774 mmd->setMemberGroupId(root->mGrpId);
2775 mmd->setDefinition(def);
2776 mmd->setLanguage(root->lang);
2777 mmd->setId(root->id);
2779 mmd->setExplicitExternal(root->explicitExternal,fileName,root->startLine,root->startColumn);
2780 mmd->addQualifiers(root->qualifiers);
2781 //md->setOuterScope(fd);
2782 if (!root->explicitExternal)
2783 {
2784 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2785 mmd->setBodyDef(fd);
2786 }
2787 addMemberToGroups(root,md.get());
2789
2790 mmd->setRefItems(root->sli);
2791 if (nd && !nd->isAnonymous())
2792 {
2793 mmd->setNamespace(nd);
2794 nd->insertMember(md.get());
2795 }
2796
2797 // add member to the file (we do this even if we have already inserted
2798 // it into the namespace.
2799 if (fd)
2800 {
2801 mmd->setFileDef(fd);
2802 fd->insertMember(md.get());
2803 }
2804
2805 root->markAsProcessed();
2806
2807 if (mtype==MemberType::Typedef)
2808 {
2809 resolveTemplateInstanceInType(root,nd,md.get());
2810 }
2811
2812 // add member definition to the list of globals
2813 MemberDef *result = md.get();
2814 mn = Doxygen::functionNameLinkedMap->add(name);
2815 mn->push_back(std::move(md));
2816
2817
2818
2819 return result;
2820}
2821
2822/*! See if the return type string \a type is that of a function pointer
2823 * \returns -1 if this is not a function pointer variable or
2824 * the index at which the closing brace of (...*name) was found.
2825 */
2826static int findFunctionPtr(const std::string &type,SrcLangExt lang, int *pLength=nullptr)
2827{
2828 AUTO_TRACE("type='{}' lang={}",type,lang);
2829 if (lang == SrcLangExt::Fortran || lang == SrcLangExt::VHDL)
2830 {
2831 return -1; // Fortran and VHDL do not have function pointers
2832 }
2833
2834 static const reg::Ex re(R"(\‍([^)]*[*&^][^)]*\))");
2835 reg::Match match;
2836 size_t i=std::string::npos;
2837 size_t l=0;
2838 if (reg::search(type,match,re)) // contains (...*...) or (...&...) or (...^...)
2839 {
2840 i = match.position();
2841 l = match.length();
2842 }
2843 if (i!=std::string::npos)
2844 {
2845 size_t di = type.find("decltype(");
2846 if (di!=std::string::npos && di<i)
2847 {
2848 i = std::string::npos;
2849 }
2850 }
2851 size_t bb=type.find('<');
2852 size_t be=type.rfind('>');
2853 bool templFp = false;
2854 if (be!=std::string::npos) {
2855 size_t cc_ast = type.find("::*");
2856 size_t cc_amp = type.find("::&");
2857 templFp = (cc_ast != std::string::npos && cc_ast>be) || (cc_amp != std::string::npos && cc_amp>be); // hack to find, e.g 'B<X>(A<int>::*)'
2858 }
2859
2860 if (!type.empty() && // return type is non-empty
2861 i!=std::string::npos && // contains (...*...)
2862 type.find("operator")==std::string::npos && // not an operator
2863 (type.find(")(")==std::string::npos || type.find("typedef ")!=std::string::npos) &&
2864 // not a function pointer return type
2865 (!(bb<i && i<be) || templFp) // bug665855: avoid treating "typedef A<void (T*)> type" as a function pointer
2866 )
2867 {
2868 if (pLength) *pLength=static_cast<int>(l);
2869 //printf("findFunctionPtr=%d\n",(int)i);
2870 AUTO_TRACE_EXIT("result={}",i);
2871 return static_cast<int>(i);
2872 }
2873 else
2874 {
2875 //printf("findFunctionPtr=%d\n",-1);
2876 AUTO_TRACE_EXIT("result=-1");
2877 return -1;
2878 }
2879}
2880
2881//--------------------------------------------------------------------------------------
2882
2883/*! Returns TRUE iff \a type is a class within scope \a context.
2884 * Used to detect variable declarations that look like function prototypes.
2885 */
2886static bool isVarWithConstructor(const Entry *root)
2887{
2888 bool result = false;
2889 bool typeIsClass = false;
2890 bool typePtrType = false;
2891 QCString type;
2892 Definition *ctx = nullptr;
2893 FileDef *fd = root->fileDef();
2894 SymbolResolver resolver(fd);
2895
2896 AUTO_TRACE("isVarWithConstructor({})",root->name);
2897 if (root->parent()->section.isCompound())
2898 { // inside a class
2899 result=FALSE;
2900 AUTO_TRACE_EXIT("inside class: result={}",result);
2901 return result;
2902 }
2903 else if ((fd != nullptr) && (fd->name().endsWith(".c") || fd->name().endsWith(".h")))
2904 { // inside a .c file
2905 result=FALSE;
2906 AUTO_TRACE_EXIT("inside C file: result={}",result);
2907 return result;
2908 }
2909 if (root->type.isEmpty())
2910 {
2911 result=FALSE;
2912 AUTO_TRACE_EXIT("no type: result={}",result);
2913 return result;
2914 }
2915 if (!root->parent()->name.isEmpty())
2916 {
2917 ctx=Doxygen::namespaceLinkedMap->find(root->parent()->name);
2918 }
2919 type = root->type;
2920 // remove qualifiers
2921 findAndRemoveWord(type,"const");
2922 findAndRemoveWord(type,"static");
2923 findAndRemoveWord(type,"volatile");
2924 typePtrType = type.find('*')!=-1 || type.find('&')!=-1;
2925 if (!typePtrType)
2926 {
2927 typeIsClass = resolver.resolveClass(ctx,type)!=nullptr;
2928 int ti=0;
2929 if (!typeIsClass && (ti=type.find('<'))!=-1)
2930 {
2931 typeIsClass=resolver.resolveClass(ctx,type.left(ti))!=nullptr;
2932 }
2933 }
2934 if (typeIsClass) // now we still have to check if the arguments are
2935 // types or values. Since we do not have complete type info
2936 // we need to rely on heuristics :-(
2937 {
2938 if (root->argList.empty())
2939 {
2940 result=FALSE; // empty arg list -> function prototype.
2941 AUTO_TRACE_EXIT("empty arg list: result={}",result);
2942 return result;
2943 }
2944 for (const Argument &a : root->argList)
2945 {
2946 static const reg::Ex initChars(R"([\d"'&*!^]+)");
2947 reg::Match match;
2948 if (!a.name.isEmpty() || !a.defval.isEmpty())
2949 {
2950 std::string name = a.name.str();
2951 if (reg::search(name,match,initChars) && match.position()==0)
2952 {
2953 result=TRUE;
2954 }
2955 else
2956 {
2957 result=FALSE; // arg has (type,name) pair -> function prototype
2958 }
2959 AUTO_TRACE_EXIT("function prototype: result={}",result);
2960 return result;
2961 }
2962 if (!a.type.isEmpty() &&
2963 (a.type.at(a.type.length()-1)=='*' ||
2964 a.type.at(a.type.length()-1)=='&'))
2965 // type ends with * or & => pointer or reference
2966 {
2967 result=FALSE;
2968 AUTO_TRACE_EXIT("pointer or reference: result={}",result);
2969 return result;
2970 }
2971 if (a.type.isEmpty() || resolver.resolveClass(ctx,a.type)!=nullptr)
2972 {
2973 result=FALSE; // arg type is a known type
2974 AUTO_TRACE_EXIT("known type: result={}",result);
2975 return result;
2976 }
2977 if (checkIfTypedef(ctx,fd,a.type))
2978 {
2979 result=FALSE; // argument is a typedef
2980 AUTO_TRACE_EXIT("typedef: result={}",result);
2981 return result;
2982 }
2983 std::string atype = a.type.str();
2984 if (reg::search(atype,match,initChars) && match.position()==0)
2985 {
2986 result=TRUE; // argument type starts with typical initializer char
2987 AUTO_TRACE_EXIT("argument with init char: result={}",result);
2988 return result;
2989 }
2990 std::string resType=resolveTypeDef(ctx,a.type).str();
2991 if (resType.empty()) resType=atype;
2992 static const reg::Ex idChars(R"(\a\w*)");
2993 if (reg::search(resType,match,idChars) && match.position()==0) // resType starts with identifier
2994 {
2995 resType=match.str();
2996 if (resType=="int" || resType=="long" ||
2997 resType=="float" || resType=="double" ||
2998 resType=="char" || resType=="void" ||
2999 resType=="signed" || resType=="unsigned" ||
3000 resType=="const" || resType=="volatile" )
3001 {
3002 result=FALSE; // type keyword -> function prototype
3003 AUTO_TRACE_EXIT("type keyword: result={}",result);
3004 return result;
3005 }
3006 }
3007 }
3008 result=TRUE;
3009 }
3010
3011 AUTO_TRACE_EXIT("end: result={}",result);
3012 return result;
3013}
3014
3015//--------------------------------------------------------------------------------------
3016
3017/*! Searches for the end of a template in prototype \a s starting from
3018 * character position \a startPos. If the end was found the position
3019 * of the closing > is returned, otherwise -1 is returned.
3020 *
3021 * Handles exotic cases such as
3022 * \code
3023 * Class<(id<0)>
3024 * Class<bits<<2>
3025 * Class<"<">
3026 * Class<'<'>
3027 * Class<(")<")>
3028 * \endcode
3029 */
3030static int findEndOfTemplate(const QCString &s,size_t startPos)
3031{
3032 // locate end of template
3033 size_t e=startPos;
3034 int brCount=1;
3035 int roundCount=0;
3036 size_t len = s.length();
3037 bool insideString=FALSE;
3038 bool insideChar=FALSE;
3039 char pc = 0;
3040 while (e<len && brCount!=0)
3041 {
3042 char c=s.at(e);
3043 switch(c)
3044 {
3045 case '<':
3046 if (!insideString && !insideChar)
3047 {
3048 if (e<len-1 && s.at(e+1)=='<')
3049 e++;
3050 else if (roundCount==0)
3051 brCount++;
3052 }
3053 break;
3054 case '>':
3055 if (!insideString && !insideChar)
3056 {
3057 if (e<len-1 && s.at(e+1)=='>')
3058 e++;
3059 else if (roundCount==0)
3060 brCount--;
3061 }
3062 break;
3063 case '(':
3064 if (!insideString && !insideChar)
3065 roundCount++;
3066 break;
3067 case ')':
3068 if (!insideString && !insideChar)
3069 roundCount--;
3070 break;
3071 case '"':
3072 if (!insideChar)
3073 {
3074 if (insideString && pc!='\\')
3075 insideString=FALSE;
3076 else
3077 insideString=TRUE;
3078 }
3079 break;
3080 case '\'':
3081 if (!insideString)
3082 {
3083 if (insideChar && pc!='\\')
3084 insideChar=FALSE;
3085 else
3086 insideChar=TRUE;
3087 }
3088 break;
3089 }
3090 pc = c;
3091 e++;
3092 }
3093 return brCount==0 ? static_cast<int>(e) : -1;
3094}
3095
3096//--------------------------------------------------------------------------------------
3097
3098static void addVariable(const Entry *root,int isFuncPtr=-1)
3099{
3100 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
3101
3102 AUTO_TRACE("VARIABLE_SEC: type='{}' name='{}' args='{}' bodyLine={} endBodyLine={} mGrpId={} relates='{}'",
3103 root->type, root->name, root->args, root->bodyLine, root->endBodyLine, root->mGrpId, root->relates);
3104 //printf("root->parent->name=%s\n",qPrint(root->parent->name));
3105
3106 QCString type = root->type;
3107 QCString name = root->name;
3108 QCString args = root->args;
3109 if (type.isEmpty() && name.find("operator")==-1 &&
3110 (name.find('*')!=-1 || name.find('&')!=-1))
3111 {
3112 // recover from parse error caused by redundant braces
3113 // like in "int *(var[10]);", which is parsed as
3114 // type="" name="int *" args="(var[10])"
3115
3116 type=name;
3117 std::string sargs = args.str();
3118 static const reg::Ex reName(R"(\a\w*)");
3119 reg::Match match;
3120 if (reg::search(sargs,match,reName))
3121 {
3122 name = match.str(); // e.g. 'var' in '(var[10])'
3123 sargs = match.suffix().str(); // e.g. '[10]) in '(var[10])'
3124 size_t j = sargs.find(')');
3125 if (j!=std::string::npos) args=sargs.substr(0,j); // extract, e.g '[10]' from '[10])'
3126 }
3127 }
3128 else
3129 {
3130 int i=isFuncPtr;
3131 if (i==-1 && (root->spec.isAlias())==0) i=findFunctionPtr(type.str(),root->lang); // for typedefs isFuncPtr is not yet set
3132 AUTO_TRACE_ADD("functionPtr={}",i!=-1?"yes":"no");
3133 if (i>=0) // function pointer
3134 {
3135 int ai = type.find('[',i);
3136 if (ai>i) // function pointer array
3137 {
3138 args.prepend(type.right(type.length()-ai));
3139 type=type.left(ai);
3140 }
3141 else if (type.find(')',i)!=-1) // function ptr, not variable like "int (*bla)[10]"
3142 {
3143 type=type.left(type.length()-1);
3144 args.prepend(") ");
3145 }
3146 }
3147 }
3148 AUTO_TRACE_ADD("after correction: type='{}' name='{}' args='{}'",type,name,args);
3149
3150 QCString scope;
3151 name=removeRedundantWhiteSpace(name);
3152
3153 // find the scope of this variable
3154 int index = computeQualifiedIndex(name);
3155 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3156 // grouped members are stored with full scope
3157 {
3158 buildScopeFromQualifiedName(name.left(index+2),root->lang,root->tagInfo());
3159 scope=name.left(index);
3160 name=name.mid(index+2);
3161 }
3162 else
3163 {
3164 Entry *p = root->parent();
3165 while (p->section.isScope())
3166 {
3167 QCString scopeName = p->name;
3168 if (!scopeName.isEmpty())
3169 {
3170 scope.prepend(scopeName);
3171 break;
3172 }
3173 p=p->parent();
3174 }
3175 }
3176
3177 type=type.stripWhiteSpace();
3178 ClassDefMutable *cd=nullptr;
3179 bool isRelated=FALSE;
3180 bool isMemberOf=FALSE;
3181
3182 QCString classScope=stripAnonymousNamespaceScope(scope);
3183 if (root->lang==SrcLangExt::CSharp)
3184 {
3185 classScope=mangleCSharpGenericName(classScope);
3186 }
3187 else
3188 {
3189 classScope=stripTemplateSpecifiersFromScope(classScope,FALSE);
3190 }
3191 QCString annScopePrefix=scope.left(scope.length()-classScope.length());
3192
3193
3194 // Look for last :: not part of template specifier
3195 int p=-1;
3196 for (size_t i=0;i<name.length()-1;i++)
3197 {
3198 if (name[i]==':' && name[i+1]==':')
3199 {
3200 p=static_cast<int>(i);
3201 }
3202 else if (name[i]=='<') // skip over template parts,
3203 // i.e. A::B<C::D> => p=1 and
3204 // A<B::C>::D => p=8
3205 {
3206 int e = findEndOfTemplate(name,i+1);
3207 if (e!=-1) i=static_cast<int>(e);
3208 }
3209 }
3210
3211 if (p!=-1) // found it
3212 {
3213 if (type=="friend class" || type=="friend struct" ||
3214 type=="friend union")
3215 {
3216 cd=getClassMutable(scope);
3217 if (cd)
3218 {
3219 addVariableToClass(root, // entry
3220 cd, // class to add member to
3221 MemberType::Friend, // type of member
3222 type, // type value as string
3223 name, // name of the member
3224 args, // arguments as string
3225 FALSE, // from Anonymous scope
3226 nullptr, // anonymous member
3227 Protection::Public, // protection
3228 Relationship::Member // related to a class
3229 );
3230 }
3231 }
3232 if (root->bodyLine!=-1 && root->endBodyLine!=-1) // store the body location for later use
3233 {
3234 Doxygen::staticInitMap.emplace(name.str(),BodyInfo{root->startLine,root->bodyLine,root->endBodyLine});
3235 }
3236
3237
3238 AUTO_TRACE_ADD("static variable {} body=[{}..{}]",name,root->bodyLine,root->endBodyLine);
3239 return; /* skip this member, because it is a
3240 * static variable definition (always?), which will be
3241 * found in a class scope as well, but then we know the
3242 * correct protection level, so only then it will be
3243 * inserted in the correct list!
3244 */
3245 }
3246
3248 if (type=="@")
3250 else if (type.startsWith("typedef "))
3251 mtype=MemberType::Typedef;
3252 else if (type.startsWith("friend "))
3253 mtype=MemberType::Friend;
3254 else if (root->mtype==MethodTypes::Property)
3256 else if (root->mtype==MethodTypes::Event)
3257 mtype=MemberType::Event;
3258 else if (type.find("sequence<") != -1)
3259 mtype=sliceOpt ? MemberType::Sequence : MemberType::Typedef;
3260 else if (type.find("dictionary<") != -1)
3262
3263 if (!root->relates.isEmpty()) // related variable
3264 {
3265 isRelated=TRUE;
3266 isMemberOf=(root->relatesType==RelatesType::MemberOf);
3267 if (getClass(root->relates)==nullptr && !scope.isEmpty())
3268 scope=mergeScopes(scope,root->relates);
3269 else
3270 scope=root->relates;
3271 }
3272
3273 cd=getClassMutable(scope);
3274 if (cd==nullptr && classScope!=scope) cd=getClassMutable(classScope);
3275 if (cd)
3276 {
3277 MemberDef *md=nullptr;
3278
3279 // if cd is an anonymous (=tag less) scope we insert the member
3280 // into a non-anonymous parent scope as well. This is needed to
3281 // be able to refer to it using \var or \fn
3282
3283 //int indentDepth=0;
3284 int si=scope.find('@');
3285 //int anonyScopes = 0;
3286 //bool added=FALSE;
3287
3288 bool inlineSimpleStructs = Config_getBool(INLINE_SIMPLE_STRUCTS);
3289 Relationship relationship = isMemberOf ? Relationship::Foreign :
3290 isRelated ? Relationship::Related :
3291 Relationship::Member ;
3292 if (si!=-1 && !inlineSimpleStructs) // anonymous scope or type
3293 {
3294 QCString pScope;
3295 ClassDefMutable *pcd=nullptr;
3296 pScope = scope.left(std::max(si-2,0)); // scope without tag less parts
3297 if (!pScope.isEmpty())
3298 pScope.prepend(annScopePrefix);
3299 else if (annScopePrefix.length()>2)
3300 pScope=annScopePrefix.left(annScopePrefix.length()-2);
3301 if (name.at(0)!='@')
3302 {
3303 if (!pScope.isEmpty() && (pcd=getClassMutable(pScope)))
3304 {
3305 AUTO_TRACE_ADD("Adding anonymous member to scope '{}'",pScope);
3306 md=addVariableToClass(root, // entry
3307 pcd, // class to add member to
3308 mtype, // member type
3309 type, // type value as string
3310 name, // member name
3311 args, // arguments as string
3312 TRUE, // from anonymous scope
3313 nullptr, // from anonymous member
3314 root->protection,
3315 relationship
3316 );
3317 //added=TRUE;
3318 }
3319 else // anonymous scope inside namespace or file => put variable in the global scope
3320 {
3321 if (mtype==MemberType::Variable)
3322 {
3323 AUTO_TRACE_ADD("Adding anonymous member to global scope '{}'");
3324 md=addVariableToFile(root,mtype,pScope,type,name,args,TRUE,nullptr);
3325 }
3326 //added=TRUE;
3327 }
3328 }
3329 }
3330
3331 addVariableToClass(root, // entry
3332 cd, // class to add member to
3333 mtype, // member type
3334 type, // type value as string
3335 name, // name of the member
3336 args, // arguments as string
3337 FALSE, // from anonymous scope
3338 md, // from anonymous member
3339 root->protection,
3340 relationship
3341 );
3342 }
3343 else if (!name.isEmpty()) // global variable
3344 {
3345 addVariableToFile(root,mtype,scope,type,name,args,FALSE,/*nullptr,*/nullptr);
3346 }
3347
3348}
3349
3350//----------------------------------------------------------------------
3351// Searches the Entry tree for typedef documentation sections.
3352// If found they are stored in their class or in the global list.
3353static void buildTypedefList(const Entry *root)
3354{
3355 //printf("buildVarList(%s)\n",qPrint(rootNav->name()));
3356 if (!root->name.isEmpty() &&
3357 root->section.isVariable() &&
3358 root->type.find("typedef ")!=-1 // its a typedef
3359 )
3360 {
3361 AUTO_TRACE();
3363 QCString scope;
3364 int index = computeQualifiedIndex(rname);
3365 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3366 // grouped members are stored with full scope
3367 {
3368 buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
3369 scope=rname.left(index);
3370 rname=rname.mid(index+2);
3371 }
3372 else
3373 {
3374 scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
3375 }
3378 MemberName *mn = Doxygen::functionNameLinkedMap->find(rname);
3379 bool found=false;
3380 if (mn) // symbol with the same name already found
3381 {
3382 for (auto &imd : *mn)
3383 {
3384 if (!imd->isTypedef())
3385 continue;
3386
3387 QCString rtype = root->type;
3388 rtype.stripPrefix("typedef ");
3389
3390 // merge the typedefs only if they're not both grouped, and both are
3391 // either part of the same class, part of the same namespace, or both
3392 // are global (i.e., neither in a class or a namespace)
3393 bool notBothGrouped = root->groups.empty() || imd->getGroupDef()==nullptr; // see example #100
3394 bool bothSameScope = (!cd && !nd) || (cd && imd->getClassDef() == cd) || (nd && imd->getNamespaceDef() == nd);
3395 //printf("imd->isTypedef()=%d imd->typeString()=%s root->type=%s\n",imd->isTypedef(),
3396 // qPrint(imd->typeString()),qPrint(root->type));
3397 if (notBothGrouped && bothSameScope && imd->typeString()==rtype)
3398 {
3399 MemberDefMutable *md = toMemberDefMutable(imd.get());
3400 if (md)
3401 {
3402 md->setDocumentation(root->doc,root->docFile,root->docLine);
3404 md->setDocsForDefinition(!root->proto);
3405 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3407 md->setRefItems(root->sli);
3408 md->addQualifiers(root->qualifiers);
3409
3410 // merge ingroup specifiers
3411 if (md->getGroupDef()==nullptr && !root->groups.empty())
3412 {
3413 addMemberToGroups(root,md);
3414 }
3415 else if (md->getGroupDef()!=nullptr && root->groups.empty())
3416 {
3417 //printf("existing member is grouped, new member not\n");
3418 }
3419 else if (md->getGroupDef()!=nullptr && !root->groups.empty())
3420 {
3421 //printf("both members are grouped\n");
3422 }
3423 found=true;
3424 break;
3425 }
3426 }
3427 }
3428 }
3429 if (found)
3430 {
3431 AUTO_TRACE_ADD("typedef '{}' already found",rname);
3432 // mark the entry as processed, as we copied everything from it elsewhere
3433 // also, otherwise, due to containing `typedef` it may later get treated
3434 // as a function typedef in filterMemberDocumentation, which is incorrect
3435 root->markAsProcessed();
3436 }
3437 else
3438 {
3439 AUTO_TRACE_ADD("new typedef '{}'",rname);
3440 addVariable(root);
3441 }
3442
3443 }
3444 for (const auto &e : root->children())
3445 if (!e->section.isEnum())
3446 buildTypedefList(e.get());
3447}
3448
3449//----------------------------------------------------------------------
3450// Searches the Entry tree for sequence documentation sections.
3451// If found they are stored in the global list.
3452static void buildSequenceList(const Entry *root)
3453{
3454 if (!root->name.isEmpty() &&
3455 root->section.isVariable() &&
3456 root->type.find("sequence<")!=-1 // it's a sequence
3457 )
3458 {
3459 AUTO_TRACE();
3460 addVariable(root);
3461 }
3462 for (const auto &e : root->children())
3463 if (!e->section.isEnum())
3464 buildSequenceList(e.get());
3465}
3466
3467//----------------------------------------------------------------------
3468// Searches the Entry tree for dictionary documentation sections.
3469// If found they are stored in the global list.
3470static void buildDictionaryList(const Entry *root)
3471{
3472 if (!root->name.isEmpty() &&
3473 root->section.isVariable() &&
3474 root->type.find("dictionary<")!=-1 // it's a dictionary
3475 )
3476 {
3477 AUTO_TRACE();
3478 addVariable(root);
3479 }
3480 for (const auto &e : root->children())
3481 if (!e->section.isEnum())
3482 buildDictionaryList(e.get());
3483}
3484
3485//----------------------------------------------------------------------
3486// Searches the Entry tree for Variable documentation sections.
3487// If found they are stored in their class or in the global list.
3488
3489static void buildVarList(const Entry *root)
3490{
3491 //printf("buildVarList(%s) section=%08x\n",qPrint(rootNav->name()),rootNav->section());
3492 int isFuncPtr=-1;
3493 if (!root->name.isEmpty() &&
3494 (root->type.isEmpty() || g_compoundKeywords.find(root->type.str())==g_compoundKeywords.end()) &&
3495 (
3496 (root->section.isVariable() && // it's a variable
3497 root->type.find("typedef ")==-1 // and not a typedef
3498 ) ||
3499 (root->section.isFunction() && // or maybe a function pointer variable
3500 (isFuncPtr=findFunctionPtr(root->type.str(),root->lang))!=-1
3501 ) ||
3502 (root->section.isFunction() && // class variable initialized by constructor
3504 )
3505 )
3506 ) // documented variable
3507 {
3508 AUTO_TRACE();
3509 addVariable(root,isFuncPtr);
3510 }
3511 for (const auto &e : root->children())
3512 if (!e->section.isEnum())
3513 buildVarList(e.get());
3514}
3515
3516//----------------------------------------------------------------------
3517// Searches the Entry tree for Interface sections (UNO IDL only).
3518// If found they are stored in their service or in the global list.
3519//
3520
3522 const Entry *root,
3523 ClassDefMutable *cd,
3524 QCString const& rname)
3525{
3526 FileDef *fd = root->fileDef();
3527 enum MemberType type = root->section.isExportedInterface() ? MemberType::Interface : MemberType::Service;
3528 QCString fileName = root->fileName;
3529 if (fileName.isEmpty() && root->tagInfo())
3530 {
3531 fileName = root->tagInfo()->tagName;
3532 }
3533 auto md = createMemberDef(
3534 fileName, root->startLine, root->startColumn, root->type, rname,
3535 "", "", root->protection, root->virt, root->isStatic, Relationship::Member,
3536 type, ArgumentList(), root->argList, root->metaData);
3537 auto mmd = toMemberDefMutable(md.get());
3538 mmd->setTagInfo(root->tagInfo());
3539 mmd->setMemberClass(cd);
3540 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3541 mmd->setDocsForDefinition(false);
3542 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3543 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3544 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3545 mmd->setMemberSpecifiers(root->spec);
3546 mmd->setVhdlSpecifiers(root->vhdlSpec);
3547 mmd->setMemberGroupId(root->mGrpId);
3548 mmd->setTypeConstraints(root->typeConstr);
3549 mmd->setLanguage(root->lang);
3550 mmd->setBodyDef(fd);
3551 mmd->setFileDef(fd);
3552 mmd->addSectionsToDefinition(root->anchors);
3553 QCString const def = root->type + " " + rname;
3554 mmd->setDefinition(def);
3556 mmd->addQualifiers(root->qualifiers);
3557
3558 AUTO_TRACE("Interface member: fileName='{}' type='{}' name='{}' mtype='{}' prot={} virt={} state={} proto={} def='{}'",
3559 fileName,root->type,rname,type,root->protection,root->virt,root->isStatic,root->proto,def);
3560
3561 // add member to the class cd
3562 cd->insertMember(md.get());
3563 // also add the member as a "base" (to get nicer diagrams)
3564 // "optional" interface/service get Protected which turns into dashed line
3565 BaseInfo base(rname,
3566 root->spec.isOptional() ? Protection::Protected : Protection::Public, Specifier::Normal);
3567 TemplateNameMap templateNames;
3568 findClassRelation(root,cd,cd,&base,templateNames,DocumentedOnly,true) ||
3569 findClassRelation(root,cd,cd,&base,templateNames,Undocumented,true);
3570 // add file to list of used files
3571 cd->insertUsedFile(fd);
3572
3573 addMemberToGroups(root,md.get());
3575 root->markAsProcessed();
3576 mmd->setRefItems(root->sli);
3577
3578 // add member to the global list of all members
3579 MemberName *mn = Doxygen::memberNameLinkedMap->add(rname);
3580 mn->push_back(std::move(md));
3581}
3582
3583static void buildInterfaceAndServiceList(const Entry *root)
3584{
3585 if (root->section.isExportedInterface() || root->section.isIncludedService())
3586 {
3587 AUTO_TRACE("Exported interface/included service: type='{}' scope='{}' name='{}' args='{}'"
3588 " relates='{}' relatesType='{}' file='{}' line={} bodyLine={} #tArgLists={}"
3589 " mGrpId={} spec={} proto={} docFile='{}'",
3590 root->type, root->parent()->name, root->name, root->args,
3591 root->relates, root->relatesType, root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(),
3592 root->mGrpId, root->spec, root->proto, root->docFile);
3593
3595
3596 if (!rname.isEmpty())
3597 {
3598 QCString scope = root->parent()->name;
3599 ClassDefMutable *cd = getClassMutable(scope);
3600 assert(cd);
3601 if (cd && ((ClassDef::Interface == cd->compoundType()) ||
3602 (ClassDef::Service == cd->compoundType()) ||
3604 {
3606 }
3607 else
3608 {
3609 assert(false); // was checked by scanner.l
3610 }
3611 }
3612 else if (rname.isEmpty())
3613 {
3614 warn(root->fileName,root->startLine,
3615 "Illegal member name found.");
3616 }
3617 }
3618 // can only have these in IDL anyway
3619 switch (root->lang)
3620 {
3621 case SrcLangExt::Unknown: // fall through (root node always is Unknown)
3622 case SrcLangExt::IDL:
3623 for (const auto &e : root->children()) buildInterfaceAndServiceList(e.get());
3624 break;
3625 default:
3626 return; // nothing to do here
3627 }
3628}
3629
3630
3631//----------------------------------------------------------------------
3632// Searches the Entry tree for Function sections.
3633// If found they are stored in their class or in the global list.
3634
3635static void addMethodToClass(const Entry *root,ClassDefMutable *cd,
3636 const QCString &rtype,const QCString &rname,const QCString &rargs,
3637 bool isFriend,
3638 Protection protection,bool stat,Specifier virt,TypeSpecifier spec,
3639 const QCString &relates
3640 )
3641{
3642 FileDef *fd=root->fileDef();
3643
3644 QCString type = rtype;
3645 QCString args = rargs;
3646
3648 name.stripPrefix("::");
3649
3651 if (isFriend) mtype=MemberType::Friend;
3652 else if (root->mtype==MethodTypes::Signal) mtype=MemberType::Signal;
3653 else if (root->mtype==MethodTypes::Slot) mtype=MemberType::Slot;
3654 else if (root->mtype==MethodTypes::DCOP) mtype=MemberType::DCOP;
3655
3656 // strip redundant template specifier for constructors
3657 int i = -1;
3658 int j = -1;
3659 if ((fd==nullptr || fd->getLanguage()==SrcLangExt::Cpp) &&
3660 !name.startsWith("operator ") && // not operator
3661 (i=name.find('<'))!=-1 && // containing <
3662 (j=name.find('>'))!=-1 && // or >
3663 (j!=i+2 || name.at(i+1)!='=') // but not the C++20 spaceship operator <=>
3664 )
3665 {
3666 name=name.left(i);
3667 }
3668
3669 QCString fileName = root->fileName;
3670 if (fileName.isEmpty() && root->tagInfo())
3671 {
3672 fileName = root->tagInfo()->tagName;
3673 }
3674
3675 //printf("root->name='%s; args='%s' root->argList='%s'\n",
3676 // qPrint(root->name),qPrint(args),qPrint(argListToString(root->argList))
3677 // );
3678
3679 // adding class member
3680 Relationship relationship = relates.isEmpty() ? Relationship::Member :
3681 root->relatesType==RelatesType::MemberOf ? Relationship::Foreign :
3682 Relationship::Related ;
3683 auto md = createMemberDef(
3684 fileName,root->startLine,root->startColumn,
3685 type,name,args,root->exception,
3686 protection,virt,
3687 stat && root->relatesType!=RelatesType::MemberOf,
3688 relationship,
3689 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3690 root->argList, root->metaData);
3691 auto mmd = toMemberDefMutable(md.get());
3692 mmd->setTagInfo(root->tagInfo());
3693 mmd->setMemberClass(cd);
3694 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3695 mmd->setDocsForDefinition(!root->proto);
3696 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3697 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3698 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3699 mmd->setMemberSpecifiers(spec);
3700 mmd->setVhdlSpecifiers(root->vhdlSpec);
3701 mmd->setMemberGroupId(root->mGrpId);
3702 mmd->setTypeConstraints(root->typeConstr);
3703 mmd->setLanguage(root->lang);
3704 mmd->setRequiresClause(root->req);
3705 mmd->setId(root->id);
3706 mmd->setBodyDef(fd);
3707 mmd->setFileDef(fd);
3708 mmd->addSectionsToDefinition(root->anchors);
3709 QCString def;
3711 SrcLangExt lang = cd->getLanguage();
3712 QCString scopeSeparator=getLanguageSpecificSeparator(lang);
3713 if (scopeSeparator!="::")
3714 {
3715 qualScope = substitute(qualScope,"::",scopeSeparator);
3716 }
3717 if (lang==SrcLangExt::PHP)
3718 {
3719 // for PHP we use Class::method and Namespace\method
3720 scopeSeparator="::";
3721 }
3722// QCString optArgs = root->argList.empty() ? args : QCString();
3723 if (!relates.isEmpty() || isFriend || Config_getBool(HIDE_SCOPE_NAMES))
3724 {
3725 if (!type.isEmpty())
3726 {
3727 def=type+" "+name; //+optArgs;
3728 }
3729 else
3730 {
3731 def=name; //+optArgs;
3732 }
3733 }
3734 else
3735 {
3736 if (!type.isEmpty())
3737 {
3738 def=type+" "+qualScope+scopeSeparator+name; //+optArgs;
3739 }
3740 else
3741 {
3742 def=qualScope+scopeSeparator+name; //+optArgs;
3743 }
3744 }
3745 def.stripPrefix("friend ");
3746 mmd->setDefinition(def);
3748 mmd->addQualifiers(root->qualifiers);
3749
3750 AUTO_TRACE("function member: type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
3751 type, qualScope, rname, args, root->proto, def);
3752
3753 // add member to the class cd
3754 cd->insertMember(md.get());
3755 // add file to list of used files
3756 cd->insertUsedFile(fd);
3757
3758 addMemberToGroups(root,md.get());
3760 root->markAsProcessed();
3761 mmd->setRefItems(root->sli);
3762
3763 // add member to the global list of all members
3764 //printf("Adding member=%s class=%s\n",qPrint(md->name()),qPrint(cd->name()));
3766 mn->push_back(std::move(md));
3767}
3768
3769//------------------------------------------------------------------------------------------
3770
3771static void addGlobalFunction(const Entry *root,const QCString &rname,const QCString &sc)
3772{
3773 QCString scope = sc;
3774
3775 // new global function
3777 auto md = createMemberDef(
3778 root->fileName,root->startLine,root->startColumn,
3779 root->type,name,root->args,root->exception,
3780 root->protection,root->virt,root->isStatic,Relationship::Member,
3782 !root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3783 root->argList,root->metaData);
3784 auto mmd = toMemberDefMutable(md.get());
3785 mmd->setTagInfo(root->tagInfo());
3786 mmd->setLanguage(root->lang);
3787 mmd->setId(root->id);
3788 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
3789 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3790 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3791 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
3792 mmd->setDocsForDefinition(!root->proto);
3793 mmd->setTypeConstraints(root->typeConstr);
3794 //md->setBody(root->body);
3795 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3796 FileDef *fd=root->fileDef();
3797 mmd->setBodyDef(fd);
3798 mmd->addSectionsToDefinition(root->anchors);
3799 mmd->setMemberSpecifiers(root->spec);
3800 mmd->setVhdlSpecifiers(root->vhdlSpec);
3801 mmd->setMemberGroupId(root->mGrpId);
3802 mmd->setRequiresClause(root->req);
3803 mmd->setExplicitExternal(root->explicitExternal,root->fileName,root->startLine,root->startColumn);
3804
3805 NamespaceDefMutable *nd = nullptr;
3806 // see if the function is inside a namespace that was not part of
3807 // the name already (in that case nd should be non-zero already)
3808 if (root->parent()->section.isNamespace())
3809 {
3810 //QCString nscope=removeAnonymousScopes(root->parent()->name);
3811 QCString nscope=root->parent()->name;
3812 if (!nscope.isEmpty())
3813 {
3814 nd = getResolvedNamespaceMutable(nscope);
3815 }
3816 }
3817 else if (root->parent()->section.isGroupDoc() && !scope.isEmpty())
3818 {
3820 }
3821
3822 if (!scope.isEmpty())
3823 {
3825 if (sep!="::")
3826 {
3827 scope = substitute(scope,"::",sep);
3828 }
3829 scope+=sep;
3830 }
3831
3832 if (Config_getBool(HIDE_SCOPE_NAMES)) scope = "";
3833 QCString def;
3834 //QCString optArgs = root->argList.empty() ? QCString() : root->args;
3835 if (!root->type.isEmpty())
3836 {
3837 def=root->type+" "+scope+name; //+optArgs;
3838 }
3839 else
3840 {
3841 def=scope+name; //+optArgs;
3842 }
3843 AUTO_TRACE("new non-member function type='{}' scope='{}' name='{}' args='{}' proto={} def='{}'",
3844 root->type,scope,rname,root->args,root->proto,def);
3845 mmd->setDefinition(def);
3847 mmd->addQualifiers(root->qualifiers);
3848
3849 mmd->setRefItems(root->sli);
3850 if (nd && !nd->name().isEmpty() && nd->name().at(0)!='@')
3851 {
3852 // add member to namespace
3853 mmd->setNamespace(nd);
3854 nd->insertMember(md.get());
3855 }
3856 if (fd)
3857 {
3858 // add member to the file (we do this even if we have already
3859 // inserted it into the namespace)
3860 mmd->setFileDef(fd);
3861 fd->insertMember(md.get());
3862 }
3863
3864 addMemberToGroups(root,md.get());
3866 if (root->relatesType == RelatesType::Simple) // if this is a relatesalso command,
3867 // allow find Member to pick it up
3868 {
3869 root->markAsProcessed(); // Otherwise we have finished with this entry.
3870 }
3871
3872 // add member to the list of file members
3874 mn->push_back(std::move(md));
3875}
3876
3877//------------------------------------------------------------------------------------------
3878
3879static void buildFunctionList(const Entry *root)
3880{
3881 if (root->section.isFunction())
3882 {
3883 AUTO_TRACE("member function: type='{}' scope='{}' name='{}' args='{}' relates='{}' relatesType='{}'"
3884 " file='{}' line={} bodyLine={} #tArgLists={} mGrpId={}"
3885 " spec={} proto={} docFile='{}'",
3886 root->type, root->parent()->name, root->name, root->args, root->relates, root->relatesType,
3887 root->fileName, root->startLine, root->bodyLine, root->tArgLists.size(), root->mGrpId,
3888 root->spec, root->proto, root->docFile);
3889
3890 bool isFriend=root->type.find("friend ")!=-1;
3892 //printf("rname=%s\n",qPrint(rname));
3893
3894 QCString scope;
3895 int index = computeQualifiedIndex(rname);
3896 if (index!=-1 && root->parent()->section.isGroupDoc() && root->parent()->tagInfo())
3897 // grouped members are stored with full scope
3898 {
3899 buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo());
3900 scope=rname.left(index);
3901 rname=rname.mid(index+2);
3902 }
3903 else
3904 {
3905 scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
3906 }
3907 if (!rname.isEmpty() && scope.find('@')==-1)
3908 {
3909 // check if this function's parent is a class
3910 if (root->lang==SrcLangExt::CSharp)
3911 {
3912 scope=mangleCSharpGenericName(scope);
3913 }
3914 else
3915 {
3917 }
3918
3919 FileDef *rfd=root->fileDef();
3920
3921 int memIndex=rname.findRev("::");
3922
3924 if (cd && scope+"::"==rname.left(scope.length()+2)) // found A::f inside A
3925 {
3926 // strip scope from name
3927 rname=rname.right(rname.length()-root->parent()->name.length()-2);
3928 }
3929
3930 bool isMember=FALSE;
3931 if (memIndex!=-1)
3932 {
3933 int ts=rname.find('<');
3934 int te=rname.find('>');
3935 if (memIndex>0 && (ts==-1 || te==-1))
3936 {
3937 // note: the following code was replaced by inMember=TRUE to deal with a
3938 // function rname='X::foo' of class X inside a namespace also called X...
3939 // bug id 548175
3940 //nd = Doxygen::namespaceLinkedMap->find(rname.left(memIndex));
3941 //isMember = nd==nullptr;
3942 //if (nd)
3943 //{
3944 // // strip namespace scope from name
3945 // scope=rname.left(memIndex);
3946 // rname=rname.right(rname.length()-memIndex-2);
3947 //}
3948 isMember = TRUE;
3949 }
3950 else
3951 {
3952 isMember=memIndex<ts || memIndex>te;
3953 }
3954 }
3955
3956 if (!root->parent()->name.isEmpty() && root->parent()->section.isCompound() && cd)
3957 {
3958 AUTO_TRACE_ADD("member '{}' of class '{}'", rname,cd->name());
3959 addMethodToClass(root,cd,root->type,rname,root->args,isFriend,
3960 root->protection,root->isStatic,root->virt,root->spec,root->relates);
3961 }
3962 else if (root->parent()->section.isObjcImpl() && cd)
3963 {
3964 const MemberDef *md = cd->getMemberByName(rname);
3965 if (md)
3966 {
3967 MemberDefMutable *mdm = toMemberDefMutable(const_cast<MemberDef*>(md));
3968 if (mdm)
3969 {
3970 mdm->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3971 mdm->setBodyDef(root->fileDef());
3972 }
3973 }
3974 }
3975 else if (!root->parent()->section.isCompound() && !root->parent()->section.isObjcImpl() &&
3976 !isMember &&
3977 (root->relates.isEmpty() || root->relatesType==RelatesType::Duplicate) &&
3978 !root->type.startsWith("extern ") && !root->type.startsWith("typedef ")
3979 )
3980 // no member => unrelated function
3981 {
3982 /* check the uniqueness of the function name in the file.
3983 * A file could contain a function prototype and a function definition
3984 * or even multiple function prototypes.
3985 */
3986 bool found=FALSE;
3987 MemberDef *md_found=nullptr;
3988 MemberName *mn = Doxygen::functionNameLinkedMap->find(rname);
3989 if (mn)
3990 {
3991 AUTO_TRACE_ADD("function '{}' already found",rname);
3992 for (const auto &imd : *mn)
3993 {
3994 MemberDefMutable *md = toMemberDefMutable(imd.get());
3995 if (md)
3996 {
3997 const NamespaceDef *mnd = md->getNamespaceDef();
3998 NamespaceDef *rnd = nullptr;
3999 //printf("root namespace=%s\n",qPrint(rootNav->parent()->name()));
4000 QCString fullScope = scope;
4001 QCString parentScope = root->parent()->name;
4002 if (!parentScope.isEmpty() && !leftScopeMatch(parentScope,scope))
4003 {
4004 if (!scope.isEmpty()) fullScope.prepend("::");
4005 fullScope.prepend(parentScope);
4006 }
4007 //printf("fullScope=%s\n",qPrint(fullScope));
4008 rnd = getResolvedNamespace(fullScope);
4009 const FileDef *mfd = md->getFileDef();
4010 QCString nsName,rnsName;
4011 if (mnd) nsName = mnd->name();
4012 if (rnd) rnsName = rnd->name();
4013 //printf("matching arguments for %s%s %s%s\n",
4014 // qPrint(md->name()),md->argsString(),qPrint(rname),qPrint(argListToString(root->argList)));
4015 const ArgumentList &mdAl = md->argumentList();
4016 const ArgumentList &mdTempl = md->templateArguments();
4017
4018 // in case of template functions, we need to check if the
4019 // functions have the same number of template parameters
4020 bool sameTemplateArgs = TRUE;
4021 bool matchingReturnTypes = TRUE;
4022 bool sameRequiresClause = TRUE;
4023 if (!mdTempl.empty() && !root->tArgLists.empty())
4024 {
4025 sameTemplateArgs = matchTemplateArguments(mdTempl,root->tArgLists.back());
4026 if (md->typeString()!=removeRedundantWhiteSpace(root->type))
4027 {
4028 matchingReturnTypes = FALSE;
4029 }
4030 if (md->requiresClause()!=root->req)
4031 {
4032 sameRequiresClause = FALSE;
4033 }
4034 }
4035 else if (!mdTempl.empty() || !root->tArgLists.empty())
4036 { // if one has template parameters and the other doesn't then that also counts as a
4037 // difference
4038 sameTemplateArgs = FALSE;
4039 }
4040
4041 bool staticsInDifferentFiles =
4042 root->isStatic && md->isStatic() && root->fileName!=md->getDefFileName();
4043
4044 if (
4045 matchArguments2(md->getOuterScope(),mfd,&mdAl,
4046 rnd ? rnd : Doxygen::globalScope,rfd,&root->argList,
4047 FALSE,root->lang) &&
4048 sameTemplateArgs &&
4049 matchingReturnTypes &&
4050 sameRequiresClause &&
4051 !staticsInDifferentFiles
4052 )
4053 {
4054 GroupDef *gd=nullptr;
4055 if (!root->groups.empty() && !root->groups.front().groupname.isEmpty())
4056 {
4057 gd = Doxygen::groupLinkedMap->find(root->groups.front().groupname);
4058 }
4059 //printf("match!\n");
4060 //printf("mnd=%p rnd=%p nsName=%s rnsName=%s\n",mnd,rnd,qPrint(nsName),qPrint(rnsName));
4061 // see if we need to create a new member
4062 found=(mnd && rnd && nsName==rnsName) || // members are in the same namespace
4063 ((mnd==nullptr && rnd==nullptr && mfd!=nullptr && // no external reference and
4064 mfd->absFilePath()==root->fileName // prototype in the same file
4065 )
4066 );
4067 // otherwise, allow a duplicate global member with the same argument list
4068 if (!found && gd && gd==md->getGroupDef() && nsName==rnsName)
4069 {
4070 // member is already in the group, so we don't want to add it again.
4071 found=TRUE;
4072 }
4073
4074 AUTO_TRACE_ADD("combining function with prototype found={} in namespace '{}'",found,nsName);
4075
4076 if (found)
4077 {
4078 // merge argument lists
4079 ArgumentList mergedArgList = root->argList;
4080 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.isEmpty());
4081 // merge documentation
4082 if (md->documentation().isEmpty() && !root->doc.isEmpty())
4083 {
4084 if (root->proto)
4085 {
4087 }
4088 else
4089 {
4091 }
4092 }
4093
4094 md->setDocumentation(root->doc,root->docFile,root->docLine);
4096 md->setDocsForDefinition(!root->proto);
4097 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
4098 {
4099 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
4100 md->setBodyDef(rfd);
4101 }
4102
4103 if (md->briefDescription().isEmpty() && !root->brief.isEmpty())
4104 {
4105 md->setArgsString(root->args);
4106 }
4107 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
4108
4110
4112 md->addQualifiers(root->qualifiers);
4113
4114 // merge ingroup specifiers
4115 if (md->getGroupDef()==nullptr && !root->groups.empty())
4116 {
4117 addMemberToGroups(root,md);
4118 }
4119 else if (md->getGroupDef()!=nullptr && root->groups.empty())
4120 {
4121 //printf("existing member is grouped, new member not\n");
4122 }
4123 else if (md->getGroupDef()!=nullptr && !root->groups.empty())
4124 {
4125 //printf("both members are grouped\n");
4126 }
4128
4129 // if md is a declaration and root is the corresponding
4130 // definition, then turn md into a definition.
4131 if (md->isPrototype() && !root->proto)
4132 {
4133 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
4134 md->setPrototype(FALSE,root->fileName,root->startLine,root->startColumn);
4135 }
4136 // if md is already the definition, then add the declaration info
4137 else if (!md->isPrototype() && root->proto)
4138 {
4139 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
4140 }
4141 }
4142 }
4143 }
4144 if (found)
4145 {
4146 md_found = md;
4147 break;
4148 }
4149 }
4150 }
4151 if (!found) /* global function is unique with respect to the file */
4152 {
4153 addGlobalFunction(root,rname,scope);
4154 }
4155 else
4156 {
4157 FileDef *fd=root->fileDef();
4158 if (fd)
4159 {
4160 // add member to the file (we do this even if we have already
4161 // inserted it into the namespace)
4162 fd->insertMember(md_found);
4163 }
4164 }
4165
4166 AUTO_TRACE_ADD("unrelated function type='{}' name='{}' args='{}'",root->type,rname,root->args);
4167 }
4168 else
4169 {
4170 AUTO_TRACE_ADD("function '{}' is not processed",rname);
4171 }
4172 }
4173 else if (rname.isEmpty())
4174 {
4175 warn(root->fileName,root->startLine,
4176 "Illegal member name found."
4177 );
4178 }
4179 }
4180 for (const auto &e : root->children()) buildFunctionList(e.get());
4181}
4182
4183//----------------------------------------------------------------------
4184
4185static void findFriends()
4186{
4187 AUTO_TRACE();
4188 for (const auto &fn : *Doxygen::functionNameLinkedMap) // for each global function name
4189 {
4190 MemberName *mn = Doxygen::memberNameLinkedMap->find(fn->memberName());
4191 if (mn)
4192 { // there are members with the same name
4193 // for each function with that name
4194 for (const auto &ifmd : *fn)
4195 {
4196 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
4197 // for each member with that name
4198 for (const auto &immd : *mn)
4199 {
4200 MemberDefMutable *mmd = toMemberDefMutable(immd.get());
4201 //printf("Checking for matching arguments
4202 // mmd->isRelated()=%d mmd->isFriend()=%d mmd->isFunction()=%d\n",
4203 // mmd->isRelated(),mmd->isFriend(),mmd->isFunction());
4204 if (fmd && mmd &&
4205 (mmd->isFriend() || (mmd->isRelated() && mmd->isFunction())) &&
4206 matchArguments2(mmd->getOuterScope(), mmd->getFileDef(), &mmd->argumentList(),
4207 fmd->getOuterScope(), fmd->getFileDef(), &fmd->argumentList(),
4208 TRUE,mmd->getLanguage()
4209 )
4210
4211 ) // if the member is related and the arguments match then the
4212 // function is actually a friend.
4213 {
4214 AUTO_TRACE_ADD("Merging related global and member '{}' isFriend={} isRelated={} isFunction={}",
4215 mmd->name(),mmd->isFriend(),mmd->isRelated(),mmd->isFunction());
4216 const ArgumentList &mmdAl = mmd->argumentList();
4217 const ArgumentList &fmdAl = fmd->argumentList();
4218 mergeArguments(const_cast<ArgumentList&>(fmdAl),const_cast<ArgumentList&>(mmdAl));
4219
4220 // reset argument lists to add missing default parameters
4221 QCString mmdAlStr = argListToString(mmdAl);
4222 QCString fmdAlStr = argListToString(fmdAl);
4223 mmd->setArgsString(mmdAlStr);
4224 fmd->setArgsString(fmdAlStr);
4225 mmd->moveDeclArgumentList(std::make_unique<ArgumentList>(mmdAl));
4226 fmd->moveDeclArgumentList(std::make_unique<ArgumentList>(fmdAl));
4227 AUTO_TRACE_ADD("friend args='{}' member args='{}'",argListToString(fmd->argumentList()),argListToString(mmd->argumentList()));
4228
4229 if (!fmd->documentation().isEmpty())
4230 {
4231 mmd->setDocumentation(fmd->documentation(),fmd->docFile(),fmd->docLine());
4232 }
4233 else if (!mmd->documentation().isEmpty())
4234 {
4235 fmd->setDocumentation(mmd->documentation(),mmd->docFile(),mmd->docLine());
4236 }
4237 if (mmd->briefDescription().isEmpty() && !fmd->briefDescription().isEmpty())
4238 {
4239 mmd->setBriefDescription(fmd->briefDescription(),fmd->briefFile(),fmd->briefLine());
4240 }
4241 else if (!mmd->briefDescription().isEmpty() && !fmd->briefDescription().isEmpty())
4242 {
4243 fmd->setBriefDescription(mmd->briefDescription(),mmd->briefFile(),mmd->briefLine());
4244 }
4245 if (!fmd->inbodyDocumentation().isEmpty())
4246 {
4248 }
4249 else if (!mmd->inbodyDocumentation().isEmpty())
4250 {
4252 }
4253 //printf("body mmd %d fmd %d\n",mmd->getStartBodyLine(),fmd->getStartBodyLine());
4254 if (mmd->getStartBodyLine()==-1 && fmd->getStartBodyLine()!=-1)
4255 {
4256 mmd->setBodySegment(fmd->getDefLine(),fmd->getStartBodyLine(),fmd->getEndBodyLine());
4257 mmd->setBodyDef(fmd->getBodyDef());
4258 //mmd->setBodyMember(fmd);
4259 }
4260 else if (mmd->getStartBodyLine()!=-1 && fmd->getStartBodyLine()==-1)
4261 {
4262 fmd->setBodySegment(mmd->getDefLine(),mmd->getStartBodyLine(),mmd->getEndBodyLine());
4263 fmd->setBodyDef(mmd->getBodyDef());
4264 //fmd->setBodyMember(mmd);
4265 }
4267
4269
4270 mmd->addQualifiers(fmd->getQualifiers());
4271 fmd->addQualifiers(mmd->getQualifiers());
4272
4273 }
4274 }
4275 }
4276 }
4277 }
4278}
4279
4280//----------------------------------------------------------------------
4281
4283{
4284 AUTO_TRACE();
4285
4286 // find matching function declaration and definitions.
4287 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4288 {
4289 //printf("memberName=%s count=%zu\n",qPrint(mn->memberName()),mn->size());
4290 /* find a matching function declaration and definition for this function */
4291 for (const auto &imdec : *mn)
4292 {
4293 MemberDefMutable *mdec = toMemberDefMutable(imdec.get());
4294 if (mdec &&
4295 (mdec->isPrototype() ||
4296 (mdec->isVariable() && mdec->isExternal())
4297 ))
4298 {
4299 for (const auto &imdef : *mn)
4300 {
4301 MemberDefMutable *mdef = toMemberDefMutable(imdef.get());
4302 if (mdef && mdec!=mdef &&
4303 mdec->getNamespaceDef()==mdef->getNamespaceDef())
4304 {
4306 }
4307 }
4308 }
4309 }
4310 }
4311}
4312
4313//----------------------------------------------------------------------
4314
4316{
4317 AUTO_TRACE();
4318 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4319 {
4320 MemberDefMutable *mdef=nullptr,*mdec=nullptr;
4321 /* find a matching function declaration and definition for this function */
4322 for (const auto &imd : *mn)
4323 {
4324 MemberDefMutable *md = toMemberDefMutable(imd.get());
4325 if (md)
4326 {
4327 if (md->isPrototype())
4328 mdec=md;
4329 else if (md->isVariable() && md->isExternal())
4330 mdec=md;
4331
4332 if (md->isFunction() && !md->isStatic() && !md->isPrototype())
4333 mdef=md;
4334 else if (md->isVariable() && !md->isExternal() && !md->isStatic())
4335 mdef=md;
4336 }
4337
4338 if (mdef && mdec) break;
4339 }
4340 if (mdef && mdec)
4341 {
4342 const ArgumentList &mdefAl = mdef->argumentList();
4343 const ArgumentList &mdecAl = mdec->argumentList();
4344 if (
4345 matchArguments2(mdef->getOuterScope(),mdef->getFileDef(),const_cast<ArgumentList*>(&mdefAl),
4346 mdec->getOuterScope(),mdec->getFileDef(),const_cast<ArgumentList*>(&mdecAl),
4347 TRUE,mdef->getLanguage()
4348 )
4349 ) /* match found */
4350 {
4351 AUTO_TRACE_ADD("merging references for mdec={} mdef={}",mdec->name(),mdef->name());
4352 mdef->mergeReferences(mdec);
4353 mdec->mergeReferences(mdef);
4354 mdef->mergeReferencedBy(mdec);
4355 mdec->mergeReferencedBy(mdef);
4356 }
4357 }
4358 }
4359}
4360
4361//----------------------------------------------------------------------
4362
4364{
4365 AUTO_TRACE();
4366 // find match between function declaration and definition for
4367 // related functions
4368 for (const auto &mn : *Doxygen::functionNameLinkedMap)
4369 {
4370 /* find a matching function declaration and definition for this function */
4371 // for each global function
4372 for (const auto &imd : *mn)
4373 {
4374 MemberDefMutable *md = toMemberDefMutable(imd.get());
4375 if (md)
4376 {
4377 //printf(" Function '%s'\n",qPrint(md->name()));
4378 MemberName *rmn = Doxygen::memberNameLinkedMap->find(md->name());
4379 if (rmn) // check if there is a member with the same name
4380 {
4381 //printf(" Member name found\n");
4382 // for each member with the same name
4383 for (const auto &irmd : *rmn)
4384 {
4385 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
4386 //printf(" Member found: related='%d'\n",rmd->isRelated());
4387 if (rmd &&
4388 (rmd->isRelated() || rmd->isForeign()) && // related function
4390 rmd->getOuterScope(),rmd->getFileDef(),&rmd->argumentList(),
4391 TRUE,md->getLanguage()
4392 )
4393 )
4394 {
4395 AUTO_TRACE_ADD("Found related member '{}'",md->name());
4396 if (rmd->relatedAlso())
4397 md->setRelatedAlso(rmd->relatedAlso());
4398 else if (rmd->isForeign())
4399 md->makeForeign();
4400 else
4401 md->makeRelated();
4402 }
4403 }
4404 }
4405 }
4406 }
4407 }
4408}
4409
4410//----------------------------------------------------------------------
4411
4413{
4414 AUTO_TRACE();
4415 for (const auto &[qualifiedName,bodyInfo] : Doxygen::staticInitMap)
4416 {
4417 size_t i=qualifiedName.rfind("::");
4418 if (i!=std::string::npos)
4419 {
4420 QCString scope = qualifiedName.substr(0,i);
4421 QCString name = qualifiedName.substr(i+2);
4422 MemberName *mn = Doxygen::memberNameLinkedMap->find(name);
4423 if (mn)
4424 {
4425 for (const auto &imd : *mn)
4426 {
4427 MemberDefMutable *md = toMemberDefMutable(imd.get());
4428 if (md && md->qualifiedName().str()==qualifiedName && md->isVariable())
4429 {
4430 AUTO_TRACE_ADD("found static member {} body [{}..{}]\n",
4431 md->qualifiedName(),bodyInfo.startLine,bodyInfo.endLine);
4432 md->setBodySegment(bodyInfo.defLine,
4433 bodyInfo.startLine,
4434 bodyInfo.endLine);
4435 }
4436 }
4437 }
4438 }
4439 }
4440}
4441
4442//----------------------------------------------------------------------
4443
4444/*! make a dictionary of all template arguments of class cd
4445 * that are part of the base class name.
4446 * Example: A template class A with template arguments <R,S,T>
4447 * that inherits from B<T,T,S> will have T and S in the dictionary.
4448 */
4449static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments,const std::string &name)
4450{
4451 std::map<std::string,int> templateNames;
4452 int count=0;
4453 for (const Argument &arg : templateArguments)
4454 {
4455 static const reg::Ex re(R"(\a[\w:]*)");
4456 reg::Iterator it(name,re);
4458 for (; it!=end ; ++it)
4459 {
4460 const auto &match = *it;
4461 std::string n = match.str();
4462 if (n==arg.name.str())
4463 {
4464 if (templateNames.find(n)==templateNames.end())
4465 {
4466 templateNames.emplace(n,count);
4467 }
4468 }
4469 }
4470 }
4471 return templateNames;
4472}
4473
4474/*! Searches a class from within \a context and \a cd and returns its
4475 * definition if found (otherwise nullptr is returned).
4476 */
4478{
4479 ClassDef *result=nullptr;
4480 if (cd==nullptr)
4481 {
4482 return result;
4483 }
4484 FileDef *fd=cd->getFileDef();
4485 SymbolResolver resolver(fd);
4486 if (context && cd!=context)
4487 {
4488 result = const_cast<ClassDef*>(resolver.resolveClass(context,name,true,true));
4489 }
4490 //printf("1. result=%p\n",result);
4491 if (result==nullptr)
4492 {
4493 result = const_cast<ClassDef*>(resolver.resolveClass(cd,name,true,true));
4494 }
4495 //printf("2. result=%p\n",result);
4496 if (result==nullptr) // try direct class, needed for namespaced classes imported via tag files (see bug624095)
4497 {
4498 result = getClass(name);
4499 }
4500 //printf("3. result=%p\n",result);
4501 //printf("** Trying to find %s within context %s class %s result=%s lookup=%p\n",
4502 // qPrint(name),
4503 // context ? qPrint(context->name()) : "<none>",
4504 // cd ? qPrint(cd->name()) : "<none>",
4505 // result ? qPrint(result->name()) : "<none>",
4506 // Doxygen::classLinkedMap->find(name)
4507 // );
4508 return result;
4509}
4510
4511
4512static void findUsedClassesForClass(const Entry *root,
4513 Definition *context,
4514 ClassDefMutable *masterCd,
4515 ClassDefMutable *instanceCd,
4516 bool isArtificial,
4517 const ArgumentList *actualArgs = nullptr,
4518 const TemplateNameMap &templateNames = TemplateNameMap()
4519 )
4520{
4521 AUTO_TRACE();
4522 const ArgumentList &formalArgs = masterCd->templateArguments();
4523 for (auto &mni : masterCd->memberNameInfoLinkedMap())
4524 {
4525 for (auto &mi : *mni)
4526 {
4527 const MemberDef *md=mi->memberDef();
4528 if (md->isVariable() || md->isObjCProperty()) // for each member variable in this class
4529 {
4530 AUTO_TRACE_ADD("Found variable '{}' in class '{}'",md->name(),masterCd->name());
4531 QCString type = normalizeNonTemplateArgumentsInString(md->typeString(),masterCd,formalArgs);
4532 QCString typedefValue = md->getLanguage()==SrcLangExt::Java ? type : resolveTypeDef(masterCd,type);
4533 if (!typedefValue.isEmpty())
4534 {
4535 type = typedefValue;
4536 }
4537 int pos=0;
4538 QCString usedClassName;
4539 QCString templSpec;
4540 bool found=FALSE;
4541 // the type can contain template variables, replace them if present
4542 type = substituteTemplateArgumentsInString(type,formalArgs,actualArgs);
4543
4544 //printf(" template substitution gives=%s\n",qPrint(type));
4545 while (!found && extractClassNameFromType(type,pos,usedClassName,templSpec,root->lang)!=-1)
4546 {
4547 // find the type (if any) that matches usedClassName
4548 SymbolResolver resolver(masterCd->getFileDef());
4549 const ClassDefMutable *typeCd = resolver.resolveClassMutable(masterCd,usedClassName,false,true);
4550 //printf("====> usedClassName=%s -> typeCd=%s\n",
4551 // qPrint(usedClassName),typeCd?qPrint(typeCd->name()):"<none>");
4552 if (typeCd)
4553 {
4554 usedClassName = typeCd->name();
4555 }
4556
4557 int sp=usedClassName.find('<');
4558 if (sp==-1) sp=0;
4559 // replace any namespace aliases
4560 replaceNamespaceAliases(usedClassName);
4561 // add any template arguments to the class
4562 QCString usedName = removeRedundantWhiteSpace(usedClassName+templSpec);
4563 //printf(" usedName=%s usedClassName=%s templSpec=%s\n",qPrint(usedName),qPrint(usedClassName),qPrint(templSpec));
4564
4565 TemplateNameMap formTemplateNames;
4566 if (templateNames.empty())
4567 {
4568 formTemplateNames = getTemplateArgumentsInName(formalArgs,usedName.str());
4569 }
4570 BaseInfo bi(usedName,Protection::Public,Specifier::Normal);
4571 findClassRelation(root,context,instanceCd,&bi,formTemplateNames,TemplateInstances,isArtificial);
4572
4573 for (const Argument &arg : masterCd->templateArguments())
4574 {
4575 if (arg.name==usedName) // type is a template argument
4576 {
4577 ClassDef *usedCd = Doxygen::hiddenClassLinkedMap->find(usedName);
4578 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4579 if (usedCd==nullptr)
4580 {
4581 usedCdm = toClassDefMutable(
4582 Doxygen::hiddenClassLinkedMap->add(usedName,
4584 masterCd->getDefFileName(),masterCd->getDefLine(),
4585 masterCd->getDefColumn(),
4586 usedName,
4587 ClassDef::Class)));
4588 if (usedCdm)
4589 {
4590 //printf("making %s a template argument!!!\n",qPrint(usedCd->name()));
4591 usedCdm->makeTemplateArgument();
4592 usedCdm->setUsedOnly(TRUE);
4593 usedCdm->setLanguage(masterCd->getLanguage());
4594 usedCd = usedCdm;
4595 }
4596 }
4597 if (usedCd)
4598 {
4599 found=TRUE;
4600 AUTO_TRACE_ADD("case 1: adding used class '{}'", usedCd->name());
4601 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4602 if (usedCdm)
4603 {
4604 if (isArtificial) usedCdm->setArtificial(TRUE);
4605 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4606 }
4607 }
4608 }
4609 }
4610
4611 if (!found)
4612 {
4613 ClassDef *usedCd=findClassWithinClassContext(context,masterCd,usedName);
4614 //printf("Looking for used class %s: result=%s master=%s\n",
4615 // qPrint(usedName),usedCd?qPrint(usedCd->name()):"<none>",masterCd?qPrint(masterCd->name()):"<none>");
4616
4617 if (usedCd)
4618 {
4619 found=TRUE;
4620 AUTO_TRACE_ADD("case 2: adding used class '{}'", usedCd->name());
4621 instanceCd->addUsedClass(usedCd,md->name(),md->protection()); // class exists
4622 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4623 if (usedCdm)
4624 {
4625 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4626 }
4627 }
4628 }
4629 }
4630 if (!found && !type.isEmpty()) // used class is not documented in any scope
4631 {
4632 ClassDef *usedCd = Doxygen::hiddenClassLinkedMap->find(type);
4633 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4634 if (usedCd==nullptr && !Config_getBool(HIDE_UNDOC_RELATIONS))
4635 {
4636 if (type.endsWith("(*") || type.endsWith("(^")) // type is a function pointer
4637 {
4638 type+=md->argsString();
4639 }
4640 AUTO_TRACE_ADD("New undocumented used class '{}'", type);
4641 usedCdm = toClassDefMutable(
4644 masterCd->getDefFileName(),masterCd->getDefLine(),
4645 masterCd->getDefColumn(),
4646 type,ClassDef::Class)));
4647 if (usedCdm)
4648 {
4649 usedCdm->setUsedOnly(TRUE);
4650 usedCdm->setLanguage(masterCd->getLanguage());
4651 usedCd = usedCdm;
4652 }
4653 }
4654 if (usedCd)
4655 {
4656 AUTO_TRACE_ADD("case 3: adding used class '{}'", usedCd->name());
4657 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4658 if (usedCdm)
4659 {
4660 if (isArtificial) usedCdm->setArtificial(TRUE);
4661 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4662 }
4663 }
4664 }
4665 }
4666 }
4667 }
4668}
4669
4671 const Entry *root,
4672 Definition *context,
4673 ClassDefMutable *masterCd,
4674 ClassDefMutable *instanceCd,
4676 bool isArtificial,
4677 const ArgumentList *actualArgs = nullptr,
4678 const TemplateNameMap &templateNames=TemplateNameMap()
4679 )
4680{
4681 AUTO_TRACE("name={}",root->name);
4682 // The base class could ofcouse also be a non-nested class
4683 const ArgumentList &formalArgs = masterCd->templateArguments();
4684 for (const BaseInfo &bi : root->extends)
4685 {
4686 //printf("masterCd=%s bi.name='%s' #actualArgs=%d\n",
4687 // qPrint(masterCd->localName()),qPrint(bi.name),actualArgs ? (int)actualArgs->size() : -1);
4688 TemplateNameMap formTemplateNames;
4689 if (templateNames.empty())
4690 {
4691 formTemplateNames = getTemplateArgumentsInName(formalArgs,bi.name.str());
4692 }
4693 BaseInfo tbi = bi;
4694 tbi.name = substituteTemplateArgumentsInString(bi.name,formalArgs,actualArgs);
4695 //printf("masterCd=%p instanceCd=%p bi->name=%s tbi.name=%s\n",(void*)masterCd,(void*)instanceCd,qPrint(bi.name),qPrint(tbi.name));
4696
4697 if (mode==DocumentedOnly)
4698 {
4699 // find a documented base class in the correct scope
4700 if (!findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,DocumentedOnly,isArtificial))
4701 {
4702 // 1.8.2: decided to show inheritance relations even if not documented,
4703 // we do make them artificial, so they do not appear in the index
4704 //if (!Config_getBool(HIDE_UNDOC_RELATIONS))
4705 bool b = Config_getBool(HIDE_UNDOC_RELATIONS) ? TRUE : isArtificial;
4706 //{
4707 // no documented base class -> try to find an undocumented one
4708 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,Undocumented,b);
4709 //}
4710 }
4711 }
4712 else if (mode==TemplateInstances)
4713 {
4714 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,TemplateInstances,isArtificial);
4715 }
4716 }
4717}
4718
4719//----------------------------------------------------------------------
4720
4721static void findTemplateInstanceRelation(const Entry *root,
4722 Definition *context,
4723 ClassDefMutable *templateClass,const QCString &templSpec,
4724 const TemplateNameMap &templateNames,
4725 bool isArtificial)
4726{
4727 AUTO_TRACE("Derived from template '{}' with parameters '{}' isArtificial={}",
4728 templateClass->name(),templSpec,isArtificial);
4729
4730 QCString tempArgsStr = tempArgListToString(templateClass->templateArguments(),root->lang,false);
4731 bool existingClass = templSpec==tempArgsStr;
4732 if (existingClass) return; // avoid recursion
4733
4734 bool freshInstance=FALSE;
4735 ClassDefMutable *instanceClass = toClassDefMutable(
4736 templateClass->insertTemplateInstance(
4737 root->fileName,root->startLine,root->startColumn,templSpec,freshInstance));
4738 if (instanceClass)
4739 {
4740 if (freshInstance)
4741 {
4742 instanceClass->setArtificial(TRUE);
4743 instanceClass->setLanguage(root->lang);
4744
4745 AUTO_TRACE_ADD("found fresh instance '{}'",instanceClass->name());
4746 instanceClass->setTemplateBaseClassNames(templateNames);
4747
4748 // search for new template instances caused by base classes of
4749 // instanceClass
4750 auto it_pair = g_classEntries.equal_range(templateClass->name().str());
4751 for (auto it=it_pair.first ; it!=it_pair.second ; ++it)
4752 {
4753 const Entry *templateRoot = it->second;
4754 AUTO_TRACE_ADD("template root found '{}' templSpec='{}'",templateRoot->name,templSpec);
4755 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(root->lang,templSpec);
4756 findBaseClassesForClass(templateRoot,context,templateClass,instanceClass,
4757 TemplateInstances,isArtificial,templArgs.get(),templateNames);
4758
4759 findUsedClassesForClass(templateRoot,context,templateClass,instanceClass,
4760 isArtificial,templArgs.get(),templateNames);
4761 }
4762 }
4763 else
4764 {
4765 AUTO_TRACE_ADD("instance already exists");
4766 }
4767 }
4768}
4769
4770//----------------------------------------------------------------------
4771
4772static void resolveTemplateInstanceInType(const Entry *root,const Definition *scope,const MemberDef *md)
4773{
4774 // For a statement like 'using X = T<A>', add a template instance 'T<A>' as a symbol, so it can
4775 // be used to match arguments (see issue #11111)
4776 AUTO_TRACE();
4777 QCString ttype = md->typeString();
4778 ttype.stripPrefix("typedef ");
4779 int ti=ttype.find('<');
4780 if (ti!=-1)
4781 {
4782 QCString templateClassName = ttype.left(ti);
4783 SymbolResolver resolver(root->fileDef());
4784 ClassDefMutable *baseClass = resolver.resolveClassMutable(scope ? scope : Doxygen::globalScope,
4785 templateClassName, true, true);
4786 AUTO_TRACE_ADD("templateClassName={} baseClass={}",templateClassName,baseClass?baseClass->name():"<none>");
4787 if (baseClass)
4788 {
4789 const ArgumentList &tl = baseClass->templateArguments();
4790 TemplateNameMap templateNames = getTemplateArgumentsInName(tl,templateClassName.str());
4792 baseClass,
4793 ttype.mid(ti),
4794 templateNames,
4795 baseClass->isArtificial());
4796 }
4797 }
4798}
4799
4800//----------------------------------------------------------------------
4801
4802static bool isRecursiveBaseClass(const QCString &scope,const QCString &name)
4803{
4804 QCString n=name;
4805 int index=n.find('<');
4806 if (index!=-1)
4807 {
4808 n=n.left(index);
4809 }
4810 bool result = rightScopeMatch(scope,n);
4811 return result;
4812}
4813
4815{
4816 if (name.isEmpty()) return 0;
4817 int l = static_cast<int>(name.length());
4818 if (name[l-1]=='>') // search backward to find the matching <, allowing nested <...> and strings.
4819 {
4820 int count=1;
4821 int i=l-2;
4822 char insideQuote=0;
4823 while (count>0 && i>=0)
4824 {
4825 char c = name[i--];
4826 switch (c)
4827 {
4828 case '>': if (!insideQuote) count++; break;
4829 case '<': if (!insideQuote) count--; break;
4830 case '\'': if (!insideQuote) insideQuote=c;
4831 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
4832 break;
4833 case '"': if (!insideQuote) insideQuote=c;
4834 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
4835 break;
4836 default: break;
4837 }
4838 }
4839 if (i>=0) l=i+1;
4840 }
4841 return l;
4842}
4843
4845 const Entry *root,
4846 Definition *context,
4847 ClassDefMutable *cd,
4848 const BaseInfo *bi,
4849 const TemplateNameMap &templateNames,
4851 bool isArtificial
4852 )
4853{
4854 AUTO_TRACE("name={} base={} isArtificial={} mode={}",cd->name(),bi->name,isArtificial,(int)mode);
4855
4856 QCString biName=bi->name;
4857 bool explicitGlobalScope=FALSE;
4858 if (biName.startsWith("::")) // explicit global scope
4859 {
4860 biName=biName.right(biName.length()-2);
4861 explicitGlobalScope=TRUE;
4862 }
4863
4864 Entry *parentNode=root->parent();
4865 bool lastParent=FALSE;
4866 do // for each parent scope, starting with the largest scope
4867 // (in case of nested classes)
4868 {
4869 QCString scopeName= parentNode ? parentNode->name : QCString();
4870 int scopeOffset=explicitGlobalScope ? 0 : static_cast<int>(scopeName.length());
4871 do // try all parent scope prefixes, starting with the largest scope
4872 {
4873 //printf("scopePrefix='%s' biName='%s'\n",
4874 // qPrint(scopeName.left(scopeOffset)),qPrint(biName));
4875
4876 QCString baseClassName=biName;
4877 if (scopeOffset>0)
4878 {
4879 baseClassName.prepend(scopeName.left(scopeOffset)+"::");
4880 }
4881 if (root->lang==SrcLangExt::CSharp)
4882 {
4883 baseClassName = mangleCSharpGenericName(baseClassName);
4884 }
4885 AUTO_TRACE_ADD("cd='{}' baseClassName='{}'",cd->name(),baseClassName);
4886 SymbolResolver resolver(cd->getFileDef());
4887 ClassDefMutable *baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
4888 baseClassName,
4889 mode==Undocumented,
4890 true
4891 );
4892 const MemberDef *baseClassTypeDef = resolver.getTypedef();
4893 QCString templSpec = resolver.getTemplateSpec();
4894 //printf("baseClassName=%s baseClass=%p cd=%p explicitGlobalScope=%d\n",
4895 // qPrint(baseClassName),baseClass,cd,explicitGlobalScope);
4896 //printf(" scope='%s' baseClassName='%s' baseClass=%s templSpec=%s\n",
4897 // cd ? qPrint(cd->name()):"<none>",
4898 // qPrint(baseClassName),
4899 // baseClass?qPrint(baseClass->name()):"<none>",
4900 // qPrint(templSpec)
4901 // );
4902 //if (baseClassName.left(root->name.length())!=root->name ||
4903 // baseClassName.at(root->name.length())!='<'
4904 // ) // Check for base class with the same name.
4905 // // If found then look in the outer scope for a match
4906 // // and prevent recursion.
4907 if (!isRecursiveBaseClass(root->name,baseClassName)
4908 || explicitGlobalScope
4909 // sadly isRecursiveBaseClass always true for UNO IDL ifc/svc members
4910 // (i.e. this is needed for addInterfaceOrServiceToServiceOrSingleton)
4911 || (root->lang==SrcLangExt::IDL &&
4912 (root->section.isExportedInterface() ||
4913 root->section.isIncludedService()))
4914 )
4915 {
4916 AUTO_TRACE_ADD("class relation '{}' inherited/used by '{}' found prot={} virt={} templSpec='{}'",
4917 baseClassName, root->name, bi->prot, bi->virt, templSpec);
4918
4919 int i=findTemplateSpecializationPosition(baseClassName);
4920 int si=baseClassName.findRev("::",i);
4921 if (si==-1) si=0;
4922 if (baseClass==nullptr && static_cast<size_t>(i)!=baseClassName.length())
4923 // base class has template specifiers
4924 {
4925 // TODO: here we should try to find the correct template specialization
4926 // but for now, we only look for the unspecialized base class.
4927 int e=findEndOfTemplate(baseClassName,i+1);
4928 //printf("baseClass==0 i=%d e=%d\n",i,e);
4929 if (e!=-1) // end of template was found at e
4930 {
4931 templSpec = removeRedundantWhiteSpace(baseClassName.mid(i,e-i));
4932 baseClassName = baseClassName.left(i)+baseClassName.right(baseClassName.length()-e);
4933 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
4934 baseClassName,
4935 mode==Undocumented,
4936 true
4937 );
4938 baseClassTypeDef = resolver.getTypedef();
4939 //printf("baseClass=%p -> baseClass=%s templSpec=%s\n",
4940 // baseClass,qPrint(baseClassName),qPrint(templSpec));
4941 }
4942 }
4943 else if (baseClass && !templSpec.isEmpty()) // we have a known class, but also
4944 // know it is a template, so see if
4945 // we can also link to the explicit
4946 // instance (for instance if a class
4947 // derived from a template argument)
4948 {
4949 //printf("baseClass=%s templSpec=%s\n",qPrint(baseClass->name()),qPrint(templSpec));
4950 ClassDefMutable *templClass=getClassMutable(baseClass->name()+templSpec);
4951 if (templClass)
4952 {
4953 // use the template instance instead of the template base.
4954 baseClass = templClass;
4955 templSpec.clear();
4956 }
4957 }
4958
4959 //printf("cd=%p baseClass=%p\n",cd,baseClass);
4960 bool found=baseClass!=nullptr && (baseClass!=cd || mode==TemplateInstances);
4961 AUTO_TRACE_ADD("1. found={}",found);
4962 if (!found && si!=-1)
4963 {
4964 // replace any namespace aliases
4965 replaceNamespaceAliases(baseClassName);
4966 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
4967 baseClassName,
4968 mode==Undocumented,
4969 true
4970 );
4971 baseClassTypeDef = resolver.getTypedef();
4972 found=baseClass!=nullptr && baseClass!=cd;
4973 if (found) templSpec = resolver.getTemplateSpec();
4974 }
4975 AUTO_TRACE_ADD("2. found={}",found);
4976
4977 if (!found)
4978 {
4979 baseClass=toClassDefMutable(findClassWithinClassContext(context,cd,baseClassName));
4980 //printf("findClassWithinClassContext(%s,%s)=%p\n",
4981 // qPrint(cd->name()),qPrint(baseClassName),baseClass);
4982 found = baseClass!=nullptr && baseClass!=cd;
4983
4984 }
4985 AUTO_TRACE_ADD("3. found={}",found);
4986 if (!found)
4987 {
4988 // for PHP the "use A\B as C" construct map class C to A::B, so we lookup
4989 // the class name also in the alias mapping.
4990 auto it = Doxygen::namespaceAliasMap.find(baseClassName.str());
4991 if (it!=Doxygen::namespaceAliasMap.end()) // see if it is indeed a class.
4992 {
4993 baseClass=getClassMutable(it->second.alias);
4994 found = baseClass!=nullptr && baseClass!=cd;
4995 }
4996 }
4997 bool isATemplateArgument = templateNames.find(biName.str())!=templateNames.end();
4998
4999 AUTO_TRACE_ADD("4. found={}",found);
5000 if (found)
5001 {
5002 AUTO_TRACE_ADD("Documented base class '{}' templSpec='{}'",biName,templSpec);
5003 // add base class to this class
5004
5005 // if templSpec is not empty then we should "instantiate"
5006 // the template baseClass. A new ClassDef should be created
5007 // to represent the instance. To be able to add the (instantiated)
5008 // members and documentation of a template class
5009 // (inserted in that template class at a later stage),
5010 // the template should know about its instances.
5011 // the instantiation process, should be done in a recursive way,
5012 // since instantiating a template may introduce new inheritance
5013 // relations.
5014 if (!templSpec.isEmpty() && mode==TemplateInstances)
5015 {
5016 // if baseClass is actually a typedef then we should not
5017 // instantiate it, since typedefs are in a different namespace
5018 // see bug531637 for an example where this would otherwise hang
5019 // Doxygen
5020 if (baseClassTypeDef==nullptr)
5021 {
5022 //printf(" => findTemplateInstanceRelation: %s\n",qPrint(baseClass->name()));
5023 findTemplateInstanceRelation(root,context,baseClass,templSpec,templateNames,baseClass->isArtificial());
5024 }
5025 }
5026 else if (mode==DocumentedOnly || mode==Undocumented)
5027 {
5028 //printf(" => insert base class\n");
5029 QCString usedName;
5030 if (baseClassTypeDef)
5031 {
5032 usedName=biName;
5033 //printf("***** usedName=%s templSpec=%s\n",qPrint(usedName),qPrint(templSpec));
5034 }
5035 Protection prot = bi->prot;
5036 if (Config_getBool(SIP_SUPPORT)) prot=Protection::Public;
5037 if (cd!=baseClass && !cd->isSubClass(baseClass) && baseClass->isBaseClass(cd,true,templSpec)==0) // check for recursion, see bug690787
5038 {
5039 AUTO_TRACE_ADD("insertBaseClass name={} prot={} virt={} templSpec={}",usedName,prot,bi->virt,templSpec);
5040 cd->insertBaseClass(baseClass,usedName,prot,bi->virt,templSpec);
5041 // add this class as super class to the base class
5042 baseClass->insertSubClass(cd,prot,bi->virt,templSpec);
5043 }
5044 else
5045 {
5046 warn(root->fileName,root->startLine,
5047 "Detected potential recursive class relation "
5048 "between class {} and base class {}!",
5049 cd->name(),baseClass->name()
5050 );
5051 }
5052 }
5053 return TRUE;
5054 }
5055 else if (mode==Undocumented && (scopeOffset==0 || isATemplateArgument))
5056 {
5057 AUTO_TRACE_ADD("New undocumented base class '{}' baseClassName='{}' templSpec='{}' isArtificial={}",
5058 biName,baseClassName,templSpec,isArtificial);
5059 baseClass=nullptr;
5060 if (isATemplateArgument)
5061 {
5062 baseClass = toClassDefMutable(Doxygen::hiddenClassLinkedMap->find(baseClassName));
5063 if (baseClass==nullptr) // not found (or alias)
5064 {
5065 baseClass= toClassDefMutable(
5066 Doxygen::hiddenClassLinkedMap->add(baseClassName,
5067 createClassDef(root->fileName,root->startLine,root->startColumn,
5068 baseClassName,
5069 ClassDef::Class)));
5070 if (baseClass) // really added (not alias)
5071 {
5072 if (isArtificial) baseClass->setArtificial(TRUE);
5073 baseClass->setLanguage(root->lang);
5074 }
5075 }
5076 }
5077 else
5078 {
5079 baseClass = toClassDefMutable(Doxygen::classLinkedMap->find(baseClassName));
5080 //printf("*** classDDict->find(%s)=%p biName=%s templSpec=%s\n",
5081 // qPrint(baseClassName),baseClass,qPrint(biName),qPrint(templSpec));
5082 if (baseClass==nullptr) // not found (or alias)
5083 {
5084 baseClass = toClassDefMutable(
5085 Doxygen::classLinkedMap->add(baseClassName,
5086 createClassDef(root->fileName,root->startLine,root->startColumn,
5087 baseClassName,
5088 ClassDef::Class)));
5089 if (baseClass) // really added (not alias)
5090 {
5091 if (isArtificial) baseClass->setArtificial(TRUE);
5092 baseClass->setLanguage(root->lang);
5093 si = baseClassName.findRev("::");
5094 if (si!=-1) // class is nested
5095 {
5096 Definition *sd = findScopeFromQualifiedName(Doxygen::globalScope,baseClassName.left(si),nullptr,root->tagInfo());
5097 if (sd==nullptr || sd==Doxygen::globalScope) // outer scope not found
5098 {
5099 baseClass->setArtificial(TRUE); // see bug678139
5100 }
5101 }
5102 }
5103 }
5104 }
5105 if (baseClass)
5106 {
5107 if (biName.endsWith("-p"))
5108 {
5109 biName="<"+biName.left(biName.length()-2)+">";
5110 }
5111 if (!cd->isSubClass(baseClass) && cd!=baseClass && cd->isBaseClass(baseClass,true,templSpec)==0) // check for recursion
5112 {
5113 AUTO_TRACE_ADD("insertBaseClass name={} prot={} virt={} templSpec={}",biName,bi->prot,bi->virt,templSpec);
5114 // add base class to this class
5115 cd->insertBaseClass(baseClass,biName,bi->prot,bi->virt,templSpec);
5116 // add this class as super class to the base class
5117 baseClass->insertSubClass(cd,bi->prot,bi->virt,templSpec);
5118 }
5119 // the undocumented base was found in this file
5120 baseClass->insertUsedFile(root->fileDef());
5121
5122 Definition *scope = buildScopeFromQualifiedName(baseClass->name(),root->lang,nullptr);
5123 if (scope!=baseClass)
5124 {
5125 baseClass->setOuterScope(scope);
5126 }
5127
5128 if (baseClassName.endsWith("-p"))
5129 {
5131 }
5132 return TRUE;
5133 }
5134 else
5135 {
5136 AUTO_TRACE_ADD("Base class '{}' not created (alias?)",biName);
5137 }
5138 }
5139 else
5140 {
5141 AUTO_TRACE_ADD("Base class '{}' not found",biName);
5142 }
5143 }
5144 else
5145 {
5146 if (mode!=TemplateInstances)
5147 {
5148 warn(root->fileName,root->startLine,
5149 "Detected potential recursive class relation "
5150 "between class {} and base class {}!",
5151 root->name,baseClassName
5152 );
5153 }
5154 // for mode==TemplateInstance this case is quite common and
5155 // indicates a relation between a template class and a template
5156 // instance with the same name.
5157 }
5158 if (scopeOffset==0)
5159 {
5160 scopeOffset=-1;
5161 }
5162 else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1)
5163 {
5164 scopeOffset=0;
5165 }
5166 //printf("new scopeOffset='%d'",scopeOffset);
5167 } while (scopeOffset>=0);
5168
5169 if (parentNode==nullptr)
5170 {
5171 lastParent=TRUE;
5172 }
5173 else
5174 {
5175 parentNode=parentNode->parent();
5176 }
5177 } while (lastParent);
5178
5179 return FALSE;
5180}
5181
5182//----------------------------------------------------------------------
5183// Computes the base and super classes for each class in the tree
5184
5185static bool isClassSection(const Entry *root)
5186{
5187 if ( !root->name.isEmpty() )
5188 {
5189 if (root->section.isCompound())
5190 // is it a compound (class, struct, union, interface ...)
5191 {
5192 return TRUE;
5193 }
5194 else if (root->section.isCompoundDoc())
5195 // is it a documentation block with inheritance info.
5196 {
5197 bool hasExtends = !root->extends.empty();
5198 if (hasExtends) return TRUE;
5199 }
5200 }
5201 return FALSE;
5202}
5203
5204
5205/*! Builds a dictionary of all entry nodes in the tree starting with \a root
5206 */
5207static void findClassEntries(const Entry *root)
5208{
5209 if (isClassSection(root))
5210 {
5211 g_classEntries.emplace(root->name.str(),root);
5212 }
5213 for (const auto &e : root->children()) findClassEntries(e.get());
5214}
5215
5217{
5218 // strip any anonymous scopes first
5221 int i=0;
5222 if ((root->lang==SrcLangExt::CSharp || root->lang==SrcLangExt::Java) &&
5223 (i=bName.find('<'))!=-1)
5224 {
5225 // a Java/C# generic class looks like a C++ specialization, so we need to strip the
5226 // template part before looking for matches
5227 if (root->lang==SrcLangExt::CSharp)
5228 {
5229 bName = mangleCSharpGenericName(root->name);
5230 }
5231 else
5232 {
5233 bName = bName.left(i);
5234 }
5235 }
5236 return bName;
5237}
5238
5239/*! Using the dictionary build by findClassEntries(), this
5240 * function will look for additional template specialization that
5241 * exists as inheritance relations only. These instances will be
5242 * added to the template they are derived from.
5243 */
5245{
5246 AUTO_TRACE();
5247 ClassDefSet visitedClasses;
5248 for (const auto &[name,root] : g_classEntries)
5249 {
5250 QCString bName = extractClassName(root);
5251 ClassDefMutable *cdm = getClassMutable(bName);
5252 if (cdm)
5253 {
5255 }
5256 }
5257}
5258
5260{
5261 AUTO_TRACE("root->name={} cd={}",root->name,cd->name());
5262 int i = root->name.find('<');
5263 int j = root->name.findRev('>');
5264 int k = root->name.find("::",j+1); // A<T::B> => ok, A<T>::B => nok
5265 if (i!=-1 && j!=-1 && k==-1 && root->lang!=SrcLangExt::CSharp && root->lang!=SrcLangExt::Java)
5266 {
5267 ClassDefMutable *master = getClassMutable(root->name.left(i));
5268 if (master && master!=cd && !cd->templateMaster())
5269 {
5270 AUTO_TRACE_ADD("class={} master={}",cd->name(),cd->templateMaster()?cd->templateMaster()->name():"<none>",master->name());
5271 cd->setTemplateMaster(master);
5272 master->insertExplicitTemplateInstance(cd,root->name.mid(i));
5273 }
5274 }
5275}
5276
5278{
5279 AUTO_TRACE();
5280 for (const auto &[name,root] : g_classEntries)
5281 {
5282 QCString bName = extractClassName(root);
5283 ClassDefMutable *cdm = getClassMutable(bName);
5284 if (cdm)
5285 {
5286 findUsedClassesForClass(root,cdm,cdm,cdm,TRUE);
5288 cdm->addTypeConstraints();
5289 }
5290 }
5291}
5292
5294{
5295 AUTO_TRACE();
5296 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5297 {
5298 if (!nd->hasDocumentation())
5299 {
5300 if ((guessSection(nd->getDefFileName()).isHeader() ||
5301 nd->getLanguage() == SrcLangExt::Fortran) && // Fortran doesn't have header files.
5302 !Config_getBool(HIDE_UNDOC_NAMESPACES) // undocumented namespaces are visible
5303 )
5304 {
5305 warn_undoc(nd->getDefFileName(),nd->getDefLine(), "{} {} is not documented.",
5306 nd->getLanguage() == SrcLangExt::Fortran ? "Module" : "Namespace",
5307 nd->name());
5308 }
5309 }
5310 }
5311}
5312
5314{
5315 AUTO_TRACE();
5316 for (const auto &[name,root] : g_classEntries)
5317 {
5318 QCString bName = extractClassName(root);
5319 ClassDefMutable *cd = getClassMutable(bName);
5320 if (cd)
5321 {
5323 }
5324 size_t numMembers = cd ? cd->memberNameInfoLinkedMap().size() : 0;
5325 if ((cd==nullptr || (!cd->hasDocumentation() && !cd->isReference())) && numMembers>0 && !bName.endsWith("::"))
5326 {
5327 if (!root->name.isEmpty() && root->name.find('@')==-1 && // normal name
5328 (guessSection(root->fileName).isHeader() ||
5329 Config_getBool(EXTRACT_LOCAL_CLASSES)) && // not defined in source file
5330 protectionLevelVisible(root->protection) && // hidden by protection
5331 !Config_getBool(HIDE_UNDOC_CLASSES) // undocumented class are visible
5332 )
5333 warn_undoc(root->fileName,root->startLine, "Compound {} is not documented.", root->name);
5334 }
5335 }
5336}
5337
5339{
5340 AUTO_TRACE();
5341 for (const auto &[name,root] : g_classEntries)
5342 {
5346 // strip any anonymous scopes first
5347 if (cd && !cd->getTemplateInstances().empty())
5348 {
5349 AUTO_TRACE_ADD("Template class '{}'",cd->name());
5350 for (const auto &ti : cd->getTemplateInstances()) // for each template instance
5351 {
5352 ClassDefMutable *tcd=toClassDefMutable(ti.classDef);
5353 if (tcd)
5354 {
5355 AUTO_TRACE_ADD("Template instance '{}'",tcd->name());
5356 QCString templSpec = ti.templSpec;
5357 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(tcd->getLanguage(),templSpec);
5358 for (const BaseInfo &bi : root->extends)
5359 {
5360 // check if the base class is a template argument
5361 BaseInfo tbi = bi;
5362 const ArgumentList &tl = cd->templateArguments();
5363 if (!tl.empty())
5364 {
5365 TemplateNameMap baseClassNames = tcd->getTemplateBaseClassNames();
5366 TemplateNameMap templateNames = getTemplateArgumentsInName(tl,bi.name.str());
5367 // for each template name that we inherit from we need to
5368 // substitute the formal with the actual arguments
5369 TemplateNameMap actualTemplateNames;
5370 for (const auto &tn_kv : templateNames)
5371 {
5372 size_t templIndex = tn_kv.second;
5373 Argument actArg;
5374 bool hasActArg=FALSE;
5375 if (templIndex<templArgs->size())
5376 {
5377 actArg=templArgs->at(templIndex);
5378 hasActArg=TRUE;
5379 }
5380 if (hasActArg &&
5381 baseClassNames.find(actArg.type.str())!=baseClassNames.end() &&
5382 actualTemplateNames.find(actArg.type.str())==actualTemplateNames.end()
5383 )
5384 {
5385 actualTemplateNames.emplace(actArg.type.str(),static_cast<int>(templIndex));
5386 }
5387 }
5388
5389 tbi.name = substituteTemplateArgumentsInString(bi.name,tl,templArgs.get());
5390 // find a documented base class in the correct scope
5391 if (!findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,DocumentedOnly,FALSE))
5392 {
5393 // no documented base class -> try to find an undocumented one
5394 findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,Undocumented,TRUE);
5395 }
5396 }
5397 }
5398 }
5399 }
5400 }
5401 }
5402}
5403
5404//-----------------------------------------------------------------------
5405// compute the references (anchors in HTML) for each function in the file
5406
5408{
5409 AUTO_TRACE();
5410 for (const auto &cd : *Doxygen::classLinkedMap)
5411 {
5412 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5413 if (cdm)
5414 {
5415 cdm->computeAnchors();
5416 }
5417 }
5418 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5419 {
5420 for (const auto &fd : *fn)
5421 {
5422 fd->computeAnchors();
5423 }
5424 }
5425 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5426 {
5428 if (ndm)
5429 {
5430 ndm->computeAnchors();
5431 }
5432 }
5433 for (const auto &gd : *Doxygen::groupLinkedMap)
5434 {
5435 gd->computeAnchors();
5436 }
5437}
5438
5439//----------------------------------------------------------------------
5440
5442{
5443 AUTO_TRACE();
5444 for (const auto &cd : *Doxygen::classLinkedMap)
5445 {
5446 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5447 if (cdm)
5448 {
5449 cdm->addListReferences();
5450 }
5451 }
5452
5453 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5454 {
5455 for (const auto &fd : *fn)
5456 {
5457 fd->addListReferences();
5458 }
5459 }
5460
5461 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5462 {
5464 if (ndm)
5465 {
5466 ndm->addListReferences();
5467 }
5468 }
5469
5470 for (const auto &gd : *Doxygen::groupLinkedMap)
5471 {
5472 gd->addListReferences();
5473 }
5474
5475 for (const auto &pd : *Doxygen::pageLinkedMap)
5476 {
5477 QCString name = pd->getOutputFileBase();
5478 if (pd->getGroupDef())
5479 {
5480 name = pd->getGroupDef()->getOutputFileBase();
5481 }
5482 {
5483 const RefItemVector &xrefItems = pd->xrefListItems();
5484 addRefItem(xrefItems,
5485 name,
5486 theTranslator->trPage(TRUE,TRUE),
5487 name,pd->title(),QCString(),nullptr);
5488 }
5489 }
5490
5491 for (const auto &dd : *Doxygen::dirLinkedMap)
5492 {
5493 QCString name = dd->getOutputFileBase();
5494 //if (dd->getGroupDef())
5495 //{
5496 // name = dd->getGroupDef()->getOutputFileBase();
5497 //}
5498 const RefItemVector &xrefItems = dd->xrefListItems();
5499 addRefItem(xrefItems,
5500 name,
5501 theTranslator->trDir(TRUE,TRUE),
5502 name,dd->displayName(),QCString(),nullptr);
5503 }
5504
5506}
5507
5508//----------------------------------------------------------------------
5509
5511{
5512 AUTO_TRACE();
5514 {
5515 rl->generatePage();
5516 }
5517}
5518
5519//----------------------------------------------------------------------
5520// Copy the documentation in entry 'root' to member definition 'md' and
5521// set the function declaration of the member to 'funcDecl'. If the boolean
5522// over_load is set the standard overload text is added.
5523
5524static void addMemberDocs(const Entry *root,
5525 MemberDefMutable *md, const QCString &funcDecl,
5526 const ArgumentList *al,
5527 bool over_load,
5528 TypeSpecifier spec
5529 )
5530{
5531 if (md==nullptr) return;
5532 AUTO_TRACE("scope='{}' name='{}' args='{}' funcDecl='{}' mSpec={}",
5533 root->parent()->name,md->name(),md->argsString(),funcDecl,spec);
5534 if (!root->section.isDoc()) // @fn or @var does not need to specify the complete definition, so don't overwrite it
5535 {
5536 QCString fDecl=funcDecl;
5537 // strip extern specifier
5538 fDecl.stripPrefix("extern ");
5539 md->setDefinition(fDecl);
5540 }
5542 md->addQualifiers(root->qualifiers);
5544 const NamespaceDef *nd=md->getNamespaceDef();
5545 QCString fullName;
5546 if (cd)
5547 fullName = cd->name();
5548 else if (nd)
5549 fullName = nd->name();
5550
5551 if (!fullName.isEmpty()) fullName+="::";
5552 fullName+=md->name();
5553 FileDef *rfd=root->fileDef();
5554
5555 // TODO determine scope based on root not md
5556 Definition *rscope = md->getOuterScope();
5557
5558 const ArgumentList &mdAl = md->argumentList();
5559 if (al)
5560 {
5561 ArgumentList mergedAl = *al;
5562 //printf("merging arguments (1) docs=%d\n",root->doc.isEmpty());
5563 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedAl,!root->doc.isEmpty());
5564 }
5565 else
5566 {
5567 if (
5568 matchArguments2( md->getOuterScope(), md->getFileDef(),const_cast<ArgumentList*>(&mdAl),
5569 rscope,rfd,&root->argList,
5570 TRUE, root->lang
5571 )
5572 )
5573 {
5574 //printf("merging arguments (2)\n");
5575 ArgumentList mergedArgList = root->argList;
5576 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.isEmpty());
5577 }
5578 }
5579 if (over_load) // the \overload keyword was used
5580 {
5582 if (!root->doc.isEmpty())
5583 {
5584 doc+="<p>";
5585 doc+=root->doc;
5586 }
5587 md->setDocumentation(doc,root->docFile,root->docLine);
5589 md->setDocsForDefinition(!root->proto);
5590 }
5591 else
5592 {
5593 //printf("overwrite!\n");
5594 md->setDocumentation(root->doc,root->docFile,root->docLine);
5595 md->setDocsForDefinition(!root->proto);
5596
5597 //printf("overwrite!\n");
5598 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
5599
5600 if (
5601 (md->inbodyDocumentation().isEmpty() ||
5602 !root->parent()->name.isEmpty()
5603 ) && !root->inbodyDocs.isEmpty()
5604 )
5605 {
5607 }
5608 }
5609
5610 //printf("initializer: '%s'(isEmpty=%d) '%s'(isEmpty=%d)\n",
5611 // qPrint(md->initializer()),md->initializer().isEmpty(),
5612 // qPrint(root->initializer),root->initializer.isEmpty()
5613 // );
5614 std::string rootInit = root->initializer.str();
5615 if (md->initializer().isEmpty() && !rootInit.empty())
5616 {
5617 //printf("setInitializer\n");
5618 md->setInitializer(rootInit);
5619 }
5620 if (md->requiresClause().isEmpty() && !root->req.isEmpty())
5621 {
5622 md->setRequiresClause(root->req);
5623 }
5624
5625 md->setMaxInitLines(root->initLines);
5626
5627 if (rfd)
5628 {
5629 if ((md->getStartBodyLine()==-1 && root->bodyLine!=-1)
5630 )
5631 {
5632 //printf("Setting new body segment [%d,%d]\n",root->bodyLine,root->endBodyLine);
5633 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
5634 md->setBodyDef(rfd);
5635 }
5636
5637 md->setRefItems(root->sli);
5638 }
5639
5641 md->addQualifiers(root->qualifiers);
5642
5643 md->mergeMemberSpecifiers(spec);
5645 addMemberToGroups(root,md);
5647 if (cd) cd->insertUsedFile(rfd);
5648 //printf("root->mGrpId=%d\n",root->mGrpId);
5649 if (root->mGrpId!=-1)
5650 {
5651 if (md->getMemberGroupId()!=-1)
5652 {
5653 if (md->getMemberGroupId()!=root->mGrpId)
5654 {
5655 warn(root->fileName,root->startLine,
5656 "member {} belongs to two different groups. The second one found here will be ignored.",
5657 md->name()
5658 );
5659 }
5660 }
5661 else // set group id
5662 {
5663 //printf("setMemberGroupId=%d md=%s\n",root->mGrpId,qPrint(md->name()));
5664 md->setMemberGroupId(root->mGrpId);
5665 }
5666 }
5667 md->addQualifiers(root->qualifiers);
5668}
5669
5670//----------------------------------------------------------------------
5671// find a class definition given the scope name and (optionally) a
5672// template list specifier
5673
5675 const QCString &scopeName)
5676{
5677 SymbolResolver resolver(fd);
5678 const ClassDef *tcd = resolver.resolveClass(nd,scopeName,true,true);
5679 //printf("findClassDefinition(fd=%s,ns=%s,scopeName=%s)='%s'\n",
5680 // qPrint(fd?fd->name():""),qPrint(nd?nd->name():""),
5681 // qPrint(scopeName),qPrint(tcd?tcd->name():""));
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 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=%s cd=%s\n",
6206 // qPrint(scopeName),nd?qPrint(nd->name()):"<none>",tcd?qPrint(tcd->name()):"",qPrint(cd->name()));
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,
6504 root->virt,root->isStatic,Relationship::Member,
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
6559 if (root->mtype==MethodTypes::Signal) mtype=MemberType::Signal;
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,
6570 root->protection,root->virt,root->isStatic,Relationship::Related,
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("thread_local "))
6698 {
6699 spec.setThreadLocal(true);
6700 done=false;
6701 }
6702 if (funcDecl.stripPrefix("virtual "))
6703 {
6704 done=false;
6705 }
6706 }
6707
6708 // delete any ; from the function declaration
6709 int sep=0;
6710 while ((sep=funcDecl.find(';'))!=-1)
6711 {
6712 funcDecl=(funcDecl.left(sep)+funcDecl.right(funcDecl.length()-sep-1)).stripWhiteSpace();
6713 }
6714
6715 // make sure the first character is a space to simplify searching.
6716 if (!funcDecl.isEmpty() && funcDecl[0]!=' ') funcDecl.prepend(" ");
6717
6718 // remove some superfluous spaces
6719 funcDecl= substitute(
6720 substitute(
6721 substitute(funcDecl,"~ ","~"),
6722 ":: ","::"
6723 ),
6724 " ::","::"
6725 ).stripWhiteSpace();
6726
6727 //printf("funcDecl='%s'\n",qPrint(funcDecl));
6728 if (isFriend && funcDecl.startsWith("class "))
6729 {
6730 //printf("friend class\n");
6731 funcDecl=funcDecl.right(funcDecl.length()-6);
6732 funcName = funcDecl;
6733 }
6734 else if (isFriend && funcDecl.startsWith("struct "))
6735 {
6736 funcDecl=funcDecl.right(funcDecl.length()-7);
6737 funcName = funcDecl;
6738 }
6739 else
6740 {
6741 // extract information from the declarations
6742 parseFuncDecl(funcDecl,root->lang,scopeName,funcType,funcName,
6743 funcArgs,funcTempList,exceptions
6744 );
6745 }
6746
6747 // the class name can also be a namespace name, we decide this later.
6748 // if a related class name is specified and the class name could
6749 // not be derived from the function declaration, then use the
6750 // related field.
6751 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}' funcType='{}' funcName='{}' funcArgs='{}'",
6752 scopeName,className,namespaceName,funcType,funcName,funcArgs);
6753 if (!relates.isEmpty())
6754 { // related member, prefix user specified scope
6755 isRelated=TRUE;
6756 isMemberOf=(root->relatesType == RelatesType::MemberOf);
6757 if (getClass(relates)==nullptr && !scopeName.isEmpty())
6758 {
6759 scopeName= mergeScopes(scopeName,relates);
6760 }
6761 else
6762 {
6763 scopeName = relates;
6764 }
6765 }
6766
6767 if (relates.isEmpty() && root->parent() &&
6768 (root->parent()->section.isScope() || root->parent()->section.isObjcImpl()) &&
6769 !root->parent()->name.isEmpty()) // see if we can combine scopeName
6770 // with the scope in which it was found
6771 {
6772 QCString joinedName = root->parent()->name+"::"+scopeName;
6773 if (!scopeName.isEmpty() &&
6774 (getClass(joinedName) || Doxygen::namespaceLinkedMap->find(joinedName)))
6775 {
6776 scopeName = joinedName;
6777 }
6778 else
6779 {
6780 scopeName = mergeScopes(root->parent()->name,scopeName);
6781 }
6782 }
6783 else // see if we can prefix a namespace or class that is used from the file
6784 {
6785 FileDef *fd=root->fileDef();
6786 if (fd)
6787 {
6788 for (const auto &fnd : fd->getUsedNamespaces())
6789 {
6790 QCString joinedName = fnd->name()+"::"+scopeName;
6791 if (Doxygen::namespaceLinkedMap->find(joinedName))
6792 {
6793 scopeName=joinedName;
6794 break;
6795 }
6796 }
6797 }
6798 }
6800 removeRedundantWhiteSpace(scopeName),false,&funcSpec,QCString(),false);
6801
6802 // funcSpec contains the last template specifiers of the given scope.
6803 // If this method does not have any template arguments or they are
6804 // empty while funcSpec is not empty we assume this is a
6805 // specialization of a method. If not, we clear the funcSpec and treat
6806 // this as a normal method of a template class.
6807 if (!(root->tArgLists.size()>0 &&
6808 root->tArgLists.front().size()==0
6809 )
6810 )
6811 {
6812 funcSpec.clear();
6813 }
6814
6815 //namespaceName=removeAnonymousScopes(namespaceName);
6816 if (!Config_getBool(EXTRACT_ANON_NSPACES) && scopeName.find('@')!=-1) return; // skip stuff in anonymous namespace...
6817
6818 // split scope into a namespace and a class part
6819 extractNamespaceName(scopeName,className,namespaceName,TRUE);
6820 AUTO_TRACE_ADD("scopeName='{}' className='{}' namespaceName='{}'",scopeName,className,namespaceName);
6821
6822 //printf("namespaceName='%s' className='%s'\n",qPrint(namespaceName),qPrint(className));
6823 // merge class and namespace scopes again
6824 scopeName.clear();
6825 if (!namespaceName.isEmpty())
6826 {
6827 if (className.isEmpty())
6828 {
6829 scopeName=namespaceName;
6830 }
6831 else if (!relates.isEmpty() || // relates command with explicit scope
6832 !getClass(className)) // class name only exists in a namespace
6833 {
6834 scopeName=namespaceName+"::"+className;
6835 }
6836 else
6837 {
6838 scopeName=className;
6839 }
6840 }
6841 else if (!className.isEmpty())
6842 {
6843 scopeName=className;
6844 }
6845 //printf("new scope='%s'\n",qPrint(scopeName));
6846
6847 QCString tempScopeName=scopeName;
6848 ClassDefMutable *cd=getClassMutable(scopeName);
6849 if (cd)
6850 {
6851 if (funcSpec.isEmpty())
6852 {
6853 uint32_t argListIndex=0;
6854 tempScopeName=cd->qualifiedNameWithTemplateParameters(&root->tArgLists,&argListIndex);
6855 }
6856 else
6857 {
6858 tempScopeName=scopeName+funcSpec;
6859 }
6860 }
6861 //printf("scopeName=%s cd=%p root->tArgLists=%p result=%s\n",
6862 // qPrint(scopeName),cd,root->tArgLists,qPrint(tempScopeName));
6863
6864 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
6865 // rebuild the function declaration (needed to get the scope right).
6866 if (!scopeName.isEmpty() && !isRelated && !isFriend && !Config_getBool(HIDE_SCOPE_NAMES))
6867 {
6868 if (!funcType.isEmpty())
6869 {
6870 if (isFunc) // a function -> we use argList for the arguments
6871 {
6872 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcTempList;
6873 }
6874 else
6875 {
6876 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcArgs;
6877 }
6878 }
6879 else
6880 {
6881 if (isFunc) // a function => we use argList for the arguments
6882 {
6883 funcDecl=tempScopeName+"::"+funcName+funcTempList;
6884 }
6885 else // variable => add 'argument' list
6886 {
6887 funcDecl=tempScopeName+"::"+funcName+funcArgs;
6888 }
6889 }
6890 }
6891 else // build declaration without scope
6892 {
6893 if (!funcType.isEmpty()) // but with a type
6894 {
6895 if (isFunc) // function => omit argument list
6896 {
6897 funcDecl=funcType+" "+funcName+funcTempList;
6898 }
6899 else // variable => add 'argument' list
6900 {
6901 funcDecl=funcType+" "+funcName+funcArgs;
6902 }
6903 }
6904 else // no type
6905 {
6906 if (isFunc)
6907 {
6908 funcDecl=funcName+funcTempList;
6909 }
6910 else
6911 {
6912 funcDecl=funcName+funcArgs;
6913 }
6914 }
6915 }
6916
6917 if (funcType=="template class" && !funcTempList.isEmpty())
6918 return; // ignore explicit template instantiations
6919
6920 AUTO_TRACE_ADD("Parse results: namespaceName='{}' className=`{}` funcType='{}' funcSpec='{}' "
6921 " funcName='{}' funcArgs='{}' funcTempList='{}' funcDecl='{}' relates='{}'"
6922 " exceptions='{}' isRelated={} isMemberOf={} isFriend={} isFunc={}",
6923 namespaceName, className, funcType, funcSpec,
6924 funcName, funcArgs, funcTempList, funcDecl, relates,
6925 exceptions, isRelated, isMemberOf, isFriend, isFunc);
6926
6927 if (!funcName.isEmpty()) // function name is valid
6928 {
6929 // check if 'className' is actually a scoped enum, in which case we need to
6930 // process it as a global, see issue #6471
6931 bool strongEnum = false;
6932 MemberName *mn=nullptr;
6933 if (!className.isEmpty() && (mn=Doxygen::functionNameLinkedMap->find(className)))
6934 {
6935 for (const auto &imd : *mn)
6936 {
6937 MemberDefMutable *md = toMemberDefMutable(imd.get());
6938 Definition *mdScope = nullptr;
6939 if (md && md->isEnumerate() && md->isStrong() && (mdScope=md->getOuterScope()) &&
6940 // need filter for the correct scope, see issue #9668
6941 ((namespaceName.isEmpty() && mdScope==Doxygen::globalScope) || (mdScope->name()==namespaceName)))
6942 {
6943 AUTO_TRACE_ADD("'{}' is a strong enum! (namespace={} md->getOuterScope()->name()={})",md->name(),namespaceName,md->getOuterScope()->name());
6944 strongEnum = true;
6945 // pass the scope name name as a 'namespace' to the findGlobalMember function
6946 if (!namespaceName.isEmpty())
6947 {
6948 namespaceName+="::"+className;
6949 }
6950 else
6951 {
6952 namespaceName=className;
6953 }
6954 }
6955 }
6956 }
6957
6958 if (funcName.startsWith("operator ")) // strip class scope from cast operator
6959 {
6960 funcName = substitute(funcName,className+"::","");
6961 }
6962 mn = nullptr;
6963 if (!funcTempList.isEmpty()) // try with member specialization
6964 {
6965 mn=Doxygen::memberNameLinkedMap->find(funcName+funcTempList);
6966 }
6967 if (mn==nullptr) // try without specialization
6968 {
6969 mn=Doxygen::memberNameLinkedMap->find(funcName);
6970 }
6971 if (!isRelated && !strongEnum && mn) // function name already found
6972 {
6973 AUTO_TRACE_ADD("member name exists ({} members with this name)",mn->size());
6974 if (!className.isEmpty()) // class name is valid
6975 {
6976 if (funcSpec.isEmpty()) // not a member specialization
6977 {
6978 addMemberFunction(root,mn,scopeName,namespaceName,className,funcType,funcName,
6979 funcArgs,funcTempList,exceptions,
6980 type,args,isFriend,spec,relates,funcDecl,overloaded,isFunc);
6981 }
6982 else if (cd) // member specialization
6983 {
6984 addMemberSpecialization(root,mn,cd,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
6985 }
6986 else
6987 {
6988 //printf("*** Specialized member %s of unknown scope %s%s found!\n",
6989 // qPrint(scopeName),qPrint(funcName),qPrint(funcArgs));
6990 }
6991 }
6992 else if (overloaded) // check if the function belongs to only one class
6993 {
6994 addOverloaded(root,mn,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
6995 }
6996 else // unrelated function with the same name as a member
6997 {
6998 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
6999 {
7000 QCString fullFuncDecl=funcDecl;
7001 if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
7002 warn(root->fileName,root->startLine,
7003 "Cannot determine class for function\n{}",
7004 fullFuncDecl
7005 );
7006 }
7007 }
7008 }
7009 else if (isRelated && !relates.isEmpty())
7010 {
7011 AUTO_TRACE_ADD("related function scopeName='{}' className='{}'",scopeName,className);
7012 if (className.isEmpty()) className=relates;
7013 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
7014 if ((cd=getClassMutable(scopeName)))
7015 {
7016 bool newMember=TRUE; // assume we have a new member
7017 MemberDefMutable *mdDefine=nullptr;
7018 {
7019 mn = Doxygen::functionNameLinkedMap->find(funcName);
7020 if (mn)
7021 {
7022 for (const auto &imd : *mn)
7023 {
7024 MemberDefMutable *md = toMemberDefMutable(imd.get());
7025 if (md && md->isDefine())
7026 {
7027 mdDefine = md;
7028 break;
7029 }
7030 }
7031 }
7032 }
7033
7034 if (mdDefine) // macro definition is already created by the preprocessor and inserted as a file member
7035 {
7036 //printf("moving #define %s into class %s\n",qPrint(mdDefine->name()),qPrint(cd->name()));
7037
7038 // take mdDefine from the Doxygen::functionNameLinkedMap (without deleting the data)
7039 auto mdDefineTaken = Doxygen::functionNameLinkedMap->take(funcName,mdDefine);
7040 // insert it as a class member
7041 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==nullptr)
7042 {
7043 mn=Doxygen::memberNameLinkedMap->add(funcName);
7044 }
7045
7046 if (mdDefine->getFileDef())
7047 {
7048 mdDefine->getFileDef()->removeMember(mdDefine);
7049 }
7050 mdDefine->makeRelated();
7051 mdDefine->setMemberClass(cd);
7052 mdDefine->moveTo(cd);
7053 cd->insertMember(mdDefine);
7054 // also insert the member as an alias in the parent's scope, so it can be referenced also without cd's scope
7055 insertMemberAlias(cd->getOuterScope(),mdDefine);
7056 mn->push_back(std::move(mdDefineTaken));
7057 }
7058 else // normal member, needs to be created and added to the class
7059 {
7060 FileDef *fd=root->fileDef();
7061
7062 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==nullptr)
7063 {
7064 mn=Doxygen::memberNameLinkedMap->add(funcName);
7065 }
7066 else
7067 {
7068 // see if we got another member with matching arguments
7069 MemberDefMutable *rmd_found = nullptr;
7070 for (const auto &irmd : *mn)
7071 {
7072 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
7073 if (rmd)
7074 {
7075 const ArgumentList &rmdAl = rmd->argumentList();
7076
7077 newMember=
7078 className!=rmd->getOuterScope()->name() ||
7079 !matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),&rmdAl,
7080 cd,fd,&root->argList,
7081 TRUE,root->lang);
7082 if (!newMember)
7083 {
7084 rmd_found = rmd;
7085 }
7086 }
7087 }
7088 if (rmd_found) // member already exists as rmd -> add docs
7089 {
7090 AUTO_TRACE_ADD("addMemberDocs for related member {}",root->name);
7091 addMemberDocs(root,rmd_found,funcDecl,nullptr,overloaded,spec);
7092 newMember=false;
7093 }
7094 }
7095
7096 if (newMember) // need to create a new member
7097 {
7099 switch (root->mtype)
7100 {
7101 case MethodTypes::Method: mtype = MemberType::Function; break;
7102 case MethodTypes::Signal: mtype = MemberType::Signal; break;
7103 case MethodTypes::Slot: mtype = MemberType::Slot; break;
7104 case MethodTypes::DCOP: mtype = MemberType::DCOP; break;
7105 case MethodTypes::Property: mtype = MemberType::Property; break;
7106 case MethodTypes::Event: mtype = MemberType::Event; break;
7107 }
7108
7109 //printf("New related name '%s' '%d'\n",qPrint(funcName),
7110 // root->argList ? (int)root->argList->count() : -1);
7111
7112 // first note that we pass:
7113 // (root->tArgLists ? root->tArgLists->last() : nullptr)
7114 // for the template arguments for the new "member."
7115 // this accurately reflects the template arguments of
7116 // the related function, which don't have to do with
7117 // those of the related class.
7118 auto md = createMemberDef(
7119 root->fileName,root->startLine,root->startColumn,
7120 funcType,funcName,funcArgs,exceptions,
7121 root->protection,root->virt,
7122 root->isStatic,
7123 isMemberOf ? Relationship::Foreign : Relationship::Related,
7124 mtype,
7125 (!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList()),
7126 funcArgs.isEmpty() ? ArgumentList() : root->argList,
7127 root->metaData);
7128 auto mmd = toMemberDefMutable(md.get());
7129
7130 // also insert the member as an alias in the parent's scope, so it can be referenced also without cd's scope
7131 insertMemberAlias(cd->getOuterScope(),md.get());
7132
7133 // we still have the problem that
7134 // MemberDef::writeDocumentation() in memberdef.cpp
7135 // writes the template argument list for the class,
7136 // as if this member is a member of the class.
7137 // fortunately, MemberDef::writeDocumentation() has
7138 // a special mechanism that allows us to totally
7139 // override the set of template argument lists that
7140 // are printed. We use that and set it to the
7141 // template argument lists of the related function.
7142 //
7143 mmd->setDefinitionTemplateParameterLists(root->tArgLists);
7144
7145 mmd->setTagInfo(root->tagInfo());
7146
7147 //printf("Related member name='%s' decl='%s' bodyLine='%d'\n",
7148 // qPrint(funcName),qPrint(funcDecl),root->bodyLine);
7149
7150 // try to find the matching line number of the body from the
7151 // global function list
7152 bool found=FALSE;
7153 if (root->bodyLine==-1)
7154 {
7155 MemberName *rmn=Doxygen::functionNameLinkedMap->find(funcName);
7156 if (rmn)
7157 {
7158 const MemberDefMutable *rmd_found=nullptr;
7159 for (const auto &irmd : *rmn)
7160 {
7161 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
7162 if (rmd)
7163 {
7164 const ArgumentList &rmdAl = rmd->argumentList();
7165 // check for matching argument lists
7166 if (
7167 matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),&rmdAl,
7168 cd,fd,&root->argList,
7169 TRUE,root->lang)
7170 )
7171 {
7172 found=TRUE;
7173 rmd_found = rmd;
7174 break;
7175 }
7176 }
7177 }
7178 if (rmd_found) // member found -> copy line number info
7179 {
7180 mmd->setBodySegment(rmd_found->getDefLine(),rmd_found->getStartBodyLine(),rmd_found->getEndBodyLine());
7181 mmd->setBodyDef(rmd_found->getBodyDef());
7182 //md->setBodyMember(rmd);
7183 }
7184 }
7185 }
7186 if (!found) // line number could not be found or is available in this
7187 // entry
7188 {
7189 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7190 mmd->setBodyDef(fd);
7191 }
7192
7193 //if (root->mGrpId!=-1)
7194 //{
7195 // md->setMemberGroup(memberGroupDict[root->mGrpId]);
7196 //}
7197 mmd->setMemberClass(cd);
7198 mmd->setMemberSpecifiers(spec);
7199 mmd->setVhdlSpecifiers(root->vhdlSpec);
7200 mmd->setDefinition(funcDecl);
7202 mmd->addQualifiers(root->qualifiers);
7203 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7204 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7205 mmd->setDocsForDefinition(!root->proto);
7206 mmd->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
7207 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7208 mmd->addSectionsToDefinition(root->anchors);
7209 mmd->setMemberGroupId(root->mGrpId);
7210 mmd->setLanguage(root->lang);
7211 mmd->setId(root->id);
7212 //md->setMemberDefTemplateArguments(root->mtArgList);
7213 cd->insertMember(md.get());
7214 cd->insertUsedFile(fd);
7215 mmd->setRefItems(root->sli);
7216 if (root->relatesType==RelatesType::Duplicate) mmd->setRelatedAlso(cd);
7217 addMemberToGroups(root,md.get());
7219 //printf("Adding member=%s\n",qPrint(md->name()));
7220 mn->push_back(std::move(md));
7221 }
7222 if (root->relatesType==RelatesType::Duplicate)
7223 {
7224 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
7225 {
7226 QCString fullFuncDecl=funcDecl;
7227 if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
7228 warn(root->fileName,root->startLine,
7229 "Cannot determine file/namespace for relatedalso function\n{}",
7230 fullFuncDecl
7231 );
7232 }
7233 }
7234 }
7235 }
7236 else
7237 {
7238 warn_undoc(root->fileName,root->startLine, "class '{}' for related function '{}' is not documented.", className,funcName);
7239 }
7240 }
7241 else if (root->parent() && root->parent()->section.isObjcImpl())
7242 {
7243 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
7244 }
7245 else // unrelated not overloaded member found
7246 {
7247 bool globMem = findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec);
7248 if (className.isEmpty() && !globMem)
7249 {
7250 warn(root->fileName,root->startLine, "class for member '{}' cannot be found.", funcName);
7251 }
7252 else if (!className.isEmpty() && !globMem)
7253 {
7254 warn(root->fileName,root->startLine,
7255 "member '{}' of class '{}' cannot be found",
7256 funcName,className);
7257 }
7258 }
7259 }
7260 else
7261 {
7262 // this should not be called
7263 warn(root->fileName,root->startLine,"member with no name found.");
7264 }
7265 return;
7266}
7267
7268//----------------------------------------------------------------------
7269// find the members corresponding to the different documentation blocks
7270// that are extracted from the sources.
7271
7272static void filterMemberDocumentation(const Entry *root,const QCString &relates)
7273{
7274 AUTO_TRACE("root->type='{}' root->inside='{}' root->name='{}' root->args='{}' section={} root->spec={} root->mGrpId={}",
7275 root->type,root->inside,root->name,root->args,root->section,root->spec,root->mGrpId);
7276 //printf("root->parent()->name=%s\n",qPrint(root->parent()->name));
7277 bool isFunc=TRUE;
7278
7279 QCString type = root->type;
7280 QCString args = root->args;
7281 int i=-1, l=0;
7282 if ( // detect func variable/typedef to func ptr
7283 (i=findFunctionPtr(type.str(),root->lang,&l))!=-1
7284 )
7285 {
7286 //printf("Fixing function pointer!\n");
7287 // fix type and argument
7288 args.prepend(type.right(type.length()-i-l));
7289 type=type.left(i+l);
7290 //printf("Results type=%s,name=%s,args=%s\n",qPrint(type),qPrint(root->name),qPrint(args));
7291 isFunc=FALSE;
7292 }
7293 else if ((type.startsWith("typedef ") && args.find('(')!=-1))
7294 // detect function types marked as functions
7295 {
7296 isFunc=FALSE;
7297 }
7298
7299 //printf("Member %s isFunc=%d\n",qPrint(root->name),isFunc);
7300 if (root->section.isMemberDoc())
7301 {
7302 //printf("Documentation for inline member '%s' found args='%s'\n",
7303 // qPrint(root->name),qPrint(args));
7304 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
7305 if (type.isEmpty())
7306 {
7307 findMember(root,
7308 relates,
7309 type,
7310 args,
7311 root->name + args + root->exception,
7312 FALSE,
7313 isFunc);
7314 }
7315 else
7316 {
7317 findMember(root,
7318 relates,
7319 type,
7320 args,
7321 type + " " + root->name + args + root->exception,
7322 FALSE,
7323 isFunc);
7324 }
7325 }
7326 else if (root->section.isOverloadDoc())
7327 {
7328 //printf("Overloaded member %s found\n",qPrint(root->name));
7329 findMember(root,
7330 relates,
7331 type,
7332 args,
7333 root->name,
7334 TRUE,
7335 isFunc);
7336 }
7337 else if
7338 ((root->section.isFunction() // function
7339 ||
7340 (root->section.isVariable() && // variable
7341 !type.isEmpty() && // with a type
7342 g_compoundKeywords.find(type.str())==g_compoundKeywords.end() // that is not a keyword
7343 // (to skip forward declaration of class etc.)
7344 )
7345 )
7346 )
7347 {
7348 //printf("Documentation for member '%s' found args='%s' excp='%s'\n",
7349 // qPrint(root->name),qPrint(args),qPrint(root->exception));
7350 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
7351 //printf("Inside=%s\n Relates=%s\n",qPrint(root->inside),qPrint(relates));
7352 if (type=="friend class" || type=="friend struct" ||
7353 type=="friend union")
7354 {
7355 findMember(root,
7356 relates,
7357 type,
7358 args,
7359 type+" "+root->name,
7360 FALSE,FALSE);
7361
7362 }
7363 else if (!type.isEmpty())
7364 {
7365 findMember(root,
7366 relates,
7367 type,
7368 args,
7369 type+" "+ root->inside + root->name + args + root->exception,
7370 FALSE,isFunc);
7371 }
7372 else
7373 {
7374 findMember(root,
7375 relates,
7376 type,
7377 args,
7378 root->inside + root->name + args + root->exception,
7379 FALSE,isFunc);
7380 }
7381 }
7382 else if (root->section.isDefine() && !relates.isEmpty())
7383 {
7384 findMember(root,
7385 relates,
7386 type,
7387 args,
7388 root->name + args,
7389 FALSE,
7390 !args.isEmpty());
7391 }
7392 else if (root->section.isVariableDoc())
7393 {
7394 //printf("Documentation for variable %s found\n",qPrint(root->name));
7395 //if (!relates.isEmpty()) printf(" Relates %s\n",qPrint(relates));
7396 findMember(root,
7397 relates,
7398 type,
7399 args,
7400 root->name,
7401 FALSE,
7402 FALSE);
7403 }
7404 else if (root->section.isExportedInterface() ||
7405 root->section.isIncludedService())
7406 {
7407 findMember(root,
7408 relates,
7409 type,
7410 args,
7411 type + " " + root->name,
7412 FALSE,
7413 FALSE);
7414 }
7415 else
7416 {
7417 // skip section
7418 //printf("skip section\n");
7419 }
7420}
7421
7422static void findMemberDocumentation(const Entry *root)
7423{
7424 if (root->section.isMemberDoc() ||
7425 root->section.isOverloadDoc() ||
7426 root->section.isFunction() ||
7427 root->section.isVariable() ||
7428 root->section.isVariableDoc() ||
7429 root->section.isDefine() ||
7430 root->section.isIncludedService() ||
7431 root->section.isExportedInterface()
7432 )
7433 {
7434 AUTO_TRACE();
7435 if (root->relatesType==RelatesType::Duplicate && !root->relates.isEmpty())
7436 {
7438 }
7440 }
7441 for (const auto &e : root->children())
7442 {
7443 if (!e->section.isEnum())
7444 {
7445 findMemberDocumentation(e.get());
7446 }
7447 }
7448}
7449
7450//----------------------------------------------------------------------
7451
7452static void findObjCMethodDefinitions(const Entry *root)
7453{
7454 AUTO_TRACE();
7455 for (const auto &objCImpl : root->children())
7456 {
7457 if (objCImpl->section.isObjcImpl())
7458 {
7459 for (const auto &objCMethod : objCImpl->children())
7460 {
7461 if (objCMethod->section.isFunction())
7462 {
7463 //printf(" Found ObjC method definition %s\n",qPrint(objCMethod->name));
7464 findMember(objCMethod.get(),
7465 objCMethod->relates,
7466 objCMethod->type,
7467 objCMethod->args,
7468 objCMethod->type+" "+objCImpl->name+"::"+objCMethod->name+" "+objCMethod->args,
7469 FALSE,TRUE);
7470 objCMethod->section=EntryType::makeEmpty();
7471 }
7472 }
7473 }
7474 }
7475}
7476
7477//----------------------------------------------------------------------
7478// find and add the enumeration to their classes, namespaces or files
7479
7480static void findEnums(const Entry *root)
7481{
7482 if (root->section.isEnum())
7483 {
7484 AUTO_TRACE("name={}",root->name);
7485 ClassDefMutable *cd = nullptr;
7486 FileDef *fd = nullptr;
7487 NamespaceDefMutable *nd = nullptr;
7488 MemberNameLinkedMap *mnsd = nullptr;
7489 bool isGlobal = false;
7490 bool isRelated = false;
7491 bool isMemberOf = false;
7492 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7493
7494 QCString name;
7495 QCString scope;
7496
7497 int i = root->name.findRev("::");
7498 if (i!=-1) // scope is specified
7499 {
7500 scope=root->name.left(i); // extract scope
7501 if (root->lang==SrcLangExt::CSharp)
7502 {
7503 scope = mangleCSharpGenericName(scope);
7504 }
7505 name=root->name.right(root->name.length()-i-2); // extract name
7506 if ((cd=getClassMutable(scope))==nullptr)
7507 {
7509 }
7510 }
7511 else // no scope, check the scope in which the docs where found
7512 {
7513 if (root->parent()->section.isScope() && !root->parent()->name.isEmpty()) // found enum docs inside a compound
7514 {
7515 scope=root->parent()->name;
7516 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7517 }
7518 name=root->name;
7519 }
7520
7521 if (!root->relates.isEmpty())
7522 { // related member, prefix user specified scope
7523 isRelated=TRUE;
7524 isMemberOf=(root->relatesType==RelatesType::MemberOf);
7525 if (getClass(root->relates)==nullptr && !scope.isEmpty())
7526 scope=mergeScopes(scope,root->relates);
7527 else
7528 scope=root->relates;
7529 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7530 }
7531
7532 if (cd && !name.isEmpty()) // found a enum inside a compound
7533 {
7534 //printf("Enum '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7535 fd=nullptr;
7537 isGlobal=false;
7538 }
7539 else if (nd) // found enum inside namespace
7540 {
7542 isGlobal=true;
7543 }
7544 else // found a global enum
7545 {
7546 fd=root->fileDef();
7548 isGlobal=true;
7549 }
7550
7551 if (!name.isEmpty())
7552 {
7553 // new enum type
7554 AUTO_TRACE_ADD("new enum {} at line {} of {}",name,root->bodyLine,root->fileName);
7555 auto md = createMemberDef(
7556 root->fileName,root->startLine,root->startColumn,
7557 QCString(),name,QCString(),QCString(),
7558 root->protection,Specifier::Normal,FALSE,
7559 isMemberOf ? Relationship::Foreign : isRelated ? Relationship::Related : Relationship::Member,
7562 auto mmd = toMemberDefMutable(md.get());
7563 mmd->setTagInfo(root->tagInfo());
7564 mmd->setLanguage(root->lang);
7565 mmd->setId(root->id);
7566 if (!isGlobal) mmd->setMemberClass(cd); else mmd->setFileDef(fd);
7567 mmd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7568 mmd->setBodyDef(root->fileDef());
7569 mmd->setMemberSpecifiers(root->spec);
7570 mmd->setVhdlSpecifiers(root->vhdlSpec);
7571 mmd->setEnumBaseType(root->args);
7572 //printf("Enum %s definition at line %d of %s: protection=%d scope=%s\n",
7573 // qPrint(root->name),root->bodyLine,qPrint(root->fileName),root->protection,cd?qPrint(cd->name()):"<none>");
7574 mmd->addSectionsToDefinition(root->anchors);
7575 mmd->setMemberGroupId(root->mGrpId);
7577 mmd->addQualifiers(root->qualifiers);
7578 //printf("%s::setRefItems(%zu)\n",qPrint(md->name()),root->sli.size());
7579 mmd->setRefItems(root->sli);
7580 //printf("found enum %s nd=%p\n",qPrint(md->name()),nd);
7581 bool defSet=FALSE;
7582
7583 QCString baseType = root->args;
7584 if (!baseType.isEmpty())
7585 {
7586 baseType.prepend(" : ");
7587 }
7588
7589 if (nd)
7590 {
7591 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES))
7592 {
7593 mmd->setDefinition(name+baseType);
7594 }
7595 else
7596 {
7597 mmd->setDefinition(nd->name()+"::"+name+baseType);
7598 }
7599 //printf("definition=%s\n",md->definition());
7600 defSet=TRUE;
7601 mmd->setNamespace(nd);
7602 nd->insertMember(md.get());
7603 }
7604
7605 // even if we have already added the enum to a namespace, we still
7606 // also want to add it to other appropriate places such as file
7607 // or class.
7608 if (isGlobal && (nd==nullptr || !nd->isAnonymous()))
7609 {
7610 if (!defSet) mmd->setDefinition(name+baseType);
7611 if (fd==nullptr && root->parent())
7612 {
7613 fd=root->parent()->fileDef();
7614 }
7615 if (fd)
7616 {
7617 mmd->setFileDef(fd);
7618 fd->insertMember(md.get());
7619 }
7620 }
7621 else if (cd)
7622 {
7623 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES))
7624 {
7625 mmd->setDefinition(name+baseType);
7626 }
7627 else
7628 {
7629 mmd->setDefinition(cd->name()+"::"+name+baseType);
7630 }
7631 cd->insertMember(md.get());
7632 cd->insertUsedFile(fd);
7633 }
7634 mmd->setDocumentation(root->doc,root->docFile,root->docLine);
7635 mmd->setDocsForDefinition(!root->proto);
7636 mmd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7637 mmd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7638
7639 //printf("Adding member=%s\n",qPrint(md->name()));
7640 addMemberToGroups(root,md.get());
7642
7643 MemberName *mn = mnsd->add(name);
7644 mn->push_back(std::move(md));
7645 }
7646 }
7647 else
7648 {
7649 for (const auto &e : root->children()) findEnums(e.get());
7650 }
7651}
7652
7653//----------------------------------------------------------------------
7654
7655static void addEnumValuesToEnums(const Entry *root)
7656{
7657 if (root->section.isEnum())
7658 // non anonymous enumeration
7659 {
7660 AUTO_TRACE("name={}",root->name);
7661 ClassDefMutable *cd = nullptr;
7662 FileDef *fd = nullptr;
7663 NamespaceDefMutable *nd = nullptr;
7664 MemberNameLinkedMap *mnsd = nullptr;
7665 bool isGlobal = false;
7666 bool isRelated = false;
7667 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7668
7669 QCString name;
7670 QCString scope;
7671
7672 int i = root->name.findRev("::");
7673 if (i!=-1) // scope is specified
7674 {
7675 scope=root->name.left(i); // extract scope
7676 if (root->lang==SrcLangExt::CSharp)
7677 {
7678 scope = mangleCSharpGenericName(scope);
7679 }
7680 name=root->name.right(root->name.length()-i-2); // extract name
7681 if ((cd=getClassMutable(scope))==nullptr)
7682 {
7684 }
7685 }
7686 else // no scope, check the scope in which the docs where found
7687 {
7688 if (root->parent()->section.isScope() && !root->parent()->name.isEmpty()) // found enum docs inside a compound
7689 {
7690 scope=root->parent()->name;
7691 if (root->lang==SrcLangExt::CSharp)
7692 {
7693 scope = mangleCSharpGenericName(scope);
7694 }
7695 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7696 }
7697 name=root->name;
7698 }
7699
7700 if (!root->relates.isEmpty())
7701 { // related member, prefix user specified scope
7702 isRelated=TRUE;
7703 if (getClassMutable(root->relates)==nullptr && !scope.isEmpty())
7704 scope=mergeScopes(scope,root->relates);
7705 else
7706 scope=root->relates;
7707 if ((cd=getClassMutable(scope))==nullptr) nd=getResolvedNamespaceMutable(scope);
7708 }
7709
7710 if (cd && !name.isEmpty()) // found a enum inside a compound
7711 {
7712 //printf("Enum in class '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7713 fd=nullptr;
7715 isGlobal=false;
7716 }
7717 else if (nd && !nd->isAnonymous()) // found enum inside namespace
7718 {
7719 //printf("Enum in namespace '%s'::'%s'\n",qPrint(nd->name()),qPrint(name));
7721 isGlobal=true;
7722 }
7723 else // found a global enum
7724 {
7725 fd=root->fileDef();
7726 //printf("Enum in file '%s': '%s'\n",qPrint(fd->name()),qPrint(name));
7728 isGlobal=true;
7729 }
7730
7731 if (!name.isEmpty())
7732 {
7733 //printf("** name=%s\n",qPrint(name));
7734 MemberName *mn = mnsd->find(name); // for all members with this name
7735 if (mn)
7736 {
7737 struct EnumValueInfo
7738 {
7739 EnumValueInfo(const QCString &n,std::unique_ptr<MemberDef> &&md) :
7740 name(n), member(std::move(md)) {}
7741 QCString name;
7742 std::unique_ptr<MemberDef> member;
7743 };
7744 std::vector< EnumValueInfo > extraMembers;
7745 // for each enum in this list
7746 for (const auto &imd : *mn)
7747 {
7748 MemberDefMutable *md = toMemberDefMutable(imd.get());
7749 // use raw pointer in this loop, since we modify mn and can then invalidate mdp.
7750 if (md && md->isEnumerate() && !root->children().empty())
7751 {
7752 AUTO_TRACE_ADD("enum {} with {} children",md->name(),root->children().size());
7753 for (const auto &e : root->children())
7754 {
7755 SrcLangExt sle = root->lang;
7756 bool isJavaLike = sle==SrcLangExt::CSharp || sle==SrcLangExt::Java || sle==SrcLangExt::XML;
7757 if ( isJavaLike || root->spec.isStrong())
7758 {
7759 if (sle == SrcLangExt::Cpp && e->section.isDefine()) continue;
7760 // Unlike classic C/C++ enums, for C++11, C# & Java enum
7761 // values are only visible inside the enum scope, so we must create
7762 // them here and only add them to the enum
7763 //printf("md->qualifiedName()=%s e->name=%s tagInfo=%p name=%s\n",
7764 // qPrint(md->qualifiedName()),qPrint(e->name),(void*)e->tagInfo(),qPrint(e->name));
7765 QCString qualifiedName = root->name;
7766 i = qualifiedName.findRev("::");
7767 if (i!=-1 && sle==SrcLangExt::CSharp)
7768 {
7769 qualifiedName = mangleCSharpGenericName(qualifiedName.left(i))+qualifiedName.mid(i);
7770 }
7771 if (isJavaLike)
7772 {
7773 qualifiedName=substitute(qualifiedName,"::",".");
7774 }
7775 if (md->qualifiedName()==qualifiedName) // enum value scope matches that of the enum
7776 {
7777 QCString fileName = e->fileName;
7778 if (fileName.isEmpty() && e->tagInfo())
7779 {
7780 fileName = e->tagInfo()->tagName;
7781 }
7782 AUTO_TRACE_ADD("strong enum value {}",e->name);
7783 auto fmd = createMemberDef(
7784 fileName,e->startLine,e->startColumn,
7785 e->type,e->name,e->args,QCString(),
7786 e->protection, Specifier::Normal,e->isStatic,Relationship::Member,
7788 auto fmmd = toMemberDefMutable(fmd.get());
7789 NamespaceDef *mnd = md->getNamespaceDef();
7790 if (md->getClassDef())
7791 fmmd->setMemberClass(md->getClassDef());
7792 else if (mnd && (mnd->isLinkable() || mnd->isAnonymous()))
7793 fmmd->setNamespace(mnd);
7794 else if (md->getFileDef())
7795 fmmd->setFileDef(md->getFileDef());
7796 fmmd->setOuterScope(md->getOuterScope());
7797 fmmd->setTagInfo(e->tagInfo());
7798 fmmd->setLanguage(e->lang);
7799 fmmd->setBodySegment(e->startLine,e->bodyLine,e->endBodyLine);
7800 fmmd->setBodyDef(e->fileDef());
7801 fmmd->setId(e->id);
7802 fmmd->setDocumentation(e->doc,e->docFile,e->docLine);
7803 fmmd->setBriefDescription(e->brief,e->briefFile,e->briefLine);
7804 fmmd->addSectionsToDefinition(e->anchors);
7805 fmmd->setInitializer(e->initializer.str());
7806 fmmd->setMaxInitLines(e->initLines);
7807 fmmd->setMemberGroupId(e->mGrpId);
7808 fmmd->setExplicitExternal(e->explicitExternal,fileName,e->startLine,e->startColumn);
7809 fmmd->setRefItems(e->sli);
7810 fmmd->setAnchor();
7811 md->insertEnumField(fmd.get());
7812 fmmd->setEnumScope(md,TRUE);
7813 extraMembers.emplace_back(e->name,std::move(fmd));
7814 }
7815 }
7816 else
7817 {
7818 AUTO_TRACE_ADD("enum value {}",e->name);
7819 //printf("e->name=%s isRelated=%d\n",qPrint(e->name),isRelated);
7820 MemberName *fmn=nullptr;
7821 MemberNameLinkedMap *emnsd = isRelated ? Doxygen::functionNameLinkedMap : mnsd;
7822 if (!e->name.isEmpty() && (fmn=emnsd->find(e->name)))
7823 // get list of members with the same name as the field
7824 {
7825 for (const auto &ifmd : *fmn)
7826 {
7827 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
7828 if (fmd && fmd->isEnumValue() && fmd->getOuterScope()==md->getOuterScope()) // in same scope
7829 {
7830 //printf("found enum value with same name %s in scope %s\n",
7831 // qPrint(fmd->name()),qPrint(fmd->getOuterScope()->name()));
7832 if (nd && !nd->isAnonymous())
7833 {
7834 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
7835 {
7836 const NamespaceDef *fnd=fmd->getNamespaceDef();
7837 if (fnd==nd) // enum value is inside a namespace
7838 {
7839 md->insertEnumField(fmd);
7840 fmd->setEnumScope(md);
7841 }
7842 }
7843 }
7844 else if (isGlobal)
7845 {
7846 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
7847 {
7848 const FileDef *ffd=fmd->getFileDef();
7849 if (ffd==fd && ffd==md->getFileDef()) // enum value has file scope
7850 {
7851 md->insertEnumField(fmd);
7852 fmd->setEnumScope(md);
7853 }
7854 }
7855 }
7856 else if (isRelated && cd) // reparent enum value to
7857 // match the enum's scope
7858 {
7859 md->insertEnumField(fmd); // add field def to list
7860 fmd->setEnumScope(md); // cross ref with enum name
7861 fmd->setEnumClassScope(cd); // cross ref with enum name
7862 fmd->setOuterScope(cd);
7863 fmd->makeRelated();
7864 cd->insertMember(fmd);
7865 }
7866 else
7867 {
7868 if (!fmd->isStrongEnumValue()) // only non strong enum values can be globally added
7869 {
7870 const ClassDef *fcd=fmd->getClassDef();
7871 if (fcd==cd) // enum value is inside a class
7872 {
7873 //printf("Inserting enum field %s in enum scope %s\n",
7874 // qPrint(fmd->name()),qPrint(md->name()));
7875 md->insertEnumField(fmd); // add field def to list
7876 fmd->setEnumScope(md); // cross ref with enum name
7877 }
7878 }
7879 }
7880 }
7881 }
7882 }
7883 }
7884 }
7885 }
7886 }
7887 // move the newly added members into mn
7888 for (auto &e : extraMembers)
7889 {
7890 MemberName *emn=mnsd->add(e.name);
7891 emn->push_back(std::move(e.member));
7892 }
7893 }
7894 }
7895 }
7896 else
7897 {
7898 for (const auto &e : root->children()) addEnumValuesToEnums(e.get());
7899 }
7900}
7901
7902//----------------------------------------------------------------------
7903
7904static void addEnumDocs(const Entry *root,MemberDefMutable *md)
7905{
7906 AUTO_TRACE();
7907 // documentation outside a compound overrides the documentation inside it
7908 {
7909 md->setDocumentation(root->doc,root->docFile,root->docLine);
7910 md->setDocsForDefinition(!root->proto);
7911 }
7912
7913 // brief descriptions inside a compound override the documentation
7914 // outside it
7915 {
7916 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7917 }
7918
7919 if (md->inbodyDocumentation().isEmpty() || !root->parent()->name.isEmpty())
7920 {
7922 }
7923
7924 if (root->mGrpId!=-1 && md->getMemberGroupId()==-1)
7925 {
7926 md->setMemberGroupId(root->mGrpId);
7927 }
7928
7930 md->setRefItems(root->sli);
7931
7932 const GroupDef *gd=md->getGroupDef();
7933 if (gd==nullptr && !root->groups.empty()) // member not grouped but out-of-line documentation is
7934 {
7935 addMemberToGroups(root,md);
7936 }
7938}
7939
7940//----------------------------------------------------------------------
7941// Search for the name in the associated groups. If a matching member
7942// definition exists, then add the documentation to it and return TRUE,
7943// otherwise FALSE.
7944
7945static bool tryAddEnumDocsToGroupMember(const Entry *root,const QCString &name)
7946{
7947 for (const auto &g : root->groups)
7948 {
7949 const GroupDef *gd = Doxygen::groupLinkedMap->find(g.groupname);
7950 if (gd)
7951 {
7952 MemberList *ml = gd->getMemberList(MemberListType::DecEnumMembers());
7953 if (ml)
7954 {
7955 MemberDefMutable *md = toMemberDefMutable(ml->find(name));
7956 if (md)
7957 {
7958 addEnumDocs(root,md);
7959 return TRUE;
7960 }
7961 }
7962 }
7963 else if (!gd && g.pri == Grouping::GROUPING_INGROUP)
7964 {
7965 warn(root->fileName, root->startLine,
7966 "Found non-existing group '{}' for the command '{}', ignoring command",
7967 g.groupname, Grouping::getGroupPriName( g.pri )
7968 );
7969 }
7970 }
7971
7972 return FALSE;
7973}
7974
7975//----------------------------------------------------------------------
7976// find the documentation blocks for the enumerations
7977
7978static void findEnumDocumentation(const Entry *root)
7979{
7980 if (root->section.isEnumDoc() &&
7981 !root->name.isEmpty() &&
7982 root->name.at(0)!='@' // skip anonymous enums
7983 )
7984 {
7985 QCString name;
7986 QCString scope;
7987 int i = root->name.findRev("::");
7988 if (i!=-1) // scope is specified as part of the name
7989 {
7990 name=root->name.right(root->name.length()-i-2); // extract name
7991 scope=root->name.left(i); // extract scope
7992 //printf("Scope='%s' Name='%s'\n",qPrint(scope),qPrint(name));
7993 }
7994 else // just the name
7995 {
7996 name=root->name;
7997 }
7998 if (root->parent()->section.isScope() && !root->parent()->name.isEmpty()) // found enum docs inside a compound
7999 {
8000 if (!scope.isEmpty()) scope.prepend("::");
8001 scope.prepend(root->parent()->name);
8002 }
8003 const ClassDef *cd = getClass(scope);
8004 const NamespaceDef *nd=Doxygen::namespaceLinkedMap->find(scope);
8005 const FileDef *fd = root->fileDef();
8006 AUTO_TRACE("Found docs for enum with name '{}' and scope '{}' in context '{}' cd='{}', nd='{}' fd='{}'",
8007 name,scope,root->parent()->name,
8008 cd ? cd->name() : QCString("<none>"),
8009 nd ? nd->name() : QCString("<none>"),
8010 fd ? fd->name() : QCString("<none>"));
8011
8012 if (!name.isEmpty())
8013 {
8014 bool found = tryAddEnumDocsToGroupMember(root, name);
8015 if (!found)
8016 {
8017 MemberName *mn = cd ? Doxygen::memberNameLinkedMap->find(name) : Doxygen::functionNameLinkedMap->find(name);
8018 if (mn)
8019 {
8020 for (const auto &imd : *mn)
8021 {
8022 MemberDefMutable *md = toMemberDefMutable(imd.get());
8023 if (md && md->isEnumerate())
8024 {
8025 const ClassDef *mcd = md->getClassDef();
8026 const NamespaceDef *mnd = md->getNamespaceDef();
8027 const FileDef *mfd = md->getFileDef();
8028 if (cd && mcd==cd)
8029 {
8030 AUTO_TRACE_ADD("Match found for class scope");
8031 addEnumDocs(root,md);
8032 found = TRUE;
8033 break;
8034 }
8035 else if (cd==nullptr && mcd==nullptr && nd!=nullptr && mnd==nd)
8036 {
8037 AUTO_TRACE_ADD("Match found for namespace scope");
8038 addEnumDocs(root,md);
8039 found = TRUE;
8040 break;
8041 }
8042 else if (cd==nullptr && nd==nullptr && mcd==nullptr && mnd==nullptr && fd==mfd)
8043 {
8044 AUTO_TRACE_ADD("Match found for global scope");
8045 addEnumDocs(root,md);
8046 found = TRUE;
8047 break;
8048 }
8049 }
8050 }
8051 }
8052 }
8053 if (!found)
8054 {
8055 warn(root->fileName,root->startLine, "Documentation for undefined enum '{}' found.", name);
8056 }
8057 }
8058 }
8059 for (const auto &e : root->children()) findEnumDocumentation(e.get());
8060}
8061
8062// search for each enum (member or function) in mnl if it has documented
8063// enum values.
8064static void findDEV(const MemberNameLinkedMap &mnsd)
8065{
8066 // for each member name
8067 for (const auto &mn : mnsd)
8068 {
8069 // for each member definition
8070 for (const auto &imd : *mn)
8071 {
8072 MemberDefMutable *md = toMemberDefMutable(imd.get());
8073 if (md && md->isEnumerate()) // member is an enum
8074 {
8075 int documentedEnumValues=0;
8076 // for each enum value
8077 for (const auto &fmd : md->enumFieldList())
8078 {
8079 if (fmd->isLinkableInProject()) documentedEnumValues++;
8080 }
8081 // at least one enum value is documented
8082 if (documentedEnumValues>0) md->setDocumentedEnumValues(TRUE);
8083 }
8084 }
8085 }
8086}
8087
8088// search for each enum (member or function) if it has documented enum
8089// values.
8095
8096//----------------------------------------------------------------------
8097
8099{
8100 auto &index = Index::instance();
8101 // for each class member name
8102 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8103 {
8104 // for each member definition
8105 for (const auto &md : *mn)
8106 {
8107 index.addClassMemberNameToIndex(md.get());
8108 if (md->getModuleDef())
8109 {
8110 index.addModuleMemberNameToIndex(md.get());
8111 }
8112 }
8113 }
8114 // for each file/namespace function name
8115 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8116 {
8117 // for each member definition
8118 for (const auto &md : *mn)
8119 {
8120 if (md->getNamespaceDef())
8121 {
8122 index.addNamespaceMemberNameToIndex(md.get());
8123 }
8124 else
8125 {
8126 index.addFileMemberNameToIndex(md.get());
8127 }
8128 if (md->getModuleDef())
8129 {
8130 index.addModuleMemberNameToIndex(md.get());
8131 }
8132 }
8133 }
8134
8135 index.sortMemberIndexLists();
8136}
8137
8138//----------------------------------------------------------------------
8139
8140static void addToIndices()
8141{
8142 for (const auto &cd : *Doxygen::classLinkedMap)
8143 {
8144 if (cd->isLinkableInProject())
8145 {
8146 Doxygen::indexList->addIndexItem(cd.get(),nullptr);
8147 if (Doxygen::searchIndex.enabled())
8148 {
8149 Doxygen::searchIndex.setCurrentDoc(cd.get(),cd->anchor(),FALSE);
8150 Doxygen::searchIndex.addWord(cd->localName(),TRUE);
8151 }
8152 }
8153 }
8154
8155 for (const auto &cd : *Doxygen::conceptLinkedMap)
8156 {
8157 if (cd->isLinkableInProject())
8158 {
8159 Doxygen::indexList->addIndexItem(cd.get(),nullptr);
8160 if (Doxygen::searchIndex.enabled())
8161 {
8162 Doxygen::searchIndex.setCurrentDoc(cd.get(),cd->anchor(),FALSE);
8163 Doxygen::searchIndex.addWord(cd->localName(),TRUE);
8164 }
8165 }
8166 }
8167
8168 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8169 {
8170 if (nd->isLinkableInProject())
8171 {
8172 Doxygen::indexList->addIndexItem(nd.get(),nullptr);
8173 if (Doxygen::searchIndex.enabled())
8174 {
8175 Doxygen::searchIndex.setCurrentDoc(nd.get(),nd->anchor(),FALSE);
8176 Doxygen::searchIndex.addWord(nd->localName(),TRUE);
8177 }
8178 }
8179 }
8180
8181 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8182 {
8183 for (const auto &fd : *fn)
8184 {
8185 if (Doxygen::searchIndex.enabled() && fd->isLinkableInProject())
8186 {
8187 Doxygen::searchIndex.setCurrentDoc(fd.get(),fd->anchor(),FALSE);
8188 Doxygen::searchIndex.addWord(fd->localName(),TRUE);
8189 }
8190 }
8191 }
8192
8193 auto addWordsForTitle = [](const Definition *d,const QCString &anchor,const QCString &title)
8194 {
8195 Doxygen::indexList->addIndexItem(d,nullptr,QCString(),filterTitle(title));
8196 if (Doxygen::searchIndex.enabled())
8197 {
8198 Doxygen::searchIndex.setCurrentDoc(d,anchor,false);
8199 std::string s = title.str();
8200 static const reg::Ex re(R"(\a[\w-]*)");
8201 reg::Iterator it(s,re);
8203 for (; it!=end ; ++it)
8204 {
8205 const auto &match = *it;
8206 std::string matchStr = match.str();
8207 Doxygen::searchIndex.addWord(matchStr,true);
8208 }
8209 }
8210 };
8211
8212 for (const auto &gd : *Doxygen::groupLinkedMap)
8213 {
8214 if (gd->isLinkableInProject())
8215 {
8216 addWordsForTitle(gd.get(),gd->anchor(),gd->groupTitle());
8217 }
8218 }
8219
8220 for (const auto &pd : *Doxygen::pageLinkedMap)
8221 {
8222 if (pd->isLinkableInProject())
8223 {
8224 addWordsForTitle(pd.get(),pd->anchor(),pd->title());
8225 }
8226 }
8227
8229 {
8230 addWordsForTitle(Doxygen::mainPage.get(),Doxygen::mainPage->anchor(),Doxygen::mainPage->title());
8231 }
8232
8233 auto addMemberToSearchIndex = [](const MemberDef *md)
8234 {
8235 if (Doxygen::searchIndex.enabled())
8236 {
8237 Doxygen::searchIndex.setCurrentDoc(md,md->anchor(),FALSE);
8238 QCString ln=md->localName();
8239 QCString qn=md->qualifiedName();
8240 Doxygen::searchIndex.addWord(ln,TRUE);
8241 if (ln!=qn)
8242 {
8243 Doxygen::searchIndex.addWord(qn,TRUE);
8244 if (md->getClassDef())
8245 {
8246 Doxygen::searchIndex.addWord(md->getClassDef()->displayName(),TRUE);
8247 }
8248 if (md->getNamespaceDef())
8249 {
8250 Doxygen::searchIndex.addWord(md->getNamespaceDef()->displayName(),TRUE);
8251 }
8252 }
8253 }
8254 };
8255
8256 auto getScope = [](const MemberDef *md)
8257 {
8258 const Definition *scope = nullptr;
8259 if (md->getGroupDef()) scope = md->getGroupDef();
8260 else if (md->getClassDef()) scope = md->getClassDef();
8261 else if (md->getNamespaceDef()) scope = md->getNamespaceDef();
8262 else if (md->getFileDef()) scope = md->getFileDef();
8263 return scope;
8264 };
8265
8266 auto addMemberToIndices = [addMemberToSearchIndex,getScope](const MemberDef *md)
8267 {
8268 if (md->isLinkableInProject())
8269 {
8270 if (!(md->isEnumerate() && md->isAnonymous()))
8271 {
8272 Doxygen::indexList->addIndexItem(getScope(md),md);
8274 }
8275 if (md->isEnumerate())
8276 {
8277 for (const auto &fmd : md->enumFieldList())
8278 {
8279 Doxygen::indexList->addIndexItem(getScope(fmd),fmd);
8281 }
8282 }
8283 }
8284 };
8285
8286 // for each class member name
8287 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8288 {
8289 // for each member definition
8290 for (const auto &md : *mn)
8291 {
8292 addMemberToIndices(md.get());
8293 }
8294 }
8295 // for each file/namespace function name
8296 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8297 {
8298 // for each member definition
8299 for (const auto &md : *mn)
8300 {
8301 addMemberToIndices(md.get());
8302 }
8303 }
8304}
8305
8306//----------------------------------------------------------------------
8307
8309{
8310 // for each member name
8311 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8312 {
8313 // for each member definition
8314 for (const auto &imd : *mn)
8315 {
8316 MemberDefMutable *md = toMemberDefMutable(imd.get());
8317 if (md)
8318 {
8320 }
8321 }
8322 }
8323 // for each member name
8324 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8325 {
8326 // for each member definition
8327 for (const auto &imd : *mn)
8328 {
8329 MemberDefMutable *md = toMemberDefMutable(imd.get());
8330 if (md)
8331 {
8333 }
8334 }
8335 }
8336}
8337
8338// recursive helper function looking for reimplements/implemented
8339// by relations between class cd and direct or indirect base class bcd
8341{
8342 for (const auto &mn : cd->memberNameInfoLinkedMap()) // for each member in class cd with a unique name
8343 {
8344 for (const auto &imd : *mn) // for each member with a given name
8345 {
8346 MemberDefMutable *md = toMemberDefMutable(imd->memberDef());
8347 if (md && (md->isFunction() || md->isCSharpProperty())) // filter on reimplementable members
8348 {
8349 ClassDef *mbcd = bcd->classDef;
8350 if (mbcd && mbcd->isLinkable()) // filter on linkable classes
8351 {
8352 const auto &bmn = mbcd->memberNameInfoLinkedMap();
8353 const auto &bmni = bmn.find(mn->memberName());
8354 if (bmni) // there are base class members with the same name
8355 {
8356 for (const auto &ibmd : *bmni) // for base class member with that name
8357 {
8358 MemberDefMutable *bmd = toMemberDefMutable(ibmd->memberDef());
8359 if (bmd) // not part of an inline namespace
8360 {
8361 auto lang = bmd->getLanguage();
8362 auto compType = mbcd->compoundType();
8363 if (bmd->virtualness()!=Specifier::Normal ||
8364 lang==SrcLangExt::Python ||
8365 lang==SrcLangExt::Java ||
8366 lang==SrcLangExt::PHP ||
8367 compType==ClassDef::Interface ||
8368 compType==ClassDef::Protocol)
8369 {
8370 const ArgumentList &bmdAl = bmd->argumentList();
8371 const ArgumentList &mdAl = md->argumentList();
8372 //printf(" Base argList='%s'\n Super argList='%s'\n",
8373 // qPrint(argListToString(bmdAl)),
8374 // qPrint(argListToString(mdAl))
8375 // );
8376 if (
8377 lang==SrcLangExt::Python ||
8378 matchArguments2(bmd->getOuterScope(),bmd->getFileDef(),&bmdAl,
8379 md->getOuterScope(), md->getFileDef(), &mdAl,
8380 TRUE,lang
8381 )
8382 )
8383 {
8384 if (lang==SrcLangExt::Python && md->name().startsWith("__")) continue; // private members do not reimplement
8385 //printf("match!\n");
8386 const MemberDef *rmd = md->reimplements();
8387 if (rmd==nullptr) // not already assigned
8388 {
8389 //printf("%s: setting (new) reimplements member %s\n",qPrint(md->qualifiedName()),qPrint(bmd->qualifiedName()));
8390 md->setReimplements(bmd);
8391 }
8392 //printf("%s: add reimplementedBy member %s\n",qPrint(bmd->qualifiedName()),qPrint(md->qualifiedName()));
8393 bmd->insertReimplementedBy(md);
8394 }
8395 else
8396 {
8397 //printf("no match!\n");
8398 }
8399 }
8400 }
8401 }
8402 }
8403 }
8404 }
8405 }
8406 }
8407
8408 // do also for indirect base classes
8409 for (const auto &bbcd : bcd->classDef->baseClasses())
8410 {
8412 }
8413}
8414
8415//----------------------------------------------------------------------
8416// computes the relation between all members. For each member 'm'
8417// the members that override the implementation of 'm' are searched and
8418// the member that 'm' overrides is searched.
8419
8421{
8422 for (const auto &cd : *Doxygen::classLinkedMap)
8423 {
8424 if (cd->isLinkable())
8425 {
8426 for (const auto &bcd : cd->baseClasses())
8427 {
8429 }
8430 }
8431 }
8432}
8433
8434//----------------------------------------------------------------------------
8435
8437{
8438 // for each class
8439 for (const auto &cd : *Doxygen::classLinkedMap)
8440 {
8441 // that is a template
8442 for (const auto &ti : cd->getTemplateInstances())
8443 {
8444 ClassDefMutable *tcdm = toClassDefMutable(ti.classDef);
8445 if (tcdm)
8446 {
8447 tcdm->addMembersToTemplateInstance(cd.get(),cd->templateArguments(),ti.templSpec);
8448 }
8449 }
8450 }
8451}
8452
8453//----------------------------------------------------------------------------
8454
8455static void mergeCategories()
8456{
8457 AUTO_TRACE();
8458 // merge members of categories into the class they extend
8459 for (const auto &cd : *Doxygen::classLinkedMap)
8460 {
8461 int i=cd->name().find('(');
8462 if (i!=-1) // it is an Objective-C category
8463 {
8464 QCString baseName=cd->name().left(i);
8465 ClassDefMutable *baseClass=toClassDefMutable(Doxygen::classLinkedMap->find(baseName));
8466 if (baseClass)
8467 {
8468 AUTO_TRACE_ADD("merging members of category {} into {}",cd->name(),baseClass->name());
8469 baseClass->mergeCategory(cd.get());
8470 }
8471 }
8472 }
8473}
8474
8475// builds the list of all members for each class
8476
8478{
8479 // merge the member list of base classes into the inherited classes.
8480 for (const auto &cd : *Doxygen::classLinkedMap)
8481 {
8482 if (// !cd->isReference() && // not an external class
8483 cd->subClasses().empty() && // is a root of the hierarchy
8484 !cd->baseClasses().empty()) // and has at least one base class
8485 {
8486 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8487 if (cdm)
8488 {
8489 //printf("*** merging members for %s\n",qPrint(cd->name()));
8490 cdm->mergeMembers();
8491 }
8492 }
8493 }
8494 // now sort the member list of all members for all classes.
8495 for (const auto &cd : *Doxygen::classLinkedMap)
8496 {
8497 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8498 if (cdm)
8499 {
8500 cdm->sortAllMembersList();
8501 }
8502 }
8503}
8504
8505//----------------------------------------------------------------------------
8506
8508{
8509 auto processSourceFile = [](FileDef *fd,OutputList &ol,ClangTUParser *parser)
8510 {
8511 bool showSources = fd->generateSourceFile() && !Htags::useHtags; // sources need to be shown in the output
8512 bool parseSources = !fd->isReference() && Doxygen::parseSourcesNeeded; // we needed to parse the sources even if we do not show them
8513 if (showSources)
8514 {
8515 msg("Generating code for file {}...\n",fd->docName());
8516 fd->writeSourceHeader(ol);
8517 fd->writeSourceBody(ol,parser);
8518 fd->writeSourceFooter(ol);
8519 }
8520 else if (parseSources)
8521 {
8522 msg("Parsing code for file {}...\n",fd->docName());
8523 fd->parseSource(parser);
8524 }
8525 };
8526 if (!Doxygen::inputNameLinkedMap->empty())
8527 {
8528#if USE_LIBCLANG
8530 {
8531 StringUnorderedSet processedFiles;
8532
8533 // create a dictionary with files to process
8534 StringUnorderedSet filesToProcess;
8535
8536 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8537 {
8538 for (const auto &fd : *fn)
8539 {
8540 filesToProcess.insert(fd->absFilePath().str());
8541 }
8542 }
8543 // process source files (and their include dependencies)
8544 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8545 {
8546 for (const auto &fd : *fn)
8547 {
8548 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt::Cpp &&
8549 (fd->generateSourceFile() ||
8551 )
8552 )
8553 {
8554 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8555 clangParser->parse();
8556 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8557
8558 for (auto incFile : clangParser->filesInSameTU())
8559 {
8560 if (filesToProcess.find(incFile)!=filesToProcess.end() && // part of input
8561 fd->absFilePath()!=incFile && // not same file
8562 processedFiles.find(incFile)==processedFiles.end()) // not yet marked as processed
8563 {
8564 StringVector moreFiles;
8565 bool ambig = false;
8567 if (ifd && !ifd->isReference())
8568 {
8569 processSourceFile(ifd,*g_outputList,clangParser.get());
8570 processedFiles.insert(incFile);
8571 }
8572 }
8573 }
8574 processedFiles.insert(fd->absFilePath().str());
8575 }
8576 }
8577 }
8578 // process remaining files
8579 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8580 {
8581 for (const auto &fd : *fn)
8582 {
8583 if (processedFiles.find(fd->absFilePath().str())==processedFiles.end()) // not yet processed
8584 {
8585 if (fd->getLanguage()==SrcLangExt::Cpp) // C/C++ file, use clang parser
8586 {
8587 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8588 clangParser->parse();
8589 processSourceFile(fd.get(),*g_outputList,clangParser.get());
8590 }
8591 else // non C/C++ file, use built-in parser
8592 {
8593 processSourceFile(fd.get(),*g_outputList,nullptr);
8594 }
8595 }
8596 }
8597 }
8598 }
8599 else
8600#endif
8601 {
8602 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8603 if (numThreads>1)
8604 {
8605 msg("Generating code files using {} threads.\n",numThreads);
8606 struct SourceContext
8607 {
8608 SourceContext(FileDef *fd_,bool gen_,const OutputList &ol_)
8609 : fd(fd_), generateSourceFile(gen_), ol(ol_) {}
8610 FileDef *fd;
8611 bool generateSourceFile;
8612 OutputList ol;
8613 };
8614 ThreadPool threadPool(numThreads);
8615 std::vector< std::future< std::shared_ptr<SourceContext> > > results;
8616 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8617 {
8618 for (const auto &fd : *fn)
8619 {
8620 bool generateSourceFile = fd->generateSourceFile() && !Htags::useHtags;
8621 auto ctx = std::make_shared<SourceContext>(fd.get(),generateSourceFile,*g_outputList);
8622 auto processFile = [ctx]()
8623 {
8624 if (ctx->generateSourceFile)
8625 {
8626 msg("Generating code for file {}...\n",ctx->fd->docName());
8627 }
8628 else
8629 {
8630 msg("Parsing code for file {}...\n",ctx->fd->docName());
8631 }
8632 StringVector filesInSameTu;
8633 ctx->fd->getAllIncludeFilesRecursively(filesInSameTu);
8634 if (ctx->generateSourceFile) // sources need to be shown in the output
8635 {
8636 ctx->fd->writeSourceHeader(ctx->ol);
8637 ctx->fd->writeSourceBody(ctx->ol,nullptr);
8638 ctx->fd->writeSourceFooter(ctx->ol);
8639 }
8640 else if (!ctx->fd->isReference() && Doxygen::parseSourcesNeeded)
8641 // we needed to parse the sources even if we do not show them
8642 {
8643 ctx->fd->parseSource(nullptr);
8644 }
8645 return ctx;
8646 };
8647 results.emplace_back(threadPool.queue(processFile));
8648 }
8649 }
8650 for (auto &f : results)
8651 {
8652 auto ctx = f.get();
8653 }
8654 }
8655 else // single threaded version
8656 {
8657 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8658 {
8659 for (const auto &fd : *fn)
8660 {
8661 StringVector filesInSameTu;
8662 fd->getAllIncludeFilesRecursively(filesInSameTu);
8663 processSourceFile(fd.get(),*g_outputList,nullptr);
8664 }
8665 }
8666 }
8667 }
8668 }
8669}
8670
8671//----------------------------------------------------------------------------
8672
8673static void generateFileDocs()
8674{
8675 if (Index::instance().numDocumentedFiles()==0) return;
8676
8677 if (!Doxygen::inputNameLinkedMap->empty())
8678 {
8679 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8680 if (numThreads>1) // multi threaded processing
8681 {
8682 struct DocContext
8683 {
8684 DocContext(FileDef *fd_,const OutputList &ol_)
8685 : fd(fd_), ol(ol_) {}
8686 FileDef *fd;
8687 OutputList ol;
8688 };
8689 ThreadPool threadPool(numThreads);
8690 std::vector< std::future< std::shared_ptr<DocContext> > > results;
8691 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8692 {
8693 for (const auto &fd : *fn)
8694 {
8695 bool doc = fd->isLinkableInProject();
8696 if (doc)
8697 {
8698 auto ctx = std::make_shared<DocContext>(fd.get(),*g_outputList);
8699 auto processFile = [ctx]() {
8700 msg("Generating docs for file {}...\n",ctx->fd->docName());
8701 ctx->fd->writeDocumentation(ctx->ol);
8702 return ctx;
8703 };
8704 results.emplace_back(threadPool.queue(processFile));
8705 }
8706 }
8707 }
8708 for (auto &f : results)
8709 {
8710 auto ctx = f.get();
8711 }
8712 }
8713 else // single threaded processing
8714 {
8715 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8716 {
8717 for (const auto &fd : *fn)
8718 {
8719 bool doc = fd->isLinkableInProject();
8720 if (doc)
8721 {
8722 msg("Generating docs for file {}...\n",fd->docName());
8723 fd->writeDocumentation(*g_outputList);
8724 }
8725 }
8726 }
8727 }
8728 }
8729}
8730
8731//----------------------------------------------------------------------------
8732
8734{
8735 // add source references for class definitions
8736 for (const auto &cd : *Doxygen::classLinkedMap)
8737 {
8738 const FileDef *fd=cd->getBodyDef();
8739 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8740 {
8741 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),nullptr);
8742 }
8743 }
8744 // add source references for concept definitions
8745 for (const auto &cd : *Doxygen::conceptLinkedMap)
8746 {
8747 const FileDef *fd=cd->getBodyDef();
8748 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8749 {
8750 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),nullptr);
8751 }
8752 }
8753 // add source references for namespace definitions
8754 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8755 {
8756 const FileDef *fd=nd->getBodyDef();
8757 if (fd && nd->isLinkableInProject() && nd->getStartDefLine()!=-1)
8758 {
8759 const_cast<FileDef*>(fd)->addSourceRef(nd->getStartDefLine(),nd.get(),nullptr);
8760 }
8761 }
8762
8763 // add source references for member names
8764 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8765 {
8766 for (const auto &md : *mn)
8767 {
8768 //printf("class member %s: def=%s body=%d link?=%d\n",
8769 // qPrint(md->name()),
8770 // md->getBodyDef()?qPrint(md->getBodyDef()->name()):"<none>",
8771 // md->getStartBodyLine(),md->isLinkableInProject());
8772 const FileDef *fd=md->getBodyDef();
8773 if (fd &&
8774 md->getStartDefLine()!=-1 &&
8775 md->isLinkableInProject() &&
8777 )
8778 {
8779 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8780 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8781 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8782 }
8783 }
8784 }
8785 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8786 {
8787 for (const auto &md : *mn)
8788 {
8789 const FileDef *fd=md->getBodyDef();
8790 //printf("member %s body=[%d,%d] fd=%p link=%d parseSources=%d\n",
8791 // qPrint(md->name()),
8792 // md->getStartBodyLine(),md->getEndBodyLine(),fd,
8793 // md->isLinkableInProject(),
8794 // Doxygen::parseSourcesNeeded);
8795 if (fd &&
8796 md->getStartDefLine()!=-1 &&
8797 md->isLinkableInProject() &&
8799 )
8800 {
8801 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8802 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8803 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8804 }
8805 }
8806 }
8807}
8808
8809//----------------------------------------------------------------------------
8810
8811// add the macro definitions found during preprocessing as file members
8812static void buildDefineList()
8813{
8814 AUTO_TRACE();
8815 for (const auto &s : g_inputFiles)
8816 {
8817 auto it = Doxygen::macroDefinitions.find(s);
8819 {
8820 for (const auto &def : it->second)
8821 {
8822 auto md = createMemberDef(
8823 def.fileName,def.lineNr,def.columnNr,
8824 "#define",def.name,def.args,QCString(),
8825 Protection::Public,Specifier::Normal,FALSE,Relationship::Member,MemberType::Define,
8826 ArgumentList(),ArgumentList(),"");
8827 auto mmd = toMemberDefMutable(md.get());
8828
8829 if (!def.args.isEmpty())
8830 {
8831 mmd->moveArgumentList(stringToArgumentList(SrcLangExt::Cpp, def.args));
8832 }
8833 mmd->setInitializer(def.definition);
8834 mmd->setFileDef(def.fileDef);
8835 mmd->setDefinition("#define "+def.name);
8836
8837 MemberName *mn=Doxygen::functionNameLinkedMap->add(def.name);
8838 if (def.fileDef)
8839 {
8840 const MemberList *defMl = def.fileDef->getMemberList(MemberListType::DocDefineMembers());
8841 if (defMl)
8842 {
8843 const MemberDef *defMd = defMl->findRev(def.name);
8844 if (defMd) // definition already stored
8845 {
8846 mmd->setRedefineCount(defMd->redefineCount()+1);
8847 }
8848 }
8849 def.fileDef->insertMember(md.get());
8850 }
8851 AUTO_TRACE_ADD("adding macro {} with definition {}",def.name,def.definition);
8852 mn->push_back(std::move(md));
8853 }
8854 }
8855 }
8856}
8857
8858//----------------------------------------------------------------------------
8859
8860static void sortMemberLists()
8861{
8862 // sort class member lists
8863 for (const auto &cd : *Doxygen::classLinkedMap)
8864 {
8865 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8866 if (cdm)
8867 {
8868 cdm->sortMemberLists();
8869 }
8870 }
8871
8872 // sort namespace member lists
8873 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8874 {
8876 if (ndm)
8877 {
8878 ndm->sortMemberLists();
8879 }
8880 }
8881
8882 // sort file member lists
8883 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8884 {
8885 for (const auto &fd : *fn)
8886 {
8887 fd->sortMemberLists();
8888 }
8889 }
8890
8891 // sort group member lists
8892 for (const auto &gd : *Doxygen::groupLinkedMap)
8893 {
8894 gd->sortMemberLists();
8895 }
8896
8898}
8899
8900//----------------------------------------------------------------------------
8901
8902static bool isSymbolHidden(const Definition *d)
8903{
8904 bool hidden = d->isHidden();
8905 const Definition *parent = d->getOuterScope();
8906 return parent ? hidden || isSymbolHidden(parent) : hidden;
8907}
8908
8910{
8911 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8912 if (numThreads>1)
8913 {
8914 ThreadPool threadPool(numThreads);
8915 std::vector < std::future< void > > results;
8916 // queue the work
8917 for (const auto &[name,symList] : *Doxygen::symbolMap)
8918 {
8919 for (const auto &def : symList)
8920 {
8922 if (dm && !isSymbolHidden(def) && !def->isArtificial() && def->isLinkableInProject())
8923 {
8924 auto processTooltip = [dm]() {
8925 dm->computeTooltip();
8926 };
8927 results.emplace_back(threadPool.queue(processTooltip));
8928 }
8929 }
8930 }
8931 // wait for the results
8932 for (auto &f : results)
8933 {
8934 f.get();
8935 }
8936 }
8937 else
8938 {
8939 for (const auto &[name,symList] : *Doxygen::symbolMap)
8940 {
8941 for (const auto &def : symList)
8942 {
8944 if (dm && !isSymbolHidden(def) && !def->isArtificial() && def->isLinkableInProject())
8945 {
8946 dm->computeTooltip();
8947 }
8948 }
8949 }
8950 }
8951}
8952
8953//----------------------------------------------------------------------------
8954
8956{
8957 for (const auto &cd : *Doxygen::classLinkedMap)
8958 {
8959 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8960 if (cdm)
8961 {
8962 cdm->setAnonymousEnumType();
8963 }
8964 }
8965}
8966
8967//----------------------------------------------------------------------------
8968
8969static void countMembers()
8970{
8971 for (const auto &cd : *Doxygen::classLinkedMap)
8972 {
8973 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8974 if (cdm)
8975 {
8976 cdm->countMembers();
8977 }
8978 }
8979
8980 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8981 {
8983 if (ndm)
8984 {
8985 ndm->countMembers();
8986 }
8987 }
8988
8989 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8990 {
8991 for (const auto &fd : *fn)
8992 {
8993 fd->countMembers();
8994 }
8995 }
8996
8997 for (const auto &gd : *Doxygen::groupLinkedMap)
8998 {
8999 gd->countMembers();
9000 }
9001
9002 auto &mm = ModuleManager::instance();
9003 mm.countMembers();
9004}
9005
9006
9007//----------------------------------------------------------------------------
9008// generate the documentation for all classes
9009
9010static void generateDocsForClassList(const std::vector<ClassDefMutable*> &classList)
9011{
9012 AUTO_TRACE();
9013 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9014 if (numThreads>1) // multi threaded processing
9015 {
9016 struct DocContext
9017 {
9018 DocContext(ClassDefMutable *cd_,const OutputList &ol_)
9019 : cd(cd_), ol(ol_) {}
9020 ClassDefMutable *cd;
9021 OutputList ol;
9022 };
9023 ThreadPool threadPool(numThreads);
9024 std::vector< std::future< std::shared_ptr<DocContext> > > results;
9025 for (const auto &cd : classList)
9026 {
9027 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
9028 if (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9029 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
9030 )
9031 {
9032 auto ctx = std::make_shared<DocContext>(cd,*g_outputList);
9033 auto processFile = [ctx]()
9034 {
9035 msg("Generating docs for compound {}...\n",ctx->cd->displayName());
9036
9037 // skip external references, anonymous compounds and
9038 // template instances
9039 if (!ctx->cd->isHidden() && !ctx->cd->isEmbeddedInOuterScope() &&
9040 ctx->cd->isLinkableInProject() && !ctx->cd->isImplicitTemplateInstance())
9041 {
9042 ctx->cd->writeDocumentation(ctx->ol);
9043 ctx->cd->writeMemberList(ctx->ol);
9044 }
9045
9046 // even for undocumented classes, the inner classes can be documented.
9047 ctx->cd->writeDocumentationForInnerClasses(ctx->ol);
9048 return ctx;
9049 };
9050 results.emplace_back(threadPool.queue(processFile));
9051 }
9052 }
9053 for (auto &f : results)
9054 {
9055 auto ctx = f.get();
9056 }
9057 }
9058 else // single threaded processing
9059 {
9060 for (const auto &cd : classList)
9061 {
9062 //printf("cd=%s getOuterScope=%p global=%p hidden=%d embeddedInOuterScope=%d\n",
9063 // qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope,cd->isHidden(),cd->isEmbeddedInOuterScope());
9064 if (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9065 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
9066 )
9067 {
9068 // skip external references, anonymous compounds and
9069 // template instances
9070 if ( !cd->isHidden() && !cd->isEmbeddedInOuterScope() &&
9071 cd->isLinkableInProject() && !cd->isImplicitTemplateInstance())
9072 {
9073 msg("Generating docs for compound {}...\n",cd->displayName());
9074
9075 cd->writeDocumentation(*g_outputList);
9076 cd->writeMemberList(*g_outputList);
9077 }
9078 // even for undocumented classes, the inner classes can be documented.
9079 cd->writeDocumentationForInnerClasses(*g_outputList);
9080 }
9081 }
9082 }
9083}
9084
9085static void addClassAndNestedClasses(std::vector<ClassDefMutable*> &list,ClassDefMutable *cd)
9086{
9087 list.push_back(cd);
9088 for (const auto &innerCdi : cd->getClasses())
9089 {
9090 ClassDefMutable *innerCd = toClassDefMutable(innerCdi);
9091 if (innerCd)
9092 {
9093 AUTO_TRACE("innerCd={} isLinkable={} isImplicitTemplateInstance={} protectLevelVisible={} embeddedInOuterScope={}",
9094 innerCd->name(),innerCd->isLinkableInProject(),innerCd->isImplicitTemplateInstance(),protectionLevelVisible(innerCd->protection()),
9095 innerCd->isEmbeddedInOuterScope());
9096 }
9097 if (innerCd && innerCd->isLinkableInProject() && !innerCd->isImplicitTemplateInstance() &&
9098 protectionLevelVisible(innerCd->protection()) &&
9099 !innerCd->isEmbeddedInOuterScope()
9100 )
9101 {
9102 list.push_back(innerCd);
9103 addClassAndNestedClasses(list,innerCd);
9104 }
9105 }
9106}
9107
9109{
9110 std::vector<ClassDefMutable*> classList;
9111 for (const auto &cdi : *Doxygen::classLinkedMap)
9112 {
9113 ClassDefMutable *cd = toClassDefMutable(cdi.get());
9114 if (cd && (cd->getOuterScope()==nullptr ||
9116 {
9117 addClassAndNestedClasses(classList,cd);
9118 }
9119 }
9120 for (const auto &cdi : *Doxygen::hiddenClassLinkedMap)
9121 {
9122 ClassDefMutable *cd = toClassDefMutable(cdi.get());
9123 if (cd && (cd->getOuterScope()==nullptr ||
9125 {
9126 addClassAndNestedClasses(classList,cd);
9127 }
9128 }
9129 generateDocsForClassList(classList);
9130}
9131
9132//----------------------------------------------------------------------------
9133
9135{
9136 for (const auto &cdi : *Doxygen::conceptLinkedMap)
9137 {
9139
9140 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
9141 if (cd &&
9142 (cd->getOuterScope()==nullptr || // <-- should not happen, but can if we read an old tag file
9143 cd->getOuterScope()==Doxygen::globalScope // only look at global concepts
9144 ) && !cd->isHidden() && cd->isLinkableInProject()
9145 )
9146 {
9147 msg("Generating docs for concept {}...\n",cd->displayName());
9149 }
9150 }
9151}
9152
9153//----------------------------------------------------------------------------
9154
9156{
9157 for (const auto &mn : *Doxygen::memberNameLinkedMap)
9158 {
9159 for (const auto &imd : *mn)
9160 {
9161 MemberDefMutable *md = toMemberDefMutable(imd.get());
9162 //static int count=0;
9163 //printf("%04d Member '%s'\n",count++,qPrint(md->qualifiedName()));
9164 if (md && md->documentation().isEmpty() && md->briefDescription().isEmpty())
9165 { // no documentation yet
9166 const MemberDef *bmd = md->reimplements();
9167 while (bmd && bmd->documentation().isEmpty() &&
9168 bmd->briefDescription().isEmpty()
9169 )
9170 { // search up the inheritance tree for a documentation member
9171 //printf("bmd=%s class=%s\n",qPrint(bmd->name()),qPrint(bmd->getClassDef()->name()));
9172 bmd = bmd->reimplements();
9173 }
9174 if (bmd) // copy the documentation from the reimplemented member
9175 {
9176 md->setInheritsDocsFrom(bmd);
9177 md->setDocumentation(bmd->documentation(),bmd->docFile(),bmd->docLine());
9179 md->setBriefDescription(bmd->briefDescription(),bmd->briefFile(),bmd->briefLine());
9180 md->copyArgumentNames(bmd);
9182 }
9183 }
9184 }
9185 }
9186}
9187
9188//----------------------------------------------------------------------------
9189
9191{
9192 // for each file
9193 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9194 {
9195 for (const auto &fd : *fn)
9196 {
9197 fd->combineUsingRelations();
9198 }
9199 }
9200
9201 // for each namespace
9202 NamespaceDefSet visitedNamespaces;
9203 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9204 {
9206 if (ndm)
9207 {
9208 ndm->combineUsingRelations(visitedNamespaces);
9209 }
9210 }
9211}
9212
9213//----------------------------------------------------------------------------
9214
9216{
9217 // for each class
9218 for (const auto &cd : *Doxygen::classLinkedMap)
9219 {
9220 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9221 if (cdm)
9222 {
9224 }
9225 }
9226 // for each file
9227 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9228 {
9229 for (const auto &fd : *fn)
9230 {
9231 fd->addMembersToMemberGroup();
9232 }
9233 }
9234 // for each namespace
9235 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9236 {
9238 if (ndm)
9239 {
9241 }
9242 }
9243 // for each group
9244 for (const auto &gd : *Doxygen::groupLinkedMap)
9245 {
9246 gd->addMembersToMemberGroup();
9247 }
9249}
9250
9251//----------------------------------------------------------------------------
9252
9254{
9255 // for each class
9256 for (const auto &cd : *Doxygen::classLinkedMap)
9257 {
9258 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9259 if (cdm)
9260 {
9262 }
9263 }
9264 // for each file
9265 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9266 {
9267 for (const auto &fd : *fn)
9268 {
9269 fd->distributeMemberGroupDocumentation();
9270 }
9271 }
9272 // for each namespace
9273 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9274 {
9276 if (ndm)
9277 {
9279 }
9280 }
9281 // for each group
9282 for (const auto &gd : *Doxygen::groupLinkedMap)
9283 {
9284 gd->distributeMemberGroupDocumentation();
9285 }
9287}
9288
9289//----------------------------------------------------------------------------
9290
9292{
9293 // for each class
9294 for (const auto &cd : *Doxygen::classLinkedMap)
9295 {
9296 ClassDefMutable *cdm = toClassDefMutable(cd.get());
9297 if (cdm)
9298 {
9300 }
9301 }
9302 // for each concept
9303 for (const auto &cd : *Doxygen::conceptLinkedMap)
9304 {
9305 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
9306 if (cdm)
9307 {
9309 }
9310 }
9311 // for each file
9312 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9313 {
9314 for (const auto &fd : *fn)
9315 {
9316 fd->findSectionsInDocumentation();
9317 }
9318 }
9319 // for each namespace
9320 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9321 {
9323 if (ndm)
9324 {
9326 }
9327 }
9328 // for each group
9329 for (const auto &gd : *Doxygen::groupLinkedMap)
9330 {
9331 gd->findSectionsInDocumentation();
9332 }
9333 // for each page
9334 for (const auto &pd : *Doxygen::pageLinkedMap)
9335 {
9336 pd->findSectionsInDocumentation();
9337 }
9338 // for each directory
9339 for (const auto &dd : *Doxygen::dirLinkedMap)
9340 {
9341 dd->findSectionsInDocumentation();
9342 }
9344 if (Doxygen::mainPage) Doxygen::mainPage->findSectionsInDocumentation();
9345}
9346
9347//----------------------------------------------------------------------
9348
9349
9351{
9352 // remove all references to classes from the cache
9353 // as there can be new template instances in the inheritance path
9354 // to this class. Optimization: only remove those classes that
9355 // have inheritance instances as direct or indirect sub classes.
9356 StringVector elementsToRemove;
9357 for (const auto &ci : *Doxygen::typeLookupCache)
9358 {
9359 const LookupInfo &li = ci.second;
9360 if (li.definition)
9361 {
9362 elementsToRemove.push_back(ci.first);
9363 }
9364 }
9365 for (const auto &k : elementsToRemove)
9366 {
9367 Doxygen::typeLookupCache->remove(k);
9368 }
9369
9370 // remove all cached typedef resolutions whose target is a
9371 // template class as this may now be a template instance
9372 // for each global function name
9373 for (const auto &fn : *Doxygen::functionNameLinkedMap)
9374 {
9375 // for each function with that name
9376 for (const auto &ifmd : *fn)
9377 {
9378 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
9379 if (fmd && fmd->isTypedefValCached())
9380 {
9381 const ClassDef *cd = fmd->getCachedTypedefVal();
9382 if (cd->isTemplate()) fmd->invalidateTypedefValCache();
9383 }
9384 }
9385 }
9386 // for each class method name
9387 for (const auto &nm : *Doxygen::memberNameLinkedMap)
9388 {
9389 // for each function with that name
9390 for (const auto &imd : *nm)
9391 {
9392 MemberDefMutable *md = toMemberDefMutable(imd.get());
9393 if (md && md->isTypedefValCached())
9394 {
9395 const ClassDef *cd = md->getCachedTypedefVal();
9396 if (cd->isTemplate()) md->invalidateTypedefValCache();
9397 }
9398 }
9399 }
9400}
9401
9402//----------------------------------------------------------------------------
9403
9405{
9406 // Remove all unresolved references to classes from the cache.
9407 // This is needed before resolving the inheritance relations, since
9408 // it would otherwise not find the inheritance relation
9409 // for C in the example below, as B::I was already found to be unresolvable
9410 // (which is correct if you ignore the inheritance relation between A and B).
9411 //
9412 // class A { class I {} };
9413 // class B : public A {};
9414 // class C : public B::I {};
9415
9416 StringVector elementsToRemove;
9417 for (const auto &ci : *Doxygen::typeLookupCache)
9418 {
9419 const LookupInfo &li = ci.second;
9420 if (li.definition==nullptr && li.typeDef==nullptr)
9421 {
9422 elementsToRemove.push_back(ci.first);
9423 }
9424 }
9425 for (const auto &k : elementsToRemove)
9426 {
9427 Doxygen::typeLookupCache->remove(k);
9428 }
9429
9430 // for each global function name
9431 for (const auto &fn : *Doxygen::functionNameLinkedMap)
9432 {
9433 // for each function with that name
9434 for (const auto &ifmd : *fn)
9435 {
9436 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
9437 if (fmd)
9438 {
9440 }
9441 }
9442 }
9443 // for each class method name
9444 for (const auto &nm : *Doxygen::memberNameLinkedMap)
9445 {
9446 // for each function with that name
9447 for (const auto &imd : *nm)
9448 {
9449 MemberDefMutable *md = toMemberDefMutable(imd.get());
9450 if (md)
9451 {
9453 }
9454 }
9455 }
9456
9457}
9458
9459//----------------------------------------------------------------------------
9460// Returns TRUE if the entry and member definition have equal file names,
9461// otherwise FALSE.
9462
9463static bool haveEqualFileNames(const Entry *root,const MemberDef *md)
9464{
9465 const FileDef *fd = md->getFileDef();
9466 if (!fd)
9467 {
9468 return FALSE;
9469 }
9470
9471 return fd->absFilePath() == root->fileName;
9472}
9473
9474//----------------------------------------------------------------------------
9475
9476static void addDefineDoc(const Entry *root, MemberDefMutable *md)
9477{
9478 md->setDocumentation(root->doc,root->docFile,root->docLine);
9479 md->setDocsForDefinition(!root->proto);
9480 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9481 if (md->inbodyDocumentation().isEmpty())
9482 {
9484 }
9485 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
9486 {
9487 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
9488 md->setBodyDef(root->fileDef());
9489 }
9491 md->setMaxInitLines(root->initLines);
9493 md->setRefItems(root->sli);
9494 md->addQualifiers(root->qualifiers);
9495 if (root->mGrpId!=-1) md->setMemberGroupId(root->mGrpId);
9496 addMemberToGroups(root,md);
9498}
9499
9500//----------------------------------------------------------------------------
9501
9503{
9504 if ((root->section.isDefineDoc() || root->section.isDefine()) && !root->name.isEmpty())
9505 {
9506 //printf("found define '%s' '%s' brief='%s' doc='%s'\n",
9507 // qPrint(root->name),qPrint(root->args),qPrint(root->brief),qPrint(root->doc));
9508
9509 if (root->tagInfo() && !root->name.isEmpty()) // define read from a tag file
9510 {
9511 auto md = createMemberDef(root->tagInfo()->tagName,1,1,
9512 "#define",root->name,root->args,QCString(),
9513 Protection::Public,Specifier::Normal,FALSE,Relationship::Member,MemberType::Define,
9514 ArgumentList(),ArgumentList(),"");
9515 auto mmd = toMemberDefMutable(md.get());
9516 mmd->setTagInfo(root->tagInfo());
9517 mmd->setLanguage(root->lang);
9518 mmd->addQualifiers(root->qualifiers);
9519 //printf("Searching for '%s' fd=%p\n",qPrint(filePathName),fd);
9520 mmd->setFileDef(root->parent()->fileDef());
9521 //printf("Adding member=%s\n",qPrint(md->name()));
9523 mn->push_back(std::move(md));
9524 }
9526 if (mn)
9527 {
9528 int count=0;
9529 for (const auto &md : *mn)
9530 {
9531 if (md->memberType()==MemberType::Define) count++;
9532 }
9533 if (count==1)
9534 {
9535 for (const auto &imd : *mn)
9536 {
9537 MemberDefMutable *md = toMemberDefMutable(imd.get());
9538 if (md && md->memberType()==MemberType::Define)
9539 {
9540 addDefineDoc(root,md);
9541 }
9542 }
9543 }
9544 else if (count>1 &&
9545 (!root->doc.isEmpty() ||
9546 !root->brief.isEmpty() ||
9547 root->bodyLine!=-1
9548 )
9549 )
9550 // multiple defines don't know where to add docs
9551 // but maybe they are in different files together with their documentation
9552 {
9553 for (const auto &imd : *mn)
9554 {
9555 MemberDefMutable *md = toMemberDefMutable(imd.get());
9556 if (md && md->memberType()==MemberType::Define)
9557 {
9558 if (haveEqualFileNames(root, md) || isEntryInGroupOfMember(root, md))
9559 // doc and define in the same file or group assume they belong together.
9560 {
9561 addDefineDoc(root,md);
9562 }
9563 }
9564 }
9565 //warn("define {} found in the following files:\n",root->name);
9566 //warn("Cannot determine where to add the documentation found "
9567 // "at line {} of file {}. \n",
9568 // root->startLine,root->fileName);
9569 }
9570 }
9571 else if (!root->doc.isEmpty() || !root->brief.isEmpty()) // define not found
9572 {
9573 bool preEnabled = Config_getBool(ENABLE_PREPROCESSING);
9574 if (preEnabled)
9575 {
9576 warn(root->fileName,root->startLine,"documentation for unknown define {} found.",root->name);
9577 }
9578 else
9579 {
9580 warn(root->fileName,root->startLine, "found documented #define {} but ignoring it because ENABLE_PREPROCESSING is NO.", root->name);
9581 }
9582 }
9583 }
9584 for (const auto &e : root->children()) findDefineDocumentation(e.get());
9585}
9586
9587//----------------------------------------------------------------------------
9588
9589static void findDirDocumentation(const Entry *root)
9590{
9591 if (root->section.isDirDoc())
9592 {
9593 QCString normalizedName = root->name;
9594 normalizedName = substitute(normalizedName,"\\","/");
9595 //printf("root->docFile=%s normalizedName=%s\n",
9596 // qPrint(root->docFile),qPrint(normalizedName));
9597 if (root->docFile==normalizedName) // current dir?
9598 {
9599 int lastSlashPos=normalizedName.findRev('/');
9600 if (lastSlashPos!=-1) // strip file name
9601 {
9602 normalizedName=normalizedName.left(lastSlashPos);
9603 }
9604 }
9605 if (normalizedName.at(normalizedName.length()-1)!='/')
9606 {
9607 normalizedName+='/';
9608 }
9609 DirDef *matchingDir=nullptr;
9610 for (const auto &dir : *Doxygen::dirLinkedMap)
9611 {
9612 //printf("Dir: %s<->%s\n",qPrint(dir->name()),qPrint(normalizedName));
9613 if (dir->name().right(normalizedName.length())==normalizedName)
9614 {
9615 if (matchingDir)
9616 {
9617 warn(root->fileName,root->startLine,
9618 "\\dir command matches multiple directories.\n"
9619 " Applying the command for directory {}\n"
9620 " Ignoring the command for directory {}",
9621 matchingDir->name(),dir->name()
9622 );
9623 }
9624 else
9625 {
9626 matchingDir=dir.get();
9627 }
9628 }
9629 }
9630 if (matchingDir)
9631 {
9632 //printf("Match for with dir %s #anchor=%zu\n",qPrint(matchingDir->name()),root->anchors.size());
9633 matchingDir->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9634 matchingDir->setDocumentation(root->doc,root->docFile,root->docLine);
9635 matchingDir->setRefItems(root->sli);
9636 matchingDir->addSectionsToDefinition(root->anchors);
9637 root->commandOverrides.apply_directoryGraph([&](bool b) { matchingDir->overrideDirectoryGraph(b); });
9638 addDirToGroups(root,matchingDir);
9639 }
9640 else
9641 {
9642 warn(root->fileName,root->startLine,"No matching directory found for command \\dir {}",normalizedName);
9643 }
9644 }
9645 for (const auto &e : root->children()) findDirDocumentation(e.get());
9646}
9647
9648
9649//----------------------------------------------------------------------------
9650// create a (sorted) list of separate documentation pages
9651
9652static void buildPageList(Entry *root)
9653{
9654 if (root->section.isPageDoc())
9655 {
9656 if (!root->name.isEmpty())
9657 {
9658 addRelatedPage(root);
9659 }
9660 }
9661 else if (root->section.isMainpageDoc())
9662 {
9663 QCString title=root->args.stripWhiteSpace();
9664 if (title.isEmpty()) title=theTranslator->trMainPage();
9665 //QCString name = Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9666 QCString name = "index";
9667 addRefItem(root->sli,
9668 name,
9669 "page",
9670 name,
9671 title,
9672 QCString(),nullptr
9673 );
9674 }
9675 for (const auto &e : root->children()) buildPageList(e.get());
9676}
9677
9678// search for the main page defined in this project
9679static void findMainPage(Entry *root)
9680{
9681 if (root->section.isMainpageDoc())
9682 {
9683 if (Doxygen::mainPage==nullptr && root->tagInfo()==nullptr)
9684 {
9685 //printf("mainpage: docLine=%d startLine=%d\n",root->docLine,root->startLine);
9686 //printf("Found main page! \n======\n%s\n=======\n",qPrint(root->doc));
9687 QCString title=root->args.stripWhiteSpace();
9688 if (title.isEmpty()) title = Config_getString(PROJECT_NAME);
9689 //QCString indexName=Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9690 QCString indexName="index";
9692 indexName, root->brief+root->doc+root->inbodyDocs,title);
9693 //setFileNameForSections(root->anchors,"index",Doxygen::mainPage);
9694 Doxygen::mainPage->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9695 Doxygen::mainPage->setBodySegment(root->startLine,root->startLine,-1);
9696 Doxygen::mainPage->setFileName(indexName);
9697 Doxygen::mainPage->setLocalToc(root->localToc);
9699
9701 if (si)
9702 {
9703 if (!si->ref().isEmpty()) // we are from a tag file
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 else if (si->lineNr() != -1)
9715 {
9716 warn(root->fileName,root->startLine,"multiple use of section label '{}' for main page, (first occurrence: {}, line {})",
9717 Doxygen::mainPage->name(),si->fileName(),si->lineNr());
9718 }
9719 else
9720 {
9721 warn(root->fileName,root->startLine,"multiple use of section label '{}' for main page, (first occurrence: {})",
9722 Doxygen::mainPage->name(),si->fileName());
9723 }
9724 }
9725 else
9726 {
9727 // a page name is a label as well! but should no be double either
9729 Doxygen::mainPage->name(),
9730 indexName,
9731 root->startLine,
9732 Doxygen::mainPage->title(),
9734 0); // level 0
9735 }
9736 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9737 }
9738 else if (root->tagInfo()==nullptr)
9739 {
9740 warn(root->fileName,root->startLine,
9741 "found more than one \\mainpage comment block! (first occurrence: {}, line {}), Skipping current block!",
9742 Doxygen::mainPage->docFile(),Doxygen::mainPage->getStartBodyLine());
9743 }
9744 }
9745 for (const auto &e : root->children()) findMainPage(e.get());
9746}
9747
9748// search for the main page imported via tag files and add only the section labels
9749static void findMainPageTagFiles(Entry *root)
9750{
9751 if (root->section.isMainpageDoc())
9752 {
9753 if (Doxygen::mainPage && root->tagInfo())
9754 {
9755 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9756 }
9757 }
9758 for (const auto &e : root->children()) findMainPageTagFiles(e.get());
9759}
9760
9761static void computePageRelations(Entry *root)
9762{
9763 if ((root->section.isPageDoc() || root->section.isMainpageDoc()) && !root->name.isEmpty())
9764 {
9765 PageDef *pd = root->section.isPageDoc() ?
9766 Doxygen::pageLinkedMap->find(root->name) :
9767 Doxygen::mainPage.get();
9768 if (pd)
9769 {
9770 for (const BaseInfo &bi : root->extends)
9771 {
9772 PageDef *subPd = Doxygen::pageLinkedMap->find(bi.name);
9773 if (pd==subPd)
9774 {
9775 term("page defined {} with label {} is a direct "
9776 "subpage of itself! Please remove this cyclic dependency.\n",
9777 warn_line(pd->docFile(),pd->docLine()),pd->name());
9778 }
9779 else if (subPd)
9780 {
9781 pd->addInnerCompound(subPd);
9782 //printf("*** Added subpage relation: %s->%s\n",
9783 // qPrint(pd->name()),qPrint(subPd->name()));
9784 }
9785 }
9786 }
9787 }
9788 for (const auto &e : root->children()) computePageRelations(e.get());
9789}
9790
9792{
9793 for (const auto &pd : *Doxygen::pageLinkedMap)
9794 {
9795 Definition *ppd = pd->getOuterScope();
9796 while (ppd)
9797 {
9798 if (ppd==pd.get())
9799 {
9800 term("page defined {} with label {} is a subpage "
9801 "of itself! Please remove this cyclic dependency.\n",
9802 warn_line(pd->docFile(),pd->docLine()),pd->name());
9803 }
9804 ppd=ppd->getOuterScope();
9805 }
9806 }
9807}
9808
9809//----------------------------------------------------------------------------
9810
9812{
9813 for (const auto &si : SectionManager::instance())
9814 {
9815 //printf("si->label='%s' si->definition=%s si->fileName='%s'\n",
9816 // qPrint(si->label),si->definition?qPrint(si->definition->name()):"<none>",
9817 // qPrint(si->fileName));
9818 PageDef *pd=nullptr;
9819
9820 // hack: the items of a todo/test/bug/deprecated list are all fragments from
9821 // different files, so the resulting section's all have the wrong file
9822 // name (not from the todo/test/bug/deprecated list, but from the file in
9823 // which they are defined). We correct this here by looking at the
9824 // generated section labels!
9826 {
9827 QCString label="_"+rl->listName(); // "_todo", "_test", ...
9828 if (si->label().left(label.length())==label)
9829 {
9830 si->setFileName(rl->listName());
9831 si->setGenerated(TRUE);
9832 break;
9833 }
9834 }
9835
9836 //printf("start: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
9837 if (!si->generated())
9838 {
9839 // if this section is in a page and the page is in a group, then we
9840 // have to adjust the link file name to point to the group.
9841 if (!si->fileName().isEmpty() &&
9842 (pd=Doxygen::pageLinkedMap->find(si->fileName())) &&
9843 pd->getGroupDef())
9844 {
9845 si->setFileName(pd->getGroupDef()->getOutputFileBase());
9846 }
9847
9848 if (si->definition())
9849 {
9850 // TODO: there should be one function in Definition that returns
9851 // the file to link to, so we can avoid the following tests.
9852 const GroupDef *gd=nullptr;
9853 if (si->definition()->definitionType()==Definition::TypeMember)
9854 {
9855 gd = (toMemberDef(si->definition()))->getGroupDef();
9856 }
9857
9858 if (gd)
9859 {
9860 si->setFileName(gd->getOutputFileBase());
9861 }
9862 else
9863 {
9864 //si->fileName=si->definition->getOutputFileBase();
9865 //printf("Setting si->fileName to %s\n",qPrint(si->fileName));
9866 }
9867 }
9868 }
9869 //printf("end: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
9870 }
9871}
9872
9873
9874
9875//----------------------------------------------------------------------------
9876// generate all separate documentation pages
9877
9878
9879static void generatePageDocs()
9880{
9881 //printf("documentedPages=%d real=%d\n",documentedPages,Doxygen::pageLinkedMap->count());
9882 if (Index::instance().numDocumentedPages()==0) return;
9883 for (const auto &pd : *Doxygen::pageLinkedMap)
9884 {
9885 if (!pd->getGroupDef() && !pd->isReference())
9886 {
9887 msg("Generating docs for page {}...\n",pd->name());
9888 pd->writeDocumentation(*g_outputList);
9889 }
9890 }
9891}
9892
9893//----------------------------------------------------------------------------
9894// create a (sorted) list & dictionary of example pages
9895
9896static void buildExampleList(Entry *root)
9897{
9898 if ((root->section.isExample() || root->section.isExampleLineno()) && !root->name.isEmpty())
9899 {
9900 if (Doxygen::exampleLinkedMap->find(root->name))
9901 {
9902 warn(root->fileName,root->startLine,"Example {} was already documented. Ignoring documentation found here.",root->name);
9903 }
9904 else
9905 {
9906 PageDef *pd = Doxygen::exampleLinkedMap->add(root->name,
9907 createPageDef(root->fileName,root->startLine,
9908 root->name,root->brief+root->doc+root->inbodyDocs,root->args));
9909 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9910 pd->setFileName(convertNameToFile(pd->name()+"-example",FALSE,TRUE));
9912 pd->setLanguage(root->lang);
9913 pd->setShowLineNo(root->section.isExampleLineno());
9914
9915 //we don't add example to groups
9916 //addExampleToGroups(root,pd);
9917 }
9918 }
9919 for (const auto &e : root->children()) buildExampleList(e.get());
9920}
9921
9922//----------------------------------------------------------------------------
9923// prints the Entry tree (for debugging)
9924
9925void printNavTree(Entry *root,int indent)
9926{
9928 {
9929 QCString indentStr;
9930 indentStr.fill(' ',indent);
9931 Debug::print(Debug::Entries,0,"{}{} at {}:{} (sec={}, spec={})\n",
9932 indentStr.isEmpty()?"":indentStr,
9933 root->name.isEmpty()?"<empty>":root->name,
9934 root->fileName,root->startLine,
9935 root->section.to_string(),
9936 root->spec.to_string());
9937 for (const auto &e : root->children())
9938 {
9939 printNavTree(e.get(),indent+2);
9940 }
9941 }
9942}
9943
9944
9945//----------------------------------------------------------------------------
9946// prints the Sections tree (for debugging)
9947
9949{
9951 {
9952 for (const auto &si : SectionManager::instance())
9953 {
9954 Debug::print(Debug::Sections,0,"Section = {}, file = {}, title = {}, type = {}, ref = {}\n",
9955 si->label(),si->fileName(),si->title(),si->type().level(),si->ref());
9956 }
9957 }
9958}
9959
9960
9961//----------------------------------------------------------------------------
9962// generate the example documentation
9963
9965{
9966 g_outputList->disable(OutputType::Man);
9967 for (const auto &pd : *Doxygen::exampleLinkedMap)
9968 {
9969 msg("Generating docs for example {}...\n",pd->name());
9970 SrcLangExt lang = getLanguageFromFileName(pd->name(), SrcLangExt::Unknown);
9971 if (lang != SrcLangExt::Unknown)
9972 {
9973 QCString ext = getFileNameExtension(pd->name());
9974 auto intf = Doxygen::parserManager->getCodeParser(ext);
9975 intf->resetCodeParserState();
9976 }
9977 QCString n=pd->getOutputFileBase();
9978 startFile(*g_outputList,n,false,n,pd->name());
9980 g_outputList->docify(pd->name());
9982 g_outputList->startContents();
9983 QCString lineNoOptStr;
9984 if (pd->showLineNo())
9985 {
9986 lineNoOptStr="{lineno}";
9987 }
9988 g_outputList->generateDoc(pd->docFile(), // file
9989 pd->docLine(), // startLine
9990 pd.get(), // context
9991 nullptr, // memberDef
9992 (pd->briefDescription().isEmpty()?"":pd->briefDescription()+"\n\n")+
9993 pd->documentation()+"\n\n\\include"+lineNoOptStr+" "+pd->name(), // docs
9994 DocOptions()
9995 .setIndexWords(true)
9996 .setExample(pd->name()));
9997 endFile(*g_outputList); // contains g_outputList->endContents()
9998 }
10000}
10001
10002//----------------------------------------------------------------------------
10003// generate module pages
10004
10006{
10007 for (const auto &gd : *Doxygen::groupLinkedMap)
10008 {
10009 if (!gd->isReference())
10010 {
10011 gd->writeDocumentation(*g_outputList);
10012 }
10013 }
10014}
10015
10016//----------------------------------------------------------------------------
10017// generate module pages
10018
10020{
10021 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10022 if (numThreads>1) // multi threaded processing
10023 {
10024 struct DocContext
10025 {
10026 DocContext(ClassDefMutable *cdm_,const OutputList &ol_)
10027 : cdm(cdm_), ol(ol_) {}
10028 ClassDefMutable *cdm;
10029 OutputList ol;
10030 };
10031 ThreadPool threadPool(numThreads);
10032 std::vector< std::future< std::shared_ptr<DocContext> > > results;
10033 // for each class in the namespace...
10034 for (const auto &cd : classList)
10035 {
10037 if (cdm)
10038 {
10039 auto ctx = std::make_shared<DocContext>(cdm,*g_outputList);
10040 auto processFile = [ctx]()
10041 {
10042 if ( ( ctx->cdm->isLinkableInProject() &&
10043 !ctx->cdm->isImplicitTemplateInstance()
10044 ) // skip external references, anonymous compounds and
10045 // template instances and nested classes
10046 && !ctx->cdm->isHidden() && !ctx->cdm->isEmbeddedInOuterScope()
10047 )
10048 {
10049 msg("Generating docs for compound {}...\n",ctx->cdm->displayName());
10050 ctx->cdm->writeDocumentation(ctx->ol);
10051 ctx->cdm->writeMemberList(ctx->ol);
10052 }
10053 ctx->cdm->writeDocumentationForInnerClasses(ctx->ol);
10054 return ctx;
10055 };
10056 results.emplace_back(threadPool.queue(processFile));
10057 }
10058 }
10059 // wait for the results
10060 for (auto &f : results)
10061 {
10062 auto ctx = f.get();
10063 }
10064 }
10065 else // single threaded processing
10066 {
10067 // for each class in the namespace...
10068 for (const auto &cd : classList)
10069 {
10071 if (cdm)
10072 {
10073 if ( ( cd->isLinkableInProject() &&
10074 !cd->isImplicitTemplateInstance()
10075 ) // skip external references, anonymous compounds and
10076 // template instances and nested classes
10077 && !cd->isHidden() && !cd->isEmbeddedInOuterScope()
10078 )
10079 {
10080 msg("Generating docs for compound {}...\n",cd->displayName());
10081
10084 }
10086 }
10087 }
10088 }
10089}
10090
10092{
10093 // for each concept in the namespace...
10094 for (const auto &cd : conceptList)
10095 {
10097 if ( cdm && cd->isLinkableInProject() && !cd->isHidden())
10098 {
10099 msg("Generating docs for concept {}...\n",cd->name());
10101 }
10102 }
10103}
10104
10106{
10107 bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
10108
10109 //writeNamespaceIndex(*g_outputList);
10110
10111 // for each namespace...
10112 for (const auto &nd : *Doxygen::namespaceLinkedMap)
10113 {
10114 if (nd->isLinkableInProject())
10115 {
10117 if (ndm)
10118 {
10119 msg("Generating docs for namespace {}\n",nd->displayName());
10121 }
10122 }
10123
10124 generateNamespaceClassDocs(nd->getClasses());
10125 if (sliceOpt)
10126 {
10127 generateNamespaceClassDocs(nd->getInterfaces());
10128 generateNamespaceClassDocs(nd->getStructs());
10129 generateNamespaceClassDocs(nd->getExceptions());
10130 }
10131 generateNamespaceConceptDocs(nd->getConcepts());
10132 }
10133}
10134
10136{
10137 std::string oldDir = Dir::currentDirPath();
10138 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
10141 {
10142 err("failed to run html help compiler on {}\n", HtmlHelp::hhpFileName);
10143 }
10144 Dir::setCurrent(oldDir);
10145}
10146
10148{
10149 QCString args = Qhp::qhpFileName + " -o \"" + Qhp::getQchFileName() + "\"";
10150 std::string oldDir = Dir::currentDirPath();
10151 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
10152
10153 QCString qhgLocation=Config_getString(QHG_LOCATION);
10154 if (Debug::isFlagSet(Debug::Qhp)) // produce info for debugging
10155 {
10156 // run qhelpgenerator -v and extract the Qt version used
10157 QCString cmd=qhgLocation+ " -v 2>&1";
10158 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
10159 FILE *f=Portable::popen(cmd,"r");
10160 if (!f)
10161 {
10162 err("could not execute {}\n",qhgLocation);
10163 }
10164 else
10165 {
10166 const size_t bufSize = 1024;
10167 char inBuf[bufSize+1];
10168 size_t numRead=fread(inBuf,1,bufSize,f);
10169 inBuf[numRead] = '\0';
10170 Debug::print(Debug::Qhp,0,"{}",inBuf);
10172
10173 int qtVersion=0;
10174 static const reg::Ex versionReg(R"(Qt (\d+)\.(\d+)\.(\d+))");
10175 reg::Match match;
10176 std::string s = inBuf;
10177 if (reg::search(inBuf,match,versionReg))
10178 {
10179 qtVersion = 10000*QCString(match[1].str()).toInt() +
10180 100*QCString(match[2].str()).toInt() +
10181 QCString(match[3].str()).toInt();
10182 }
10183 if (qtVersion>0 && (qtVersion<60000 || qtVersion >= 60205))
10184 {
10185 // dump the output of qhelpgenerator -c file.qhp
10186 // Qt<6 or Qt>=6.2.5 or higher, see https://bugreports.qt.io/browse/QTBUG-101070
10187 cmd=qhgLocation+ " -c " + Qhp::qhpFileName + " 2>&1";
10188 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
10189 f=Portable::popen(cmd,"r");
10190 if (!f)
10191 {
10192 err("could not execute {}\n",qhgLocation);
10193 }
10194 else
10195 {
10196 std::string output;
10197 while ((numRead=fread(inBuf,1,bufSize,f))>0)
10198 {
10199 inBuf[numRead] = '\0';
10200 output += inBuf;
10201 }
10203 Debug::print(Debug::Qhp,0,"{}",output);
10204 }
10205 }
10206 }
10207 }
10208
10209 if (Portable::system(qhgLocation, args, FALSE))
10210 {
10211 err("failed to run qhelpgenerator on {}\n",Qhp::qhpFileName);
10212 }
10213 Dir::setCurrent(oldDir);
10214}
10215
10216//----------------------------------------------------------------------------
10217
10219{
10220 // check dot path
10221 QCString dotPath = Config_getString(DOT_PATH);
10222 if (!dotPath.isEmpty())
10223 {
10224 FileInfo fi(dotPath.str());
10225 if (!(fi.exists() && fi.isFile()) )// not an existing user specified path + exec
10226 {
10227 dotPath = dotPath+"/dot"+Portable::commandExtension();
10228 FileInfo dp(dotPath.str());
10229 if (!dp.exists() || !dp.isFile())
10230 {
10231 warn_uncond("the dot tool could not be found as '{}'\n",dotPath);
10232 dotPath = "dot";
10233 dotPath += Portable::commandExtension();
10234 }
10235 }
10236#if defined(_WIN32) // convert slashes
10237 size_t l=dotPath.length();
10238 for (size_t i=0;i<l;i++) if (dotPath.at(i)=='/') dotPath.at(i)='\\';
10239#endif
10240 }
10241 else
10242 {
10243 dotPath = "dot";
10244 dotPath += Portable::commandExtension();
10245 }
10246 Doxygen::verifiedDotPath = dotPath;
10248}
10249
10250//----------------------------------------------------------------------------
10251
10252/*! Generate a template version of the configuration file.
10253 * If the \a shortList parameter is TRUE a configuration file without
10254 * comments will be generated.
10255 */
10256static void generateConfigFile(const QCString &configFile,bool shortList,
10257 bool updateOnly=FALSE)
10258{
10259 std::ofstream f;
10260 bool fileOpened=openOutputFile(configFile,f);
10261 bool writeToStdout=configFile=="-";
10262 if (fileOpened)
10263 {
10264 TextStream t(&f);
10265 Config::writeTemplate(t,shortList,updateOnly);
10266 if (!writeToStdout)
10267 {
10268 if (!updateOnly)
10269 {
10270 msg("\n\nConfiguration file '{}' created.\n\n",configFile);
10271 msg("Now edit the configuration file and enter\n\n");
10272 if (configFile!="Doxyfile" && configFile!="doxyfile")
10273 msg(" doxygen {}\n\n",configFile);
10274 else
10275 msg(" doxygen\n\n");
10276 msg("to generate the documentation for your project\n\n");
10277 }
10278 else
10279 {
10280 msg("\n\nConfiguration file '{}' updated.\n\n",configFile);
10281 }
10282 }
10283 }
10284 else
10285 {
10286 term("Cannot open file {} for writing\n",configFile);
10287 }
10288}
10289
10291{
10292 std::ofstream f;
10293 bool fileOpened=openOutputFile("-",f);
10294 if (fileOpened)
10295 {
10296 TextStream t(&f);
10297 Config::compareDoxyfile(t,diffList);
10298 }
10299 else
10300 {
10301 term("Cannot open stdout for writing\n");
10302 }
10303}
10304
10305//----------------------------------------------------------------------------
10306// read and parse a tag file
10307
10308static void readTagFile(const std::shared_ptr<Entry> &root,const QCString &tagLine)
10309{
10310 QCString fileName;
10311 QCString destName;
10312 int eqPos = tagLine.find('=');
10313 if (eqPos!=-1) // tag command contains a destination
10314 {
10315 fileName = tagLine.left(eqPos).stripWhiteSpace();
10316 destName = tagLine.right(tagLine.length()-eqPos-1).stripWhiteSpace();
10317 if (fileName.isEmpty() || destName.isEmpty()) return;
10318 //printf("insert tagDestination %s->%s\n",qPrint(fi.fileName()),qPrint(destName));
10319 }
10320 else
10321 {
10322 fileName = tagLine;
10323 }
10324
10325 FileInfo fi(fileName.str());
10326 if (!fi.exists() || !fi.isFile())
10327 {
10328 err("Tag file '{}' does not exist or is not a file. Skipping it...\n",fileName);
10329 return;
10330 }
10331
10332 if (Doxygen::tagFileSet.find(fi.absFilePath()) != Doxygen::tagFileSet.end()) return;
10333
10334 Doxygen::tagFileSet.emplace(fi.absFilePath());
10335
10336 if (!destName.isEmpty())
10337 {
10338 Doxygen::tagDestinationMap.emplace(fi.absFilePath(), destName.str());
10339 msg("Reading tag file '{}', location '{}'...\n",fileName,destName);
10340 }
10341 else
10342 {
10343 msg("Reading tag file '{}'...\n",fileName);
10344 }
10345
10346 parseTagFile(root,fi.absFilePath().c_str());
10347}
10348
10349//----------------------------------------------------------------------------
10351{
10352 const StringVector &latexExtraStyleSheet = Config_getList(LATEX_EXTRA_STYLESHEET);
10353 for (const auto &sheet : latexExtraStyleSheet)
10354 {
10355 std::string fileName = sheet;
10356 if (!fileName.empty())
10357 {
10358 FileInfo fi(fileName);
10359 if (!fi.exists())
10360 {
10361 err("Style sheet '{}' specified by LATEX_EXTRA_STYLESHEET does not exist!\n",fileName);
10362 }
10363 else if (fi.isDir())
10364 {
10365 err("Style sheet '{}' specified by LATEX_EXTRA_STYLESHEET is a directory, it has to be a file!\n", fileName);
10366 }
10367 else
10368 {
10369 QCString destFileName = Config_getString(LATEX_OUTPUT)+"/"+fi.fileName();
10371 {
10372 destFileName += LATEX_STYLE_EXTENSION;
10373 }
10374 copyFile(fileName, destFileName);
10375 }
10376 }
10377 }
10378}
10379
10380//----------------------------------------------------------------------------
10381static void copyStyleSheet()
10382{
10383 QCString htmlStyleSheet = Config_getString(HTML_STYLESHEET);
10384 if (!htmlStyleSheet.isEmpty())
10385 {
10386 if (!htmlStyleSheet.startsWith("http:") && !htmlStyleSheet.startsWith("https:"))
10387 {
10388 FileInfo fi(htmlStyleSheet.str());
10389 if (!fi.exists())
10390 {
10391 err("Style sheet '{}' specified by HTML_STYLESHEET does not exist!\n",htmlStyleSheet);
10392 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
10393 }
10394 else if (fi.isDir())
10395 {
10396 err("Style sheet '{}' specified by HTML_STYLESHEET is a directory, it has to be a file!\n",htmlStyleSheet);
10397 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
10398 }
10399 else
10400 {
10401 QCString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
10402 copyFile(htmlStyleSheet,destFileName);
10403 }
10404 }
10405 }
10406 const StringVector &htmlExtraStyleSheet = Config_getList(HTML_EXTRA_STYLESHEET);
10407 for (const auto &sheet : htmlExtraStyleSheet)
10408 {
10409 QCString fileName(sheet);
10410 if (!fileName.isEmpty() && !fileName.startsWith("http:") && !fileName.startsWith("https:"))
10411 {
10412 FileInfo fi(fileName.str());
10413 if (!fi.exists())
10414 {
10415 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET does not exist!\n",fileName);
10416 }
10417 else if (fi.fileName()=="doxygen.css" || fi.fileName()=="tabs.css" || fi.fileName()=="navtree.css")
10418 {
10419 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET is already a built-in stylesheet. Please use a different name\n",fi.fileName());
10420 }
10421 else if (fi.isDir())
10422 {
10423 err("Style sheet '{}' specified by HTML_EXTRA_STYLESHEET is a directory, it has to be a file!\n",fileName);
10424 }
10425 else
10426 {
10427 QCString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
10428 copyFile(fileName, destFileName);
10429 }
10430 }
10431 }
10432}
10433
10434static void copyLogo(const QCString &outputOption)
10435{
10436 QCString projectLogo = projectLogoFile();
10437 if (!projectLogo.isEmpty())
10438 {
10439 FileInfo fi(projectLogo.str());
10440 if (!fi.exists())
10441 {
10442 err("Project logo '{}' specified by PROJECT_LOGO does not exist!\n",projectLogo);
10443 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
10444 }
10445 else if (fi.isDir())
10446 {
10447 err("Project logo '{}' specified by PROJECT_LOGO is a directory, it has to be a file!\n",projectLogo);
10448 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
10449 }
10450 else
10451 {
10452 QCString destFileName = outputOption+"/"+fi.fileName();
10453 copyFile(projectLogo,destFileName);
10454 Doxygen::indexList->addImageFile(fi.fileName());
10455 }
10456 }
10457}
10458
10459static void copyIcon(const QCString &outputOption)
10460{
10461 QCString projectIcon = Config_getString(PROJECT_ICON);
10462 if (!projectIcon.isEmpty())
10463 {
10464 FileInfo fi(projectIcon.str());
10465 if (!fi.exists())
10466 {
10467 err("Project icon '{}' specified by PROJECT_ICON does not exist!\n",projectIcon);
10468 projectIcon = Config_updateString(PROJECT_ICON,""); // revert to the default
10469 }
10470 else if (fi.isDir())
10471 {
10472 err("Project icon '{}' specified by PROJECT_ICON is a directory, it has to be a file!\n",projectIcon);
10473 projectIcon = Config_updateString(PROJECT_ICON,""); // revert to the default
10474 }
10475 else
10476 {
10477 QCString destFileName = outputOption+"/"+fi.fileName();
10478 copyFile(projectIcon,destFileName);
10479 Doxygen::indexList->addImageFile(fi.fileName());
10480 }
10481 }
10482}
10483
10484static void copyExtraFiles(const StringVector &files,const QCString &filesOption,const QCString &outputOption)
10485{
10486 for (const auto &fileName : files)
10487 {
10488 if (!fileName.empty())
10489 {
10490 FileInfo fi(fileName);
10491 if (!fi.exists())
10492 {
10493 err("Extra file '{}' specified in {} does not exist!\n", fileName,filesOption);
10494 }
10495 else if (fi.isDir())
10496 {
10497 err("Extra file '{}' specified in {} is a directory, it has to be a file!\n", fileName,filesOption);
10498 }
10499 else
10500 {
10501 QCString destFileName = outputOption+"/"+fi.fileName();
10502 Doxygen::indexList->addImageFile(fi.fileName());
10503 copyFile(fileName, destFileName);
10504 }
10505 }
10506 }
10507}
10508
10509//----------------------------------------------------------------------------
10510
10512{
10513 for (const auto &fn : *Doxygen::inputNameLinkedMap)
10514 {
10515 struct FileEntry
10516 {
10517 FileEntry(const QCString &p,FileDef *fd) : path(p), fileDef(fd) {}
10518 QCString path;
10519 FileDef *fileDef;
10520 };
10521
10522 // collect the entry for which to compute the longest common prefix (LCP) of the path
10523 std::vector<FileEntry> fileEntries;
10524 for (const auto &fd : *fn)
10525 {
10526 if (!fd->isReference()) // skip external references
10527 {
10528 fileEntries.emplace_back(fd->getPath(),fd.get());
10529 }
10530 }
10531
10532 size_t size = fileEntries.size();
10533
10534 if (size==1) // name if unique, so diskname is simply the name
10535 {
10536 FileDef *fd = fileEntries[0].fileDef;
10537 fd->setDiskName(fn->fileName());
10538 }
10539 else if (size>1) // multiple occurrences of the same file name
10540 {
10541 // sort the array
10542 std::stable_sort(fileEntries.begin(),
10543 fileEntries.end(),
10544 [](const FileEntry &fe1,const FileEntry &fe2)
10545 { return qstricmp_sort(fe1.path,fe2.path)<0; }
10546 );
10547
10548 // since the entries are sorted, the common prefix of the whole array is same
10549 // as the common prefix between the first and last entry
10550 const FileEntry &first = fileEntries[0];
10551 const FileEntry &last = fileEntries[size-1];
10552 int first_path_size = static_cast<int>(first.path.size())-1; // -1 to skip trailing slash
10553 int last_path_size = static_cast<int>(last.path.size())-1; // -1 to skip trailing slash
10554 int j=0;
10555 int i=0;
10556 for (i=0;i<first_path_size && i<last_path_size;i++)
10557 {
10558 if (first.path[i]=='/') j=i;
10559 if (first.path[i]!=last.path[i]) break;
10560 }
10561 if (i==first_path_size && i<last_path_size && last.path[i]=='/')
10562 {
10563 // case first='some/path' and last='some/path/more' => match is 'some/path'
10564 j=first_path_size;
10565 }
10566 else if (i==last_path_size && i<first_path_size && first.path[i]=='/')
10567 {
10568 // case first='some/path/more' and last='some/path' => match is 'some/path'
10569 j=last_path_size;
10570 }
10571
10572 // add non-common part of the path to the name
10573 for (auto &fileEntry : fileEntries)
10574 {
10575 QCString prefix = fileEntry.path.right(fileEntry.path.length()-j-1);
10576 fileEntry.fileDef->setName(prefix+fn->fileName());
10577 //printf("!!!!!!!! non unique disk name=%s:%s\n",qPrint(prefix),fn->fileName());
10578 fileEntry.fileDef->setDiskName(prefix+fn->fileName());
10579 }
10580 }
10581 }
10582}
10583
10584
10585
10586//----------------------------------------------------------------------------
10587
10588static std::unique_ptr<OutlineParserInterface> getParserForFile(const QCString &fn)
10589{
10590 QCString fileName=fn;
10591 QCString extension;
10592 int sep = fileName.findRev('/');
10593 int ei = fileName.findRev('.');
10594 if (ei!=-1 && (sep==-1 || ei>sep)) // matches dir/file.ext but not dir.1/file
10595 {
10596 extension=fileName.right(fileName.length()-ei);
10597 }
10598 else
10599 {
10600 extension = ".no_extension";
10601 }
10602
10603 return Doxygen::parserManager->getOutlineParser(extension);
10604}
10605
10606static std::shared_ptr<Entry> parseFile(OutlineParserInterface &parser,
10607 FileDef *fd,const QCString &fn,
10608 ClangTUParser *clangParser,bool newTU)
10609{
10610 QCString fileName=fn;
10611 AUTO_TRACE("fileName={}",fileName);
10612 QCString extension;
10613 int ei = fileName.findRev('.');
10614 if (ei!=-1)
10615 {
10616 extension=fileName.right(fileName.length()-ei);
10617 }
10618 else
10619 {
10620 extension = ".no_extension";
10621 }
10622
10623 FileInfo fi(fileName.str());
10624 std::string preBuf;
10625
10626 if (Config_getBool(ENABLE_PREPROCESSING) &&
10627 parser.needsPreprocessing(extension))
10628 {
10629 Preprocessor preprocessor;
10630 const StringVector &includePath = Config_getList(INCLUDE_PATH);
10631 for (const auto &s : includePath)
10632 {
10633 std::string absPath = FileInfo(s).absFilePath();
10634 preprocessor.addSearchDir(absPath);
10635 }
10636 std::string inBuf;
10637 msg("Preprocessing {}...\n",fn);
10638 readInputFile(fileName,inBuf);
10639 addTerminalCharIfMissing(inBuf,'\n');
10640 preprocessor.processFile(fileName,inBuf,preBuf);
10641 }
10642 else // no preprocessing
10643 {
10644 msg("Reading {}...\n",fn);
10645 readInputFile(fileName,preBuf);
10646 addTerminalCharIfMissing(preBuf,'\n');
10647 }
10648
10649 std::string convBuf;
10650 convBuf.reserve(preBuf.size()+1024);
10651
10652 // convert multi-line C++ comments to C style comments
10653 convertCppComments(preBuf,convBuf,fileName.str());
10654
10655 std::shared_ptr<Entry> fileRoot = std::make_shared<Entry>();
10656 // use language parse to parse the file
10657 if (clangParser)
10658 {
10659 if (newTU) clangParser->parse();
10660 clangParser->switchToFile(fd);
10661 }
10662 parser.parseInput(fileName,convBuf.data(),fileRoot,clangParser);
10663 fileRoot->setFileDef(fd);
10664 return fileRoot;
10665}
10666
10667//! parse the list of input files
10668static void parseFilesMultiThreading(const std::shared_ptr<Entry> &root)
10669{
10670 AUTO_TRACE();
10671#if USE_LIBCLANG
10673 {
10674 StringUnorderedSet processedFiles;
10675
10676 // create a dictionary with files to process
10677 StringUnorderedSet filesToProcess;
10678 for (const auto &s : g_inputFiles)
10679 {
10680 filesToProcess.insert(s);
10681 }
10682
10683 std::mutex processedFilesLock;
10684 // process source files (and their include dependencies)
10685 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10686 msg("Processing input using {} threads.\n",numThreads);
10687 ThreadPool threadPool(numThreads);
10688 using FutureType = std::vector< std::shared_ptr<Entry> >;
10689 std::vector< std::future< FutureType > > results;
10690 for (const auto &s : g_inputFiles)
10691 {
10692 bool ambig = false;
10693 QCString qs = s;
10695 ASSERT(fd!=nullptr);
10696 if (fd->isSource() && !fd->isReference() && fd->getLanguage()==SrcLangExt::Cpp) // this is a source file
10697 {
10698 // lambda representing the work to executed by a thread
10699 auto processFile = [qs,&filesToProcess,&processedFilesLock,&processedFiles]() {
10700 bool ambig_l = false;
10701 std::vector< std::shared_ptr<Entry> > roots;
10703 auto clangParser = ClangParser::instance()->createTUParser(fd_l);
10704 auto parser = getParserForFile(qs);
10705 auto fileRoot { parseFile(*parser.get(),fd_l,qs,clangParser.get(),true) };
10706 roots.push_back(fileRoot);
10707
10708 // Now process any include files in the same translation unit
10709 // first. When libclang is used this is much more efficient.
10710 for (auto incFile : clangParser->filesInSameTU())
10711 {
10712 QCString qincFile = incFile;
10713 if (filesToProcess.find(incFile)!=filesToProcess.end())
10714 {
10715 bool needsToBeProcessed = false;
10716 {
10717 std::lock_guard<std::mutex> lock(processedFilesLock);
10718 needsToBeProcessed = processedFiles.find(incFile)==processedFiles.end();
10719 if (needsToBeProcessed) processedFiles.insert(incFile);
10720 }
10721 if (qincFile!=qs && needsToBeProcessed)
10722 {
10723 FileDef *ifd=findFileDef(Doxygen::inputNameLinkedMap,qincFile,ambig_l);
10724 if (ifd && !ifd->isReference())
10725 {
10726 //printf(" Processing %s in same translation unit as %s\n",incFile,qPrint(s));
10727 fileRoot = parseFile(*parser.get(),ifd,qincFile,clangParser.get(),false);
10728 roots.push_back(fileRoot);
10729 }
10730 }
10731 }
10732 }
10733 return roots;
10734 };
10735 // dispatch the work and collect the future results
10736 results.emplace_back(threadPool.queue(processFile));
10737 }
10738 }
10739 // synchronize with the Entry result lists produced and add them to the root
10740 for (auto &f : results)
10741 {
10742 auto l = f.get();
10743 for (auto &e : l)
10744 {
10745 root->moveToSubEntryAndKeep(e);
10746 }
10747 }
10748 // process remaining files
10749 results.clear();
10750 for (const auto &s : g_inputFiles)
10751 {
10752 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
10753 {
10754 // lambda representing the work to executed by a thread
10755 auto processFile = [s]() {
10756 bool ambig = false;
10757 QCString qs = s;
10758 std::vector< std::shared_ptr<Entry> > roots;
10760 auto parser { getParserForFile(qs) };
10761 bool useClang = getLanguageFromFileName(qs)==SrcLangExt::Cpp;
10762 if (useClang)
10763 {
10764 auto clangParser = ClangParser::instance()->createTUParser(fd);
10765 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
10766 roots.push_back(fileRoot);
10767 }
10768 else
10769 {
10770 auto fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10771 roots.push_back(fileRoot);
10772 }
10773 return roots;
10774 };
10775 results.emplace_back(threadPool.queue(processFile));
10776 }
10777 }
10778 // synchronize with the Entry result lists produced and add them to the root
10779 for (auto &f : results)
10780 {
10781 auto l = f.get();
10782 for (auto &e : l)
10783 {
10784 root->moveToSubEntryAndKeep(e);
10785 }
10786 }
10787 }
10788 else // normal processing
10789#endif
10790 {
10791 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
10792 msg("Processing input using {} threads.\n",numThreads);
10793 ThreadPool threadPool(numThreads);
10794 using FutureType = std::shared_ptr<Entry>;
10795 std::vector< std::future< FutureType > > results;
10796 for (const auto &s : g_inputFiles)
10797 {
10798 // lambda representing the work to executed by a thread
10799 auto processFile = [s]() {
10800 bool ambig = false;
10801 QCString qs = s;
10803 auto parser = getParserForFile(qs);
10804 auto fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10805 return fileRoot;
10806 };
10807 // dispatch the work and collect the future results
10808 results.emplace_back(threadPool.queue(processFile));
10809 }
10810 // synchronize with the Entry results produced and add them to the root
10811 for (auto &f : results)
10812 {
10813 root->moveToSubEntryAndKeep(f.get());
10814 }
10815 }
10816}
10817
10818//! parse the list of input files
10819static void parseFilesSingleThreading(const std::shared_ptr<Entry> &root)
10820{
10821 AUTO_TRACE();
10822#if USE_LIBCLANG
10824 {
10825 StringUnorderedSet processedFiles;
10826
10827 // create a dictionary with files to process
10828 StringUnorderedSet filesToProcess;
10829 for (const auto &s : g_inputFiles)
10830 {
10831 filesToProcess.insert(s);
10832 }
10833
10834 // process source files (and their include dependencies)
10835 for (const auto &s : g_inputFiles)
10836 {
10837 bool ambig = false;
10838 QCString qs =s;
10840 ASSERT(fd!=nullptr);
10841 if (fd->isSource() && !fd->isReference() && getLanguageFromFileName(qs)==SrcLangExt::Cpp) // this is a source file
10842 {
10843 auto clangParser = ClangParser::instance()->createTUParser(fd);
10844 auto parser { getParserForFile(qs) };
10845 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
10846 root->moveToSubEntryAndKeep(fileRoot);
10847 processedFiles.insert(s);
10848
10849 // Now process any include files in the same translation unit
10850 // first. When libclang is used this is much more efficient.
10851 for (auto incFile : clangParser->filesInSameTU())
10852 {
10853 //printf(" file %s\n",qPrint(incFile));
10854 if (filesToProcess.find(incFile)!=filesToProcess.end() && // file need to be processed
10855 processedFiles.find(incFile)==processedFiles.end()) // and is not processed already
10856 {
10858 if (ifd && !ifd->isReference())
10859 {
10860 //printf(" Processing %s in same translation unit as %s\n",qPrint(incFile),qPrint(qs));
10861 fileRoot = parseFile(*parser.get(),ifd,incFile,clangParser.get(),false);
10862 root->moveToSubEntryAndKeep(fileRoot);
10863 processedFiles.insert(incFile);
10864 }
10865 }
10866 }
10867 }
10868 }
10869 // process remaining files
10870 for (const auto &s : g_inputFiles)
10871 {
10872 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
10873 {
10874 bool ambig = false;
10875 QCString qs = s;
10877 if (getLanguageFromFileName(qs)==SrcLangExt::Cpp) // not yet processed
10878 {
10879 auto clangParser = ClangParser::instance()->createTUParser(fd);
10880 auto parser { getParserForFile(qs) };
10881 auto fileRoot = parseFile(*parser.get(),fd,qs,clangParser.get(),true);
10882 root->moveToSubEntryAndKeep(fileRoot);
10883 }
10884 else
10885 {
10886 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(qs) };
10887 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10888 root->moveToSubEntryAndKeep(fileRoot);
10889 }
10890 processedFiles.insert(s);
10891 }
10892 }
10893 }
10894 else // normal processing
10895#endif
10896 {
10897 for (const auto &s : g_inputFiles)
10898 {
10899 bool ambig = false;
10900 QCString qs = s;
10902 ASSERT(fd!=nullptr);
10903 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(qs) };
10904 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,qs,nullptr,true);
10905 root->moveToSubEntryAndKeep(std::move(fileRoot));
10906 }
10907 }
10908}
10909
10910// resolves a path that may include symlinks, if a recursive symlink is
10911// found an empty string is returned.
10912static std::string resolveSymlink(const std::string &path)
10913{
10914 int sepPos=0;
10915 int oldPos=0;
10916 StringUnorderedSet nonSymlinks;
10917 StringUnorderedSet known;
10918 QCString result(path);
10919 QCString oldPrefix = "/";
10920 do
10921 {
10922#if defined(_WIN32)
10923 // UNC path, skip server and share name
10924 if (sepPos==0 && (result.startsWith("//") || result.startsWith("\\\\")))
10925 sepPos = result.find('/',2);
10926 if (sepPos!=-1)
10927 sepPos = result.find('/',sepPos+1);
10928#else
10929 sepPos = result.find('/',sepPos+1);
10930#endif
10931 QCString prefix = sepPos==-1 ? result : result.left(sepPos);
10932 if (nonSymlinks.find(prefix.str())==nonSymlinks.end())
10933 {
10934 FileInfo fi(prefix.str());
10935 if (fi.isSymLink())
10936 {
10937 QCString target = fi.readLink();
10938 bool isRelative = FileInfo(target.str()).isRelative();
10939 if (isRelative)
10940 {
10941 target = Dir::cleanDirPath(oldPrefix.str()+"/"+target.str());
10942 }
10943 if (sepPos!=-1)
10944 {
10945 if (fi.isDir() && target.length()>0 && target.at(target.length()-1)!='/')
10946 {
10947 target+='/';
10948 }
10949 target+=result.mid(sepPos);
10950 }
10951 result = Dir::cleanDirPath(target.str());
10952 if (known.find(result.str())!=known.end()) return std::string(); // recursive symlink!
10953 known.insert(result.str());
10954 if (isRelative)
10955 {
10956 sepPos = oldPos;
10957 }
10958 else // link to absolute path
10959 {
10960 sepPos = 0;
10961 oldPrefix = "/";
10962 }
10963 }
10964 else
10965 {
10966 nonSymlinks.insert(prefix.str());
10967 oldPrefix = prefix;
10968 }
10969 oldPos = sepPos;
10970 }
10971 }
10972 while (sepPos!=-1);
10973 return Dir::cleanDirPath(result.str());
10974}
10975
10977
10978//----------------------------------------------------------------------------
10979// Read all files matching at least one pattern in 'patList' in the
10980// directory represented by 'fi'.
10981// The directory is read iff the recursiveFlag is set.
10982// The contents of all files is append to the input string
10983
10984static void readDir(FileInfo *fi,
10985 FileNameLinkedMap *fnMap,
10986 StringUnorderedSet *exclSet,
10987 const StringVector *patList,
10988 const StringVector *exclPatList,
10989 StringVector *resultList,
10990 StringUnorderedSet *resultSet,
10991 bool errorIfNotExist,
10992 bool recursive,
10993 StringUnorderedSet *killSet,
10994 StringUnorderedSet *paths
10995 )
10996{
10997 std::string dirName = fi->absFilePath();
10998 if (paths && !dirName.empty())
10999 {
11000 paths->insert(dirName);
11001 }
11002 //printf("%s isSymLink()=%d\n",qPrint(dirName),fi->isSymLink());
11003 if (fi->isSymLink())
11004 {
11005 dirName = resolveSymlink(dirName);
11006 if (dirName.empty())
11007 {
11008 //printf("RECURSIVE SYMLINK: %s\n",qPrint(dirName));
11009 return; // recursive symlink
11010 }
11011 }
11012
11013 if (g_pathsVisited.find(dirName)!=g_pathsVisited.end())
11014 {
11015 //printf("PATH ALREADY VISITED: %s\n",qPrint(dirName));
11016 return; // already visited path
11017 }
11018 g_pathsVisited.insert(dirName);
11019
11020 Dir dir(dirName);
11021 msg("Searching for files in directory {}\n", fi->absFilePath());
11022 //printf("killSet=%p count=%d\n",killSet,killSet ? (int)killSet->count() : -1);
11023
11024 StringVector dirResultList;
11025
11026 for (const auto &dirEntry : dir.iterator())
11027 {
11028 FileInfo cfi(dirEntry.path());
11029 auto checkPatterns = [&]() -> bool
11030 {
11031 return (patList==nullptr || patternMatch(cfi,*patList)) &&
11032 (exclPatList==nullptr || !patternMatch(cfi,*exclPatList)) &&
11033 (killSet==nullptr || killSet->find(cfi.absFilePath())==killSet->end());
11034 };
11035
11036 if (exclSet==nullptr || exclSet->find(cfi.absFilePath())==exclSet->end())
11037 { // file should not be excluded
11038 //printf("killSet->find(%s)\n",qPrint(cfi->absFilePath()));
11039 if (Config_getBool(EXCLUDE_SYMLINKS) && cfi.isSymLink())
11040 {
11041 }
11042 else if (!cfi.exists() || !cfi.isReadable())
11043 {
11044 if (errorIfNotExist && checkPatterns())
11045 {
11046 warn_uncond("source '{}' is not a readable file or directory... skipping.\n",cfi.absFilePath());
11047 }
11048 }
11049 else if (cfi.isFile() && checkPatterns())
11050 {
11051 std::string name=cfi.fileName();
11052 std::string path=cfi.dirPath()+"/";
11053 std::string fullName=path+name;
11054 if (fnMap)
11055 {
11056 auto fd = createFileDef(path,name);
11057 FileName *fn=nullptr;
11058 if (!name.empty())
11059 {
11060 fn = fnMap->add(name,fullName);
11061 fn->push_back(std::move(fd));
11062 }
11063 }
11064 dirResultList.push_back(fullName);
11065 if (resultSet) resultSet->insert(fullName);
11066 if (killSet) killSet->insert(fullName);
11067 }
11068 else if (recursive &&
11069 cfi.isDir() &&
11070 (exclPatList==nullptr || !patternMatch(cfi,*exclPatList)) &&
11071 cfi.fileName().at(0)!='.') // skip "." ".." and ".dir"
11072 {
11073 FileInfo acfi(cfi.absFilePath());
11074 readDir(&acfi,fnMap,exclSet,
11075 patList,exclPatList,&dirResultList,resultSet,errorIfNotExist,
11076 recursive,killSet,paths);
11077 }
11078 }
11079 }
11080 if (resultList && !dirResultList.empty())
11081 {
11082 // sort the resulting list to make the order platform independent.
11083 std::stable_sort(dirResultList.begin(),
11084 dirResultList.end(),
11085 [](const auto &f1,const auto &f2) { return qstricmp_sort(f1.c_str(),f2.c_str())<0; });
11086
11087 // append the sorted results to resultList
11088 resultList->insert(resultList->end(), dirResultList.begin(), dirResultList.end());
11089 }
11090}
11091
11092
11093//----------------------------------------------------------------------------
11094// read a file or all files in a directory and append their contents to the
11095// input string. The names of the files are appended to the 'fiList' list.
11096
11098 FileNameLinkedMap *fnMap,
11099 StringUnorderedSet *exclSet,
11100 const StringVector *patList,
11101 const StringVector *exclPatList,
11102 StringVector *resultList,
11103 StringUnorderedSet *resultSet,
11104 bool recursive,
11105 bool errorIfNotExist,
11106 StringUnorderedSet *killSet,
11107 StringUnorderedSet *paths
11108 )
11109{
11110 //printf("killSet count=%d\n",killSet ? (int)killSet->size() : -1);
11111 // strip trailing slashes
11112 if (s.isEmpty()) return;
11113
11114 g_pathsVisited.clear();
11115
11116 FileInfo fi(s.str());
11117 //printf("readFileOrDirectory(%s)\n",s);
11118 {
11119 if (exclSet==nullptr || exclSet->find(fi.absFilePath())==exclSet->end())
11120 {
11121 if (Config_getBool(EXCLUDE_SYMLINKS) && fi.isSymLink())
11122 {
11123 }
11124 else if (!fi.exists() || !fi.isReadable())
11125 {
11126 if (errorIfNotExist)
11127 {
11128 warn_uncond("source '{}' is not a readable file or directory... skipping.\n",s);
11129 }
11130 }
11131 else if (fi.isFile())
11132 {
11133 std::string dirPath = fi.dirPath(true);
11134 std::string filePath = fi.absFilePath();
11135 if (paths && !dirPath.empty())
11136 {
11137 paths->insert(dirPath);
11138 }
11139 //printf("killSet.find(%s)=%d\n",qPrint(fi.absFilePath()),killSet.find(fi.absFilePath())!=killSet.end());
11140 if (killSet==nullptr || killSet->find(filePath)==killSet->end())
11141 {
11142 std::string name=fi.fileName();
11143 if (fnMap)
11144 {
11145 auto fd = createFileDef(dirPath+"/",name);
11146 if (!name.empty())
11147 {
11148 FileName *fn = fnMap->add(name,filePath);
11149 fn->push_back(std::move(fd));
11150 }
11151 }
11152 if (resultList || resultSet)
11153 {
11154 if (resultList) resultList->push_back(filePath);
11155 if (resultSet) resultSet->insert(filePath);
11156 }
11157
11158 if (killSet) killSet->insert(fi.absFilePath());
11159 }
11160 }
11161 else if (fi.isDir()) // readable dir
11162 {
11163 readDir(&fi,fnMap,exclSet,patList,
11164 exclPatList,resultList,resultSet,errorIfNotExist,
11165 recursive,killSet,paths);
11166 }
11167 }
11168 }
11169}
11170
11171//----------------------------------------------------------------------------
11172
11174{
11175 QCString anchor;
11177 {
11178 MemberDef *md = toMemberDef(d);
11179 anchor=":"+md->anchor();
11180 }
11181 QCString scope;
11182 QCString fn = d->getOutputFileBase();
11185 {
11186 scope = fn;
11187 }
11188 t << "REPLACE INTO symbols (symbol_id,scope_id,name,file,line) VALUES('"
11189 << fn+anchor << "','"
11190 << scope << "','"
11191 << d->name() << "','"
11192 << d->getDefFileName() << "','"
11193 << d->getDefLine()
11194 << "');\n";
11195}
11196
11197static void dumpSymbolMap()
11198{
11199 std::ofstream f = Portable::openOutputStream("symbols.sql");
11200 if (f.is_open())
11201 {
11202 TextStream t(&f);
11203 for (const auto &[name,symList] : *Doxygen::symbolMap)
11204 {
11205 for (const auto &def : symList)
11206 {
11207 dumpSymbol(t,def);
11208 }
11209 }
11210 }
11211}
11212
11213// print developer options of Doxygen
11214static void devUsage()
11215{
11217 msg("Developer parameters:\n");
11218 msg(" -m dump symbol map\n");
11219 msg(" -b making messages output unbuffered\n");
11220 msg(" -c <file> process input file as a comment block and produce HTML output\n");
11221#if ENABLE_TRACING
11222 msg(" -t [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout)\n");
11223 msg(" -t_time [<file|stdout|stderr>] trace debug info to file, stdout, or stderr (default file stdout),\n"
11224 " and include time and thread information\n");
11225#endif
11226 msg(" -d <level> enable a debug level, such as (multiple invocations of -d are possible):\n");
11228}
11229
11230
11231//----------------------------------------------------------------------------
11232// print the version of Doxygen
11233
11234static void version(const bool extended)
11235{
11237 QCString versionString = getFullVersion();
11238 msg("{}\n",versionString);
11239 if (extended)
11240 {
11241 QCString extVers;
11242 if (!extVers.isEmpty()) extVers+= ", ";
11243 extVers += "sqlite3 ";
11244 extVers += sqlite3_libversion();
11245#if USE_LIBCLANG
11246 if (!extVers.isEmpty()) extVers+= ", ";
11247 extVers += "clang support ";
11248 extVers += CLANG_VERSION_STRING;
11249#endif
11250 if (!extVers.isEmpty())
11251 {
11252 int lastComma = extVers.findRev(',');
11253 if (lastComma != -1) extVers = extVers.replace(lastComma,1," and");
11254 msg(" with {}.\n",extVers);
11255 }
11256 }
11257}
11258
11259//----------------------------------------------------------------------------
11260// print the usage of Doxygen
11261
11262static void usage(const QCString &name,const QCString &versionString)
11263{
11265 msg("Doxygen version {0}\nCopyright Dimitri van Heesch 1997-2025\n\n"
11266 "You can use Doxygen in a number of ways:\n\n"
11267 "1) Use Doxygen to generate a template configuration file*:\n"
11268 " {1} [-s] -g [configName]\n\n"
11269 "2) Use Doxygen to update an old configuration file*:\n"
11270 " {1} [-s] -u [configName]\n\n"
11271 "3) Use Doxygen to generate documentation using an existing "
11272 "configuration file*:\n"
11273 " {1} [configName]\n\n"
11274 "4) Use Doxygen to generate a template file controlling the layout of the\n"
11275 " generated documentation:\n"
11276 " {1} -l [layoutFileName]\n\n"
11277 " In case layoutFileName is omitted DoxygenLayout.xml will be used as filename.\n"
11278 " If - is used for layoutFileName Doxygen will write to standard output.\n\n"
11279 "5) Use Doxygen to generate a template style sheet file for RTF, HTML or Latex.\n"
11280 " RTF: {1} -w rtf styleSheetFile\n"
11281 " HTML: {1}-w html headerFile footerFile styleSheetFile [configFile]\n"
11282 " LaTeX: {1} -w latex headerFile footerFile styleSheetFile [configFile]\n\n"
11283 "6) Use Doxygen to generate a rtf extensions file\n"
11284 " {1} -e rtf extensionsFile\n\n"
11285 " If - is used for extensionsFile Doxygen will write to standard output.\n\n"
11286 "7) Use Doxygen to compare the used configuration file with the template configuration file\n"
11287 " {1} -x [configFile]\n\n"
11288 " Use Doxygen to compare the used configuration file with the template configuration file\n"
11289 " without replacing the environment variables or CMake type replacement variables\n"
11290 " {1} -x_noenv [configFile]\n\n"
11291 "8) Use Doxygen to show a list of built-in emojis.\n"
11292 " {1} -f emoji outputFileName\n\n"
11293 " If - is used for outputFileName Doxygen will write to standard output.\n\n"
11294 "*) If -s is specified the comments of the configuration items in the config file will be omitted.\n"
11295 " If configName is omitted 'Doxyfile' will be used as a default.\n"
11296 " If - is used for configFile Doxygen will write / read the configuration to /from standard output / input.\n\n"
11297 "If -q is used for a Doxygen documentation run, Doxygen will see this as if QUIET=YES has been set.\n\n"
11298 "-v print version string, -V print extended version information\n"
11299 "-h,-? prints usage help information\n"
11300 "{1} -d prints additional usage flags for debugging purposes\n",versionString,name);
11301}
11302
11303//----------------------------------------------------------------------------
11304// read the argument of option 'c' from the comment argument list and
11305// update the option index 'optInd'.
11306
11307static const char *getArg(int argc,char **argv,int &optInd)
11308{
11309 char *s=nullptr;
11310 if (qstrlen(&argv[optInd][2])>0)
11311 s=&argv[optInd][2];
11312 else if (optInd+1<argc && argv[optInd+1][0]!='-')
11313 s=argv[++optInd];
11314 return s;
11315}
11316
11317//----------------------------------------------------------------------------
11318
11319/** @brief /dev/null outline parser */
11321{
11322 public:
11323 void parseInput(const QCString &/* file */, const char * /* buf */,const std::shared_ptr<Entry> &, ClangTUParser*) override {}
11324 bool needsPreprocessing(const QCString &) const override { return FALSE; }
11325 void parsePrototype(const QCString &) override {}
11326};
11327
11328
11329template<class T> std::function< std::unique_ptr<T>() > make_parser_factory()
11330{
11331 return []() { return std::make_unique<T>(); };
11332}
11333
11335{
11336 initResources();
11337 QCString lang = Portable::getenv("LC_ALL");
11338 if (!lang.isEmpty()) Portable::setenv("LANG",lang);
11339 std::setlocale(LC_ALL,"");
11340 std::setlocale(LC_CTYPE,"C"); // to get isspace(0xA0)==0, needed for UTF-8
11341 std::setlocale(LC_NUMERIC,"C");
11342
11344
11368
11369 // register any additional parsers here...
11370
11372
11373#if USE_LIBCLANG
11375#endif
11384 Doxygen::pageLinkedMap = new PageLinkedMap; // all doc pages
11385 Doxygen::exampleLinkedMap = new PageLinkedMap; // all examples
11386 //Doxygen::tagDestinationDict.setAutoDelete(TRUE);
11388
11389 // initialization of these globals depends on
11390 // configuration switches so we need to postpone these
11391 Doxygen::globalScope = nullptr;
11400
11401}
11402
11434
11435static int computeIdealCacheParam(size_t v)
11436{
11437 //printf("computeIdealCacheParam(v=%u)\n",v);
11438
11439 int r=0;
11440 while (v!=0) v>>=1,r++;
11441 // r = log2(v)
11442
11443 // convert to a valid cache size value
11444 return std::max(0,std::min(r-16,9));
11445}
11446
11447void readConfiguration(int argc, char **argv)
11448{
11449 QCString versionString = getFullVersion();
11450
11451 // helper that calls \a func to write to file \a fileName via a TextStream
11452 auto writeFile = [](const char *fileName,std::function<void(TextStream&)> func) -> bool
11453 {
11454 std::ofstream f;
11455 if (openOutputFile(fileName,f))
11456 {
11457 TextStream t(&f);
11458 func(t);
11459 return true;
11460 }
11461 return false;
11462 };
11463
11464
11465 /**************************************************************************
11466 * Handle arguments *
11467 **************************************************************************/
11468
11469 int optInd=1;
11470 QCString configName;
11471 QCString traceName;
11472 bool genConfig=false;
11473 bool shortList=false;
11474 bool traceTiming=false;
11476 bool updateConfig=false;
11477 bool quiet = false;
11478 while (optInd<argc && argv[optInd][0]=='-' &&
11479 (isalpha(argv[optInd][1]) || argv[optInd][1]=='?' ||
11480 argv[optInd][1]=='-')
11481 )
11482 {
11483 switch(argv[optInd][1])
11484 {
11485 case 'g':
11486 {
11487 genConfig=TRUE;
11488 }
11489 break;
11490 case 'l':
11491 {
11492 QCString layoutName;
11493 if (optInd+1>=argc)
11494 {
11495 layoutName="DoxygenLayout.xml";
11496 }
11497 else
11498 {
11499 layoutName=argv[optInd+1];
11500 }
11501 writeDefaultLayoutFile(layoutName);
11503 exit(0);
11504 }
11505 break;
11506 case 'c':
11507 if (optInd+1>=argc) // no file name given
11508 {
11509 msg("option \"-c\" is missing the file name to read\n");
11510 devUsage();
11512 exit(1);
11513 }
11514 else
11515 {
11516 g_commentFileName=argv[optInd+1];
11517 optInd++;
11518 }
11519 g_singleComment=true;
11520 quiet=true;
11521 break;
11522 case 'd':
11523 {
11524 QCString debugLabel=getArg(argc,argv,optInd);
11525 if (debugLabel.isEmpty())
11526 {
11527 devUsage();
11529 exit(0);
11530 }
11531 int retVal = Debug::setFlagStr(debugLabel);
11532 if (!retVal)
11533 {
11534 msg("option \"-d\" has unknown debug specifier: \"{}\".\n",debugLabel);
11535 devUsage();
11537 exit(1);
11538 }
11539 }
11540 break;
11541 case 't':
11542 {
11543#if ENABLE_TRACING
11544 if (!strcmp(argv[optInd]+1,"t_time"))
11545 {
11546 traceTiming = true;
11547 }
11548 else if (!strcmp(argv[optInd]+1,"t"))
11549 {
11550 traceTiming = false;
11551 }
11552 else
11553 {
11554 err("option should be \"-t\" or \"-t_time\", found: \"{}\".\n",argv[optInd]);
11556 exit(1);
11557 }
11558 if (optInd+1>=argc || argv[optInd+1][0] == '-') // no file name given
11559 {
11560 traceName="stdout";
11561 }
11562 else
11563 {
11564 traceName=argv[optInd+1];
11565 optInd++;
11566 }
11567#else
11568 err("support for option \"-t\" has not been compiled in (use a debug build or a release build with tracing enabled).\n");
11570 exit(1);
11571#endif
11572 }
11573 break;
11574 case 'x':
11575 if (!strcmp(argv[optInd]+1,"x_noenv")) diffList=Config::CompareMode::CompressedNoEnv;
11576 else if (!strcmp(argv[optInd]+1,"x")) diffList=Config::CompareMode::Compressed;
11577 else
11578 {
11579 err("option should be \"-x\" or \"-x_noenv\", found: \"{}\".\n",argv[optInd]);
11581 exit(1);
11582 }
11583 break;
11584 case 's':
11585 shortList=TRUE;
11586 break;
11587 case 'u':
11588 updateConfig=TRUE;
11589 break;
11590 case 'e':
11591 {
11592 QCString formatName=getArg(argc,argv,optInd);
11593 if (formatName.isEmpty())
11594 {
11595 err("option \"-e\" is missing format specifier rtf.\n");
11597 exit(1);
11598 }
11599 if (qstricmp(formatName.data(),"rtf")==0)
11600 {
11601 if (optInd+1>=argc)
11602 {
11603 err("option \"-e rtf\" is missing an extensions file name\n");
11605 exit(1);
11606 }
11607 writeFile(argv[optInd+1],RTFGenerator::writeExtensionsFile);
11609 exit(0);
11610 }
11611 err("option \"-e\" has invalid format specifier.\n");
11613 exit(1);
11614 }
11615 break;
11616 case 'f':
11617 {
11618 QCString listName=getArg(argc,argv,optInd);
11619 if (listName.isEmpty())
11620 {
11621 err("option \"-f\" is missing list specifier.\n");
11623 exit(1);
11624 }
11625 if (qstricmp(listName.data(),"emoji")==0)
11626 {
11627 if (optInd+1>=argc)
11628 {
11629 err("option \"-f emoji\" is missing an output file name\n");
11631 exit(1);
11632 }
11633 writeFile(argv[optInd+1],[](TextStream &t) { EmojiEntityMapper::instance().writeEmojiFile(t); });
11635 exit(0);
11636 }
11637 err("option \"-f\" has invalid list specifier.\n");
11639 exit(1);
11640 }
11641 break;
11642 case 'w':
11643 {
11644 QCString formatName=getArg(argc,argv,optInd);
11645 if (formatName.isEmpty())
11646 {
11647 err("option \"-w\" is missing format specifier rtf, html or latex\n");
11649 exit(1);
11650 }
11651 if (qstricmp(formatName.data(),"rtf")==0)
11652 {
11653 if (optInd+1>=argc)
11654 {
11655 err("option \"-w rtf\" is missing a style sheet file name\n");
11657 exit(1);
11658 }
11659 if (!writeFile(argv[optInd+1],RTFGenerator::writeStyleSheetFile))
11660 {
11661 err("error opening RTF style sheet file {}!\n",argv[optInd+1]);
11663 exit(1);
11664 }
11666 exit(0);
11667 }
11668 else if (qstricmp(formatName.data(),"html")==0)
11669 {
11670 Config::init();
11671 if (optInd+4<argc || FileInfo("Doxyfile").exists() || FileInfo("doxyfile").exists())
11672 // explicit config file mentioned or default found on disk
11673 {
11674 QCString df = optInd+4<argc ? argv[optInd+4] : (FileInfo("Doxyfile").exists() ? QCString("Doxyfile") : QCString("doxyfile"));
11675 if (!Config::parse(df)) // parse the config file
11676 {
11677 err("error opening or reading configuration file {}!\n",argv[optInd+4]);
11679 exit(1);
11680 }
11681 }
11682 if (optInd+3>=argc)
11683 {
11684 err("option \"-w html\" does not have enough arguments\n");
11686 exit(1);
11687 }
11691 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11692 writeFile(argv[optInd+1],[&](TextStream &t) { HtmlGenerator::writeHeaderFile(t,argv[optInd+3]); });
11693 writeFile(argv[optInd+2],HtmlGenerator::writeFooterFile);
11694 writeFile(argv[optInd+3],HtmlGenerator::writeStyleSheetFile);
11696 exit(0);
11697 }
11698 else if (qstricmp(formatName.data(),"latex")==0)
11699 {
11700 Config::init();
11701 if (optInd+4<argc || FileInfo("Doxyfile").exists() || FileInfo("doxyfile").exists())
11702 {
11703 QCString df = optInd+4<argc ? argv[optInd+4] : (FileInfo("Doxyfile").exists() ? QCString("Doxyfile") : QCString("doxyfile"));
11704 if (!Config::parse(df))
11705 {
11706 err("error opening or reading configuration file {}!\n",argv[optInd+4]);
11708 exit(1);
11709 }
11710 }
11711 if (optInd+3>=argc)
11712 {
11713 err("option \"-w latex\" does not have enough arguments\n");
11715 exit(1);
11716 }
11720 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11721 writeFile(argv[optInd+1],LatexGenerator::writeHeaderFile);
11722 writeFile(argv[optInd+2],LatexGenerator::writeFooterFile);
11723 writeFile(argv[optInd+3],LatexGenerator::writeStyleSheetFile);
11725 exit(0);
11726 }
11727 else
11728 {
11729 err("Illegal format specifier \"{}\": should be one of rtf, html or latex\n",formatName);
11731 exit(1);
11732 }
11733 }
11734 break;
11735 case 'm':
11737 break;
11738 case 'v':
11739 version(false);
11741 exit(0);
11742 break;
11743 case 'V':
11744 version(true);
11746 exit(0);
11747 break;
11748 case '-':
11749 if (qstrcmp(&argv[optInd][2],"help")==0)
11750 {
11751 usage(argv[0],versionString);
11752 exit(0);
11753 }
11754 else if (qstrcmp(&argv[optInd][2],"version")==0)
11755 {
11756 version(false);
11758 exit(0);
11759 }
11760 else if ((qstrcmp(&argv[optInd][2],"Version")==0) ||
11761 (qstrcmp(&argv[optInd][2],"VERSION")==0))
11762 {
11763 version(true);
11765 exit(0);
11766 }
11767 else
11768 {
11769 err("Unknown option \"-{}\"\n",&argv[optInd][1]);
11770 usage(argv[0],versionString);
11771 exit(1);
11772 }
11773 break;
11774 case 'b':
11775 setvbuf(stdout,nullptr,_IONBF,0);
11776 break;
11777 case 'q':
11778 quiet = true;
11779 break;
11780 case 'h':
11781 case '?':
11782 usage(argv[0],versionString);
11783 exit(0);
11784 break;
11785 default:
11786 err("Unknown option \"-{:c}\"\n",argv[optInd][1]);
11787 usage(argv[0],versionString);
11788 exit(1);
11789 }
11790 optInd++;
11791 }
11792
11793 /**************************************************************************
11794 * Parse or generate the config file *
11795 **************************************************************************/
11796
11797 initTracing(traceName.data(),traceTiming);
11798 TRACE("Doxygen version used: {}",getFullVersion());
11799 Config::init();
11800
11801 FileInfo configFileInfo1("Doxyfile"),configFileInfo2("doxyfile");
11802 if (optInd>=argc)
11803 {
11804 if (configFileInfo1.exists())
11805 {
11806 configName="Doxyfile";
11807 }
11808 else if (configFileInfo2.exists())
11809 {
11810 configName="doxyfile";
11811 }
11812 else if (genConfig)
11813 {
11814 configName="Doxyfile";
11815 }
11816 else
11817 {
11818 err("Doxyfile not found and no input file specified!\n");
11819 usage(argv[0],versionString);
11820 exit(1);
11821 }
11822 }
11823 else
11824 {
11825 FileInfo fi(argv[optInd]);
11826 if (fi.exists() || qstrcmp(argv[optInd],"-")==0 || genConfig)
11827 {
11828 configName=argv[optInd];
11829 }
11830 else
11831 {
11832 err("configuration file {} not found!\n",argv[optInd]);
11833 usage(argv[0],versionString);
11834 exit(1);
11835 }
11836 }
11837
11838 if (genConfig)
11839 {
11840 generateConfigFile(configName,shortList);
11842 exit(0);
11843 }
11844
11845 if (!Config::parse(configName,updateConfig,diffList))
11846 {
11847 err("could not open or read configuration file {}!\n",configName);
11849 exit(1);
11850 }
11851
11852 if (diffList!=Config::CompareMode::Full)
11853 {
11855 compareDoxyfile(diffList);
11857 exit(0);
11858 }
11859
11860 if (updateConfig)
11861 {
11863 generateConfigFile(configName,shortList,TRUE);
11865 exit(0);
11866 }
11867
11868 /* Perlmod wants to know the path to the config file.*/
11869 FileInfo configFileInfo(configName.str());
11870 setPerlModDoxyfile(configFileInfo.absFilePath());
11871
11872 /* handle -q option */
11873 if (quiet) Config_updateBool(QUIET,TRUE);
11874}
11875
11876/** check and resolve config options */
11878{
11879 AUTO_TRACE();
11880
11885}
11886
11887/** adjust globals that depend on configuration settings. */
11889{
11890 AUTO_TRACE();
11891 Doxygen::globalNamespaceDef = createNamespaceDef("<globalScope>",1,1,"<globalScope>");
11901
11902 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11903
11904 /* Set the global html file extension. */
11905 Doxygen::htmlFileExtension = Config_getString(HTML_FILE_EXTENSION);
11906
11907
11909 Config_getBool(CALLER_GRAPH) ||
11910 Config_getBool(REFERENCES_RELATION) ||
11911 Config_getBool(REFERENCED_BY_RELATION);
11912
11913 /**************************************************************************
11914 * Add custom extension mappings
11915 **************************************************************************/
11916
11917 const StringVector &extMaps = Config_getList(EXTENSION_MAPPING);
11918 for (const auto &mapping : extMaps)
11919 {
11920 QCString mapStr = mapping;
11921 int i=mapStr.find('=');
11922 if (i==-1)
11923 {
11924 continue;
11925 }
11926 else
11927 {
11928 QCString ext = mapStr.left(i).stripWhiteSpace().lower();
11929 QCString language = mapStr.mid(i+1).stripWhiteSpace().lower();
11930 if (ext.isEmpty() || language.isEmpty())
11931 {
11932 continue;
11933 }
11934
11935 if (!updateLanguageMapping(ext,language))
11936 {
11937 err("Failed to map file extension '{}' to unsupported language '{}'.\n"
11938 "Check the EXTENSION_MAPPING setting in the config file.\n",
11939 ext,language);
11940 }
11941 else
11942 {
11943 msg("Adding custom extension mapping: '{}' will be treated as language '{}'\n",
11944 ext,language);
11945 }
11946 }
11947 }
11948 // create input file exncodings
11949
11950 // check INPUT_ENCODING
11951 void *cd = portable_iconv_open("UTF-8",Config_getString(INPUT_ENCODING).data());
11952 if (cd==reinterpret_cast<void *>(-1))
11953 {
11954 term("unsupported character conversion: '{}'->'UTF-8': {}\n"
11955 "Check the 'INPUT_ENCODING' setting in the config file!\n",
11956 Config_getString(INPUT_ENCODING),strerror(errno));
11957 }
11958 else
11959 {
11961 }
11962
11963 // check and split INPUT_FILE_ENCODING
11964 const StringVector &fileEncod = Config_getList(INPUT_FILE_ENCODING);
11965 for (const auto &mapping : fileEncod)
11966 {
11967 QCString mapStr = mapping;
11968 int i=mapStr.find('=');
11969 if (i==-1)
11970 {
11971 continue;
11972 }
11973 else
11974 {
11975 QCString pattern = mapStr.left(i).stripWhiteSpace().lower();
11976 QCString encoding = mapStr.mid(i+1).stripWhiteSpace().lower();
11977 if (pattern.isEmpty() || encoding.isEmpty())
11978 {
11979 continue;
11980 }
11981 cd = portable_iconv_open("UTF-8",encoding.data());
11982 if (cd==reinterpret_cast<void *>(-1))
11983 {
11984 term("unsupported character conversion: '{}'->'UTF-8': {}\n"
11985 "Check the 'INPUT_FILE_ENCODING' setting in the config file!\n",
11986 encoding,strerror(errno));
11987 }
11988 else
11989 {
11991 }
11992
11993 Doxygen::inputFileEncodingList.emplace_back(pattern, encoding);
11994 }
11995 }
11996
11997 // add predefined macro name to a dictionary
11998 const StringVector &expandAsDefinedList =Config_getList(EXPAND_AS_DEFINED);
11999 for (const auto &s : expandAsDefinedList)
12000 {
12002 }
12003
12004 // read aliases and store them in a dictionary
12005 readAliases();
12006
12007 // store number of spaces in a tab into Doxygen::spaces
12008 int tabSize = Config_getInt(TAB_SIZE);
12009 Doxygen::spaces.resize(tabSize);
12010 for (int sp=0; sp<tabSize; sp++) Doxygen::spaces.at(sp)=' ';
12011 Doxygen::spaces.at(tabSize)='\0';
12012}
12013
12014#ifdef HAS_SIGNALS
12015static void stopDoxygen(int)
12016{
12017 signal(SIGINT,SIG_DFL); // Re-register signal handler for default action
12018 Dir thisDir;
12019 msg("Cleaning up...\n");
12020 if (!Doxygen::filterDBFileName.isEmpty())
12021 {
12022 thisDir.remove(Doxygen::filterDBFileName.str());
12023 }
12024 killpg(0,SIGINT);
12026 exitTracing();
12027 exit(1);
12028}
12029#endif
12030
12031static void writeTagFile()
12032{
12033 QCString generateTagFile = Config_getString(GENERATE_TAGFILE);
12034 if (generateTagFile.isEmpty()) return;
12035
12036 std::ofstream f = Portable::openOutputStream(generateTagFile);
12037 if (!f.is_open())
12038 {
12039 err("cannot open tag file {} for writing\n", generateTagFile);
12040 return;
12041 }
12042 TextStream tagFile(&f);
12043 tagFile << "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>\n";
12044 tagFile << "<tagfile doxygen_version=\"" << getDoxygenVersion() << "\"";
12045 std::string gitVersion = getGitVersion();
12046 if (!gitVersion.empty())
12047 {
12048 tagFile << " doxygen_gitid=\"" << gitVersion << "\"";
12049 }
12050 tagFile << ">\n";
12051
12052 // for each file
12053 for (const auto &fn : *Doxygen::inputNameLinkedMap)
12054 {
12055 for (const auto &fd : *fn)
12056 {
12057 if (fd->isLinkableInProject()) fd->writeTagFile(tagFile);
12058 }
12059 }
12060 // for each class
12061 for (const auto &cd : *Doxygen::classLinkedMap)
12062 {
12063 ClassDefMutable *cdm = toClassDefMutable(cd.get());
12064 if (cdm && cdm->isLinkableInProject())
12065 {
12066 cdm->writeTagFile(tagFile);
12067 }
12068 }
12069 // for each concept
12070 for (const auto &cd : *Doxygen::conceptLinkedMap)
12071 {
12072 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
12073 if (cdm && cdm->isLinkableInProject())
12074 {
12075 cdm->writeTagFile(tagFile);
12076 }
12077 }
12078 // for each namespace
12079 for (const auto &nd : *Doxygen::namespaceLinkedMap)
12080 {
12082 if (ndm && nd->isLinkableInProject())
12083 {
12084 ndm->writeTagFile(tagFile);
12085 }
12086 }
12087 // for each group
12088 for (const auto &gd : *Doxygen::groupLinkedMap)
12089 {
12090 if (gd->isLinkableInProject()) gd->writeTagFile(tagFile);
12091 }
12092 // for each module
12093 for (const auto &mod : ModuleManager::instance().modules())
12094 {
12095 if (mod->isLinkableInProject()) mod->writeTagFile(tagFile);
12096 }
12097 // for each page
12098 for (const auto &pd : *Doxygen::pageLinkedMap)
12099 {
12100 if (pd->isLinkableInProject()) pd->writeTagFile(tagFile);
12101 }
12102 // for each directory
12103 for (const auto &dd : *Doxygen::dirLinkedMap)
12104 {
12105 if (dd->isLinkableInProject()) dd->writeTagFile(tagFile);
12106 }
12107 if (Doxygen::mainPage) Doxygen::mainPage->writeTagFile(tagFile);
12108
12109 tagFile << "</tagfile>\n";
12110}
12111
12112static void exitDoxygen() noexcept
12113{
12114 if (!g_successfulRun) // premature exit
12115 {
12116 Dir thisDir;
12117 msg("Exiting...\n");
12118 if (!Doxygen::filterDBFileName.isEmpty())
12119 {
12120 thisDir.remove(Doxygen::filterDBFileName.str());
12121 }
12122 }
12123}
12124
12125static QCString createOutputDirectory(const QCString &baseDirName,
12126 const QCString &formatDirName,
12127 const char *defaultDirName)
12128{
12129 QCString result = formatDirName;
12130 if (result.isEmpty())
12131 {
12132 result = baseDirName + defaultDirName;
12133 }
12134 else if (formatDirName[0]!='/' && (formatDirName.length()==1 || formatDirName[1]!=':'))
12135 {
12136 result.prepend(baseDirName+"/");
12137 }
12138 Dir formatDir(result.str());
12139 if (!formatDir.exists() && !formatDir.mkdir(result.str()))
12140 {
12141 term("Could not create output directory {}\n", result);
12142 }
12143 return result;
12144}
12145
12147{
12148 StringUnorderedSet killSet;
12149
12150 const StringVector &exclPatterns = Config_getList(EXCLUDE_PATTERNS);
12151 bool alwaysRecursive = Config_getBool(RECURSIVE);
12152 StringUnorderedSet excludeNameSet;
12153
12154 // gather names of all files in the include path
12155 g_s.begin("Searching for include files...\n");
12156 killSet.clear();
12157 const StringVector &includePathList = Config_getList(INCLUDE_PATH);
12158 for (const auto &s : includePathList)
12159 {
12160 size_t plSize = Config_getList(INCLUDE_FILE_PATTERNS).size();
12161 const StringVector &pl = plSize==0 ? Config_getList(FILE_PATTERNS) :
12162 Config_getList(INCLUDE_FILE_PATTERNS);
12163 readFileOrDirectory(s, // s
12165 nullptr, // exclSet
12166 &pl, // patList
12167 &exclPatterns, // exclPatList
12168 nullptr, // resultList
12169 nullptr, // resultSet
12170 false, // INCLUDE_PATH isn't recursive
12171 TRUE, // errorIfNotExist
12172 &killSet); // killSet
12173 }
12174 g_s.end();
12175
12176 g_s.begin("Searching for example files...\n");
12177 killSet.clear();
12178 const StringVector &examplePathList = Config_getList(EXAMPLE_PATH);
12179 for (const auto &s : examplePathList)
12180 {
12181 readFileOrDirectory(s, // s
12183 nullptr, // exclSet
12184 &Config_getList(EXAMPLE_PATTERNS), // patList
12185 nullptr, // exclPatList
12186 nullptr, // resultList
12187 nullptr, // resultSet
12188 (alwaysRecursive || Config_getBool(EXAMPLE_RECURSIVE)), // recursive
12189 TRUE, // errorIfNotExist
12190 &killSet); // killSet
12191 }
12192 g_s.end();
12193
12194 g_s.begin("Searching for images...\n");
12195 killSet.clear();
12196 const StringVector &imagePathList=Config_getList(IMAGE_PATH);
12197 for (const auto &s : imagePathList)
12198 {
12199 readFileOrDirectory(s, // s
12201 nullptr, // exclSet
12202 nullptr, // patList
12203 nullptr, // exclPatList
12204 nullptr, // resultList
12205 nullptr, // resultSet
12206 alwaysRecursive, // recursive
12207 TRUE, // errorIfNotExist
12208 &killSet); // killSet
12209 }
12210 g_s.end();
12211
12212 g_s.begin("Searching for dot files...\n");
12213 killSet.clear();
12214 const StringVector &dotFileList=Config_getList(DOTFILE_DIRS);
12215 for (const auto &s : dotFileList)
12216 {
12217 readFileOrDirectory(s, // s
12219 nullptr, // exclSet
12220 nullptr, // patList
12221 nullptr, // exclPatList
12222 nullptr, // resultList
12223 nullptr, // resultSet
12224 alwaysRecursive, // recursive
12225 TRUE, // errorIfNotExist
12226 &killSet); // killSet
12227 }
12228 g_s.end();
12229
12230 g_s.begin("Searching for msc files...\n");
12231 killSet.clear();
12232 const StringVector &mscFileList=Config_getList(MSCFILE_DIRS);
12233 for (const auto &s : mscFileList)
12234 {
12235 readFileOrDirectory(s, // s
12237 nullptr, // exclSet
12238 nullptr, // patList
12239 nullptr, // exclPatList
12240 nullptr, // resultList
12241 nullptr, // resultSet
12242 alwaysRecursive, // recursive
12243 TRUE, // errorIfNotExist
12244 &killSet); // killSet
12245 }
12246 g_s.end();
12247
12248 g_s.begin("Searching for dia files...\n");
12249 killSet.clear();
12250 const StringVector &diaFileList=Config_getList(DIAFILE_DIRS);
12251 for (const auto &s : diaFileList)
12252 {
12253 readFileOrDirectory(s, // s
12255 nullptr, // exclSet
12256 nullptr, // patList
12257 nullptr, // exclPatList
12258 nullptr, // resultList
12259 nullptr, // resultSet
12260 alwaysRecursive, // recursive
12261 TRUE, // errorIfNotExist
12262 &killSet); // killSet
12263 }
12264 g_s.end();
12265
12266 g_s.begin("Searching for plantuml files...\n");
12267 killSet.clear();
12268 const StringVector &plantUmlFileList=Config_getList(PLANTUMLFILE_DIRS);
12269 for (const auto &s : plantUmlFileList)
12270 {
12271 readFileOrDirectory(s, // s
12273 nullptr, // exclSet
12274 nullptr, // patList
12275 nullptr, // exclPatList
12276 nullptr, // resultList
12277 nullptr, // resultSet
12278 alwaysRecursive, // recursive
12279 TRUE, // errorIfNotExist
12280 &killSet); // killSet
12281 }
12282 g_s.end();
12283
12284 g_s.begin("Searching for files to exclude\n");
12285 const StringVector &excludeList = Config_getList(EXCLUDE);
12286 for (const auto &s : excludeList)
12287 {
12288 readFileOrDirectory(s, // s
12289 nullptr, // fnDict
12290 nullptr, // exclSet
12291 &Config_getList(FILE_PATTERNS), // patList
12292 nullptr, // exclPatList
12293 nullptr, // resultList
12294 &excludeNameSet, // resultSet
12295 alwaysRecursive, // recursive
12296 FALSE); // errorIfNotExist
12297 }
12298 g_s.end();
12299
12300 /**************************************************************************
12301 * Determine Input Files *
12302 **************************************************************************/
12303
12304 g_s.begin("Searching INPUT for files to process...\n");
12305 killSet.clear();
12306 Doxygen::inputPaths.clear();
12307 const StringVector &inputList=Config_getList(INPUT);
12308 for (const auto &s : inputList)
12309 {
12310 QCString path = s;
12311 size_t l = path.length();
12312 if (l>0)
12313 {
12314 // strip trailing slashes
12315 if (path.at(l-1)=='\\' || path.at(l-1)=='/') path=path.left(l-1);
12316
12318 path, // s
12320 &excludeNameSet, // exclSet
12321 &Config_getList(FILE_PATTERNS), // patList
12322 &exclPatterns, // exclPatList
12323 &g_inputFiles, // resultList
12324 nullptr, // resultSet
12325 alwaysRecursive, // recursive
12326 TRUE, // errorIfNotExist
12327 &killSet, // killSet
12328 &Doxygen::inputPaths); // paths
12329 }
12330 }
12331
12332 // Sort the FileDef objects by full path to get a predictable ordering over multiple runs
12333 std::stable_sort(Doxygen::inputNameLinkedMap->begin(),
12335 [](const auto &f1,const auto &f2)
12336 {
12337 return qstricmp_sort(f1->fullName(),f2->fullName())<0;
12338 });
12339 for (auto &fileName : *Doxygen::inputNameLinkedMap)
12340 {
12341 if (fileName->size()>1)
12342 {
12343 std::stable_sort(fileName->begin(),fileName->end(),[](const auto &f1,const auto &f2)
12344 {
12345 return qstricmp_sort(f1->absFilePath(),f2->absFilePath())<0;
12346 });
12347 }
12348 }
12349 if (Doxygen::inputNameLinkedMap->empty())
12350 {
12351 warn_uncond("No files to be processed, please check your settings, in particular INPUT, FILE_PATTERNS, and RECURSIVE\n");
12352 }
12353 g_s.end();
12354}
12355
12356
12358{
12359 if (Config_getBool(MARKDOWN_SUPPORT))
12360 {
12361 QCString mdfileAsMainPage = Config_getString(USE_MDFILE_AS_MAINPAGE);
12362 if (mdfileAsMainPage.isEmpty()) return;
12363 FileInfo fi(mdfileAsMainPage.data());
12364 if (!fi.exists())
12365 {
12366 warn_uncond("Specified markdown mainpage '{}' does not exist\n",mdfileAsMainPage);
12367 return;
12368 }
12369 bool ambig = false;
12370 if (findFileDef(Doxygen::inputNameLinkedMap,fi.absFilePath(),ambig)==nullptr)
12371 {
12372 warn_uncond("Specified markdown mainpage '{}' has not been defined as input file\n",mdfileAsMainPage);
12373 return;
12374 }
12375 }
12376}
12377
12379{
12380 AUTO_TRACE();
12381 std::atexit(exitDoxygen);
12382
12383 Portable::correctPath(Config_getList(EXTERNAL_TOOL_PATH));
12384
12385#if USE_LIBCLANG
12386 Doxygen::clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING);
12387#endif
12388
12389 // we would like to show the versionString earlier, but we first have to handle the configuration file
12390 // to know the value of the QUIET setting.
12391 QCString versionString = getFullVersion();
12392 msg("Doxygen version used: {}\n",versionString);
12393
12395
12396 /**************************************************************************
12397 * Make sure the output directory exists
12398 **************************************************************************/
12399 QCString outputDirectory = Config_getString(OUTPUT_DIRECTORY);
12400 if (!g_singleComment)
12401 {
12402 if (outputDirectory.isEmpty())
12403 {
12404 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,Dir::currentDirPath());
12405 }
12406 else
12407 {
12408 Dir dir(outputDirectory.str());
12409 if (!dir.exists())
12410 {
12412 if (!dir.mkdir(outputDirectory.str()))
12413 {
12414 term("tag OUTPUT_DIRECTORY: Output directory '{}' does not "
12415 "exist and cannot be created\n",outputDirectory);
12416 }
12417 else
12418 {
12419 msg("Notice: Output directory '{}' does not exist. "
12420 "I have created it for you.\n", outputDirectory);
12421 }
12422 dir.setPath(outputDirectory.str());
12423 }
12424 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,dir.absPath());
12425 }
12426 }
12427 AUTO_TRACE_ADD("outputDirectory={}",outputDirectory);
12428
12429 /**************************************************************************
12430 * Initialize global lists and dictionaries
12431 **************************************************************************/
12432
12433 // also scale lookup cache with SYMBOL_CACHE_SIZE
12434 int cacheSize = Config_getInt(LOOKUP_CACHE_SIZE);
12435 if (cacheSize<0) cacheSize=0;
12436 if (cacheSize>9) cacheSize=9;
12437 uint32_t lookupSize = 65536 << cacheSize;
12440
12441#ifdef HAS_SIGNALS
12442 signal(SIGINT, stopDoxygen);
12443#endif
12444
12445 uint32_t pid = Portable::pid();
12446 Doxygen::filterDBFileName.sprintf("doxygen_filterdb_%d.tmp",pid);
12447 Doxygen::filterDBFileName.prepend(outputDirectory+"/");
12448
12449 /**************************************************************************
12450 * Check/create output directories *
12451 **************************************************************************/
12452
12453 bool generateHtml = Config_getBool(GENERATE_HTML);
12454 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
12455 bool generateXml = Config_getBool(GENERATE_XML);
12456 bool generateLatex = Config_getBool(GENERATE_LATEX);
12457 bool generateRtf = Config_getBool(GENERATE_RTF);
12458 bool generateMan = Config_getBool(GENERATE_MAN);
12459 bool generateSql = Config_getBool(GENERATE_SQLITE3);
12460 QCString htmlOutput;
12461 QCString docbookOutput;
12462 QCString xmlOutput;
12463 QCString latexOutput;
12464 QCString rtfOutput;
12465 QCString manOutput;
12466 QCString sqlOutput;
12467
12468 if (!g_singleComment)
12469 {
12470 if (generateHtml)
12471 {
12472 htmlOutput = createOutputDirectory(outputDirectory,Config_getString(HTML_OUTPUT),"/html");
12473 Config_updateString(HTML_OUTPUT,htmlOutput);
12474
12475 QCString sitemapUrl = Config_getString(SITEMAP_URL);
12476 bool generateSitemap = !sitemapUrl.isEmpty();
12477 if (generateSitemap && !sitemapUrl.endsWith("/"))
12478 {
12479 Config_updateString(SITEMAP_URL,sitemapUrl+"/");
12480 }
12481
12482 // add HTML indexers that are enabled
12483 bool generateHtmlHelp = Config_getBool(GENERATE_HTMLHELP);
12484 bool generateEclipseHelp = Config_getBool(GENERATE_ECLIPSEHELP);
12485 bool generateQhp = Config_getBool(GENERATE_QHP);
12486 bool generateTreeView = Config_getBool(GENERATE_TREEVIEW);
12487 bool generateDocSet = Config_getBool(GENERATE_DOCSET);
12488 if (generateEclipseHelp) Doxygen::indexList->addIndex<EclipseHelp>();
12489 if (generateHtmlHelp) Doxygen::indexList->addIndex<HtmlHelp>();
12490 if (generateQhp) Doxygen::indexList->addIndex<Qhp>();
12491 if (generateSitemap) Doxygen::indexList->addIndex<Sitemap>();
12492 if (generateTreeView) Doxygen::indexList->addIndex<FTVHelp>(TRUE);
12493 if (generateDocSet) Doxygen::indexList->addIndex<DocSets>();
12494 Doxygen::indexList->addIndex<Crawlmap>();
12495 Doxygen::indexList->initialize();
12496 }
12497
12498 if (generateDocbook)
12499 {
12500 docbookOutput = createOutputDirectory(outputDirectory,Config_getString(DOCBOOK_OUTPUT),"/docbook");
12501 Config_updateString(DOCBOOK_OUTPUT,docbookOutput);
12502 }
12503
12504 if (generateXml)
12505 {
12506 xmlOutput = createOutputDirectory(outputDirectory,Config_getString(XML_OUTPUT),"/xml");
12507 Config_updateString(XML_OUTPUT,xmlOutput);
12508 }
12509
12510 if (generateLatex)
12511 {
12512 latexOutput = createOutputDirectory(outputDirectory,Config_getString(LATEX_OUTPUT), "/latex");
12513 Config_updateString(LATEX_OUTPUT,latexOutput);
12514 }
12515
12516 if (generateRtf)
12517 {
12518 rtfOutput = createOutputDirectory(outputDirectory,Config_getString(RTF_OUTPUT),"/rtf");
12519 Config_updateString(RTF_OUTPUT,rtfOutput);
12520 }
12521
12522 if (generateMan)
12523 {
12524 manOutput = createOutputDirectory(outputDirectory,Config_getString(MAN_OUTPUT),"/man");
12525 Config_updateString(MAN_OUTPUT,manOutput);
12526 }
12527
12528 if (generateSql)
12529 {
12530 sqlOutput = createOutputDirectory(outputDirectory,Config_getString(SQLITE3_OUTPUT),"/sqlite3");
12531 Config_updateString(SQLITE3_OUTPUT,sqlOutput);
12532 }
12533 }
12534
12535 if (Config_getBool(HAVE_DOT))
12536 {
12537 QCString curFontPath = Config_getString(DOT_FONTPATH);
12538 if (curFontPath.isEmpty())
12539 {
12540 Portable::getenv("DOTFONTPATH");
12541 QCString newFontPath = ".";
12542 if (!curFontPath.isEmpty())
12543 {
12544 newFontPath+=Portable::pathListSeparator();
12545 newFontPath+=curFontPath;
12546 }
12547 Portable::setenv("DOTFONTPATH",qPrint(newFontPath));
12548 }
12549 else
12550 {
12551 Portable::setenv("DOTFONTPATH",qPrint(curFontPath));
12552 }
12553 }
12554
12555 /**************************************************************************
12556 * Handle layout file *
12557 **************************************************************************/
12558
12560 QCString layoutFileName = Config_getString(LAYOUT_FILE);
12561 bool defaultLayoutUsed = FALSE;
12562 if (layoutFileName.isEmpty())
12563 {
12564 layoutFileName = Config_updateString(LAYOUT_FILE,"DoxygenLayout.xml");
12565 defaultLayoutUsed = TRUE;
12566 }
12567 AUTO_TRACE_ADD("defaultLayoutUsed={}, layoutFileName={}",defaultLayoutUsed,layoutFileName);
12568
12569 FileInfo fi(layoutFileName.str());
12570 if (fi.exists())
12571 {
12572 msg("Parsing layout file {}...\n",layoutFileName);
12573 LayoutDocManager::instance().parse(layoutFileName);
12574 }
12575 else if (!defaultLayoutUsed)
12576 {
12577 warn_uncond("failed to open layout file '{}' for reading! Using default settings.\n",layoutFileName);
12578 }
12579 printLayout();
12580
12581 /**************************************************************************
12582 * Read and preprocess input *
12583 **************************************************************************/
12584
12585 // prevent search in the output directories
12586 StringVector exclPatterns = Config_getList(EXCLUDE_PATTERNS);
12587 if (generateHtml) exclPatterns.push_back(htmlOutput.str());
12588 if (generateDocbook) exclPatterns.push_back(docbookOutput.str());
12589 if (generateXml) exclPatterns.push_back(xmlOutput.str());
12590 if (generateLatex) exclPatterns.push_back(latexOutput.str());
12591 if (generateRtf) exclPatterns.push_back(rtfOutput.str());
12592 if (generateMan) exclPatterns.push_back(manOutput.str());
12593 Config_updateList(EXCLUDE_PATTERNS,exclPatterns);
12594
12595 if (!g_singleComment)
12596 {
12598
12600 }
12601
12602 // Notice: the order of the function calls below is very important!
12603
12604 if (generateHtml && !Config_getBool(USE_MATHJAX))
12605 {
12607 }
12608 if (generateRtf)
12609 {
12611 }
12612 if (generateDocbook)
12613 {
12615 }
12616
12618
12619 /**************************************************************************
12620 * Handle Tag Files *
12621 **************************************************************************/
12622
12623 std::shared_ptr<Entry> root = std::make_shared<Entry>();
12624
12625 if (!g_singleComment)
12626 {
12627 msg("Reading and parsing tag files\n");
12628 const StringVector &tagFileList = Config_getList(TAGFILES);
12629 for (const auto &s : tagFileList)
12630 {
12631 readTagFile(root,s.c_str());
12632 }
12633 }
12634
12635 /**************************************************************************
12636 * Parse source files *
12637 **************************************************************************/
12638
12639 addSTLSupport(root);
12640
12641 g_s.begin("Parsing files\n");
12642 if (g_singleComment)
12643 {
12644 //printf("Parsing comment %s\n",qPrint(g_commentFileName));
12645 if (g_commentFileName=="-")
12646 {
12647 std::string text = fileToString(g_commentFileName).str();
12648 addTerminalCharIfMissing(text,'\n');
12649 generateHtmlForComment("stdin.md",text);
12650 }
12651 else if (FileInfo(g_commentFileName.str()).isFile())
12652 {
12653 std::string text;
12655 addTerminalCharIfMissing(text,'\n');
12657 }
12658 else
12659 {
12660 }
12662 exit(0);
12663 }
12664 else
12665 {
12666 if (Config_getInt(NUM_PROC_THREADS)==1)
12667 {
12669 }
12670 else
12671 {
12673 }
12674 }
12675 g_s.end();
12676
12677 /**************************************************************************
12678 * Gather information *
12679 **************************************************************************/
12680
12681 g_s.begin("Building macro definition list...\n");
12683 g_s.end();
12684
12685 g_s.begin("Building group list...\n");
12686 buildGroupList(root.get());
12687 organizeSubGroups(root.get());
12688 g_s.end();
12689
12690 g_s.begin("Building directory list...\n");
12692 findDirDocumentation(root.get());
12693 g_s.end();
12694
12695 g_s.begin("Building namespace list...\n");
12696 buildNamespaceList(root.get());
12697 findUsingDirectives(root.get());
12698 g_s.end();
12699
12700 g_s.begin("Building file list...\n");
12701 buildFileList(root.get());
12702 g_s.end();
12703
12704 g_s.begin("Building class list...\n");
12705 buildClassList(root.get());
12706 g_s.end();
12707
12708 g_s.begin("Building concept list...\n");
12709 buildConceptList(root.get());
12710 g_s.end();
12711
12712 // build list of using declarations here (global list)
12713 buildListOfUsingDecls(root.get());
12714 g_s.end();
12715
12716 g_s.begin("Computing nesting relations for classes...\n");
12718 g_s.end();
12719 // 1.8.2-20121111: no longer add nested classes to the group as well
12720 //distributeClassGroupRelations();
12721
12722 // calling buildClassList may result in cached relations that
12723 // become invalid after resolveClassNestingRelations(), that's why
12724 // we need to clear the cache here
12725 Doxygen::typeLookupCache->clear();
12726 // we don't need the list of using declaration anymore
12727 g_usingDeclarations.clear();
12728
12729 g_s.begin("Associating documentation with classes...\n");
12730 buildClassDocList(root.get());
12731 g_s.end();
12732
12733 g_s.begin("Associating documentation with concepts...\n");
12734 buildConceptDocList(root.get());
12736 g_s.end();
12737
12738 g_s.begin("Associating documentation with modules...\n");
12739 findModuleDocumentation(root.get());
12740 g_s.end();
12741
12742 g_s.begin("Building example list...\n");
12743 buildExampleList(root.get());
12744 g_s.end();
12745
12746 g_s.begin("Searching for enumerations...\n");
12747 findEnums(root.get());
12748 g_s.end();
12749
12750 // Since buildVarList calls isVarWithConstructor
12751 // and this calls getResolvedClass we need to process
12752 // typedefs first so the relations between classes via typedefs
12753 // are properly resolved. See bug 536385 for an example.
12754 g_s.begin("Searching for documented typedefs...\n");
12755 buildTypedefList(root.get());
12756 g_s.end();
12757
12758 if (Config_getBool(OPTIMIZE_OUTPUT_SLICE))
12759 {
12760 g_s.begin("Searching for documented sequences...\n");
12761 buildSequenceList(root.get());
12762 g_s.end();
12763
12764 g_s.begin("Searching for documented dictionaries...\n");
12765 buildDictionaryList(root.get());
12766 g_s.end();
12767 }
12768
12769 g_s.begin("Searching for members imported via using declarations...\n");
12770 // this should be after buildTypedefList in order to properly import
12771 // used typedefs
12772 findUsingDeclarations(root.get(),TRUE); // do for python packages first
12773 findUsingDeclarations(root.get(),FALSE); // then the rest
12774 g_s.end();
12775
12776 g_s.begin("Searching for included using directives...\n");
12778 g_s.end();
12779
12780 g_s.begin("Searching for documented variables...\n");
12781 buildVarList(root.get());
12782 g_s.end();
12783
12784 g_s.begin("Building interface member list...\n");
12785 buildInterfaceAndServiceList(root.get()); // UNO IDL
12786
12787 g_s.begin("Building member list...\n"); // using class info only !
12788 buildFunctionList(root.get());
12789 g_s.end();
12790
12791 g_s.begin("Searching for friends...\n");
12792 findFriends();
12793 g_s.end();
12794
12795 g_s.begin("Searching for documented defines...\n");
12796 findDefineDocumentation(root.get());
12797 g_s.end();
12798
12799 g_s.begin("Computing class inheritance relations...\n");
12800 findClassEntries(root.get());
12802 g_s.end();
12803
12804 g_s.begin("Computing class usage relations...\n");
12806 g_s.end();
12807
12808 if (Config_getBool(INLINE_SIMPLE_STRUCTS))
12809 {
12810 g_s.begin("Searching for tag less structs...\n");
12812 g_s.end();
12813 }
12814
12815 g_s.begin("Flushing cached template relations that have become invalid...\n");
12817 g_s.end();
12818
12819 g_s.begin("Warn for undocumented namespaces...\n");
12821 g_s.end();
12822
12823 g_s.begin("Computing class relations...\n");
12826 if (Config_getBool(OPTIMIZE_OUTPUT_VHDL))
12827 {
12829 }
12831 g_classEntries.clear();
12832 g_s.end();
12833
12834 g_s.begin("Add enum values to enums...\n");
12835 addEnumValuesToEnums(root.get());
12836 findEnumDocumentation(root.get());
12837 g_s.end();
12838
12839 g_s.begin("Searching for member function documentation...\n");
12840 findObjCMethodDefinitions(root.get());
12841 findMemberDocumentation(root.get()); // may introduce new members !
12842 findUsingDeclImports(root.get()); // may introduce new members !
12843 g_usingClassMap.clear();
12847 g_s.end();
12848
12849 // moved to after finding and copying documentation,
12850 // as this introduces new members see bug 722654
12851 g_s.begin("Creating members for template instances...\n");
12853 g_s.end();
12854
12855 g_s.begin("Building page list...\n");
12856 buildPageList(root.get());
12857 g_s.end();
12858
12859 g_s.begin("Search for main page...\n");
12860 findMainPage(root.get());
12861 findMainPageTagFiles(root.get());
12862 g_s.end();
12863
12864 g_s.begin("Computing page relations...\n");
12865 computePageRelations(root.get());
12867 g_s.end();
12868
12869 g_s.begin("Determining the scope of groups...\n");
12870 findGroupScope(root.get());
12871 g_s.end();
12872
12873 g_s.begin("Computing module relations...\n");
12874 auto &mm = ModuleManager::instance();
12875 mm.resolvePartitions();
12876 mm.resolveImports();
12877 mm.collectExportedSymbols();
12878 g_s.end();
12879
12880 auto memberNameComp = [](const MemberNameLinkedMap::Ptr &n1,const MemberNameLinkedMap::Ptr &n2)
12881 {
12882 return qstricmp_sort(n1->memberName().data()+getPrefixIndex(n1->memberName()),
12883 n2->memberName().data()+getPrefixIndex(n2->memberName())
12884 )<0;
12885 };
12886
12887 auto classComp = [](const ClassLinkedMap::Ptr &c1,const ClassLinkedMap::Ptr &c2)
12888 {
12889 if (Config_getBool(SORT_BY_SCOPE_NAME))
12890 {
12891 return qstricmp_sort(c1->name(), c2->name())<0;
12892 }
12893 else
12894 {
12895 int i = qstricmp_sort(c1->className(), c2->className());
12896 return i==0 ? qstricmp_sort(c1->name(), c2->name())<0 : i<0;
12897 }
12898 };
12899
12900 auto namespaceComp = [](const NamespaceLinkedMap::Ptr &n1,const NamespaceLinkedMap::Ptr &n2)
12901 {
12902 return qstricmp_sort(n1->name(),n2->name())<0;
12903 };
12904
12905 auto conceptComp = [](const ConceptLinkedMap::Ptr &c1,const ConceptLinkedMap::Ptr &c2)
12906 {
12907 return qstricmp_sort(c1->name(),c2->name())<0;
12908 };
12909
12910 g_s.begin("Sorting lists...\n");
12911 std::stable_sort(Doxygen::memberNameLinkedMap->begin(),
12913 memberNameComp);
12914 std::stable_sort(Doxygen::functionNameLinkedMap->begin(),
12916 memberNameComp);
12917 std::stable_sort(Doxygen::hiddenClassLinkedMap->begin(),
12919 classComp);
12920 std::stable_sort(Doxygen::classLinkedMap->begin(),
12922 classComp);
12923 std::stable_sort(Doxygen::conceptLinkedMap->begin(),
12925 conceptComp);
12926 std::stable_sort(Doxygen::namespaceLinkedMap->begin(),
12928 namespaceComp);
12929 g_s.end();
12930
12931 g_s.begin("Determining which enums are documented\n");
12933 g_s.end();
12934
12935 g_s.begin("Computing member relations...\n");
12938 g_s.end();
12939
12940 g_s.begin("Building full member lists recursively...\n");
12942 g_s.end();
12943
12944 g_s.begin("Adding members to member groups.\n");
12946 g_s.end();
12947
12948 if (Config_getBool(DISTRIBUTE_GROUP_DOC))
12949 {
12950 g_s.begin("Distributing member group documentation.\n");
12952 g_s.end();
12953 }
12954
12955 g_s.begin("Computing member references...\n");
12957 g_s.end();
12958
12959 if (Config_getBool(INHERIT_DOCS))
12960 {
12961 g_s.begin("Inheriting documentation...\n");
12963 g_s.end();
12964 }
12965
12966
12967 // compute the shortest possible names of all files
12968 // without losing the uniqueness of the file names.
12969 g_s.begin("Generating disk names...\n");
12971 g_s.end();
12972
12973 g_s.begin("Adding source references...\n");
12975 g_s.end();
12976
12977 g_s.begin("Adding xrefitems...\n");
12980 g_s.end();
12981
12982 g_s.begin("Sorting member lists...\n");
12984 g_s.end();
12985
12986 g_s.begin("Setting anonymous enum type...\n");
12988 g_s.end();
12989
12990 g_s.begin("Computing dependencies between directories...\n");
12992 g_s.end();
12993
12994 g_s.begin("Generating citations page...\n");
12996 g_s.end();
12997
12998 g_s.begin("Counting members...\n");
12999 countMembers();
13000 g_s.end();
13001
13002 g_s.begin("Counting data structures...\n");
13004 g_s.end();
13005
13006 g_s.begin("Resolving user defined references...\n");
13008 g_s.end();
13009
13010 g_s.begin("Finding anchors and sections in the documentation...\n");
13012 g_s.end();
13013
13014 g_s.begin("Transferring function references...\n");
13016 g_s.end();
13017
13018 g_s.begin("Combining using relations...\n");
13020 g_s.end();
13021
13023 g_s.begin("Adding members to index pages...\n");
13025 addToIndices();
13026 g_s.end();
13027
13028 g_s.begin("Correcting members for VHDL...\n");
13030 g_s.end();
13031
13032 g_s.begin("Computing tooltip texts...\n");
13034 g_s.end();
13035
13036 if (Config_getBool(SORT_GROUP_NAMES))
13037 {
13038 std::stable_sort(Doxygen::groupLinkedMap->begin(),
13040 [](const auto &g1,const auto &g2)
13041 { return g1->groupTitle() < g2->groupTitle(); });
13042
13043 for (const auto &gd : *Doxygen::groupLinkedMap)
13044 {
13045 gd->sortSubGroups();
13046 }
13047 }
13048
13049 printNavTree(root.get(),0);
13051}
13052
13054{
13055 AUTO_TRACE();
13056 /**************************************************************************
13057 * Initialize output generators *
13058 **************************************************************************/
13059
13060 /// add extra languages for which we can only produce syntax highlighted code
13062
13063 //// dump all symbols
13064 if (g_dumpSymbolMap)
13065 {
13066 dumpSymbolMap();
13067 exit(0);
13068 }
13069
13070 bool generateHtml = Config_getBool(GENERATE_HTML);
13071 bool generateLatex = Config_getBool(GENERATE_LATEX);
13072 bool generateMan = Config_getBool(GENERATE_MAN);
13073 bool generateRtf = Config_getBool(GENERATE_RTF);
13074 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
13075
13076
13078 if (generateHtml)
13079 {
13083 }
13084 if (generateLatex)
13085 {
13088 }
13089 if (generateDocbook)
13090 {
13093 }
13094 if (generateMan)
13095 {
13096 g_outputList->add<ManGenerator>();
13098 }
13099 if (generateRtf)
13100 {
13101 g_outputList->add<RTFGenerator>();
13103 }
13104 if (Config_getBool(USE_HTAGS))
13105 {
13107 QCString htmldir = Config_getString(HTML_OUTPUT);
13108 if (!Htags::execute(htmldir))
13109 err("USE_HTAGS is YES but htags(1) failed. \n");
13110 else if (!Htags::loadFilemap(htmldir))
13111 err("htags(1) ended normally but failed to load the filemap. \n");
13112 }
13113
13114 /**************************************************************************
13115 * Generate documentation *
13116 **************************************************************************/
13117
13118 g_s.begin("Generating style sheet...\n");
13119 //printf("writing style info\n");
13120 g_outputList->writeStyleInfo(0); // write first part
13121 g_s.end();
13122
13123 bool searchEngine = Config_getBool(SEARCHENGINE);
13124 bool serverBasedSearch = Config_getBool(SERVER_BASED_SEARCH);
13125
13126 g_s.begin("Generating search indices...\n");
13127 if (searchEngine && !serverBasedSearch && generateHtml)
13128 {
13130 }
13131
13132 // generate search indices (need to do this before writing other HTML
13133 // pages as these contain a drop down menu with options depending on
13134 // what categories we find in this function.
13135 if (generateHtml && searchEngine)
13136 {
13137 QCString searchDirName = Config_getString(HTML_OUTPUT)+"/search";
13138 Dir searchDir(searchDirName.str());
13139 if (!searchDir.exists() && !searchDir.mkdir(searchDirName.str()))
13140 {
13141 term("Could not create search results directory '{}' $PWD='{}'\n",
13142 searchDirName,Dir::currentDirPath());
13143 }
13144 HtmlGenerator::writeSearchData(searchDirName);
13145 if (!serverBasedSearch) // client side search index
13146 {
13148 }
13149 }
13150 g_s.end();
13151
13152 // copy static stuff
13153 if (generateHtml)
13154 {
13156 copyLogo(Config_getString(HTML_OUTPUT));
13157 copyIcon(Config_getString(HTML_OUTPUT));
13158 copyExtraFiles(Config_getList(HTML_EXTRA_FILES),"HTML_EXTRA_FILES",Config_getString(HTML_OUTPUT));
13159 }
13160 if (generateLatex)
13161 {
13163 copyLogo(Config_getString(LATEX_OUTPUT));
13164 copyIcon(Config_getString(LATEX_OUTPUT));
13165 copyExtraFiles(Config_getList(LATEX_EXTRA_FILES),"LATEX_EXTRA_FILES",Config_getString(LATEX_OUTPUT));
13166 }
13167 if (generateDocbook)
13168 {
13169 copyLogo(Config_getString(DOCBOOK_OUTPUT));
13170 copyIcon(Config_getString(DOCBOOK_OUTPUT));
13171 }
13172 if (generateRtf)
13173 {
13174 copyLogo(Config_getString(RTF_OUTPUT));
13175 copyIcon(Config_getString(RTF_OUTPUT));
13176 copyExtraFiles(Config_getList(RTF_EXTRA_FILES),"RTF_EXTRA_FILES",Config_getString(RTF_OUTPUT));
13177 }
13178
13180 if (fm.hasFormulas() && generateHtml
13181 && !Config_getBool(USE_MATHJAX))
13182 {
13183 g_s.begin("Generating images for formulas in HTML...\n");
13184 fm.generateImages(Config_getString(HTML_OUTPUT), Config_getEnum(HTML_FORMULA_FORMAT)==HTML_FORMULA_FORMAT_t::svg ?
13186 g_s.end();
13187 }
13188 if (fm.hasFormulas() && generateRtf)
13189 {
13190 g_s.begin("Generating images for formulas in RTF...\n");
13192 g_s.end();
13193 }
13194
13195 if (fm.hasFormulas() && generateDocbook)
13196 {
13197 g_s.begin("Generating images for formulas in Docbook...\n");
13199 g_s.end();
13200 }
13201
13202 g_s.begin("Generating example documentation...\n");
13204 g_s.end();
13205
13206 g_s.begin("Generating file sources...\n");
13208 g_s.end();
13209
13210 g_s.begin("Generating file documentation...\n");
13212 g_s.end();
13213
13214 g_s.begin("Generating page documentation...\n");
13216 g_s.end();
13217
13218 g_s.begin("Generating group documentation...\n");
13220 g_s.end();
13221
13222 g_s.begin("Generating class documentation...\n");
13224 g_s.end();
13225
13226 g_s.begin("Generating concept documentation...\n");
13228 g_s.end();
13229
13230 g_s.begin("Generating module documentation...\n");
13232 g_s.end();
13233
13234 g_s.begin("Generating namespace documentation...\n");
13236 g_s.end();
13237
13238 if (Config_getBool(GENERATE_LEGEND))
13239 {
13240 g_s.begin("Generating graph info page...\n");
13242 g_s.end();
13243 }
13244
13245 g_s.begin("Generating directory documentation...\n");
13247 g_s.end();
13248
13249 if (g_outputList->size()>0)
13250 {
13252 }
13253
13254 g_s.begin("finalizing index lists...\n");
13255 Doxygen::indexList->finalize();
13256 g_s.end();
13257
13258 g_s.begin("writing tag file...\n");
13259 writeTagFile();
13260 g_s.end();
13261
13262 if (Config_getBool(GENERATE_XML))
13263 {
13264 g_s.begin("Generating XML output...\n");
13266 generateXML();
13268 g_s.end();
13269 }
13270 if (Config_getBool(GENERATE_SQLITE3))
13271 {
13272 g_s.begin("Generating SQLITE3 output...\n");
13274 g_s.end();
13275 }
13276
13277 if (Config_getBool(GENERATE_AUTOGEN_DEF))
13278 {
13279 g_s.begin("Generating AutoGen DEF output...\n");
13280 generateDEF();
13281 g_s.end();
13282 }
13283 if (Config_getBool(GENERATE_PERLMOD))
13284 {
13285 g_s.begin("Generating Perl module output...\n");
13287 g_s.end();
13288 }
13289 if (generateHtml && searchEngine && serverBasedSearch)
13290 {
13291 g_s.begin("Generating search index\n");
13292 if (Doxygen::searchIndex.kind()==SearchIndexIntf::Internal) // write own search index
13293 {
13295 Doxygen::searchIndex.write(Config_getString(HTML_OUTPUT)+"/search/search.idx");
13296 }
13297 else // write data for external search index
13298 {
13300 QCString searchDataFile = Config_getString(SEARCHDATA_FILE);
13301 if (searchDataFile.isEmpty())
13302 {
13303 searchDataFile="searchdata.xml";
13304 }
13305 if (!Portable::isAbsolutePath(searchDataFile.data()))
13306 {
13307 searchDataFile.prepend(Config_getString(OUTPUT_DIRECTORY)+"/");
13308 }
13309 Doxygen::searchIndex.write(searchDataFile);
13310 }
13311 g_s.end();
13312 }
13313
13314 if (generateRtf)
13315 {
13316 g_s.begin("Combining RTF output...\n");
13317 if (!RTFGenerator::preProcessFileInplace(Config_getString(RTF_OUTPUT),"refman.rtf"))
13318 {
13319 err("An error occurred during post-processing the RTF files!\n");
13320 }
13321 g_s.end();
13322 }
13323
13324 g_s.begin("Running plantuml with JAVA...\n");
13326 g_s.end();
13327
13328 if (Config_getBool(HAVE_DOT))
13329 {
13330 g_s.begin("Running dot...\n");
13332 g_s.end();
13333 }
13334
13335 if (generateHtml &&
13336 Config_getBool(GENERATE_HTMLHELP) &&
13337 !Config_getString(HHC_LOCATION).isEmpty())
13338 {
13339 g_s.begin("Running html help compiler...\n");
13341 g_s.end();
13342 }
13343
13344 if ( generateHtml &&
13345 Config_getBool(GENERATE_QHP) &&
13346 !Config_getString(QHG_LOCATION).isEmpty())
13347 {
13348 g_s.begin("Running qhelpgenerator...\n");
13350 g_s.end();
13351 }
13352
13353 g_outputList->cleanup();
13354
13355 msg("type lookup cache used {}/{} hits={} misses={}\n",
13357 Doxygen::typeLookupCache->capacity(),
13359 Doxygen::typeLookupCache->misses());
13360 msg("symbol lookup cache used {}/{} hits={} misses={}\n",
13362 Doxygen::symbolLookupCache->capacity(),
13364 Doxygen::symbolLookupCache->misses());
13365 int typeCacheParam = computeIdealCacheParam(static_cast<size_t>(Doxygen::typeLookupCache->misses()*2/3)); // part of the cache is flushed, hence the 2/3 correction factor
13366 int symbolCacheParam = computeIdealCacheParam(static_cast<size_t>(Doxygen::symbolLookupCache->misses()));
13367 int cacheParam = std::max(typeCacheParam,symbolCacheParam);
13368 if (cacheParam>Config_getInt(LOOKUP_CACHE_SIZE))
13369 {
13370 msg("Note: based on cache misses the ideal setting for LOOKUP_CACHE_SIZE is {} at the cost of higher memory usage.\n",cacheParam);
13371 }
13372
13374 {
13375
13376 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
13377 if (numThreads<1) numThreads=1;
13378 msg("Total elapsed time: {:.6f} seconds\n(of which an average of {:.6f} seconds per thread waiting for external tools to finish)\n",
13379 (static_cast<double>(Debug::elapsedTime())),
13380 Portable::getSysElapsedTime()/static_cast<double>(numThreads)
13381 );
13382 g_s.print();
13383
13385 msg("finished...\n");
13387 }
13388 else
13389 {
13390 msg("finished...\n");
13391 }
13392
13393
13394 /**************************************************************************
13395 * Start cleaning up *
13396 **************************************************************************/
13397
13399
13401 Dir thisDir;
13402 thisDir.remove(Doxygen::filterDBFileName.str());
13404 exitTracing();
13406 delete Doxygen::clangUsrMap;
13408
13409 //dumpDocNodeSizes();
13410}
void readAliases()
Definition aliases.cpp:170
constexpr auto prefix
Definition anchor.cpp:44
std::vector< ArgumentList > ArgumentLists
Definition arguments.h:145
This class represents an function or template argument list.
Definition arguments.h:65
RefQualifierType refQualifier() const
Definition arguments.h:116
bool noParameters() const
Definition arguments.h:117
bool pureSpecifier() const
Definition arguments.h:113
iterator end()
Definition arguments.h:94
void setTrailingReturnType(const QCString &s)
Definition arguments.h:122
bool hasParameters() const
Definition arguments.h:76
bool isDeleted() const
Definition arguments.h:115
QCString trailingReturnType() const
Definition arguments.h:114
size_t size() const
Definition arguments.h:100
void setPureSpecifier(bool b)
Definition arguments.h:121
bool constSpecifier() const
Definition arguments.h:111
void push_back(const Argument &a)
Definition arguments.h:102
bool empty() const
Definition arguments.h:99
void setConstSpecifier(bool b)
Definition arguments.h:119
void setRefQualifier(RefQualifierType t)
Definition arguments.h:124
void setIsDeleted(bool b)
Definition arguments.h:123
iterator begin()
Definition arguments.h:93
bool volatileSpecifier() const
Definition arguments.h:112
void setNoParameters(bool b)
Definition arguments.h:125
void setVolatileSpecifier(bool b)
Definition arguments.h:120
Definition cache.h:32
static CitationManager & instance()
Definition cite.cpp:86
void clear()
clears the database
Definition cite.cpp:112
void generatePage()
Generate the citations page.
Definition cite.cpp:333
std::unique_ptr< ClangTUParser > createTUParser(const FileDef *fd) const
static ClangParser * instance()
Returns the one and only instance of the class.
Clang parser object for a single translation unit, which consists of a source file and the directly o...
Definition clangparser.h:25
void switchToFile(const FileDef *fd)
Switches to another file within the translation unit started with start().
void parse()
Parse the file given at construction time as a translation unit This file should already be preproces...
A abstract class representing of a compound symbol.
Definition classdef.h:104
virtual const ArgumentList & templateArguments() const =0
Returns the template arguments of this class.
virtual QCString compoundTypeString() const =0
Returns the type of compound as a string.
virtual void writeDocumentation(OutputList &ol) const =0
virtual void writeMemberList(OutputList &ol) const =0
virtual bool isTemplate() const =0
Returns TRUE if this class is a template.
virtual const BaseClassList & baseClasses() const =0
Returns the list of base classes from which this class directly inherits.
virtual const MemberDef * getMemberByName(const QCString &) const =0
Returns the member with the given name.
virtual const TemplateInstanceList & getTemplateInstances() const =0
Returns a sorted dictionary with all template instances found for this template class.
virtual int isBaseClass(const ClassDef *bcd, bool followInstances, const QCString &templSpec=QCString()) const =0
Returns TRUE iff bcd is a direct or indirect base class of this class.
virtual ArgumentLists getTemplateParameterLists() const =0
Returns the template parameter lists that form the template declaration of this class.
virtual Protection protection() const =0
Return the protection level (Public,Protected,Private) in which this compound was found.
virtual MemberList * getMemberList(MemberListType lt) const =0
Returns the members in the list identified by lt.
virtual bool isForwardDeclared() const =0
Returns TRUE if this class represents a forward declaration of a template class.
virtual bool isSubClass(ClassDef *bcd, int level=0) const =0
Returns TRUE iff bcd is a direct or indirect sub class of this class.
virtual void moveTo(Definition *)=0
virtual const TemplateNameMap & getTemplateBaseClassNames() const =0
virtual bool isEmbeddedInOuterScope() const =0
virtual const MemberNameInfoLinkedMap & memberNameInfoLinkedMap() const =0
Returns a dictionary of all members.
virtual bool isImplicitTemplateInstance() const =0
virtual QCString qualifiedNameWithTemplateParameters(const ArgumentLists *actualParams=nullptr, uint32_t *actualParamIndex=nullptr) const =0
virtual std::unique_ptr< ClassDef > deepCopy(const QCString &name) const =0
virtual const ClassDef * templateMaster() const =0
Returns the template master of which this class is an instance.
CompoundType
The various compound types.
Definition classdef.h:109
@ Singleton
Definition classdef.h:117
@ Interface
Definition classdef.h:112
@ Exception
Definition classdef.h:115
virtual CompoundType compoundType() const =0
Returns the type of compound this is, i.e.
virtual bool containsOverload(const MemberDef *md) const =0
virtual ClassLinkedRefMap getClasses() const =0
returns the classes nested into this class
virtual FileDef * getFileDef() const =0
Returns the namespace this compound is in, or 0 if it has a global scope.
virtual QCString requiresClause() const =0
virtual void writeTagFile(TextStream &) const =0
virtual void writeDocumentationForInnerClasses(OutputList &ol) const =0
virtual void computeAnchors()=0
virtual void addTypeConstraints()=0
virtual void overrideCollaborationGraph(bool e)=0
virtual void setClassName(const QCString &name)=0
virtual void countMembers()=0
virtual void addMembersToMemberGroup()=0
virtual void makeTemplateArgument(bool b=TRUE)=0
virtual void setTemplateBaseClassNames(const TemplateNameMap &templateNames)=0
virtual void insertExplicitTemplateInstance(ClassDef *instance, const QCString &spec)=0
virtual void setMetaData(const QCString &md)=0
virtual void setFileDef(FileDef *fd)=0
virtual void addUsedClass(ClassDef *cd, const QCString &accessName, Protection prot)=0
virtual void reclassifyMember(MemberDefMutable *md, MemberType t)=0
virtual ClassDef * insertTemplateInstance(const QCString &fileName, int startLine, int startColumn, const QCString &templSpec, bool &freshInstance)=0
virtual void insertBaseClass(ClassDef *, const QCString &name, Protection p, Specifier s, const QCString &t=QCString())=0
virtual void setTemplateArguments(const ArgumentList &al)=0
virtual void setTemplateMaster(const ClassDef *tm)=0
virtual void mergeCategory(ClassDef *category)=0
virtual void addQualifiers(const StringVector &qualifiers)=0
virtual void setClassSpecifier(TypeSpecifier spec)=0
virtual void addListReferences()=0
virtual void insertSubClass(ClassDef *, Protection p, Specifier s, const QCString &t=QCString())=0
virtual void insertUsedFile(const FileDef *)=0
virtual void setRequiresClause(const QCString &req)=0
virtual void setTagLessReference(const ClassDef *cd)=0
virtual void setUsedOnly(bool b)=0
virtual void sortMemberLists()=0
virtual void setProtection(Protection p)=0
virtual void setTypeConstraints(const ArgumentList &al)=0
virtual void overrideInheritanceGraph(CLASS_GRAPH_t e)=0
virtual void setAnonymousEnumType()=0
virtual void setCompoundType(CompoundType t)=0
virtual void distributeMemberGroupDocumentation()=0
virtual void findSectionsInDocumentation()=0
virtual void addUsedByClass(ClassDef *cd, const QCString &accessName, Protection prot)=0
virtual void insertMember(MemberDef *)=0
virtual void sortAllMembersList()=0
virtual void addMembersToTemplateInstance(const ClassDef *cd, const ArgumentList &templateArguments, const QCString &templSpec)=0
virtual void mergeMembers()=0
virtual void setIsStatic(bool b)=0
virtual void setSubGrouping(bool enabled)=0
virtual void setFileDef(FileDef *fd)=0
virtual void writeTagFile(TextStream &)=0
virtual void setInitializer(const QCString &init)=0
virtual void writeDocumentation(OutputList &ol)=0
virtual void setGroupId(int id)=0
virtual void findSectionsInDocumentation()=0
virtual void setTemplateArguments(const ArgumentList &al)=0
@ ExtCmd
Definition debug.h:36
@ Sections
Definition debug.h:48
@ Time
Definition debug.h:35
@ Qhp
Definition debug.h:44
@ Entries
Definition debug.h:47
static void printFlags()
Definition debug.cpp:137
static void clearFlag(const DebugMask mask)
Definition debug.cpp:122
static bool isFlagSet(const DebugMask mask)
Definition debug.cpp:132
static void print(DebugMask mask, int prio, fmt::format_string< Args... > fmt, Args &&... args)
Definition debug.h:76
static double elapsedTime()
Definition debug.cpp:201
static void startTimer()
Definition debug.cpp:196
static bool setFlagStr(const QCString &label)
Definition debug.cpp:103
static void setFlag(const DebugMask mask)
Definition debug.cpp:117
The common base class of all entity definitions found in the sources.
Definition definition.h:76
virtual QCString docFile() const =0
virtual const QCString & localName() const =0
virtual int getEndBodyLine() const =0
virtual SrcLangExt getLanguage() const =0
Returns the programming language this definition was written in.
virtual int docLine() const =0
virtual QCString getDefFileName() const =0
virtual bool isLinkable() const =0
virtual int getDefLine() const =0
virtual DefType definitionType() const =0
virtual QCString anchor() const =0
virtual int inbodyLine() const =0
virtual const FileDef * getBodyDef() const =0
virtual int briefLine() const =0
virtual bool hasDocumentation() const =0
virtual bool isLinkableInProject() const =0
virtual QCString briefDescription(bool abbreviate=FALSE) const =0
virtual bool isAnonymous() const =0
virtual bool isHidden() const =0
virtual const Definition * findInnerCompound(const QCString &name) const =0
virtual int getStartDefLine() const =0
virtual const GroupList & partOfGroups() const =0
virtual QCString documentation() const =0
virtual QCString qualifiedName() const =0
virtual QCString displayName(bool includeScope=TRUE) const =0
virtual bool isArtificial() const =0
virtual QCString briefFile() const =0
virtual QCString getOutputFileBase() const =0
virtual Definition * getOuterScope() const =0
virtual int getStartBodyLine() const =0
virtual int getDefColumn() const =0
virtual bool isReference() const =0
virtual QCString inbodyDocumentation() const =0
virtual QCString inbodyFile() const =0
virtual const QCString & name() const =0
virtual void mergeReferencedBy(const Definition *other)=0
virtual void setExported(bool b)=0
virtual void setBodySegment(int defLine, int bls, int ble)=0
virtual void setName(const QCString &name)=0
virtual void setHidden(bool b)=0
virtual void mergeReferences(const Definition *other)=0
virtual void setDocumentation(const QCString &d, const QCString &docFile, int docLine, bool stripWhiteSpace=TRUE)=0
virtual void setDefFile(const QCString &df, int defLine, int defColumn)=0
virtual void addInnerCompound(Definition *d)=0
virtual void addSectionsToDefinition(const std::vector< const SectionInfo * > &anchorList)=0
virtual void setInbodyDocumentation(const QCString &d, const QCString &docFile, int docLine)=0
virtual void setLanguage(SrcLangExt lang)=0
virtual void setOuterScope(Definition *d)=0
virtual void setArtificial(bool b)=0
virtual void setId(const QCString &name)=0
virtual void makePartOfGroup(GroupDef *gd)=0
virtual void setBodyDef(const FileDef *fd)=0
virtual void setBriefDescription(const QCString &b, const QCString &briefFile, int briefLine)=0
virtual void setReference(const QCString &r)=0
virtual void setRefItems(const RefItemVector &sli)=0
virtual void computeTooltip()=0
A model of a directory symbol.
Definition dirdef.h:110
virtual void overrideDirectoryGraph(bool e)=0
Class representing a directory in the file system.
Definition dir.h:75
static std::string currentDirPath()
Definition dir.cpp:342
std::string absPath() const
Definition dir.cpp:364
bool mkdir(const std::string &path, bool acceptsAbsPath=true) const
Definition dir.cpp:295
void setPath(const std::string &path)
Definition dir.cpp:229
bool remove(const std::string &path, bool acceptsAbsPath=true) const
Definition dir.cpp:314
DirIterator iterator() const
Definition dir.cpp:239
static std::string cleanDirPath(const std::string &path)
Definition dir.cpp:357
static bool setCurrent(const std::string &path)
Definition dir.cpp:350
bool exists() const
Definition dir.cpp:257
A linked map of directories.
Definition dirdef.h:173
A class that generates docset files.
Definition docsets.h:36
static void init()
bool run()
Definition dot.cpp:128
static DotManager * instance()
Definition dot.cpp:78
static NamespaceLinkedMap * namespaceLinkedMap
Definition doxygen.h:115
static ConceptLinkedMap * conceptLinkedMap
Definition doxygen.h:98
static bool suppressDocWarnings
Definition doxygen.h:132
static FileNameLinkedMap * plantUmlFileNameLinkedMap
Definition doxygen.h:110
static bool parseSourcesNeeded
Definition doxygen.h:123
static StringUnorderedSet inputPaths
Definition doxygen.h:104
static std::unique_ptr< PageDef > mainPage
Definition doxygen.h:101
static bool clangAssistedParsing
Definition doxygen.h:138
static StringUnorderedSet expandAsDefinedSet
Definition doxygen.h:119
static FileNameLinkedMap * inputNameLinkedMap
Definition doxygen.h:105
static ParserManager * parserManager
Definition doxygen.h:131
static Cache< std::string, LookupInfo > * typeLookupCache
Definition doxygen.h:127
static InputFileEncodingList inputFileEncodingList
Definition doxygen.h:140
static ClassLinkedMap * classLinkedMap
Definition doxygen.h:96
static MemberNameLinkedMap * functionNameLinkedMap
Definition doxygen.h:112
static PageLinkedMap * exampleLinkedMap
Definition doxygen.h:99
static FileNameLinkedMap * dotFileNameLinkedMap
Definition doxygen.h:107
static NamespaceDefMutable * globalScope
Definition doxygen.h:121
static FileNameLinkedMap * imageNameLinkedMap
Definition doxygen.h:106
static FileNameLinkedMap * mscFileNameLinkedMap
Definition doxygen.h:108
static QCString verifiedDotPath
Definition doxygen.h:139
static MemberGroupInfoMap memberGroupInfoMap
Definition doxygen.h:118
static QCString spaces
Definition doxygen.h:135
static IndexList * indexList
Definition doxygen.h:134
static StaticInitMap staticInitMap
Definition doxygen.h:143
static Cache< std::string, LookupInfo > * symbolLookupCache
Definition doxygen.h:128
static StringMap tagDestinationMap
Definition doxygen.h:116
static std::mutex countFlowKeywordsMutex
Definition doxygen.h:141
static ClassLinkedMap * hiddenClassLinkedMap
Definition doxygen.h:97
static FileNameLinkedMap * diaFileNameLinkedMap
Definition doxygen.h:109
static QCString htmlFileExtension
Definition doxygen.h:122
static QCString filterDBFileName
Definition doxygen.h:133
static PageLinkedMap * pageLinkedMap
Definition doxygen.h:100
static bool generatingXmlOutput
Definition doxygen.h:136
static std::unique_ptr< NamespaceDef > globalNamespaceDef
Definition doxygen.h:120
static DefinesPerFileList macroDefinitions
Definition doxygen.h:137
static DirLinkedMap * dirLinkedMap
Definition doxygen.h:129
static NamespaceAliasInfoMap namespaceAliasMap
Definition doxygen.h:113
static MemberNameLinkedMap * memberNameLinkedMap
Definition doxygen.h:111
static SymbolMap< Definition > * symbolMap
Definition doxygen.h:125
static StringUnorderedSet tagFileSet
Definition doxygen.h:117
static FileNameLinkedMap * includeNameLinkedMap
Definition doxygen.h:102
static FileNameLinkedMap * exampleNameLinkedMap
Definition doxygen.h:103
static SearchIndexIntf searchIndex
Definition doxygen.h:124
static DirRelationLinkedMap dirRelations
Definition doxygen.h:130
static std::mutex addExampleMutex
Definition doxygen.h:142
static ClangUsrMap * clangUsrMap
Definition doxygen.h:126
static GroupLinkedMap * groupLinkedMap
Definition doxygen.h:114
Generator for Eclipse help files.
Definition eclipsehelp.h:44
static EmojiEntityMapper & instance()
Returns the one and only instance of the Emoji entity mapper.
Definition emoji.cpp:1978
void writeEmojiFile(TextStream &t)
Writes the list of supported emojis to the given file.
Definition emoji.cpp:1999
Represents an unstructured piece of information, about an entity found in the sources.
Definition entry.h:116
TextStream initializer
initial value (for variables)
Definition entry.h:197
VhdlSpecifier vhdlSpec
VHDL specifiers.
Definition entry.h:183
bool subGrouping
automatically group class members?
Definition entry.h:188
const std::vector< std::shared_ptr< Entry > > & children() const
Definition entry.h:139
bool proto
prototype ?
Definition entry.h:187
GroupDocType groupDocType
Definition entry.h:230
QCString metaData
Slice metadata.
Definition entry.h:233
int docLine
line number at which the documentation was found
Definition entry.h:201
QCString bitfields
member's bit fields
Definition entry.h:193
ArgumentList typeConstr
where clause (C#) for type constraints
Definition entry.h:215
void markAsProcessed() const
Definition entry.h:167
int endBodyLine
line number where the definition ends
Definition entry.h:218
bool exported
is the symbol exported from a C++20 module
Definition entry.h:189
const TagInfo * tagInfo() const
Definition entry.h:177
QCString includeName
include name (3 arg of \class)
Definition entry.h:199
QCString id
libclang id
Definition entry.h:231
ArgumentLists tArgLists
template argument declarations
Definition entry.h:195
LocalToc localToc
Definition entry.h:232
MethodTypes mtype
signal, slot, (dcop) method, or property?
Definition entry.h:181
@ GROUPDOC_NORMAL
defgroup
Definition entry.h:121
SrcLangExt lang
programming language in which this entry was found
Definition entry.h:227
Entry * parent() const
Definition entry.h:134
QCString inbodyDocs
documentation inside the body of a function
Definition entry.h:206
int startColumn
start column of entry in the source
Definition entry.h:225
QCString relates
related class (doc block)
Definition entry.h:209
bool explicitExternal
explicitly defined as external?
Definition entry.h:186
std::vector< const SectionInfo * > anchors
list of anchors defined in this entry
Definition entry.h:222
QCString fileName
file this entry was extracted from
Definition entry.h:223
RelatesType relatesType
how relates is handled
Definition entry.h:210
QCString write
property write accessor
Definition entry.h:212
QCString args
member argument string
Definition entry.h:192
QCString type
member type
Definition entry.h:173
std::vector< Grouping > groups
list of groups this entry belongs to
Definition entry.h:221
CommandOverrides commandOverrides
store info for commands whose default can be overridden
Definition entry.h:190
QCString exception
throw specification
Definition entry.h:214
int startLine
start line of entry in the source
Definition entry.h:224
QCString req
C++20 requires clause.
Definition entry.h:234
ArgumentList argList
member arguments as a list
Definition entry.h:194
QCString name
member name
Definition entry.h:174
QCString includeFile
include file (2 arg of \class, must be unique)
Definition entry.h:198
int inbodyLine
line number at which the body doc was found
Definition entry.h:207
EntryType section
entry type (see Sections);
Definition entry.h:172
QCString briefFile
file in which the brief desc. was found
Definition entry.h:205
int bodyLine
line number of the body in the source
Definition entry.h:216
int mGrpId
member group id
Definition entry.h:219
std::vector< BaseInfo > extends
list of base classes
Definition entry.h:220
Specifier virt
virtualness of the entry
Definition entry.h:191
std::vector< std::string > qualifiers
qualifiers specified with the qualifier command
Definition entry.h:235
QCString doc
documentation block (partly parsed)
Definition entry.h:200
QCString read
property read accessor
Definition entry.h:211
RefItemVector sli
special lists (test/todo/bug/deprecated/..) this entry is in
Definition entry.h:226
QCString docFile
file in which the documentation was found
Definition entry.h:202
Protection protection
class protection
Definition entry.h:180
bool artificial
Artificially introduced item.
Definition entry.h:229
bool hidden
does this represent an entity that is hidden from the output
Definition entry.h:228
QCString brief
brief description (doc block)
Definition entry.h:203
int briefLine
line number at which the brief desc. was found
Definition entry.h:204
FileDef * fileDef() const
Definition entry.h:169
int initLines
define/variable initializer lines to show
Definition entry.h:184
bool isStatic
static ?
Definition entry.h:185
QCString inbodyFile
file in which the body doc was found
Definition entry.h:208
TypeSpecifier spec
class/member specifiers
Definition entry.h:182
QCString inside
name of the class in which documents are found
Definition entry.h:213
Wrapper class for the Entry type.
Definition types.h:793
bool isDoc() const
Definition types.h:804
ENTRY_TYPES bool isCompound() const
Definition types.h:800
bool isFile() const
Definition types.h:802
std::string to_string() const
Definition types.h:805
bool isScope() const
Definition types.h:801
bool isCompoundDoc() const
Definition types.h:803
A class that generates a dynamic tree view side panel.
Definition ftvhelp.h:41
A model of a file symbol.
Definition filedef.h:99
virtual void addUsingDeclaration(const Definition *d)=0
virtual void removeMember(MemberDef *md)=0
virtual void insertClass(ClassDef *cd)=0
virtual void insertConcept(ConceptDef *cd)=0
virtual void overrideIncludeGraph(bool e)=0
virtual void writeSourceHeader(OutputList &ol)=0
virtual bool generateSourceFile() const =0
virtual const LinkedRefMap< NamespaceDef > & getUsedNamespaces() const =0
virtual QCString absFilePath() const =0
virtual bool isSource() const =0
virtual void parseSource(ClangTUParser *clangParser)=0
virtual void getAllIncludeFilesRecursively(StringVector &incFiles) const =0
virtual void setDiskName(const QCString &name)=0
virtual void writeSourceFooter(OutputList &ol)=0
virtual void writeSourceBody(OutputList &ol, ClangTUParser *clangParser)=0
virtual void addUsingDirective(NamespaceDef *nd)=0
virtual void overrideIncludedByGraph(bool e)=0
virtual const QCString & docName() const =0
virtual void insertMember(MemberDef *md)=0
virtual void insertNamespace(NamespaceDef *nd)=0
Minimal replacement for QFileInfo.
Definition fileinfo.h:23
std::string readLink() const
Definition fileinfo.cpp:84
bool isRelative() const
Definition fileinfo.cpp:58
bool isSymLink() const
Definition fileinfo.cpp:77
bool exists() const
Definition fileinfo.cpp:30
std::string fileName() const
Definition fileinfo.cpp:118
bool isReadable() const
Definition fileinfo.cpp:44
bool isDir() const
Definition fileinfo.cpp:70
bool isFile() const
Definition fileinfo.cpp:63
std::string dirPath(bool absPath=true) const
Definition fileinfo.cpp:137
std::string absFilePath() const
Definition fileinfo.cpp:101
Class representing all files with a certain base name.
Definition filename.h:30
Ordered dictionary of FileName objects.
Definition filename.h:73
void initFromRepository(const QCString &dir)
Definition formula.cpp:60
bool hasFormulas() const
Definition formula.cpp:720
void checkRepositories()
Definition formula.cpp:173
static FormulaManager & instance()
Definition formula.cpp:54
void generateImages(const QCString &outputDir, Format format, HighDPI hd=HighDPI::Off)
Definition formula.cpp:636
A model of a group of symbols.
Definition groupdef.h:52
virtual QCString groupTitle() const =0
virtual void overrideGroupGraph(bool e)=0
virtual bool addClass(ClassDef *def)=0
virtual bool containsFile(const FileDef *def) const =0
virtual bool addNamespace(NamespaceDef *def)=0
virtual void setGroupScope(Definition *d)=0
virtual void addFile(FileDef *def)=0
virtual MemberList * getMemberList(MemberListType lt) const =0
virtual void setGroupTitle(const QCString &newtitle)=0
virtual bool hasGroupTitle() const =0
Generator for HTML output.
Definition htmlgen.h:96
static void init()
Definition htmlgen.cpp:1203
static void writeSearchPage()
Definition htmlgen.cpp:3170
static void writeFooterFile(TextStream &t)
Definition htmlgen.cpp:1537
static void writeTabData()
Additional initialization after indices have been created.
Definition htmlgen.cpp:1354
static void writeSearchData(const QCString &dir)
Definition htmlgen.cpp:1363
static void writeExternalSearchPage()
Definition htmlgen.cpp:3269
static void writeStyleSheetFile(TextStream &t)
Definition htmlgen.cpp:1525
static void writeHeaderFile(TextStream &t, const QCString &cssname)
Definition htmlgen.cpp:1531
A class that generated the HTML Help specific files.
Definition htmlhelp.h:36
static const QCString hhpFileName
Definition htmlhelp.h:89
static Index & instance()
Definition index.cpp:106
void countDataStructures()
Definition index.cpp:262
A list of index interfaces.
Definition indexlist.h:64
Generator for LaTeX output.
Definition latexgen.h:94
static void writeFooterFile(TextStream &t)
Definition latexgen.cpp:697
static void writeStyleSheetFile(TextStream &t)
Definition latexgen.cpp:703
static void writeHeaderFile(TextStream &t)
Definition latexgen.cpp:691
static void init()
Definition latexgen.cpp:633
static LayoutDocManager & instance()
Returns a reference to this singleton.
Definition layout.cpp:1435
void parse(const QCString &fileName, const char *data=nullptr)
Parses a user provided layout.
Definition layout.cpp:1468
void clear()
Definition linkedmap.h:212
std::unique_ptr< RefList > Ptr
Definition linkedmap.h:38
size_t size() const
Definition linkedmap.h:210
T * add(const char *k, Args &&... args)
Definition linkedmap.h:90
const T * find(const std::string &key) const
Definition linkedmap.h:47
Container class representing a vector of objects with keys.
Definition linkedmap.h:232
const T * find(const std::string &key) const
Definition linkedmap.h:243
bool empty() const
Definition linkedmap.h:374
Generator for Man page output.
Definition mangen.h:69
static void init()
Definition mangen.cpp:272
A model of a class/file/namespace member symbol.
Definition memberdef.h:48
virtual QCString typeString() const =0
virtual QCString requiresClause() const =0
virtual bool isFriend() const =0
virtual bool isForeign() const =0
virtual QCString definition() const =0
virtual bool isRelated() const =0
virtual const ClassDef * getCachedTypedefVal() const =0
virtual QCString excpString() const =0
virtual const ClassDef * getClassDef() const =0
virtual const ArgumentList & templateArguments() const =0
virtual GroupDef * getGroupDef()=0
virtual bool isCSharpProperty() const =0
virtual bool isTypedef() const =0
virtual const MemberVector & enumFieldList() const =0
virtual void moveTo(Definition *)=0
virtual const FileDef * getFileDef() const =0
virtual const ArgumentList & argumentList() const =0
virtual bool isStrongEnumValue() const =0
virtual VhdlSpecifier getVhdlSpecifiers() const =0
virtual bool isFunction() const =0
virtual bool isExternal() const =0
virtual int getMemberGroupId() const =0
virtual bool isStatic() const =0
virtual const MemberDef * reimplements() const =0
virtual StringVector getQualifiers() const =0
virtual QCString bitfieldString() const =0
virtual bool isTypedefValCached() const =0
virtual bool isDocsForDefinition() const =0
virtual bool isDefine() const =0
virtual const NamespaceDef * getNamespaceDef() const =0
virtual bool isObjCProperty() const =0
virtual Protection protection() const =0
virtual TypeSpecifier getMemberSpecifiers() const =0
virtual bool isEnumerate() const =0
virtual MemberType memberType() const =0
virtual ClassDef * relatedAlso() const =0
virtual bool isVariable() const =0
virtual bool isStrong() const =0
virtual QCString argsString() const =0
virtual Specifier virtualness(int count=0) const =0
virtual int redefineCount() const =0
virtual int initializerLines() const =0
virtual const MemberDef * getEnumScope() const =0
virtual bool isEnumValue() const =0
virtual bool isPrototype() const =0
virtual const QCString & initializer() const =0
virtual void setMemberClass(ClassDef *cd)=0
virtual void setProtection(Protection p)=0
virtual void setMemberGroupId(int id)=0
virtual void setDocumentedEnumValues(bool value)=0
virtual void setMemberSpecifiers(TypeSpecifier s)=0
virtual void setDefinition(const QCString &d)=0
virtual ClassDefMutable * getClassDefMutable()=0
virtual void setExplicitExternal(bool b, const QCString &df, int line, int column)=0
virtual void setAccessorType(ClassDef *cd, const QCString &t)=0
virtual void setDefinitionTemplateParameterLists(const ArgumentLists &lists)=0
virtual void invalidateTypedefValCache()=0
virtual void setBitfields(const QCString &s)=0
virtual void setVhdlSpecifiers(VhdlSpecifier s)=0
virtual void setEnumScope(MemberDef *md, bool livesInsideEnum=FALSE)=0
virtual void setEnumClassScope(ClassDef *cd)=0
virtual void setMaxInitLines(int lines)=0
virtual void setInheritsDocsFrom(const MemberDef *md)=0
virtual void setRelatedAlso(ClassDef *cd)=0
virtual void setPrototype(bool p, const QCString &df, int line, int column)=0
virtual void overrideReferencesRelation(bool e)=0
virtual void makeForeign()=0
virtual void overrideReferencedByRelation(bool e)=0
virtual void setDocsForDefinition(bool b)=0
virtual void setRequiresClause(const QCString &req)=0
virtual void overrideCallGraph(bool e)=0
virtual void overrideInlineSource(bool e)=0
virtual void setArgsString(const QCString &as)=0
virtual void setInitializer(const QCString &i)=0
virtual void copyArgumentNames(const MemberDef *bmd)=0
virtual void overrideEnumValues(bool e)=0
virtual void mergeMemberSpecifiers(TypeSpecifier s)=0
virtual void addQualifiers(const StringVector &qualifiers)=0
virtual void insertEnumField(MemberDef *md)=0
virtual void moveDeclArgumentList(std::unique_ptr< ArgumentList > al)=0
virtual void overrideCallerGraph(bool e)=0
virtual void setReimplements(MemberDef *md)=0
virtual void setDeclFile(const QCString &df, int line, int column)=0
virtual void invalidateCachedArgumentTypes()=0
virtual void moveArgumentList(std::unique_ptr< ArgumentList > al)=0
virtual void makeRelated()=0
virtual void insertReimplementedBy(MemberDef *md)=0
A list of MemberDef objects as shown in documentation sections.
Definition memberlist.h:125
Ptr & front()
Definition membername.h:51
size_t size() const
Definition membername.h:48
iterator begin()
Definition membername.h:37
iterator end()
Definition membername.h:38
void push_back(Ptr &&p)
Definition membername.h:54
Ordered dictionary of MemberName objects.
Definition membername.h:63
const MemberDef * find(const QCString &name) const
Definition memberlist.h:93
const MemberDef * findRev(const QCString &name) const
Definition memberlist.h:106
void addListReferences()
void sortMemberLists()
static ModuleManager & instance()
void addDocs(const Entry *root)
void addConceptToModule(const Entry *root, ConceptDef *cd)
void addClassToModule(const Entry *root, ClassDef *cd)
void addMemberToModule(const Entry *root, MemberDef *md)
void writeDocumentation(OutputList &ol)
void addMembersToMemberGroup()
void findSectionsInDocumentation()
void distributeMemberGroupDocumentation()
An abstract interface of a namespace symbol.
virtual const LinkedRefMap< NamespaceDef > & getUsedNamespaces() const =0
virtual bool isInline() const =0
virtual void insertUsedFile(FileDef *fd)=0
virtual void setMetaData(const QCString &m)=0
virtual void findSectionsInDocumentation()=0
virtual void countMembers()=0
virtual void addUsingDirective(NamespaceDef *nd)=0
virtual void insertMember(MemberDef *md)=0
virtual void addUsingDeclaration(const Definition *d)=0
virtual void distributeMemberGroupDocumentation()=0
virtual void writeTagFile(TextStream &)=0
virtual void addListReferences()=0
virtual void writeDocumentation(OutputList &ol)=0
virtual void setInline(bool isInline)=0
virtual void computeAnchors()=0
virtual void combineUsingRelations(NamespaceDefSet &visitedNamespace)=0
virtual void setFileName(const QCString &fn)=0
virtual void sortMemberLists()=0
virtual void addMembersToMemberGroup()=0
/dev/null outline parser
bool needsPreprocessing(const QCString &) const override
Returns TRUE if the language identified by extension needs the C preprocessor to be run before feed t...
void parseInput(const QCString &, const char *, const std::shared_ptr< Entry > &, ClangTUParser *) override
Parses a single input file with the goal to build an Entry tree.
void parsePrototype(const QCString &) override
Callback function called by the comment block scanner.
Abstract interface for outline parsers.
Definition parserintf.h:42
virtual bool needsPreprocessing(const QCString &extension) const =0
Returns TRUE if the language identified by extension needs the C preprocessor to be run before feed t...
virtual void parseInput(const QCString &fileName, const char *fileBuf, const std::shared_ptr< Entry > &root, ClangTUParser *clangParser)=0
Parses a single input file with the goal to build an Entry tree.
Class representing a list of output generators that are written to in parallel.
Definition outputlist.h:315
A model of a page symbol.
Definition pagedef.h:26
virtual void setLocalToc(const LocalToc &tl)=0
virtual void setFileName(const QCString &name)=0
virtual void setShowLineNo(bool)=0
virtual void setPageScope(Definition *)=0
virtual const GroupDef * getGroupDef() const =0
Manages programming language parsers.
Definition parserintf.h:147
static PlantumlManager & instance()
Definition plantuml.cpp:231
void run()
Run plant UML tool for all images.
Definition plantuml.cpp:389
void processFile(const QCString &fileName, const std::string &input, std::string &output)
Definition pre.l:4073
void addSearchDir(const QCString &dir)
Definition pre.l:4055
This is an alternative implementation of QCString.
Definition qcstring.h:101
int find(char c, int index=0, bool cs=TRUE) const
Definition qcstring.cpp:43
QCString & prepend(const char *s)
Definition qcstring.h:422
int toInt(bool *ok=nullptr, int base=10) const
Definition qcstring.cpp:254
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition qcstring.h:166
bool startsWith(const char *s) const
Definition qcstring.h:507
QCString mid(size_t index, size_t len=static_cast< size_t >(-1)) const
Definition qcstring.h:241
QCString lower() const
Definition qcstring.h:249
bool endsWith(const char *s) const
Definition qcstring.h:524
char & at(size_t i)
Returns a reference to the character at index i.
Definition qcstring.h:593
bool isEmpty() const
Returns TRUE iff the string is empty.
Definition qcstring.h:163
QCString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
Definition qcstring.h:260
QCString fill(char c, int len=-1)
Fills a string with a predefined character.
Definition qcstring.h:193
const std::string & str() const
Definition qcstring.h:552
QCString & setNum(short n)
Definition qcstring.h:459
QCString right(size_t len) const
Definition qcstring.h:234
size_t size() const
Returns the length of the string, not counting the 0-terminator.
Definition qcstring.h:169
QCString & sprintf(const char *format,...)
Definition qcstring.cpp:29
@ ExplicitSize
Definition qcstring.h:146
int findRev(char c, int index=-1, bool cs=TRUE) const
Definition qcstring.cpp:96
QCString & replace(size_t index, size_t len, const char *s)
Definition qcstring.cpp:217
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition qcstring.h:172
QCString left(size_t len) const
Definition qcstring.h:229
int contains(char c, bool cs=TRUE) const
Definition qcstring.cpp:148
bool stripPrefix(const QCString &prefix)
Definition qcstring.h:213
void clear()
Definition qcstring.h:182
Definition qhp.h:27
static const QCString qhpFileName
Definition qhp.h:47
static QCString getQchFileName()
Definition qhp.cpp:426
Generator for RTF output.
Definition rtfgen.h:80
static void init()
Definition rtfgen.cpp:461
static bool preProcessFileInplace(const QCString &path, const QCString &name)
This is an API to a VERY brittle RTF preprocessor that combines nested RTF files.
Definition rtfgen.cpp:2461
static void writeStyleSheetFile(TextStream &t)
Definition rtfgen.cpp:394
static void writeExtensionsFile(TextStream &t)
Definition rtfgen.cpp:409
static RefListManager & instance()
Definition reflist.h:121
Abstract proxy interface for non-javascript based search indices.
class that provide information about a section.
Definition section.h:57
QCString ref() const
Definition section.h:71
QCString fileName() const
Definition section.h:73
int lineNr() const
Definition section.h:72
SectionInfo * replace(const QCString &label, const QCString &fileName, int lineNr, const QCString &title, SectionType type, int level, const QCString &ref=QCString())
Definition section.h:156
SectionInfo * add(const SectionInfo &si)
Definition section.h:138
static SectionManager & instance()
returns a reference to the singleton
Definition section.h:178
static constexpr int Page
Definition section.h:31
std::vector< stat > stats
Definition doxygen.cpp:268
void print()
Definition doxygen.cpp:245
void begin(const char *name)
Definition doxygen.cpp:232
void end()
Definition doxygen.cpp:238
std::chrono::steady_clock::time_point startTime
Definition doxygen.cpp:269
const Definition * resolveSymbol(const Definition *scope, const QCString &name, const QCString &args=QCString(), bool checkCV=false, bool insideCode=false, bool onlyLinkable=false)
Find the symbool definition matching name within the scope set.
const ClassDef * resolveClass(const Definition *scope, const QCString &name, bool maybeUnlinkable=false, bool mayBeHidden=false)
Find the class definition matching name within the scope set.
QCString getTemplateSpec() const
In case a call to resolveClass() points to a template specialization, the template part is return via...
ClassDefMutable * resolveClassMutable(const Definition *scope, const QCString &name, bool mayBeUnlinkable=false, bool mayBeHidden=false)
Wrapper around resolveClass that returns a mutable interface to the class object or a nullptr if the ...
const MemberDef * getTypedef() const
In case a call to resolveClass() resolves to a type member (e.g.
Text streaming class that buffers data.
Definition textstream.h:36
std::string str() const
Return the contents of the buffer as a std::string object.
Definition textstream.h:229
Class managing a pool of worker threads.
Definition threadpool.h:48
auto queue(F &&f, Args &&... args) -> std::future< decltype(f(args...))>
Queue the callable function f for the threads to execute.
Definition threadpool.h:77
Wrapper class for a number of boolean properties.
Definition types.h:654
std::string to_string() const
Definition types.h:682
static void correctMemberProperties(MemberDefMutable *md)
static void computeVhdlComponentRelations()
ClassDefMutable * toClassDefMutable(Definition *d)
ClassDef * getClass(const QCString &n)
std::unique_ptr< ClassDef > createClassDefAlias(const Definition *newScope, const ClassDef *cd)
Definition classdef.cpp:788
std::unique_ptr< ClassDef > createClassDef(const QCString &fileName, int startLine, int startColumn, const QCString &name, ClassDef::CompoundType ct, const QCString &ref, const QCString &fName, bool isSymbol, bool isJavaEnum)
Factory method to create a new ClassDef object.
Definition classdef.cpp:559
ClassDef * toClassDef(Definition *d)
std::unordered_set< const ClassDef * > ClassDefSet
Definition classdef.h:95
std::map< std::string, int > TemplateNameMap
Definition classdef.h:93
ClassDefMutable * getClassMutable(const QCString &key)
Definition classdef.h:469
Class representing a regular expression.
Definition regex.h:39
Class to iterate through matches.
Definition regex.h:230
Object representing the matching results.
Definition regex.h:151
First pass comment processing.
void convertCppComments(const std::string &inBuf, std::string &outBuf, const std::string &fn)
Converts the comments in a file.
ConceptDefMutable * toConceptDefMutable(Definition *d)
std::unique_ptr< ConceptDef > createConceptDef(const QCString &fileName, int startLine, int startColumn, const QCString &name, const QCString &tagRef, const QCString &tagFile)
ConceptDefMutable * getConceptMutable(const QCString &key)
Definition conceptdef.h:90
#define Config_getInt(name)
Definition config.h:34
#define Config_getList(name)
Definition config.h:38
#define Config_updateString(name, value)
Definition config.h:39
#define Config_updateBool(name, value)
Definition config.h:40
#define Config_getBool(name)
Definition config.h:33
#define Config_getString(name)
Definition config.h:32
#define Config_updateList(name,...)
Definition config.h:43
#define Config_getEnum(name)
Definition config.h:35
std::set< std::string > StringSet
Definition containers.h:31
std::unordered_set< std::string > StringUnorderedSet
Definition containers.h:29
std::map< std::string, std::string > StringMap
Definition containers.h:30
std::vector< std::string > StringVector
Definition containers.h:33
void parseFuncDecl(const QCString &decl, const SrcLangExt lang, QCString &clName, QCString &type, QCString &name, QCString &args, QCString &funcTempList, QCString &exceptions)
Definition declinfo.l:325
std::unique_ptr< ArgumentList > stringToArgumentList(SrcLangExt lang, const QCString &argsString, QCString *extraTypeChars=nullptr)
Definition defargs.l:822
void generateDEF()
Definition defgen.cpp:482
std::unordered_map< std::string, DefineList > DefinesPerFileList
Definition define.h:50
Definition * toDefinition(DefinitionMutable *dm)
DefinitionMutable * toDefinitionMutable(Definition *d)
DirIterator begin(DirIterator it) noexcept
Definition dir.cpp:170
DirIterator end(const DirIterator &) noexcept
Definition dir.cpp:175
void buildDirectories()
Definition dirdef.cpp:1102
void computeDirDependencies()
Definition dirdef.cpp:1176
void generateDirDocs(OutputList &ol)
Definition dirdef.cpp:1193
#define AUTO_TRACE_ADD(...)
Definition docnode.cpp:47
#define AUTO_TRACE(...)
Definition docnode.cpp:46
#define AUTO_TRACE_EXIT(...)
Definition docnode.cpp:48
constexpr DocNodeVariant * parent(DocNodeVariant *n)
returns the parent node of a given node n or nullptr if the node has no parent.
Definition docnode.h:1330
static void findInheritedTemplateInstances()
Definition doxygen.cpp:5244
void printNavTree(Entry *root, int indent)
Definition doxygen.cpp:9925
static void addClassToContext(const Entry *root)
Definition doxygen.cpp:931
static void makeTemplateInstanceRelation(const Entry *root, ClassDefMutable *cd)
Definition doxygen.cpp:5259
static StringUnorderedSet g_pathsVisited(1009)
static int findEndOfTemplate(const QCString &s, size_t startPos)
Definition doxygen.cpp:3030
static void buildGroupList(const Entry *root)
Definition doxygen.cpp:425
static void insertMemberAlias(Definition *outerScope, const MemberDef *md)
Definition doxygen.cpp:6604
static void findUsingDeclarations(const Entry *root, bool filterPythonPackages)
Definition doxygen.cpp:2006
static void flushCachedTemplateRelations()
Definition doxygen.cpp:9350
static bool isRecursiveBaseClass(const QCString &scope, const QCString &name)
Definition doxygen.cpp:4802
static void copyLatexStyleSheet()
static void generateDocsForClassList(const std::vector< ClassDefMutable * > &classList)
Definition doxygen.cpp:9010
static std::shared_ptr< Entry > parseFile(OutlineParserInterface &parser, FileDef *fd, const QCString &fn, ClangTUParser *clangParser, bool newTU)
static int findFunctionPtr(const std::string &type, SrcLangExt lang, int *pLength=nullptr)
Definition doxygen.cpp:2826
static bool isSpecialization(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists)
Definition doxygen.cpp: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:5338
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:8064
static void runQHelpGenerator()
static void addConceptToContext(const Entry *root)
Definition doxygen.cpp:1152
static void addRelatedPage(Entry *root)
Definition doxygen.cpp:328
void initDoxygen()
static void addIncludeFile(DefMutable *cd, FileDef *ifd, const Entry *root)
Definition doxygen.cpp:581
static StringVector g_inputFiles
Definition doxygen.cpp:187
void printSectionsTree()
Definition doxygen.cpp:9948
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:5510
static void findUsingDeclImports(const Entry *root)
Definition doxygen.cpp:2159
static void copyStyleSheet()
FindBaseClassRelation_Mode
Definition doxygen.cpp:285
@ Undocumented
Definition doxygen.cpp:288
@ TemplateInstances
Definition doxygen.cpp:286
@ DocumentedOnly
Definition doxygen.cpp:287
void distributeClassGroupRelations()
Definition doxygen.cpp:1447
static void generateGroupDocs()
static void findDirDocumentation(const Entry *root)
Definition doxygen.cpp:9589
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:4844
static void resolveTemplateInstanceInType(const Entry *root, const Definition *scope, const MemberDef *md)
Definition doxygen.cpp:4772
static void organizeSubGroupsFiltered(const Entry *root, bool additional)
Definition doxygen.cpp:465
static void warnUndocumentedNamespaces()
Definition doxygen.cpp:5293
static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments, const std::string &name)
Definition doxygen.cpp:4449
static NamespaceDef * findUsedNamespace(const LinkedRefMap< NamespaceDef > &unl, const QCString &name)
Definition doxygen.cpp:1838
static void buildConceptList(const Entry *root)
Definition doxygen.cpp:1277
static void resolveClassNestingRelations()
Definition doxygen.cpp:1332
static void generateNamespaceConceptDocs(const ConceptLinkedRefMap &conceptList)
static void findMember(const Entry *root, const QCString &relates, const QCString &type, const QCString &args, QCString funcDecl, bool overloaded, bool isFunc)
Definition doxygen.cpp:6647
static void findClassEntries(const Entry *root)
Definition doxygen.cpp:5207
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:8308
static void generateExampleDocs()
Definition doxygen.cpp:9964
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:4721
static void substituteTemplatesInArgList(const ArgumentLists &srcTempArgLists, const ArgumentLists &dstTempArgLists, const ArgumentList &src, ArgumentList &dst)
Definition doxygen.cpp:6034
static void computeMemberReferences()
Definition doxygen.cpp:5407
static void generateConfigFile(const QCString &configFile, bool shortList, bool updateOnly=FALSE)
static void transferRelatedFunctionDocumentation()
Definition doxygen.cpp:4363
static void addMembersToMemberGroup()
Definition doxygen.cpp:9215
static bool tryAddEnumDocsToGroupMember(const Entry *root, const QCString &name)
Definition doxygen.cpp:7945
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:9749
static void distributeConceptGroups()
Definition doxygen.cpp:1299
static void transferFunctionDocumentation()
Definition doxygen.cpp:4282
static void setAnonymousEnumType()
Definition doxygen.cpp:8955
static void sortMemberLists()
Definition doxygen.cpp:8860
static void createTemplateInstanceMembers()
Definition doxygen.cpp:8436
void transferStaticInstanceInitializers()
Definition doxygen.cpp:4412
static void findObjCMethodDefinitions(const Entry *root)
Definition doxygen.cpp:7452
static void dumpSymbolMap()
static void buildTypedefList(const Entry *root)
Definition doxygen.cpp:3353
static void findGroupScope(const Entry *root)
Definition doxygen.cpp:440
static void generateFileDocs()
Definition doxygen.cpp:8673
static void findDefineDocumentation(Entry *root)
Definition doxygen.cpp:9502
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:4512
void parseInput()
static void findMemberDocumentation(const Entry *root)
Definition doxygen.cpp:7422
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:9253
static void generateNamespaceClassDocs(const ClassLinkedRefMap &classList)
static void addEnumValuesToEnums(const Entry *root)
Definition doxygen.cpp:7655
static void generatePageDocs()
Definition doxygen.cpp:9879
static void resolveUserReferences()
Definition doxygen.cpp:9811
static void compareDoxyfile(Config::CompareMode diffList)
static void addPageToContext(PageDef *pd, Entry *root)
Definition doxygen.cpp:309
static void buildVarList(const Entry *root)
Definition doxygen.cpp:3489
static void buildSequenceList(const Entry *root)
Definition doxygen.cpp:3452
static void generateFileSources()
Definition doxygen.cpp:8507
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:2586
static void generateClassDocs()
Definition doxygen.cpp:9108
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:9476
static void addMemberDocs(const Entry *root, MemberDefMutable *md, const QCString &funcDecl, const ArgumentList *al, bool over_load, TypeSpecifier spec)
Definition doxygen.cpp:5524
static void countMembers()
Definition doxygen.cpp:8969
void clearAll()
Definition doxygen.cpp:201
static void devUsage()
static void addInterfaceOrServiceToServiceOrSingleton(const Entry *root, ClassDefMutable *cd, QCString const &rname)
Definition doxygen.cpp:3521
static void organizeSubGroups(const Entry *root)
Definition doxygen.cpp:484
static void applyMemberOverrideOptions(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:2098
void searchInputFiles()
static void findFriends()
Definition doxygen.cpp:4185
static void findEnums(const Entry *root)
Definition doxygen.cpp:7480
static void dumpSymbol(TextStream &t, Definition *d)
static void addClassAndNestedClasses(std::vector< ClassDefMutable * > &list, ClassDefMutable *cd)
Definition doxygen.cpp:9085
static void addEnumDocs(const Entry *root, MemberDefMutable *md)
Definition doxygen.cpp:7904
static void addListReferences()
Definition doxygen.cpp:5441
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:5185
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:4477
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:5216
static void addMembersToIndex()
Definition doxygen.cpp:8098
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:3771
static OutputList * g_outputList
Definition doxygen.cpp:188
static void findMainPage(Entry *root)
Definition doxygen.cpp:9679
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:8733
std::function< std::unique_ptr< T >() > make_parser_factory()
static void buildExampleList(Entry *root)
Definition doxygen.cpp:9896
static void inheritDocumentation()
Definition doxygen.cpp:9155
static void flushUnresolvedRelations()
Definition doxygen.cpp:9404
static bool isSymbolHidden(const Definition *d)
Definition doxygen.cpp:8902
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:8090
static void findTagLessClasses()
Definition doxygen.cpp:1647
static void generateDiskNames()
static void addToIndices()
Definition doxygen.cpp:8140
static void computeClassRelations()
Definition doxygen.cpp:5313
static void buildFunctionList(const Entry *root)
Definition doxygen.cpp:3879
static void checkPageRelations()
Definition doxygen.cpp:9791
static void findModuleDocumentation(const Entry *root)
Definition doxygen.cpp:1267
static void findEnumDocumentation(const Entry *root)
Definition doxygen.cpp:7978
static void computePageRelations(Entry *root)
Definition doxygen.cpp:9761
static void filterMemberDocumentation(const Entry *root, const QCString &relates)
Definition doxygen.cpp:7272
static QCString createOutputDirectory(const QCString &baseDirName, const QCString &formatDirName, const char *defaultDirName)
void initResources()
static bool isVarWithConstructor(const Entry *root)
Definition doxygen.cpp:2886
static StringSet g_usingDeclarations
Definition doxygen.cpp:189
static void buildDictionaryList(const Entry *root)
Definition doxygen.cpp:3470
static bool haveEqualFileNames(const Entry *root, const MemberDef *md)
Definition doxygen.cpp:9463
static void generateNamespaceDocs()
static void buildClassDocList(const Entry *root)
Definition doxygen.cpp:1138
static void buildPageList(Entry *root)
Definition doxygen.cpp:9652
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:9291
static void mergeCategories()
Definition doxygen.cpp:8455
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:1128
static void addMethodToClass(const Entry *root, ClassDefMutable *cd, const QCString &rtype, const QCString &rname, const QCString &rargs, bool isFriend, Protection protection, bool stat, Specifier virt, TypeSpecifier spec, const QCString &relates)
Definition doxygen.cpp:3635
static std::unique_ptr< OutlineParserInterface > getParserForFile(const QCString &fn)
static void findUsedTemplateInstances()
Definition doxygen.cpp:5277
static void computeTooltipTexts()
Definition doxygen.cpp:8909
void readFileOrDirectory(const QCString &s, FileNameLinkedMap *fnMap, StringUnorderedSet *exclSet, const StringVector *patList, const StringVector *exclPatList, StringVector *resultList, StringUnorderedSet *resultSet, bool recursive, bool errorIfNotExist, StringUnorderedSet *killSet, StringUnorderedSet *paths)
static void addVariable(const Entry *root, int isFuncPtr=-1)
Definition doxygen.cpp:3098
static void addMemberSpecialization(const Entry *root, MemberName *mn, ClassDefMutable *cd, const QCString &funcType, const QCString &funcName, const QCString &funcArgs, const QCString &funcDecl, const QCString &exceptions, TypeSpecifier spec)
Definition doxygen.cpp: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:4670
static void parseFilesSingleThreading(const std::shared_ptr< Entry > &root)
parse the list of input files
static void buildCompleteMemberLists()
Definition doxygen.cpp:8477
static void generateConceptDocs()
Definition doxygen.cpp:9134
static void combineUsingRelations()
Definition doxygen.cpp:9190
static const char * getArg(int argc, char **argv, int &optInd)
static const ClassDef * findClassDefinition(FileDef *fd, NamespaceDef *nd, const QCString &scopeName)
Definition doxygen.cpp:5674
static void checkMarkdownMainfile()
static std::unordered_map< std::string, std::vector< ClassDefMutable * > > g_usingClassMap
Definition doxygen.cpp:2157
static void buildConceptDocList(const Entry *root)
Definition doxygen.cpp:1287
static int findTemplateSpecializationPosition(const QCString &name)
Definition doxygen.cpp:4814
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:4315
static void buildDefineList()
Definition doxygen.cpp:8812
static void buildInterfaceAndServiceList(const Entry *root)
Definition doxygen.cpp:3583
static void computeMemberRelations()
Definition doxygen.cpp:8420
static void buildListOfUsingDecls(const Entry *root)
Definition doxygen.cpp:1993
static void computeMemberRelationsForBaseClass(const ClassDef *cd, const BaseClassDef *bcd)
Definition doxygen.cpp:8340
static std::multimap< std::string, const Entry * > g_classEntries
Definition doxygen.cpp:186
std::vector< InputFileEncoding > InputFileEncodingList
Definition doxygen.h:81
std::unordered_map< std::string, BodyInfo > StaticInitMap
Definition doxygen.h:85
std::unordered_map< std::string, NamespaceAliasInfo > NamespaceAliasInfoMap
Definition doxygen.h:87
std::unordered_map< std::string, const Definition * > ClangUsrMap
Definition doxygen.h:83
std::unique_ptr< FileDef > createFileDef(const QCString &p, const QCString &n, const QCString &ref, const QCString &dn)
Definition filedef.cpp:268
FileDef * toFileDef(Definition *d)
Definition filedef.cpp:1954
std::unordered_set< const FileDef * > FileDefSet
Definition filedef.h:44
static void startScope(yyscan_t yyscanner)
start scope
void addNamespaceToGroups(const Entry *root, NamespaceDef *nd)
void addGroupToGroups(const Entry *root, GroupDef *subGroup)
void addClassToGroups(const Entry *root, ClassDef *cd)
void addDirToGroups(const Entry *root, DirDef *dd)
std::unique_ptr< GroupDef > createGroupDef(const QCString &fileName, int line, const QCString &name, const QCString &title, const QCString &refFileName)
Definition groupdef.cpp:175
void addConceptToGroups(const Entry *root, ConceptDef *cd)
void addMemberToGroups(const Entry *root, MemberDef *md)
void startTitle(OutputList &ol, const QCString &fileName, const DefinitionMutable *def)
Definition index.cpp:384
void endFile(OutputList &ol, bool skipNavIndex, bool skipEndContents, const QCString &navPath)
Definition index.cpp:427
void writeGraphInfo(OutputList &ol)
Definition index.cpp:4052
void endTitle(OutputList &ol, const QCString &fileName, const QCString &name)
Definition index.cpp:394
void startFile(OutputList &ol, const QCString &name, bool isSource, const QCString &manName, const QCString &title, HighlightedItem hli, bool additionalIndices, const QCString &altSidebarName, int hierarchyLevel, const QCString &allMembersFile)
Definition index.cpp:401
void writeIndexHierarchy(OutputList &ol)
Definition index.cpp:5758
Translator * theTranslator
Definition language.cpp:71
void setTranslator(OUTPUT_LANGUAGE_t langName)
Definition language.cpp:73
#define LATEX_STYLE_EXTENSION
Definition latexgen.h:22
void writeDefaultLayoutFile(const QCString &fileName)
Definition layout.cpp:1732
void printLayout()
Definition layout.cpp:1820
std::unique_ptr< MemberDef > createMemberDefAlias(const Definition *newScope, const MemberDef *aliasMd)
MemberDefMutable * toMemberDefMutable(Definition *d)
void combineDeclarationAndDefinition(MemberDefMutable *mdec, MemberDefMutable *mdef)
MemberDef * toMemberDef(Definition *d)
std::unique_ptr< MemberDef > createMemberDef(const QCString &defFileName, int defLine, int defColumn, const QCString &type, const QCString &name, const QCString &args, const QCString &excp, Protection prot, Specifier virt, bool stat, Relationship related, MemberType t, const ArgumentList &tal, const ArgumentList &al, const QCString &metaData)
Factory method to create a new instance of a MemberDef.
std::unordered_map< int, std::unique_ptr< MemberGroupInfo > > MemberGroupInfoMap
#define DOX_NOGROUP
Definition membergroup.h:26
QCString warn_line(const QCString &file, int line)
Definition message.cpp:215
void initWarningFormat()
Definition message.cpp:237
void warn_flush()
Definition message.cpp:230
void finishWarnExit()
Definition message.cpp:295
#define warn_undoc(file, line, fmt,...)
Definition message.h:102
#define warn_uncond(fmt,...)
Definition message.h:122
#define warn(file, line, fmt,...)
Definition message.h:97
#define msg(fmt,...)
Definition message.h:94
#define err(fmt,...)
Definition message.h:127
#define term(fmt,...)
Definition message.h:137
void postProcess(bool clearHeaderAndFooter, CompareMode compareMode=CompareMode::Full)
CompareMode
Definition config.h:54
void checkAndCorrect(bool quiet, const bool check)
void compareDoxyfile(TextStream &t, CompareMode compareMode)
void deinit()
bool parse(const QCString &fileName, bool update=FALSE, CompareMode compareMode=CompareMode::Full)
void init()
void writeTemplate(TextStream &t, bool shortList, bool updateOnly=FALSE)
void updateObsolete()
void correctPath(const StringVector &list)
Correct a possible wrong PATH variable.
Definition portable.cpp:517
bool isAbsolutePath(const QCString &fileName)
Definition portable.cpp:498
FILE * popen(const QCString &name, const QCString &type)
Definition portable.cpp:480
std::ofstream openOutputStream(const QCString &name, bool append=false)
Definition portable.cpp:649
double getSysElapsedTime()
Definition portable.cpp:98
QCString pathListSeparator()
Definition portable.cpp:384
uint32_t pid()
Definition portable.cpp:249
int pclose(FILE *stream)
Definition portable.cpp:489
int system(const QCString &command, const QCString &args, bool commandHasConsole=true)
Definition portable.cpp:106
void setenv(const QCString &variable, const QCString &value)
Definition portable.cpp:287
const char * commandExtension()
Definition portable.cpp:462
QCString getenv(const QCString &variable)
Definition portable.cpp:322
void setShortDir()
Definition portable.cpp:554
QCString trunc(const QCString &s, size_t numChars=15)
Definition trace.h:56
void replaceNamespaceAliases(QCString &name)
std::unique_ptr< NamespaceDef > createNamespaceDefAlias(const Definition *newScope, const NamespaceDef *nd)
Factory method to create an alias of an existing namespace.
std::unique_ptr< NamespaceDef > createNamespaceDef(const QCString &defFileName, int defLine, int defColumn, const QCString &name, const QCString &ref, const QCString &refFile, const QCString &type, bool isPublished)
Factory method to create new NamespaceDef instance.
NamespaceDef * getResolvedNamespace(const QCString &name)
NamespaceDef * toNamespaceDef(Definition *d)
NamespaceDefMutable * toNamespaceDefMutable(Definition *d)
NamespaceDefMutable * getResolvedNamespaceMutable(const QCString &key)
std::unordered_set< const NamespaceDef * > NamespaceDefSet
bool search(std::string_view str, Match &match, const Ex &re, size_t pos)
Search in a given string str starting at position pos for a match against regular expression re.
Definition regex.cpp:778
std::unique_ptr< PageDef > createPageDef(const QCString &f, int l, const QCString &n, const QCString &d, const QCString &t)
Definition pagedef.cpp:80
void generatePerlMod()
void setPerlModDoxyfile(const QCString &qs)
Portable versions of functions that are platform dependent.
int portable_iconv_close(void *cd)
void * portable_iconv_open(const char *tocode, const char *fromcode)
int qstricmp(const char *s1, const char *s2)
Definition qcstring.cpp:447
QCString substitute(const QCString &s, const QCString &src, const QCString &dst)
substitute all occurrences of src in s by dst
Definition qcstring.cpp:482
int qstricmp_sort(const char *str1, const char *str2)
Definition qcstring.h:86
const char * qPrint(const char *s)
Definition qcstring.h:687
#define TRUE
Definition qcstring.h:37
#define FALSE
Definition qcstring.h:34
uint32_t qstrlen(const char *str)
Returns the length of string str, or 0 if a null pointer is passed.
Definition qcstring.h:58
#define ASSERT(x)
Definition qcstring.h:39
int qstrcmp(const char *str1, const char *str2)
Definition qcstring.h:69
std::vector< RefItem * > RefItemVector
Definition reflist.h:133
void initSearchIndexer()
void finalizeSearchIndexer()
static void addMemberToSearchIndex(const MemberDef *md)
void createJavaScriptSearchIndex()
void writeJavaScriptSearchIndex()
Javascript based search engine.
void generateHtmlForComment(const std::string &fn, const std::string &text)
Helper for implemented the -c option of doxygen, which produces HTML output for a given doxygen forma...
void generateSqlite3()
void addSTLSupport(std::shared_ptr< Entry > &root)
Add stub entries for the most used classes in the standard template library.
Some helper functions for std::string.
void addTerminalCharIfMissing(std::string &s, char c)
Definition stringutil.h:84
This class contains the information about the argument of a function or template.
Definition arguments.h:27
QCString type
Definition arguments.h:42
QCString name
Definition arguments.h:44
QCString defval
Definition arguments.h:46
QCString array
Definition arguments.h:45
Class that contains information about an inheritance relation.
Definition classdef.h:55
ClassDef * classDef
Class definition that this relation inherits from.
Definition classdef.h:60
This class stores information about an inheritance relation.
Definition entry.h:90
Protection prot
inheritance type
Definition entry.h:95
Specifier virt
virtualness
Definition entry.h:96
QCString name
the name of the base class
Definition entry.h:94
Data associated with description found in the body.
Definition definition.h:63
Grouping info.
Definition types.h:227
QCString groupname
name of the group
Definition types.h:257
@ GROUPING_INGROUP
membership in group was defined by @ingroup
Definition types.h:236
static const char * getGroupPriName(GroupPri_t priority)
Definition types.h:240
GroupPri_t pri
priority of this definition
Definition types.h:258
static bool execute(const QCString &htmldir)
Definition htags.cpp:38
static bool loadFilemap(const QCString &htmldir)
Definition htags.cpp:107
static bool useHtags
Definition htags.h:23
const Definition * definition
Definition doxygen.h:60
const MemberDef * typeDef
Definition doxygen.h:61
stat(const char *n, double el)
Definition doxygen.cpp:266
const char * name
Definition doxygen.cpp:263
This struct is used to capture the tag file information for an Entry.
Definition entry.h:103
QCString fileName
Definition entry.h:105
QCString tagName
Definition entry.h:104
void parseTagFile(const std::shared_ptr< Entry > &root, const char *fullName)
void exitTracing()
Definition trace.cpp:52
void initTracing(const QCString &logFile, bool timing)
Definition trace.cpp:22
#define TRACE(...)
Definition trace.h:77
MemberType
Definition types.h:552
@ Enumeration
Definition types.h:557
@ EnumValue
Definition types.h:558
@ Dictionary
Definition types.h:568
@ Interface
Definition types.h:565
@ Sequence
Definition types.h:567
@ Variable
Definition types.h:555
@ Property
Definition types.h:563
@ Typedef
Definition types.h:556
@ Function
Definition types.h:554
@ Service
Definition types.h:566
Protection
Definition types.h:32
SrcLangExt
Definition types.h:207
Relationship
Definition types.h:167
Specifier
Definition types.h:80
QCString removeRedundantWhiteSpace(const QCString &s)
Definition util.cpp:576
QCString mergeScopes(const QCString &leftScope, const QCString &rightScope)
Definition util.cpp:4504
QCString normalizeNonTemplateArgumentsInString(const QCString &name, const Definition *context, const ArgumentList &formalArgs)
Definition util.cpp:4210
SrcLangExt getLanguageFromFileName(const QCString &fileName, SrcLangExt defLang)
Definition util.cpp:5107
bool protectionLevelVisible(Protection prot)
Definition util.cpp:5849
bool matchTemplateArguments(const ArgumentList &srcAl, const ArgumentList &dstAl)
Definition util.cpp:2194
void addCodeOnlyMappings()
Definition util.cpp:5101
QCString substituteTemplateArgumentsInString(const QCString &nm, const ArgumentList &formalArgs, const ArgumentList *actualArgs)
Definition util.cpp:4275
int extractClassNameFromType(const QCString &type, int &pos, QCString &name, QCString &templSpec, SrcLangExt lang)
Definition util.cpp:4136
bool leftScopeMatch(const QCString &scope, const QCString &name)
Definition util.cpp:890
QCString stripAnonymousNamespaceScope(const QCString &s)
Definition util.cpp:239
QCString stripFromIncludePath(const QCString &path)
Definition util.cpp:338
bool checkIfTypedef(const Definition *scope, const FileDef *fileScope, const QCString &n)
Definition util.cpp:5211
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:5446
bool patternMatch(const FileInfo &fi, const StringVector &patList)
Definition util.cpp:5600
bool openOutputFile(const QCString &outFile, std::ofstream &f)
Definition util.cpp:6206
QCString tempArgListToString(const ArgumentList &al, SrcLangExt lang, bool includeDefault)
Definition util.cpp:1244
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:4726
QCString showFileDefMatches(const FileNameLinkedMap *fnMap, const QCString &n)
Definition util.cpp:2951
QCString fileToString(const QCString &name, bool filter, bool isSourceCode)
Definition util.cpp:1439
QCString filterTitle(const QCString &title)
Definition util.cpp:5526
QCString removeAnonymousScopes(const QCString &str)
Definition util.cpp:170
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:1956
QCString resolveTypeDef(const Definition *context, const QCString &qualifiedName, const Definition **typedefContext)
Definition util.cpp:382
bool checkExtension(const QCString &fName, const QCString &ext)
Definition util.cpp:4818
int computeQualifiedIndex(const QCString &name)
Return the index of the last :: in the string name that is still before the first <.
Definition util.cpp:6734
void initDefaultExtensionMapping()
Definition util.cpp:5034
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:4883
QCString convertNameToFile(const QCString &name, bool allowDots, bool allowUnderscore)
Definition util.cpp:3425
QCString langToString(SrcLangExt lang)
Returns a string representation of lang.
Definition util.cpp:5803
EntryType guessSection(const QCString &name)
Definition util.cpp:347
void extractNamespaceName(const QCString &scopeName, QCString &className, QCString &namespaceName, bool allowEmptyClass)
Definition util.cpp:3618
QCString argListToString(const ArgumentList &al, bool useCanonicalType, bool showDefVals)
Definition util.cpp:1199
QCString getLanguageSpecificSeparator(SrcLangExt lang, bool classScope)
Returns the scope separator to use given the programming language lang.
Definition util.cpp:5809
void mergeMemberOverrideOptions(MemberDefMutable *md1, MemberDefMutable *md2)
Definition util.cpp:6771
QCString mangleCSharpGenericName(const QCString &name)
Definition util.cpp:6823
QCString projectLogoFile()
Definition util.cpp:3067
void mergeArguments(ArgumentList &srcAl, ArgumentList &dstAl, bool forceNameOverwrite)
Definition util.cpp:2050
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:5769
QCString stripTemplateSpecifiersFromScope(const QCString &fullName, bool parentOnly, QCString *pLastScopeStripped, QCString scopeName, bool allowArtificial)
Definition util.cpp:4437
QCString getOverloadDocs()
Definition util.cpp:4010
int getPrefixIndex(const QCString &name)
Definition util.cpp:3158
bool rightScopeMatch(const QCString &scope, const QCString &name)
Definition util.cpp:879
bool updateLanguageMapping(const QCString &extension, const QCString &language)
Definition util.cpp:5002
QCString getFileNameExtension(const QCString &fn)
Definition util.cpp:5149
QCString replaceAnonymousScopes(const QCString &s, const QCString &replacement)
Definition util.cpp:227
FileDef * findFileDef(const FileNameLinkedMap *fnMap, const QCString &n, bool &ambig)
Definition util.cpp:2823
int getScopeFragment(const QCString &s, int p, int *l)
Definition util.cpp:4549
void addHtmlExtensionIfMissing(QCString &fName)
Definition util.cpp:4823
A bunch of utility functions.
void generateXML()
Definition xmlgen.cpp:2214