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 <stdio.h>
17 #include <stdlib.h>
18 #include <sys/stat.h>
19 #include <errno.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 "message.h"
35 #include "config.h"
36 #include "util.h"
37 #include "pre.h"
38 #include "tagreader.h"
39 #include "dot.h"
40 #include "msc.h"
41 #include "docparser.h"
42 #include "dirdef.h"
43 #include "outputlist.h"
44 #include "declinfo.h"
45 #include "htmlgen.h"
46 #include "latexgen.h"
47 #include "mangen.h"
48 #include "language.h"
49 #include "debug.h"
50 #include "htmlhelp.h"
51 #include "qhp.h"
52 #include "ftvhelp.h"
53 #include "defargs.h"
54 #include "rtfgen.h"
55 #include "sqlite3gen.h"
56 #include "xmlgen.h"
57 #include "docbookgen.h"
58 #include "defgen.h"
59 #include "perlmodgen.h"
60 #include "reflist.h"
61 #include "pagedef.h"
62 #include "bufstr.h"
63 #include "commentcnv.h"
64 #include "cmdmapper.h"
65 #include "searchindex.h"
66 #include "parserintf.h"
67 #include "htags.h"
68 #include "pycode.h"
69 #include "pyscanner.h"
70 #include "fortrancode.h"
71 #include "fortranscanner.h"
72 #include "xmlcode.h"
73 #include "sqlcode.h"
74 #include "lexcode.h"
75 #include "lexscanner.h"
76 #include "code.h"
77 #include "portable.h"
78 #include "vhdljjparser.h"
79 #include "vhdldocgen.h"
80 #include "vhdlcode.h"
81 #include "eclipsehelp.h"
82 #include "cite.h"
83 #include "markdown.h"
84 #include "arguments.h"
85 #include "memberlist.h"
86 #include "layout.h"
87 #include "groupdef.h"
88 #include "classlist.h"
89 #include "namespacedef.h"
90 #include "filename.h"
91 #include "membername.h"
92 #include "membergroup.h"
93 #include "docsets.h"
94 #include "formula.h"
95 #include "settings.h"
96 #include "context.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 "fileinfo.h"
106 #include "dir.h"
107 #include "conceptdef.h"
108
109 #if USE_SQLITE3
110 #include <sqlite3.h>
111 #endif
112
113 #if USE_LIBCLANG
114 #include <clang/Basic/Version.h>
115 #endif
116
117 // provided by the generated file resources.cpp
118 extern void initResources();
119
120 #if !defined(_WIN32) || defined(__CYGWIN__)
121 #include <signal.h>
122 #define HAS_SIGNALS
123 #endif
124
125 // globally accessible variables
126 ClassLinkedMap *Doxygen::classLinkedMap = 0;
127 ClassLinkedMap *Doxygen::hiddenClassLinkedMap = 0;
128 ConceptLinkedMap *Doxygen::conceptLinkedMap = 0;
129 NamespaceLinkedMap *Doxygen::namespaceLinkedMap = 0;
130 MemberNameLinkedMap *Doxygen::memberNameLinkedMap = 0;
131 MemberNameLinkedMap *Doxygen::functionNameLinkedMap = 0;
132 FileNameLinkedMap *Doxygen::inputNameLinkedMap = 0;
133 GroupLinkedMap *Doxygen::groupLinkedMap = 0;
134 PageLinkedMap *Doxygen::pageLinkedMap = 0;
135 PageLinkedMap *Doxygen::exampleLinkedMap = 0;
136 StringMap Doxygen::aliasMap; // aliases
137 StringSet Doxygen::inputPaths;
138 FileNameLinkedMap *Doxygen::includeNameLinkedMap = 0; // include names
139 FileNameLinkedMap *Doxygen::exampleNameLinkedMap = 0; // examples
140 FileNameLinkedMap *Doxygen::imageNameLinkedMap = 0; // images
141 FileNameLinkedMap *Doxygen::dotFileNameLinkedMap = 0; // dot files
142 FileNameLinkedMap *Doxygen::mscFileNameLinkedMap = 0; // msc files
143 FileNameLinkedMap *Doxygen::diaFileNameLinkedMap = 0; // dia files
144 StringUnorderedMap Doxygen::namespaceAliasMap; // all namespace aliases
145 StringMap Doxygen::tagDestinationMap; // all tag locations
146 StringUnorderedSet Doxygen::expandAsDefinedSet; // all macros that should be expanded
147 MemberGroupInfoMap Doxygen::memberGroupInfoMap; // dictionary of the member groups heading
148 std::unique_ptr<PageDef> Doxygen::mainPage;
149 bool Doxygen::insideMainPage = FALSE; // are we generating docs for the main page?
150 NamespaceDefMutable *Doxygen::globalScope = 0;
151 bool Doxygen::parseSourcesNeeded = FALSE;
152 SearchIndexIntf *Doxygen::searchIndex=0;
153 SymbolMap<Definition>*Doxygen::symbolMap;
154 ClangUsrMap *Doxygen::clangUsrMap = 0;
155 Cache<std::string,LookupInfo> *Doxygen::lookupCache;
156 DirLinkedMap *Doxygen::dirLinkedMap;
157 DirRelationLinkedMap Doxygen::dirRelations;
158 ParserManager *Doxygen::parserManager = 0;
159 QCString Doxygen::htmlFileExtension;
160 bool Doxygen::suppressDocWarnings = FALSE;
161 QCString Doxygen::filterDBFileName;
162 IndexList *Doxygen::indexList;
163 int Doxygen::subpageNestingLevel = 0;
164 QCString Doxygen::spaces;
165 bool Doxygen::generatingXmlOutput = FALSE;
166 DefinesPerFileList Doxygen::macroDefinitions;
167 bool Doxygen::clangAssistedParsing = FALSE;
168
169 // locally accessible globals
170 static std::multimap< std::string, const Entry* > g_classEntries;
171 static StringVector g_inputFiles;
172 static StringSet g_compoundKeywords; // keywords recognised as compounds
173 static OutputList *g_outputList = 0; // list of output generating objects
174 static StringSet g_usingDeclarations; // used classes
175 static bool g_successfulRun = FALSE;
176 static bool g_dumpSymbolMap = FALSE;
177 static bool g_useOutputTemplate = FALSE;
178
clearAll()179 void clearAll()
180 {
181 g_inputFiles.clear();
182 //g_excludeNameDict.clear();
183 //delete g_outputList; g_outputList=0;
184
185 Doxygen::classLinkedMap->clear();
186 Doxygen::hiddenClassLinkedMap->clear();
187 Doxygen::conceptLinkedMap->clear();
188 Doxygen::namespaceLinkedMap->clear();
189 Doxygen::pageLinkedMap->clear();
190 Doxygen::exampleLinkedMap->clear();
191 Doxygen::inputNameLinkedMap->clear();
192 Doxygen::includeNameLinkedMap->clear();
193 Doxygen::exampleNameLinkedMap->clear();
194 Doxygen::imageNameLinkedMap->clear();
195 Doxygen::dotFileNameLinkedMap->clear();
196 Doxygen::mscFileNameLinkedMap->clear();
197 Doxygen::diaFileNameLinkedMap->clear();
198 Doxygen::tagDestinationMap.clear();
199 SectionManager::instance().clear();
200 CitationManager::instance().clear();
201 Doxygen::mainPage.reset();
202 FormulaManager::instance().clear();
203 }
204
205 class Statistics
206 {
207 public:
Statistics()208 Statistics() {}
begin(const char * name)209 void begin(const char *name)
210 {
211 msg("%s", name);
212 stats.emplace_back(name,0);
213 startTime = std::chrono::steady_clock::now();
214 }
end()215 void end()
216 {
217 std::chrono::steady_clock::time_point endTime = std::chrono::steady_clock::now();
218 stats.back().elapsed = std::chrono::duration_cast<
219 std::chrono::microseconds>(endTime - startTime).count()/1000000.0;
220 }
print()221 void print()
222 {
223 bool restore=FALSE;
224 if (Debug::isFlagSet(Debug::Time))
225 {
226 Debug::clearFlag("time");
227 restore=TRUE;
228 }
229 msg("----------------------\n");
230 for (const auto &s : stats)
231 {
232 msg("Spent %.6f seconds in %s",s.elapsed,s.name);
233 }
234 if (restore) Debug::setFlag("time");
235 }
236 private:
237 struct stat
238 {
239 const char *name;
240 double elapsed;
241 //stat() : name(NULL),elapsed(0) {}
statStatistics::stat242 stat(const char *n, double el) : name(n),elapsed(el) {}
243 };
244 std::vector<stat> stats;
245 std::chrono::steady_clock::time_point startTime;
246 } g_s;
247
248
249 static void addMemberDocs(const Entry *root,MemberDefMutable *md, const QCString &funcDecl,
250 const ArgumentList *al,bool over_load,uint64 spec);
251 static void findMember(const Entry *root,
252 const QCString &relates,
253 const QCString &type,
254 const QCString &args,
255 QCString funcDecl,
256 bool overloaded,
257 bool isFunc
258 );
259
260 enum FindBaseClassRelation_Mode
261 {
262 TemplateInstances,
263 DocumentedOnly,
264 Undocumented
265 };
266
267
268 static bool findClassRelation(
269 const Entry *root,
270 Definition *context,
271 ClassDefMutable *cd,
272 const BaseInfo *bi,
273 const TemplateNameMap &templateNames,
274 /*bool insertUndocumented*/
275 FindBaseClassRelation_Mode mode,
276 bool isArtificial
277 );
278
279 //----------------------------------------------------------------------------
280
281 static Definition *findScopeFromQualifiedName(NamespaceDefMutable *startScope,const QCString &n,
282 FileDef *fileScope,const TagInfo *tagInfo);
283
addPageToContext(PageDef * pd,Entry * root)284 static void addPageToContext(PageDef *pd,Entry *root)
285 {
286 if (root->parent()) // add the page to it's scope
287 {
288 QCString scope = root->parent()->name;
289 if (root->parent()->section==Entry::PACKAGEDOC_SEC)
290 {
291 scope=substitute(scope,".","::");
292 }
293 scope = stripAnonymousNamespaceScope(scope);
294 scope+="::"+pd->name();
295 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,scope,0,root->tagInfo());
296 if (d)
297 {
298 pd->setPageScope(d);
299 }
300 }
301 }
302
addRelatedPage(Entry * root)303 static void addRelatedPage(Entry *root)
304 {
305 GroupDef *gd=0;
306 for (const Grouping &g : root->groups)
307 {
308 if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname))) break;
309 }
310 //printf("---> addRelatedPage() %s gd=%p\n",qPrint(root->name),gd);
311 QCString doc=root->doc+root->inbodyDocs;
312
313 PageDef *pd = addRelatedPage(root->name,root->args,doc,
314 root->docFile,
315 root->docLine,
316 root->startLine,
317 root->sli,
318 gd,root->tagInfo(),
319 FALSE,
320 root->lang
321 );
322 if (pd)
323 {
324 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
325 pd->addSectionsToDefinition(root->anchors);
326 pd->setLocalToc(root->localToc);
327 addPageToContext(pd,root);
328 }
329 }
330
buildGroupListFiltered(const Entry * root,bool additional,bool includeExternal)331 static void buildGroupListFiltered(const Entry *root,bool additional, bool includeExternal)
332 {
333 if (root->section==Entry::GROUPDOC_SEC && !root->name.isEmpty() &&
334 ((!includeExternal && root->tagInfo()==0) ||
335 ( includeExternal && root->tagInfo()!=0))
336 )
337 {
338 if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
339 (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
340 {
341 GroupDef *gd = Doxygen::groupLinkedMap->find(root->name);
342 //printf("Processing group '%s':'%s' add=%d ext=%d gd=%p\n",
343 // qPrint(root->type),qPrint(root->name),additional,includeExternal,gd);
344
345 if (gd)
346 {
347 if ( !gd->hasGroupTitle() )
348 {
349 gd->setGroupTitle( root->type );
350 }
351 else if ( root->type.length() > 0 && root->name != root->type && gd->groupTitle() != root->type )
352 {
353 warn( root->fileName,root->startLine,
354 "group %s: ignoring title \"%s\" that does not match old title \"%s\"\n",
355 qPrint(root->name), qPrint(root->type), qPrint(gd->groupTitle()) );
356 }
357 gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
358 gd->setDocumentation( root->doc, root->docFile, root->docLine );
359 gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
360 gd->addSectionsToDefinition(root->anchors);
361 gd->setRefItems(root->sli);
362 gd->setLanguage(root->lang);
363 }
364 else
365 {
366 if (root->tagInfo())
367 {
368 gd = Doxygen::groupLinkedMap->add(root->name,
369 std::unique_ptr<GroupDef>(
370 createGroupDef(root->fileName,root->startLine,root->name,root->type,root->tagInfo()->fileName)));
371 gd->setReference(root->tagInfo()->tagName);
372 }
373 else
374 {
375 gd = Doxygen::groupLinkedMap->add(root->name,
376 std::unique_ptr<GroupDef>(
377 createGroupDef(root->fileName,root->startLine,root->name,root->type)));
378 }
379 gd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
380 // allow empty docs for group
381 gd->setDocumentation(!root->doc.isEmpty() ? root->doc : QCString(" "),root->docFile,root->docLine,FALSE);
382 gd->setInbodyDocumentation( root->inbodyDocs, root->inbodyFile, root->inbodyLine );
383 gd->addSectionsToDefinition(root->anchors);
384 gd->setRefItems(root->sli);
385 gd->setLanguage(root->lang);
386 }
387 }
388 }
389 for (const auto &e : root->children()) buildGroupListFiltered(e.get(),additional,includeExternal);
390 }
391
buildGroupList(const Entry * root)392 static void buildGroupList(const Entry *root)
393 {
394 // --- first process only local groups
395 // first process the @defgroups blocks
396 buildGroupListFiltered(root,FALSE,FALSE);
397 // then process the @addtogroup, @weakgroup blocks
398 buildGroupListFiltered(root,TRUE,FALSE);
399
400 // --- then also process external groups
401 // first process the @defgroups blocks
402 buildGroupListFiltered(root,FALSE,TRUE);
403 // then process the @addtogroup, @weakgroup blocks
404 buildGroupListFiltered(root,TRUE,TRUE);
405 }
406
findGroupScope(const Entry * root)407 static void findGroupScope(const Entry *root)
408 {
409 if (root->section==Entry::GROUPDOC_SEC && !root->name.isEmpty() &&
410 root->parent() && !root->parent()->name.isEmpty())
411 {
412 GroupDef *gd;
413 if ((gd=Doxygen::groupLinkedMap->find(root->name)))
414 {
415 QCString scope = root->parent()->name;
416 if (root->parent()->section==Entry::PACKAGEDOC_SEC)
417 {
418 scope=substitute(scope,".","::");
419 }
420 scope = stripAnonymousNamespaceScope(scope);
421 scope+="::"+gd->name();
422 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,scope,0,root->tagInfo());
423 if (d)
424 {
425 gd->setGroupScope(d);
426 }
427 }
428 }
429 for (const auto &e : root->children()) findGroupScope(e.get());
430 }
431
organizeSubGroupsFiltered(const Entry * root,bool additional)432 static void organizeSubGroupsFiltered(const Entry *root,bool additional)
433 {
434 if (root->section==Entry::GROUPDOC_SEC && !root->name.isEmpty())
435 {
436 if ((root->groupDocType==Entry::GROUPDOC_NORMAL && !additional) ||
437 (root->groupDocType!=Entry::GROUPDOC_NORMAL && additional))
438 {
439 GroupDef *gd;
440 if ((gd=Doxygen::groupLinkedMap->find(root->name)))
441 {
442 //printf("adding %s to group %s\n",qPrint(root->name),qPrint(gd->name()));
443 addGroupToGroups(root,gd);
444 }
445 }
446 }
447 for (const auto &e : root->children()) organizeSubGroupsFiltered(e.get(),additional);
448 }
449
organizeSubGroups(const Entry * root)450 static void organizeSubGroups(const Entry *root)
451 {
452 //printf("Defining groups\n");
453 // first process the @defgroups blocks
454 organizeSubGroupsFiltered(root,FALSE);
455 //printf("Additional groups\n");
456 // then process the @addtogroup, @weakgroup blocks
457 organizeSubGroupsFiltered(root,TRUE);
458 }
459
460 //----------------------------------------------------------------------
461
buildFileList(const Entry * root)462 static void buildFileList(const Entry *root)
463 {
464 if (((root->section==Entry::FILEDOC_SEC) ||
465 ((root->section & Entry::FILE_MASK) && Config_getBool(EXTRACT_ALL))) &&
466 !root->name.isEmpty() && !root->tagInfo() // skip any file coming from tag files
467 )
468 {
469 bool ambig;
470 FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,root->name,ambig);
471 if (!fd || ambig)
472 {
473 int save_ambig = ambig;
474 // use the directory of the file to see if the described file is in the same
475 // directory as the describing file.
476 QCString fn = root->fileName;
477 int newIndex=fn.findRev('/');
478 if (newIndex<0)
479 {
480 fn = root->name;
481 }
482 else
483 {
484 fn = fn.left(newIndex)+"/"+root->name;
485 }
486 fd=findFileDef(Doxygen::inputNameLinkedMap,fn,ambig);
487 if (!fd) ambig = save_ambig;
488 }
489 //printf("**************** root->name=%s fd=%p\n",qPrint(root->name),fd);
490 if (fd && !ambig)
491 {
492 //printf("Adding documentation!\n");
493 // using FALSE in setDocumentation is small hack to make sure a file
494 // is documented even if a \file command is used without further
495 // documentation
496 fd->setDocumentation(root->doc,root->docFile,root->docLine,FALSE);
497 fd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
498 fd->addSectionsToDefinition(root->anchors);
499 fd->setRefItems(root->sli);
500 for (const Grouping &g : root->groups)
501 {
502 GroupDef *gd=0;
503 if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname)))
504 {
505 gd->addFile(fd);
506 fd->makePartOfGroup(gd);
507 //printf("File %s: in group %s\n",qPrint(fd->name()),qPrint(gd->name()));
508 }
509 }
510 }
511 else
512 {
513 QCString text(4096);
514 text.sprintf("the name '%s' supplied as "
515 "the argument in the \\file statement ",
516 qPrint(root->name));
517 if (ambig) // name is ambiguous
518 {
519 text+="matches the following input files:\n";
520 text+=showFileDefMatches(Doxygen::inputNameLinkedMap,root->name);
521 text+="Please use a more specific name by "
522 "including a (larger) part of the path!";
523 }
524 else // name is not an input file
525 {
526 text+="is not an input file";
527 }
528 warn(root->fileName,root->startLine,"%s", qPrint(text));
529 }
530 }
531 for (const auto &e : root->children()) buildFileList(e.get());
532 }
533
534 template<class DefMutable>
addIncludeFile(DefMutable * cd,FileDef * ifd,const Entry * root)535 static void addIncludeFile(DefMutable *cd,FileDef *ifd,const Entry *root)
536 {
537 if (
538 (!root->doc.stripWhiteSpace().isEmpty() ||
539 !root->brief.stripWhiteSpace().isEmpty() ||
540 Config_getBool(EXTRACT_ALL)
541 ) && root->protection!=Private
542 )
543 {
544 //printf(">>>>>> includeFile=%s\n",qPrint(root->includeFile));
545
546 bool local=Config_getBool(FORCE_LOCAL_INCLUDES);
547 QCString includeFile = root->includeFile;
548 if (!includeFile.isEmpty() && includeFile.at(0)=='"')
549 {
550 local = TRUE;
551 includeFile=includeFile.mid(1,includeFile.length()-2);
552 }
553 else if (!includeFile.isEmpty() && includeFile.at(0)=='<')
554 {
555 local = FALSE;
556 includeFile=includeFile.mid(1,includeFile.length()-2);
557 }
558
559 bool ambig;
560 FileDef *fd=0;
561 // see if we need to include a verbatim copy of the header file
562 //printf("root->includeFile=%s\n",qPrint(root->includeFile));
563 if (!includeFile.isEmpty() &&
564 (fd=findFileDef(Doxygen::inputNameLinkedMap,includeFile,ambig))==0
565 )
566 { // explicit request
567 QCString text;
568 text.sprintf("the name '%s' supplied as "
569 "the argument of the \\class, \\struct, \\union, or \\include command ",
570 qPrint(includeFile)
571 );
572 if (ambig) // name is ambiguous
573 {
574 text+="matches the following input files:\n";
575 text+=showFileDefMatches(Doxygen::inputNameLinkedMap,root->includeFile);
576 text+="Please use a more specific name by "
577 "including a (larger) part of the path!";
578 }
579 else // name is not an input file
580 {
581 text+="is not an input file";
582 }
583 warn(root->fileName,root->startLine, "%s", qPrint(text));
584 }
585 else if (includeFile.isEmpty() && ifd &&
586 // see if the file extension makes sense
587 guessSection(ifd->name())==Entry::HEADER_SEC)
588 { // implicit assumption
589 fd=ifd;
590 }
591
592 // if a file is found, we mark it as a source file.
593 if (fd)
594 {
595 QCString iName = !root->includeName.isEmpty() ?
596 root->includeName : includeFile;
597 if (!iName.isEmpty()) // user specified include file
598 {
599 if (iName.at(0)=='<') local=FALSE; // explicit override
600 else if (iName.at(0)=='"') local=TRUE;
601 if (iName.at(0)=='"' || iName.at(0)=='<')
602 {
603 iName=iName.mid(1,iName.length()-2); // strip quotes or brackets
604 }
605 if (iName.isEmpty())
606 {
607 iName=fd->name();
608 }
609 }
610 else if (!Config_getList(STRIP_FROM_INC_PATH).empty())
611 {
612 iName=stripFromIncludePath(fd->absFilePath());
613 }
614 else // use name of the file containing the class definition
615 {
616 iName=fd->name();
617 }
618 if (fd->generateSourceFile()) // generate code for header
619 {
620 cd->setIncludeFile(fd,iName,local,!root->includeName.isEmpty());
621 }
622 else // put #include in the class documentation without link
623 {
624 cd->setIncludeFile(0,iName,local,TRUE);
625 }
626 }
627 }
628 }
629
630 #if 0
631 static bool addNamespace(Entry *root,ClassDef *cd)
632 {
633 // see if this class is defined inside a namespace
634 if (root->section & Entry::COMPOUND_MASK)
635 {
636 Entry *e = root->parent;
637 while (e)
638 {
639 if (e->section==Entry::NAMESPACE_SEC)
640 {
641 NamespaceDef *nd=0;
642 QCString nsName = stripAnonymousNamespaceScope(e->name);
643 //printf("addNameSpace() trying: %s\n",qPrint(nsName));
644 if (!nsName.isEmpty() && nsName.at(0)!='@' &&
645 (nd=getResolvedNamespace(nsName))
646 )
647 {
648 cd->setNamespace(nd);
649 cd->setOuterScope(nd);
650 nd->insertClass(cd);
651 return TRUE;
652 }
653 }
654 e=e->parent;
655 }
656 }
657 return FALSE;
658 }
659 #endif
660
661 #if 0
662 static Definition *findScope(Entry *root,int level=0)
663 {
664 if (root==0) return 0;
665 //printf("start findScope name=%s\n",qPrint(root->name));
666 Definition *result=0;
667 if (root->section&Entry::SCOPE_MASK)
668 {
669 result = findScope(root->parent,level+1); // traverse to the root of the tree
670 if (result)
671 {
672 //printf("Found %s inside %s at level %d\n",qPrint(root->name),qPrint(result->name()),level);
673 // TODO: look at template arguments
674 result = result->findInnerCompound(root->name);
675 }
676 else // reached the global scope
677 {
678 // TODO: look at template arguments
679 result = Doxygen::globalScope->findInnerCompound(root->name);
680 //printf("Found in globalScope %s at level %d\n",qPrint(result->name()),level);
681 }
682 }
683 //printf("end findScope(%s,%d)=%s\n",qPrint(root->name),
684 // level,result==0 ? "<none>" : qPrint(result->name()));
685 return result;
686 }
687 #endif
688
stripTemplateSpecifiers(const QCString & s)689 QCString stripTemplateSpecifiers(const QCString &s)
690 {
691 int l = s.length();
692 int count=0;
693 int round=0;
694 QCString result;
695 for (int i=0;i<l;i++)
696 {
697 char c=s.at(i);
698 if (c=='(') round++;
699 else if (c==')' && round>0) round--;
700 else if (c=='<' && round==0) count++;
701 if (count==0)
702 {
703 result+=c;
704 }
705 if (c=='>' && round==0 && count>0) count--;
706 }
707 //printf("stripTemplateSpecifiers(%s)=%s\n",qPrint(s),qPrint(result));
708 return result;
709 }
710
711 /*! returns the Definition object belonging to the first \a level levels of
712 * full qualified name \a name. Creates an artificial scope if the scope is
713 * not found and set the parent/child scope relation if the scope is found.
714 */
buildScopeFromQualifiedName(const QCString & name_,SrcLangExt lang,const TagInfo * tagInfo)715 static Definition *buildScopeFromQualifiedName(const QCString &name_,SrcLangExt lang,const TagInfo *tagInfo)
716 {
717 QCString name = stripTemplateSpecifiers(name_);
718 int level = name.contains("::");
719 //printf("buildScopeFromQualifiedName(%s) level=%d\n",qPrint(name),level);
720 int i=0;
721 int p=0,l;
722 Definition *prevScope=Doxygen::globalScope;
723 QCString fullScope;
724 while (i<level)
725 {
726 int idx=getScopeFragment(name,p,&l);
727 if (idx==-1) return prevScope;
728 QCString nsName = name.mid(idx,l);
729 if (nsName.isEmpty()) return prevScope;
730 if (!fullScope.isEmpty()) fullScope+="::";
731 fullScope+=nsName;
732 NamespaceDef *nd=Doxygen::namespaceLinkedMap->find(fullScope);
733 DefinitionMutable *innerScope = toDefinitionMutable(nd);
734 ClassDef *cd=0;
735 if (nd==0) cd = getClass(fullScope);
736 if (nd==0 && cd) // scope is a class
737 {
738 innerScope = toDefinitionMutable(cd);
739 }
740 else if (nd==0 && cd==0 && fullScope.find('<')==-1) // scope is not known and could be a namespace!
741 {
742 // introduce bogus namespace
743 //printf("++ adding dummy namespace %s to %s tagInfo=%p\n",qPrint(nsName),qPrint(prevScope->name()),(void*)tagInfo);
744 NamespaceDefMutable *newNd=
745 toNamespaceDefMutable(
746 Doxygen::namespaceLinkedMap->add(fullScope,
747 std::unique_ptr<NamespaceDef>(
748 createNamespaceDef(
749 "[generated]",1,1,fullScope,
750 tagInfo?tagInfo->tagName:QCString(),
751 tagInfo?tagInfo->fileName:QCString()))));
752 if (newNd)
753 {
754 newNd->setLanguage(lang);
755 newNd->setArtificial(TRUE);
756 // add namespace to the list
757 innerScope = newNd;
758 }
759 }
760 else // scope is a namespace
761 {
762 }
763 if (innerScope)
764 {
765 // make the parent/child scope relation
766 DefinitionMutable *prevScopeMutable = toDefinitionMutable(prevScope);
767 if (prevScopeMutable)
768 {
769 prevScopeMutable->addInnerCompound(toDefinition(innerScope));
770 }
771 innerScope->setOuterScope(prevScope);
772 }
773 else // current scope is a class, so return only the namespace part...
774 {
775 return prevScope;
776 }
777 // proceed to the next scope fragment
778 p=idx+l+2;
779 prevScope=toDefinition(innerScope);
780 i++;
781 }
782 return prevScope;
783 }
784
findScopeFromQualifiedName(NamespaceDefMutable * startScope,const QCString & n,FileDef * fileScope,const TagInfo * tagInfo)785 static Definition *findScopeFromQualifiedName(NamespaceDefMutable *startScope,const QCString &n,
786 FileDef *fileScope,const TagInfo *tagInfo)
787 {
788 //printf("<findScopeFromQualifiedName(%s,%s)\n",startScope ? qPrint(startScope->name()) : 0, qPrint(n));
789 Definition *resultScope=toDefinition(startScope);
790 if (resultScope==0) resultScope=Doxygen::globalScope;
791 QCString scope=stripTemplateSpecifiersFromScope(n,FALSE);
792 int l1=0,i1;
793 i1=getScopeFragment(scope,0,&l1);
794 if (i1==-1)
795 {
796 //printf(">no fragments!\n");
797 return resultScope;
798 }
799 int p=i1+l1,l2=0,i2;
800 while ((i2=getScopeFragment(scope,p,&l2))!=-1)
801 {
802 QCString nestedNameSpecifier = scope.mid(i1,l1);
803 Definition *orgScope = resultScope;
804 //printf(" nestedNameSpecifier=%s\n",qPrint(nestedNameSpecifier));
805 resultScope = const_cast<Definition*>(resultScope->findInnerCompound(nestedNameSpecifier));
806 //printf(" resultScope=%p\n",resultScope);
807 if (resultScope==0)
808 {
809 if (orgScope==Doxygen::globalScope && fileScope && !fileScope->getUsedNamespaces().empty())
810 // also search for used namespaces
811 {
812 for (const auto &nd : fileScope->getUsedNamespaces())
813 {
814 resultScope = findScopeFromQualifiedName(toNamespaceDefMutable(nd),n,fileScope,tagInfo);
815 if (resultScope!=0) break;
816 }
817 if (resultScope)
818 {
819 // for a nested class A::I in used namespace N, we get
820 // N::A::I while looking for A, so we should compare
821 // resultScope->name() against scope.left(i2+l2)
822 //printf(" -> result=%s scope=%s\n",qPrint(resultScope->name()),qPrint(scope));
823 if (rightScopeMatch(resultScope->name(),scope.left(i2+l2)))
824 {
825 break;
826 }
827 goto nextFragment;
828 }
829 }
830
831 // also search for used classes. Complication: we haven't been able
832 // to put them in the right scope yet, because we are still resolving
833 // the scope relations!
834 // Therefore loop through all used classes and see if there is a right
835 // scope match between the used class and nestedNameSpecifier.
836 for (const auto &usedName : g_usingDeclarations)
837 {
838 //printf("Checking using class %s\n",ui.currentKey());
839 if (rightScopeMatch(usedName.c_str(),nestedNameSpecifier))
840 {
841 // ui.currentKey() is the fully qualified name of nestedNameSpecifier
842 // so use this instead.
843 QCString fqn = QCString(usedName) + scope.right(scope.length()-p);
844 resultScope = buildScopeFromQualifiedName(fqn,startScope->getLanguage(),0);
845 //printf("Creating scope from fqn=%s result %p\n",qPrint(fqn),resultScope);
846 if (resultScope)
847 {
848 //printf("> Match! resultScope=%s\n",qPrint(resultScope->name()));
849 return resultScope;
850 }
851 }
852 }
853
854 //printf("> name %s not found in scope %s\n",qPrint(nestedNameSpecifier),qPrint(orgScope->name()));
855 return 0;
856 }
857 nextFragment:
858 i1=i2;
859 l1=l2;
860 p=i2+l2;
861 }
862 //printf(">findScopeFromQualifiedName scope %s\n",qPrint(resultScope->name()));
863 return resultScope;
864 }
865
getTemplateArgumentsFromName(const QCString & name,const ArgumentLists & tArgLists)866 std::unique_ptr<ArgumentList> getTemplateArgumentsFromName(
867 const QCString &name,
868 const ArgumentLists &tArgLists)
869 {
870 // for each scope fragment, check if it is a template and advance through
871 // the list if so.
872 int i,p=0;
873 auto alIt = tArgLists.begin();
874 while ((i=name.find("::",p))!=-1 && alIt!=tArgLists.end())
875 {
876 NamespaceDef *nd = Doxygen::namespaceLinkedMap->find(name.left(i));
877 if (nd==0)
878 {
879 ClassDef *cd = getClass(name.left(i));
880 if (cd)
881 {
882 if (!cd->templateArguments().empty())
883 {
884 ++alIt;
885 }
886 }
887 }
888 p=i+2;
889 }
890 return alIt!=tArgLists.end() ?
891 std::make_unique<ArgumentList>(*alIt) :
892 std::unique_ptr<ArgumentList>();
893 }
894
895 static
convertToCompoundType(int section,uint64 specifier)896 ClassDef::CompoundType convertToCompoundType(int section,uint64 specifier)
897 {
898 ClassDef::CompoundType sec=ClassDef::Class;
899 if (specifier&Entry::Struct)
900 sec=ClassDef::Struct;
901 else if (specifier&Entry::Union)
902 sec=ClassDef::Union;
903 else if (specifier&Entry::Category)
904 sec=ClassDef::Category;
905 else if (specifier&Entry::Interface)
906 sec=ClassDef::Interface;
907 else if (specifier&Entry::Protocol)
908 sec=ClassDef::Protocol;
909 else if (specifier&Entry::Exception)
910 sec=ClassDef::Exception;
911 else if (specifier&Entry::Service)
912 sec=ClassDef::Service;
913 else if (specifier&Entry::Singleton)
914 sec=ClassDef::Singleton;
915
916 switch(section)
917 {
918 //case Entry::UNION_SEC:
919 case Entry::UNIONDOC_SEC:
920 sec=ClassDef::Union;
921 break;
922 //case Entry::STRUCT_SEC:
923 case Entry::STRUCTDOC_SEC:
924 sec=ClassDef::Struct;
925 break;
926 //case Entry::INTERFACE_SEC:
927 case Entry::INTERFACEDOC_SEC:
928 sec=ClassDef::Interface;
929 break;
930 //case Entry::PROTOCOL_SEC:
931 case Entry::PROTOCOLDOC_SEC:
932 sec=ClassDef::Protocol;
933 break;
934 //case Entry::CATEGORY_SEC:
935 case Entry::CATEGORYDOC_SEC:
936 sec=ClassDef::Category;
937 break;
938 //case Entry::EXCEPTION_SEC:
939 case Entry::EXCEPTIONDOC_SEC:
940 sec=ClassDef::Exception;
941 break;
942 case Entry::SERVICEDOC_SEC:
943 sec=ClassDef::Service;
944 break;
945 case Entry::SINGLETONDOC_SEC:
946 sec=ClassDef::Singleton;
947 break;
948 }
949 return sec;
950 }
951
952
addClassToContext(const Entry * root)953 static void addClassToContext(const Entry *root)
954 {
955 FileDef *fd = root->fileDef();
956
957 QCString scName;
958 if (root->parent()->section&Entry::SCOPE_MASK)
959 {
960 scName=root->parent()->name;
961 }
962 // name without parent's scope
963 QCString fullName = root->name;
964
965 // strip off any template parameters (but not those for specializations)
966 fullName=stripTemplateSpecifiersFromScope(fullName);
967
968 // name with scope (if not present already)
969 QCString qualifiedName = fullName;
970 if (!scName.isEmpty() && !leftScopeMatch(fullName,scName))
971 {
972 qualifiedName.prepend(scName+"::");
973 }
974
975 // see if we already found the class before
976 ClassDefMutable *cd = getClassMutable(qualifiedName);
977
978 Debug::print(Debug::Classes,0, " Found class with name %s (qualifiedName=%s -> cd=%p)\n",
979 cd ? qPrint(cd->name()) : qPrint(root->name), qPrint(qualifiedName),cd);
980
981 if (cd)
982 {
983 fullName=cd->name();
984 Debug::print(Debug::Classes,0," Existing class %s!\n",qPrint(cd->name()));
985 //if (cd->templateArguments()==0)
986 //{
987 // //printf("existing ClassDef tempArgList=%p specScope=%s\n",root->tArgList,qPrint(root->scopeSpec));
988 // cd->setTemplateArguments(tArgList);
989 //}
990
991 cd->setDocumentation(root->doc,root->docFile,root->docLine);
992 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
993
994 if ((root->spec&Entry::ForwardDecl)==0 && cd->isForwardDeclared())
995 {
996 cd->setDefFile(root->fileName,root->startLine,root->startColumn);
997 if (root->bodyLine!=-1)
998 {
999 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1000 cd->setBodyDef(fd);
1001 }
1002 }
1003
1004 if (cd->templateArguments().empty() || (cd->isForwardDeclared() && (root->spec&Entry::ForwardDecl)==0))
1005 {
1006 // this happens if a template class declared with @class is found
1007 // before the actual definition or if a forward declaration has different template
1008 // parameter names.
1009 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(cd->name(),root->tArgLists);
1010 if (tArgList)
1011 {
1012 cd->setTemplateArguments(*tArgList);
1013 }
1014 }
1015 if (cd->requiresClause().isEmpty() && !root->req.isEmpty())
1016 {
1017 cd->setRequiresClause(root->req);
1018 }
1019
1020 cd->setCompoundType(convertToCompoundType(root->section,root->spec));
1021
1022 cd->setMetaData(root->metaData);
1023 }
1024 else // new class
1025 {
1026 ClassDef::CompoundType sec = convertToCompoundType(root->section,root->spec);
1027
1028 QCString className;
1029 QCString namespaceName;
1030 extractNamespaceName(fullName,className,namespaceName);
1031
1032 //printf("New class: fullname %s namespace '%s' name='%s' brief='%s' docs='%s'\n",
1033 // qPrint(fullName),qPrint(namespaceName),qPrint(className),qPrint(root->brief),qPrint(root->doc));
1034
1035 QCString tagName;
1036 QCString refFileName;
1037 const TagInfo *tagInfo = root->tagInfo();
1038 int i;
1039 if (tagInfo)
1040 {
1041 tagName = tagInfo->tagName;
1042 refFileName = tagInfo->fileName;
1043 if (fullName.find("::")!=-1)
1044 // symbols imported via tag files may come without the parent scope,
1045 // so we artificially create it here
1046 {
1047 buildScopeFromQualifiedName(fullName,root->lang,tagInfo);
1048 }
1049 }
1050 std::unique_ptr<ArgumentList> tArgList;
1051 if ((root->lang==SrcLangExt_CSharp || root->lang==SrcLangExt_Java) && (i=fullName.findRev('<'))!=-1)
1052 {
1053 // a Java/C# generic class looks like a C++ specialization, so we need to split the
1054 // name and template arguments here
1055 tArgList = stringToArgumentList(root->lang,fullName.mid(i));
1056 fullName=fullName.left(i);
1057 }
1058 else
1059 {
1060 tArgList = getTemplateArgumentsFromName(fullName,root->tArgLists);
1061 }
1062 // add class to the list
1063 //printf("ClassDict.insert(%s)\n",qPrint(fullName));
1064 cd = toClassDefMutable(
1065 Doxygen::classLinkedMap->add(fullName,
1066 std::unique_ptr<ClassDef>(
1067 createClassDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1068 fullName,sec,tagName,refFileName,TRUE,root->spec&Entry::Enum) )));
1069 if (cd)
1070 {
1071 Debug::print(Debug::Classes,0," New class '%s' (sec=0x%08x)! #tArgLists=%d tagInfo=%p hidden=%d artificial=%d\n",
1072 qPrint(fullName),sec,root->tArgLists.size(), tagInfo,root->hidden,root->artificial);
1073 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1074 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1075 cd->setLanguage(root->lang);
1076 cd->setId(root->id);
1077 cd->setHidden(root->hidden);
1078 cd->setArtificial(root->artificial);
1079 cd->setClassSpecifier(root->spec);
1080 cd->setTypeConstraints(root->typeConstr);
1081 //printf("new ClassDef %s tempArgList=%p specScope=%s\n",qPrint(fullName),root->tArgList,qPrint(root->scopeSpec));
1082
1083 //printf("class %s template args=%s\n",qPrint(fullName),
1084 // tArgList ? tempArgListToString(tArgList,qPrint(root->lang)) : "<none>");
1085 if (tArgList)
1086 {
1087 cd->setTemplateArguments(*tArgList);
1088 }
1089 cd->setRequiresClause(root->req);
1090 cd->setProtection(root->protection);
1091 cd->setIsStatic(root->stat);
1092
1093 // file definition containing the class cd
1094 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1095 cd->setBodyDef(fd);
1096
1097 cd->setMetaData(root->metaData);
1098
1099 // see if the class is found inside a namespace
1100 //bool found=addNamespace(root,cd);
1101
1102 cd->insertUsedFile(fd);
1103 }
1104 else
1105 {
1106 Debug::print(Debug::Classes,0," Not added class '%s', already exists as alias\n", qPrint(fullName));
1107 }
1108 }
1109
1110 if (cd)
1111 {
1112 cd->addSectionsToDefinition(root->anchors);
1113 if (!root->subGrouping) cd->setSubGrouping(FALSE);
1114 if ((root->spec&Entry::ForwardDecl)==0)
1115 {
1116 if (cd->hasDocumentation())
1117 {
1118 addIncludeFile(cd,fd,root);
1119 }
1120 if (fd && (root->section & Entry::COMPOUND_MASK))
1121 {
1122 //printf(">> Inserting class '%s' in file '%s' (root->fileName='%s')\n",
1123 // qPrint(cd->name()),
1124 // qPrint(fd->name()),
1125 // qPrint(root->fileName)
1126 // );
1127 cd->setFileDef(fd);
1128 fd->insertClass(cd);
1129 }
1130 }
1131 addClassToGroups(root,cd);
1132 cd->setRefItems(root->sli);
1133 }
1134 }
1135
1136 //----------------------------------------------------------------------
1137 // build a list of all classes mentioned in the documentation
1138 // and all classes that have a documentation block before their definition.
buildClassList(const Entry * root)1139 static void buildClassList(const Entry *root)
1140 {
1141 if (
1142 ((root->section & Entry::COMPOUND_MASK) ||
1143 root->section==Entry::OBJCIMPL_SEC) && !root->name.isEmpty()
1144 )
1145 {
1146 addClassToContext(root);
1147 }
1148 for (const auto &e : root->children()) buildClassList(e.get());
1149 }
1150
buildClassDocList(const Entry * root)1151 static void buildClassDocList(const Entry *root)
1152 {
1153 if (
1154 (root->section & Entry::COMPOUNDDOC_MASK) && !root->name.isEmpty()
1155 )
1156 {
1157 addClassToContext(root);
1158 }
1159 for (const auto &e : root->children()) buildClassDocList(e.get());
1160 }
1161
1162 //----------------------------------------------------------------------
1163 // build a list of all classes mentioned in the documentation
1164 // and all classes that have a documentation block before their definition.
1165
addConceptToContext(const Entry * root)1166 static void addConceptToContext(const Entry *root)
1167 {
1168 FileDef *fd = root->fileDef();
1169
1170 QCString scName;
1171 if (root->parent()->section&Entry::SCOPE_MASK)
1172 {
1173 scName=root->parent()->name;
1174 }
1175
1176 // name with scope (if not present already)
1177 QCString qualifiedName = root->name;
1178 if (!scName.isEmpty() && !leftScopeMatch(qualifiedName,scName))
1179 {
1180 qualifiedName.prepend(scName+"::");
1181 }
1182
1183 // see if we already found the concept before
1184 ConceptDefMutable *cd = getConceptMutable(qualifiedName);
1185
1186 Debug::print(Debug::Classes,0, " Found concept with name %s (qualifiedName=%s -> cd=%p)\n",
1187 cd ? qPrint(cd->name()) : qPrint(root->name), qPrint(qualifiedName),cd);
1188
1189 if (cd)
1190 {
1191 qualifiedName=cd->name();
1192 Debug::print(Debug::Classes,0," Existing concept %s!\n",qPrint(cd->name()));
1193
1194 cd->setDocumentation(root->doc,root->docFile,root->docLine);
1195 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1196
1197 addIncludeFile(cd,fd,root);
1198 }
1199 else // new concept
1200 {
1201 QCString className;
1202 QCString namespaceName;
1203 extractNamespaceName(qualifiedName,className,namespaceName);
1204
1205 //printf("New concept: fullname %s namespace '%s' name='%s' brief='%s' docs='%s'\n",
1206 // qPrint(qualifiedName),qPrint(namespaceName),qPrint(className),qPrint(root->brief),qPrint(root->doc));
1207
1208 QCString tagName;
1209 QCString refFileName;
1210 const TagInfo *tagInfo = root->tagInfo();
1211 if (tagInfo)
1212 {
1213 tagName = tagInfo->tagName;
1214 refFileName = tagInfo->fileName;
1215 if (qualifiedName.find("::")!=-1)
1216 // symbols imported via tag files may come without the parent scope,
1217 // so we artificially create it here
1218 {
1219 buildScopeFromQualifiedName(qualifiedName,root->lang,tagInfo);
1220 }
1221 }
1222 std::unique_ptr<ArgumentList> tArgList = getTemplateArgumentsFromName(qualifiedName,root->tArgLists);
1223 // add concept to the list
1224 //printf("ClassDict.insert(%s)\n",qPrint(fullName));
1225 cd = toConceptDefMutable(
1226 Doxygen::conceptLinkedMap->add(qualifiedName,
1227 std::unique_ptr<ConceptDef>(
1228 createConceptDef(tagInfo?tagName:root->fileName,root->startLine,root->startColumn,
1229 qualifiedName,tagName,refFileName))));
1230 if (cd)
1231 {
1232 Debug::print(Debug::Classes,0," New concept '%s' #tArgLists=%d tagInfo=%p\n",
1233 qPrint(qualifiedName),root->tArgLists.size(),tagInfo);
1234 cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1235 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1236 cd->setLanguage(root->lang);
1237 cd->setId(root->id);
1238 cd->setHidden(root->hidden);
1239 cd->setGroupId(root->mGrpId);
1240 if (tArgList)
1241 {
1242 cd->setTemplateArguments(*tArgList);
1243 }
1244 cd->setInitializer(root->initializer.str().c_str());
1245 // file definition containing the class cd
1246 cd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1247 cd->setBodyDef(fd);
1248 addIncludeFile(cd,fd,root);
1249
1250 // also add namespace to the correct structural context
1251 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,qualifiedName,0,tagInfo);
1252 if (d && d->definitionType()==Definition::TypeNamespace)
1253 {
1254 DefinitionMutable *dm = toDefinitionMutable(d);
1255 if (dm)
1256 {
1257 dm->addInnerCompound(cd);
1258 }
1259 cd->setOuterScope(d);
1260 }
1261 }
1262 else
1263 {
1264 Debug::print(Debug::Classes,0," Not added concept '%s', already exists (as alias)\n", qPrint(qualifiedName));
1265 }
1266 }
1267
1268 if (cd)
1269 {
1270 cd->addSectionsToDefinition(root->anchors);
1271 if (fd)
1272 {
1273 //printf(">> Inserting concept '%s' in file '%s' (root->fileName='%s')\n",
1274 // qPrint(cd->name()),
1275 // qPrint(fd->name()),
1276 // qPrint(root->fileName)
1277 // );
1278 cd->setFileDef(fd);
1279 fd->insertConcept(cd);
1280 }
1281 addConceptToGroups(root,cd);
1282 cd->setRefItems(root->sli);
1283 }
1284 }
buildConceptList(const Entry * root)1285 static void buildConceptList(const Entry *root)
1286 {
1287 if (root->section & Entry::CONCEPT_SEC)
1288 {
1289 addConceptToContext(root);
1290 }
1291 for (const auto &e : root->children()) buildConceptList(e.get());
1292 }
1293
buildConceptDocList(const Entry * root)1294 static void buildConceptDocList(const Entry *root)
1295 {
1296 if (root->section & Entry::CONCEPTDOC_SEC)
1297 {
1298 addConceptToContext(root);
1299 }
1300 for (const auto &e : root->children()) buildConceptDocList(e.get());
1301 }
1302
1303 // This routine is to allow @ingroup X @{ concept A; concept B; @} to work
1304 // (same also works for variable and functions because of logic in MemberGroup::insertMember)
distributeConceptGroups()1305 static void distributeConceptGroups()
1306 {
1307 for (const auto &cd : *Doxygen::conceptLinkedMap)
1308 {
1309 if (cd->groupId()!=DOX_NOGROUP)
1310 {
1311 for (const auto &ocd : *Doxygen::conceptLinkedMap)
1312 {
1313 if (cd!=ocd && cd->groupId()==ocd->groupId() &&
1314 !cd->partOfGroups().empty() && ocd->partOfGroups().empty())
1315 {
1316 ConceptDefMutable *ocdm = toConceptDefMutable(ocd.get());
1317 if (ocdm)
1318 {
1319 for (const auto &gd : cd->partOfGroups())
1320 {
1321 if (gd)
1322 {
1323 ocdm->makePartOfGroup(gd);
1324 const_cast<GroupDef*>(gd)->addConcept(ocd.get());
1325 }
1326 }
1327 }
1328 }
1329 }
1330 }
1331 }
1332 }
1333
1334 //----------------------------------------------------------------------
1335
resolveClassNestingRelations()1336 static void resolveClassNestingRelations()
1337 {
1338 ClassDefSet visitedClasses;
1339
1340 bool done=FALSE;
1341 int iteration=0;
1342 while (!done)
1343 {
1344 done=TRUE;
1345 ++iteration;
1346 struct ClassAlias
1347 {
1348 ClassAlias(const QCString &name,std::unique_ptr<ClassDef> cd,DefinitionMutable *ctx) :
1349 aliasFullName(name),aliasCd(std::move(cd)), aliasContext(ctx) {}
1350 QCString aliasFullName;
1351 std::unique_ptr<ClassDef> aliasCd;
1352 DefinitionMutable *aliasContext;
1353 };
1354 std::vector<ClassAlias> aliases;
1355 for (const auto &icd : *Doxygen::classLinkedMap)
1356 {
1357 ClassDefMutable *cd = toClassDefMutable(icd.get());
1358 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1359 {
1360 QCString name = stripAnonymousNamespaceScope(icd->name());
1361 //printf("processing=%s, iteration=%d\n",qPrint(cd->name()),iteration);
1362 // also add class to the correct structural context
1363 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,
1364 name,icd->getFileDef(),0);
1365 if (d)
1366 {
1367 //printf("****** adding %s to scope %s in iteration %d\n",qPrint(cd->name()),qPrint(d->name()),iteration);
1368 DefinitionMutable *dm = toDefinitionMutable(d);
1369 if (dm)
1370 {
1371 dm->addInnerCompound(cd);
1372 }
1373 cd->setOuterScope(d);
1374
1375 // for inline namespace add an alias of the class to the outer scope
1376 while (d->definitionType()==Definition::TypeNamespace)
1377 {
1378 NamespaceDef *nd = toNamespaceDef(d);
1379 //printf("nd->isInline()=%d\n",nd->isInline());
1380 if (nd && nd->isInline())
1381 {
1382 d = d->getOuterScope();
1383 if (d)
1384 {
1385 dm = toDefinitionMutable(d);
1386 if (dm)
1387 {
1388 std::unique_ptr<ClassDef> aliasCd { createClassDefAlias(d,cd) };
1389 QCString aliasFullName = d->qualifiedName()+"::"+aliasCd->localName();
1390 aliases.push_back(ClassAlias(aliasFullName,std::move(aliasCd),dm));
1391 //printf("adding %s to %s as %s\n",qPrint(aliasCd->name()),qPrint(d->name()),qPrint(aliasFullName));
1392 }
1393 }
1394 }
1395 else
1396 {
1397 break;
1398 }
1399 }
1400
1401 visitedClasses.insert(icd.get());
1402 done=FALSE;
1403 }
1404 //else
1405 //{
1406 // printf("****** ignoring %s: scope not (yet) found in iteration %d\n",qPrint(cd->name()),iteration);
1407 //}
1408 }
1409 }
1410 // add aliases
1411 for (auto &alias : aliases)
1412 {
1413 ClassDef *aliasCd = Doxygen::classLinkedMap->add(alias.aliasFullName,std::move(alias.aliasCd));
1414 if (aliasCd)
1415 {
1416 alias.aliasContext->addInnerCompound(aliasCd);
1417 }
1418 }
1419 }
1420
1421 //give warnings for unresolved compounds
1422 for (const auto &icd : *Doxygen::classLinkedMap)
1423 {
1424 ClassDefMutable *cd = toClassDefMutable(icd.get());
1425 if (cd && visitedClasses.find(icd.get())==visitedClasses.end())
1426 {
1427 QCString name = stripAnonymousNamespaceScope(cd->name());
1428 //printf("processing unresolved=%s, iteration=%d\n",qPrint(cd->name()),iteration);
1429 /// create the scope artificially
1430 // anyway, so we can at least relate scopes properly.
1431 Definition *d = buildScopeFromQualifiedName(name,cd->getLanguage(),0);
1432 if (d && d!=cd && !cd->getDefFileName().isEmpty())
1433 // avoid recursion in case of redundant scopes, i.e: namespace N { class N::C {}; }
1434 // for this case doxygen assumes the existence of a namespace N::N in which C is to be found!
1435 // also avoid warning for stuff imported via a tagfile.
1436 {
1437 DefinitionMutable *dm = toDefinitionMutable(d);
1438 if (dm)
1439 {
1440 dm->addInnerCompound(cd);
1441 }
1442 cd->setOuterScope(d);
1443 warn(cd->getDefFileName(),cd->getDefLine(),
1444 "Internal inconsistency: scope for class %s not "
1445 "found!",qPrint(name)
1446 );
1447 }
1448 }
1449 }
1450 }
1451
distributeClassGroupRelations()1452 void distributeClassGroupRelations()
1453 {
1454 //static bool inlineGroupedClasses = Config_getBool(INLINE_GROUPED_CLASSES);
1455 //if (!inlineGroupedClasses) return;
1456 //printf("** distributeClassGroupRelations()\n");
1457
1458 ClassDefSet visitedClasses;
1459 for (const auto &cd : *Doxygen::classLinkedMap)
1460 {
1461 //printf("Checking %s\n",qPrint(cd->name()));
1462 // distribute the group to nested classes as well
1463 if (visitedClasses.find(cd.get())==visitedClasses.end() && !cd->partOfGroups().empty())
1464 {
1465 //printf(" Candidate for merging\n");
1466 const GroupDef *gd = cd->partOfGroups().front();
1467 for (const auto &ncd : cd->getClasses())
1468 {
1469 ClassDefMutable *ncdm = toClassDefMutable(ncd);
1470 if (ncdm && ncdm->partOfGroups().empty())
1471 {
1472 //printf(" Adding %s to group '%s'\n",qPrint(ncd->name()),
1473 // gd->groupTitle());
1474 ncdm->makePartOfGroup(gd);
1475 const_cast<GroupDef*>(gd)->addClass(ncdm);
1476 }
1477 }
1478 visitedClasses.insert(cd.get()); // only visit every class once
1479 }
1480 }
1481 }
1482
1483 //----------------------------
1484
createTagLessInstance(const ClassDef * rootCd,const ClassDef * templ,const QCString & fieldName)1485 static ClassDefMutable *createTagLessInstance(const ClassDef *rootCd,const ClassDef *templ,const QCString &fieldName)
1486 {
1487 QCString fullName = removeAnonymousScopes(templ->name());
1488 if (fullName.right(2)=="::") fullName=fullName.left(fullName.length()-2);
1489 fullName+="."+fieldName;
1490
1491 //printf("** adding class %s based on %s\n",qPrint(fullName),qPrint(templ->name()));
1492 ClassDefMutable *cd = toClassDefMutable(
1493 Doxygen::classLinkedMap->add(fullName,
1494 std::unique_ptr<ClassDef>(
1495 createClassDef(templ->getDefFileName(),
1496 templ->getDefLine(),
1497 templ->getDefColumn(),
1498 fullName,
1499 templ->compoundType()))));
1500 if (cd)
1501 {
1502 cd->setDocumentation(templ->documentation(),templ->docFile(),templ->docLine()); // copy docs to definition
1503 cd->setBriefDescription(templ->briefDescription(),templ->briefFile(),templ->briefLine());
1504 cd->setLanguage(templ->getLanguage());
1505 cd->setBodySegment(templ->getDefLine(),templ->getStartBodyLine(),templ->getEndBodyLine());
1506 cd->setBodyDef(templ->getBodyDef());
1507
1508 cd->setOuterScope(rootCd->getOuterScope());
1509 if (rootCd->getOuterScope()!=Doxygen::globalScope)
1510 {
1511 DefinitionMutable *outerScope = toDefinitionMutable(rootCd->getOuterScope());
1512 if (outerScope)
1513 {
1514 outerScope->addInnerCompound(cd);
1515 }
1516 }
1517
1518 FileDef *fd = templ->getFileDef();
1519 if (fd)
1520 {
1521 cd->setFileDef(fd);
1522 fd->insertClass(cd);
1523 }
1524 for (const auto &gd : rootCd->partOfGroups())
1525 {
1526 cd->makePartOfGroup(gd);
1527 const_cast<GroupDef*>(gd)->addClass(cd);
1528 }
1529
1530 MemberList *ml = templ->getMemberList(MemberListType_pubAttribs);
1531 if (ml)
1532 {
1533 for (const auto &md : *ml)
1534 {
1535 //printf(" Member %s type=%s\n",qPrint(md->name()),md->typeString());
1536 MemberDefMutable *imd = createMemberDef(md->getDefFileName(),md->getDefLine(),md->getDefColumn(),
1537 md->typeString(),md->name(),md->argsString(),md->excpString(),
1538 md->protection(),md->virtualness(),md->isStatic(),Member,
1539 md->memberType(),
1540 ArgumentList(),ArgumentList(),"");
1541 imd->setMemberClass(cd);
1542 imd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
1543 imd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
1544 imd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
1545 imd->setMemberSpecifiers(md->getMemberSpecifiers());
1546 imd->setMemberGroupId(md->getMemberGroupId());
1547 imd->setInitializer(md->initializer());
1548 imd->setRequiresClause(md->requiresClause());
1549 imd->setMaxInitLines(md->initializerLines());
1550 imd->setBitfields(md->bitfieldString());
1551 imd->setLanguage(md->getLanguage());
1552 cd->insertMember(imd);
1553 }
1554 }
1555 }
1556 return cd;
1557 }
1558
1559 /** Look through the members of class \a cd and its public members.
1560 * If there is a member m of a tag less struct/union,
1561 * then we create a duplicate of the struct/union with the name of the
1562 * member to identify it.
1563 * So if cd has name S, then the tag less struct/union will get name S.m
1564 * Since tag less structs can be nested we need to call this function
1565 * recursively. Later on we need to patch the member types so we keep
1566 * track of the hierarchy of classes we create.
1567 */
processTagLessClasses(const ClassDef * rootCd,const ClassDef * cd,ClassDefMutable * tagParentCd,const QCString & prefix,int count)1568 static void processTagLessClasses(const ClassDef *rootCd,
1569 const ClassDef *cd,
1570 ClassDefMutable *tagParentCd,
1571 const QCString &prefix,int count)
1572 {
1573 //printf("%d: processTagLessClasses %s\n",count,qPrint(cd->name()));
1574 //printf("checking members for %s\n",qPrint(cd->name()));
1575 if (tagParentCd && !cd->getClasses().empty())
1576 {
1577 MemberList *ml = cd->getMemberList(MemberListType_pubAttribs);
1578 if (ml)
1579 {
1580 for (const auto &md : *ml)
1581 {
1582 QCString type = md->typeString();
1583 if (type.find("::@")!=-1) // member of tag less struct/union
1584 {
1585 for (const auto &icd : cd->getClasses())
1586 {
1587 //printf(" member %s: type='%s'\n",qPrint(md->name()),qPrint(type));
1588 //printf(" comparing '%s'<->'%s'\n",qPrint(type),qPrint(icd->name()));
1589 if (type.find(icd->name())!=-1) // matching tag less struct/union
1590 {
1591 QCString name = md->name();
1592 if (md->isAnonymous()) name = "__unnamed" + name.right(name.length()-1)+"__";
1593 if (!prefix.isEmpty()) name.prepend(prefix+".");
1594 //printf(" found %s for class %s\n",qPrint(name),qPrint(cd->name()));
1595 ClassDefMutable *ncd = createTagLessInstance(rootCd,icd,name);
1596 if (ncd)
1597 {
1598 processTagLessClasses(rootCd,icd,ncd,name,count+1);
1599 //printf(" addTagged %s to %s\n",qPrint(ncd->name()),qPrint(tagParentCd->name()));
1600 ncd->setTagLessReference(icd);
1601
1602 // replace tag-less type for generated/original member
1603 // by newly created class name.
1604 // note the difference between changing cd and tagParentCd.
1605 // for the initial call this is the same pointer, but for
1606 // recursive calls cd is the original tag-less struct (of which
1607 // there is only one instance) and tagParentCd is the newly
1608 // generated tagged struct of which there can be multiple instances!
1609 MemberList *pml = tagParentCd->getMemberList(MemberListType_pubAttribs);
1610 if (pml)
1611 {
1612 for (const auto &pmd : *pml)
1613 {
1614 MemberDefMutable *pmdm = toMemberDefMutable(pmd);
1615 if (pmdm && pmd->name()==md->name())
1616 {
1617 pmdm->setAccessorType(ncd,substitute(pmd->typeString(),icd->name(),ncd->name()));
1618 //pmd->setType(substitute(pmd->typeString(),icd->name(),ncd->name()));
1619 }
1620 }
1621 }
1622 }
1623 }
1624 }
1625 }
1626 }
1627 }
1628 }
1629 }
1630
findTagLessClasses(std::vector<ClassDefMutable * > & candidates,const ClassDef * cd)1631 static void findTagLessClasses(std::vector<ClassDefMutable*> &candidates,const ClassDef *cd)
1632 {
1633 for (const auto &icd : cd->getClasses())
1634 {
1635 if (icd->name().find("@")==-1) // process all non-anonymous inner classes
1636 {
1637 findTagLessClasses(candidates,icd);
1638 }
1639 }
1640
1641 ClassDefMutable *cdm = toClassDefMutable(cd);
1642 if (cdm)
1643 {
1644 candidates.push_back(cdm);
1645 }
1646 }
1647
findTagLessClasses()1648 static void findTagLessClasses()
1649 {
1650 std::vector<ClassDefMutable *> candidates;
1651 for (const auto &cd : *Doxygen::classLinkedMap)
1652 {
1653 Definition *scope = cd->getOuterScope();
1654 if (scope && scope->definitionType()!=Definition::TypeClass) // that is not nested
1655 {
1656 findTagLessClasses(candidates,cd.get());
1657 }
1658 }
1659
1660 // since processTagLessClasses is potentially adding classes to Doxygen::classLinkedMap
1661 // we need to call it outside of the loop above, otherwise the iterator gets invalidated!
1662 for (auto &cd : candidates)
1663 {
1664 processTagLessClasses(cd,cd,cd,"",0); // process tag less inner struct/classes
1665 }
1666 }
1667
1668
1669 //----------------------------------------------------------------------
1670 // build a list of all namespaces mentioned in the documentation
1671 // and all namespaces that have a documentation block before their definition.
buildNamespaceList(const Entry * root)1672 static void buildNamespaceList(const Entry *root)
1673 {
1674 if (
1675 (root->section==Entry::NAMESPACE_SEC ||
1676 root->section==Entry::NAMESPACEDOC_SEC ||
1677 root->section==Entry::PACKAGEDOC_SEC
1678 ) &&
1679 !root->name.isEmpty()
1680 )
1681 {
1682 //printf("** buildNamespaceList(%s)\n",qPrint(root->name));
1683
1684 QCString fName = root->name;
1685 if (root->section==Entry::PACKAGEDOC_SEC)
1686 {
1687 fName=substitute(fName,".","::");
1688 }
1689
1690 QCString fullName = stripAnonymousNamespaceScope(fName);
1691 if (!fullName.isEmpty())
1692 {
1693 //printf("Found namespace %s in %s at line %d\n",qPrint(root->name),
1694 // qPrint(root->fileName), root->startLine);
1695 NamespaceDef *ndi = Doxygen::namespaceLinkedMap->find(fullName);
1696 if (ndi) // existing namespace
1697 {
1698 NamespaceDefMutable *nd = toNamespaceDefMutable(ndi);
1699 if (nd) // non-inline namespace
1700 {
1701 nd->setDocumentation(root->doc,root->docFile,root->docLine);
1702 nd->setName(fullName); // change name to match docs
1703 nd->addSectionsToDefinition(root->anchors);
1704 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1705 if (nd->getLanguage()==SrcLangExt_Unknown)
1706 {
1707 nd->setLanguage(root->lang);
1708 }
1709 if (root->tagInfo()==0) // if we found the namespace in a tag file
1710 // and also in a project file, 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 //printf("++ new namespace %s lang=%s tagName=%s\n",qPrint(fullName),qPrint(langToString(root->lang)),qPrint(tagName));
1742 // add namespace to the list
1743 NamespaceDefMutable *nd = toNamespaceDefMutable(
1744 Doxygen::namespaceLinkedMap->add(fullName,
1745 std::unique_ptr<NamespaceDef>(
1746 createNamespaceDef(tagInfo?tagName:root->fileName,root->startLine,
1747 root->startColumn,fullName,tagName,tagFileName,
1748 root->type,root->spec&Entry::Published))));
1749 if (nd)
1750 {
1751 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1752 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1753 nd->addSectionsToDefinition(root->anchors);
1754 nd->setHidden(root->hidden);
1755 nd->setArtificial(root->artificial);
1756 nd->setLanguage(root->lang);
1757 nd->setId(root->id);
1758 nd->setMetaData(root->metaData);
1759 nd->setInline((root->spec&Entry::Inline)!=0);
1760
1761 //printf("Adding namespace to group\n");
1762 addNamespaceToGroups(root,nd);
1763 nd->setRefItems(root->sli);
1764
1765 // file definition containing the namespace nd
1766 FileDef *fd=root->fileDef();
1767 // insert the namespace in the file definition
1768 if (fd) fd->insertNamespace(nd);
1769
1770 // the empty string test is needed for extract all case
1771 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1772 nd->insertUsedFile(fd);
1773 nd->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
1774 nd->setBodyDef(fd);
1775
1776 // also add namespace to the correct structural context
1777 Definition *d = findScopeFromQualifiedName(Doxygen::globalScope,fullName,0,tagInfo);
1778 //printf("adding namespace %s to context %s\n",qPrint(nd->name()),d?qPrint(d->name()):"<none>");
1779 if (d==0) // we didn't find anything, create the scope artificially
1780 // anyway, so we can at least relate scopes properly.
1781 {
1782 d = buildScopeFromQualifiedName(fullName,nd->getLanguage(),tagInfo);
1783 DefinitionMutable *dm = toDefinitionMutable(d);
1784 if (dm)
1785 {
1786 dm->addInnerCompound(nd);
1787 }
1788 nd->setOuterScope(d);
1789 // TODO: Due to the order in which the tag file is written
1790 // a nested class can be found before its parent!
1791 }
1792 else
1793 {
1794 DefinitionMutable *dm = toDefinitionMutable(d);
1795 if (dm)
1796 {
1797 dm->addInnerCompound(nd);
1798 }
1799 nd->setOuterScope(d);
1800 // in case of d is an inline namespace, alias insert nd in the part scope of d.
1801 while (d->definitionType()==Definition::TypeNamespace)
1802 {
1803 NamespaceDef *pnd = toNamespaceDef(d);
1804 if (pnd && pnd->isInline())
1805 {
1806 d = d->getOuterScope();
1807 if (d)
1808 {
1809 dm = toDefinitionMutable(d);
1810 if (dm)
1811 {
1812 NamespaceDef *aliasNd = createNamespaceDefAlias(d,nd);
1813 dm->addInnerCompound(aliasNd);
1814 QCString aliasName = aliasNd->name();
1815 //printf("adding alias %s (%p) to %s\n",qPrint(aliasName),aliasNd,qPrint(d->name()));
1816 Doxygen::namespaceLinkedMap->add(
1817 aliasName,std::unique_ptr<NamespaceDef>(aliasNd));
1818 }
1819 }
1820 else
1821 {
1822 break;
1823 }
1824 }
1825 else
1826 {
1827 break;
1828 }
1829 }
1830 }
1831 }
1832 }
1833 }
1834 }
1835 for (const auto &e : root->children()) buildNamespaceList(e.get());
1836 }
1837
1838 //----------------------------------------------------------------------
1839
findUsedNamespace(const LinkedRefMap<const NamespaceDef> & unl,const QCString & name)1840 static const NamespaceDef *findUsedNamespace(const LinkedRefMap<const NamespaceDef> &unl,
1841 const QCString &name)
1842 {
1843 const NamespaceDef *usingNd =0;
1844 for (const auto &und : unl)
1845 {
1846 QCString uScope=und->name()+"::";
1847 usingNd = getResolvedNamespace(uScope+name);
1848 if (usingNd!=0) break;
1849 }
1850 return usingNd;
1851 }
1852
findUsingDirectives(const Entry * root)1853 static void findUsingDirectives(const Entry *root)
1854 {
1855 if (root->section==Entry::USINGDIR_SEC)
1856 {
1857 //printf("Found using directive %s at line %d of %s\n",
1858 // qPrint(root->name),root->startLine,qPrint(root->fileName));
1859 QCString name=substitute(root->name,".","::");
1860 if (name.right(2)=="::")
1861 {
1862 name=name.left(name.length()-2);
1863 }
1864 if (!name.isEmpty())
1865 {
1866 const NamespaceDef *usingNd = 0;
1867 NamespaceDefMutable *nd = 0;
1868 FileDef *fd = root->fileDef();
1869 QCString nsName;
1870
1871 // see if the using statement was found inside a namespace or inside
1872 // the global file scope.
1873 if (root->parent() && root->parent()->section==Entry::NAMESPACE_SEC &&
1874 (fd==0 || fd->getLanguage()!=SrcLangExt_Java) // not a .java file
1875 )
1876 {
1877 nsName=stripAnonymousNamespaceScope(root->parent()->name);
1878 if (!nsName.isEmpty())
1879 {
1880 nd = getResolvedNamespaceMutable(nsName);
1881 }
1882 }
1883
1884 // find the scope in which the 'using' namespace is defined by prepending
1885 // the possible scopes in which the using statement was found, starting
1886 // with the most inner scope and going to the most outer scope (i.e.
1887 // file scope).
1888 int scopeOffset = nsName.length();
1889 do
1890 {
1891 QCString scope=scopeOffset>0 ?
1892 nsName.left(scopeOffset)+"::" : QCString();
1893 usingNd = getResolvedNamespace(scope+name);
1894 //printf("Trying with scope='%s' usingNd=%p\n",(scope+qPrint(name)),usingNd);
1895 if (scopeOffset==0)
1896 {
1897 scopeOffset=-1;
1898 }
1899 else if ((scopeOffset=nsName.findRev("::",scopeOffset-1))==-1)
1900 {
1901 scopeOffset=0;
1902 }
1903 } while (scopeOffset>=0 && usingNd==0);
1904
1905 if (usingNd==0 && nd) // not found, try used namespaces in this scope
1906 // or in one of the parent namespace scopes
1907 {
1908 const NamespaceDefMutable *pnd = nd;
1909 while (pnd && usingNd==0)
1910 {
1911 // also try with one of the used namespaces found earlier
1912 usingNd = toNamespaceDefMutable(findUsedNamespace(pnd->getUsedNamespaces(),name));
1913
1914 // goto the parent
1915 const Definition *s = pnd->getOuterScope();
1916 if (s && s->definitionType()==Definition::TypeNamespace)
1917 {
1918 pnd = toNamespaceDefMutable(toNamespaceDef(s));
1919 }
1920 else
1921 {
1922 pnd = 0;
1923 }
1924 }
1925 }
1926 if (usingNd==0 && fd) // still nothing, also try used namespace in the
1927 // global scope
1928 {
1929 usingNd = findUsedNamespace(fd->getUsedNamespaces(),name);
1930 }
1931
1932 //printf("%s -> %s\n",qPrint(name),usingNd?qPrint(usingNd->name()):"<none>");
1933
1934 // add the namespace the correct scope
1935 if (usingNd)
1936 {
1937 //printf("using fd=%p nd=%p\n",fd,nd);
1938 if (nd)
1939 {
1940 //printf("Inside namespace %s\n",qPrint(nd->name()));
1941 nd->addUsingDirective(usingNd);
1942 }
1943 else if (fd)
1944 {
1945 //printf("Inside file %s\n",qPrint(fd->name()));
1946 fd->addUsingDirective(usingNd);
1947 }
1948 }
1949 else // unknown namespace, but add it anyway.
1950 {
1951 //printf("++ new unknown namespace %s lang=%s\n",qPrint(name),qPrint(langToString(root->lang)));
1952 // add namespace to the list
1953 nd = toNamespaceDefMutable(
1954 Doxygen::namespaceLinkedMap->add(name,
1955 std::unique_ptr<NamespaceDef>(
1956 createNamespaceDef(root->fileName,root->startLine,root->startColumn,name))));
1957 if (nd)
1958 {
1959 nd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition
1960 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1961 nd->addSectionsToDefinition(root->anchors);
1962 //printf("** Adding namespace %s hidden=%d\n",qPrint(name),root->hidden);
1963 nd->setHidden(root->hidden);
1964 nd->setArtificial(TRUE);
1965 nd->setLanguage(root->lang);
1966 nd->setId(root->id);
1967 nd->setMetaData(root->metaData);
1968 nd->setInline((root->spec&Entry::Inline)!=0);
1969
1970 for (const Grouping &g : root->groups)
1971 {
1972 GroupDef *gd=0;
1973 if (!g.groupname.isEmpty() && (gd=Doxygen::groupLinkedMap->find(g.groupname)))
1974 gd->addNamespace(nd);
1975 }
1976
1977 // insert the namespace in the file definition
1978 if (fd)
1979 {
1980 fd->insertNamespace(nd);
1981 fd->addUsingDirective(nd);
1982 }
1983
1984 // the empty string test is needed for extract all case
1985 nd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
1986 nd->insertUsedFile(fd);
1987 nd->setRefItems(root->sli);
1988 }
1989 }
1990 }
1991 }
1992 for (const auto &e : root->children()) findUsingDirectives(e.get());
1993 }
1994
1995 //----------------------------------------------------------------------
1996
buildListOfUsingDecls(const Entry * root)1997 static void buildListOfUsingDecls(const Entry *root)
1998 {
1999 if (root->section==Entry::USINGDECL_SEC &&
2000 !(root->parent()->section&Entry::COMPOUND_MASK) // not a class/struct member
2001 )
2002 {
2003 QCString name = substitute(root->name,".","::");
2004 g_usingDeclarations.insert(name.str());
2005 }
2006 for (const auto &e : root->children()) buildListOfUsingDecls(e.get());
2007 }
2008
2009
findUsingDeclarations(const Entry * root,bool filterPythonPackages)2010 static void findUsingDeclarations(const Entry *root,bool filterPythonPackages)
2011 {
2012 if (root->section==Entry::USINGDECL_SEC &&
2013 !(root->parent()->section&Entry::COMPOUND_MASK) && // not a class/struct member
2014 (!filterPythonPackages || (root->lang==SrcLangExt_Python && root->fileName.endsWith("__init__.py")))
2015 )
2016 {
2017 //printf("Found using declaration %s at line %d of %s inside section %x\n",
2018 // qPrint(root->name),root->startLine,qPrint(root->fileName),
2019 // root->parent()->section);
2020 if (!root->name.isEmpty())
2021 {
2022 ClassDefMutable *usingCd = 0;
2023 NamespaceDefMutable *nd = 0;
2024 FileDef *fd = root->fileDef();
2025 QCString scName;
2026
2027 // see if the using statement was found inside a namespace or inside
2028 // the global file scope.
2029 if (root->parent()->section == Entry::NAMESPACE_SEC)
2030 {
2031 scName=root->parent()->name;
2032 if (!scName.isEmpty())
2033 {
2034 nd = getResolvedNamespaceMutable(scName);
2035 }
2036 }
2037
2038 // Assume the using statement was used to import a class.
2039 // Find the scope in which the 'using' namespace is defined by prepending
2040 // the possible scopes in which the using statement was found, starting
2041 // with the most inner scope and going to the most outer scope (i.e.
2042 // file scope).
2043
2044 QCString name = substitute(root->name,".","::"); //Java/C# scope->internal
2045 usingCd = getClassMutable(name); // try direct lookup first, this is needed to get
2046 // builtin STL classes to properly resolve, e.g.
2047 // vector -> std::vector
2048 if (usingCd==0)
2049 {
2050 SymbolResolver resolver(fd);
2051 usingCd = resolver.resolveClassMutable(nd,name); // try via resolving (see also bug757509)
2052 }
2053 if (usingCd==0)
2054 {
2055 usingCd = toClassDefMutable(Doxygen::hiddenClassLinkedMap->find(name)); // check if it is already hidden
2056 }
2057
2058 //printf("%s -> %p\n",qPrint(root->name),(void*)usingCd);
2059 if (usingCd==0) // definition not in the input => add an artificial class
2060 {
2061 Debug::print(Debug::Classes,0," New using class '%s' (sec=0x%08x)! #tArgLists=%d\n",
2062 qPrint(name),root->section,root->tArgLists.size());
2063 usingCd = toClassDefMutable(
2064 Doxygen::hiddenClassLinkedMap->add(name,
2065 std::unique_ptr<ClassDef>(
2066 createClassDef( "<using>",1,1, name, ClassDef::Class))));
2067 if (usingCd)
2068 {
2069 usingCd->setArtificial(TRUE);
2070 usingCd->setLanguage(root->lang);
2071 }
2072 }
2073 else
2074 {
2075 Debug::print(Debug::Classes,0," Found used class %s in scope=%s\n",
2076 qPrint(usingCd->name()),
2077 nd?qPrint(nd->name()):
2078 fd?qPrint(fd->name()):
2079 "<unknown>");
2080 }
2081
2082 if (nd)
2083 {
2084 //printf("Inside namespace %s\n",qPrint(nd->name()));
2085 nd->addUsingDeclaration(usingCd);
2086 }
2087 else if (fd)
2088 {
2089 //printf("Inside file %s\n",qPrint(fd->name()));
2090 fd->addUsingDeclaration(usingCd);
2091 }
2092 }
2093 }
2094 for (const auto &e : root->children()) findUsingDeclarations(e.get(),filterPythonPackages);
2095 }
2096
2097 //----------------------------------------------------------------------
2098
findUsingDeclImports(const Entry * root)2099 static void findUsingDeclImports(const Entry *root)
2100 {
2101 if (root->section==Entry::USINGDECL_SEC &&
2102 (root->parent()->section&Entry::COMPOUND_MASK) // in a class/struct member
2103 )
2104 {
2105 //printf("Found using declaration %s inside section %x\n",
2106 // qPrint(root->name), root->parent()->section);
2107 QCString fullName=removeRedundantWhiteSpace(root->parent()->name);
2108 fullName=stripAnonymousNamespaceScope(fullName);
2109 fullName=stripTemplateSpecifiersFromScope(fullName);
2110 ClassDefMutable *cd = getClassMutable(fullName);
2111 if (cd)
2112 {
2113 //printf("found class %s\n",qPrint(cd->name()));
2114 int i=root->name.find("::");
2115 if (i!=-1)
2116 {
2117 QCString scope=root->name.left(i);
2118 QCString memName=root->name.right(root->name.length()-i-2);
2119 SymbolResolver resolver;
2120 const ClassDef *bcd = resolver.resolveClass(cd,scope); // todo: file in fileScope parameter
2121 if (bcd && bcd!=cd)
2122 {
2123 //printf("found class %s memName=%s\n",qPrint(bcd->name()),qPrint(memName));
2124 const MemberNameInfoLinkedMap &mnlm=bcd->memberNameInfoLinkedMap();
2125 const MemberNameInfo *mni = mnlm.find(memName);
2126 if (mni)
2127 {
2128 for (auto &mi : *mni)
2129 {
2130 const MemberDef *md = mi->memberDef();
2131 if (md && md->protection()!=Private)
2132 {
2133 //printf("found member %s\n",mni->memberName());
2134 QCString fileName = root->fileName;
2135 if (fileName.isEmpty() && root->tagInfo())
2136 {
2137 fileName = root->tagInfo()->tagName;
2138 }
2139 const ArgumentList &templAl = md->templateArguments();
2140 const ArgumentList &al = md->argumentList();
2141 std::unique_ptr<MemberDefMutable> newMd { createMemberDef(
2142 fileName,root->startLine,root->startColumn,
2143 md->typeString(),memName,md->argsString(),
2144 md->excpString(),root->protection,root->virt,
2145 md->isStatic(),Member,md->memberType(),
2146 templAl,al,root->metaData
2147 ) };
2148 newMd->setMemberClass(cd);
2149 cd->insertMember(newMd.get());
2150 if (!root->doc.isEmpty() || !root->brief.isEmpty())
2151 {
2152 newMd->setDocumentation(root->doc,root->docFile,root->docLine);
2153 newMd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2154 newMd->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2155 }
2156 else
2157 {
2158 newMd->setDocumentation(md->documentation(),md->docFile(),md->docLine());
2159 newMd->setBriefDescription(md->briefDescription(),md->briefFile(),md->briefLine());
2160 newMd->setInbodyDocumentation(md->inbodyDocumentation(),md->inbodyFile(),md->inbodyLine());
2161 }
2162 newMd->setDefinition(md->definition());
2163 newMd->enableCallGraph(root->callGraph);
2164 newMd->enableCallerGraph(root->callerGraph);
2165 newMd->enableReferencedByRelation(root->referencedByRelation);
2166 newMd->enableReferencesRelation(root->referencesRelation);
2167 newMd->setBitfields(md->bitfieldString());
2168 newMd->addSectionsToDefinition(root->anchors);
2169 newMd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine());
2170 newMd->setBodyDef(md->getBodyDef());
2171 newMd->setInitializer(md->initializer());
2172 newMd->setRequiresClause(md->requiresClause());
2173 newMd->setMaxInitLines(md->initializerLines());
2174 newMd->setMemberGroupId(root->mGrpId);
2175 newMd->setMemberSpecifiers(md->getMemberSpecifiers());
2176 newMd->setLanguage(root->lang);
2177 newMd->setId(root->id);
2178 MemberName *mn = Doxygen::memberNameLinkedMap->add(memName);
2179 mn->push_back(std::move(newMd));
2180 }
2181 }
2182 }
2183 }
2184 }
2185 }
2186
2187 }
2188 for (const auto &e : root->children()) findUsingDeclImports(e.get());
2189 }
2190
2191 //----------------------------------------------------------------------
2192
findIncludedUsingDirectives()2193 static void findIncludedUsingDirectives()
2194 {
2195 FileDefSet visitedFiles;
2196 // then recursively add using directives found in #include files
2197 // to files that have not been visited.
2198 for (const auto &fn : *Doxygen::inputNameLinkedMap)
2199 {
2200 for (const auto &fd : *fn)
2201 {
2202 //printf("----- adding using directives for file %s\n",qPrint(fd->name()));
2203 fd->addIncludedUsingDirectives(visitedFiles);
2204 }
2205 }
2206 }
2207
2208 //----------------------------------------------------------------------
2209
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)2210 static MemberDef *addVariableToClass(
2211 const Entry *root,
2212 ClassDefMutable *cd,
2213 MemberType mtype,
2214 const QCString &type,
2215 const QCString &name,
2216 const QCString &args,
2217 bool fromAnnScope,
2218 MemberDef *fromAnnMemb,
2219 Protection prot,
2220 Relationship related)
2221 {
2222 QCString qualScope = cd->qualifiedNameWithTemplateParameters();
2223 QCString scopeSeparator="::";
2224 SrcLangExt lang = cd->getLanguage();
2225 if (lang==SrcLangExt_Java || lang==SrcLangExt_CSharp)
2226 {
2227 qualScope = substitute(qualScope,"::",".");
2228 scopeSeparator=".";
2229 }
2230 Debug::print(Debug::Variables,0,
2231 " class variable:\n"
2232 " '%s' '%s'::'%s' '%s' prot=%d ann=%d init='%s'\n",
2233 qPrint(type),
2234 qPrint(qualScope),
2235 qPrint(name),
2236 qPrint(args),
2237 root->protection,
2238 fromAnnScope,
2239 qPrint(root->initializer.str())
2240 );
2241
2242 QCString def;
2243 if (!type.isEmpty())
2244 {
2245 if (related || mtype==MemberType_Friend || Config_getBool(HIDE_SCOPE_NAMES))
2246 {
2247 if (root->spec&Entry::Alias) // turn 'typedef B A' into 'using A = B'
2248 {
2249 def="using "+name+" = "+type.mid(7);
2250 }
2251 else
2252 {
2253 def=type+" "+name+args;
2254 }
2255 }
2256 else
2257 {
2258 if (root->spec&Entry::Alias) // turn 'typedef B C::A' into 'using C::A = B'
2259 {
2260 def="using "+qualScope+scopeSeparator+name+" = "+type.mid(7);
2261 }
2262 else
2263 {
2264 def=type+" "+qualScope+scopeSeparator+name+args;
2265 }
2266 }
2267 }
2268 else
2269 {
2270 if (Config_getBool(HIDE_SCOPE_NAMES))
2271 {
2272 def=name+args;
2273 }
2274 else
2275 {
2276 def=qualScope+scopeSeparator+name+args;
2277 }
2278 }
2279 def.stripPrefix("static ");
2280
2281 // see if the member is already found in the same scope
2282 // (this may be the case for a static member that is initialized
2283 // outside the class)
2284 MemberName *mn=Doxygen::memberNameLinkedMap->find(name);
2285 if (mn)
2286 {
2287 for (const auto &imd : *mn)
2288 {
2289 //printf("md->getClassDef()=%p cd=%p type=[%s] md->typeString()=[%s]\n",
2290 // md->getClassDef(),cd,qPrint(type),md->typeString());
2291 MemberDefMutable *md = toMemberDefMutable(imd.get());
2292 if (md &&
2293 md->getClassDef()==cd &&
2294 removeRedundantWhiteSpace(type)==md->typeString())
2295 // member already in the scope
2296 {
2297
2298 if (root->lang==SrcLangExt_ObjC &&
2299 root->mtype==Property &&
2300 md->memberType()==MemberType_Variable)
2301 { // Objective-C 2.0 property
2302 // turn variable into a property
2303 md->setProtection(root->protection);
2304 cd->reclassifyMember(md,MemberType_Property);
2305 }
2306 addMemberDocs(root,md,def,0,FALSE,root->spec);
2307 //printf(" Member already found!\n");
2308 return md;
2309 }
2310 }
2311 }
2312
2313 QCString fileName = root->fileName;
2314 if (fileName.isEmpty() && root->tagInfo())
2315 {
2316 fileName = root->tagInfo()->tagName;
2317 }
2318
2319 // new member variable, typedef or enum value
2320 std::unique_ptr<MemberDefMutable> md { createMemberDef(
2321 fileName,root->startLine,root->startColumn,
2322 type,name,args,root->exception,
2323 prot,Normal,root->stat,related,
2324 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2325 ArgumentList(), root->metaData) };
2326 md->setTagInfo(root->tagInfo());
2327 md->setMemberClass(cd); // also sets outer scope (i.e. getOuterScope())
2328 md->setDocumentation(root->doc,root->docFile,root->docLine);
2329 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2330 md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2331 md->setDefinition(def);
2332 md->setBitfields(root->bitfields);
2333 md->addSectionsToDefinition(root->anchors);
2334 md->setFromAnonymousScope(fromAnnScope);
2335 md->setFromAnonymousMember(fromAnnMemb);
2336 //md->setIndentDepth(indentDepth);
2337 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2338 std::string init = root->initializer.str();
2339 md->setInitializer(init.c_str());
2340 md->setMaxInitLines(root->initLines);
2341 md->setMemberGroupId(root->mGrpId);
2342 md->setMemberSpecifiers(root->spec);
2343 md->setReadAccessor(root->read);
2344 md->setWriteAccessor(root->write);
2345 md->enableCallGraph(root->callGraph);
2346 md->enableCallerGraph(root->callerGraph);
2347 md->enableReferencedByRelation(root->referencedByRelation);
2348 md->enableReferencesRelation(root->referencesRelation);
2349 md->setHidden(root->hidden);
2350 md->setArtificial(root->artificial);
2351 md->setLanguage(root->lang);
2352 md->setId(root->id);
2353 addMemberToGroups(root,md.get());
2354 md->setBodyDef(root->fileDef());
2355
2356 //printf(" New member adding to %s (%p)!\n",qPrint(cd->name()),cd);
2357 cd->insertMember(md.get());
2358 md->setRefItems(root->sli);
2359
2360 //TODO: insert FileDef instead of filename strings.
2361 cd->insertUsedFile(root->fileDef());
2362 root->markAsProcessed();
2363
2364 //printf(" Adding member=%s\n",qPrint(md->name()));
2365 // add the member to the global list
2366 MemberDef *result = md.get();
2367 mn = Doxygen::memberNameLinkedMap->add(name);
2368 mn->push_back(std::move(md));
2369
2370 return result;
2371 }
2372
2373 //----------------------------------------------------------------------
2374
addVariableToFile(const Entry * root,MemberType mtype,const QCString & scope,const QCString & type,const QCString & name,const QCString & args,bool fromAnnScope,MemberDef * fromAnnMemb)2375 static MemberDef *addVariableToFile(
2376 const Entry *root,
2377 MemberType mtype,
2378 const QCString &scope,
2379 const QCString &type,
2380 const QCString &name,
2381 const QCString &args,
2382 bool fromAnnScope,
2383 /*int indentDepth,*/
2384 MemberDef *fromAnnMemb)
2385 {
2386 Debug::print(Debug::Variables,0,
2387 " global variable:\n"
2388 " file='%s' type='%s' scope='%s' name='%s' args='%s' prot=`%d mtype=%d lang=%d\n",
2389 qPrint(root->fileName),
2390 qPrint(type),
2391 qPrint(scope),
2392 qPrint(name),
2393 qPrint(args),
2394 root->protection,
2395 mtype,
2396 root->lang
2397 );
2398
2399 FileDef *fd = root->fileDef();
2400
2401 // see if we have a typedef that should hide a struct or union
2402 if (mtype==MemberType_Typedef && Config_getBool(TYPEDEF_HIDES_STRUCT))
2403 {
2404 QCString ttype = type;
2405 ttype.stripPrefix("typedef ");
2406 if (ttype.left(7)=="struct " || ttype.left(6)=="union ")
2407 {
2408 ttype.stripPrefix("struct ");
2409 ttype.stripPrefix("union ");
2410 static const reg::Ex re(R"(\a\w*)");
2411 reg::Match match;
2412 std::string typ = ttype.str();
2413 if (reg::search(typ,match,re))
2414 {
2415 QCString typeValue = match.str();
2416 ClassDefMutable *cd = getClassMutable(typeValue);
2417 if (cd)
2418 {
2419 // this typedef should hide compound name cd, so we
2420 // change the name that is displayed from cd.
2421 cd->setClassName(name);
2422 cd->setDocumentation(root->doc,root->docFile,root->docLine);
2423 cd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2424 return 0;
2425 }
2426 }
2427 }
2428 }
2429
2430 // see if the function is inside a namespace
2431 NamespaceDefMutable *nd = 0;
2432 if (!scope.isEmpty())
2433 {
2434 if (scope.find('@')!=-1) return 0; // anonymous scope!
2435 nd = getResolvedNamespaceMutable(scope);
2436 }
2437 QCString def;
2438
2439 // determine the definition of the global variable
2440 if (nd && !nd->isAnonymous() &&
2441 !Config_getBool(HIDE_SCOPE_NAMES)
2442 )
2443 // variable is inside a namespace, so put the scope before the name
2444 {
2445 SrcLangExt lang = nd->getLanguage();
2446 QCString sep=getLanguageSpecificSeparator(lang);
2447
2448 if (!type.isEmpty())
2449 {
2450 if (root->spec&Entry::Alias) // turn 'typedef B NS::A' into 'using NS::A = B'
2451 {
2452 def="using "+nd->name()+sep+name+" = "+type;
2453 }
2454 else // normal member
2455 {
2456 def=type+" "+nd->name()+sep+name+args;
2457 }
2458 }
2459 else
2460 {
2461 def=nd->name()+sep+name+args;
2462 }
2463 }
2464 else
2465 {
2466 if (!type.isEmpty() && !root->name.isEmpty())
2467 {
2468 if (name.at(0)=='@') // dummy variable representing anonymous union
2469 {
2470 def=type;
2471 }
2472 else
2473 {
2474 if (root->spec&Entry::Alias) // turn 'typedef B A' into 'using A = B'
2475 {
2476 def="using "+root->name+" = "+type.mid(7);
2477 }
2478 else // normal member
2479 {
2480 def=type+" "+name+args;
2481 }
2482 }
2483 }
2484 else
2485 {
2486 def=name+args;
2487 }
2488 }
2489 def.stripPrefix("static ");
2490
2491 MemberName *mn=Doxygen::functionNameLinkedMap->find(name);
2492 if (mn)
2493 {
2494 //QCString nscope=removeAnonymousScopes(scope);
2495 //NamespaceDef *nd=0;
2496 //if (!nscope.isEmpty())
2497 if (!scope.isEmpty())
2498 {
2499 nd = getResolvedNamespaceMutable(scope);
2500 }
2501 for (const auto &imd : *mn)
2502 {
2503 MemberDefMutable *md = toMemberDefMutable(imd.get());
2504 if (md &&
2505 ((nd==0 && md->getNamespaceDef()==0 && md->getFileDef() &&
2506 root->fileName==md->getFileDef()->absFilePath()
2507 ) // both variable names in the same file
2508 || (nd!=0 && md->getNamespaceDef()==nd) // both in same namespace
2509 )
2510 && !md->isDefine() // function style #define's can be "overloaded" by typedefs or variables
2511 && !md->isEnumerate() // in C# an enum value and enum can have the same name
2512 )
2513 // variable already in the scope
2514 {
2515 bool isPHPArray = md->getLanguage()==SrcLangExt_PHP &&
2516 md->argsString()!=args &&
2517 args.find('[')!=-1;
2518 bool staticsInDifferentFiles =
2519 root->stat && md->isStatic() &&
2520 root->fileName!=md->getDefFileName();
2521
2522 if (md->getFileDef() &&
2523 !isPHPArray && // not a php array
2524 !staticsInDifferentFiles
2525 )
2526 // not a php array variable
2527 {
2528 Debug::print(Debug::Variables,0,
2529 " variable already found: scope=%s\n",qPrint(md->getOuterScope()->name()));
2530 addMemberDocs(root,md,def,0,FALSE,root->spec);
2531 md->setRefItems(root->sli);
2532 // if md is a variable forward declaration and root is the definition that
2533 // turn md into the definition
2534 if (!root->explicitExternal && md->isExternal())
2535 {
2536 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
2537 md->setExplicitExternal(FALSE,root->fileName,root->startLine,root->startColumn);
2538 }
2539 // if md is the definition and root point at a declaration, then add the
2540 // declaration info
2541 else if (root->explicitExternal && !md->isExternal())
2542 {
2543 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
2544 }
2545 return md;
2546 }
2547 }
2548 }
2549 }
2550
2551 QCString fileName = root->fileName;
2552 if (fileName.isEmpty() && root->tagInfo())
2553 {
2554 fileName = root->tagInfo()->tagName;
2555 }
2556
2557 Debug::print(Debug::Variables,0,
2558 " new variable, nd=%s tagInfo=%p!\n",nd?qPrint(nd->name()):"<global>",root->tagInfo());
2559 // new global variable, enum value or typedef
2560 std::unique_ptr<MemberDefMutable> md { createMemberDef(
2561 fileName,root->startLine,root->startColumn,
2562 type,name,args,QCString(),
2563 root->protection, Normal,root->stat,Member,
2564 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
2565 root->argList, root->metaData) };
2566 md->setTagInfo(root->tagInfo());
2567 md->setMemberSpecifiers(root->spec);
2568 md->setDocumentation(root->doc,root->docFile,root->docLine);
2569 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
2570 md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
2571 md->addSectionsToDefinition(root->anchors);
2572 md->setFromAnonymousScope(fromAnnScope);
2573 md->setFromAnonymousMember(fromAnnMemb);
2574 std::string init = root->initializer.str();
2575 md->setInitializer(init.c_str());
2576 md->setMaxInitLines(root->initLines);
2577 md->setMemberGroupId(root->mGrpId);
2578 md->setDefinition(def);
2579 md->setLanguage(root->lang);
2580 md->setId(root->id);
2581 md->enableCallGraph(root->callGraph);
2582 md->enableCallerGraph(root->callerGraph);
2583 md->enableReferencedByRelation(root->referencedByRelation);
2584 md->enableReferencesRelation(root->referencesRelation);
2585 md->setExplicitExternal(root->explicitExternal,fileName,root->startLine,root->startColumn);
2586 //md->setOuterScope(fd);
2587 if (!root->explicitExternal)
2588 {
2589 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
2590 md->setBodyDef(fd);
2591 }
2592 addMemberToGroups(root,md.get());
2593
2594 md->setRefItems(root->sli);
2595 if (nd && !nd->isAnonymous())
2596 {
2597 md->setNamespace(nd);
2598 nd->insertMember(md.get());
2599 }
2600
2601 // add member to the file (we do this even if we have already inserted
2602 // it into the namespace.
2603 if (fd)
2604 {
2605 md->setFileDef(fd);
2606 fd->insertMember(md.get());
2607 }
2608
2609 root->markAsProcessed();
2610
2611 // add member definition to the list of globals
2612 MemberDef *result = md.get();
2613 mn = Doxygen::functionNameLinkedMap->add(name);
2614 mn->push_back(std::move(md));
2615
2616 return result;
2617 }
2618
2619 /*! See if the return type string \a type is that of a function pointer
2620 * \returns -1 if this is not a function pointer variable or
2621 * the index at which the closing brace of (...*name) was found.
2622 */
findFunctionPtr(const std::string & type,SrcLangExt lang,int * pLength=0)2623 static int findFunctionPtr(const std::string &type,SrcLangExt lang, int *pLength=0)
2624 {
2625 if (lang == SrcLangExt_Fortran || lang == SrcLangExt_VHDL)
2626 {
2627 return -1; // Fortran and VHDL do not have function pointers
2628 }
2629
2630 static const reg::Ex re(R"(\([^)]*[*^][^)]*\))");
2631 reg::Match match;
2632 size_t i=std::string::npos;
2633 size_t l=0;
2634 if (reg::search(type,match,re)) // contains (...*...)
2635 {
2636 i = match.position();
2637 l = match.length();
2638 }
2639 size_t bb=type.find('<');
2640 size_t be=type.rfind('>');
2641
2642 if (!type.empty() && // return type is non-empty
2643 i!=std::string::npos && // contains (...*...)
2644 type.find("operator")==std::string::npos && // not an operator
2645 (type.find(")(")==std::string::npos || type.find("typedef ")!=std::string::npos) &&
2646 // not a function pointer return type
2647 !(bb<i && i<be) // bug665855: avoid treating "typedef A<void (T*)> type" as a function pointer
2648 )
2649 {
2650 if (pLength) *pLength=(int)l;
2651 //printf("findFunctionPtr=%d\n",(int)i);
2652 return (int)i;
2653 }
2654 else
2655 {
2656 //printf("findFunctionPtr=%d\n",-1);
2657 return -1;
2658 }
2659 }
2660
2661
2662 /*! Returns TRUE iff \a type is a class within scope \a context.
2663 * Used to detect variable declarations that look like function prototypes.
2664 */
isVarWithConstructor(const Entry * root)2665 static bool isVarWithConstructor(const Entry *root)
2666 {
2667 bool result=FALSE;
2668 bool typeIsClass = false;
2669 bool typePtrType = false;
2670 QCString type;
2671 Definition *ctx = 0;
2672 FileDef *fd = root->fileDef();
2673 int ti;
2674 SymbolResolver resolver(fd);
2675
2676 //printf("isVarWithConstructor(%s)\n",qPrint(rootNav->name()));
2677 if (root->parent()->section & Entry::COMPOUND_MASK)
2678 { // inside a class
2679 result=FALSE;
2680 goto done;
2681 }
2682 else if ((fd != nullptr) && (fd->name().right(2)==".c" || fd->name().right(2)==".h"))
2683 { // inside a .c file
2684 result=FALSE;
2685 goto done;
2686 }
2687 if (root->type.isEmpty())
2688 {
2689 result=FALSE;
2690 goto done;
2691 }
2692 if (!root->parent()->name.isEmpty())
2693 {
2694 ctx=Doxygen::namespaceLinkedMap->find(root->parent()->name);
2695 }
2696 type = root->type;
2697 // remove qualifiers
2698 findAndRemoveWord(type,"const");
2699 findAndRemoveWord(type,"static");
2700 findAndRemoveWord(type,"volatile");
2701 typePtrType = type.find('*')!=-1 || type.find('&')!=-1;
2702 //if (type.left(6)=="const ") type=type.right(type.length()-6);
2703 if (!typePtrType)
2704 {
2705 typeIsClass = resolver.resolveClass(ctx,type)!=0;
2706 if (!typeIsClass && (ti=type.find('<'))!=-1)
2707 {
2708 typeIsClass=resolver.resolveClass(ctx,type.left(ti))!=0;
2709 }
2710 }
2711 if (typeIsClass) // now we still have to check if the arguments are
2712 // types or values. Since we do not have complete type info
2713 // we need to rely on heuristics :-(
2714 {
2715 //printf("typeIsClass\n");
2716 if (root->argList.empty())
2717 {
2718 result=FALSE; // empty arg list -> function prototype.
2719 goto done;
2720 }
2721 for (const Argument &a : root->argList)
2722 {
2723 static const reg::Ex initChars(R"([\d"'&*!^]+)");
2724 reg::Match match;
2725 if (!a.name.isEmpty() || !a.defval.isEmpty())
2726 {
2727 std::string name = a.name.str();
2728 if (reg::search(name,match,initChars) && match.position()==0)
2729 {
2730 result=TRUE;
2731 }
2732 else
2733 {
2734 result=FALSE; // arg has (type,name) pair -> function prototype
2735 }
2736 goto done;
2737 }
2738 if (!a.type.isEmpty() &&
2739 (a.type.at(a.type.length()-1)=='*' ||
2740 a.type.at(a.type.length()-1)=='&'))
2741 // type ends with * or & => pointer or reference
2742 {
2743 result=FALSE;
2744 goto done;
2745 }
2746 if (a.type.isEmpty() || resolver.resolveClass(ctx,a.type)!=0)
2747 {
2748 result=FALSE; // arg type is a known type
2749 goto done;
2750 }
2751 if (checkIfTypedef(ctx,fd,a.type))
2752 {
2753 //printf("%s:%d: false (arg is typedef)\n",__FILE__,__LINE__);
2754 result=FALSE; // argument is a typedef
2755 goto done;
2756 }
2757 std::string atype = a.type.str();
2758 if (reg::search(atype,match,initChars) && match.position()==0)
2759 {
2760 result=TRUE; // argument type starts with typical initializer char
2761 goto done;
2762 }
2763 std::string resType=resolveTypeDef(ctx,a.type).str();
2764 if (resType.empty()) resType=atype;
2765 static const reg::Ex idChars(R"(\a\w*)");
2766 if (reg::search(resType,match,idChars) && match.position()==0) // resType starts with identifier
2767 {
2768 resType=match.str();
2769 //printf("resType=%s\n",resType.data());
2770 if (resType=="int" || resType=="long" ||
2771 resType=="float" || resType=="double" ||
2772 resType=="char" || resType=="void" ||
2773 resType=="signed" || resType=="unsigned" ||
2774 resType=="const" || resType=="volatile" )
2775 {
2776 result=FALSE; // type keyword -> function prototype
2777 goto done;
2778 }
2779 }
2780 }
2781 result=TRUE;
2782 }
2783
2784 done:
2785 //printf("isVarWithConstructor(%s,%s)=%d\n",qPrint(rootNav->parent()->name()),
2786 // qPrint(root->type),result);
2787 return result;
2788 }
2789
2790 static void addVariable(const Entry *root,int isFuncPtr=-1)
2791 {
2792 static bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
2793
2794 Debug::print(Debug::Variables,0,
2795 "VARIABLE_SEC: \n"
2796 " type='%s' name='%s' args='%s' bodyLine=%d mGrpId=%d relates='%s'\n",
2797 qPrint(root->type),
2798 qPrint(root->name),
2799 qPrint(root->args),
2800 root->bodyLine,
2801 root->mGrpId,
2802 qPrint(root->relates)
2803 );
2804 //printf("root->parent->name=%s\n",qPrint(root->parent->name));
2805
2806 QCString type = root->type;
2807 QCString name = root->name;
2808 QCString args = root->args;
2809 if (type.isEmpty() && name.find("operator")==-1 &&
2810 (name.find('*')!=-1 || name.find('&')!=-1))
2811 {
2812 // recover from parse error caused by redundant braces
2813 // like in "int *(var[10]);", which is parsed as
2814 // type="" name="int *" args="(var[10])"
2815
2816 type=name;
2817 std::string sargs = args.str();
2818 static const reg::Ex reName(R"(\a\w*)");
2819 reg::Match match;
2820 if (reg::search(sargs,match,reName))
2821 {
2822 name = match.str(); // e.g. 'var' in '(var[10])'
2823 sargs = match.suffix().str(); // e.g. '[10]) in '(var[10])'
2824 size_t j = sargs.find(')');
2825 if (j!=std::string::npos) args=sargs.substr(0,j); // extract, e.g '[10]' from '[10])'
2826 }
2827 //printf("new: type='%s' name='%s' args='%s'\n",
2828 // qPrint(type),qPrint(name),qPrint(args));
2829 }
2830 else
2831 {
2832 int i=isFuncPtr;
2833 if (i==-1 && (root->spec&Entry::Alias)==0) i=findFunctionPtr(type.str(),root->lang); // for typedefs isFuncPtr is not yet set
2834 Debug::print(Debug::Variables,0," functionPtr? %s\n",i!=-1?"yes":"no");
2835 if (i>=0) // function pointer
2836 {
2837 int ai = type.find('[',i);
2838 if (ai>i) // function pointer array
2839 {
2840 args.prepend(type.right(type.length()-ai));
2841 type=type.left(ai);
2842 }
2843 else if (type.find(')',i)!=-1) // function ptr, not variable like "int (*bla)[10]"
2844 {
2845 type=type.left(type.length()-1);
2846 args.prepend(") ");
2847 //printf("type=%s args=%s\n",qPrint(type),qPrint(args));
2848 }
2849 }
2850 }
2851
2852 QCString scope;
2853 name=removeRedundantWhiteSpace(name);
2854
2855 // find the scope of this variable
2856 Entry *p = root->parent();
2857 while ((p->section & Entry::SCOPE_MASK))
2858 {
2859 QCString scopeName = p->name;
2860 if (!scopeName.isEmpty())
2861 {
2862 scope.prepend(scopeName);
2863 break;
2864 }
2865 p=p->parent();
2866 }
2867
2868 MemberType mtype;
2869 type=type.stripWhiteSpace();
2870 ClassDefMutable *cd=0;
2871 bool isRelated=FALSE;
2872 bool isMemberOf=FALSE;
2873
2874 QCString classScope=stripAnonymousNamespaceScope(scope);
2875 classScope=stripTemplateSpecifiersFromScope(classScope,FALSE);
2876 QCString annScopePrefix=scope.left(scope.length()-classScope.length());
2877
2878 if (name.findRev("::")!=-1)
2879 {
2880 if (type=="friend class" || type=="friend struct" ||
2881 type=="friend union")
2882 {
2883 cd=getClassMutable(scope);
2884 if (cd)
2885 {
2886 addVariableToClass(root, // entry
2887 cd, // class to add member to
2888 MemberType_Friend, // type of member
2889 type, // type value as string
2890 name, // name of the member
2891 args, // arguments as string
2892 FALSE, // from Anonymous scope
2893 0, // anonymous member
2894 Public, // protection
2895 Member // related to a class
2896 );
2897 }
2898 }
2899 return; /* skip this member, because it is a
2900 * static variable definition (always?), which will be
2901 * found in a class scope as well, but then we know the
2902 * correct protection level, so only then it will be
2903 * inserted in the correct list!
2904 */
2905 }
2906
2907 if (type=="@")
2908 mtype=MemberType_EnumValue;
2909 else if (type.left(8)=="typedef ")
2910 mtype=MemberType_Typedef;
2911 else if (type.left(7)=="friend ")
2912 mtype=MemberType_Friend;
2913 else if (root->mtype==Property)
2914 mtype=MemberType_Property;
2915 else if (root->mtype==Event)
2916 mtype=MemberType_Event;
2917 else if (type.find("sequence<") != -1)
2918 mtype=sliceOpt ? MemberType_Sequence : MemberType_Typedef;
2919 else if (type.find("dictionary<") != -1)
2920 mtype=sliceOpt ? MemberType_Dictionary : MemberType_Typedef;
2921 else
2922 mtype=MemberType_Variable;
2923
2924 if (!root->relates.isEmpty()) // related variable
2925 {
2926 isRelated=TRUE;
2927 isMemberOf=(root->relatesType == MemberOf);
2928 if (getClass(root->relates)==0 && !scope.isEmpty())
2929 scope=mergeScopes(scope,root->relates);
2930 else
2931 scope=root->relates;
2932 }
2933
2934 cd=getClassMutable(scope);
2935 if (cd==0 && classScope!=scope) cd=getClassMutable(classScope);
2936 if (cd)
2937 {
2938 MemberDef *md=0;
2939
2940 // if cd is an anonymous (=tag less) scope we insert the member
2941 // into a non-anonymous parent scope as well. This is needed to
2942 // be able to refer to it using \var or \fn
2943
2944 //int indentDepth=0;
2945 int si=scope.find('@');
2946 //int anonyScopes = 0;
2947 //bool added=FALSE;
2948
2949 static bool inlineSimpleStructs = Config_getBool(INLINE_SIMPLE_STRUCTS);
2950 if (si!=-1 && !inlineSimpleStructs) // anonymous scope or type
2951 {
2952 QCString pScope;
2953 ClassDefMutable *pcd=0;
2954 pScope = scope.left(std::max(si-2,0)); // scope without tag less parts
2955 if (!pScope.isEmpty())
2956 pScope.prepend(annScopePrefix);
2957 else if (annScopePrefix.length()>2)
2958 pScope=annScopePrefix.left(annScopePrefix.length()-2);
2959 if (name.at(0)!='@')
2960 {
2961 if (!pScope.isEmpty() && (pcd=getClassMutable(pScope)))
2962 {
2963 md=addVariableToClass(root, // entry
2964 pcd, // class to add member to
2965 mtype, // member type
2966 type, // type value as string
2967 name, // member name
2968 args, // arguments as string
2969 TRUE, // from anonymous scope
2970 0, // from anonymous member
2971 root->protection,
2972 isMemberOf ? Foreign : isRelated ? Related : Member
2973 );
2974 //added=TRUE;
2975 }
2976 else // anonymous scope inside namespace or file => put variable in the global scope
2977 {
2978 if (mtype==MemberType_Variable)
2979 {
2980 md=addVariableToFile(root,mtype,pScope,type,name,args,TRUE,0);
2981 }
2982 //added=TRUE;
2983 }
2984 }
2985 }
2986
2987 //printf("name='%s' scope=%s scope.right=%s\n",
2988 // qPrint(name),qPrint(scope),
2989 // qPrint(scope.right(scope.length()-si)));
2990 addVariableToClass(root, // entry
2991 cd, // class to add member to
2992 mtype, // member type
2993 type, // type value as string
2994 name, // name of the member
2995 args, // arguments as string
2996 FALSE, // from anonymous scope
2997 md, // from anonymous member
2998 root->protection,
2999 isMemberOf ? Foreign : isRelated ? Related : Member);
3000 }
3001 else if (!name.isEmpty()) // global variable
3002 {
3003 //printf("Inserting member in global scope %s!\n",qPrint(scope));
3004 addVariableToFile(root,mtype,scope,type,name,args,FALSE,/*0,*/0);
3005 }
3006
3007 }
3008
3009 //----------------------------------------------------------------------
3010 // Searches the Entry tree for typedef documentation sections.
3011 // If found they are stored in their class or in the global list.
buildTypedefList(const Entry * root)3012 static void buildTypedefList(const Entry *root)
3013 {
3014 //printf("buildVarList(%s)\n",qPrint(rootNav->name()));
3015 if (!root->name.isEmpty() &&
3016 root->section==Entry::VARIABLE_SEC &&
3017 root->type.find("typedef ")!=-1 // its a typedef
3018 )
3019 {
3020 addVariable(root);
3021 }
3022 for (const auto &e : root->children())
3023 if (e->section!=Entry::ENUM_SEC)
3024 buildTypedefList(e.get());
3025 }
3026
3027 //----------------------------------------------------------------------
3028 // Searches the Entry tree for sequence documentation sections.
3029 // If found they are stored in the global list.
buildSequenceList(const Entry * root)3030 static void buildSequenceList(const Entry *root)
3031 {
3032 if (!root->name.isEmpty() &&
3033 root->section==Entry::VARIABLE_SEC &&
3034 root->type.find("sequence<")!=-1 // it's a sequence
3035 )
3036 {
3037 addVariable(root);
3038 }
3039 for (const auto &e : root->children())
3040 if (e->section!=Entry::ENUM_SEC)
3041 buildSequenceList(e.get());
3042 }
3043
3044 //----------------------------------------------------------------------
3045 // Searches the Entry tree for dictionary documentation sections.
3046 // If found they are stored in the global list.
buildDictionaryList(const Entry * root)3047 static void buildDictionaryList(const Entry *root)
3048 {
3049 if (!root->name.isEmpty() &&
3050 root->section==Entry::VARIABLE_SEC &&
3051 root->type.find("dictionary<")!=-1 // it's a dictionary
3052 )
3053 {
3054 addVariable(root);
3055 }
3056 for (const auto &e : root->children())
3057 if (e->section!=Entry::ENUM_SEC)
3058 buildDictionaryList(e.get());
3059 }
3060
3061 //----------------------------------------------------------------------
3062 // Searches the Entry tree for Variable documentation sections.
3063 // If found they are stored in their class or in the global list.
3064
buildVarList(const Entry * root)3065 static void buildVarList(const Entry *root)
3066 {
3067 //printf("buildVarList(%s) section=%08x\n",qPrint(rootNav->name()),rootNav->section());
3068 int isFuncPtr=-1;
3069 if (!root->name.isEmpty() &&
3070 (root->type.isEmpty() || g_compoundKeywords.find(root->type.str())==g_compoundKeywords.end()) &&
3071 (
3072 (root->section==Entry::VARIABLE_SEC // it's a variable
3073 ) ||
3074 (root->section==Entry::FUNCTION_SEC && // or maybe a function pointer variable
3075 (isFuncPtr=findFunctionPtr(root->type.str(),root->lang))!=-1
3076 ) ||
3077 (root->section==Entry::FUNCTION_SEC && // class variable initialized by constructor
3078 isVarWithConstructor(root)
3079 )
3080 )
3081 ) // documented variable
3082 {
3083 addVariable(root,isFuncPtr);
3084 }
3085 for (const auto &e : root->children())
3086 if (e->section!=Entry::ENUM_SEC)
3087 buildVarList(e.get());
3088 }
3089
3090 //----------------------------------------------------------------------
3091 // Searches the Entry tree for Interface sections (UNO IDL only).
3092 // If found they are stored in their service or in the global list.
3093 //
3094
addInterfaceOrServiceToServiceOrSingleton(const Entry * root,ClassDefMutable * cd,QCString const & rname)3095 static void addInterfaceOrServiceToServiceOrSingleton(
3096 const Entry *root,
3097 ClassDefMutable *cd,
3098 QCString const& rname)
3099 {
3100 FileDef *fd = root->fileDef();
3101 enum MemberType type = (root->section==Entry::EXPORTED_INTERFACE_SEC)
3102 ? MemberType_Interface
3103 : MemberType_Service;
3104 QCString fileName = root->fileName;
3105 if (fileName.isEmpty() && root->tagInfo())
3106 {
3107 fileName = root->tagInfo()->tagName;
3108 }
3109 std::unique_ptr<MemberDefMutable> md { createMemberDef(
3110 fileName, root->startLine, root->startColumn, root->type, rname,
3111 "", "", root->protection, root->virt, root->stat, Member,
3112 type, ArgumentList(), root->argList, root->metaData) };
3113 md->setTagInfo(root->tagInfo());
3114 md->setMemberClass(cd);
3115 md->setDocumentation(root->doc,root->docFile,root->docLine);
3116 md->setDocsForDefinition(false);
3117 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3118 md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3119 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3120 md->setMemberSpecifiers(root->spec);
3121 md->setMemberGroupId(root->mGrpId);
3122 md->setTypeConstraints(root->typeConstr);
3123 md->setLanguage(root->lang);
3124 md->setBodyDef(fd);
3125 md->setFileDef(fd);
3126 md->addSectionsToDefinition(root->anchors);
3127 QCString const def = root->type + " " + rname;
3128 md->setDefinition(def);
3129 md->enableCallGraph(root->callGraph);
3130 md->enableCallerGraph(root->callerGraph);
3131 md->enableReferencedByRelation(root->referencedByRelation);
3132 md->enableReferencesRelation(root->referencesRelation);
3133
3134 Debug::print(Debug::Functions,0,
3135 " Interface Member:\n"
3136 " '%s' '%s' proto=%d\n"
3137 " def='%s'\n",
3138 qPrint(root->type),
3139 qPrint(rname),
3140 root->proto,
3141 qPrint(def)
3142 );
3143
3144
3145 // add member to the class cd
3146 cd->insertMember(md.get());
3147 // also add the member as a "base" (to get nicer diagrams)
3148 // "optional" interface/service get Protected which turns into dashed line
3149 BaseInfo base(rname,
3150 (root->spec & (Entry::Optional)) ? Protected : Public,Normal);
3151 TemplateNameMap templateNames;
3152 findClassRelation(root,cd,cd,&base,templateNames,DocumentedOnly,true) ||
3153 findClassRelation(root,cd,cd,&base,templateNames,Undocumented,true);
3154 // add file to list of used files
3155 cd->insertUsedFile(fd);
3156
3157 addMemberToGroups(root,md.get());
3158 root->markAsProcessed();
3159 md->setRefItems(root->sli);
3160
3161 // add member to the global list of all members
3162 MemberName *mn = Doxygen::memberNameLinkedMap->add(rname);
3163 mn->push_back(std::move(md));
3164 }
3165
buildInterfaceAndServiceList(const Entry * root)3166 static void buildInterfaceAndServiceList(const Entry *root)
3167 {
3168 if (root->section==Entry::EXPORTED_INTERFACE_SEC ||
3169 root->section==Entry::INCLUDED_SERVICE_SEC)
3170 {
3171 Debug::print(Debug::Functions,0,
3172 "EXPORTED_INTERFACE_SEC:\n"
3173 " '%s' '%s'::'%s' '%s' relates='%s' relatesType='%d' file='%s' line='%d' bodyLine='%d' #tArgLists=%d mGrpId=%d spec=%lld proto=%d docFile=%s\n",
3174 qPrint(root->type),
3175 qPrint(root->parent()->name),
3176 qPrint(root->name),
3177 qPrint(root->args),
3178 qPrint(root->relates),
3179 root->relatesType,
3180 qPrint(root->fileName),
3181 root->startLine,
3182 root->bodyLine,
3183 root->tArgLists.size(),
3184 root->mGrpId,
3185 root->spec,
3186 root->proto,
3187 qPrint(root->docFile)
3188 );
3189
3190 QCString rname = removeRedundantWhiteSpace(root->name);
3191
3192 if (!rname.isEmpty())
3193 {
3194 QCString scope = root->parent()->name;
3195 ClassDefMutable *cd = getClassMutable(scope);
3196 assert(cd);
3197 if (cd && ((ClassDef::Interface == cd->compoundType()) ||
3198 (ClassDef::Service == cd->compoundType()) ||
3199 (ClassDef::Singleton == cd->compoundType())))
3200 {
3201 addInterfaceOrServiceToServiceOrSingleton(root,cd,rname);
3202 }
3203 else
3204 {
3205 assert(false); // was checked by scanner.l
3206 }
3207 }
3208 else if (rname.isEmpty())
3209 {
3210 warn(root->fileName,root->startLine,
3211 "Illegal member name found.");
3212 }
3213 }
3214 // can only have these in IDL anyway
3215 switch (root->lang)
3216 {
3217 case SrcLangExt_Unknown: // fall through (root node always is Unknown)
3218 case SrcLangExt_IDL:
3219 for (const auto &e : root->children()) buildInterfaceAndServiceList(e.get());
3220 break;
3221 default:
3222 return; // nothing to do here
3223 }
3224 }
3225
3226
3227 //----------------------------------------------------------------------
3228 // Searches the Entry tree for Function sections.
3229 // If found they are stored in their class or in the global list.
3230
addMethodToClass(const Entry * root,ClassDefMutable * cd,const QCString & rtype,const QCString & rname,const QCString & rargs,bool isFriend,Protection protection,bool stat,Specifier virt,uint64 spec,const QCString & relates)3231 static void addMethodToClass(const Entry *root,ClassDefMutable *cd,
3232 const QCString &rtype,const QCString &rname,const QCString &rargs,
3233 bool isFriend,
3234 Protection protection,bool stat,Specifier virt,uint64 spec,
3235 const QCString &relates
3236 )
3237 {
3238 FileDef *fd=root->fileDef();
3239
3240 QCString type = rtype;
3241 QCString args = rargs;
3242
3243 QCString name=removeRedundantWhiteSpace(rname);
3244 if (name.left(2)=="::") name=name.right(name.length()-2);
3245
3246 MemberType mtype;
3247 if (isFriend) mtype=MemberType_Friend;
3248 else if (root->mtype==Signal) mtype=MemberType_Signal;
3249 else if (root->mtype==Slot) mtype=MemberType_Slot;
3250 else if (root->mtype==DCOP) mtype=MemberType_DCOP;
3251 else mtype=MemberType_Function;
3252
3253 // strip redundant template specifier for constructors
3254 int i = -1;
3255 int j = -1;
3256 if ((fd==0 || fd->getLanguage()==SrcLangExt_Cpp) &&
3257 name.left(9)!="operator " && // not operator
3258 (i=name.find('<'))!=-1 && // containing <
3259 (j=name.find('>'))!=-1 && // or >
3260 (j!=i+2 || name.at(i+1)!='=') // but not the C++20 spaceship operator <=>
3261 )
3262 {
3263 name=name.left(i);
3264 }
3265
3266 QCString fileName = root->fileName;
3267 if (fileName.isEmpty() && root->tagInfo())
3268 {
3269 fileName = root->tagInfo()->tagName;
3270 }
3271
3272 //printf("root->name='%s; args='%s' root->argList='%s'\n",
3273 // qPrint(root->name),qPrint(args),qPrint(argListToString(root->argList))
3274 // );
3275
3276 // adding class member
3277 std::unique_ptr<MemberDefMutable> md { createMemberDef(
3278 fileName,root->startLine,root->startColumn,
3279 type,name,args,root->exception,
3280 protection,virt,
3281 stat && root->relatesType != MemberOf,
3282 relates.isEmpty() ? Member :
3283 root->relatesType == MemberOf ? Foreign : Related,
3284 mtype,!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3285 root->argList, root->metaData) };
3286 md->setTagInfo(root->tagInfo());
3287 md->setMemberClass(cd);
3288 md->setDocumentation(root->doc,root->docFile,root->docLine);
3289 md->setDocsForDefinition(!root->proto);
3290 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3291 md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3292 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3293 md->setMemberSpecifiers(spec);
3294 md->setMemberGroupId(root->mGrpId);
3295 md->setTypeConstraints(root->typeConstr);
3296 md->setLanguage(root->lang);
3297 md->setRequiresClause(root->req);
3298 md->setId(root->id);
3299 md->setBodyDef(fd);
3300 md->setFileDef(fd);
3301 md->addSectionsToDefinition(root->anchors);
3302 QCString def;
3303 QCString qualScope = cd->qualifiedNameWithTemplateParameters();
3304 SrcLangExt lang = cd->getLanguage();
3305 QCString scopeSeparator=getLanguageSpecificSeparator(lang);
3306 if (scopeSeparator!="::")
3307 {
3308 qualScope = substitute(qualScope,"::",scopeSeparator);
3309 }
3310 if (lang==SrcLangExt_PHP)
3311 {
3312 // for PHP we use Class::method and Namespace\method
3313 scopeSeparator="::";
3314 }
3315 // QCString optArgs = root->argList.empty() ? args : QCString();
3316 if (!relates.isEmpty() || isFriend || Config_getBool(HIDE_SCOPE_NAMES))
3317 {
3318 if (!type.isEmpty())
3319 {
3320 def=type+" "+name; //+optArgs;
3321 }
3322 else
3323 {
3324 def=name; //+optArgs;
3325 }
3326 }
3327 else
3328 {
3329 if (!type.isEmpty())
3330 {
3331 def=type+" "+qualScope+scopeSeparator+name; //+optArgs;
3332 }
3333 else
3334 {
3335 def=qualScope+scopeSeparator+name; //+optArgs;
3336 }
3337 }
3338 if (def.left(7)=="friend ") def=def.right(def.length()-7);
3339 md->setDefinition(def);
3340 md->enableCallGraph(root->callGraph);
3341 md->enableCallerGraph(root->callerGraph);
3342 md->enableReferencedByRelation(root->referencedByRelation);
3343 md->enableReferencesRelation(root->referencesRelation);
3344
3345 Debug::print(Debug::Functions,0,
3346 " Func Member:\n"
3347 " '%s' '%s'::'%s' '%s' proto=%d\n"
3348 " def='%s'\n",
3349 qPrint(type),
3350 qPrint(qualScope),
3351 qPrint(rname),
3352 qPrint(args),
3353 root->proto,
3354 qPrint(def)
3355 );
3356
3357 // add member to the class cd
3358 cd->insertMember(md.get());
3359 // add file to list of used files
3360 cd->insertUsedFile(fd);
3361
3362 addMemberToGroups(root,md.get());
3363 root->markAsProcessed();
3364 md->setRefItems(root->sli);
3365
3366 // add member to the global list of all members
3367 //printf("Adding member=%s class=%s\n",qPrint(md->name()),qPrint(cd->name()));
3368 MemberName *mn = Doxygen::memberNameLinkedMap->add(name);
3369 mn->push_back(std::move(md));
3370 }
3371
3372 //------------------------------------------------------------------------------------------
3373
addGlobalFunction(const Entry * root,const QCString & rname,const QCString & sc)3374 static void addGlobalFunction(const Entry *root,const QCString &rname,const QCString &sc)
3375 {
3376 QCString scope = sc;
3377 Debug::print(Debug::Functions,0," --> new function %s found!\n",qPrint(rname));
3378 //printf("New function type='%s' name='%s' args='%s' bodyLine=%d\n",
3379 // qPrint(root->type),qPrint(rname),qPrint(root->args),root->bodyLine);
3380
3381 // new global function
3382 QCString name=removeRedundantWhiteSpace(rname);
3383 std::unique_ptr<MemberDefMutable> md { createMemberDef(
3384 root->fileName,root->startLine,root->startColumn,
3385 root->type,name,root->args,root->exception,
3386 root->protection,root->virt,root->stat,Member,
3387 MemberType_Function,
3388 !root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList(),
3389 root->argList,root->metaData) };
3390
3391 md->setTagInfo(root->tagInfo());
3392 md->setLanguage(root->lang);
3393 md->setId(root->id);
3394 md->setDocumentation(root->doc,root->docFile,root->docLine);
3395 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3396 md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3397 md->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
3398 md->setDocsForDefinition(!root->proto);
3399 md->setTypeConstraints(root->typeConstr);
3400 //md->setBody(root->body);
3401 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3402 FileDef *fd=root->fileDef();
3403 md->setBodyDef(fd);
3404 md->addSectionsToDefinition(root->anchors);
3405 md->setMemberSpecifiers(root->spec);
3406 md->setMemberGroupId(root->mGrpId);
3407 md->setRequiresClause(root->req);
3408
3409 NamespaceDefMutable *nd = 0;
3410 // see if the function is inside a namespace that was not part of
3411 // the name already (in that case nd should be non-zero already)
3412 if (root->parent()->section == Entry::NAMESPACE_SEC )
3413 {
3414 //QCString nscope=removeAnonymousScopes(root->parent()->name);
3415 QCString nscope=root->parent()->name;
3416 if (!nscope.isEmpty())
3417 {
3418 nd = getResolvedNamespaceMutable(nscope);
3419 }
3420 }
3421
3422 if (!scope.isEmpty())
3423 {
3424 QCString sep = getLanguageSpecificSeparator(root->lang);
3425 if (sep!="::")
3426 {
3427 scope = substitute(scope,"::",sep);
3428 }
3429 scope+=sep;
3430 }
3431
3432 QCString def;
3433 //QCString optArgs = root->argList.empty() ? QCString() : root->args;
3434 if (!root->type.isEmpty())
3435 {
3436 def=root->type+" "+scope+name; //+optArgs;
3437 }
3438 else
3439 {
3440 def=scope+name; //+optArgs;
3441 }
3442 Debug::print(Debug::Functions,0,
3443 " Global Function:\n"
3444 " '%s' '%s'::'%s' '%s' proto=%d\n"
3445 " def='%s'\n",
3446 qPrint(root->type),
3447 qPrint(root->parent()->name),
3448 qPrint(rname),
3449 qPrint(root->args),
3450 root->proto,
3451 qPrint(def)
3452 );
3453 md->setDefinition(def);
3454 md->enableCallGraph(root->callGraph);
3455 md->enableCallerGraph(root->callerGraph);
3456 md->enableReferencedByRelation(root->referencedByRelation);
3457 md->enableReferencesRelation(root->referencesRelation);
3458 //if (root->mGrpId!=-1)
3459 //{
3460 // md->setMemberGroup(memberGroupDict[root->mGrpId]);
3461 //}
3462
3463 md->setRefItems(root->sli);
3464 if (nd && !nd->name().isEmpty() && nd->name().at(0)!='@')
3465 {
3466 // add member to namespace
3467 md->setNamespace(nd);
3468 nd->insertMember(md.get());
3469 }
3470 if (fd)
3471 {
3472 // add member to the file (we do this even if we have already
3473 // inserted it into the namespace)
3474 md->setFileDef(fd);
3475 fd->insertMember(md.get());
3476 }
3477
3478 addMemberToGroups(root,md.get());
3479 if (root->relatesType == Simple) // if this is a relatesalso command,
3480 // allow find Member to pick it up
3481 {
3482 root->markAsProcessed(); // Otherwise we have finished with this entry.
3483 }
3484
3485 // add member to the list of file members
3486 //printf("Adding member=%s\n",qPrint(md->name()));
3487 MemberName *mn = Doxygen::functionNameLinkedMap->add(name);
3488 mn->push_back(std::move(md));
3489 }
3490
3491 //------------------------------------------------------------------------------------------
3492
buildFunctionList(const Entry * root)3493 static void buildFunctionList(const Entry *root)
3494 {
3495 if (root->section==Entry::FUNCTION_SEC)
3496 {
3497 Debug::print(Debug::Functions,0,
3498 "FUNCTION_SEC:\n"
3499 " '%s' '%s'::'%s' '%s' relates='%s' relatesType='%d' file='%s' line='%d' bodyLine='%d' #tArgLists=%d mGrpId=%d spec=%lld proto=%d docFile=%s\n",
3500 qPrint(root->type),
3501 qPrint(root->parent()->name),
3502 qPrint(root->name),
3503 qPrint(root->args),
3504 qPrint(root->relates),
3505 root->relatesType,
3506 qPrint(root->fileName),
3507 root->startLine,
3508 root->bodyLine,
3509 root->tArgLists.size(),
3510 root->mGrpId,
3511 root->spec,
3512 root->proto,
3513 qPrint(root->docFile)
3514 );
3515
3516 bool isFriend=root->type.find("friend ")!=-1;
3517 QCString rname = removeRedundantWhiteSpace(root->name);
3518 //printf("rname=%s\n",qPrint(rname));
3519
3520 QCString scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name);
3521 if (!rname.isEmpty() && scope.find('@')==-1)
3522 {
3523 ClassDefMutable *cd=0;
3524 // check if this function's parent is a class
3525 scope=stripTemplateSpecifiersFromScope(scope,FALSE);
3526
3527 FileDef *rfd=root->fileDef();
3528
3529 int memIndex=rname.findRev("::");
3530
3531 cd=getClassMutable(scope);
3532 if (cd && scope+"::"==rname.left(scope.length()+2)) // found A::f inside A
3533 {
3534 // strip scope from name
3535 rname=rname.right(rname.length()-root->parent()->name.length()-2);
3536 }
3537
3538 bool isMember=FALSE;
3539 if (memIndex!=-1)
3540 {
3541 int ts=rname.find('<');
3542 int te=rname.find('>');
3543 if (memIndex>0 && (ts==-1 || te==-1))
3544 {
3545 // note: the following code was replaced by inMember=TRUE to deal with a
3546 // function rname='X::foo' of class X inside a namespace also called X...
3547 // bug id 548175
3548 //nd = Doxygen::namespaceLinkedMap->find(rname.left(memIndex));
3549 //isMember = nd==0;
3550 //if (nd)
3551 //{
3552 // // strip namespace scope from name
3553 // scope=rname.left(memIndex);
3554 // rname=rname.right(rname.length()-memIndex-2);
3555 //}
3556 isMember = TRUE;
3557 }
3558 else
3559 {
3560 isMember=memIndex<ts || memIndex>te;
3561 }
3562 }
3563
3564 if (!root->parent()->name.isEmpty() &&
3565 (root->parent()->section & Entry::COMPOUND_MASK) &&
3566 cd
3567 )
3568 {
3569 Debug::print(Debug::Functions,0," --> member %s of class %s!\n",
3570 qPrint(rname),qPrint(cd->name()));
3571 addMethodToClass(root,cd,root->type,rname,root->args,isFriend,
3572 root->protection,root->stat,root->virt,root->spec,root->relates);
3573 }
3574 else if (!((root->parent()->section & Entry::COMPOUND_MASK)
3575 || root->parent()->section==Entry::OBJCIMPL_SEC
3576 ) &&
3577 !isMember &&
3578 (root->relates.isEmpty() || root->relatesType == Duplicate) &&
3579 root->type.left(7)!="extern " && root->type.left(8)!="typedef "
3580 )
3581 // no member => unrelated function
3582 {
3583 /* check the uniqueness of the function name in the file.
3584 * A file could contain a function prototype and a function definition
3585 * or even multiple function prototypes.
3586 */
3587 bool found=FALSE;
3588 MemberName *mn;
3589 MemberDef *md_found=0;
3590 if ((mn=Doxygen::functionNameLinkedMap->find(rname)))
3591 {
3592 Debug::print(Debug::Functions,0," --> function %s already found!\n",qPrint(rname));
3593 for (const auto &imd : *mn)
3594 {
3595 MemberDefMutable *md = toMemberDefMutable(imd.get());
3596 if (md)
3597 {
3598 const NamespaceDef *mnd = md->getNamespaceDef();
3599 NamespaceDef *rnd = 0;
3600 //printf("root namespace=%s\n",qPrint(rootNav->parent()->name()));
3601 QCString fullScope = scope;
3602 QCString parentScope = root->parent()->name;
3603 if (!parentScope.isEmpty() && !leftScopeMatch(parentScope,scope))
3604 {
3605 if (!scope.isEmpty()) fullScope.prepend("::");
3606 fullScope.prepend(parentScope);
3607 }
3608 //printf("fullScope=%s\n",qPrint(fullScope));
3609 rnd = getResolvedNamespace(fullScope);
3610 const FileDef *mfd = md->getFileDef();
3611 QCString nsName,rnsName;
3612 if (mnd) nsName = mnd->name();
3613 if (rnd) rnsName = rnd->name();
3614 //printf("matching arguments for %s%s %s%s\n",
3615 // qPrint(md->name()),md->argsString(),qPrint(rname),qPrint(argListToString(root->argList)));
3616 const ArgumentList &mdAl = md->argumentList();
3617 const ArgumentList &mdTempl = md->templateArguments();
3618
3619 // in case of template functions, we need to check if the
3620 // functions have the same number of template parameters
3621 bool sameNumTemplateArgs = TRUE;
3622 bool matchingReturnTypes = TRUE;
3623 bool sameRequiresClause = TRUE;
3624 if (!mdTempl.empty() && !root->tArgLists.empty())
3625 {
3626 if (mdTempl.size()!=root->tArgLists.back().size())
3627 {
3628 sameNumTemplateArgs = FALSE;
3629 }
3630 if (md->typeString()!=removeRedundantWhiteSpace(root->type))
3631 {
3632 matchingReturnTypes = FALSE;
3633 }
3634 if (md->requiresClause()!=root->req)
3635 {
3636 sameRequiresClause = FALSE;
3637 }
3638 }
3639 else if (!mdTempl.empty() || !root->tArgLists.empty())
3640 { // if one has template parameters and the other doesn't then that also counts as a
3641 // difference
3642 sameNumTemplateArgs = FALSE;
3643 }
3644
3645 bool staticsInDifferentFiles =
3646 root->stat && md->isStatic() && root->fileName!=md->getDefFileName();
3647
3648 if (
3649 matchArguments2(md->getOuterScope(),mfd,&mdAl,
3650 rnd ? rnd : Doxygen::globalScope,rfd,&root->argList,
3651 FALSE) &&
3652 sameNumTemplateArgs &&
3653 matchingReturnTypes &&
3654 sameRequiresClause &&
3655 !staticsInDifferentFiles
3656 )
3657 {
3658 GroupDef *gd=0;
3659 if (!root->groups.empty() && !root->groups.front().groupname.isEmpty())
3660 {
3661 gd = Doxygen::groupLinkedMap->find(root->groups.front().groupname);
3662 }
3663 //printf("match!\n");
3664 //printf("mnd=%p rnd=%p nsName=%s rnsName=%s\n",mnd,rnd,qPrint(nsName),qPrint(rnsName));
3665 // see if we need to create a new member
3666 found=(mnd && rnd && nsName==rnsName) || // members are in the same namespace
3667 ((mnd==0 && rnd==0 && mfd!=0 && // no external reference and
3668 mfd->absFilePath()==root->fileName // prototype in the same file
3669 )
3670 );
3671 // otherwise, allow a duplicate global member with the same argument list
3672 if (!found && gd && gd==md->getGroupDef() && nsName==rnsName)
3673 {
3674 // member is already in the group, so we don't want to add it again.
3675 found=TRUE;
3676 }
3677
3678 //printf("combining function with prototype found=%d in namespace %s\n",
3679 // found,qPrint(nsName));
3680
3681 if (found)
3682 {
3683 // merge argument lists
3684 ArgumentList mergedArgList = root->argList;
3685 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.isEmpty());
3686 // merge documentation
3687 if (md->documentation().isEmpty() && !root->doc.isEmpty())
3688 {
3689 if (root->proto)
3690 {
3691 //printf("setDeclArgumentList to %p\n",argList);
3692 md->moveDeclArgumentList(stringToArgumentList(root->lang,root->args));
3693 }
3694 else
3695 {
3696 md->moveArgumentList(stringToArgumentList(root->lang,root->args));
3697 }
3698 }
3699
3700 md->setDocumentation(root->doc,root->docFile,root->docLine);
3701 md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
3702 md->setDocsForDefinition(!root->proto);
3703 if (md->getStartBodyLine()==-1 && root->bodyLine!=-1)
3704 {
3705 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
3706 md->setBodyDef(rfd);
3707 }
3708
3709 if (md->briefDescription().isEmpty() && !root->brief.isEmpty())
3710 {
3711 md->setArgsString(root->args);
3712 }
3713 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
3714
3715 md->addSectionsToDefinition(root->anchors);
3716
3717 md->enableCallGraph(md->hasCallGraph() || root->callGraph);
3718 md->enableCallerGraph(md->hasCallerGraph() || root->callerGraph);
3719 md->enableReferencedByRelation(md->hasReferencedByRelation() || root->referencedByRelation);
3720 md->enableReferencesRelation(md->hasReferencesRelation() || root->referencesRelation);
3721
3722 // merge ingroup specifiers
3723 if (md->getGroupDef()==0 && !root->groups.empty())
3724 {
3725 addMemberToGroups(root,md);
3726 }
3727 else if (md->getGroupDef()!=0 && root->groups.empty())
3728 {
3729 //printf("existing member is grouped, new member not\n");
3730 }
3731 else if (md->getGroupDef()!=0 && !root->groups.empty())
3732 {
3733 //printf("both members are grouped\n");
3734 }
3735
3736 // if md is a declaration and root is the corresponding
3737 // definition, then turn md into a definition.
3738 if (md->isPrototype() && !root->proto)
3739 {
3740 md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn());
3741 md->setPrototype(FALSE,root->fileName,root->startLine,root->startColumn);
3742 }
3743 // if md is already the definition, then add the declaration info
3744 else if (!md->isPrototype() && root->proto)
3745 {
3746 md->setDeclFile(root->fileName,root->startLine,root->startColumn);
3747 }
3748 }
3749 }
3750 }
3751 if (found)
3752 {
3753 md_found = md;
3754 break;
3755 }
3756 }
3757 }
3758 if (!found) /* global function is unique with respect to the file */
3759 {
3760 addGlobalFunction(root,rname,scope);
3761 }
3762 else
3763 {
3764 FileDef *fd=root->fileDef();
3765 if (fd)
3766 {
3767 // add member to the file (we do this even if we have already
3768 // inserted it into the namespace)
3769 fd->insertMember(md_found);
3770 }
3771 }
3772
3773 //printf("unrelated function %d '%s' '%s' '%s'\n",
3774 // root->parent->section,qPrint(root->type),qPrint(rname),qPrint(root->args));
3775 }
3776 else
3777 {
3778 Debug::print(Debug::Functions,0," --> %s not processed!\n",qPrint(rname));
3779 }
3780 }
3781 else if (rname.isEmpty())
3782 {
3783 warn(root->fileName,root->startLine,
3784 "Illegal member name found."
3785 );
3786 }
3787 }
3788 for (const auto &e : root->children()) buildFunctionList(e.get());
3789 }
3790
3791 //----------------------------------------------------------------------
3792
findFriends()3793 static void findFriends()
3794 {
3795 //printf("findFriends()\n");
3796 for (const auto &fn : *Doxygen::functionNameLinkedMap) // for each global function name
3797 {
3798 //printf("Function name='%s'\n",fn->memberName());
3799 MemberName *mn;
3800 if ((mn=Doxygen::memberNameLinkedMap->find(fn->memberName())))
3801 { // there are members with the same name
3802 //printf("Function name is also a member name\n");
3803 // for each function with that name
3804 for (const auto &ifmd : *fn)
3805 {
3806 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
3807 // for each member with that name
3808 for (const auto &immd : *mn)
3809 {
3810 MemberDefMutable *mmd = toMemberDefMutable(immd.get());
3811 //printf("Checking for matching arguments
3812 // mmd->isRelated()=%d mmd->isFriend()=%d mmd->isFunction()=%d\n",
3813 // mmd->isRelated(),mmd->isFriend(),mmd->isFunction());
3814 if (fmd && mmd &&
3815 (mmd->isFriend() || (mmd->isRelated() && mmd->isFunction())) &&
3816 matchArguments2(mmd->getOuterScope(), mmd->getFileDef(), &mmd->argumentList(),
3817 fmd->getOuterScope(), fmd->getFileDef(), &fmd->argumentList(),
3818 TRUE
3819 )
3820
3821 ) // if the member is related and the arguments match then the
3822 // function is actually a friend.
3823 {
3824 const ArgumentList &mmdAl = mmd->argumentList();
3825 const ArgumentList &fmdAl = fmd->argumentList();
3826 mergeArguments(const_cast<ArgumentList&>(mmdAl),const_cast<ArgumentList&>(fmdAl));
3827 if (!fmd->documentation().isEmpty())
3828 {
3829 mmd->setDocumentation(fmd->documentation(),fmd->docFile(),fmd->docLine());
3830 }
3831 else if (!mmd->documentation().isEmpty())
3832 {
3833 fmd->setDocumentation(mmd->documentation(),mmd->docFile(),mmd->docLine());
3834 }
3835 if (mmd->briefDescription().isEmpty() && !fmd->briefDescription().isEmpty())
3836 {
3837 mmd->setBriefDescription(fmd->briefDescription(),fmd->briefFile(),fmd->briefLine());
3838 }
3839 else if (!mmd->briefDescription().isEmpty() && !fmd->briefDescription().isEmpty())
3840 {
3841 fmd->setBriefDescription(mmd->briefDescription(),mmd->briefFile(),mmd->briefLine());
3842 }
3843 if (!fmd->inbodyDocumentation().isEmpty())
3844 {
3845 mmd->setInbodyDocumentation(fmd->inbodyDocumentation(),fmd->inbodyFile(),fmd->inbodyLine());
3846 }
3847 else if (!mmd->inbodyDocumentation().isEmpty())
3848 {
3849 fmd->setInbodyDocumentation(mmd->inbodyDocumentation(),mmd->inbodyFile(),mmd->inbodyLine());
3850 }
3851 //printf("body mmd %d fmd %d\n",mmd->getStartBodyLine(),fmd->getStartBodyLine());
3852 if (mmd->getStartBodyLine()==-1 && fmd->getStartBodyLine()!=-1)
3853 {
3854 mmd->setBodySegment(fmd->getDefLine(),fmd->getStartBodyLine(),fmd->getEndBodyLine());
3855 mmd->setBodyDef(fmd->getBodyDef());
3856 //mmd->setBodyMember(fmd);
3857 }
3858 else if (mmd->getStartBodyLine()!=-1 && fmd->getStartBodyLine()==-1)
3859 {
3860 fmd->setBodySegment(mmd->getDefLine(),mmd->getStartBodyLine(),mmd->getEndBodyLine());
3861 fmd->setBodyDef(mmd->getBodyDef());
3862 //fmd->setBodyMember(mmd);
3863 }
3864 mmd->setDocsForDefinition(fmd->isDocsForDefinition());
3865
3866 mmd->enableCallGraph(mmd->hasCallGraph() || fmd->hasCallGraph());
3867 mmd->enableCallerGraph(mmd->hasCallerGraph() || fmd->hasCallerGraph());
3868 mmd->enableReferencedByRelation(mmd->hasReferencedByRelation() || fmd->hasReferencedByRelation());
3869 mmd->enableReferencesRelation(mmd->hasReferencesRelation() || fmd->hasReferencesRelation());
3870
3871 fmd->enableCallGraph(mmd->hasCallGraph() || fmd->hasCallGraph());
3872 fmd->enableCallerGraph(mmd->hasCallerGraph() || fmd->hasCallerGraph());
3873 fmd->enableReferencedByRelation(mmd->hasReferencedByRelation() || fmd->hasReferencedByRelation());
3874 fmd->enableReferencesRelation(mmd->hasReferencesRelation() || fmd->hasReferencesRelation());
3875 }
3876 }
3877 }
3878 }
3879 }
3880 }
3881
3882 //----------------------------------------------------------------------
3883
transferFunctionDocumentation()3884 static void transferFunctionDocumentation()
3885 {
3886 //printf("---- transferFunctionDocumentation()\n");
3887
3888 // find matching function declaration and definitions.
3889 for (const auto &mn : *Doxygen::functionNameLinkedMap)
3890 {
3891 //printf("memberName=%s count=%d\n",mn->memberName(),mn->count());
3892 /* find a matching function declaration and definition for this function */
3893 for (const auto &imdec : *mn)
3894 {
3895 MemberDefMutable *mdec = toMemberDefMutable(imdec.get());
3896 if (mdec &&
3897 (mdec->isPrototype() ||
3898 (mdec->isVariable() && mdec->isExternal())
3899 ))
3900 {
3901 for (const auto &imdef : *mn)
3902 {
3903 MemberDefMutable *mdef = toMemberDefMutable(imdef.get());
3904 if (mdef && mdec!=mdef &&
3905 mdec->getNamespaceDef()==mdef->getNamespaceDef())
3906 {
3907 combineDeclarationAndDefinition(mdec,mdef);
3908 }
3909 }
3910 }
3911 }
3912 }
3913 }
3914
3915 //----------------------------------------------------------------------
3916
transferFunctionReferences()3917 static void transferFunctionReferences()
3918 {
3919 for (const auto &mn : *Doxygen::functionNameLinkedMap)
3920 {
3921 MemberDefMutable *mdef=0,*mdec=0;
3922 /* find a matching function declaration and definition for this function */
3923 for (const auto &imd : *mn)
3924 {
3925 MemberDefMutable *md = toMemberDefMutable(imd.get());
3926 if (md)
3927 {
3928 if (md->isPrototype())
3929 mdec=md;
3930 else if (md->isVariable() && md->isExternal())
3931 mdec=md;
3932
3933 if (md->isFunction() && !md->isStatic() && !md->isPrototype())
3934 mdef=md;
3935 else if (md->isVariable() && !md->isExternal() && !md->isStatic())
3936 mdef=md;
3937 }
3938
3939 if (mdef && mdec) break;
3940 }
3941 if (mdef && mdec)
3942 {
3943 const ArgumentList &mdefAl = mdef->argumentList();
3944 const ArgumentList &mdecAl = mdec->argumentList();
3945 if (
3946 matchArguments2(mdef->getOuterScope(),mdef->getFileDef(),const_cast<ArgumentList*>(&mdefAl),
3947 mdec->getOuterScope(),mdec->getFileDef(),const_cast<ArgumentList*>(&mdecAl),
3948 TRUE
3949 )
3950 ) /* match found */
3951 {
3952 mdef->mergeReferences(mdec);
3953 mdec->mergeReferences(mdef);
3954 mdef->mergeReferencedBy(mdec);
3955 mdec->mergeReferencedBy(mdef);
3956 }
3957 }
3958 }
3959 }
3960
3961 //----------------------------------------------------------------------
3962
transferRelatedFunctionDocumentation()3963 static void transferRelatedFunctionDocumentation()
3964 {
3965 // find match between function declaration and definition for
3966 // related functions
3967 for (const auto &mn : *Doxygen::functionNameLinkedMap)
3968 {
3969 /* find a matching function declaration and definition for this function */
3970 // for each global function
3971 for (const auto &imd : *mn)
3972 {
3973 MemberDefMutable *md = toMemberDefMutable(imd.get());
3974 if (md)
3975 {
3976 //printf(" Function '%s'\n",qPrint(md->name()));
3977 MemberName *rmn;
3978 if ((rmn=Doxygen::memberNameLinkedMap->find(md->name()))) // check if there is a member with the same name
3979 {
3980 //printf(" Member name found\n");
3981 // for each member with the same name
3982 for (const auto &irmd : *rmn)
3983 {
3984 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
3985 //printf(" Member found: related='%d'\n",rmd->isRelated());
3986 if (rmd &&
3987 (rmd->isRelated() || rmd->isForeign()) && // related function
3988 matchArguments2( md->getOuterScope(), md->getFileDef(), &md->argumentList(),
3989 rmd->getOuterScope(),rmd->getFileDef(),&rmd->argumentList(),
3990 TRUE
3991 )
3992 )
3993 {
3994 //printf(" Found related member '%s'\n",qPrint(md->name()));
3995 if (rmd->relatedAlso())
3996 md->setRelatedAlso(rmd->relatedAlso());
3997 else if (rmd->isForeign())
3998 md->makeForeign();
3999 else
4000 md->makeRelated();
4001 }
4002 }
4003 }
4004 }
4005 }
4006 }
4007 }
4008
4009 //----------------------------------------------------------------------
4010
4011 /*! make a dictionary of all template arguments of class cd
4012 * that are part of the base class name.
4013 * Example: A template class A with template arguments <R,S,T>
4014 * that inherits from B<T,T,S> will have T and S in the dictionary.
4015 */
getTemplateArgumentsInName(const ArgumentList & templateArguments,const std::string & name)4016 static TemplateNameMap getTemplateArgumentsInName(const ArgumentList &templateArguments,const std::string &name)
4017 {
4018 std::map<std::string,int> templateNames;
4019 int count=0;
4020 for (const Argument &arg : templateArguments)
4021 {
4022 static const reg::Ex re(R"(\a[\w:]*)");
4023 reg::Iterator it(name,re);
4024 reg::Iterator end;
4025 for (; it!=end ; ++it)
4026 {
4027 const auto &match = *it;
4028 std::string n = match.str();
4029 if (n==arg.name.str())
4030 {
4031 if (templateNames.find(n)==templateNames.end())
4032 {
4033 templateNames.insert(std::make_pair(n,count));
4034 }
4035 }
4036 }
4037 }
4038 return templateNames;
4039 }
4040
4041 /*! Searches a class from within \a context and \a cd and returns its
4042 * definition if found (otherwise 0 is returned).
4043 */
findClassWithinClassContext(Definition * context,ClassDef * cd,const QCString & name)4044 static ClassDef *findClassWithinClassContext(Definition *context,ClassDef *cd,const QCString &name)
4045 {
4046 ClassDef *result=0;
4047 if (cd==0)
4048 {
4049 return result;
4050 }
4051 FileDef *fd=cd->getFileDef();
4052 SymbolResolver resolver(fd);
4053 if (context && cd!=context)
4054 {
4055 result = const_cast<ClassDef*>(resolver.resolveClass(context,name,true,true));
4056 }
4057 //printf("1. result=%p\n",result);
4058 if (result==0)
4059 {
4060 result = const_cast<ClassDef*>(resolver.resolveClass(cd,name,true,true));
4061 }
4062 //printf("2. result=%p\n",result);
4063 if (result==0) // try direct class, needed for namespaced classes imported via tag files (see bug624095)
4064 {
4065 result = getClass(name);
4066 }
4067 //printf("3. result=%p\n",result);
4068 //printf("** Trying to find %s within context %s class %s result=%s lookup=%p\n",
4069 // qPrint(name),
4070 // context ? qPrint(context->name()) : "<none>",
4071 // cd ? qPrint(cd->name()) : "<none>",
4072 // result ? qPrint(result->name()) : "<none>",
4073 // Doxygen::classLinkedMap->find(name)
4074 // );
4075 return result;
4076 }
4077
4078
findUsedClassesForClass(const Entry * root,Definition * context,ClassDefMutable * masterCd,ClassDefMutable * instanceCd,bool isArtificial,const std::unique_ptr<ArgumentList> & actualArgs=std::unique_ptr<ArgumentList> (),const TemplateNameMap & templateNames=TemplateNameMap ())4079 static void findUsedClassesForClass(const Entry *root,
4080 Definition *context,
4081 ClassDefMutable *masterCd,
4082 ClassDefMutable *instanceCd,
4083 bool isArtificial,
4084 const std::unique_ptr<ArgumentList> &actualArgs = std::unique_ptr<ArgumentList>(),
4085 const TemplateNameMap &templateNames = TemplateNameMap()
4086 )
4087 {
4088 const ArgumentList &formalArgs = masterCd->templateArguments();
4089 for (auto &mni : masterCd->memberNameInfoLinkedMap())
4090 {
4091 for (auto &mi : *mni)
4092 {
4093 const MemberDef *md=mi->memberDef();
4094 if (md->isVariable() || md->isObjCProperty()) // for each member variable in this class
4095 {
4096 //printf(" Found variable %s in class %s\n",qPrint(md->name()),qPrint(masterCd->name()));
4097 QCString type = normalizeNonTemplateArgumentsInString(md->typeString(),masterCd,formalArgs);
4098 QCString typedefValue = resolveTypeDef(masterCd,type);
4099 if (!typedefValue.isEmpty())
4100 {
4101 type = typedefValue;
4102 }
4103 int pos=0;
4104 QCString usedClassName;
4105 QCString templSpec;
4106 bool found=FALSE;
4107 // the type can contain template variables, replace them if present
4108 type = substituteTemplateArgumentsInString(type,formalArgs,actualArgs);
4109
4110 //printf(" template substitution gives=%s\n",qPrint(type));
4111 while (!found && extractClassNameFromType(type,pos,usedClassName,templSpec,root->lang)!=-1)
4112 {
4113 // find the type (if any) that matches usedClassName
4114 SymbolResolver resolver(masterCd->getFileDef());
4115 const ClassDefMutable *typeCd = resolver.resolveClassMutable(masterCd,usedClassName,false,true);
4116 //printf("====> usedClassName=%s -> typeCd=%s\n",
4117 // qPrint(usedClassName),typeCd?qPrint(typeCd->name()):"<none>");
4118 if (typeCd)
4119 {
4120 usedClassName = typeCd->name();
4121 }
4122
4123 int sp=usedClassName.find('<');
4124 if (sp==-1) sp=0;
4125 int si=usedClassName.findRev("::",sp);
4126 if (si!=-1)
4127 {
4128 // replace any namespace aliases
4129 replaceNamespaceAliases(usedClassName,si);
4130 }
4131 // add any template arguments to the class
4132 QCString usedName = removeRedundantWhiteSpace(usedClassName+templSpec);
4133 //printf(" usedName=%s usedClassName=%s templSpec=%s\n",qPrint(usedName),qPrint(usedClassName),qPrint(templSpec));
4134
4135 TemplateNameMap formTemplateNames;
4136 if (templateNames.empty())
4137 {
4138 formTemplateNames = getTemplateArgumentsInName(formalArgs,usedName.str());
4139 }
4140 BaseInfo bi(usedName,Public,Normal);
4141 findClassRelation(root,context,instanceCd,&bi,formTemplateNames,TemplateInstances,isArtificial);
4142
4143 for (const Argument &arg : masterCd->templateArguments())
4144 {
4145 if (arg.name==usedName) // type is a template argument
4146 {
4147 Debug::print(Debug::Classes,0," New used class '%s'\n", qPrint(usedName));
4148
4149 ClassDef *usedCd = Doxygen::hiddenClassLinkedMap->find(usedName);
4150 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4151 if (usedCd==0)
4152 {
4153 usedCdm = toClassDefMutable(
4154 Doxygen::hiddenClassLinkedMap->add(usedName,
4155 std::unique_ptr<ClassDef>(
4156 createClassDef(
4157 masterCd->getDefFileName(),masterCd->getDefLine(),
4158 masterCd->getDefColumn(),
4159 usedName,
4160 ClassDef::Class))));
4161 if (usedCdm)
4162 {
4163 //printf("making %s a template argument!!!\n",qPrint(usedCd->name()));
4164 usedCdm->makeTemplateArgument();
4165 usedCdm->setUsedOnly(TRUE);
4166 usedCdm->setLanguage(masterCd->getLanguage());
4167 usedCd = usedCdm;
4168 }
4169 }
4170 if (usedCd)
4171 {
4172 found=TRUE;
4173 Debug::print(Debug::Classes,0," Adding used class '%s' (1)\n", qPrint(usedCd->name()));
4174 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4175 if (usedCdm)
4176 {
4177 if (isArtificial) usedCdm->setArtificial(TRUE);
4178 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4179 }
4180 }
4181 }
4182 }
4183
4184 if (!found)
4185 {
4186 ClassDef *usedCd=findClassWithinClassContext(context,masterCd,usedName);
4187 //printf("Looking for used class %s: result=%s master=%s\n",
4188 // qPrint(usedName),usedCd?qPrint(usedCd->name()):"<none>",masterCd?qPrint(masterCd->name()):"<none>");
4189
4190 if (usedCd)
4191 {
4192 found=TRUE;
4193 Debug::print(Debug::Classes,0," Adding used class '%s' (2)\n", qPrint(usedCd->name()));
4194 instanceCd->addUsedClass(usedCd,md->name(),md->protection()); // class exists
4195 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4196 if (usedCdm)
4197 {
4198 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4199 }
4200 }
4201 }
4202 }
4203 if (!found && !type.isEmpty()) // used class is not documented in any scope
4204 {
4205 ClassDef *usedCd = Doxygen::hiddenClassLinkedMap->find(type);
4206 ClassDefMutable *usedCdm = toClassDefMutable(usedCd);
4207 if (usedCd==0 && !Config_getBool(HIDE_UNDOC_RELATIONS))
4208 {
4209 if (type.right(2)=="(*" || type.right(2)=="(^") // type is a function pointer
4210 {
4211 type+=md->argsString();
4212 }
4213 Debug::print(Debug::Classes,0," New undocumented used class '%s'\n", qPrint(type));
4214 usedCdm = toClassDefMutable(
4215 Doxygen::hiddenClassLinkedMap->add(type,
4216 std::unique_ptr<ClassDef>(
4217 createClassDef(
4218 masterCd->getDefFileName(),masterCd->getDefLine(),
4219 masterCd->getDefColumn(),
4220 type,ClassDef::Class))));
4221 if (usedCdm)
4222 {
4223 usedCdm->setUsedOnly(TRUE);
4224 usedCdm->setLanguage(masterCd->getLanguage());
4225 usedCd = usedCdm;
4226 }
4227 }
4228 if (usedCd)
4229 {
4230 Debug::print(Debug::Classes,0," Adding used class '%s' (3)\n", qPrint(usedCd->name()));
4231 instanceCd->addUsedClass(usedCd,md->name(),md->protection());
4232 if (usedCdm)
4233 {
4234 if (isArtificial) usedCdm->setArtificial(TRUE);
4235 usedCdm->addUsedByClass(instanceCd,md->name(),md->protection());
4236 }
4237 }
4238 }
4239 }
4240 }
4241 }
4242 }
4243
findBaseClassesForClass(const Entry * root,Definition * context,ClassDefMutable * masterCd,ClassDefMutable * instanceCd,FindBaseClassRelation_Mode mode,bool isArtificial,const std::unique_ptr<ArgumentList> & actualArgs=std::unique_ptr<ArgumentList> (),const TemplateNameMap & templateNames=TemplateNameMap ())4244 static void findBaseClassesForClass(
4245 const Entry *root,
4246 Definition *context,
4247 ClassDefMutable *masterCd,
4248 ClassDefMutable *instanceCd,
4249 FindBaseClassRelation_Mode mode,
4250 bool isArtificial,
4251 const std::unique_ptr<ArgumentList> &actualArgs = std::unique_ptr<ArgumentList>(),
4252 const TemplateNameMap &templateNames=TemplateNameMap()
4253 )
4254 {
4255 // The base class could ofcouse also be a non-nested class
4256 const ArgumentList &formalArgs = masterCd->templateArguments();
4257 for (const BaseInfo &bi : root->extends)
4258 {
4259 //printf("masterCd=%s bi.name='%s' #actualArgs=%d\n",
4260 // qPrint(masterCd->localName()),qPrint(bi.name),actualArgs ? (int)actualArgs->size() : -1);
4261 TemplateNameMap formTemplateNames;
4262 if (templateNames.empty())
4263 {
4264 formTemplateNames = getTemplateArgumentsInName(formalArgs,bi.name.str());
4265 }
4266 BaseInfo tbi = bi;
4267 tbi.name = substituteTemplateArgumentsInString(bi.name,formalArgs,actualArgs);
4268 //printf("bi->name=%s tbi.name=%s\n",qPrint(bi->name),qPrint(tbi.name));
4269
4270 if (mode==DocumentedOnly)
4271 {
4272 // find a documented base class in the correct scope
4273 if (!findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,DocumentedOnly,isArtificial))
4274 {
4275 // 1.8.2: decided to show inheritance relations even if not documented,
4276 // we do make them artificial, so they do not appear in the index
4277 //if (!Config_getBool(HIDE_UNDOC_RELATIONS))
4278 bool b = Config_getBool(HIDE_UNDOC_RELATIONS) ? TRUE : isArtificial;
4279 //{
4280 // no documented base class -> try to find an undocumented one
4281 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,Undocumented,b);
4282 //}
4283 }
4284 }
4285 else if (mode==TemplateInstances)
4286 {
4287 findClassRelation(root,context,instanceCd,&tbi,formTemplateNames,TemplateInstances,isArtificial);
4288 }
4289 }
4290 }
4291
4292 //----------------------------------------------------------------------
4293
findTemplateInstanceRelation(const Entry * root,Definition * context,ClassDefMutable * templateClass,const QCString & templSpec,const TemplateNameMap & templateNames,bool isArtificial)4294 static void findTemplateInstanceRelation(const Entry *root,
4295 Definition *context,
4296 ClassDefMutable *templateClass,const QCString &templSpec,
4297 const TemplateNameMap &templateNames,
4298 bool isArtificial)
4299 {
4300 Debug::print(Debug::Classes,0," derived from template %s with parameters %s isArtificial=%d\n",
4301 qPrint(templateClass->name()),qPrint(templSpec),isArtificial);
4302 //printf("findTemplateInstanceRelation(base=%s templSpec=%s templateNames=",
4303 // qPrint(templateClass->name()),qPrint(templSpec));
4304 //for (const auto &kv : templNames)
4305 //{
4306 // printf("(%s->%d) ",kv.first.c_str(),kv.second);
4307 //}
4308 //printf("\n");
4309
4310 bool existingClass = (templSpec ==
4311 tempArgListToString(templateClass->templateArguments(),root->lang,false)
4312 );
4313 if (existingClass) return;
4314
4315 bool freshInstance=FALSE;
4316 ClassDefMutable *instanceClass = toClassDefMutable(
4317 templateClass->insertTemplateInstance(
4318 root->fileName,root->startLine,root->startColumn,templSpec,freshInstance));
4319 if (instanceClass)
4320 {
4321 instanceClass->setArtificial(TRUE);
4322 instanceClass->setLanguage(root->lang);
4323
4324 if (freshInstance)
4325 {
4326 Debug::print(Debug::Classes,0," found fresh instance '%s'!\n",qPrint(instanceClass->name()));
4327 instanceClass->setTemplateBaseClassNames(templateNames);
4328
4329 // search for new template instances caused by base classes of
4330 // instanceClass
4331 auto it_pair = g_classEntries.equal_range(templateClass->name().str());
4332 for (auto it=it_pair.first ; it!=it_pair.second ; ++it)
4333 {
4334 const Entry *templateRoot = it->second;
4335 Debug::print(Debug::Classes,0," template root found %s templSpec=%s!\n",
4336 qPrint(templateRoot->name),qPrint(templSpec));
4337 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(root->lang,templSpec);
4338 findBaseClassesForClass(templateRoot,context,templateClass,instanceClass,
4339 TemplateInstances,isArtificial,templArgs,templateNames);
4340
4341 findUsedClassesForClass(templateRoot,context,templateClass,instanceClass,
4342 isArtificial,templArgs,templateNames);
4343 }
4344
4345 //Debug::print(Debug::Classes,0," Template instance %s : \n",qPrint(instanceClass->name()));
4346 //ArgumentList *tl = templateClass->templateArguments();
4347 }
4348 else
4349 {
4350 Debug::print(Debug::Classes,0," instance already exists!\n");
4351 }
4352 }
4353 }
4354
isRecursiveBaseClass(const QCString & scope,const QCString & name)4355 static bool isRecursiveBaseClass(const QCString &scope,const QCString &name)
4356 {
4357 QCString n=name;
4358 int index=n.find('<');
4359 if (index!=-1)
4360 {
4361 n=n.left(index);
4362 }
4363 bool result = rightScopeMatch(scope,n);
4364 return result;
4365 }
4366
4367 /*! Searches for the end of a template in prototype \a s starting from
4368 * character position \a startPos. If the end was found the position
4369 * of the closing \> is returned, otherwise -1 is returned.
4370 *
4371 * Handles exotic cases such as
4372 * \code
4373 * Class<(id<0)>
4374 * Class<bits<<2>
4375 * Class<"<">
4376 * Class<'<'>
4377 * Class<(")<")>
4378 * \endcode
4379 */
findEndOfTemplate(const QCString & s,int startPos)4380 static int findEndOfTemplate(const QCString &s,int startPos)
4381 {
4382 // locate end of template
4383 int e=startPos;
4384 int brCount=1;
4385 int roundCount=0;
4386 int len = s.length();
4387 bool insideString=FALSE;
4388 bool insideChar=FALSE;
4389 char pc = 0;
4390 while (e<len && brCount!=0)
4391 {
4392 char c=s.at(e);
4393 switch(c)
4394 {
4395 case '<':
4396 if (!insideString && !insideChar)
4397 {
4398 if (e<len-1 && s.at(e+1)=='<')
4399 e++;
4400 else if (roundCount==0)
4401 brCount++;
4402 }
4403 break;
4404 case '>':
4405 if (!insideString && !insideChar)
4406 {
4407 if (e<len-1 && s.at(e+1)=='>')
4408 e++;
4409 else if (roundCount==0)
4410 brCount--;
4411 }
4412 break;
4413 case '(':
4414 if (!insideString && !insideChar)
4415 roundCount++;
4416 break;
4417 case ')':
4418 if (!insideString && !insideChar)
4419 roundCount--;
4420 break;
4421 case '"':
4422 if (!insideChar)
4423 {
4424 if (insideString && pc!='\\')
4425 insideString=FALSE;
4426 else
4427 insideString=TRUE;
4428 }
4429 break;
4430 case '\'':
4431 if (!insideString)
4432 {
4433 if (insideChar && pc!='\\')
4434 insideChar=FALSE;
4435 else
4436 insideChar=TRUE;
4437 }
4438 break;
4439 }
4440 pc = c;
4441 e++;
4442 }
4443 return brCount==0 ? e : -1;
4444 }
4445
findTemplateSpecializationPosition(const QCString & name)4446 static int findTemplateSpecializationPosition(const QCString &name)
4447 {
4448 if (name.isEmpty()) return 0;
4449 int l = static_cast<int>(name.length());
4450 if (name[l-1]=='>') // search backward to find the matching <, allowing nested <...> and strings.
4451 {
4452 int count=1;
4453 int i=l-2;
4454 char insideQuote=0;
4455 while (count>0 && i>=0)
4456 {
4457 char c = name[i--];
4458 switch (c)
4459 {
4460 case '>': if (!insideQuote) count++; break;
4461 case '<': if (!insideQuote) count--; break;
4462 case '\'': if (!insideQuote) insideQuote=c;
4463 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
4464 break;
4465 case '"': if (!insideQuote) insideQuote=c;
4466 else if (insideQuote==c && (i<0 || name[i]!='\\')) insideQuote=0;
4467 break;
4468 default: break;
4469 }
4470 }
4471 if (i>=0) l=i+1;
4472 }
4473 return l;
4474 }
4475
findClassRelation(const Entry * root,Definition * context,ClassDefMutable * cd,const BaseInfo * bi,const TemplateNameMap & templateNames,FindBaseClassRelation_Mode mode,bool isArtificial)4476 static bool findClassRelation(
4477 const Entry *root,
4478 Definition *context,
4479 ClassDefMutable *cd,
4480 const BaseInfo *bi,
4481 const TemplateNameMap &templateNames,
4482 FindBaseClassRelation_Mode mode,
4483 bool isArtificial
4484 )
4485 {
4486 //printf("findClassRelation(class=%s base=%s templateNames=",
4487 // qPrint(cd->name()),qPrint(bi->name));
4488 //for (const auto &kv : templateNames)
4489 //{
4490 // printf("(%s->%d) ",kv.first.c_str(),kv.second);
4491 //}
4492 //printf("\n");
4493
4494 QCString biName=bi->name;
4495 bool explicitGlobalScope=FALSE;
4496 //printf("findClassRelation: biName='%s'\n",qPrint(biName));
4497 if (biName.left(2)=="::") // explicit global scope
4498 {
4499 biName=biName.right(biName.length()-2);
4500 explicitGlobalScope=TRUE;
4501 }
4502
4503 Entry *parentNode=root->parent();
4504 bool lastParent=FALSE;
4505 do // for each parent scope, starting with the largest scope
4506 // (in case of nested classes)
4507 {
4508 QCString scopeName= parentNode ? parentNode->name : QCString();
4509 int scopeOffset=explicitGlobalScope ? 0 : scopeName.length();
4510 do // try all parent scope prefixes, starting with the largest scope
4511 {
4512 //printf("scopePrefix='%s' biName='%s'\n",
4513 // qPrint(scopeName.left(scopeOffset)),qPrint(biName));
4514
4515 QCString baseClassName=biName;
4516 if (scopeOffset>0)
4517 {
4518 baseClassName.prepend(scopeName.left(scopeOffset)+"::");
4519 }
4520 //QCString stripped;
4521 //baseClassName=stripTemplateSpecifiersFromScope
4522 // (removeRedundantWhiteSpace(baseClassName),TRUE,
4523 // &stripped);
4524 SymbolResolver resolver(cd->getFileDef());
4525 ClassDefMutable *baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
4526 baseClassName,
4527 mode==Undocumented,
4528 true
4529 );
4530 const MemberDef *baseClassTypeDef = resolver.getTypedef();
4531 QCString templSpec = resolver.getTemplateSpec();
4532 //printf("baseClassName=%s baseClass=%p cd=%p explicitGlobalScope=%d\n",
4533 // qPrint(baseClassName),baseClass,cd,explicitGlobalScope);
4534 //printf(" scope='%s' baseClassName='%s' baseClass=%s templSpec=%s\n",
4535 // cd ? qPrint(cd->name()):"<none>",
4536 // qPrint(baseClassName),
4537 // baseClass?qPrint(baseClass->name()):"<none>",
4538 // qPrint(templSpec)
4539 // );
4540 //if (baseClassName.left(root->name.length())!=root->name ||
4541 // baseClassName.at(root->name.length())!='<'
4542 // ) // Check for base class with the same name.
4543 // // If found then look in the outer scope for a match
4544 // // and prevent recursion.
4545 if (!isRecursiveBaseClass(root->name,baseClassName)
4546 || explicitGlobalScope
4547 // sadly isRecursiveBaseClass always true for UNO IDL ifc/svc members
4548 // (i.e. this is needed for addInterfaceOrServiceToServiceOrSingleton)
4549 || (root->lang==SrcLangExt_IDL &&
4550 (root->section==Entry::EXPORTED_INTERFACE_SEC ||
4551 root->section==Entry::INCLUDED_SERVICE_SEC)))
4552 {
4553 Debug::print(
4554 Debug::Classes,0," class relation %s inherited/used by %s found (%s and %s) templSpec='%s'\n",
4555 qPrint(baseClassName),
4556 qPrint(root->name),
4557 (bi->prot==Private)?"private":((bi->prot==Protected)?"protected":"public"),
4558 (bi->virt==Normal)?"normal":"virtual",
4559 qPrint(templSpec)
4560 );
4561
4562 int i=findTemplateSpecializationPosition(baseClassName);
4563 int si=baseClassName.findRev("::",i);
4564 if (si==-1) si=0;
4565 if (baseClass==0 && static_cast<uint>(i)!=baseClassName.length())
4566 // base class has template specifiers
4567 {
4568 // TODO: here we should try to find the correct template specialization
4569 // but for now, we only look for the unspecialized base class.
4570 int e=findEndOfTemplate(baseClassName,i+1);
4571 //printf("baseClass==0 i=%d e=%d\n",i,e);
4572 if (e!=-1) // end of template was found at e
4573 {
4574 templSpec = removeRedundantWhiteSpace(baseClassName.mid(i,e-i));
4575 baseClassName = baseClassName.left(i)+baseClassName.right(baseClassName.length()-e);
4576 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
4577 baseClassName,
4578 mode==Undocumented,
4579 true
4580 );
4581 baseClassTypeDef = resolver.getTypedef();
4582 //printf("baseClass=%p -> baseClass=%s templSpec=%s\n",
4583 // baseClass,qPrint(baseClassName),qPrint(templSpec));
4584 }
4585 }
4586 else if (baseClass && !templSpec.isEmpty()) // we have a known class, but also
4587 // know it is a template, so see if
4588 // we can also link to the explicit
4589 // instance (for instance if a class
4590 // derived from a template argument)
4591 {
4592 //printf("baseClass=%s templSpec=%s\n",qPrint(baseClass->name()),qPrint(templSpec));
4593 ClassDefMutable *templClass=getClassMutable(baseClass->name()+templSpec);
4594 if (templClass)
4595 {
4596 // use the template instance instead of the template base.
4597 baseClass = templClass;
4598 templSpec.resize(0);
4599 }
4600 }
4601
4602 //printf("cd=%p baseClass=%p\n",cd,baseClass);
4603 bool found=baseClass!=0 && (baseClass!=cd || mode==TemplateInstances);
4604 //printf("1. found=%d\n",found);
4605 if (!found && si!=-1)
4606 {
4607 // replace any namespace aliases
4608 replaceNamespaceAliases(baseClassName,si);
4609 baseClass = resolver.resolveClassMutable(explicitGlobalScope ? Doxygen::globalScope : context,
4610 baseClassName,
4611 mode==Undocumented,
4612 true
4613 );
4614 baseClassTypeDef = resolver.getTypedef();
4615 QCString tmpTemplSpec = resolver.getTemplateSpec();
4616 found=baseClass!=0 && baseClass!=cd;
4617 if (found) templSpec = tmpTemplSpec;
4618 }
4619 //printf("2. found=%d\n",found);
4620
4621 if (!found)
4622 {
4623 baseClass=toClassDefMutable(findClassWithinClassContext(context,cd,baseClassName));
4624 //printf("findClassWithinClassContext(%s,%s)=%p\n",
4625 // qPrint(cd->name()),qPrint(baseClassName),baseClass);
4626 found = baseClass!=0 && baseClass!=cd;
4627
4628 }
4629 //printf("3. found=%d\n",found);
4630 if (!found)
4631 {
4632 // for PHP the "use A\B as C" construct map class C to A::B, so we lookup
4633 // the class name also in the alias mapping.
4634 auto it = Doxygen::namespaceAliasMap.find(baseClassName.str());
4635 if (it!=Doxygen::namespaceAliasMap.end()) // see if it is indeed a class.
4636 {
4637 baseClass=getClassMutable(it->second.c_str());
4638 found = baseClass!=0 && baseClass!=cd;
4639 }
4640 }
4641 bool isATemplateArgument = templateNames.find(biName.str())!=templateNames.end();
4642 // make templSpec canonical
4643 // warning: the following line doesn't work for Mixin classes (see bug 560623)
4644 // templSpec = getCanonicalTemplateSpec(cd, cd->getFileDef(), templSpec);
4645
4646 //printf("4. found=%d\n",found);
4647 if (found)
4648 {
4649 Debug::print(Debug::Classes,0," Documented base class '%s' templSpec=%s\n",qPrint(biName),qPrint(templSpec));
4650 // add base class to this class
4651
4652 // if templSpec is not empty then we should "instantiate"
4653 // the template baseClass. A new ClassDef should be created
4654 // to represent the instance. To be able to add the (instantiated)
4655 // members and documentation of a template class
4656 // (inserted in that template class at a later stage),
4657 // the template should know about its instances.
4658 // the instantiation process, should be done in a recursive way,
4659 // since instantiating a template may introduce new inheritance
4660 // relations.
4661 if (!templSpec.isEmpty() && mode==TemplateInstances)
4662 {
4663 // if baseClass is actually a typedef then we should not
4664 // instantiate it, since typedefs are in a different namespace
4665 // see bug531637 for an example where this would otherwise hang
4666 // doxygen
4667 if (baseClassTypeDef==0)
4668 {
4669 //printf(" => findTemplateInstanceRelation: %p\n",baseClassTypeDef);
4670 findTemplateInstanceRelation(root,context,baseClass,templSpec,templateNames,baseClass->isArtificial());
4671 }
4672 }
4673 else if (mode==DocumentedOnly || mode==Undocumented)
4674 {
4675 //printf(" => insert base class\n");
4676 QCString usedName;
4677 if (baseClassTypeDef || cd->isCSharp())
4678 {
4679 usedName=biName;
4680 //printf("***** usedName=%s templSpec=%s\n",qPrint(usedName),qPrint(templSpec));
4681 }
4682 Protection prot = bi->prot;
4683 if (Config_getBool(SIP_SUPPORT)) prot=Public;
4684 if (!cd->isSubClass(baseClass)) // check for recursion, see bug690787
4685 {
4686 cd->insertBaseClass(baseClass,usedName,prot,bi->virt,templSpec);
4687 // add this class as super class to the base class
4688 baseClass->insertSubClass(cd,prot,bi->virt,templSpec);
4689 }
4690 else
4691 {
4692 warn(root->fileName,root->startLine,
4693 "Detected potential recursive class relation "
4694 "between class %s and base class %s!",
4695 qPrint(cd->name()),qPrint(baseClass->name())
4696 );
4697 }
4698 }
4699 return TRUE;
4700 }
4701 else if (mode==Undocumented && (scopeOffset==0 || isATemplateArgument))
4702 {
4703 Debug::print(Debug::Classes,0,
4704 " New undocumented base class '%s' baseClassName=%s templSpec=%s isArtificial=%d\n",
4705 qPrint(biName),qPrint(baseClassName),qPrint(templSpec),isArtificial
4706 );
4707 baseClass=0;
4708 if (isATemplateArgument)
4709 {
4710 baseClass = toClassDefMutable(Doxygen::hiddenClassLinkedMap->find(baseClassName));
4711 if (baseClass==0) // not found (or alias)
4712 {
4713 baseClass= toClassDefMutable(
4714 Doxygen::hiddenClassLinkedMap->add(baseClassName,
4715 std::unique_ptr<ClassDef>(
4716 createClassDef(root->fileName,root->startLine,root->startColumn,
4717 baseClassName,
4718 ClassDef::Class))));
4719 if (baseClass) // really added (not alias)
4720 {
4721 if (isArtificial) baseClass->setArtificial(TRUE);
4722 baseClass->setLanguage(root->lang);
4723 }
4724 }
4725 }
4726 else
4727 {
4728 baseClass = toClassDefMutable(Doxygen::classLinkedMap->find(baseClassName));
4729 //printf("*** classDDict->find(%s)=%p biName=%s templSpec=%s\n",
4730 // qPrint(baseClassName),baseClass,qPrint(biName),qPrint(templSpec));
4731 if (baseClass==0) // not found (or alias)
4732 {
4733 baseClass = toClassDefMutable(
4734 Doxygen::classLinkedMap->add(baseClassName,
4735 std::unique_ptr<ClassDef>(
4736 createClassDef(root->fileName,root->startLine,root->startColumn,
4737 baseClassName,
4738 ClassDef::Class))));
4739 if (baseClass) // really added (not alias)
4740 {
4741 if (isArtificial) baseClass->setArtificial(TRUE);
4742 baseClass->setLanguage(root->lang);
4743 si = baseClassName.findRev("::");
4744 if (si!=-1) // class is nested
4745 {
4746 Definition *sd = findScopeFromQualifiedName(Doxygen::globalScope,baseClassName.left(si),0,root->tagInfo());
4747 if (sd==0 || sd==Doxygen::globalScope) // outer scope not found
4748 {
4749 baseClass->setArtificial(TRUE); // see bug678139
4750 }
4751 }
4752 }
4753 }
4754 }
4755 if (baseClass)
4756 {
4757 if (biName.right(2)=="-p")
4758 {
4759 biName="<"+biName.left(biName.length()-2)+">";
4760 }
4761 // add base class to this class
4762 cd->insertBaseClass(baseClass,biName,bi->prot,bi->virt,templSpec);
4763 // add this class as super class to the base class
4764 baseClass->insertSubClass(cd,bi->prot,bi->virt,templSpec);
4765 // the undocumented base was found in this file
4766 baseClass->insertUsedFile(root->fileDef());
4767
4768 Definition *scope = buildScopeFromQualifiedName(baseClass->name(),root->lang,0);
4769 if (scope!=baseClass)
4770 {
4771 baseClass->setOuterScope(scope);
4772 }
4773
4774 if (baseClassName.right(2)=="-p")
4775 {
4776 baseClass->setCompoundType(ClassDef::Protocol);
4777 }
4778 return TRUE;
4779 }
4780 else
4781 {
4782 Debug::print(Debug::Classes,0," Base class '%s' not created (alias?)\n",qPrint(biName));
4783 }
4784 }
4785 else
4786 {
4787 Debug::print(Debug::Classes,0," Base class '%s' not found\n",qPrint(biName));
4788 }
4789 }
4790 else
4791 {
4792 if (mode!=TemplateInstances)
4793 {
4794 warn(root->fileName,root->startLine,
4795 "Detected potential recursive class relation "
4796 "between class %s and base class %s!\n",
4797 qPrint(root->name),qPrint(baseClassName)
4798 );
4799 }
4800 // for mode==TemplateInstance this case is quite common and
4801 // indicates a relation between a template class and a template
4802 // instance with the same name.
4803 }
4804 if (scopeOffset==0)
4805 {
4806 scopeOffset=-1;
4807 }
4808 else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1)
4809 {
4810 scopeOffset=0;
4811 }
4812 //printf("new scopeOffset='%d'",scopeOffset);
4813 } while (scopeOffset>=0);
4814
4815 if (parentNode==0)
4816 {
4817 lastParent=TRUE;
4818 }
4819 else
4820 {
4821 parentNode=parentNode->parent();
4822 }
4823 } while (lastParent);
4824
4825 return FALSE;
4826 }
4827
4828 //----------------------------------------------------------------------
4829 // Computes the base and super classes for each class in the tree
4830
isClassSection(const Entry * root)4831 static bool isClassSection(const Entry *root)
4832 {
4833 if ( !root->name.isEmpty() )
4834 {
4835 if (root->section & Entry::COMPOUND_MASK)
4836 // is it a compound (class, struct, union, interface ...)
4837 {
4838 return TRUE;
4839 }
4840 else if (root->section & Entry::COMPOUNDDOC_MASK)
4841 // is it a documentation block with inheritance info.
4842 {
4843 bool hasExtends = !root->extends.empty();
4844 if (hasExtends) return TRUE;
4845 }
4846 }
4847 return FALSE;
4848 }
4849
4850
4851 /*! Builds a dictionary of all entry nodes in the tree starting with \a root
4852 */
findClassEntries(const Entry * root)4853 static void findClassEntries(const Entry *root)
4854 {
4855 if (isClassSection(root))
4856 {
4857 g_classEntries.insert({root->name.str(),root});
4858 }
4859 for (const auto &e : root->children()) findClassEntries(e.get());
4860 }
4861
extractClassName(const Entry * root)4862 static QCString extractClassName(const Entry *root)
4863 {
4864 // strip any anonymous scopes first
4865 QCString bName=stripAnonymousNamespaceScope(root->name);
4866 bName=stripTemplateSpecifiersFromScope(bName);
4867 int i;
4868 if ((root->lang==SrcLangExt_CSharp || root->lang==SrcLangExt_Java) &&
4869 (i=bName.find('<'))!=-1)
4870 {
4871 // a Java/C# generic class looks like a C++ specialization, so we need to strip the
4872 // template part before looking for matches
4873 bName=bName.left(i);
4874 }
4875 return bName;
4876 }
4877
4878 /*! Using the dictionary build by findClassEntries(), this
4879 * function will look for additional template specialization that
4880 * exists as inheritance relations only. These instances will be
4881 * added to the template they are derived from.
4882 */
findInheritedTemplateInstances()4883 static void findInheritedTemplateInstances()
4884 {
4885 ClassDefSet visitedClasses;
4886 for (const auto &kv : g_classEntries)
4887 {
4888 const Entry *root = kv.second;
4889 ClassDef *cd;
4890 QCString bName = extractClassName(root);
4891 Debug::print(Debug::Classes,0," Inheritance: Class %s : \n",qPrint(bName));
4892 if ((cd=getClass(bName)))
4893 {
4894 ClassDefMutable *cdm = toClassDefMutable(cd);
4895 if (cdm)
4896 {
4897 //printf("Class %s %zu\n",qPrint(cd->name()),root->extends.size());
4898 findBaseClassesForClass(root,cd,cdm,cdm,TemplateInstances,FALSE);
4899 }
4900 }
4901 }
4902 }
4903
findUsedTemplateInstances()4904 static void findUsedTemplateInstances()
4905 {
4906 for (const auto &kv : g_classEntries)
4907 {
4908 const Entry *root = kv.second;
4909 ClassDef *cd;
4910 QCString bName = extractClassName(root);
4911 Debug::print(Debug::Classes,0," Usage: Class %s : \n",qPrint(bName));
4912 if ((cd=getClass(bName)))
4913 {
4914 ClassDefMutable *cdm = toClassDefMutable(cd);
4915 if (cdm)
4916 {
4917 findUsedClassesForClass(root,cd,cdm,cdm,TRUE);
4918 cdm->addTypeConstraints();
4919 }
4920 }
4921 }
4922 }
4923
computeClassRelations()4924 static void computeClassRelations()
4925 {
4926 for (const auto &kv : g_classEntries)
4927 {
4928 const Entry *root = kv.second;
4929 ClassDefMutable *cd;
4930
4931 QCString bName = extractClassName(root);
4932 Debug::print(Debug::Classes,0," Relations: Class %s : \n",qPrint(bName));
4933 if ((cd=getClassMutable(bName)))
4934 {
4935 findBaseClassesForClass(root,cd,cd,cd,DocumentedOnly,FALSE);
4936 }
4937 size_t numMembers = cd ? cd->memberNameInfoLinkedMap().size() : 0;
4938 if ((cd==0 || (!cd->hasDocumentation() && !cd->isReference())) && numMembers>0 &&
4939 bName.right(2)!="::")
4940 {
4941 if (!root->name.isEmpty() && root->name.find('@')==-1 && // normal name
4942 (guessSection(root->fileName)==Entry::HEADER_SEC ||
4943 Config_getBool(EXTRACT_LOCAL_CLASSES)) && // not defined in source file
4944 protectionLevelVisible(root->protection) && // hidden by protection
4945 !Config_getBool(HIDE_UNDOC_CLASSES) // undocumented class are visible
4946 )
4947 warn_undoc(
4948 root->fileName,root->startLine,
4949 "Compound %s is not documented.",
4950 qPrint(root->name)
4951 );
4952 }
4953 }
4954 }
4955
computeTemplateClassRelations()4956 static void computeTemplateClassRelations()
4957 {
4958 for (const auto &kv : g_classEntries)
4959 {
4960 const Entry *root = kv.second;
4961 QCString bName=stripAnonymousNamespaceScope(root->name);
4962 bName=stripTemplateSpecifiersFromScope(bName);
4963 ClassDefMutable *cd=getClassMutable(bName);
4964 // strip any anonymous scopes first
4965 if (cd && !cd->getTemplateInstances().empty())
4966 {
4967 Debug::print(Debug::Classes,0," Template class %s : \n",qPrint(cd->name()));
4968 for (const auto &ti : cd->getTemplateInstances()) // for each template instance
4969 {
4970 ClassDefMutable *tcd=toClassDefMutable(ti.classDef);
4971 if (tcd)
4972 {
4973 Debug::print(Debug::Classes,0," Template instance %s : \n",qPrint(tcd->name()));
4974 QCString templSpec = ti.templSpec;
4975 std::unique_ptr<ArgumentList> templArgs = stringToArgumentList(tcd->getLanguage(),templSpec);
4976 for (const BaseInfo &bi : root->extends)
4977 {
4978 // check if the base class is a template argument
4979 BaseInfo tbi = bi;
4980 const ArgumentList &tl = cd->templateArguments();
4981 if (!tl.empty())
4982 {
4983 TemplateNameMap baseClassNames = tcd->getTemplateBaseClassNames();
4984 TemplateNameMap templateNames = getTemplateArgumentsInName(tl,bi.name.str());
4985 // for each template name that we inherit from we need to
4986 // substitute the formal with the actual arguments
4987 TemplateNameMap actualTemplateNames;
4988 for (const auto &tn_kv : templateNames)
4989 {
4990 int templIndex = tn_kv.second;
4991 Argument actArg;
4992 bool hasActArg=FALSE;
4993 if (templIndex<(int)templArgs->size())
4994 {
4995 actArg=templArgs->at(templIndex);
4996 hasActArg=TRUE;
4997 }
4998 if (hasActArg &&
4999 baseClassNames.find(actArg.type.str())!=baseClassNames.end() &&
5000 actualTemplateNames.find(actArg.type.str())==actualTemplateNames.end()
5001 )
5002 {
5003 actualTemplateNames.insert(std::make_pair(actArg.type.str(),templIndex));
5004 }
5005 }
5006
5007 tbi.name = substituteTemplateArgumentsInString(bi.name,tl,templArgs);
5008 // find a documented base class in the correct scope
5009 if (!findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,DocumentedOnly,FALSE))
5010 {
5011 // no documented base class -> try to find an undocumented one
5012 findClassRelation(root,cd,tcd,&tbi,actualTemplateNames,Undocumented,TRUE);
5013 }
5014 }
5015 }
5016 }
5017 }
5018 }
5019 }
5020 }
5021
5022 //-----------------------------------------------------------------------
5023 // compute the references (anchors in HTML) for each function in the file
5024
computeMemberReferences()5025 static void computeMemberReferences()
5026 {
5027 for (const auto &cd : *Doxygen::classLinkedMap)
5028 {
5029 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5030 if (cdm)
5031 {
5032 cdm->computeAnchors();
5033 }
5034 }
5035 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5036 {
5037 for (const auto &fd : *fn)
5038 {
5039 fd->computeAnchors();
5040 }
5041 }
5042 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5043 {
5044 NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
5045 if (ndm)
5046 {
5047 ndm->computeAnchors();
5048 }
5049 }
5050 for (const auto &gd : *Doxygen::groupLinkedMap)
5051 {
5052 gd->computeAnchors();
5053 }
5054 }
5055
5056 //----------------------------------------------------------------------
5057
addListReferences()5058 static void addListReferences()
5059 {
5060 for (const auto &cd : *Doxygen::classLinkedMap)
5061 {
5062 ClassDefMutable *cdm = toClassDefMutable(cd.get());
5063 if (cdm)
5064 {
5065 cdm->addListReferences();
5066 }
5067 }
5068
5069 for (const auto &fn : *Doxygen::inputNameLinkedMap)
5070 {
5071 for (const auto &fd : *fn)
5072 {
5073 fd->addListReferences();
5074 }
5075 }
5076
5077 for (const auto &nd : *Doxygen::namespaceLinkedMap)
5078 {
5079 NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
5080 if (ndm)
5081 {
5082 ndm->addListReferences();
5083 }
5084 }
5085
5086 for (const auto &gd : *Doxygen::groupLinkedMap)
5087 {
5088 gd->addListReferences();
5089 }
5090
5091 for (const auto &pd : *Doxygen::pageLinkedMap)
5092 {
5093 QCString name = pd->getOutputFileBase();
5094 if (pd->getGroupDef())
5095 {
5096 name = pd->getGroupDef()->getOutputFileBase();
5097 }
5098 {
5099 const RefItemVector &xrefItems = pd->xrefListItems();
5100 addRefItem(xrefItems,
5101 name,
5102 theTranslator->trPage(TRUE,TRUE),
5103 name,pd->title(),QCString(),0);
5104 }
5105 }
5106
5107 for (const auto &dd : *Doxygen::dirLinkedMap)
5108 {
5109 QCString name = dd->getOutputFileBase();
5110 //if (dd->getGroupDef())
5111 //{
5112 // name = dd->getGroupDef()->getOutputFileBase();
5113 //}
5114 const RefItemVector &xrefItems = dd->xrefListItems();
5115 addRefItem(xrefItems,
5116 name,
5117 theTranslator->trDir(TRUE,TRUE),
5118 name,dd->displayName(),QCString(),0);
5119 }
5120 }
5121
5122 //----------------------------------------------------------------------
5123
generateXRefPages()5124 static void generateXRefPages()
5125 {
5126 for (RefListManager::Ptr &rl : RefListManager::instance())
5127 {
5128 rl->generatePage();
5129 }
5130 }
5131
5132 //----------------------------------------------------------------------
5133 // Copy the documentation in entry 'root' to member definition 'md' and
5134 // set the function declaration of the member to 'funcDecl'. If the boolean
5135 // over_load is set the standard overload text is added.
5136
addMemberDocs(const Entry * root,MemberDefMutable * md,const QCString & funcDecl,const ArgumentList * al,bool over_load,uint64 spec)5137 static void addMemberDocs(const Entry *root,
5138 MemberDefMutable *md, const QCString &funcDecl,
5139 const ArgumentList *al,
5140 bool over_load,
5141 uint64 spec
5142 )
5143 {
5144 if (md==0) return;
5145 //printf("addMemberDocs: '%s'::'%s' '%s' funcDecl='%s' mSpec=%lld\n",
5146 // qPrint(root->parent()->name),qPrint(md->name()),qPrint(md->argsString()),qPrint(funcDecl),spec);
5147 QCString fDecl=funcDecl;
5148 // strip extern specifier
5149 fDecl.stripPrefix("extern ");
5150 md->setDefinition(fDecl);
5151 md->enableCallGraph(root->callGraph);
5152 md->enableCallerGraph(root->callerGraph);
5153 md->enableReferencedByRelation(root->referencedByRelation);
5154 md->enableReferencesRelation(root->referencesRelation);
5155 ClassDefMutable *cd=md->getClassDefMutable();
5156 const NamespaceDef *nd=md->getNamespaceDef();
5157 QCString fullName;
5158 if (cd)
5159 fullName = cd->name();
5160 else if (nd)
5161 fullName = nd->name();
5162
5163 if (!fullName.isEmpty()) fullName+="::";
5164 fullName+=md->name();
5165 FileDef *rfd=root->fileDef();
5166
5167 // TODO determine scope based on root not md
5168 Definition *rscope = md->getOuterScope();
5169
5170 const ArgumentList &mdAl = md->argumentList();
5171 if (al)
5172 {
5173 ArgumentList mergedAl = *al;
5174 //printf("merging arguments (1) docs=%d\n",root->doc.isEmpty());
5175 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedAl,!root->doc.isEmpty());
5176 }
5177 else
5178 {
5179 if (
5180 matchArguments2( md->getOuterScope(), md->getFileDef(),const_cast<ArgumentList*>(&mdAl),
5181 rscope,rfd,&root->argList,
5182 TRUE
5183 )
5184 )
5185 {
5186 //printf("merging arguments (2)\n");
5187 ArgumentList mergedArgList = root->argList;
5188 mergeArguments(const_cast<ArgumentList&>(mdAl),mergedArgList,!root->doc.isEmpty());
5189 }
5190 }
5191 if (over_load) // the \overload keyword was used
5192 {
5193 QCString doc=getOverloadDocs();
5194 if (!root->doc.isEmpty())
5195 {
5196 doc+="<p>";
5197 doc+=root->doc;
5198 }
5199 md->setDocumentation(doc,root->docFile,root->docLine);
5200 md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
5201 md->setDocsForDefinition(!root->proto);
5202 }
5203 else
5204 {
5205 //printf("overwrite!\n");
5206 md->setDocumentation(root->doc,root->docFile,root->docLine);
5207 md->setDocsForDefinition(!root->proto);
5208
5209 //printf("overwrite!\n");
5210 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
5211
5212 if (
5213 (md->inbodyDocumentation().isEmpty() ||
5214 !root->parent()->name.isEmpty()
5215 ) && !root->inbodyDocs.isEmpty()
5216 )
5217 {
5218 md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
5219 }
5220 }
5221
5222 //printf("initializer: '%s'(isEmpty=%d) '%s'(isEmpty=%d)\n",
5223 // qPrint(md->initializer()),md->initializer().isEmpty(),
5224 // qPrint(root->initializer),root->initializer.isEmpty()
5225 // );
5226 std::string rootInit = root->initializer.str();
5227 if (md->initializer().isEmpty() && !rootInit.empty())
5228 {
5229 //printf("setInitializer\n");
5230 md->setInitializer(rootInit.c_str());
5231 }
5232 if (md->requiresClause().isEmpty() && !root->req.isEmpty())
5233 {
5234 md->setRequiresClause(root->req);
5235 }
5236
5237 md->setMaxInitLines(root->initLines);
5238
5239 if (rfd)
5240 {
5241 if ((md->getStartBodyLine()==-1 && root->bodyLine!=-1)
5242 )
5243 {
5244 //printf("Setting new body segment [%d,%d]\n",root->bodyLine,root->endBodyLine);
5245 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
5246 md->setBodyDef(rfd);
5247 }
5248
5249 md->setRefItems(root->sli);
5250 }
5251
5252 md->enableCallGraph(md->hasCallGraph() || root->callGraph);
5253 md->enableCallerGraph(md->hasCallerGraph() || root->callerGraph);
5254 md->enableReferencedByRelation(md->hasReferencedByRelation() || root->referencedByRelation);
5255 md->enableReferencesRelation(md->hasReferencesRelation() || root->referencesRelation);
5256
5257 md->mergeMemberSpecifiers(spec);
5258 md->addSectionsToDefinition(root->anchors);
5259 addMemberToGroups(root,md);
5260 if (cd) cd->insertUsedFile(rfd);
5261 //printf("root->mGrpId=%d\n",root->mGrpId);
5262 if (root->mGrpId!=-1)
5263 {
5264 if (md->getMemberGroupId()!=-1)
5265 {
5266 if (md->getMemberGroupId()!=root->mGrpId)
5267 {
5268 warn(
5269 root->fileName,root->startLine,
5270 "member %s belongs to two different groups. The second "
5271 "one found here will be ignored.",
5272 qPrint(md->name())
5273 );
5274 }
5275 }
5276 else // set group id
5277 {
5278 //printf("setMemberGroupId=%d md=%s\n",root->mGrpId,qPrint(md->name()));
5279 md->setMemberGroupId(root->mGrpId);
5280 }
5281 }
5282 }
5283
5284 //----------------------------------------------------------------------
5285 // find a class definition given the scope name and (optionally) a
5286 // template list specifier
5287
findClassDefinition(FileDef * fd,NamespaceDef * nd,const QCString & scopeName)5288 static const ClassDef *findClassDefinition(FileDef *fd,NamespaceDef *nd,
5289 const QCString &scopeName)
5290 {
5291 SymbolResolver resolver(fd);
5292 const ClassDef *tcd = resolver.resolveClass(nd,scopeName,true,true);
5293 return tcd;
5294 }
5295
5296
5297 //----------------------------------------------------------------------
5298 // Adds the documentation contained in 'root' to a global function
5299 // with name 'name' and argument list 'args' (for overloading) and
5300 // function declaration 'decl' to the corresponding member definition.
5301
findGlobalMember(const Entry * root,const QCString & namespaceName,const QCString & type,const QCString & name,const QCString & tempArg,const QCString &,const QCString & decl,uint64 spec)5302 static bool findGlobalMember(const Entry *root,
5303 const QCString &namespaceName,
5304 const QCString &type,
5305 const QCString &name,
5306 const QCString &tempArg,
5307 const QCString &,
5308 const QCString &decl,
5309 uint64 spec)
5310 {
5311 Debug::print(Debug::FindMembers,0,
5312 "2. findGlobalMember(namespace=%s,type=%s,name=%s,tempArg=%s,decl=%s)\n",
5313 qPrint(namespaceName),qPrint(type),qPrint(name),qPrint(tempArg),qPrint(decl));
5314 QCString n=name;
5315 if (n.isEmpty()) return FALSE;
5316 if (n.find("::")!=-1) return FALSE; // skip undefined class members
5317 MemberName *mn=Doxygen::functionNameLinkedMap->find(n+tempArg); // look in function dictionary
5318 if (mn==0)
5319 {
5320 mn=Doxygen::functionNameLinkedMap->find(n); // try without template arguments
5321 }
5322 if (mn) // function name defined
5323 {
5324 Debug::print(Debug::FindMembers,0,"3. Found symbol scope\n");
5325 //int count=0;
5326 bool found=FALSE;
5327 for (const auto &md : *mn)
5328 {
5329 const NamespaceDef *nd=0;
5330 if (md->isAlias() && md->getOuterScope() &&
5331 md->getOuterScope()->definitionType()==Definition::TypeNamespace)
5332 {
5333 nd = toNamespaceDef(md->getOuterScope());
5334 }
5335 else
5336 {
5337 nd = md->getNamespaceDef();
5338 }
5339
5340 // special case for strong enums
5341 int enumNamePos=0;
5342 if (nd && md->isEnumValue() && (enumNamePos=namespaceName.findRev("::"))!=-1)
5343 { // md part of a strong enum in a namespace?
5344 QCString enumName = namespaceName.mid(enumNamePos+2);
5345 if (namespaceName.left(enumNamePos)==nd->name())
5346 {
5347 MemberName *enumMn=Doxygen::functionNameLinkedMap->find(enumName);
5348 if (enumMn)
5349 {
5350 for (const auto &emd : *enumMn)
5351 {
5352 found = emd->isStrong() && md->getEnumScope()==emd.get();
5353 if (found)
5354 {
5355 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,0,FALSE,root->spec);
5356 break;
5357 }
5358 }
5359 }
5360 }
5361 if (found)
5362 {
5363 break;
5364 }
5365 }
5366 else if (nd==0 && md->isEnumValue()) // md part of global strong enum?
5367 {
5368 MemberName *enumMn=Doxygen::functionNameLinkedMap->find(namespaceName);
5369 if (enumMn)
5370 {
5371 for (const auto &emd : *enumMn)
5372 {
5373 found = emd->isStrong() && md->getEnumScope()==emd.get();
5374 if (found)
5375 {
5376 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,0,FALSE,root->spec);
5377 break;
5378 }
5379 }
5380 }
5381 }
5382
5383 const FileDef *fd=root->fileDef();
5384 //printf("File %s\n",fd ? qPrint(fd->name()) : "<none>");
5385 LinkedRefMap<const NamespaceDef> nl;
5386 if (fd)
5387 {
5388 nl = fd->getUsedNamespaces();
5389 }
5390 //printf("NamespaceList %p\n",nl);
5391
5392 // search in the list of namespaces that are imported via a
5393 // using declaration
5394 bool viaUsingDirective = nd && nl.find(nd->qualifiedName())!=0;
5395
5396 if ((namespaceName.isEmpty() && nd==0) || // not in a namespace
5397 (nd && nd->name()==namespaceName) || // or in the same namespace
5398 viaUsingDirective // member in 'using' namespace
5399 )
5400 {
5401 Debug::print(Debug::FindMembers,0,"4. Try to add member '%s' to scope '%s'\n",
5402 qPrint(md->name()),qPrint(namespaceName));
5403
5404 NamespaceDef *rnd = 0;
5405 if (!namespaceName.isEmpty()) rnd = Doxygen::namespaceLinkedMap->find(namespaceName);
5406
5407 const ArgumentList &mdAl = const_cast<const MemberDef *>(md.get())->argumentList();
5408 bool matching=
5409 (mdAl.empty() && root->argList.empty()) ||
5410 md->isVariable() || md->isTypedef() || /* in case of function pointers */
5411 matchArguments2(md->getOuterScope(),const_cast<const MemberDef *>(md.get())->getFileDef(),&mdAl,
5412 rnd ? rnd : Doxygen::globalScope,fd,&root->argList,
5413 FALSE);
5414
5415 // for template members we need to check if the number of
5416 // template arguments is the same, otherwise we are dealing with
5417 // different functions.
5418 if (matching && !root->tArgLists.empty())
5419 {
5420 const ArgumentList &mdTempl = md->templateArguments();
5421 if (root->tArgLists.back().size()!=mdTempl.size())
5422 {
5423 matching=FALSE;
5424 }
5425 }
5426
5427 //printf("%s<->%s\n",
5428 // qPrint(argListToString(md->argumentList())),
5429 // qPrint(argListToString(root->argList)));
5430
5431 // for static members we also check if the comment block was found in
5432 // the same file. This is needed because static members with the same
5433 // name can be in different files. Thus it would be wrong to just
5434 // put the comment block at the first syntactically matching member.
5435 if (matching && md->isStatic() &&
5436 md->getDefFileName()!=root->fileName &&
5437 mn->size()>1)
5438 {
5439 matching = FALSE;
5440 }
5441
5442 // for template member we also need to check the return type and requires
5443 if (!md->templateArguments().empty() && !root->tArgLists.empty())
5444 {
5445 //printf("Comparing return types '%s'<->'%s'\n",
5446 // md->typeString(),type);
5447 if (md->templateArguments().size()!=root->tArgLists.back().size() ||
5448 md->typeString()!=type ||
5449 md->requiresClause()!=root->req)
5450 {
5451 //printf(" ---> no matching\n");
5452 matching = FALSE;
5453 }
5454 }
5455
5456 if (matching) // add docs to the member
5457 {
5458 Debug::print(Debug::FindMembers,0,"5. Match found\n");
5459 addMemberDocs(root,toMemberDefMutable(md->resolveAlias()),decl,&root->argList,FALSE,root->spec);
5460 found=TRUE;
5461 break;
5462 }
5463 }
5464 }
5465 if (!found && root->relatesType != Duplicate && root->section==Entry::FUNCTION_SEC) // no match
5466 {
5467 QCString fullFuncDecl=decl;
5468 if (!root->argList.empty()) fullFuncDecl+=argListToString(root->argList,TRUE);
5469 QCString warnMsg =
5470 QCString("no matching file member found for \n")+substitute(fullFuncDecl,"%","%%");
5471 if (mn->size()>0)
5472 {
5473 warnMsg+="\nPossible candidates:\n";
5474 for (const auto &md : *mn)
5475 {
5476 warnMsg+=" '";
5477 warnMsg+=substitute(md->declaration(),"%","%%");
5478 warnMsg+="' at line "+QCString().setNum(md->getDefLine())+
5479 " of file "+md->getDefFileName()+"\n";
5480 }
5481 }
5482 warn(root->fileName,root->startLine, "%s", qPrint(warnMsg));
5483 }
5484 }
5485 else // got docs for an undefined member!
5486 {
5487 if (root->type!="friend class" &&
5488 root->type!="friend struct" &&
5489 root->type!="friend union" &&
5490 root->type!="friend" &&
5491 (!Config_getBool(TYPEDEF_HIDES_STRUCT) ||
5492 root->type.find("typedef ")==-1)
5493 )
5494 {
5495 warn(root->fileName,root->startLine,
5496 "documented symbol '%s' was not declared or defined.",qPrint(decl)
5497 );
5498 }
5499 }
5500 return TRUE;
5501 }
5502
isSpecialization(const ArgumentLists & srcTempArgLists,const ArgumentLists & dstTempArgLists)5503 static bool isSpecialization(
5504 const ArgumentLists &srcTempArgLists,
5505 const ArgumentLists &dstTempArgLists
5506 )
5507 {
5508 auto srcIt = srcTempArgLists.begin();
5509 auto dstIt = dstTempArgLists.begin();
5510 while (srcIt!=srcTempArgLists.end() && dstIt!=dstTempArgLists.end())
5511 {
5512 if ((*srcIt).size()!=(*dstIt).size()) return TRUE;
5513 ++srcIt;
5514 ++dstIt;
5515 }
5516 return FALSE;
5517 }
5518
scopeIsTemplate(const Definition * d)5519 static bool scopeIsTemplate(const Definition *d)
5520 {
5521 bool result=FALSE;
5522 if (d && d->definitionType()==Definition::TypeClass)
5523 {
5524 result = !(toClassDef(d))->templateArguments().empty() ||
5525 scopeIsTemplate(d->getOuterScope());
5526 }
5527 return result;
5528 }
5529
substituteTemplatesInString(const ArgumentLists & srcTempArgLists,const ArgumentLists & dstTempArgLists,const std::string & src)5530 static QCString substituteTemplatesInString(
5531 const ArgumentLists &srcTempArgLists,
5532 const ArgumentLists &dstTempArgLists,
5533 const std::string &src
5534 )
5535 {
5536 std::string dst;
5537 static const reg::Ex re(R"(\a\w*)");
5538 reg::Iterator it(src,re);
5539 reg::Iterator end;
5540 //printf("type=%s\n",qPrint(sa->type));
5541 size_t p=0;
5542 for (; it!=end ; ++it) // for each word in srcType
5543 {
5544 const auto &match = *it;
5545 size_t i = match.position();
5546 size_t l = match.length();
5547 bool found=FALSE;
5548 dst+=src.substr(p,i-p);
5549 std::string name=match.str();
5550
5551 auto srcIt = srcTempArgLists.begin();
5552 auto dstIt = dstTempArgLists.begin();
5553 while (srcIt!=srcTempArgLists.end() && !found)
5554 {
5555 const ArgumentList *tdAli = 0;
5556 std::vector<Argument>::const_iterator tdaIt;
5557 if (dstIt!=dstTempArgLists.end())
5558 {
5559 tdAli = &(*dstIt);
5560 tdaIt = tdAli->begin();
5561 ++dstIt;
5562 }
5563
5564 const ArgumentList &tsaLi = *srcIt;
5565 for (auto tsaIt = tsaLi.begin(); tsaIt!=tsaLi.end() && !found; ++tsaIt)
5566 {
5567 Argument tsa = *tsaIt;
5568 const Argument *tda = 0;
5569 if (tdAli && tdaIt!=tdAli->end())
5570 {
5571 tda = &(*tdaIt);
5572 ++tdaIt;
5573 }
5574 //if (tda) printf("tsa=%s|%s tda=%s|%s\n",
5575 // qPrint(tsa.type),qPrint(tsa.name),
5576 // qPrint(tda->type),qPrint(tda->name));
5577 if (name==tsa.name.str())
5578 {
5579 if (tda && tda->name.isEmpty())
5580 {
5581 QCString tdaName = tda->name;
5582 QCString tdaType = tda->type;
5583 int vc=0;
5584 if (tdaType.left(6)=="class ") vc=6;
5585 else if (tdaType.left(9)=="typename ") vc=9;
5586 if (vc>0) // convert type=="class T" to type=="class" name=="T"
5587 {
5588 tdaName = tdaType.mid(vc);
5589 }
5590 if (!tdaName.isEmpty())
5591 {
5592 name=tdaName.str(); // substitute
5593 found=TRUE;
5594 }
5595 }
5596 }
5597 }
5598
5599 //printf(" srcList='%s' dstList='%s faList='%s'\n",
5600 // qPrint(argListToString(srclali.current())),
5601 // qPrint(argListToString(dstlali.current())),
5602 // funcTempArgList ? qPrint(argListToString(funcTempArgList)) : "<none>");
5603 ++srcIt;
5604 }
5605 dst+=name;
5606 p=i+l;
5607 }
5608 dst+=src.substr(p);
5609 //printf(" substituteTemplatesInString(%s)=%s\n",
5610 // qPrint(src),qPrint(dst));
5611 return QCString(dst);
5612 }
5613
substituteTemplatesInArgList(const ArgumentLists & srcTempArgLists,const ArgumentLists & dstTempArgLists,const ArgumentList & src,ArgumentList & dst)5614 static void substituteTemplatesInArgList(
5615 const ArgumentLists &srcTempArgLists,
5616 const ArgumentLists &dstTempArgLists,
5617 const ArgumentList &src,
5618 ArgumentList &dst
5619 )
5620 {
5621 auto dstIt = dst.begin();
5622 for (const Argument &sa : src)
5623 {
5624 QCString dstType = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.type.str());
5625 QCString dstArray = substituteTemplatesInString(srcTempArgLists,dstTempArgLists,sa.array.str());
5626 if (dstIt == dst.end())
5627 {
5628 Argument da = sa;
5629 da.type = dstType;
5630 da.array = dstArray;
5631 dst.push_back(da);
5632 dstIt = dst.end();
5633 }
5634 else
5635 {
5636 Argument da = *dstIt;
5637 da.type = dstType;
5638 da.array = dstArray;
5639 ++dstIt;
5640 }
5641 }
5642 dst.setConstSpecifier(src.constSpecifier());
5643 dst.setVolatileSpecifier(src.volatileSpecifier());
5644 dst.setPureSpecifier(src.pureSpecifier());
5645 dst.setTrailingReturnType(substituteTemplatesInString(
5646 srcTempArgLists,dstTempArgLists,
5647 src.trailingReturnType().str()));
5648 //printf("substituteTemplatesInArgList: replacing %s with %s\n",
5649 // qPrint(argListToString(src)),qPrint(argListToString(dst))
5650 // );
5651 }
5652
5653 //-------------------------------------------------------------------------------------------
5654
addLocalObjCMethod(const Entry * root,const QCString & scopeName,const QCString & funcType,const QCString & funcName,const QCString & funcArgs,const QCString & exceptions,const QCString & funcDecl,uint64 spec)5655 static void addLocalObjCMethod(const Entry *root,
5656 const QCString &scopeName,
5657 const QCString &funcType,const QCString &funcName,const QCString &funcArgs,
5658 const QCString &exceptions,const QCString &funcDecl,
5659 uint64 spec)
5660 {
5661 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
5662 ClassDefMutable *cd=0;
5663 if (Config_getBool(EXTRACT_LOCAL_METHODS) && (cd=getClassMutable(scopeName)))
5664 {
5665 Debug::print(Debug::FindMembers,0,"4. Local objective C method %s\n"
5666 " scopeName=%s\n",qPrint(root->name),qPrint(scopeName));
5667 //printf("Local objective C method '%s' of class '%s' found\n",qPrint(root->name),qPrint(cd->name()));
5668 std::unique_ptr<MemberDefMutable> md { createMemberDef(
5669 root->fileName,root->startLine,root->startColumn,
5670 funcType,funcName,funcArgs,exceptions,
5671 root->protection,root->virt,root->stat,Member,
5672 MemberType_Function,ArgumentList(),root->argList,root->metaData) };
5673 md->setTagInfo(root->tagInfo());
5674 md->setLanguage(root->lang);
5675 md->setId(root->id);
5676 md->makeImplementationDetail();
5677 md->setMemberClass(cd);
5678 md->setDefinition(funcDecl);
5679 md->enableCallGraph(root->callGraph);
5680 md->enableCallerGraph(root->callerGraph);
5681 md->enableReferencedByRelation(root->referencedByRelation);
5682 md->enableReferencesRelation(root->referencesRelation);
5683 md->setDocumentation(root->doc,root->docFile,root->docLine);
5684 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
5685 md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
5686 md->setDocsForDefinition(!root->proto);
5687 md->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
5688 md->addSectionsToDefinition(root->anchors);
5689 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
5690 FileDef *fd=root->fileDef();
5691 md->setBodyDef(fd);
5692 md->setMemberSpecifiers(spec);
5693 md->setMemberGroupId(root->mGrpId);
5694 cd->insertMember(md.get());
5695 cd->insertUsedFile(fd);
5696 md->setRefItems(root->sli);
5697
5698 MemberName *mn = Doxygen::memberNameLinkedMap->add(root->name);
5699 mn->push_back(std::move(md));
5700 }
5701 else
5702 {
5703 // local objective C method found for class without interface
5704 }
5705 }
5706
5707 //-------------------------------------------------------------------------------------------
5708
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,uint64 spec,const QCString & relates,const QCString & funcDecl,bool overloaded,bool isFunc)5709 static void addMemberFunction(const Entry *root,
5710 MemberName *mn,
5711 const QCString &scopeName,
5712 const QCString &namespaceName,
5713 const QCString &className,
5714 const QCString &funcTyp,
5715 const QCString &funcName,
5716 const QCString &funcArgs,
5717 const QCString &funcTempList,
5718 const QCString &exceptions,
5719 const QCString &type,
5720 const QCString &args,
5721 bool isFriend,
5722 uint64 spec,
5723 const QCString &relates,
5724 const QCString &funcDecl,
5725 bool overloaded,
5726 bool isFunc)
5727 {
5728 QCString funcType = funcTyp;
5729 int count=0;
5730 int noMatchCount=0;
5731 bool memFound=FALSE;
5732 for (const auto &imd : *mn)
5733 {
5734 MemberDefMutable *md = toMemberDefMutable(imd.get());
5735 if (md==0) continue;
5736 ClassDefMutable *cd=md->getClassDefMutable();
5737 if (cd==0) continue;
5738 Debug::print(Debug::FindMembers,0,
5739 "3. member definition found, "
5740 "scope needed='%s' scope='%s' args='%s' fileName=%s\n",
5741 qPrint(scopeName),qPrint(cd->name()),
5742 qPrint(md->argsString()),
5743 qPrint(root->fileName));
5744 //printf("Member %s (member scopeName=%s) (this scopeName=%s) isEnumValue()=%d\n",
5745 // qPrint(md->name()),qPrint(cd->name()),qPrint(scopeName),md->isEnumValue());
5746 FileDef *fd=root->fileDef();
5747 NamespaceDef *nd=0;
5748 if (!namespaceName.isEmpty()) nd=getResolvedNamespace(namespaceName);
5749
5750 //printf("scopeName %s->%s\n",qPrint(scopeName),
5751 // qPrint(stripTemplateSpecifiersFromScope(scopeName,FALSE)));
5752
5753 // if the member we are searching for is an enum value that is part of
5754 // a "strong" enum, we need to look into the fields of the enum for a match
5755 int enumNamePos=0;
5756 if (md->isEnumValue() && (enumNamePos=className.findRev("::"))!=-1)
5757 {
5758 QCString enumName = className.mid(enumNamePos+2);
5759 QCString fullScope = className.left(enumNamePos);
5760 if (!namespaceName.isEmpty()) fullScope.prepend(namespaceName+"::");
5761 if (fullScope==cd->name())
5762 {
5763 MemberName *enumMn=Doxygen::memberNameLinkedMap->find(enumName);
5764 //printf("enumMn(%s)=%p\n",qPrint(className),(void*)enumMn);
5765 if (enumMn)
5766 {
5767 for (const auto &emd : *enumMn)
5768 {
5769 memFound = emd->isStrong() && md->getEnumScope()==emd.get();
5770 if (memFound)
5771 {
5772 addMemberDocs(root,md,funcDecl,0,overloaded,spec);
5773 count++;
5774 }
5775 if (memFound) break;
5776 }
5777 }
5778 }
5779 }
5780 if (memFound) break;
5781
5782 const ClassDef *tcd=findClassDefinition(fd,nd,scopeName);
5783 if (tcd==0 && cd && stripAnonymousNamespaceScope(cd->name())==scopeName)
5784 {
5785 // don't be fooled by anonymous scopes
5786 tcd=cd;
5787 }
5788 //printf("Looking for %s inside nd=%s result=%p (%s) cd=%p\n",
5789 // qPrint(scopeName),nd?qPrint(nd->name()):"<none>",tcd,tcd?qPrint(tcd->name()):"",cd);
5790
5791 if (cd && tcd==cd) // member's classes match
5792 {
5793 Debug::print(Debug::FindMembers,0,
5794 "4. class definition %s found\n",qPrint(cd->name()));
5795
5796 // get the template parameter lists found at the member declaration
5797 ArgumentLists declTemplArgs = cd->getTemplateParameterLists();
5798 const ArgumentList &templAl = md->templateArguments();
5799 if (!templAl.empty())
5800 {
5801 declTemplArgs.push_back(templAl);
5802 }
5803
5804 // get the template parameter lists found at the member definition
5805 const ArgumentLists &defTemplArgs = root->tArgLists;
5806 //printf("defTemplArgs=%p\n",defTemplArgs);
5807
5808 // do we replace the decl argument lists with the def argument lists?
5809 bool substDone=FALSE;
5810 ArgumentList argList;
5811
5812 /* substitute the occurrences of class template names in the
5813 * argument list before matching
5814 */
5815 const ArgumentList &mdAl = md->argumentList();
5816 if (declTemplArgs.size()>0 && declTemplArgs.size()==defTemplArgs.size())
5817 {
5818 /* the function definition has template arguments
5819 * and the class definition also has template arguments, so
5820 * we must substitute the template names of the class by that
5821 * of the function definition before matching.
5822 */
5823 substituteTemplatesInArgList(declTemplArgs,defTemplArgs,mdAl,argList);
5824
5825 substDone=TRUE;
5826 }
5827 else /* no template arguments, compare argument lists directly */
5828 {
5829 argList = mdAl;
5830 }
5831
5832 Debug::print(Debug::FindMembers,0,
5833 "5. matching '%s'<=>'%s' className=%s namespaceName=%s\n",
5834 qPrint(argListToString(argList,TRUE)),qPrint(argListToString(root->argList,TRUE)),
5835 qPrint(className),qPrint(namespaceName)
5836 );
5837
5838 bool matching=
5839 md->isVariable() || md->isTypedef() || // needed for function pointers
5840 matchArguments2(
5841 md->getClassDef(),md->getFileDef(),&argList,
5842 cd,fd,&root->argList,
5843 TRUE);
5844
5845 if (md->getLanguage()==SrcLangExt_ObjC && md->isVariable() && (root->section&Entry::FUNCTION_SEC))
5846 {
5847 matching = FALSE; // don't match methods and attributes with the same name
5848 }
5849
5850 // for template member we also need to check the return type
5851 if (!md->templateArguments().empty() && !root->tArgLists.empty())
5852 {
5853 QCString memType = md->typeString();
5854 memType.stripPrefix("static "); // see bug700696
5855 funcType=substitute(stripTemplateSpecifiersFromScope(funcType,TRUE),
5856 className+"::",""); // see bug700693 & bug732594
5857 memType=substitute(stripTemplateSpecifiersFromScope(memType,TRUE),
5858 className+"::",""); // see bug758900
5859 Debug::print(Debug::FindMembers,0,
5860 "5b. Comparing return types '%s'<->'%s' #args %d<->%d\n",
5861 qPrint(md->typeString()),qPrint(funcType),
5862 md->templateArguments().size(),root->tArgLists.back().size());
5863 if (md->templateArguments().size()!=root->tArgLists.back().size() || memType!=funcType)
5864 {
5865 //printf(" ---> no matching\n");
5866 matching = FALSE;
5867 }
5868 }
5869 bool rootIsUserDoc = (root->section&Entry::MEMBERDOC_SEC)!=0;
5870 bool classIsTemplate = scopeIsTemplate(md->getClassDef());
5871 bool mdIsTemplate = md->templateArguments().hasParameters();
5872 bool classOrMdIsTemplate = mdIsTemplate || classIsTemplate;
5873 bool rootIsTemplate = !root->tArgLists.empty();
5874 //printf("classIsTemplate=%d mdIsTemplate=%d rootIsTemplate=%d\n",classIsTemplate,mdIsTemplate,rootIsTemplate);
5875 if (!rootIsUserDoc && // don't check out-of-line @fn references, see bug722457
5876 (mdIsTemplate || rootIsTemplate) && // either md or root is a template
5877 ((classOrMdIsTemplate && !rootIsTemplate) || (!classOrMdIsTemplate && rootIsTemplate))
5878 )
5879 {
5880 // Method with template return type does not match method without return type
5881 // even if the parameters are the same. See also bug709052
5882 Debug::print(Debug::FindMembers,0,
5883 "5b. Comparing return types: template v.s. non-template\n");
5884 matching = FALSE;
5885 }
5886
5887
5888 Debug::print(Debug::FindMembers,0,
5889 "6. match results of matchArguments2 = %d substDone=%d\n",matching,substDone);
5890
5891 if (substDone) // found a new argument list
5892 {
5893 if (matching) // replace member's argument list
5894 {
5895 md->setDefinitionTemplateParameterLists(root->tArgLists);
5896 md->moveArgumentList(std::make_unique<ArgumentList>(argList));
5897 }
5898 else // no match
5899 {
5900 if (!funcTempList.isEmpty() &&
5901 isSpecialization(declTemplArgs,defTemplArgs))
5902 {
5903 // check if we are dealing with a partial template
5904 // specialization. In this case we add it to the class
5905 // even though the member arguments do not match.
5906
5907 addMethodToClass(root,cd,type,md->name(),args,isFriend,
5908 md->protection(),md->isStatic(),md->virtualness(),spec,relates);
5909 return;
5910 }
5911 }
5912 }
5913 if (matching)
5914 {
5915 addMemberDocs(root,md,funcDecl,0,overloaded,spec);
5916 count++;
5917 memFound=TRUE;
5918 }
5919 }
5920 else if (cd && cd!=tcd) // we did find a class with the same name as cd
5921 // but in a different namespace
5922 {
5923 noMatchCount++;
5924 }
5925
5926 if (memFound) break;
5927 }
5928 if (count==0 && root->parent() &&
5929 root->parent()->section==Entry::OBJCIMPL_SEC)
5930 {
5931 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
5932 return;
5933 }
5934 if (count==0 && !(isFriend && funcType=="class"))
5935 {
5936 int candidates=0;
5937 const ClassDef *ecd = 0, *ucd = 0;
5938 MemberDef *emd = 0, *umd = 0;
5939 //printf("Assume template class\n");
5940 for (const auto &md : *mn)
5941 {
5942 ClassDefMutable *ccd=md->getClassDefMutable();
5943 MemberDef *cmd=md.get();
5944 //printf("ccd->name()==%s className=%s\n",qPrint(ccd->name()),qPrint(className));
5945 if (ccd!=0 && rightScopeMatch(ccd->name(),className))
5946 {
5947 const ArgumentList &templAl = md->templateArguments();
5948 if (!root->tArgLists.empty() && !templAl.empty() &&
5949 root->tArgLists.back().size()<=templAl.size())
5950 {
5951 Debug::print(Debug::FindMembers,0,"7. add template specialization\n");
5952 addMethodToClass(root,ccd,type,md->name(),args,isFriend,
5953 root->protection,root->stat,root->virt,spec,relates);
5954 return;
5955 }
5956 if (md->argsString()==argListToString(root->argList,FALSE,FALSE))
5957 { // exact argument list match -> remember
5958 ucd = ecd = ccd;
5959 umd = emd = cmd;
5960 Debug::print(Debug::FindMembers,0,
5961 "7. new candidate className=%s scope=%s args=%s exact match\n",
5962 qPrint(className),qPrint(ccd->name()),qPrint(md->argsString()));
5963 }
5964 else // arguments do not match, but member name and scope do -> remember
5965 {
5966 ucd = ccd;
5967 umd = cmd;
5968 Debug::print(Debug::FindMembers,0,
5969 "7. new candidate className=%s scope=%s args=%s no match\n",
5970 qPrint(className),qPrint(ccd->name()),qPrint(md->argsString()));
5971 }
5972 candidates++;
5973 }
5974 }
5975 static bool strictProtoMatching = Config_getBool(STRICT_PROTO_MATCHING);
5976 if (!strictProtoMatching)
5977 {
5978 if (candidates==1 && ucd && umd)
5979 {
5980 // we didn't find an actual match on argument lists, but there is only 1 member with this
5981 // name in the same scope, so that has to be the one.
5982 addMemberDocs(root,toMemberDefMutable(umd),funcDecl,0,overloaded,spec);
5983 return;
5984 }
5985 else if (candidates>1 && ecd && emd)
5986 {
5987 // we didn't find a unique match using type resolution,
5988 // but one of the matches has the exact same signature so
5989 // we take that one.
5990 addMemberDocs(root,toMemberDefMutable(emd),funcDecl,0,overloaded,spec);
5991 return;
5992 }
5993 }
5994
5995 QCString warnMsg = "no ";
5996 if (noMatchCount>1) warnMsg+="uniquely ";
5997 warnMsg+="matching class member found for \n";
5998
5999 for (const ArgumentList &al : root->tArgLists)
6000 {
6001 warnMsg+=" template ";
6002 warnMsg+=tempArgListToString(al,root->lang);
6003 warnMsg+='\n';
6004 }
6005
6006 QCString fullFuncDecl=funcDecl;
6007 if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
6008
6009 warnMsg+=" ";
6010 warnMsg+=fullFuncDecl;
6011 warnMsg+='\n';
6012
6013 if (candidates>0)
6014 {
6015 warnMsg+="Possible candidates:\n";
6016 for (const auto &md : *mn)
6017 {
6018 const ClassDef *cd=md->getClassDef();
6019 if (cd!=0 && rightScopeMatch(cd->name(),className))
6020 {
6021 const ArgumentList &templAl = md->templateArguments();
6022 warnMsg+=" '";
6023 if (templAl.hasParameters())
6024 {
6025 warnMsg+="template ";
6026 warnMsg+=tempArgListToString(templAl,root->lang);
6027 warnMsg+='\n';
6028 warnMsg+=" ";
6029 }
6030 if (!md->typeString().isEmpty())
6031 {
6032 warnMsg+=md->typeString();
6033 warnMsg+=' ';
6034 }
6035 QCString qScope = cd->qualifiedNameWithTemplateParameters();
6036 if (!qScope.isEmpty())
6037 warnMsg+=qScope+"::"+md->name();
6038 warnMsg+=md->argsString();
6039 if (noMatchCount>1)
6040 {
6041 warnMsg+="' at line "+QCString().setNum(md->getDefLine()) +
6042 " of file "+md->getDefFileName();
6043 }
6044 else
6045 warnMsg += "'";
6046
6047 warnMsg+='\n';
6048 }
6049 }
6050 }
6051 warn_simple(root->fileName,root->startLine,qPrint(warnMsg));
6052 }
6053 }
6054
6055 //-------------------------------------------------------------------------------------------
6056
addMemberSpecialization(const Entry * root,MemberName * mn,ClassDefMutable * cd,const QCString & funcType,const QCString & funcName,const QCString & funcArgs,const QCString & funcDecl,const QCString & exceptions,uint64 spec)6057 static void addMemberSpecialization(const Entry *root,
6058 MemberName *mn,
6059 ClassDefMutable *cd,
6060 const QCString &funcType,
6061 const QCString &funcName,
6062 const QCString &funcArgs,
6063 const QCString &funcDecl,
6064 const QCString &exceptions,
6065 uint64 spec
6066 )
6067 {
6068 MemberDef *declMd=0;
6069 for (const auto &md : *mn)
6070 {
6071 if (md->getClassDef()==cd)
6072 {
6073 // TODO: we should probably also check for matching arguments
6074 declMd = md.get();
6075 break;
6076 }
6077 }
6078 MemberType mtype=MemberType_Function;
6079 ArgumentList tArgList;
6080 // getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6081 std::unique_ptr<MemberDefMutable> md { createMemberDef(
6082 root->fileName,root->startLine,root->startColumn,
6083 funcType,funcName,funcArgs,exceptions,
6084 declMd ? declMd->protection() : root->protection,
6085 root->virt,root->stat,Member,
6086 mtype,tArgList,root->argList,root->metaData) };
6087 //printf("new specialized member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6088 md->setTagInfo(root->tagInfo());
6089 md->setLanguage(root->lang);
6090 md->setId(root->id);
6091 md->setMemberClass(cd);
6092 md->setTemplateSpecialization(TRUE);
6093 md->setTypeConstraints(root->typeConstr);
6094 md->setDefinition(funcDecl);
6095 md->enableCallGraph(root->callGraph);
6096 md->enableCallerGraph(root->callerGraph);
6097 md->enableReferencedByRelation(root->referencedByRelation);
6098 md->enableReferencesRelation(root->referencesRelation);
6099 md->setDocumentation(root->doc,root->docFile,root->docLine);
6100 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6101 md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6102 md->setDocsForDefinition(!root->proto);
6103 md->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6104 md->addSectionsToDefinition(root->anchors);
6105 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6106 FileDef *fd=root->fileDef();
6107 md->setBodyDef(fd);
6108 md->setMemberSpecifiers(spec);
6109 md->setMemberGroupId(root->mGrpId);
6110 cd->insertMember(md.get());
6111 md->setRefItems(root->sli);
6112
6113 mn->push_back(std::move(md));
6114 }
6115
6116 //-------------------------------------------------------------------------------------------
6117
addOverloaded(const Entry * root,MemberName * mn,const QCString & funcType,const QCString & funcName,const QCString & funcArgs,const QCString & funcDecl,const QCString & exceptions,uint64 spec)6118 static void addOverloaded(const Entry *root,MemberName *mn,
6119 const QCString &funcType,const QCString &funcName,const QCString &funcArgs,
6120 const QCString &funcDecl,const QCString &exceptions,uint64 spec)
6121 {
6122 // for unique overloaded member we allow the class to be
6123 // omitted, this is to be Qt compatible. Using this should
6124 // however be avoided, because it is error prone
6125 bool sameClass=false;
6126 if (mn->size()>0)
6127 {
6128 // check if all members with the same name are also in the same class
6129 sameClass = std::equal(mn->begin()+1,mn->end(),mn->begin(),
6130 [](const auto &md1,const auto &md2)
6131 { return md1->getClassDef()->name()==md2->getClassDef()->name(); });
6132 }
6133 if (sameClass)
6134 {
6135 ClassDefMutable *cd = mn->front()->getClassDefMutable();
6136 MemberType mtype;
6137 if (root->mtype==Signal) mtype=MemberType_Signal;
6138 else if (root->mtype==Slot) mtype=MemberType_Slot;
6139 else if (root->mtype==DCOP) mtype=MemberType_DCOP;
6140 else mtype=MemberType_Function;
6141
6142 // new overloaded member function
6143 std::unique_ptr<ArgumentList> tArgList =
6144 getTemplateArgumentsFromName(cd->name()+"::"+funcName,root->tArgLists);
6145 //printf("new related member %s args='%s'\n",qPrint(md->name()),qPrint(funcArgs));
6146 std::unique_ptr<MemberDefMutable> md { createMemberDef(
6147 root->fileName,root->startLine,root->startColumn,
6148 funcType,funcName,funcArgs,exceptions,
6149 root->protection,root->virt,root->stat,Related,
6150 mtype,tArgList ? *tArgList : ArgumentList(),root->argList,root->metaData) };
6151 md->setTagInfo(root->tagInfo());
6152 md->setLanguage(root->lang);
6153 md->setId(root->id);
6154 md->setTypeConstraints(root->typeConstr);
6155 md->setMemberClass(cd);
6156 md->setDefinition(funcDecl);
6157 md->enableCallGraph(root->callGraph);
6158 md->enableCallerGraph(root->callerGraph);
6159 md->enableReferencedByRelation(root->referencedByRelation);
6160 md->enableReferencesRelation(root->referencesRelation);
6161 QCString doc=getOverloadDocs();
6162 doc+="<p>";
6163 doc+=root->doc;
6164 md->setDocumentation(doc,root->docFile,root->docLine);
6165 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6166 md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6167 md->setDocsForDefinition(!root->proto);
6168 md->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6169 md->addSectionsToDefinition(root->anchors);
6170 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6171 FileDef *fd=root->fileDef();
6172 md->setBodyDef(fd);
6173 md->setMemberSpecifiers(spec);
6174 md->setMemberGroupId(root->mGrpId);
6175 cd->insertMember(md.get());
6176 cd->insertUsedFile(fd);
6177 md->setRefItems(root->sli);
6178
6179 mn->push_back(std::move(md));
6180 }
6181 }
6182
6183 //-------------------------------------------------------------------------------------------
6184
6185 /*! This function tries to find a member (in a documented class/file/namespace)
6186 * that corresponds to the function/variable declaration given in \a funcDecl.
6187 *
6188 * The boolean \a overloaded is used to specify whether or not a standard
6189 * overload documentation line should be generated.
6190 *
6191 * The boolean \a isFunc is a hint that indicates that this is a function
6192 * instead of a variable or typedef.
6193 */
findMember(const Entry * root,const QCString & relates,const QCString & type,const QCString & args,QCString funcDecl,bool overloaded,bool isFunc)6194 static void findMember(const Entry *root,
6195 const QCString &relates,
6196 const QCString &type,
6197 const QCString &args,
6198 QCString funcDecl,
6199 bool overloaded,
6200 bool isFunc
6201 )
6202 {
6203 Debug::print(Debug::FindMembers,0,
6204 "findMember(root=%p,funcDecl='%s',related='%s',overload=%d,"
6205 "isFunc=%d mGrpId=%d #tArgList=%d "
6206 "spec=%lld lang=%x\n",
6207 root,qPrint(funcDecl),qPrint(relates),overloaded,isFunc,root->mGrpId,
6208 root->tArgLists.size(),
6209 root->spec,root->lang
6210 );
6211
6212 QCString scopeName;
6213 QCString className;
6214 QCString namespaceName;
6215 QCString funcType;
6216 QCString funcName;
6217 QCString funcArgs;
6218 QCString funcTempList;
6219 QCString exceptions;
6220 QCString funcSpec;
6221 bool isRelated=FALSE;
6222 bool isMemberOf=FALSE;
6223 bool isFriend=FALSE;
6224 bool done;
6225 uint64 spec = root->spec;
6226 do
6227 {
6228 done=TRUE;
6229 if (funcDecl.stripPrefix("friend ")) // treat friends as related members
6230 {
6231 isFriend=TRUE;
6232 done=FALSE;
6233 }
6234 if (funcDecl.stripPrefix("inline "))
6235 {
6236 spec|=Entry::Inline;
6237 done=FALSE;
6238 }
6239 if (funcDecl.stripPrefix("explicit "))
6240 {
6241 spec|=Entry::Explicit;
6242 done=FALSE;
6243 }
6244 if (funcDecl.stripPrefix("mutable "))
6245 {
6246 spec|=Entry::Mutable;
6247 done=FALSE;
6248 }
6249 if (funcDecl.stripPrefix("virtual "))
6250 {
6251 done=FALSE;
6252 }
6253 } while (!done);
6254
6255 // delete any ; from the function declaration
6256 int sep;
6257 while ((sep=funcDecl.find(';'))!=-1)
6258 {
6259 funcDecl=(funcDecl.left(sep)+funcDecl.right(funcDecl.length()-sep-1)).stripWhiteSpace();
6260 }
6261
6262 // make sure the first character is a space to simplify searching.
6263 if (!funcDecl.isEmpty() && funcDecl[0]!=' ') funcDecl.prepend(" ");
6264
6265 // remove some superfluous spaces
6266 funcDecl= substitute(
6267 substitute(
6268 substitute(funcDecl,"~ ","~"),
6269 ":: ","::"
6270 ),
6271 " ::","::"
6272 ).stripWhiteSpace();
6273
6274 //printf("funcDecl='%s'\n",qPrint(funcDecl));
6275 if (isFriend && funcDecl.left(6)=="class ")
6276 {
6277 //printf("friend class\n");
6278 funcDecl=funcDecl.right(funcDecl.length()-6);
6279 funcName = funcDecl;
6280 }
6281 else if (isFriend && funcDecl.left(7)=="struct ")
6282 {
6283 funcDecl=funcDecl.right(funcDecl.length()-7);
6284 funcName = funcDecl;
6285 }
6286 else
6287 {
6288 // extract information from the declarations
6289 parseFuncDecl(funcDecl,root->lang,scopeName,funcType,funcName,
6290 funcArgs,funcTempList,exceptions
6291 );
6292 }
6293 //printf("scopeName='%s' funcType='%s' funcName='%s' funcArgs='%s'\n",
6294 // qPrint(scopeName),qPrint(funcType),qPrint(funcName),qPrint(funcArgs));
6295
6296 // the class name can also be a namespace name, we decide this later.
6297 // if a related class name is specified and the class name could
6298 // not be derived from the function declaration, then use the
6299 // related field.
6300 //printf("scopeName='%s' className='%s' namespaceName='%s'\n",
6301 // qPrint(scopeName),qPrint(className),qPrint(namespaceName));
6302 if (!relates.isEmpty())
6303 { // related member, prefix user specified scope
6304 isRelated=TRUE;
6305 isMemberOf=(root->relatesType == MemberOf);
6306 if (getClass(relates)==0 && !scopeName.isEmpty())
6307 {
6308 scopeName= mergeScopes(scopeName,relates);
6309 }
6310 else
6311 {
6312 scopeName = relates;
6313 }
6314 }
6315
6316 if (relates.isEmpty() && root->parent() &&
6317 ((root->parent()->section&Entry::SCOPE_MASK) ||
6318 (root->parent()->section==Entry::OBJCIMPL_SEC)
6319 ) &&
6320 !root->parent()->name.isEmpty()) // see if we can combine scopeName
6321 // with the scope in which it was found
6322 {
6323 QCString joinedName = root->parent()->name+"::"+scopeName;
6324 if (!scopeName.isEmpty() &&
6325 (getClass(joinedName) || Doxygen::namespaceLinkedMap->find(joinedName)))
6326 {
6327 scopeName = joinedName;
6328 }
6329 else
6330 {
6331 scopeName = mergeScopes(root->parent()->name,scopeName);
6332 }
6333 }
6334 else // see if we can prefix a namespace or class that is used from the file
6335 {
6336 FileDef *fd=root->fileDef();
6337 if (fd)
6338 {
6339 for (const auto &fnd : fd->getUsedNamespaces())
6340 {
6341 QCString joinedName = fnd->name()+"::"+scopeName;
6342 if (Doxygen::namespaceLinkedMap->find(joinedName))
6343 {
6344 scopeName=joinedName;
6345 break;
6346 }
6347 }
6348 }
6349 }
6350 scopeName=stripTemplateSpecifiersFromScope(
6351 removeRedundantWhiteSpace(scopeName),FALSE,&funcSpec);
6352
6353 // funcSpec contains the last template specifiers of the given scope.
6354 // If this method does not have any template arguments or they are
6355 // empty while funcSpec is not empty we assume this is a
6356 // specialization of a method. If not, we clear the funcSpec and treat
6357 // this as a normal method of a template class.
6358 if (!(root->tArgLists.size()>0 &&
6359 root->tArgLists.front().size()==0
6360 )
6361 )
6362 {
6363 funcSpec.resize(0);
6364 }
6365
6366 // split scope into a namespace and a class part
6367 extractNamespaceName(scopeName,className,namespaceName,TRUE);
6368 //printf("scopeName='%s' className='%s' namespaceName='%s'\n",
6369 // qPrint(scopeName),qPrint(className),qPrint(namespaceName));
6370
6371 //namespaceName=removeAnonymousScopes(namespaceName);
6372 if (namespaceName.find('@')!=-1) return; // skip stuff in anonymous namespace...
6373
6374 //printf("namespaceName='%s' className='%s'\n",qPrint(namespaceName),qPrint(className));
6375 // merge class and namespace scopes again
6376 scopeName.resize(0);
6377 if (!namespaceName.isEmpty())
6378 {
6379 if (className.isEmpty())
6380 {
6381 scopeName=namespaceName;
6382 }
6383 else if (!relates.isEmpty() || // relates command with explicit scope
6384 !getClass(className)) // class name only exists in a namespace
6385 {
6386 scopeName=namespaceName+"::"+className;
6387 }
6388 else
6389 {
6390 scopeName=className;
6391 }
6392 }
6393 else if (!className.isEmpty())
6394 {
6395 scopeName=className;
6396 }
6397 //printf("new scope='%s'\n",qPrint(scopeName));
6398
6399 QCString tempScopeName=scopeName;
6400 ClassDefMutable *cd=getClassMutable(scopeName);
6401 if (cd)
6402 {
6403 if (funcSpec.isEmpty())
6404 {
6405 uint argListIndex=0;
6406 tempScopeName=cd->qualifiedNameWithTemplateParameters(&root->tArgLists,&argListIndex);
6407 }
6408 else
6409 {
6410 tempScopeName=scopeName+funcSpec;
6411 }
6412 }
6413 //printf("scopeName=%s cd=%p root->tArgLists=%p result=%s\n",
6414 // qPrint(scopeName),cd,root->tArgLists,qPrint(tempScopeName));
6415
6416 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
6417 // rebuild the function declaration (needed to get the scope right).
6418 if (!scopeName.isEmpty() && !isRelated && !isFriend && !Config_getBool(HIDE_SCOPE_NAMES))
6419 {
6420 if (!funcType.isEmpty())
6421 {
6422 if (isFunc) // a function -> we use argList for the arguments
6423 {
6424 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcTempList;
6425 }
6426 else
6427 {
6428 funcDecl=funcType+" "+tempScopeName+"::"+funcName+funcArgs;
6429 }
6430 }
6431 else
6432 {
6433 if (isFunc) // a function => we use argList for the arguments
6434 {
6435 funcDecl=tempScopeName+"::"+funcName+funcTempList;
6436 }
6437 else // variable => add 'argument' list
6438 {
6439 funcDecl=tempScopeName+"::"+funcName+funcArgs;
6440 }
6441 }
6442 }
6443 else // build declaration without scope
6444 {
6445 if (!funcType.isEmpty()) // but with a type
6446 {
6447 if (isFunc) // function => omit argument list
6448 {
6449 funcDecl=funcType+" "+funcName+funcTempList;
6450 }
6451 else // variable => add 'argument' list
6452 {
6453 funcDecl=funcType+" "+funcName+funcArgs;
6454 }
6455 }
6456 else // no type
6457 {
6458 if (isFunc)
6459 {
6460 funcDecl=funcName+funcTempList;
6461 }
6462 else
6463 {
6464 funcDecl=funcName+funcArgs;
6465 }
6466 }
6467 }
6468
6469 if (funcType=="template class" && !funcTempList.isEmpty())
6470 return; // ignore explicit template instantiations
6471
6472 Debug::print(Debug::FindMembers,0,
6473 "findMember() Parse results:\n"
6474 " namespaceName='%s'\n"
6475 " className=`%s`\n"
6476 " funcType='%s'\n"
6477 " funcSpec='%s'\n"
6478 " funcName='%s'\n"
6479 " funcArgs='%s'\n"
6480 " funcTempList='%s'\n"
6481 " funcDecl='%s'\n"
6482 " related='%s'\n"
6483 " exceptions='%s'\n"
6484 " isRelated=%d\n"
6485 " isMemberOf=%d\n"
6486 " isFriend=%d\n"
6487 " isFunc=%d\n\n",
6488 qPrint(namespaceName),qPrint(className),
6489 qPrint(funcType),qPrint(funcSpec),qPrint(funcName),qPrint(funcArgs),qPrint(funcTempList),
6490 qPrint(funcDecl),qPrint(relates),qPrint(exceptions),isRelated,isMemberOf,isFriend,
6491 isFunc
6492 );
6493
6494 if (!funcName.isEmpty()) // function name is valid
6495 {
6496 Debug::print(Debug::FindMembers,0,
6497 "1. funcName='%s'\n",qPrint(funcName));
6498
6499 // check if 'className' is actually a scoped enum, in which case we need to
6500 // process it as a global, see issue #6471
6501 bool strongEnum = false;
6502 MemberName *mn=0;
6503 if (!className.isEmpty() && (mn=Doxygen::functionNameLinkedMap->find(className)))
6504 {
6505 for (const auto &imd : *mn)
6506 {
6507 MemberDefMutable *md = toMemberDefMutable(imd.get());
6508 if (md && md->isEnumerate() && md->isStrong())
6509 {
6510 Debug::print(Debug::FindMembers,0,"%s is a strong enum!\n",qPrint(md->name()));
6511 strongEnum = true;
6512 // pass the scope name name as a 'namespace' to the findGlobalMember function
6513 if (!namespaceName.isEmpty())
6514 {
6515 namespaceName+="::"+className;
6516 }
6517 else
6518 {
6519 namespaceName=className;
6520 }
6521 }
6522 }
6523 }
6524
6525 if (funcName.left(9)=="operator ") // strip class scope from cast operator
6526 {
6527 funcName = substitute(funcName,className+"::","");
6528 }
6529 mn = 0;
6530 if (!funcTempList.isEmpty()) // try with member specialization
6531 {
6532 mn=Doxygen::memberNameLinkedMap->find(funcName+funcTempList);
6533 }
6534 if (mn==0) // try without specialization
6535 {
6536 mn=Doxygen::memberNameLinkedMap->find(funcName);
6537 }
6538 if (!isRelated && !strongEnum && mn) // function name already found
6539 {
6540 Debug::print(Debug::FindMembers,0,
6541 "2. member name exists (%d members with this name)\n",mn->size());
6542 if (!className.isEmpty()) // class name is valid
6543 {
6544 if (funcSpec.isEmpty()) // not a member specialization
6545 {
6546 addMemberFunction(root,mn,scopeName,namespaceName,className,funcType,funcName,
6547 funcArgs,funcTempList,exceptions,
6548 type,args,isFriend,spec,relates,funcDecl,overloaded,isFunc);
6549 }
6550 else if (cd) // member specialization
6551 {
6552 addMemberSpecialization(root,mn,cd,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
6553 }
6554 else
6555 {
6556 //printf("*** Specialized member %s of unknown scope %s%s found!\n",
6557 // qPrint(scopeName),qPrint(funcName),qPrint(funcArgs));
6558 }
6559 }
6560 else if (overloaded) // check if the function belongs to only one class
6561 {
6562 addOverloaded(root,mn,funcType,funcName,funcArgs,funcDecl,exceptions,spec);
6563 }
6564 else // unrelated function with the same name as a member
6565 {
6566 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
6567 {
6568 QCString fullFuncDecl=funcDecl;
6569 if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
6570 warn(root->fileName,root->startLine,
6571 "Cannot determine class for function\n%s",
6572 qPrint(fullFuncDecl)
6573 );
6574 }
6575 }
6576 }
6577 else if (isRelated && !relates.isEmpty())
6578 {
6579 Debug::print(Debug::FindMembers,0,"2. related function\n"
6580 " scopeName=%s className=%s\n",qPrint(scopeName),qPrint(className));
6581 if (className.isEmpty()) className=relates;
6582 //printf("scopeName='%s' className='%s'\n",qPrint(scopeName),qPrint(className));
6583 if ((cd=getClassMutable(scopeName)))
6584 {
6585 bool newMember=TRUE; // assume we have a new member
6586 MemberDefMutable *mdDefine=0;
6587 {
6588 mn = Doxygen::functionNameLinkedMap->find(funcName);
6589 if (mn)
6590 {
6591 for (const auto &imd : *mn)
6592 {
6593 MemberDefMutable *md = toMemberDefMutable(imd.get());
6594 if (md && md->isDefine())
6595 {
6596 mdDefine = md;
6597 break;
6598 }
6599 }
6600 }
6601 }
6602
6603 FileDef *fd=root->fileDef();
6604
6605 if ((mn=Doxygen::memberNameLinkedMap->find(funcName))==0)
6606 {
6607 mn=Doxygen::memberNameLinkedMap->add(funcName);
6608 }
6609 else
6610 {
6611 // see if we got another member with matching arguments
6612 MemberDefMutable *rmd_found = 0;
6613 for (const auto &irmd : *mn)
6614 {
6615 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
6616 if (rmd)
6617 {
6618 const ArgumentList &rmdAl = rmd->argumentList();
6619
6620 newMember=
6621 className!=rmd->getOuterScope()->name() ||
6622 !matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),&rmdAl,
6623 cd,fd,&root->argList,
6624 TRUE);
6625 if (!newMember)
6626 {
6627 rmd_found = rmd;
6628 }
6629 }
6630 }
6631 if (rmd_found) // member already exists as rmd -> add docs
6632 {
6633 //printf("addMemberDocs for related member %s\n",qPrint(root->name));
6634 //rmd->setMemberDefTemplateArguments(root->mtArgList);
6635 addMemberDocs(root,rmd_found,funcDecl,0,overloaded,spec);
6636 }
6637 }
6638
6639 if (newMember) // need to create a new member
6640 {
6641 MemberType mtype;
6642 if (mdDefine)
6643 mtype=MemberType_Define;
6644 else if (root->mtype==Signal)
6645 mtype=MemberType_Signal;
6646 else if (root->mtype==Slot)
6647 mtype=MemberType_Slot;
6648 else if (root->mtype==DCOP)
6649 mtype=MemberType_DCOP;
6650 else
6651 mtype=MemberType_Function;
6652
6653 if (mdDefine)
6654 {
6655 mdDefine->setHidden(TRUE);
6656 funcType="#define";
6657 funcArgs=mdDefine->argsString();
6658 funcDecl=funcType + " " + funcName;
6659 }
6660
6661 //printf("New related name '%s' '%d'\n",qPrint(funcName),
6662 // root->argList ? (int)root->argList->count() : -1);
6663
6664 // first note that we pass:
6665 // (root->tArgLists ? root->tArgLists->last() : 0)
6666 // for the template arguments for the new "member."
6667 // this accurately reflects the template arguments of
6668 // the related function, which don't have to do with
6669 // those of the related class.
6670 std::unique_ptr<MemberDefMutable> md { createMemberDef(
6671 root->fileName,root->startLine,root->startColumn,
6672 funcType,funcName,funcArgs,exceptions,
6673 root->protection,root->virt,
6674 root->stat,
6675 isMemberOf ? Foreign : Related,
6676 mtype,
6677 (!root->tArgLists.empty() ? root->tArgLists.back() : ArgumentList()),
6678 funcArgs.isEmpty() ? ArgumentList() : root->argList,
6679 root->metaData) };
6680
6681 if (mdDefine)
6682 {
6683 md->setInitializer(mdDefine->initializer());
6684 }
6685
6686 //
6687 // we still have the problem that
6688 // MemberDef::writeDocumentation() in memberdef.cpp
6689 // writes the template argument list for the class,
6690 // as if this member is a member of the class.
6691 // fortunately, MemberDef::writeDocumentation() has
6692 // a special mechanism that allows us to totally
6693 // override the set of template argument lists that
6694 // are printed. We use that and set it to the
6695 // template argument lists of the related function.
6696 //
6697 md->setDefinitionTemplateParameterLists(root->tArgLists);
6698
6699 md->setTagInfo(root->tagInfo());
6700
6701 //printf("Related member name='%s' decl='%s' bodyLine='%d'\n",
6702 // qPrint(funcName),qPrint(funcDecl),root->bodyLine);
6703
6704 // try to find the matching line number of the body from the
6705 // global function list
6706 bool found=FALSE;
6707 if (root->bodyLine==-1)
6708 {
6709 MemberName *rmn=Doxygen::functionNameLinkedMap->find(funcName);
6710 if (rmn)
6711 {
6712 const MemberDefMutable *rmd_found=0;
6713 for (const auto &irmd : *rmn)
6714 {
6715 MemberDefMutable *rmd = toMemberDefMutable(irmd.get());
6716 if (rmd)
6717 {
6718 const ArgumentList &rmdAl = rmd->argumentList();
6719 // check for matching argument lists
6720 if (
6721 matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),&rmdAl,
6722 cd,fd,&root->argList,
6723 TRUE)
6724 )
6725 {
6726 found=TRUE;
6727 rmd_found = rmd;
6728 break;
6729 }
6730 }
6731 }
6732 if (rmd_found) // member found -> copy line number info
6733 {
6734 md->setBodySegment(rmd_found->getDefLine(),rmd_found->getStartBodyLine(),rmd_found->getEndBodyLine());
6735 md->setBodyDef(rmd_found->getBodyDef());
6736 //md->setBodyMember(rmd);
6737 }
6738 }
6739 }
6740 if (!found) // line number could not be found or is available in this
6741 // entry
6742 {
6743 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
6744 md->setBodyDef(fd);
6745 }
6746
6747 //if (root->mGrpId!=-1)
6748 //{
6749 // md->setMemberGroup(memberGroupDict[root->mGrpId]);
6750 //}
6751 md->setMemberClass(cd);
6752 md->setMemberSpecifiers(spec);
6753 md->setDefinition(funcDecl);
6754 md->enableCallGraph(root->callGraph);
6755 md->enableCallerGraph(root->callerGraph);
6756 md->enableReferencedByRelation(root->referencedByRelation);
6757 md->enableReferencesRelation(root->referencesRelation);
6758 md->setDocumentation(root->doc,root->docFile,root->docLine);
6759 md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
6760 md->setDocsForDefinition(!root->proto);
6761 md->setPrototype(root->proto,root->fileName,root->startLine,root->startColumn);
6762 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
6763 md->addSectionsToDefinition(root->anchors);
6764 md->setMemberGroupId(root->mGrpId);
6765 md->setLanguage(root->lang);
6766 md->setId(root->id);
6767 //md->setMemberDefTemplateArguments(root->mtArgList);
6768 cd->insertMember(md.get());
6769 cd->insertUsedFile(fd);
6770 md->setRefItems(root->sli);
6771 if (root->relatesType == Duplicate) md->setRelatedAlso(cd);
6772 if (!mdDefine)
6773 {
6774 addMemberToGroups(root,md.get());
6775 }
6776 //printf("Adding member=%s\n",qPrint(md->name()));
6777 mn->push_back(std::move(md));
6778 }
6779 if (root->relatesType == Duplicate)
6780 {
6781 if (!findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec))
6782 {
6783 QCString fullFuncDecl=funcDecl;
6784 if (isFunc) fullFuncDecl+=argListToString(root->argList,TRUE);
6785 warn(root->fileName,root->startLine,
6786 "Cannot determine file/namespace for relatedalso function\n%s",
6787 qPrint(fullFuncDecl)
6788 );
6789 }
6790 }
6791 }
6792 else
6793 {
6794 warn_undoc(root->fileName,root->startLine,
6795 "class '%s' for related function '%s' is not "
6796 "documented.",
6797 qPrint(className),qPrint(funcName)
6798 );
6799 }
6800 }
6801 else if (root->parent() && root->parent()->section==Entry::OBJCIMPL_SEC)
6802 {
6803 addLocalObjCMethod(root,scopeName,funcType,funcName,funcArgs,exceptions,funcDecl,spec);
6804 }
6805 else // unrelated not overloaded member found
6806 {
6807 bool globMem = findGlobalMember(root,namespaceName,funcType,funcName,funcTempList,funcArgs,funcDecl,spec);
6808 if (className.isEmpty() && !globMem)
6809 {
6810 warn(root->fileName,root->startLine,
6811 "class for member '%s' cannot "
6812 "be found.", qPrint(funcName)
6813 );
6814 }
6815 else if (!className.isEmpty() && !globMem)
6816 {
6817 warn(root->fileName,root->startLine,
6818 "member '%s' of class '%s' cannot be found",
6819 qPrint(funcName),qPrint(className));
6820 }
6821 }
6822 }
6823 else
6824 {
6825 // this should not be called
6826 warn(root->fileName,root->startLine,
6827 "member with no name found.");
6828 }
6829 return;
6830 }
6831
6832 //----------------------------------------------------------------------
6833 // find the members corresponding to the different documentation blocks
6834 // that are extracted from the sources.
6835
filterMemberDocumentation(const Entry * root,const QCString & relates)6836 static void filterMemberDocumentation(const Entry *root,const QCString &relates)
6837 {
6838 int i=-1,l;
6839 Debug::print(Debug::FindMembers,0,
6840 "findMemberDocumentation(): root->type='%s' root->inside='%s' root->name='%s' root->args='%s' section=%x root->spec=%lld root->mGrpId=%d\n",
6841 qPrint(root->type),qPrint(root->inside),qPrint(root->name),qPrint(root->args),root->section,root->spec,root->mGrpId
6842 );
6843 //printf("root->parent()->name=%s\n",qPrint(root->parent()->name));
6844 bool isFunc=TRUE;
6845
6846 QCString type = root->type;
6847 QCString args = root->args;
6848 if ( // detect func variable/typedef to func ptr
6849 (i=findFunctionPtr(type.str(),root->lang,&l))!=-1
6850 )
6851 {
6852 //printf("Fixing function pointer!\n");
6853 // fix type and argument
6854 args.prepend(type.right(type.length()-i-l));
6855 type=type.left(i+l);
6856 //printf("Results type=%s,name=%s,args=%s\n",qPrint(type),qPrint(root->name),qPrint(args));
6857 isFunc=FALSE;
6858 }
6859 else if ((type.left(8)=="typedef " && args.find('(')!=-1))
6860 // detect function types marked as functions
6861 {
6862 isFunc=FALSE;
6863 }
6864
6865 //printf("Member %s isFunc=%d\n",qPrint(root->name),isFunc);
6866 if (root->section==Entry::MEMBERDOC_SEC)
6867 {
6868 //printf("Documentation for inline member '%s' found args='%s'\n",
6869 // qPrint(root->name),qPrint(args));
6870 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
6871 if (type.isEmpty())
6872 {
6873 findMember(root,
6874 relates,
6875 type,
6876 args,
6877 root->name + args + root->exception,
6878 FALSE,
6879 isFunc);
6880 }
6881 else
6882 {
6883 findMember(root,
6884 relates,
6885 type,
6886 args,
6887 type + " " + root->name + args + root->exception,
6888 FALSE,
6889 isFunc);
6890 }
6891 }
6892 else if (root->section==Entry::OVERLOADDOC_SEC)
6893 {
6894 //printf("Overloaded member %s found\n",qPrint(root->name));
6895 findMember(root,
6896 relates,
6897 type,
6898 args,
6899 root->name,
6900 TRUE,
6901 isFunc);
6902 }
6903 else if
6904 ((root->section==Entry::FUNCTION_SEC // function
6905 ||
6906 (root->section==Entry::VARIABLE_SEC && // variable
6907 !type.isEmpty() && // with a type
6908 g_compoundKeywords.find(type.str())==g_compoundKeywords.end() // that is not a keyword
6909 // (to skip forward declaration of class etc.)
6910 )
6911 )
6912 )
6913 {
6914 //printf("Documentation for member '%s' found args='%s' excp='%s'\n",
6915 // qPrint(root->name),qPrint(args),qPrint(root->exception));
6916 //if (relates.length()) printf(" Relates %s\n",qPrint(relates));
6917 //printf("Inside=%s\n Relates=%s\n",qPrint(root->inside),qPrint(relates));
6918 if (type=="friend class" || type=="friend struct" ||
6919 type=="friend union")
6920 {
6921 findMember(root,
6922 relates,
6923 type,
6924 args,
6925 type+" "+root->name,
6926 FALSE,FALSE);
6927
6928 }
6929 else if (!type.isEmpty())
6930 {
6931 findMember(root,
6932 relates,
6933 type,
6934 args,
6935 type+" "+ root->inside + root->name + args + root->exception,
6936 FALSE,isFunc);
6937 }
6938 else
6939 {
6940 findMember(root,
6941 relates,
6942 type,
6943 args,
6944 root->inside + root->name + args + root->exception,
6945 FALSE,isFunc);
6946 }
6947 }
6948 else if (root->section==Entry::DEFINE_SEC && !relates.isEmpty())
6949 {
6950 findMember(root,
6951 relates,
6952 type,
6953 args,
6954 root->name + args,
6955 FALSE,
6956 !args.isEmpty());
6957 }
6958 else if (root->section==Entry::VARIABLEDOC_SEC)
6959 {
6960 //printf("Documentation for variable %s found\n",qPrint(root->name));
6961 //if (!relates.isEmpty()) printf(" Relates %s\n",qPrint(relates));
6962 findMember(root,
6963 relates,
6964 type,
6965 args,
6966 root->name,
6967 FALSE,
6968 FALSE);
6969 }
6970 else if (root->section==Entry::EXPORTED_INTERFACE_SEC ||
6971 root->section==Entry::INCLUDED_SERVICE_SEC)
6972 {
6973 findMember(root,
6974 relates,
6975 type,
6976 args,
6977 type + " " + root->name,
6978 FALSE,
6979 FALSE);
6980 }
6981 else
6982 {
6983 // skip section
6984 //printf("skip section\n");
6985 }
6986 }
6987
findMemberDocumentation(const Entry * root)6988 static void findMemberDocumentation(const Entry *root)
6989 {
6990 if (root->section==Entry::MEMBERDOC_SEC ||
6991 root->section==Entry::OVERLOADDOC_SEC ||
6992 root->section==Entry::FUNCTION_SEC ||
6993 root->section==Entry::VARIABLE_SEC ||
6994 root->section==Entry::VARIABLEDOC_SEC ||
6995 root->section==Entry::DEFINE_SEC ||
6996 root->section==Entry::INCLUDED_SERVICE_SEC ||
6997 root->section==Entry::EXPORTED_INTERFACE_SEC
6998 )
6999 {
7000 if (root->relatesType == Duplicate && !root->relates.isEmpty())
7001 {
7002 filterMemberDocumentation(root,"");
7003 }
7004 filterMemberDocumentation(root,root->relates);
7005 }
7006 for (const auto &e : root->children())
7007 {
7008 if (e->section!=Entry::ENUM_SEC)
7009 {
7010 findMemberDocumentation(e.get());
7011 }
7012 }
7013 }
7014
7015 //----------------------------------------------------------------------
7016
findObjCMethodDefinitions(const Entry * root)7017 static void findObjCMethodDefinitions(const Entry *root)
7018 {
7019 for (const auto &objCImpl : root->children())
7020 {
7021 if (objCImpl->section==Entry::OBJCIMPL_SEC)
7022 {
7023 for (const auto &objCMethod : objCImpl->children())
7024 {
7025 if (objCMethod->section==Entry::FUNCTION_SEC)
7026 {
7027 //Printf(" Found ObjC method definition %s\n",qPrint(objCMethod->name));
7028 findMember(objCMethod.get(),
7029 objCMethod->relates,
7030 objCMethod->type,
7031 objCMethod->args,
7032 objCMethod->type+" "+objCImpl->name+"::"+objCMethod->name+" "+objCMethod->args,
7033 FALSE,TRUE);
7034 objCMethod->section=Entry::EMPTY_SEC;
7035 }
7036 }
7037 }
7038 }
7039 }
7040
7041 //----------------------------------------------------------------------
7042 // find and add the enumeration to their classes, namespaces or files
7043
findEnums(const Entry * root)7044 static void findEnums(const Entry *root)
7045 {
7046 if (root->section==Entry::ENUM_SEC)
7047 {
7048 ClassDefMutable *cd=0;
7049 FileDef *fd=0;
7050 NamespaceDefMutable *nd=0;
7051 MemberNameLinkedMap *mnsd=0;
7052 bool isGlobal;
7053 bool isRelated=FALSE;
7054 bool isMemberOf=FALSE;
7055 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7056 int i;
7057
7058 QCString name;
7059 QCString scope;
7060
7061 if ((i=root->name.findRev("::"))!=-1) // scope is specified
7062 {
7063 scope=root->name.left(i); // extract scope
7064 name=root->name.right(root->name.length()-i-2); // extract name
7065 if ((cd=getClassMutable(scope))==0) nd=getResolvedNamespaceMutable(scope);
7066 }
7067 else // no scope, check the scope in which the docs where found
7068 {
7069 if (( root->parent()->section & Entry::SCOPE_MASK )
7070 && !root->parent()->name.isEmpty()
7071 ) // found enum docs inside a compound
7072 {
7073 scope=root->parent()->name;
7074 if ((cd=getClassMutable(scope))==0) nd=getResolvedNamespaceMutable(scope);
7075 }
7076 name=root->name;
7077 }
7078
7079 if (!root->relates.isEmpty())
7080 { // related member, prefix user specified scope
7081 isRelated=TRUE;
7082 isMemberOf=(root->relatesType == MemberOf);
7083 if (getClass(root->relates)==0 && !scope.isEmpty())
7084 scope=mergeScopes(scope,root->relates);
7085 else
7086 scope=root->relates;
7087 if ((cd=getClassMutable(scope))==0) nd=getResolvedNamespaceMutable(scope);
7088 }
7089
7090 if (cd && !name.isEmpty()) // found a enum inside a compound
7091 {
7092 //printf("Enum '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7093 fd=0;
7094 mnsd=Doxygen::memberNameLinkedMap;
7095 isGlobal=FALSE;
7096 }
7097 else if (nd) // found enum inside namespace
7098 {
7099 mnsd=Doxygen::functionNameLinkedMap;
7100 isGlobal=TRUE;
7101 }
7102 else // found a global enum
7103 {
7104 fd=root->fileDef();
7105 mnsd=Doxygen::functionNameLinkedMap;
7106 isGlobal=TRUE;
7107 }
7108
7109 if (!name.isEmpty())
7110 {
7111 // new enum type
7112 std::unique_ptr<MemberDefMutable> md { createMemberDef(
7113 root->fileName,root->startLine,root->startColumn,
7114 QCString(),name,QCString(),QCString(),
7115 root->protection,Normal,FALSE,
7116 isMemberOf ? Foreign : isRelated ? Related : Member,
7117 MemberType_Enumeration,
7118 ArgumentList(),ArgumentList(),root->metaData) };
7119 md->setTagInfo(root->tagInfo());
7120 md->setLanguage(root->lang);
7121 md->setId(root->id);
7122 if (!isGlobal) md->setMemberClass(cd); else md->setFileDef(fd);
7123 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
7124 md->setBodyDef(root->fileDef());
7125 md->setMemberSpecifiers(root->spec);
7126 md->setEnumBaseType(root->args);
7127 //printf("Enum %s definition at line %d of %s: protection=%d scope=%s\n",
7128 // qPrint(root->name),root->bodyLine,qPrint(root->fileName),root->protection,cd?qPrint(cd->name()):"<none>");
7129 md->addSectionsToDefinition(root->anchors);
7130 md->setMemberGroupId(root->mGrpId);
7131 md->enableCallGraph(root->callGraph);
7132 md->enableCallerGraph(root->callerGraph);
7133 md->enableReferencedByRelation(root->referencedByRelation);
7134 md->enableReferencesRelation(root->referencesRelation);
7135 //printf("%s::setRefItems(%zu)\n",qPrint(md->name()),root->sli.size());
7136 md->setRefItems(root->sli);
7137 //printf("found enum %s nd=%p\n",qPrint(md->name()),nd);
7138 bool defSet=FALSE;
7139
7140 QCString baseType = root->args;
7141 if (!baseType.isEmpty())
7142 {
7143 baseType.prepend(" : ");
7144 }
7145
7146 if (nd)
7147 {
7148 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES))
7149 {
7150 md->setDefinition(name+baseType);
7151 }
7152 else
7153 {
7154 md->setDefinition(nd->name()+"::"+name+baseType);
7155 }
7156 //printf("definition=%s\n",md->definition());
7157 defSet=TRUE;
7158 md->setNamespace(nd);
7159 nd->insertMember(md.get());
7160 }
7161
7162 // even if we have already added the enum to a namespace, we still
7163 // also want to add it to other appropriate places such as file
7164 // or class.
7165 if (isGlobal && (nd==0 || !nd->isAnonymous()))
7166 {
7167 if (!defSet) md->setDefinition(name+baseType);
7168 if (fd==0 && root->parent())
7169 {
7170 fd=root->parent()->fileDef();
7171 }
7172 if (fd)
7173 {
7174 md->setFileDef(fd);
7175 fd->insertMember(md.get());
7176 }
7177 }
7178 else if (cd)
7179 {
7180 if (isRelated || Config_getBool(HIDE_SCOPE_NAMES))
7181 {
7182 md->setDefinition(name+baseType);
7183 }
7184 else
7185 {
7186 md->setDefinition(cd->name()+"::"+name+baseType);
7187 }
7188 cd->insertMember(md.get());
7189 cd->insertUsedFile(fd);
7190 }
7191 md->setDocumentation(root->doc,root->docFile,root->docLine);
7192 md->setDocsForDefinition(!root->proto);
7193 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7194 md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7195
7196 //printf("Adding member=%s\n",qPrint(md->name()));
7197 addMemberToGroups(root,md.get());
7198
7199 MemberName *mn = mnsd->add(name);
7200 mn->push_back(std::move(md));
7201 }
7202 }
7203 else
7204 {
7205 for (const auto &e : root->children()) findEnums(e.get());
7206 }
7207 }
7208
7209 //----------------------------------------------------------------------
7210
addEnumValuesToEnums(const Entry * root)7211 static void addEnumValuesToEnums(const Entry *root)
7212 {
7213 if (root->section==Entry::ENUM_SEC)
7214 // non anonymous enumeration
7215 {
7216 ClassDefMutable *cd=0;
7217 FileDef *fd=0;
7218 NamespaceDefMutable *nd=0;
7219 MemberNameLinkedMap *mnsd=0;
7220 bool isGlobal;
7221 bool isRelated=FALSE;
7222 //printf("Found enum with name '%s' relates=%s\n",qPrint(root->name),qPrint(root->relates));
7223 int i;
7224
7225 QCString name;
7226 QCString scope;
7227
7228 if ((i=root->name.findRev("::"))!=-1) // scope is specified
7229 {
7230 scope=root->name.left(i); // extract scope
7231 name=root->name.right(root->name.length()-i-2); // extract name
7232 if ((cd=getClassMutable(scope))==0) nd=getResolvedNamespaceMutable(scope);
7233 }
7234 else // no scope, check the scope in which the docs where found
7235 {
7236 if (( root->parent()->section & Entry::SCOPE_MASK )
7237 && !root->parent()->name.isEmpty()
7238 ) // found enum docs inside a compound
7239 {
7240 scope=root->parent()->name;
7241 if ((cd=getClassMutable(scope))==0) nd=getResolvedNamespaceMutable(scope);
7242 }
7243 name=root->name;
7244 }
7245
7246 if (!root->relates.isEmpty())
7247 { // related member, prefix user specified scope
7248 isRelated=TRUE;
7249 if (getClassMutable(root->relates)==0 && !scope.isEmpty())
7250 scope=mergeScopes(scope,root->relates);
7251 else
7252 scope=root->relates;
7253 if ((cd=getClassMutable(scope))==0) nd=getResolvedNamespaceMutable(scope);
7254 }
7255
7256 if (cd && !name.isEmpty()) // found a enum inside a compound
7257 {
7258 //printf("Enum in class '%s'::'%s'\n",qPrint(cd->name()),qPrint(name));
7259 fd=0;
7260 mnsd=Doxygen::memberNameLinkedMap;
7261 isGlobal=FALSE;
7262 }
7263 else if (nd && !nd->isAnonymous()) // found enum inside namespace
7264 {
7265 //printf("Enum in namespace '%s'::'%s'\n",qPrint(nd->name()),qPrint(name));
7266 mnsd=Doxygen::functionNameLinkedMap;
7267 isGlobal=TRUE;
7268 }
7269 else // found a global enum
7270 {
7271 fd=root->fileDef();
7272 //printf("Enum in file '%s': '%s'\n",qPrint(fd->name()),qPrint(name));
7273 mnsd=Doxygen::functionNameLinkedMap;
7274 isGlobal=TRUE;
7275 }
7276
7277 if (!name.isEmpty())
7278 {
7279 //printf("** name=%s\n",qPrint(name));
7280 MemberName *mn = mnsd->find(name); // for all members with this name
7281 if (mn)
7282 {
7283 struct EnumValueInfo
7284 {
7285 EnumValueInfo(const QCString &n,std::unique_ptr<MemberDefMutable> &md) :
7286 name(n), member(std::move(md)) {}
7287 QCString name;
7288 std::unique_ptr<MemberDefMutable> member;
7289 };
7290 std::vector< EnumValueInfo > extraMembers;
7291 // for each enum in this list
7292 for (const auto &imd : *mn)
7293 {
7294 MemberDefMutable *md = toMemberDefMutable(imd.get());
7295 // use raw pointer in this loop, since we modify mn and can then invalidate mdp.
7296 if (md && md->isEnumerate() && !root->children().empty())
7297 {
7298 //printf(" enum with %d children\n",root->children()->count());
7299 for (const auto &e : root->children())
7300 {
7301 SrcLangExt sle;
7302 if (
7303 (sle=root->lang)==SrcLangExt_CSharp ||
7304 sle==SrcLangExt_Java ||
7305 sle==SrcLangExt_XML ||
7306 (root->spec&Entry::Strong)
7307 )
7308 {
7309 // Unlike classic C/C++ enums, for C++11, C# & Java enum
7310 // values are only visible inside the enum scope, so we must create
7311 // them here and only add them to the enum
7312 //printf("md->qualifiedName()=%s e->name=%s tagInfo=%p name=%s\n",
7313 // qPrint(md->qualifiedName()),qPrint(e->name),e->tagInfo,qPrint(e->name));
7314 QCString qualifiedName = substitute(root->name,"::",".");
7315 if (!scope.isEmpty() && root->tagInfo())
7316 {
7317 qualifiedName=substitute(scope,"::",".")+"."+qualifiedName;
7318 }
7319 if (substitute(md->qualifiedName(),"::",".")== // TODO: add function to get canonical representation
7320 qualifiedName // enum value scope matches that of the enum
7321 )
7322 {
7323 QCString fileName = e->fileName;
7324 if (fileName.isEmpty() && e->tagInfo())
7325 {
7326 fileName = e->tagInfo()->tagName;
7327 }
7328 std::unique_ptr<MemberDefMutable> fmd { createMemberDef(
7329 fileName,e->startLine,e->startColumn,
7330 e->type,e->name,e->args,QCString(),
7331 e->protection, Normal,e->stat,Member,
7332 MemberType_EnumValue,ArgumentList(),ArgumentList(),e->metaData) };
7333 const NamespaceDef *mnd = md->getNamespaceDef();
7334 if (md->getClassDef())
7335 fmd->setMemberClass(md->getClassDef());
7336 else if (mnd && (mnd->isLinkable() || mnd->isAnonymous()))
7337 fmd->setNamespace(mnd);
7338 else if (md->getFileDef())
7339 fmd->setFileDef(md->getFileDef());
7340 fmd->setOuterScope(md->getOuterScope());
7341 fmd->setTagInfo(e->tagInfo());
7342 fmd->setLanguage(e->lang);
7343 fmd->setId(e->id);
7344 fmd->setDocumentation(e->doc,e->docFile,e->docLine);
7345 fmd->setBriefDescription(e->brief,e->briefFile,e->briefLine);
7346 fmd->addSectionsToDefinition(e->anchors);
7347 std::string init = e->initializer.str();
7348 fmd->setInitializer(init.c_str());
7349 fmd->setMaxInitLines(e->initLines);
7350 fmd->setMemberGroupId(e->mGrpId);
7351 fmd->setExplicitExternal(e->explicitExternal,fileName,e->startLine,e->startColumn);
7352 fmd->setRefItems(e->sli);
7353 fmd->setAnchor();
7354 md->insertEnumField(fmd.get());
7355 fmd->setEnumScope(md,TRUE);
7356 extraMembers.push_back(EnumValueInfo(e->name,fmd));
7357 }
7358 }
7359 else
7360 {
7361 //printf("e->name=%s isRelated=%d\n",qPrint(e->name),isRelated);
7362 MemberName *fmn=0;
7363 MemberNameLinkedMap *emnsd = isRelated ? Doxygen::functionNameLinkedMap : mnsd;
7364 if (!e->name.isEmpty() && (fmn=emnsd->find(e->name)))
7365 // get list of members with the same name as the field
7366 {
7367 for (const auto &ifmd : *fmn)
7368 {
7369 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
7370 if (fmd && fmd->isEnumValue() && fmd->getOuterScope()==md->getOuterScope()) // in same scope
7371 {
7372 //printf("found enum value with same name %s in scope %s\n",
7373 // qPrint(fmd->name()),qPrint(fmd->getOuterScope()->name()));
7374 if (nd && !nd->isAnonymous())
7375 {
7376 const NamespaceDef *fnd=fmd->getNamespaceDef();
7377 if (fnd==nd) // enum value is inside a namespace
7378 {
7379 md->insertEnumField(fmd);
7380 fmd->setEnumScope(md);
7381 }
7382 }
7383 else if (isGlobal)
7384 {
7385 const FileDef *ffd=fmd->getFileDef();
7386 if (ffd==fd) // enum value has file scope
7387 {
7388 md->insertEnumField(fmd);
7389 fmd->setEnumScope(md);
7390 }
7391 }
7392 else if (isRelated && cd) // reparent enum value to
7393 // match the enum's scope
7394 {
7395 md->insertEnumField(fmd); // add field def to list
7396 fmd->setEnumScope(md); // cross ref with enum name
7397 fmd->setEnumClassScope(cd); // cross ref with enum name
7398 fmd->setOuterScope(cd);
7399 fmd->makeRelated();
7400 cd->insertMember(fmd);
7401 }
7402 else
7403 {
7404 const ClassDef *fcd=fmd->getClassDef();
7405 if (fcd==cd) // enum value is inside a class
7406 {
7407 //printf("Inserting enum field %s in enum scope %s\n",
7408 // qPrint(fmd->name()),qPrint(md->name()));
7409 md->insertEnumField(fmd); // add field def to list
7410 fmd->setEnumScope(md); // cross ref with enum name
7411 }
7412 }
7413 }
7414 }
7415 }
7416 }
7417 }
7418 }
7419 }
7420 // move the newly added members into mn
7421 for (auto &e : extraMembers)
7422 {
7423 MemberName *emn=mnsd->add(e.name);
7424 emn->push_back(std::move(e.member));
7425 }
7426 }
7427 }
7428 }
7429 else
7430 {
7431 for (const auto &e : root->children()) addEnumValuesToEnums(e.get());
7432 }
7433 }
7434
7435 //----------------------------------------------------------------------
7436
addEnumDocs(const Entry * root,MemberDefMutable * md)7437 static void addEnumDocs(const Entry *root,MemberDefMutable *md)
7438 {
7439 // documentation outside a compound overrides the documentation inside it
7440 {
7441 md->setDocumentation(root->doc,root->docFile,root->docLine);
7442 md->setDocsForDefinition(!root->proto);
7443 }
7444
7445 // brief descriptions inside a compound override the documentation
7446 // outside it
7447 {
7448 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
7449 }
7450
7451 if (md->inbodyDocumentation().isEmpty() || !root->parent()->name.isEmpty())
7452 {
7453 md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
7454 }
7455
7456 if (root->mGrpId!=-1 && md->getMemberGroupId()==-1)
7457 {
7458 md->setMemberGroupId(root->mGrpId);
7459 }
7460
7461 md->addSectionsToDefinition(root->anchors);
7462 md->setRefItems(root->sli);
7463
7464 const GroupDef *gd=md->getGroupDef();
7465 if (gd==0 && !root->groups.empty()) // member not grouped but out-of-line documentation is
7466 {
7467 addMemberToGroups(root,md);
7468 }
7469 }
7470
7471
7472 //----------------------------------------------------------------------
7473 // find the documentation blocks for the enumerations
7474
findEnumDocumentation(const Entry * root)7475 static void findEnumDocumentation(const Entry *root)
7476 {
7477 if (root->section==Entry::ENUMDOC_SEC
7478 && !root->name.isEmpty()
7479 && root->name.at(0)!='@' // skip anonymous enums
7480 )
7481 {
7482 int i;
7483 QCString name;
7484 QCString scope;
7485 if ((i=root->name.findRev("::"))!=-1) // scope is specified as part of the name
7486 {
7487 name=root->name.right(root->name.length()-i-2); // extract name
7488 scope=root->name.left(i); // extract scope
7489 //printf("Scope='%s' Name='%s'\n",qPrint(scope),qPrint(name));
7490 }
7491 else // just the name
7492 {
7493 name=root->name;
7494 }
7495 if (( root->parent()->section & Entry::SCOPE_MASK )
7496 && !root->parent()->name.isEmpty()
7497 ) // found enum docs inside a compound
7498 {
7499 if (!scope.isEmpty()) scope.prepend("::");
7500 scope.prepend(root->parent()->name);
7501 }
7502 const ClassDef *cd = getClass(scope);
7503 const NamespaceDef *nd=Doxygen::namespaceLinkedMap->find(scope);
7504 const FileDef *fd = root->fileDef();
7505 Debug::print(Debug::FindMembers,0,"1. Found docs for enum with name '%s' and scope '%s' in context %s cd=%s, nd=%s fd=%s\n",
7506 qPrint(name),qPrint(scope),qPrint(root->parent()->name),
7507 cd?qPrint(cd->name()):"<none>",
7508 nd?qPrint(nd->name()):"<none>",
7509 fd?qPrint(fd->name()):"<none>");
7510
7511 if (!name.isEmpty())
7512 {
7513 bool found=FALSE;
7514 MemberName *mn;
7515 if (cd)
7516 {
7517 mn = Doxygen::memberNameLinkedMap->find(name);
7518 }
7519 else
7520 {
7521 mn = Doxygen::functionNameLinkedMap->find(name);
7522 }
7523 if (mn)
7524 {
7525 for (const auto &imd : *mn)
7526 {
7527 MemberDefMutable *md = toMemberDefMutable(imd.get());
7528 if (md && md->isEnumerate())
7529 {
7530 const ClassDef *mcd = md->getClassDef();
7531 const NamespaceDef *mnd = md->getNamespaceDef();
7532 const FileDef *mfd = md->getFileDef();
7533 if (cd && mcd==cd)
7534 {
7535 Debug::print(Debug::FindMembers,0,"2. Match found for class scope\n");
7536 addEnumDocs(root,md);
7537 found=TRUE;
7538 break;
7539 }
7540 else if (cd==0 && mcd==0 && nd!=0 && mnd==nd)
7541 {
7542 Debug::print(Debug::FindMembers,0,"2. Match found for namespace scope\n");
7543 addEnumDocs(root,md);
7544 found=TRUE;
7545 break;
7546 }
7547 else if (cd==0 && nd==0 && mcd==0 && mnd==0 && fd==mfd)
7548 {
7549 Debug::print(Debug::FindMembers,0,"2. Match found for global scope\n");
7550 addEnumDocs(root,md);
7551 found=TRUE;
7552 break;
7553 }
7554 }
7555 }
7556 }
7557 if (!found)
7558 {
7559 warn(root->fileName,root->startLine,
7560 "Documentation for undefined enum '%s' found.",
7561 qPrint(name)
7562 );
7563 }
7564 }
7565 }
7566 for (const auto &e : root->children()) findEnumDocumentation(e.get());
7567 }
7568
7569 // search for each enum (member or function) in mnl if it has documented
7570 // enum values.
findDEV(const MemberNameLinkedMap & mnsd)7571 static void findDEV(const MemberNameLinkedMap &mnsd)
7572 {
7573 // for each member name
7574 for (const auto &mn : mnsd)
7575 {
7576 // for each member definition
7577 for (const auto &imd : *mn)
7578 {
7579 MemberDefMutable *md = toMemberDefMutable(imd.get());
7580 if (md && md->isEnumerate()) // member is an enum
7581 {
7582 int documentedEnumValues=0;
7583 // for each enum value
7584 for (const auto &fmd : md->enumFieldList())
7585 {
7586 if (fmd->isLinkableInProject()) documentedEnumValues++;
7587 }
7588 // at least one enum value is documented
7589 if (documentedEnumValues>0) md->setDocumentedEnumValues(TRUE);
7590 }
7591 }
7592 }
7593 }
7594
7595 // search for each enum (member or function) if it has documented enum
7596 // values.
findDocumentedEnumValues()7597 static void findDocumentedEnumValues()
7598 {
7599 findDEV(*Doxygen::memberNameLinkedMap);
7600 findDEV(*Doxygen::functionNameLinkedMap);
7601 }
7602
7603 //----------------------------------------------------------------------
7604
addMembersToIndex()7605 static void addMembersToIndex()
7606 {
7607 // for each class member name
7608 for (const auto &mn : *Doxygen::memberNameLinkedMap)
7609 {
7610 // for each member definition
7611 for (const auto &md : *mn)
7612 {
7613 addClassMemberNameToIndex(md.get());
7614 }
7615 }
7616 // for each file/namespace function name
7617 for (const auto &mn : *Doxygen::functionNameLinkedMap)
7618 {
7619 // for each member definition
7620 for (const auto &md : *mn)
7621 {
7622 if (md->getNamespaceDef())
7623 {
7624 addNamespaceMemberNameToIndex(md.get());
7625 }
7626 else
7627 {
7628 addFileMemberNameToIndex(md.get());
7629 }
7630 }
7631 }
7632 sortMemberIndexLists();
7633 }
7634
7635 //----------------------------------------------------------------------
7636
addToIndices()7637 static void addToIndices()
7638 {
7639 for (const auto &cd : *Doxygen::classLinkedMap)
7640 {
7641 if (cd->isLinkableInProject())
7642 {
7643 Doxygen::indexList->addIndexItem(cd.get(),0);
7644 if (Doxygen::searchIndex)
7645 {
7646 Doxygen::searchIndex->setCurrentDoc(cd.get(),cd->anchor(),FALSE);
7647 Doxygen::searchIndex->addWord(cd->localName(),TRUE);
7648 }
7649 }
7650 }
7651
7652 for (const auto &cd : *Doxygen::conceptLinkedMap)
7653 {
7654 if (cd->isLinkableInProject())
7655 {
7656 Doxygen::indexList->addIndexItem(cd.get(),0);
7657 if (Doxygen::searchIndex)
7658 {
7659 Doxygen::searchIndex->setCurrentDoc(cd.get(),cd->anchor(),FALSE);
7660 Doxygen::searchIndex->addWord(cd->localName(),TRUE);
7661 }
7662 }
7663 }
7664
7665 for (const auto &nd : *Doxygen::namespaceLinkedMap)
7666 {
7667 if (nd->isLinkableInProject())
7668 {
7669 Doxygen::indexList->addIndexItem(nd.get(),0);
7670 if (Doxygen::searchIndex)
7671 {
7672 Doxygen::searchIndex->setCurrentDoc(nd.get(),nd->anchor(),FALSE);
7673 Doxygen::searchIndex->addWord(nd->localName(),TRUE);
7674 }
7675 }
7676 }
7677
7678 for (const auto &fn : *Doxygen::inputNameLinkedMap)
7679 {
7680 for (const auto &fd : *fn)
7681 {
7682 if (Doxygen::searchIndex && fd->isLinkableInProject())
7683 {
7684 Doxygen::searchIndex->setCurrentDoc(fd.get(),fd->anchor(),FALSE);
7685 Doxygen::searchIndex->addWord(fd->localName(),TRUE);
7686 }
7687 }
7688 }
7689
7690 for (const auto &gd : *Doxygen::groupLinkedMap)
7691 {
7692 if (gd->isLinkableInProject())
7693 {
7694 Doxygen::indexList->addIndexItem(gd.get(),0,QCString(),gd->groupTitle());
7695 if (Doxygen::searchIndex)
7696 {
7697 Doxygen::searchIndex->setCurrentDoc(gd.get(),gd->anchor(),FALSE);
7698 std::string title = gd->groupTitle().str();
7699 static const reg::Ex re(R"(\a[\w-]*)");
7700 reg::Iterator it(title,re);
7701 reg::Iterator end;
7702 for (; it!=end ; ++it)
7703 {
7704 const auto &match = *it;
7705 std::string matchStr = match.str();
7706 Doxygen::searchIndex->addWord(matchStr.c_str(),TRUE);
7707 }
7708 }
7709 }
7710 }
7711
7712 for (const auto &pd : *Doxygen::pageLinkedMap)
7713 {
7714 if (pd->isLinkableInProject())
7715 {
7716 Doxygen::indexList->addIndexItem(pd.get(),0,QCString(),filterTitle(pd->title().str()));
7717 }
7718 }
7719
7720 auto addMemberToSearchIndex = [](const MemberDef *md)
7721 {
7722 if (Doxygen::searchIndex)
7723 {
7724 Doxygen::searchIndex->setCurrentDoc(md,md->anchor(),FALSE);
7725 QCString ln=md->localName();
7726 QCString qn=md->qualifiedName();
7727 Doxygen::searchIndex->addWord(ln,TRUE);
7728 if (ln!=qn)
7729 {
7730 Doxygen::searchIndex->addWord(qn,TRUE);
7731 if (md->getClassDef())
7732 {
7733 Doxygen::searchIndex->addWord(md->getClassDef()->displayName(),TRUE);
7734 }
7735 if (md->getNamespaceDef())
7736 {
7737 Doxygen::searchIndex->addWord(md->getNamespaceDef()->displayName(),TRUE);
7738 }
7739 }
7740 }
7741 };
7742
7743 auto getScope = [](const MemberDef *md)
7744 {
7745 const Definition *scope = 0;
7746 if (md->getGroupDef()) scope = md->getGroupDef();
7747 else if (md->getClassDef()) scope = md->getClassDef();
7748 else if (md->getNamespaceDef()) scope = md->getNamespaceDef();
7749 else if (md->getFileDef()) scope = md->getFileDef();
7750 return scope;
7751 };
7752
7753 auto addMemberToIndices = [addMemberToSearchIndex,getScope](const MemberDef *md)
7754 {
7755 if (md->isLinkableInProject())
7756 {
7757 if (!(md->isEnumerate() && md->isAnonymous()))
7758 {
7759 Doxygen::indexList->addIndexItem(getScope(md),md);
7760 addMemberToSearchIndex(md);
7761 }
7762 if (md->isEnumerate())
7763 {
7764 for (const auto &fmd : md->enumFieldList())
7765 {
7766 Doxygen::indexList->addIndexItem(getScope(fmd),fmd);
7767 addMemberToSearchIndex(fmd);
7768 }
7769 }
7770 }
7771 };
7772
7773 // for each class member name
7774 for (const auto &mn : *Doxygen::memberNameLinkedMap)
7775 {
7776 // for each member definition
7777 for (const auto &md : *mn)
7778 {
7779 addMemberToIndices(md.get());
7780 }
7781 }
7782 // for each file/namespace function name
7783 for (const auto &mn : *Doxygen::functionNameLinkedMap)
7784 {
7785 // for each member definition
7786 for (const auto &md : *mn)
7787 {
7788 addMemberToIndices(md.get());
7789 }
7790 }
7791 }
7792
7793 //----------------------------------------------------------------------
7794
vhdlCorrectMemberProperties()7795 static void vhdlCorrectMemberProperties()
7796 {
7797 // for each member name
7798 for (const auto &mn : *Doxygen::memberNameLinkedMap)
7799 {
7800 // for each member definition
7801 for (const auto &imd : *mn)
7802 {
7803 MemberDefMutable *md = toMemberDefMutable(imd.get());
7804 if (md)
7805 {
7806 VhdlDocGen::correctMemberProperties(md);
7807 }
7808 }
7809 }
7810 // for each member name
7811 for (const auto &mn : *Doxygen::functionNameLinkedMap)
7812 {
7813 // for each member definition
7814 for (const auto &imd : *mn)
7815 {
7816 MemberDefMutable *md = toMemberDefMutable(imd.get());
7817 if (md)
7818 {
7819 VhdlDocGen::correctMemberProperties(md);
7820 }
7821 }
7822 }
7823 }
7824
7825
7826 //----------------------------------------------------------------------
7827 // computes the relation between all members. For each member 'm'
7828 // the members that override the implementation of 'm' are searched and
7829 // the member that 'm' overrides is searched.
7830
computeMemberRelations()7831 static void computeMemberRelations()
7832 {
7833 for (const auto &mn : *Doxygen::memberNameLinkedMap)
7834 {
7835 // for each member with a specific name
7836 for (const auto &imd : *mn)
7837 {
7838 MemberDefMutable *md = toMemberDefMutable(imd.get());
7839 if (md)
7840 {
7841 // for each other member with the same name
7842 for ( const auto &ibmd : *mn)
7843 {
7844 MemberDefMutable *bmd = toMemberDefMutable(ibmd.get());
7845 if (bmd && md!=bmd)
7846 {
7847 const ClassDef *mcd = md->getClassDef();
7848 if (mcd && !mcd->baseClasses().empty())
7849 {
7850 const ClassDef *bmcd = bmd->getClassDef();
7851 //printf("Check relation between '%s'::'%s' (%p) and '%s'::'%s' (%p)\n",
7852 // qPrint(mcd->name()),qPrint(md->name()),md.get(),
7853 // qPrint(bmcd->name()),qPrint(bmd->name()),bmd.get()
7854 // );
7855 if (bmcd && mcd && bmcd!=mcd &&
7856 (bmd->virtualness()!=Normal ||
7857 bmd->getLanguage()==SrcLangExt_Python || bmd->getLanguage()==SrcLangExt_Java || bmd->getLanguage()==SrcLangExt_PHP ||
7858 bmcd->compoundType()==ClassDef::Interface || bmcd->compoundType()==ClassDef::Protocol
7859 ) &&
7860 (md->isFunction() || md->isCSharpProperty()) &&
7861 mcd->isLinkable() &&
7862 bmcd->isLinkable() &&
7863 mcd->isBaseClass(bmcd,TRUE))
7864 {
7865 //printf(" derived scope\n");
7866 const ArgumentList &bmdAl = bmd->argumentList();
7867 const ArgumentList &mdAl = md->argumentList();
7868 //printf(" Base argList='%s'\n Super argList='%s'\n",
7869 // qPrint(argListToString(bmdAl)),
7870 // qPrint(argListToString(mdAl))
7871 // );
7872 if (
7873 bmd->getLanguage()==SrcLangExt_Python ||
7874 matchArguments2(bmd->getOuterScope(),bmd->getFileDef(),&bmdAl,
7875 md->getOuterScope(), md->getFileDef(), &mdAl,
7876 TRUE
7877 )
7878 )
7879 {
7880 //printf("match!\n");
7881 const MemberDef *rmd = md->reimplements();
7882 if (rmd==0 || minClassDistance(mcd,bmcd)<minClassDistance(mcd,rmd->getClassDef()))
7883 {
7884 //printf("setting (new) reimplements member\n");
7885 md->setReimplements(bmd);
7886 }
7887 //printf("%s: add reimplementedBy member %s\n",qPrint(bmcd->name()),qPrint(mcd->name()));
7888 bmd->insertReimplementedBy(md);
7889 }
7890 else
7891 {
7892 //printf("no match!\n");
7893 }
7894 }
7895 }
7896 }
7897 }
7898 }
7899 }
7900 }
7901 }
7902
7903 //----------------------------------------------------------------------------
7904
createTemplateInstanceMembers()7905 static void createTemplateInstanceMembers()
7906 {
7907 // for each class
7908 for (const auto &cd : *Doxygen::classLinkedMap)
7909 {
7910 // that is a template
7911 for (const auto &ti : cd->getTemplateInstances())
7912 {
7913 ClassDefMutable *tcdm = toClassDefMutable(ti.classDef);
7914 if (tcdm)
7915 {
7916 tcdm->addMembersToTemplateInstance(cd.get(),cd->templateArguments(),ti.templSpec);
7917 }
7918 }
7919 }
7920 }
7921
7922 //----------------------------------------------------------------------------
7923
mergeCategories()7924 static void mergeCategories()
7925 {
7926 // merge members of categories into the class they extend
7927 for (const auto &cd : *Doxygen::classLinkedMap)
7928 {
7929 int i=cd->name().find('(');
7930 if (i!=-1) // it is an Objective-C category
7931 {
7932 QCString baseName=cd->name().left(i);
7933 ClassDefMutable *baseClass=toClassDefMutable(Doxygen::classLinkedMap->find(baseName));
7934 if (baseClass)
7935 {
7936 //printf("*** merging members of category %s into %s\n",
7937 // qPrint(cd->name()),qPrint(baseClass->name()));
7938 baseClass->mergeCategory(cd.get());
7939 }
7940 }
7941 }
7942 }
7943
7944 // builds the list of all members for each class
7945
buildCompleteMemberLists()7946 static void buildCompleteMemberLists()
7947 {
7948 // merge the member list of base classes into the inherited classes.
7949 for (const auto &cd : *Doxygen::classLinkedMap)
7950 {
7951 if (// !cd->isReference() && // not an external class
7952 cd->subClasses().empty() && // is a root of the hierarchy
7953 !cd->baseClasses().empty()) // and has at least one base class
7954 {
7955 ClassDefMutable *cdm = toClassDefMutable(cd.get());
7956 if (cdm)
7957 {
7958 //printf("*** merging members for %s\n",qPrint(cd->name()));
7959 cdm->mergeMembers();
7960 }
7961 }
7962 }
7963 // now sort the member list of all members for all classes.
7964 for (const auto &cd : *Doxygen::classLinkedMap)
7965 {
7966 ClassDefMutable *cdm = toClassDefMutable(cd.get());
7967 if (cdm)
7968 {
7969 cdm->sortAllMembersList();
7970 }
7971 }
7972 }
7973
7974 //----------------------------------------------------------------------------
7975
generateFileSources()7976 static void generateFileSources()
7977 {
7978 if (!Doxygen::inputNameLinkedMap->empty())
7979 {
7980 #if USE_LIBCLANG
7981 if (Doxygen::clangAssistedParsing)
7982 {
7983 StringUnorderedSet processedFiles;
7984
7985 // create a dictionary with files to process
7986 StringUnorderedSet filesToProcess;
7987
7988 for (const auto &fn : *Doxygen::inputNameLinkedMap)
7989 {
7990 for (const auto &fd : *fn)
7991 {
7992 filesToProcess.insert(fd->absFilePath().str());
7993 }
7994 }
7995 // process source files (and their include dependencies)
7996 for (const auto &fn : *Doxygen::inputNameLinkedMap)
7997 {
7998 for (const auto &fd : *fn)
7999 {
8000 if (fd->isSource() && !fd->isReference() &&
8001 ((fd->generateSourceFile() && !g_useOutputTemplate) ||
8002 (!fd->isReference() && Doxygen::parseSourcesNeeded)
8003 )
8004 )
8005 {
8006 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8007 if (fd->generateSourceFile() && !g_useOutputTemplate) // sources need to be shown in the output
8008 {
8009 msg("Generating code for file %s...\n",qPrint(fd->docName()));
8010 clangParser->parse();
8011 fd->writeSourceHeader(*g_outputList);
8012 fd->writeSourceBody(*g_outputList,clangParser.get());
8013 fd->writeSourceFooter(*g_outputList);
8014 }
8015 else if (!fd->isReference() && Doxygen::parseSourcesNeeded)
8016 // we needed to parse the sources even if we do not show them
8017 {
8018 msg("Parsing code for file %s...\n",qPrint(fd->docName()));
8019 clangParser->parse();
8020 fd->parseSource(clangParser.get());
8021 }
8022
8023 for (auto incFile : clangParser->filesInSameTU())
8024 {
8025 if (filesToProcess.find(incFile)!=filesToProcess.end() && // part of input
8026 fd->absFilePath()!=QCString(incFile) && // not same file
8027 processedFiles.find(incFile)==processedFiles.end()) // not yet marked as processed
8028 {
8029 StringVector moreFiles;
8030 bool ambig;
8031 FileDef *ifd=findFileDef(Doxygen::inputNameLinkedMap,incFile.c_str(),ambig);
8032 if (ifd && !ifd->isReference())
8033 {
8034 if (ifd->generateSourceFile() && !g_useOutputTemplate) // sources need to be shown in the output
8035 {
8036 msg(" Generating code for file %s...\n",qPrint(ifd->docName()));
8037 ifd->writeSourceHeader(*g_outputList);
8038 ifd->writeSourceBody(*g_outputList,clangParser.get());
8039 ifd->writeSourceFooter(*g_outputList);
8040 }
8041 else if (!ifd->isReference() && Doxygen::parseSourcesNeeded)
8042 // we needed to parse the sources even if we do not show them
8043 {
8044 msg(" Parsing code for file %s...\n",qPrint(ifd->docName()));
8045 ifd->parseSource(clangParser.get());
8046 }
8047 processedFiles.insert(incFile);
8048 }
8049 }
8050 }
8051 processedFiles.insert(fd->absFilePath().str());
8052 }
8053 }
8054 }
8055 // process remaining files
8056 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8057 {
8058 for (const auto &fd : *fn)
8059 {
8060 if (processedFiles.find(fd->absFilePath().str())==processedFiles.end()) // not yet processed
8061 {
8062 if (fd->generateSourceFile() && !Htags::useHtags && !g_useOutputTemplate) // sources need to be shown in the output
8063 {
8064 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8065 msg("Generating code for file %s...\n",qPrint(fd->docName()));
8066 clangParser->parse();
8067 fd->writeSourceHeader(*g_outputList);
8068 fd->writeSourceBody(*g_outputList,clangParser.get());
8069 fd->writeSourceFooter(*g_outputList);
8070 }
8071 else if (!fd->isReference() && Doxygen::parseSourcesNeeded)
8072 // we needed to parse the sources even if we do not show them
8073 {
8074 auto clangParser = ClangParser::instance()->createTUParser(fd.get());
8075 msg("Parsing code for file %s...\n",qPrint(fd->docName()));
8076 clangParser->parse();
8077 fd->writeSourceHeader(*g_outputList);
8078 fd->writeSourceBody(*g_outputList,clangParser.get());
8079 fd->writeSourceFooter(*g_outputList);
8080 }
8081 }
8082 }
8083 }
8084 }
8085 else
8086 #endif
8087 {
8088 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8089 if (numThreads==0)
8090 {
8091 numThreads = std::thread::hardware_concurrency();
8092 }
8093 if (numThreads>1)
8094 {
8095 msg("Generating code files using %zu threads.\n",numThreads);
8096 struct SourceContext
8097 {
8098 SourceContext(FileDef *fd_,bool gen_,OutputList ol_)
8099 : fd(fd_), generateSourceFile(gen_), ol(ol_) {}
8100 FileDef *fd;
8101 bool generateSourceFile;
8102 OutputList ol;
8103 };
8104 ThreadPool threadPool(numThreads);
8105 std::vector< std::future< std::shared_ptr<SourceContext> > > results;
8106 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8107 {
8108 for (const auto &fd : *fn)
8109 {
8110 bool generateSourceFile = fd->generateSourceFile() && !Htags::useHtags && !g_useOutputTemplate;
8111 auto ctx = std::make_shared<SourceContext>(fd.get(),generateSourceFile,*g_outputList);
8112 if (generateSourceFile)
8113 {
8114 fd->writeSourceHeader(ctx->ol);
8115 }
8116 auto processFile = [ctx]() {
8117 if (ctx->generateSourceFile)
8118 {
8119 msg("Generating code for file %s...\n",qPrint(ctx->fd->docName()));
8120 }
8121 else
8122 {
8123 msg("Parsing code for file %s...\n",qPrint(ctx->fd->docName()));
8124 }
8125 StringVector filesInSameTu;
8126 ctx->fd->getAllIncludeFilesRecursively(filesInSameTu);
8127 if (ctx->generateSourceFile) // sources need to be shown in the output
8128 {
8129 ctx->fd->writeSourceBody(ctx->ol,nullptr);
8130 }
8131 else if (!ctx->fd->isReference() && Doxygen::parseSourcesNeeded)
8132 // we needed to parse the sources even if we do not show them
8133 {
8134 ctx->fd->parseSource(nullptr);
8135 }
8136 return ctx;
8137 };
8138 results.emplace_back(threadPool.queue(processFile));
8139 }
8140 }
8141 for (auto &f : results)
8142 {
8143 auto ctx = f.get();
8144 if (ctx->generateSourceFile)
8145 {
8146 ctx->fd->writeSourceFooter(ctx->ol);
8147 }
8148 }
8149 }
8150 else // single threaded version
8151 {
8152 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8153 {
8154 for (const auto &fd : *fn)
8155 {
8156 StringVector filesInSameTu;
8157 fd->getAllIncludeFilesRecursively(filesInSameTu);
8158 if (fd->generateSourceFile() && !Htags::useHtags && !g_useOutputTemplate) // sources need to be shown in the output
8159 {
8160 msg("Generating code for file %s...\n",qPrint(fd->docName()));
8161 fd->writeSourceHeader(*g_outputList);
8162 fd->writeSourceBody(*g_outputList,nullptr);
8163 fd->writeSourceFooter(*g_outputList);
8164 }
8165 else if (!fd->isReference() && Doxygen::parseSourcesNeeded)
8166 // we needed to parse the sources even if we do not show them
8167 {
8168 msg("Parsing code for file %s...\n",qPrint(fd->docName()));
8169 fd->parseSource(nullptr);
8170 }
8171 }
8172 }
8173 }
8174 }
8175 }
8176 }
8177
8178 //----------------------------------------------------------------------------
8179
generateFileDocs()8180 static void generateFileDocs()
8181 {
8182 if (documentedFiles==0) return;
8183
8184 if (!Doxygen::inputNameLinkedMap->empty())
8185 {
8186 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8187 if (numThreads==0)
8188 {
8189 numThreads = std::thread::hardware_concurrency();
8190 }
8191 if (numThreads>1) // multi threaded processing
8192 {
8193 struct DocContext
8194 {
8195 DocContext(FileDef *fd_,OutputList ol_)
8196 : fd(fd_), ol(ol_) {}
8197 FileDef *fd;
8198 OutputList ol;
8199 };
8200 ThreadPool threadPool(numThreads);
8201 std::vector< std::future< std::shared_ptr<DocContext> > > results;
8202 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8203 {
8204 for (const auto &fd : *fn)
8205 {
8206 bool doc = fd->isLinkableInProject();
8207 if (doc)
8208 {
8209 auto ctx = std::make_shared<DocContext>(fd.get(),*g_outputList);
8210 auto processFile = [ctx]() {
8211 msg("Generating docs for file %s...\n",qPrint(ctx->fd->docName()));
8212 ctx->fd->writeDocumentation(ctx->ol);
8213 return ctx;
8214 };
8215 results.emplace_back(threadPool.queue(processFile));
8216 }
8217 }
8218 }
8219 for (auto &f : results)
8220 {
8221 auto ctx = f.get();
8222 }
8223 }
8224 else // single threaded processing
8225 {
8226 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8227 {
8228 for (const auto &fd : *fn)
8229 {
8230 bool doc = fd->isLinkableInProject();
8231 if (doc)
8232 {
8233 msg("Generating docs for file %s...\n",qPrint(fd->docName()));
8234 fd->writeDocumentation(*g_outputList);
8235 }
8236 }
8237 }
8238 }
8239 }
8240 }
8241
8242 //----------------------------------------------------------------------------
8243
addSourceReferences()8244 static void addSourceReferences()
8245 {
8246 // add source references for class definitions
8247 for (const auto &cd : *Doxygen::classLinkedMap)
8248 {
8249 const FileDef *fd=cd->getBodyDef();
8250 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8251 {
8252 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),0);
8253 }
8254 }
8255 // add source references for concept definitions
8256 for (const auto &cd : *Doxygen::conceptLinkedMap)
8257 {
8258 const FileDef *fd=cd->getBodyDef();
8259 if (fd && cd->isLinkableInProject() && cd->getStartDefLine()!=-1)
8260 {
8261 const_cast<FileDef*>(fd)->addSourceRef(cd->getStartDefLine(),cd.get(),0);
8262 }
8263 }
8264 // add source references for namespace definitions
8265 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8266 {
8267 const FileDef *fd=nd->getBodyDef();
8268 if (fd && nd->isLinkableInProject() && nd->getStartDefLine()!=-1)
8269 {
8270 const_cast<FileDef*>(fd)->addSourceRef(nd->getStartDefLine(),nd.get(),0);
8271 }
8272 }
8273
8274 // add source references for member names
8275 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8276 {
8277 for (const auto &md : *mn)
8278 {
8279 //printf("class member %s: def=%s body=%d link?=%d\n",
8280 // qPrint(md->name()),
8281 // md->getBodyDef()?qPrint(md->getBodyDef()->name()):"<none>",
8282 // md->getStartBodyLine(),md->isLinkableInProject());
8283 const FileDef *fd=md->getBodyDef();
8284 if (fd &&
8285 md->getStartDefLine()!=-1 &&
8286 md->isLinkableInProject() &&
8287 (fd->generateSourceFile() || Doxygen::parseSourcesNeeded)
8288 )
8289 {
8290 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8291 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8292 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8293 }
8294 }
8295 }
8296 for (const auto &mn : *Doxygen::functionNameLinkedMap)
8297 {
8298 for (const auto &md : *mn)
8299 {
8300 const FileDef *fd=md->getBodyDef();
8301 //printf("member %s body=[%d,%d] fd=%p link=%d parseSources=%d\n",
8302 // qPrint(md->name()),
8303 // md->getStartBodyLine(),md->getEndBodyLine(),fd,
8304 // md->isLinkableInProject(),
8305 // Doxygen::parseSourcesNeeded);
8306 if (fd &&
8307 md->getStartDefLine()!=-1 &&
8308 md->isLinkableInProject() &&
8309 (fd->generateSourceFile() || Doxygen::parseSourcesNeeded)
8310 )
8311 {
8312 //printf("Found member '%s' in file '%s' at line '%d' def=%s\n",
8313 // qPrint(md->name()),qPrint(fd->name()),md->getStartBodyLine(),qPrint(md->getOuterScope()->name()));
8314 const_cast<FileDef*>(fd)->addSourceRef(md->getStartDefLine(),md->getOuterScope(),md.get());
8315 }
8316 }
8317 }
8318 }
8319
8320 //----------------------------------------------------------------------------
8321
8322 // add the macro definitions found during preprocessing as file members
buildDefineList()8323 static void buildDefineList()
8324 {
8325 for (const auto &s : g_inputFiles)
8326 {
8327 auto it = Doxygen::macroDefinitions.find(s);
8328 if (it!=Doxygen::macroDefinitions.end())
8329 {
8330 for (const auto &def : it->second)
8331 {
8332 std::unique_ptr<MemberDefMutable> md { createMemberDef(
8333 def.fileName,def.lineNr,def.columnNr,
8334 "#define",def.name,def.args,QCString(),
8335 Public,Normal,FALSE,Member,MemberType_Define,
8336 ArgumentList(),ArgumentList(),"") };
8337
8338 if (!def.args.isEmpty())
8339 {
8340 md->moveArgumentList(stringToArgumentList(SrcLangExt_Cpp, def.args));
8341 }
8342 md->setInitializer(def.definition);
8343 md->setFileDef(def.fileDef);
8344 md->setDefinition("#define "+def.name);
8345
8346 MemberName *mn=Doxygen::functionNameLinkedMap->add(def.name);
8347 if (def.fileDef)
8348 {
8349 def.fileDef->insertMember(md.get());
8350 }
8351 mn->push_back(std::move(md));
8352 }
8353 }
8354 }
8355 }
8356
8357 //----------------------------------------------------------------------------
8358
sortMemberLists()8359 static void sortMemberLists()
8360 {
8361 // sort class member lists
8362 for (const auto &cd : *Doxygen::classLinkedMap)
8363 {
8364 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8365 if (cdm)
8366 {
8367 cdm->sortMemberLists();
8368 }
8369 }
8370
8371 // sort namespace member lists
8372 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8373 {
8374 NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
8375 if (ndm)
8376 {
8377 ndm->sortMemberLists();
8378 }
8379 }
8380
8381 // sort file member lists
8382 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8383 {
8384 for (const auto &fd : *fn)
8385 {
8386 fd->sortMemberLists();
8387 }
8388 }
8389
8390 // sort group member lists
8391 for (const auto &gd : *Doxygen::groupLinkedMap)
8392 {
8393 gd->sortMemberLists();
8394 }
8395 }
8396
8397 //----------------------------------------------------------------------------
8398
isSymbolHidden(const Definition * d)8399 static bool isSymbolHidden(const Definition *d)
8400 {
8401 bool hidden = d->isHidden();
8402 const Definition *parent = d->getOuterScope();
8403 return parent ? hidden || isSymbolHidden(parent) : hidden;
8404 }
8405
computeTooltipTexts()8406 static void computeTooltipTexts()
8407 {
8408 for (const auto &kv : *Doxygen::symbolMap)
8409 {
8410 DefinitionMutable *dm = toDefinitionMutable(kv.second);
8411 if (dm && !isSymbolHidden(toDefinition(dm)) && toDefinition(dm)->isLinkableInProject())
8412 {
8413 dm->computeTooltip();
8414 }
8415 }
8416 }
8417
8418 //----------------------------------------------------------------------------
8419
setAnonymousEnumType()8420 static void setAnonymousEnumType()
8421 {
8422 for (const auto &cd : *Doxygen::classLinkedMap)
8423 {
8424 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8425 if (cdm)
8426 {
8427 cdm->setAnonymousEnumType();
8428 }
8429 }
8430 }
8431
8432 //----------------------------------------------------------------------------
8433
countMembers()8434 static void countMembers()
8435 {
8436 for (const auto &cd : *Doxygen::classLinkedMap)
8437 {
8438 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8439 if (cdm)
8440 {
8441 cdm->countMembers();
8442 }
8443 }
8444
8445 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8446 {
8447 NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
8448 if (ndm)
8449 {
8450 ndm->countMembers();
8451 }
8452 }
8453
8454 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8455 {
8456 for (const auto &fd : *fn)
8457 {
8458 fd->countMembers();
8459 }
8460 }
8461
8462 for (const auto &gd : *Doxygen::groupLinkedMap)
8463 {
8464 gd->countMembers();
8465 }
8466 }
8467
8468
8469 //----------------------------------------------------------------------------
8470 // generate the documentation of all classes
8471
generateClassList(const ClassLinkedMap & classList)8472 static void generateClassList(const ClassLinkedMap &classList)
8473 {
8474 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
8475 if (numThreads==0)
8476 {
8477 numThreads = std::thread::hardware_concurrency();
8478 }
8479 if (numThreads>1) // multi threaded processing
8480 {
8481 struct DocContext
8482 {
8483 DocContext(ClassDefMutable *cd_,OutputList ol_)
8484 : cd(cd_), ol(ol_) {}
8485 ClassDefMutable *cd;
8486 OutputList ol;
8487 };
8488 ThreadPool threadPool(numThreads);
8489 std::vector< std::future< std::shared_ptr<DocContext> > > results;
8490 for (const auto &cdi : classList)
8491 {
8492 ClassDefMutable *cd=toClassDefMutable(cdi.get());
8493
8494 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
8495 if (cd &&
8496 (cd->getOuterScope()==0 || // <-- should not happen, but can if we read an old tag file
8497 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
8498 ) && !cd->isHidden() && !cd->isEmbeddedInOuterScope()
8499 )
8500 {
8501 auto ctx = std::make_shared<DocContext>(cd,*g_outputList);
8502 auto processFile = [ctx]()
8503 {
8504 msg("Generating docs for compound %s...\n",qPrint(ctx->cd->name()));
8505
8506 // skip external references, anonymous compounds and
8507 // template instances
8508 if ( ctx->cd->isLinkableInProject() && ctx->cd->templateMaster()==0)
8509 {
8510 ctx->cd->writeDocumentation(ctx->ol);
8511 ctx->cd->writeMemberList(ctx->ol);
8512 }
8513
8514 // even for undocumented classes, the inner classes can be documented.
8515 ctx->cd->writeDocumentationForInnerClasses(ctx->ol);
8516 return ctx;
8517 };
8518 results.emplace_back(threadPool.queue(processFile));
8519 }
8520 }
8521 for (auto &f : results)
8522 {
8523 auto ctx = f.get();
8524 }
8525 }
8526 else // single threaded processing
8527 {
8528 for (const auto &cdi : classList)
8529 {
8530 ClassDefMutable *cd=toClassDefMutable(cdi.get());
8531
8532 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
8533 if (cd &&
8534 (cd->getOuterScope()==0 || // <-- should not happen, but can if we read an old tag file
8535 cd->getOuterScope()==Doxygen::globalScope // only look at global classes
8536 ) && !cd->isHidden() && !cd->isEmbeddedInOuterScope()
8537 )
8538 {
8539 // skip external references, anonymous compounds and
8540 // template instances
8541 if ( cd->isLinkableInProject() && cd->templateMaster()==0)
8542 {
8543 msg("Generating docs for compound %s...\n",qPrint(cd->name()));
8544
8545 cd->writeDocumentation(*g_outputList);
8546 cd->writeMemberList(*g_outputList);
8547 }
8548 // even for undocumented classes, the inner classes can be documented.
8549 cd->writeDocumentationForInnerClasses(*g_outputList);
8550 }
8551 }
8552 }
8553 }
8554
generateClassDocs()8555 static void generateClassDocs()
8556 {
8557 generateClassList(*Doxygen::classLinkedMap);
8558 generateClassList(*Doxygen::hiddenClassLinkedMap);
8559 }
8560
8561 //----------------------------------------------------------------------------
8562
generateConceptDocs()8563 static void generateConceptDocs()
8564 {
8565 for (const auto &cdi : *Doxygen::conceptLinkedMap)
8566 {
8567 ConceptDefMutable *cd=toConceptDefMutable(cdi.get());
8568
8569 //printf("cd=%s getOuterScope=%p global=%p\n",qPrint(cd->name()),cd->getOuterScope(),Doxygen::globalScope);
8570 if (cd &&
8571 (cd->getOuterScope()==0 || // <-- should not happen, but can if we read an old tag file
8572 cd->getOuterScope()==Doxygen::globalScope // only look at global concepts
8573 ) && !cd->isHidden() && cd->isLinkableInProject()
8574 )
8575 {
8576 msg("Generating docs for concept %s...\n",qPrint(cd->name()));
8577 cd->writeDocumentation(*g_outputList);
8578 }
8579 }
8580 }
8581
8582 //----------------------------------------------------------------------------
8583
inheritDocumentation()8584 static void inheritDocumentation()
8585 {
8586 for (const auto &mn : *Doxygen::memberNameLinkedMap)
8587 {
8588 for (const auto &imd : *mn)
8589 {
8590 MemberDefMutable *md = toMemberDefMutable(imd.get());
8591 //static int count=0;
8592 //printf("%04d Member '%s'\n",count++,qPrint(md->qualifiedName()));
8593 if (md && md->documentation().isEmpty() && md->briefDescription().isEmpty())
8594 { // no documentation yet
8595 const MemberDef *bmd = md->reimplements();
8596 while (bmd && bmd->documentation().isEmpty() &&
8597 bmd->briefDescription().isEmpty()
8598 )
8599 { // search up the inheritance tree for a documentation member
8600 //printf("bmd=%s class=%s\n",qPrint(bmd->name()),qPrint(bmd->getClassDef()->name()));
8601 bmd = bmd->reimplements();
8602 }
8603 if (bmd) // copy the documentation from the reimplemented member
8604 {
8605 md->setInheritsDocsFrom(bmd);
8606 md->setDocumentation(bmd->documentation(),bmd->docFile(),bmd->docLine());
8607 md->setDocsForDefinition(bmd->isDocsForDefinition());
8608 md->setBriefDescription(bmd->briefDescription(),bmd->briefFile(),bmd->briefLine());
8609 md->copyArgumentNames(bmd);
8610 md->setInbodyDocumentation(bmd->inbodyDocumentation(),bmd->inbodyFile(),bmd->inbodyLine());
8611 }
8612 }
8613 }
8614 }
8615 }
8616
8617 //----------------------------------------------------------------------------
8618
combineUsingRelations()8619 static void combineUsingRelations()
8620 {
8621 // for each file
8622 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8623 {
8624 for (const auto &fd : *fn)
8625 {
8626 fd->combineUsingRelations();
8627 }
8628 }
8629
8630 // for each namespace
8631 NamespaceDefSet visitedNamespaces;
8632 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8633 {
8634 NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
8635 if (ndm)
8636 {
8637 ndm->combineUsingRelations(visitedNamespaces);
8638 }
8639 }
8640 }
8641
8642 //----------------------------------------------------------------------------
8643
addMembersToMemberGroup()8644 static void addMembersToMemberGroup()
8645 {
8646 // for each class
8647 for (const auto &cd : *Doxygen::classLinkedMap)
8648 {
8649 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8650 if (cdm)
8651 {
8652 cdm->addMembersToMemberGroup();
8653 }
8654 }
8655 // for each file
8656 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8657 {
8658 for (const auto &fd : *fn)
8659 {
8660 fd->addMembersToMemberGroup();
8661 }
8662 }
8663 // for each namespace
8664 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8665 {
8666 NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
8667 if (ndm)
8668 {
8669 ndm->addMembersToMemberGroup();
8670 }
8671 }
8672 // for each group
8673 for (const auto &gd : *Doxygen::groupLinkedMap)
8674 {
8675 gd->addMembersToMemberGroup();
8676 }
8677 }
8678
8679 //----------------------------------------------------------------------------
8680
distributeMemberGroupDocumentation()8681 static void distributeMemberGroupDocumentation()
8682 {
8683 // for each class
8684 for (const auto &cd : *Doxygen::classLinkedMap)
8685 {
8686 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8687 if (cdm)
8688 {
8689 cdm->distributeMemberGroupDocumentation();
8690 }
8691 }
8692 // for each file
8693 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8694 {
8695 for (const auto &fd : *fn)
8696 {
8697 fd->distributeMemberGroupDocumentation();
8698 }
8699 }
8700 // for each namespace
8701 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8702 {
8703 NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
8704 if (ndm)
8705 {
8706 ndm->distributeMemberGroupDocumentation();
8707 }
8708 }
8709 // for each group
8710 for (const auto &gd : *Doxygen::groupLinkedMap)
8711 {
8712 gd->distributeMemberGroupDocumentation();
8713 }
8714 }
8715
8716 //----------------------------------------------------------------------------
8717
findSectionsInDocumentation()8718 static void findSectionsInDocumentation()
8719 {
8720 // for each class
8721 for (const auto &cd : *Doxygen::classLinkedMap)
8722 {
8723 ClassDefMutable *cdm = toClassDefMutable(cd.get());
8724 if (cdm)
8725 {
8726 cdm->findSectionsInDocumentation();
8727 }
8728 }
8729 // for each concept
8730 for (const auto &cd : *Doxygen::conceptLinkedMap)
8731 {
8732 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
8733 if (cdm)
8734 {
8735 cdm->findSectionsInDocumentation();
8736 }
8737 }
8738 // for each file
8739 for (const auto &fn : *Doxygen::inputNameLinkedMap)
8740 {
8741 for (const auto &fd : *fn)
8742 {
8743 fd->findSectionsInDocumentation();
8744 }
8745 }
8746 // for each namespace
8747 for (const auto &nd : *Doxygen::namespaceLinkedMap)
8748 {
8749 NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
8750 if (ndm)
8751 {
8752 ndm->findSectionsInDocumentation();
8753 }
8754 }
8755 // for each group
8756 for (const auto &gd : *Doxygen::groupLinkedMap)
8757 {
8758 gd->findSectionsInDocumentation();
8759 }
8760 // for each page
8761 for (const auto &pd : *Doxygen::pageLinkedMap)
8762 {
8763 pd->findSectionsInDocumentation();
8764 }
8765 if (Doxygen::mainPage) Doxygen::mainPage->findSectionsInDocumentation();
8766 }
8767
8768 //----------------------------------------------------------------------
8769
8770
flushCachedTemplateRelations()8771 static void flushCachedTemplateRelations()
8772 {
8773 // remove all references to classes from the cache
8774 // as there can be new template instances in the inheritance path
8775 // to this class. Optimization: only remove those classes that
8776 // have inheritance instances as direct or indirect sub classes.
8777 StringVector elementsToRemove;
8778 for (const auto &ci : *Doxygen::lookupCache)
8779 {
8780 const LookupInfo &li = ci.second;
8781 if (li.classDef)
8782 {
8783 elementsToRemove.push_back(ci.first);
8784 }
8785 }
8786 for (const auto &k : elementsToRemove)
8787 {
8788 Doxygen::lookupCache->remove(k);
8789 }
8790
8791 // remove all cached typedef resolutions whose target is a
8792 // template class as this may now be a template instance
8793 // for each global function name
8794 for (const auto &fn : *Doxygen::functionNameLinkedMap)
8795 {
8796 // for each function with that name
8797 for (const auto &ifmd : *fn)
8798 {
8799 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
8800 if (fmd && fmd->isTypedefValCached())
8801 {
8802 const ClassDef *cd = fmd->getCachedTypedefVal();
8803 if (cd->isTemplate()) fmd->invalidateTypedefValCache();
8804 }
8805 }
8806 }
8807 // for each class method name
8808 for (const auto &nm : *Doxygen::memberNameLinkedMap)
8809 {
8810 // for each function with that name
8811 for (const auto &imd : *nm)
8812 {
8813 MemberDefMutable *md = toMemberDefMutable(imd.get());
8814 if (md && md->isTypedefValCached())
8815 {
8816 const ClassDef *cd = md->getCachedTypedefVal();
8817 if (cd->isTemplate()) md->invalidateTypedefValCache();
8818 }
8819 }
8820 }
8821 }
8822
8823 //----------------------------------------------------------------------------
8824
flushUnresolvedRelations()8825 static void flushUnresolvedRelations()
8826 {
8827 // Remove all unresolved references to classes from the cache.
8828 // This is needed before resolving the inheritance relations, since
8829 // it would otherwise not find the inheritance relation
8830 // for C in the example below, as B::I was already found to be unresolvable
8831 // (which is correct if you ignore the inheritance relation between A and B).
8832 //
8833 // class A { class I {} };
8834 // class B : public A {};
8835 // class C : public B::I {};
8836
8837 StringVector elementsToRemove;
8838 for (const auto &ci : *Doxygen::lookupCache)
8839 {
8840 const LookupInfo &li = ci.second;
8841 if (li.classDef==0 && li.typeDef==0)
8842 {
8843 elementsToRemove.push_back(ci.first);
8844 }
8845 }
8846 for (const auto &k : elementsToRemove)
8847 {
8848 Doxygen::lookupCache->remove(k);
8849 }
8850
8851 // for each global function name
8852 for (const auto &fn : *Doxygen::functionNameLinkedMap)
8853 {
8854 // for each function with that name
8855 for (const auto &ifmd : *fn)
8856 {
8857 MemberDefMutable *fmd = toMemberDefMutable(ifmd.get());
8858 if (fmd)
8859 {
8860 fmd->invalidateCachedArgumentTypes();
8861 }
8862 }
8863 }
8864 // for each class method name
8865 for (const auto &nm : *Doxygen::memberNameLinkedMap)
8866 {
8867 // for each function with that name
8868 for (const auto &imd : *nm)
8869 {
8870 MemberDefMutable *md = toMemberDefMutable(imd.get());
8871 if (md)
8872 {
8873 md->invalidateCachedArgumentTypes();
8874 }
8875 }
8876 }
8877
8878 }
8879
8880 //----------------------------------------------------------------------------
8881
findDefineDocumentation(Entry * root)8882 static void findDefineDocumentation(Entry *root)
8883 {
8884 if ((root->section==Entry::DEFINEDOC_SEC ||
8885 root->section==Entry::DEFINE_SEC) && !root->name.isEmpty()
8886 )
8887 {
8888 //printf("found define '%s' '%s' brief='%s' doc='%s'\n",
8889 // qPrint(root->name),qPrint(root->args),qPrint(root->brief),qPrint(root->doc));
8890
8891 if (root->tagInfo() && !root->name.isEmpty()) // define read from a tag file
8892 {
8893 std::unique_ptr<MemberDefMutable> md { createMemberDef(root->tagInfo()->tagName,1,1,
8894 "#define",root->name,root->args,QCString(),
8895 Public,Normal,FALSE,Member,MemberType_Define,
8896 ArgumentList(),ArgumentList(),"") };
8897 md->setTagInfo(root->tagInfo());
8898 md->setLanguage(root->lang);
8899 //printf("Searching for '%s' fd=%p\n",qPrint(filePathName),fd);
8900 md->setFileDef(root->parent()->fileDef());
8901 //printf("Adding member=%s\n",qPrint(md->name()));
8902 MemberName *mn = Doxygen::functionNameLinkedMap->add(root->name);
8903 mn->push_back(std::move(md));
8904 }
8905 MemberName *mn=Doxygen::functionNameLinkedMap->find(root->name);
8906 if (mn)
8907 {
8908 int count=0;
8909 for (const auto &md : *mn)
8910 {
8911 if (md->memberType()==MemberType_Define) count++;
8912 }
8913 if (count==1)
8914 {
8915 for (const auto &imd : *mn)
8916 {
8917 MemberDefMutable *md = toMemberDefMutable(imd.get());
8918 if (md && md->memberType()==MemberType_Define)
8919 {
8920 md->setDocumentation(root->doc,root->docFile,root->docLine);
8921 md->setDocsForDefinition(!root->proto);
8922 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
8923 if (md->inbodyDocumentation().isEmpty())
8924 {
8925 md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
8926 }
8927 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
8928 md->setBodyDef(root->fileDef());
8929 md->addSectionsToDefinition(root->anchors);
8930 md->setMaxInitLines(root->initLines);
8931 md->setRefItems(root->sli);
8932 if (root->mGrpId!=-1) md->setMemberGroupId(root->mGrpId);
8933 addMemberToGroups(root,md);
8934 }
8935 }
8936 }
8937 else if (count>1 &&
8938 (!root->doc.isEmpty() ||
8939 !root->brief.isEmpty() ||
8940 root->bodyLine!=-1
8941 )
8942 )
8943 // multiple defines don't know where to add docs
8944 // but maybe they are in different files together with their documentation
8945 {
8946 for (const auto &imd : *mn)
8947 {
8948 MemberDefMutable *md = toMemberDefMutable(imd.get());
8949 if (md && md->memberType()==MemberType_Define)
8950 {
8951 const FileDef *fd=md->getFileDef();
8952 if (fd && fd->absFilePath()==root->fileName)
8953 // doc and define in the same file assume they belong together.
8954 {
8955 md->setDocumentation(root->doc,root->docFile,root->docLine);
8956 md->setDocsForDefinition(!root->proto);
8957 md->setBriefDescription(root->brief,root->briefFile,root->briefLine);
8958 if (md->inbodyDocumentation().isEmpty())
8959 {
8960 md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine);
8961 }
8962 md->setBodySegment(root->startLine,root->bodyLine,root->endBodyLine);
8963 md->setBodyDef(root->fileDef());
8964 md->addSectionsToDefinition(root->anchors);
8965 md->setRefItems(root->sli);
8966 md->setLanguage(root->lang);
8967 if (root->mGrpId!=-1) md->setMemberGroupId(root->mGrpId);
8968 addMemberToGroups(root,md);
8969 }
8970 }
8971 }
8972 //warn("define %s found in the following files:\n",qPrint(root->name));
8973 //warn("Cannot determine where to add the documentation found "
8974 // "at line %d of file %s. \n",
8975 // root->startLine,qPrint(root->fileName));
8976 }
8977 }
8978 else if (!root->doc.isEmpty() || !root->brief.isEmpty()) // define not found
8979 {
8980 static bool preEnabled = Config_getBool(ENABLE_PREPROCESSING);
8981 if (preEnabled)
8982 {
8983 warn(root->fileName,root->startLine,
8984 "documentation for unknown define %s found.\n",
8985 qPrint(root->name)
8986 );
8987 }
8988 else
8989 {
8990 warn(root->fileName,root->startLine,
8991 "found documented #define %s but ignoring it because "
8992 "ENABLE_PREPROCESSING is NO.\n",
8993 qPrint(root->name)
8994 );
8995 }
8996 }
8997 }
8998 for (const auto &e : root->children()) findDefineDocumentation(e.get());
8999 }
9000
9001 //----------------------------------------------------------------------------
9002
findDirDocumentation(const Entry * root)9003 static void findDirDocumentation(const Entry *root)
9004 {
9005 if (root->section == Entry::DIRDOC_SEC)
9006 {
9007 QCString normalizedName = root->name;
9008 normalizedName = substitute(normalizedName,"\\","/");
9009 //printf("root->docFile=%s normalizedName=%s\n",
9010 // qPrint(root->docFile),qPrint(normalizedName));
9011 if (root->docFile==normalizedName) // current dir?
9012 {
9013 int lastSlashPos=normalizedName.findRev('/');
9014 if (lastSlashPos!=-1) // strip file name
9015 {
9016 normalizedName=normalizedName.left(lastSlashPos);
9017 }
9018 }
9019 if (normalizedName.at(normalizedName.length()-1)!='/')
9020 {
9021 normalizedName+='/';
9022 }
9023 DirDef *matchingDir=0;
9024 for (const auto &dir : *Doxygen::dirLinkedMap)
9025 {
9026 //printf("Dir: %s<->%s\n",qPrint(dir->name()),qPrint(normalizedName));
9027 if (dir->name().right(normalizedName.length())==normalizedName)
9028 {
9029 if (matchingDir)
9030 {
9031 warn(root->fileName,root->startLine,
9032 "\\dir command matches multiple directories.\n"
9033 " Applying the command for directory %s\n"
9034 " Ignoring the command for directory %s\n",
9035 qPrint(matchingDir->name()),qPrint(dir->name())
9036 );
9037 }
9038 else
9039 {
9040 matchingDir=dir.get();
9041 }
9042 }
9043 }
9044 if (matchingDir)
9045 {
9046 //printf("Match for with dir %s\n",qPrint(matchingDir->name()));
9047 matchingDir->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9048 matchingDir->setDocumentation(root->doc,root->docFile,root->docLine);
9049 matchingDir->setRefItems(root->sli);
9050 addDirToGroups(root,matchingDir);
9051 }
9052 else
9053 {
9054 warn(root->fileName,root->startLine,"No matching "
9055 "directory found for command \\dir %s\n",qPrint(normalizedName));
9056 }
9057 }
9058 for (const auto &e : root->children()) findDirDocumentation(e.get());
9059 }
9060
9061
9062 //----------------------------------------------------------------------------
9063 // create a (sorted) list of separate documentation pages
9064
buildPageList(Entry * root)9065 static void buildPageList(Entry *root)
9066 {
9067 if (root->section == Entry::PAGEDOC_SEC)
9068 {
9069 if (!root->name.isEmpty())
9070 {
9071 addRelatedPage(root);
9072 }
9073 }
9074 else if (root->section == Entry::MAINPAGEDOC_SEC)
9075 {
9076 QCString title=root->args.stripWhiteSpace();
9077 if (title.isEmpty()) title=theTranslator->trMainPage();
9078 //QCString name = Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9079 QCString name = "index";
9080 addRefItem(root->sli,
9081 name,
9082 "page",
9083 name,
9084 title,
9085 QCString(),0
9086 );
9087 }
9088 for (const auto &e : root->children()) buildPageList(e.get());
9089 }
9090
9091 // search for the main page defined in this project
findMainPage(Entry * root)9092 static void findMainPage(Entry *root)
9093 {
9094 if (root->section == Entry::MAINPAGEDOC_SEC)
9095 {
9096 if (Doxygen::mainPage==0 && root->tagInfo()==0)
9097 {
9098 //printf("mainpage: docLine=%d startLine=%d\n",root->docLine,root->startLine);
9099 //printf("Found main page! \n======\n%s\n=======\n",qPrint(root->doc));
9100 QCString title=root->args.stripWhiteSpace();
9101 if (title.isEmpty()) title = Config_getString(PROJECT_NAME);
9102 //QCString indexName=Config_getBool(GENERATE_TREEVIEW)?"main":"index";
9103 QCString indexName="index";
9104 Doxygen::mainPage.reset(createPageDef(root->docFile,root->docLine,
9105 indexName, root->brief+root->doc+root->inbodyDocs,title));
9106 //setFileNameForSections(root->anchors,"index",Doxygen::mainPage);
9107 Doxygen::mainPage->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9108 Doxygen::mainPage->setBodySegment(root->startLine,root->startLine,-1);
9109 Doxygen::mainPage->setFileName(indexName);
9110 Doxygen::mainPage->setLocalToc(root->localToc);
9111 addPageToContext(Doxygen::mainPage.get(),root);
9112
9113 const SectionInfo *si = SectionManager::instance().find(Doxygen::mainPage->name());
9114 if (si)
9115 {
9116 if (!si->ref().isEmpty()) // we are from a tag file
9117 {
9118 // a page name is a label as well! but should no be double either
9119 SectionManager::instance().replace(
9120 Doxygen::mainPage->name(),
9121 indexName,
9122 root->startLine,
9123 Doxygen::mainPage->title(),
9124 SectionType::Page,
9125 0); // level 0
9126 }
9127 else if (si->lineNr() != -1)
9128 {
9129 warn(root->fileName,root->startLine,"multiple use of section label '%s' for main page, (first occurrence: %s, line %d)",
9130 qPrint(Doxygen::mainPage->name()),qPrint(si->fileName()),si->lineNr());
9131 }
9132 else
9133 {
9134 warn(root->fileName,root->startLine,"multiple use of section label '%s' for main page, (first occurrence: %s)",
9135 qPrint(Doxygen::mainPage->name()),qPrint(si->fileName()));
9136 }
9137 }
9138 else
9139 {
9140 // a page name is a label as well! but should no be double either
9141 SectionManager::instance().add(
9142 Doxygen::mainPage->name(),
9143 indexName,
9144 root->startLine,
9145 Doxygen::mainPage->title(),
9146 SectionType::Page,
9147 0); // level 0
9148 }
9149 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9150 }
9151 else if (root->tagInfo()==0)
9152 {
9153 warn(root->fileName,root->startLine,
9154 "found more than one \\mainpage comment block! (first occurrence: %s, line %d), Skipping current block!",
9155 qPrint(Doxygen::mainPage->docFile()),Doxygen::mainPage->getStartBodyLine());
9156 }
9157 }
9158 for (const auto &e : root->children()) findMainPage(e.get());
9159 }
9160
9161 // search for the main page imported via tag files and add only the section labels
findMainPageTagFiles(Entry * root)9162 static void findMainPageTagFiles(Entry *root)
9163 {
9164 if (root->section == Entry::MAINPAGEDOC_SEC)
9165 {
9166 if (Doxygen::mainPage && root->tagInfo())
9167 {
9168 Doxygen::mainPage->addSectionsToDefinition(root->anchors);
9169 }
9170 }
9171 for (const auto &e : root->children()) findMainPageTagFiles(e.get());
9172 }
9173
computePageRelations(Entry * root)9174 static void computePageRelations(Entry *root)
9175 {
9176 if ((root->section==Entry::PAGEDOC_SEC ||
9177 root->section==Entry::MAINPAGEDOC_SEC
9178 )
9179 && !root->name.isEmpty()
9180 )
9181 {
9182 PageDef *pd = root->section==Entry::PAGEDOC_SEC ?
9183 Doxygen::pageLinkedMap->find(root->name) :
9184 Doxygen::mainPage.get();
9185 if (pd)
9186 {
9187 for (const BaseInfo &bi : root->extends)
9188 {
9189 PageDef *subPd = Doxygen::pageLinkedMap->find(bi.name);
9190 if (pd==subPd)
9191 {
9192 term("page defined at line %d of file %s with label %s is a direct "
9193 "subpage of itself! Please remove this cyclic dependency.\n",
9194 pd->docLine(),qPrint(pd->docFile()),qPrint(pd->name()));
9195 }
9196 else if (subPd)
9197 {
9198 pd->addInnerCompound(subPd);
9199 //printf("*** Added subpage relation: %s->%s\n",
9200 // qPrint(pd->name()),qPrint(subPd->name()));
9201 }
9202 }
9203 }
9204 }
9205 for (const auto &e : root->children()) computePageRelations(e.get());
9206 }
9207
checkPageRelations()9208 static void checkPageRelations()
9209 {
9210 for (const auto &pd : *Doxygen::pageLinkedMap)
9211 {
9212 Definition *ppd = pd->getOuterScope();
9213 while (ppd)
9214 {
9215 if (ppd==pd.get())
9216 {
9217 term("page defined at line %d of file %s with label %s is a subpage "
9218 "of itself! Please remove this cyclic dependency.\n",
9219 pd->docLine(),qPrint(pd->docFile()),qPrint(pd->name()));
9220 }
9221 ppd=ppd->getOuterScope();
9222 }
9223 }
9224 }
9225
9226 //----------------------------------------------------------------------------
9227
resolveUserReferences()9228 static void resolveUserReferences()
9229 {
9230 for (const auto &si : SectionManager::instance())
9231 {
9232 //printf("si->label='%s' si->definition=%s si->fileName='%s'\n",
9233 // qPrint(si->label),si->definition?qPrint(si->definition->name()):"<none>",
9234 // qPrint(si->fileName));
9235 PageDef *pd=0;
9236
9237 // hack: the items of a todo/test/bug/deprecated list are all fragments from
9238 // different files, so the resulting section's all have the wrong file
9239 // name (not from the todo/test/bug/deprecated list, but from the file in
9240 // which they are defined). We correct this here by looking at the
9241 // generated section labels!
9242 for (const RefListManager::Ptr &rl : RefListManager::instance())
9243 {
9244 QCString label="_"+rl->listName(); // "_todo", "_test", ...
9245 if (si->label().left(label.length())==label)
9246 {
9247 si->setFileName(rl->listName());
9248 si->setGenerated(TRUE);
9249 break;
9250 }
9251 }
9252
9253 //printf("start: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
9254 if (!si->generated())
9255 {
9256 // if this section is in a page and the page is in a group, then we
9257 // have to adjust the link file name to point to the group.
9258 if (!si->fileName().isEmpty() &&
9259 (pd=Doxygen::pageLinkedMap->find(si->fileName())) &&
9260 pd->getGroupDef())
9261 {
9262 si->setFileName(pd->getGroupDef()->getOutputFileBase());
9263 }
9264
9265 if (si->definition())
9266 {
9267 // TODO: there should be one function in Definition that returns
9268 // the file to link to, so we can avoid the following tests.
9269 const GroupDef *gd=0;
9270 if (si->definition()->definitionType()==Definition::TypeMember)
9271 {
9272 gd = (toMemberDef(si->definition()))->getGroupDef();
9273 }
9274
9275 if (gd)
9276 {
9277 si->setFileName(gd->getOutputFileBase());
9278 }
9279 else
9280 {
9281 //si->fileName=si->definition->getOutputFileBase();
9282 //printf("Setting si->fileName to %s\n",qPrint(si->fileName));
9283 }
9284 }
9285 }
9286 //printf("end: si->label=%s si->fileName=%s\n",qPrint(si->label),qPrint(si->fileName));
9287 }
9288 }
9289
9290
9291
9292 //----------------------------------------------------------------------------
9293 // generate all separate documentation pages
9294
9295
generatePageDocs()9296 static void generatePageDocs()
9297 {
9298 //printf("documentedPages=%d real=%d\n",documentedPages,Doxygen::pageLinkedMap->count());
9299 if (documentedPages==0) return;
9300 for (const auto &pd : *Doxygen::pageLinkedMap)
9301 {
9302 if (!pd->getGroupDef() && !pd->isReference())
9303 {
9304 msg("Generating docs for page %s...\n",qPrint(pd->name()));
9305 Doxygen::insideMainPage=TRUE;
9306 pd->writeDocumentation(*g_outputList);
9307 Doxygen::insideMainPage=FALSE;
9308 }
9309 }
9310 }
9311
9312 //----------------------------------------------------------------------------
9313 // create a (sorted) list & dictionary of example pages
9314
buildExampleList(Entry * root)9315 static void buildExampleList(Entry *root)
9316 {
9317 if ((root->section==Entry::EXAMPLE_SEC || root->section==Entry::EXAMPLE_LINENO_SEC) && !root->name.isEmpty())
9318 {
9319 if (Doxygen::exampleLinkedMap->find(root->name))
9320 {
9321 warn(root->fileName,root->startLine,
9322 "Example %s was already documented. Ignoring "
9323 "documentation found here.",
9324 qPrint(root->name)
9325 );
9326 }
9327 else
9328 {
9329 PageDef *pd = Doxygen::exampleLinkedMap->add(root->name,
9330 std::unique_ptr<PageDef>(
9331 createPageDef(root->fileName,root->startLine,
9332 root->name,root->brief+root->doc+root->inbodyDocs,root->args)));
9333 pd->setBriefDescription(root->brief,root->briefFile,root->briefLine);
9334 pd->setFileName(convertNameToFile(pd->name()+"-example",FALSE,TRUE));
9335 pd->addSectionsToDefinition(root->anchors);
9336 pd->setLanguage(root->lang);
9337 pd->setShowLineNo(root->section==Entry::EXAMPLE_LINENO_SEC);
9338
9339 //we don't add example to groups
9340 //addExampleToGroups(root,pd);
9341 }
9342 }
9343 for (const auto &e : root->children()) buildExampleList(e.get());
9344 }
9345
9346 //----------------------------------------------------------------------------
9347 // prints the Entry tree (for debugging)
9348
printNavTree(Entry * root,int indent)9349 void printNavTree(Entry *root,int indent)
9350 {
9351 QCString indentStr;
9352 indentStr.fill(' ',indent);
9353 msg("%s%s (sec=0x%x)\n",
9354 indentStr.isEmpty()?"":qPrint(indentStr),
9355 root->name.isEmpty()?"<empty>":qPrint(root->name),
9356 root->section);
9357 for (const auto &e : root->children())
9358 {
9359 printNavTree(e.get(),indent+2);
9360 }
9361 }
9362
9363
9364 //----------------------------------------------------------------------------
9365 // generate the example documentation
9366
generateExampleDocs()9367 static void generateExampleDocs()
9368 {
9369 g_outputList->disable(OutputGenerator::Man);
9370 for (const auto &pd : *Doxygen::exampleLinkedMap)
9371 {
9372 msg("Generating docs for example %s...\n",qPrint(pd->name()));
9373 auto intf = Doxygen::parserManager->getCodeParser(".c"); // TODO: do this on code type
9374 intf->resetCodeParserState();
9375 QCString n=pd->getOutputFileBase();
9376 startFile(*g_outputList,n,n,pd->name());
9377 startTitle(*g_outputList,n);
9378 g_outputList->docify(pd->name());
9379 endTitle(*g_outputList,n,QCString());
9380 g_outputList->startContents();
9381 QCString lineNoOptStr;
9382 if (pd->showLineNo())
9383 {
9384 lineNoOptStr="{lineno}";
9385 }
9386 g_outputList->generateDoc(pd->docFile(), // file
9387 pd->docLine(), // startLine
9388 pd.get(), // context
9389 0, // memberDef
9390 (pd->briefDescription().isEmpty()?"":pd->briefDescription()+"\n\n")+
9391 pd->documentation()+"\n\n\\include"+lineNoOptStr+" "+pd->name(), // docs
9392 TRUE, // index words
9393 TRUE, // is example
9394 pd->name(),
9395 FALSE,
9396 FALSE,
9397 Config_getBool(MARKDOWN_SUPPORT)
9398 );
9399 endFile(*g_outputList); // contains g_outputList->endContents()
9400 }
9401 g_outputList->enable(OutputGenerator::Man);
9402 }
9403
9404 //----------------------------------------------------------------------------
9405 // generate module pages
9406
generateGroupDocs()9407 static void generateGroupDocs()
9408 {
9409 for (const auto &gd : *Doxygen::groupLinkedMap)
9410 {
9411 if (!gd->isReference())
9412 {
9413 gd->writeDocumentation(*g_outputList);
9414 }
9415 }
9416 }
9417
9418 //----------------------------------------------------------------------------
9419 // generate module pages
9420
generateNamespaceClassDocs(const ClassLinkedRefMap & classList)9421 static void generateNamespaceClassDocs(const ClassLinkedRefMap &classList)
9422 {
9423 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9424 if (numThreads==0)
9425 {
9426 numThreads = std::thread::hardware_concurrency();
9427 }
9428 if (numThreads>1) // multi threaded processing
9429 {
9430 struct DocContext
9431 {
9432 DocContext(ClassDefMutable *cdm_,OutputList ol_)
9433 : cdm(cdm_), ol(ol_) {}
9434 ClassDefMutable *cdm;
9435 OutputList ol;
9436 };
9437 ThreadPool threadPool(numThreads);
9438 std::vector< std::future< std::shared_ptr<DocContext> > > results;
9439 // for each class in the namespace...
9440 for (const auto &cd : classList)
9441 {
9442 ClassDefMutable *cdm = toClassDefMutable(cd);
9443 if (cdm)
9444 {
9445 auto ctx = std::make_shared<DocContext>(cdm,*g_outputList);
9446 auto processFile = [ctx]()
9447 {
9448 if ( ( ctx->cdm->isLinkableInProject() &&
9449 ctx->cdm->templateMaster()==0
9450 ) // skip external references, anonymous compounds and
9451 // template instances and nested classes
9452 && !ctx->cdm->isHidden() && !ctx->cdm->isEmbeddedInOuterScope()
9453 )
9454 {
9455 msg("Generating docs for compound %s...\n",qPrint(ctx->cdm->name()));
9456 ctx->cdm->writeDocumentation(ctx->ol);
9457 ctx->cdm->writeMemberList(ctx->ol);
9458 }
9459 ctx->cdm->writeDocumentationForInnerClasses(ctx->ol);
9460 return ctx;
9461 };
9462 results.emplace_back(threadPool.queue(processFile));
9463 }
9464 }
9465 // wait for the results
9466 for (auto &f : results)
9467 {
9468 auto ctx = f.get();
9469 }
9470 }
9471 else // single threaded processing
9472 {
9473 // for each class in the namespace...
9474 for (const auto &cd : classList)
9475 {
9476 ClassDefMutable *cdm = toClassDefMutable(cd);
9477 if (cdm)
9478 {
9479 if ( ( cd->isLinkableInProject() &&
9480 cd->templateMaster()==0
9481 ) // skip external references, anonymous compounds and
9482 // template instances and nested classes
9483 && !cd->isHidden() && !cd->isEmbeddedInOuterScope()
9484 )
9485 {
9486 msg("Generating docs for compound %s...\n",qPrint(cd->name()));
9487
9488 cdm->writeDocumentation(*g_outputList);
9489 cdm->writeMemberList(*g_outputList);
9490 }
9491 cdm->writeDocumentationForInnerClasses(*g_outputList);
9492 }
9493 }
9494 }
9495 }
9496
generateNamespaceConceptDocs(const ConceptLinkedRefMap & conceptList)9497 static void generateNamespaceConceptDocs(const ConceptLinkedRefMap &conceptList)
9498 {
9499 // for each concept in the namespace...
9500 for (const auto &cd : conceptList)
9501 {
9502 ConceptDefMutable *cdm = toConceptDefMutable(cd);
9503 if ( cdm && cd->isLinkableInProject() && !cd->isHidden())
9504 {
9505 msg("Generating docs for concept %s...\n",qPrint(cd->name()));
9506 cdm->writeDocumentation(*g_outputList);
9507 }
9508 }
9509 }
9510
generateNamespaceDocs()9511 static void generateNamespaceDocs()
9512 {
9513 static bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
9514
9515 //writeNamespaceIndex(*g_outputList);
9516
9517 // for each namespace...
9518 for (const auto &nd : *Doxygen::namespaceLinkedMap)
9519 {
9520 if (nd->isLinkableInProject())
9521 {
9522 NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
9523 if (ndm)
9524 {
9525 msg("Generating docs for namespace %s\n",qPrint(nd->name()));
9526 ndm->writeDocumentation(*g_outputList);
9527 }
9528 }
9529
9530 generateNamespaceClassDocs(nd->getClasses());
9531 if (sliceOpt)
9532 {
9533 generateNamespaceClassDocs(nd->getInterfaces());
9534 generateNamespaceClassDocs(nd->getStructs());
9535 generateNamespaceClassDocs(nd->getExceptions());
9536 }
9537 generateNamespaceConceptDocs(nd->getConcepts());
9538 }
9539 }
9540
9541 #if defined(_WIN32)
fixSlashes(QCString & s)9542 static QCString fixSlashes(QCString &s)
9543 {
9544 QCString result;
9545 uint i;
9546 for (i=0;i<s.length();i++)
9547 {
9548 switch(s.at(i))
9549 {
9550 case '/':
9551 case '\\':
9552 result+="\\\\";
9553 break;
9554 default:
9555 result+=s.at(i);
9556 }
9557 }
9558 return result;
9559 }
9560 #endif
9561
9562
9563 //----------------------------------------------------------------------------
9564
9565 /*! Generate a template version of the configuration file.
9566 * If the \a shortList parameter is TRUE a configuration file without
9567 * comments will be generated.
9568 */
generateConfigFile(const QCString & configFile,bool shortList,bool updateOnly=FALSE)9569 static void generateConfigFile(const QCString &configFile,bool shortList,
9570 bool updateOnly=FALSE)
9571 {
9572 std::ofstream f;
9573 bool fileOpened=openOutputFile(configFile,f);
9574 bool writeToStdout=configFile=="-";
9575 if (fileOpened)
9576 {
9577 TextStream t(&f);
9578 Config::writeTemplate(t,shortList,updateOnly);
9579 if (!writeToStdout)
9580 {
9581 if (!updateOnly)
9582 {
9583 msg("\n\nConfiguration file '%s' created.\n\n",qPrint(configFile));
9584 msg("Now edit the configuration file and enter\n\n");
9585 if (configFile!="Doxyfile" && configFile!="doxyfile")
9586 msg(" doxygen %s\n\n",qPrint(configFile));
9587 else
9588 msg(" doxygen\n\n");
9589 msg("to generate the documentation for your project\n\n");
9590 }
9591 else
9592 {
9593 msg("\n\nConfiguration file '%s' updated.\n\n",qPrint(configFile));
9594 }
9595 }
9596 }
9597 else
9598 {
9599 term("Cannot open file %s for writing\n",qPrint(configFile));
9600 }
9601 }
9602
compareDoxyfile()9603 static void compareDoxyfile()
9604 {
9605 std::ofstream f;
9606 bool fileOpened=openOutputFile("-",f);
9607 if (fileOpened)
9608 {
9609 TextStream t(&f);
9610 Config::compareDoxyfile(t);
9611 }
9612 else
9613 {
9614 term("Cannot open stdout for writing\n");
9615 }
9616 }
9617
9618 //----------------------------------------------------------------------------
9619 // read and parse a tag file
9620
readTagFile(const std::shared_ptr<Entry> & root,const QCString & tagLine)9621 static void readTagFile(const std::shared_ptr<Entry> &root,const QCString &tagLine)
9622 {
9623 QCString fileName;
9624 QCString destName;
9625 int eqPos = tagLine.find('=');
9626 if (eqPos!=-1) // tag command contains a destination
9627 {
9628 fileName = tagLine.left(eqPos).stripWhiteSpace();
9629 destName = tagLine.right(tagLine.length()-eqPos-1).stripWhiteSpace();
9630 if (fileName.isEmpty() || destName.isEmpty()) return;
9631 FileInfo fi(fileName.str());
9632 Doxygen::tagDestinationMap.insert(
9633 std::make_pair(fi.absFilePath(), destName.str()));
9634 //printf("insert tagDestination %s->%s\n",qPrint(fi.fileName()),qPrint(destName));
9635 }
9636 else
9637 {
9638 fileName = tagLine;
9639 }
9640
9641 FileInfo fi(fileName.str());
9642 if (!fi.exists() || !fi.isFile())
9643 {
9644 err("Tag file '%s' does not exist or is not a file. Skipping it...\n",
9645 qPrint(fileName));
9646 return;
9647 }
9648
9649 if (!destName.isEmpty())
9650 msg("Reading tag file '%s', location '%s'...\n",qPrint(fileName),qPrint(destName));
9651 else
9652 msg("Reading tag file '%s'...\n",qPrint(fileName));
9653
9654 parseTagFile(root,fi.absFilePath().c_str());
9655 }
9656
9657 //----------------------------------------------------------------------------
copyLatexStyleSheet()9658 static void copyLatexStyleSheet()
9659 {
9660 const StringVector &latexExtraStyleSheet = Config_getList(LATEX_EXTRA_STYLESHEET);
9661 for (const auto &sheet : latexExtraStyleSheet)
9662 {
9663 std::string fileName = sheet;
9664 if (!fileName.empty())
9665 {
9666 FileInfo fi(fileName);
9667 if (!fi.exists())
9668 {
9669 err("Style sheet '%s' specified by LATEX_EXTRA_STYLESHEET does not exist!\n",qPrint(fileName));
9670 }
9671 else
9672 {
9673 QCString destFileName = Config_getString(LATEX_OUTPUT)+"/"+fi.fileName();
9674 if (!checkExtension(fi.fileName().c_str(), LATEX_STYLE_EXTENSION))
9675 {
9676 destFileName += LATEX_STYLE_EXTENSION;
9677 }
9678 copyFile(QCString(fileName), destFileName);
9679 }
9680 }
9681 }
9682 }
9683
9684 //----------------------------------------------------------------------------
copyStyleSheet()9685 static void copyStyleSheet()
9686 {
9687 QCString htmlStyleSheet = Config_getString(HTML_STYLESHEET);
9688 if (!htmlStyleSheet.isEmpty())
9689 {
9690 FileInfo fi(htmlStyleSheet.str());
9691 if (!fi.exists())
9692 {
9693 err("Style sheet '%s' specified by HTML_STYLESHEET does not exist!\n",qPrint(htmlStyleSheet));
9694 htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
9695 }
9696 else
9697 {
9698 QCString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
9699 copyFile(htmlStyleSheet,destFileName);
9700 }
9701 }
9702 const StringVector &htmlExtraStyleSheet = Config_getList(HTML_EXTRA_STYLESHEET);
9703 for (const auto &sheet : htmlExtraStyleSheet)
9704 {
9705 std::string fileName = sheet;
9706 if (!fileName.empty())
9707 {
9708 FileInfo fi(fileName);
9709 if (!fi.exists())
9710 {
9711 err("Style sheet '%s' specified by HTML_EXTRA_STYLESHEET does not exist!\n",fileName.c_str());
9712 }
9713 else if (fi.fileName()=="doxygen.css" || fi.fileName()=="tabs.css" || fi.fileName()=="navtree.css")
9714 {
9715 err("Style sheet %s specified by HTML_EXTRA_STYLESHEET is already a built-in stylesheet. Please use a different name\n",qPrint(fi.fileName()));
9716 }
9717 else
9718 {
9719 QCString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName();
9720 copyFile(QCString(fileName), destFileName);
9721 }
9722 }
9723 }
9724 }
9725
copyLogo(const QCString & outputOption)9726 static void copyLogo(const QCString &outputOption)
9727 {
9728 QCString projectLogo = Config_getString(PROJECT_LOGO);
9729 if (!projectLogo.isEmpty())
9730 {
9731 FileInfo fi(projectLogo.str());
9732 if (!fi.exists())
9733 {
9734 err("Project logo '%s' specified by PROJECT_LOGO does not exist!\n",qPrint(projectLogo));
9735 projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
9736 }
9737 else
9738 {
9739 QCString destFileName = outputOption+"/"+fi.fileName();
9740 copyFile(projectLogo,destFileName);
9741 Doxygen::indexList->addImageFile(fi.fileName().c_str());
9742 }
9743 }
9744 }
9745
copyExtraFiles(const StringVector & files,const QCString & filesOption,const QCString & outputOption)9746 static void copyExtraFiles(const StringVector &files,const QCString &filesOption,const QCString &outputOption)
9747 {
9748 for (const auto &fileName : files)
9749 {
9750 if (!fileName.empty())
9751 {
9752 FileInfo fi(fileName);
9753 if (!fi.exists())
9754 {
9755 err("Extra file '%s' specified in %s does not exist!\n", fileName.c_str(),qPrint(filesOption));
9756 }
9757 else
9758 {
9759 QCString destFileName = outputOption+"/"+fi.fileName();
9760 Doxygen::indexList->addImageFile(fi.fileName().c_str());
9761 copyFile(QCString(fileName), destFileName);
9762 }
9763 }
9764 }
9765 }
9766
9767 //----------------------------------------------------------------------------
9768
generateDiskNames()9769 static void generateDiskNames()
9770 {
9771 for (const auto &fn : *Doxygen::inputNameLinkedMap)
9772 {
9773 struct FileEntry
9774 {
9775 FileEntry(const QCString &p,FileDef *fd) : path(p), fileDef(fd) {}
9776 QCString path;
9777 FileDef *fileDef;
9778 };
9779
9780 // collect the entry for which to compute the longest common prefix (LCP) of the path
9781 std::vector<FileEntry> fileEntries;
9782 for (const auto &fd : *fn)
9783 {
9784 if (!fd->isReference()) // skip external references
9785 {
9786 fileEntries.emplace_back(fd->getPath(),fd.get());
9787 }
9788 }
9789
9790 size_t size = fileEntries.size();
9791
9792 if (size==1) // name if unique, so diskname is simply the name
9793 {
9794 FileDef *fd = fileEntries[0].fileDef;
9795 fd->setDiskName(fn->fileName());
9796 }
9797 else if (size>1) // multiple occurrences of the same file name
9798 {
9799 // sort the array
9800 std::sort(fileEntries.begin(),
9801 fileEntries.end(),
9802 [](const FileEntry &fe1,const FileEntry &fe2)
9803 { return fe1.path < fe2.path; }
9804 );
9805
9806 // since the entries are sorted, the common prefix of the whole array is same
9807 // as the common prefix between the first and last entry
9808 const FileEntry &first = fileEntries[0];
9809 const FileEntry &last = fileEntries[size-1];
9810 int first_path_size = static_cast<int>(first.path.size())-1; // -1 to skip trailing slash
9811 int last_path_size = static_cast<int>(last.path.size())-1; // -1 to skip trailing slash
9812 int j=0;
9813 int i=0;
9814 for (i=0;i<first_path_size && i<last_path_size;i++)
9815 {
9816 if (first.path[i]=='/') j=i;
9817 if (first.path[i]!=last.path[i]) break;
9818 }
9819 if (i==first_path_size && i<last_path_size && last.path[i]=='/')
9820 {
9821 // case first='some/path' and last='some/path/more' => match is 'some/path'
9822 j=first_path_size;
9823 }
9824 else if (i==last_path_size && i<first_path_size && first.path[i]=='/')
9825 {
9826 // case first='some/path/more' and last='some/path' => match is 'some/path'
9827 j=last_path_size;
9828 }
9829
9830 // add non-common part of the path to the name
9831 for (auto &fileEntry : fileEntries)
9832 {
9833 QCString prefix = fileEntry.path.right(fileEntry.path.length()-j-1);
9834 fileEntry.fileDef->setName(prefix+fn->fileName());
9835 //printf("!!!!!!!! non unique disk name=%s:%s\n",qPrint(prefix),fn->fileName());
9836 fileEntry.fileDef->setDiskName(prefix+fn->fileName());
9837 }
9838 }
9839 }
9840 }
9841
9842
9843
9844 //----------------------------------------------------------------------------
9845
getParserForFile(const QCString & fn)9846 static std::unique_ptr<OutlineParserInterface> getParserForFile(const QCString &fn)
9847 {
9848 QCString fileName=fn;
9849 QCString extension;
9850 int sep = fileName.findRev('/');
9851 int ei = fileName.findRev('.');
9852 if (ei!=-1 && (sep==-1 || ei>sep)) // matches dir/file.ext but not dir.1/file
9853 {
9854 extension=fileName.right(fileName.length()-ei);
9855 }
9856 else
9857 {
9858 extension = ".no_extension";
9859 }
9860
9861 return Doxygen::parserManager->getOutlineParser(extension);
9862 }
9863
parseFile(OutlineParserInterface & parser,FileDef * fd,const QCString & fn,ClangTUParser * clangParser,bool newTU)9864 static std::shared_ptr<Entry> parseFile(OutlineParserInterface &parser,
9865 FileDef *fd,const QCString &fn,
9866 ClangTUParser *clangParser,bool newTU)
9867 {
9868 QCString fileName=fn;
9869 QCString extension;
9870 int ei = fileName.findRev('.');
9871 if (ei!=-1)
9872 {
9873 extension=fileName.right(fileName.length()-ei);
9874 }
9875 else
9876 {
9877 extension = ".no_extension";
9878 }
9879
9880 FileInfo fi(fileName.str());
9881 BufStr preBuf((uint)fi.size()+4096);
9882
9883 if (Config_getBool(ENABLE_PREPROCESSING) &&
9884 parser.needsPreprocessing(extension))
9885 {
9886 Preprocessor preprocessor;
9887 const StringVector &includePath = Config_getList(INCLUDE_PATH);
9888 for (const auto &s : includePath)
9889 {
9890 std::string absPath = FileInfo(s).absFilePath();
9891 preprocessor.addSearchDir(absPath.c_str());
9892 }
9893 BufStr inBuf((uint)fi.size()+4096);
9894 msg("Preprocessing %s...\n",qPrint(fn));
9895 readInputFile(fileName,inBuf);
9896 preprocessor.processFile(fileName,inBuf,preBuf);
9897 }
9898 else // no preprocessing
9899 {
9900 msg("Reading %s...\n",qPrint(fn));
9901 readInputFile(fileName,preBuf);
9902 }
9903 if (preBuf.data() && preBuf.curPos()>0 && *(preBuf.data()+preBuf.curPos()-1)!='\n')
9904 {
9905 preBuf.addChar('\n'); // add extra newline to help parser
9906 }
9907
9908 BufStr convBuf(preBuf.curPos()+1024);
9909
9910 // convert multi-line C++ comments to C style comments
9911 convertCppComments(&preBuf,&convBuf,fileName);
9912
9913 convBuf.addChar('\0');
9914
9915 std::shared_ptr<Entry> fileRoot = std::make_shared<Entry>();
9916 // use language parse to parse the file
9917 if (clangParser)
9918 {
9919 if (newTU) clangParser->parse();
9920 clangParser->switchToFile(fd);
9921 }
9922 parser.parseInput(fileName,convBuf.data(),fileRoot,clangParser);
9923 fileRoot->setFileDef(fd);
9924 return fileRoot;
9925 }
9926
9927 //! parse the list of input files
parseFilesMultiThreading(const std::shared_ptr<Entry> & root)9928 static void parseFilesMultiThreading(const std::shared_ptr<Entry> &root)
9929 {
9930 #if USE_LIBCLANG
9931 if (Doxygen::clangAssistedParsing)
9932 {
9933 StringUnorderedSet processedFiles;
9934
9935 // create a dictionary with files to process
9936 StringUnorderedSet filesToProcess;
9937 for (const auto &s : g_inputFiles)
9938 {
9939 filesToProcess.insert(s);
9940 }
9941
9942 std::mutex processedFilesLock;
9943 // process source files (and their include dependencies)
9944 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
9945 if (numThreads==0)
9946 {
9947 numThreads = std::thread::hardware_concurrency();
9948 }
9949 msg("Processing input using %zu threads.\n",numThreads);
9950 ThreadPool threadPool(numThreads);
9951 using FutureType = std::vector< std::shared_ptr<Entry> >;
9952 std::vector< std::future< FutureType > > results;
9953 for (const auto &s : g_inputFiles)
9954 {
9955 bool ambig;
9956 FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
9957 ASSERT(fd!=0);
9958 if (fd->isSource() && !fd->isReference()) // this is a source file
9959 {
9960 // lambda representing the work to executed by a thread
9961 auto processFile = [s,&filesToProcess,&processedFilesLock,&processedFiles]() {
9962 bool ambig_l;
9963 std::vector< std::shared_ptr<Entry> > roots;
9964 FileDef *fd_l = findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig_l);
9965 auto clangParser = ClangParser::instance()->createTUParser(fd_l);
9966 auto parser = getParserForFile(s.c_str());
9967 auto fileRoot { parseFile(*parser.get(),fd_l,s.c_str(),clangParser.get(),true) };
9968 roots.push_back(fileRoot);
9969
9970 // Now process any include files in the same translation unit
9971 // first. When libclang is used this is much more efficient.
9972 for (auto incFile : clangParser->filesInSameTU())
9973 {
9974 if (filesToProcess.find(incFile)!=filesToProcess.end())
9975 {
9976 bool needsToBeProcessed;
9977 {
9978 std::lock_guard<std::mutex> lock(processedFilesLock);
9979 needsToBeProcessed = processedFiles.find(incFile)==processedFiles.end();
9980 if (needsToBeProcessed) processedFiles.insert(incFile);
9981 }
9982 if (incFile!=s && needsToBeProcessed)
9983 {
9984 FileDef *ifd=findFileDef(Doxygen::inputNameLinkedMap,incFile.c_str(),ambig_l);
9985 if (ifd && !ifd->isReference())
9986 {
9987 //printf(" Processing %s in same translation unit as %s\n",incFile,s->c_str());
9988 fileRoot = parseFile(*parser.get(),ifd,incFile.c_str(),clangParser.get(),false);
9989 roots.push_back(fileRoot);
9990 }
9991 }
9992 }
9993 }
9994 return roots;
9995 };
9996 // dispatch the work and collect the future results
9997 results.emplace_back(threadPool.queue(processFile));
9998 }
9999 }
10000 // synchronise with the Entry result lists produced and add them to the root
10001 for (auto &f : results)
10002 {
10003 auto l = f.get();
10004 for (auto &e : l)
10005 {
10006 root->moveToSubEntryAndKeep(e);
10007 }
10008 }
10009 // process remaining files
10010 results.clear();
10011 for (const auto &s : g_inputFiles)
10012 {
10013 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
10014 {
10015 // lambda representing the work to executed by a thread
10016 auto processFile = [s]() {
10017 bool ambig;
10018 std::vector< std::shared_ptr<Entry> > roots;
10019 FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
10020 auto clangParser = ClangParser::instance()->createTUParser(fd);
10021 auto parser { getParserForFile(s.c_str()) };
10022 auto fileRoot = parseFile(*parser.get(),fd,s.c_str(),clangParser.get(),true);
10023 roots.push_back(fileRoot);
10024 return roots;
10025 };
10026 // dispatch the work and collect the future results
10027 results.emplace_back(threadPool.queue(processFile));
10028 }
10029 }
10030 // synchronise with the Entry result lists produced and add them to the root
10031 for (auto &f : results)
10032 {
10033 auto l = f.get();
10034 for (auto &e : l)
10035 {
10036 root->moveToSubEntryAndKeep(e);
10037 }
10038 }
10039 }
10040 else // normal processing
10041 #endif
10042 {
10043 std::size_t numThreads = std::thread::hardware_concurrency();
10044 msg("Processing input using %zu threads.\n",numThreads);
10045 ThreadPool threadPool(numThreads);
10046 using FutureType = std::shared_ptr<Entry>;
10047 std::vector< std::future< FutureType > > results;
10048 for (const auto &s : g_inputFiles)
10049 {
10050 // lambda representing the work to executed by a thread
10051 auto processFile = [s]() {
10052 bool ambig;
10053 FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
10054 auto parser = getParserForFile(s.c_str());
10055 auto fileRoot = parseFile(*parser.get(),fd,s.c_str(),nullptr,true);
10056 return fileRoot;
10057 };
10058 // dispatch the work and collect the future results
10059 results.emplace_back(threadPool.queue(processFile));
10060 }
10061 // synchronise with the Entry results produced and add them to the root
10062 for (auto &f : results)
10063 {
10064 root->moveToSubEntryAndKeep(f.get());
10065 }
10066 }
10067 }
10068
10069 //! parse the list of input files
parseFilesSingleThreading(const std::shared_ptr<Entry> & root)10070 static void parseFilesSingleThreading(const std::shared_ptr<Entry> &root)
10071 {
10072 #if USE_LIBCLANG
10073 if (Doxygen::clangAssistedParsing)
10074 {
10075 StringUnorderedSet processedFiles;
10076
10077 // create a dictionary with files to process
10078 StringUnorderedSet filesToProcess;
10079 for (const auto &s : g_inputFiles)
10080 {
10081 filesToProcess.insert(s);
10082 }
10083
10084 // process source files (and their include dependencies)
10085 for (const auto &s : g_inputFiles)
10086 {
10087 bool ambig;
10088 FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
10089 ASSERT(fd!=0);
10090 if (fd->isSource() && !fd->isReference()) // this is a source file
10091 {
10092 auto clangParser = ClangParser::instance()->createTUParser(fd);
10093 auto parser { getParserForFile(s.c_str()) };
10094 auto fileRoot = parseFile(*parser.get(),fd,s.c_str(),clangParser.get(),true);
10095 root->moveToSubEntryAndKeep(fileRoot);
10096 processedFiles.insert(s);
10097
10098 // Now process any include files in the same translation unit
10099 // first. When libclang is used this is much more efficient.
10100 for (auto incFile : clangParser->filesInSameTU())
10101 {
10102 //printf(" file %s\n",incFile.c_str());
10103 if (filesToProcess.find(incFile)!=filesToProcess.end() && // file need to be processed
10104 processedFiles.find(incFile)==processedFiles.end()) // and is not processed already
10105 {
10106 FileDef *ifd=findFileDef(Doxygen::inputNameLinkedMap,incFile.c_str(),ambig);
10107 if (ifd && !ifd->isReference())
10108 {
10109 //printf(" Processing %s in same translation unit as %s\n",incFile.c_str(),s.c_str());
10110 fileRoot = parseFile(*parser.get(),ifd,incFile.c_str(),clangParser.get(),false);
10111 root->moveToSubEntryAndKeep(fileRoot);
10112 processedFiles.insert(incFile);
10113 }
10114 }
10115 }
10116 }
10117 }
10118 // process remaining files
10119 for (const auto &s : g_inputFiles)
10120 {
10121 if (processedFiles.find(s)==processedFiles.end()) // not yet processed
10122 {
10123 bool ambig;
10124 FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
10125 auto clangParser = ClangParser::instance()->createTUParser(fd);
10126 auto parser { getParserForFile(s.c_str()) };
10127 auto fileRoot = parseFile(*parser.get(),fd,s.c_str(),clangParser.get(),true);
10128 root->moveToSubEntryAndKeep(fileRoot);
10129 processedFiles.insert(s);
10130 }
10131 }
10132 }
10133 else // normal processing
10134 #endif
10135 {
10136 for (const auto &s : g_inputFiles)
10137 {
10138 bool ambig;
10139 FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
10140 ASSERT(fd!=0);
10141 std::unique_ptr<OutlineParserInterface> parser { getParserForFile(s.c_str()) };
10142 std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,s.c_str(),nullptr,true);
10143 root->moveToSubEntryAndKeep(fileRoot);
10144 }
10145 }
10146 }
10147
10148 // resolves a path that may include symlinks, if a recursive symlink is
10149 // found an empty string is returned.
resolveSymlink(const std::string & path)10150 static std::string resolveSymlink(const std::string &path)
10151 {
10152 int sepPos=0;
10153 int oldPos=0;
10154 StringSet nonSymlinks;
10155 StringSet known;
10156 QCString result(path);
10157 QCString oldPrefix = "/";
10158 do
10159 {
10160 #ifdef WIN32
10161 // UNC path, skip server and share name
10162 if (sepPos==0 && (result.left(2)=="//" || result.left(2)=="\\\\"))
10163 sepPos = result.find('/',2);
10164 if (sepPos!=-1)
10165 sepPos = result.find('/',sepPos+1);
10166 #else
10167 sepPos = result.find('/',sepPos+1);
10168 #endif
10169 QCString prefix = sepPos==-1 ? result : result.left(sepPos);
10170 if (nonSymlinks.find(prefix.str())==nonSymlinks.end())
10171 {
10172 FileInfo fi(prefix.str());
10173 if (fi.isSymLink())
10174 {
10175 QCString target = fi.readLink();
10176 bool isRelative = FileInfo(target.str()).isRelative();
10177 if (isRelative)
10178 {
10179 target = Dir::cleanDirPath(oldPrefix.str()+"/"+target.str());
10180 }
10181 if (sepPos!=-1)
10182 {
10183 if (fi.isDir() && target.length()>0 && target.at(target.length()-1)!='/')
10184 {
10185 target+='/';
10186 }
10187 target+=result.mid(sepPos);
10188 }
10189 result = Dir::cleanDirPath(target.str());
10190 sepPos = 0;
10191 if (known.find(result.str())!=known.end()) return std::string(); // recursive symlink!
10192 known.insert(result.str());
10193 if (isRelative)
10194 {
10195 sepPos = oldPos;
10196 }
10197 else // link to absolute path
10198 {
10199 sepPos = 0;
10200 oldPrefix = "/";
10201 }
10202 }
10203 else
10204 {
10205 nonSymlinks.insert(prefix.str());
10206 oldPrefix = prefix;
10207 }
10208 oldPos = sepPos;
10209 }
10210 }
10211 while (sepPos!=-1);
10212 return Dir::cleanDirPath(result.str());
10213 }
10214
10215 static StringUnorderedSet g_pathsVisited(1009);
10216
10217 //----------------------------------------------------------------------------
10218 // Read all files matching at least one pattern in 'patList' in the
10219 // directory represented by 'fi'.
10220 // The directory is read iff the recursiveFlag is set.
10221 // The contents of all files is append to the input string
10222
readDir(FileInfo * fi,FileNameLinkedMap * fnMap,StringUnorderedSet * exclSet,const StringVector * patList,const StringVector * exclPatList,StringVector * resultList,StringUnorderedSet * resultSet,bool errorIfNotExist,bool recursive,StringUnorderedSet * killSet,StringSet * paths)10223 static void readDir(FileInfo *fi,
10224 FileNameLinkedMap *fnMap,
10225 StringUnorderedSet *exclSet,
10226 const StringVector *patList,
10227 const StringVector *exclPatList,
10228 StringVector *resultList,
10229 StringUnorderedSet *resultSet,
10230 bool errorIfNotExist,
10231 bool recursive,
10232 StringUnorderedSet *killSet,
10233 StringSet *paths
10234 )
10235 {
10236 std::string dirName = fi->absFilePath();
10237 if (paths && !dirName.empty())
10238 {
10239 paths->insert(dirName);
10240 }
10241 //printf("%s isSymLink()=%d\n",qPrint(dirName),fi->isSymLink());
10242 if (fi->isSymLink())
10243 {
10244 dirName = resolveSymlink(dirName);
10245 if (dirName.empty())
10246 {
10247 //printf("RECURSIVE SYMLINK: %s\n",qPrint(dirName));
10248 return; // recursive symlink
10249 }
10250 }
10251
10252 if (g_pathsVisited.find(dirName)!=g_pathsVisited.end())
10253 {
10254 //printf("PATH ALREADY VISITED: %s\n",qPrint(dirName));
10255 return; // already visited path
10256 }
10257 g_pathsVisited.insert(dirName);
10258
10259 Dir dir(dirName);
10260 msg("Searching for files in directory %s\n", qPrint(fi->absFilePath()));
10261 //printf("killSet=%p count=%d\n",killSet,killSet ? (int)killSet->count() : -1);
10262
10263 StringVector dirResultList;
10264
10265 for (const auto &dirEntry : dir.iterator())
10266 {
10267 FileInfo cfi(dirEntry.path());
10268 if (exclSet==0 || exclSet->find(cfi.absFilePath())==exclSet->end())
10269 { // file should not be excluded
10270 //printf("killSet->find(%s)\n",qPrint(cfi->absFilePath()));
10271 if (!cfi.exists() || !cfi.isReadable())
10272 {
10273 if (errorIfNotExist)
10274 {
10275 warn_uncond("source '%s' is not a readable file or directory... skipping.\n",cfi.absFilePath().c_str());
10276 }
10277 }
10278 else if (cfi.isFile() &&
10279 (!Config_getBool(EXCLUDE_SYMLINKS) || !cfi.isSymLink()) &&
10280 (patList==0 || patternMatch(cfi,*patList)) &&
10281 (exclPatList==0 || !patternMatch(cfi,*exclPatList)) &&
10282 (killSet==0 || killSet->find(cfi.absFilePath())==killSet->end())
10283 )
10284 {
10285 std::string name=cfi.fileName();
10286 std::string path=cfi.dirPath()+"/";
10287 std::string fullName=path+name;
10288 if (fnMap)
10289 {
10290 std::unique_ptr<FileDef> fd { createFileDef(QCString(path),QCString(name)) };
10291 FileName *fn=0;
10292 if (!name.empty())
10293 {
10294 fn = fnMap->add(QCString(name),QCString(fullName));
10295 fn->push_back(std::move(fd));
10296 }
10297 }
10298 dirResultList.push_back(fullName);
10299 if (resultSet) resultSet->insert(fullName);
10300 if (killSet) killSet->insert(fullName);
10301 }
10302 else if (recursive &&
10303 (!Config_getBool(EXCLUDE_SYMLINKS) || !cfi.isSymLink()) &&
10304 cfi.isDir() &&
10305 (exclPatList==0 || !patternMatch(cfi,*exclPatList)) &&
10306 cfi.fileName().at(0)!='.') // skip "." ".." and ".dir"
10307 {
10308 FileInfo acfi(cfi.absFilePath());
10309 readDir(&acfi,fnMap,exclSet,
10310 patList,exclPatList,&dirResultList,resultSet,errorIfNotExist,
10311 recursive,killSet,paths);
10312 }
10313 }
10314 }
10315 if (resultList && !dirResultList.empty())
10316 {
10317 // sort the resulting list to make the order platform independent.
10318 std::sort(dirResultList.begin(),
10319 dirResultList.end(),
10320 [](const auto &f1,const auto &f2) { return qstricmp(f1.c_str(),f2.c_str())<0; });
10321
10322 // append the sorted results to resultList
10323 resultList->insert(resultList->end(), dirResultList.begin(), dirResultList.end());
10324 }
10325 }
10326
10327
10328 //----------------------------------------------------------------------------
10329 // read a file or all files in a directory and append their contents to the
10330 // input string. The names of the files are appended to the 'fiList' list.
10331
readFileOrDirectory(const QCString & s,FileNameLinkedMap * fnMap,StringUnorderedSet * exclSet,const StringVector * patList,const StringVector * exclPatList,StringVector * resultList,StringUnorderedSet * resultSet,bool recursive,bool errorIfNotExist,StringUnorderedSet * killSet,StringSet * paths)10332 void readFileOrDirectory(const QCString &s,
10333 FileNameLinkedMap *fnMap,
10334 StringUnorderedSet *exclSet,
10335 const StringVector *patList,
10336 const StringVector *exclPatList,
10337 StringVector *resultList,
10338 StringUnorderedSet *resultSet,
10339 bool recursive,
10340 bool errorIfNotExist,
10341 StringUnorderedSet *killSet,
10342 StringSet *paths
10343 )
10344 {
10345 //printf("killSet count=%d\n",killSet ? (int)killSet->size() : -1);
10346 // strip trailing slashes
10347 if (s.isEmpty()) return;
10348
10349 g_pathsVisited.clear();
10350
10351 FileInfo fi(s.str());
10352 //printf("readFileOrDirectory(%s)\n",s);
10353 {
10354 if (exclSet==0 || exclSet->find(fi.absFilePath())==exclSet->end())
10355 {
10356 if (!fi.exists() || !fi.isReadable())
10357 {
10358 if (errorIfNotExist)
10359 {
10360 warn_uncond("source '%s' is not a readable file or directory... skipping.\n",qPrint(s));
10361 }
10362 }
10363 else if (!Config_getBool(EXCLUDE_SYMLINKS) || !fi.isSymLink())
10364 {
10365 if (fi.isFile())
10366 {
10367 std::string dirPath = fi.dirPath(true);
10368 std::string filePath = fi.absFilePath();
10369 if (paths && !dirPath.empty())
10370 {
10371 paths->insert(dirPath);
10372 }
10373 //printf("killSet.find(%s)=%d\n",qPrint(fi.absFilePath()),killSet.find(fi.absFilePath())!=killSet.end());
10374 if (killSet==0 || killSet->find(filePath)==killSet->end())
10375 {
10376 std::string name=fi.fileName();
10377 if (fnMap)
10378 {
10379 std::unique_ptr<FileDef> fd { createFileDef(QCString(dirPath+"/"),QCString(name)) };
10380 if (!name.empty())
10381 {
10382 FileName *fn = fnMap->add(QCString(name),QCString(filePath));
10383 fn->push_back(std::move(fd));
10384 }
10385 }
10386 if (resultList || resultSet)
10387 {
10388 if (resultList) resultList->push_back(filePath);
10389 if (resultSet) resultSet->insert(filePath);
10390 }
10391
10392 if (killSet) killSet->insert(fi.absFilePath());
10393 }
10394 }
10395 else if (fi.isDir()) // readable dir
10396 {
10397 readDir(&fi,fnMap,exclSet,patList,
10398 exclPatList,resultList,resultSet,errorIfNotExist,
10399 recursive,killSet,paths);
10400 }
10401 }
10402 }
10403 }
10404 }
10405
10406 //----------------------------------------------------------------------------
10407
expandAliases()10408 static void expandAliases()
10409 {
10410 for (auto &kv : Doxygen::aliasMap)
10411 {
10412 kv.second = expandAlias(kv.first,kv.second);
10413 }
10414 }
10415
10416 //----------------------------------------------------------------------------
10417
escapeAliases()10418 static void escapeAliases()
10419 {
10420 for (auto &kv : Doxygen::aliasMap)
10421 {
10422 QCString value(kv.second);
10423 QCString newValue;
10424 int in,p=0;
10425 // for each \n in the alias command value
10426 while ((in=value.find("\\n",p))!=-1)
10427 {
10428 newValue+=value.mid(p,in-p);
10429 // expand \n's except if \n is part of a built-in command.
10430 if (value.mid(in,5)!="\\note" &&
10431 value.mid(in,5)!="\\noop" &&
10432 value.mid(in,5)!="\\name" &&
10433 value.mid(in,10)!="\\namespace" &&
10434 value.mid(in,14)!="\\nosubgrouping"
10435 )
10436 {
10437 newValue+="\\ilinebr ";
10438 }
10439 else
10440 {
10441 newValue+="\\n";
10442 }
10443 p=in+2;
10444 }
10445 newValue+=value.mid(p,value.length()-p);
10446 p = 0;
10447 newValue = "";
10448 while ((in=value.find("^^",p))!=-1)
10449 {
10450 newValue+=value.mid(p,in-p);
10451 newValue+="\\ilinebr ";
10452 p=in+2;
10453 }
10454 newValue+=value.mid(p,value.length()-p);
10455 kv.second=newValue.str();
10456 //printf("Alias %s has value %s\n",kv.first.c_str(),qPrint(newValue));
10457 }
10458 }
10459
10460 //----------------------------------------------------------------------------
10461
readAliases()10462 void readAliases()
10463 {
10464 // add aliases to a dictionary
10465 const StringVector &aliasList = Config_getList(ALIASES);
10466 for (const auto &al : aliasList)
10467 {
10468 QCString alias(al);
10469 int i=alias.find('=');
10470 if (i>0)
10471 {
10472 QCString name=alias.left(i).stripWhiteSpace();
10473 QCString value=alias.right(alias.length()-i-1);
10474 //printf("Alias: found name='%s' value='%s'\n",qPrint(name),qPrint(value));
10475 if (!name.isEmpty())
10476 {
10477 auto it = Doxygen::aliasMap.find(name.str());
10478 if (it==Doxygen::aliasMap.end()) // insert new alias
10479 {
10480 Doxygen::aliasMap.insert(std::make_pair(name.str(),value.str()));
10481 }
10482 else // overwrite previous alias
10483 {
10484 it->second=value.str();
10485 }
10486 }
10487 }
10488 }
10489 expandAliases();
10490 escapeAliases();
10491 }
10492
10493 //----------------------------------------------------------------------------
10494
dumpSymbol(TextStream & t,Definition * d)10495 static void dumpSymbol(TextStream &t,Definition *d)
10496 {
10497 QCString anchor;
10498 if (d->definitionType()==Definition::TypeMember)
10499 {
10500 MemberDef *md = toMemberDef(d);
10501 anchor=":"+md->anchor();
10502 }
10503 QCString scope;
10504 if (d->getOuterScope() && d->getOuterScope()!=Doxygen::globalScope)
10505 {
10506 scope = addHtmlExtensionIfMissing(d->getOuterScope()->getOutputFileBase());
10507 }
10508 t << "REPLACE INTO symbols (symbol_id,scope_id,name,file,line) VALUES('"
10509 << addHtmlExtensionIfMissing(d->getOutputFileBase())+anchor << "','"
10510 << scope << "','"
10511 << d->name() << "','"
10512 << d->getDefFileName() << "','"
10513 << d->getDefLine()
10514 << "');\n";
10515 }
10516
dumpSymbolMap()10517 static void dumpSymbolMap()
10518 {
10519 std::ofstream f("symbols.sql",std::ofstream::out | std::ofstream::binary);
10520 if (f.is_open())
10521 {
10522 TextStream t(&f);
10523 for (const auto &kv : *Doxygen::symbolMap)
10524 {
10525 dumpSymbol(t,kv.second);
10526 }
10527 }
10528 }
10529
10530 // print developer options of doxygen
devUsage()10531 static void devUsage()
10532 {
10533 msg("Developer parameters:\n");
10534 msg(" -m dump symbol map\n");
10535 msg(" -b making messages output unbuffered\n");
10536 msg(" -T activates output generation via Django like template\n");
10537 msg(" -d <level> enable a debug level, such as (multiple invocations of -d are possible):\n");
10538 Debug::printFlags();
10539 }
10540
10541
10542 //----------------------------------------------------------------------------
10543 // print the version of doxygen
10544
version(const bool extended)10545 static void version(const bool extended)
10546 {
10547 QCString versionString = getFullVersion();
10548 msg("%s\n",qPrint(versionString));
10549 if (extended)
10550 {
10551 QCString extVers;
10552 #if USE_SQLITE3
10553 if (!extVers.isEmpty()) extVers+= ", ";
10554 extVers += "sqlite3 ";
10555 extVers += sqlite3_libversion();
10556 #endif
10557 #if USE_LIBCLANG
10558 if (!extVers.isEmpty()) extVers+= ", ";
10559 extVers += "clang support ";
10560 extVers += CLANG_VERSION_STRING;
10561 #endif
10562 if (!extVers.isEmpty())
10563 {
10564 int lastComma = extVers.findRev(',');
10565 if (lastComma != -1) extVers = extVers.replace(lastComma,1," and");
10566 msg(" with %s.\n",qPrint(extVers));
10567 }
10568 }
10569 }
10570
10571 //----------------------------------------------------------------------------
10572 // print the usage of doxygen
10573
usage(const QCString & name,const QCString & versionString)10574 static void usage(const QCString &name,const QCString &versionString)
10575 {
10576 msg("Doxygen version %s\nCopyright Dimitri van Heesch 1997-2021\n\n",qPrint(versionString));
10577 msg("You can use doxygen in a number of ways:\n\n");
10578 msg("1) Use doxygen to generate a template configuration file:\n");
10579 msg(" %s [-s] -g [configName]\n\n",qPrint(name));
10580 msg("2) Use doxygen to update an old configuration file:\n");
10581 msg(" %s [-s] -u [configName]\n\n",qPrint(name));
10582 msg("3) Use doxygen to generate documentation using an existing ");
10583 msg("configuration file:\n");
10584 msg(" %s [configName]\n\n",qPrint(name));
10585 msg("4) Use doxygen to generate a template file controlling the layout of the\n");
10586 msg(" generated documentation:\n");
10587 msg(" %s -l [layoutFileName]\n\n",qPrint(name));
10588 msg(" In case layoutFileName is omitted layoutFileName.xml will be used as filename.\n");
10589 msg(" If - is used for layoutFileName doxygen will write to standard output.\n\n");
10590 msg("5) Use doxygen to generate a template style sheet file for RTF, HTML or Latex.\n");
10591 msg(" RTF: %s -w rtf styleSheetFile\n",qPrint(name));
10592 msg(" HTML: %s -w html headerFile footerFile styleSheetFile [configFile]\n",qPrint(name));
10593 msg(" LaTeX: %s -w latex headerFile footerFile styleSheetFile [configFile]\n\n",qPrint(name));
10594 msg("6) Use doxygen to generate a rtf extensions file\n");
10595 msg(" %s -e rtf extensionsFile\n\n",qPrint(name));
10596 msg(" If - is used for extensionsFile doxygen will write to standard output.\n\n");
10597 msg("7) Use doxygen to compare the used configuration file with the template configuration file\n");
10598 msg(" %s -x [configFile]\n\n",qPrint(name));
10599 msg("8) Use doxygen to show a list of built-in emojis.\n");
10600 msg(" %s -f emoji outputFileName\n\n",qPrint(name));
10601 msg(" If - is used for outputFileName doxygen will write to standard output.\n\n");
10602 msg("If -s is specified the comments of the configuration items in the config file will be omitted.\n");
10603 msg("If configName is omitted 'Doxyfile' will be used as a default.\n");
10604 msg("If - is used for configFile doxygen will write / read the configuration to /from standard output / input.\n\n");
10605 msg("If -q is used for a doxygen documentation run, doxygen will see this as if QUIET=YES has been set.\n\n");
10606 msg("-v print version string, -V print extended version information\n");
10607 }
10608
10609 //----------------------------------------------------------------------------
10610 // read the argument of option 'c' from the comment argument list and
10611 // update the option index 'optInd'.
10612
getArg(int argc,char ** argv,int & optInd)10613 static const char *getArg(int argc,char **argv,int &optInd)
10614 {
10615 char *s=0;
10616 if (qstrlen(&argv[optInd][2])>0)
10617 s=&argv[optInd][2];
10618 else if (optInd+1<argc && argv[optInd+1][0]!='-')
10619 s=argv[++optInd];
10620 return s;
10621 }
10622
10623 //----------------------------------------------------------------------------
10624
10625 /** @brief /dev/null outline parser */
10626 class NullOutlineParser : public OutlineParserInterface
10627 {
10628 public:
parseInput(const QCString & file,const char * buf,const std::shared_ptr<Entry> &,ClangTUParser *)10629 void parseInput(const QCString &file, const char *buf,const std::shared_ptr<Entry> &, ClangTUParser*) {}
needsPreprocessing(const QCString &) const10630 bool needsPreprocessing(const QCString &) const { return FALSE; }
parsePrototype(const QCString &)10631 void parsePrototype(const QCString &) {}
10632 };
10633
10634
make_parser_factory()10635 template<class T> std::function< std::unique_ptr<T>() > make_parser_factory()
10636 {
10637 return []() { return std::make_unique<T>(); };
10638 }
10639
initDoxygen()10640 void initDoxygen()
10641 {
10642 initResources();
10643 QCString lang = Portable::getenv("LC_ALL");
10644 if (!lang.isEmpty()) Portable::setenv("LANG",lang);
10645 std::setlocale(LC_ALL,"");
10646 std::setlocale(LC_CTYPE,"C"); // to get isspace(0xA0)==0, needed for UTF-8
10647 std::setlocale(LC_NUMERIC,"C");
10648
10649 Doxygen::symbolMap = new SymbolMap<Definition>;
10650
10651 Portable::correct_path();
10652
10653 Debug::startTimer();
10654 Doxygen::parserManager = new ParserManager( make_parser_factory<NullOutlineParser>(),
10655 make_parser_factory<FileCodeParser>());
10656 Doxygen::parserManager->registerParser("c", make_parser_factory<COutlineParser>(),
10657 make_parser_factory<CCodeParser>());
10658 Doxygen::parserManager->registerParser("python", make_parser_factory<PythonOutlineParser>(),
10659 make_parser_factory<PythonCodeParser>());
10660 Doxygen::parserManager->registerParser("fortran", make_parser_factory<FortranOutlineParser>(),
10661 make_parser_factory<FortranCodeParser>());
10662 Doxygen::parserManager->registerParser("fortranfree", make_parser_factory<FortranOutlineParserFree>(),
10663 make_parser_factory<FortranCodeParserFree>());
10664 Doxygen::parserManager->registerParser("fortranfixed", make_parser_factory<FortranOutlineParserFixed>(),
10665 make_parser_factory<FortranCodeParserFixed>());
10666 Doxygen::parserManager->registerParser("vhdl", make_parser_factory<VHDLOutlineParser>(),
10667 make_parser_factory<VHDLCodeParser>());
10668 Doxygen::parserManager->registerParser("xml", make_parser_factory<NullOutlineParser>(),
10669 make_parser_factory<XMLCodeParser>());
10670 Doxygen::parserManager->registerParser("sql", make_parser_factory<NullOutlineParser>(),
10671 make_parser_factory<SQLCodeParser>());
10672 Doxygen::parserManager->registerParser("md", make_parser_factory<MarkdownOutlineParser>(),
10673 make_parser_factory<FileCodeParser>());
10674 Doxygen::parserManager->registerParser("lex", make_parser_factory<LexOutlineParser>(),
10675 make_parser_factory<LexCodeParser>());
10676
10677 // register any additional parsers here...
10678
10679 initDefaultExtensionMapping();
10680 initClassMemberIndices();
10681 initNamespaceMemberIndices();
10682 initFileMemberIndices();
10683
10684 #ifdef USE_LIBCLANG
10685 Doxygen::clangUsrMap = new ClangUsrMap;
10686 #endif
10687 Doxygen::memberNameLinkedMap = new MemberNameLinkedMap;
10688 Doxygen::functionNameLinkedMap = new MemberNameLinkedMap;
10689 Doxygen::groupLinkedMap = new GroupLinkedMap;
10690 Doxygen::namespaceLinkedMap = new NamespaceLinkedMap;
10691 Doxygen::classLinkedMap = new ClassLinkedMap;
10692 Doxygen::hiddenClassLinkedMap = new ClassLinkedMap;
10693 Doxygen::conceptLinkedMap = new ConceptLinkedMap;
10694 Doxygen::dirLinkedMap = new DirLinkedMap;
10695 Doxygen::pageLinkedMap = new PageLinkedMap; // all doc pages
10696 Doxygen::exampleLinkedMap = new PageLinkedMap; // all examples
10697 //Doxygen::tagDestinationDict.setAutoDelete(TRUE);
10698 Doxygen::indexList = new IndexList;
10699
10700 // initialisation of these globals depends on
10701 // configuration switches so we need to postpone these
10702 Doxygen::globalScope = 0;
10703 Doxygen::inputNameLinkedMap = 0;
10704 Doxygen::includeNameLinkedMap = 0;
10705 Doxygen::exampleNameLinkedMap = 0;
10706 Doxygen::imageNameLinkedMap = 0;
10707 Doxygen::dotFileNameLinkedMap = 0;
10708 Doxygen::mscFileNameLinkedMap = 0;
10709 Doxygen::diaFileNameLinkedMap = 0;
10710
10711 /**************************************************************************
10712 * Initialize some global constants
10713 **************************************************************************/
10714
10715 g_compoundKeywords.insert("template class");
10716 g_compoundKeywords.insert("template struct");
10717 g_compoundKeywords.insert("class");
10718 g_compoundKeywords.insert("struct");
10719 g_compoundKeywords.insert("union");
10720 g_compoundKeywords.insert("interface");
10721 g_compoundKeywords.insert("exception");
10722 }
10723
cleanUpDoxygen()10724 void cleanUpDoxygen()
10725 {
10726 FormulaManager::instance().clear();
10727 SectionManager::instance().clear();
10728
10729 delete Doxygen::indexList;
10730 delete Doxygen::inputNameLinkedMap;
10731 delete Doxygen::includeNameLinkedMap;
10732 delete Doxygen::exampleNameLinkedMap;
10733 delete Doxygen::imageNameLinkedMap;
10734 delete Doxygen::dotFileNameLinkedMap;
10735 delete Doxygen::mscFileNameLinkedMap;
10736 delete Doxygen::diaFileNameLinkedMap;
10737 Doxygen::mainPage.reset();
10738 delete Doxygen::pageLinkedMap;
10739 delete Doxygen::exampleLinkedMap;
10740 delete Doxygen::globalScope;
10741 delete Doxygen::parserManager;
10742 delete theTranslator;
10743 delete g_outputList;
10744 Mappers::freeMappers();
10745
10746 delete Doxygen::memberNameLinkedMap;
10747 delete Doxygen::functionNameLinkedMap;
10748 delete Doxygen::groupLinkedMap;
10749 delete Doxygen::namespaceLinkedMap;
10750 delete Doxygen::dirLinkedMap;
10751 delete Doxygen::symbolMap;
10752
10753 DotManager::deleteInstance();
10754 }
10755
computeIdealCacheParam(size_t v)10756 static int computeIdealCacheParam(size_t v)
10757 {
10758 //printf("computeIdealCacheParam(v=%u)\n",v);
10759
10760 int r=0;
10761 while (v!=0) v>>=1,r++;
10762 // r = log2(v)
10763
10764 // convert to a valid cache size value
10765 return std::max(0,std::min(r-16,9));
10766 }
10767
readConfiguration(int argc,char ** argv)10768 void readConfiguration(int argc, char **argv)
10769 {
10770 QCString versionString = getFullVersion();
10771
10772 /**************************************************************************
10773 * Handle arguments *
10774 **************************************************************************/
10775
10776 int optInd=1;
10777 QCString configName;
10778 QCString layoutName;
10779 QCString debugLabel;
10780 QCString formatName;
10781 QCString listName;
10782 bool genConfig=FALSE;
10783 bool shortList=FALSE;
10784 bool diffList=FALSE;
10785 bool updateConfig=FALSE;
10786 int retVal;
10787 bool quiet = false;
10788 while (optInd<argc && argv[optInd][0]=='-' &&
10789 (isalpha(argv[optInd][1]) || argv[optInd][1]=='?' ||
10790 argv[optInd][1]=='-')
10791 )
10792 {
10793 switch(argv[optInd][1])
10794 {
10795 case 'g':
10796 genConfig=TRUE;
10797 break;
10798 case 'l':
10799 if (optInd+1>=argc)
10800 {
10801 layoutName="DoxygenLayout.xml";
10802 }
10803 else
10804 {
10805 layoutName=argv[optInd+1];
10806 }
10807 writeDefaultLayoutFile(layoutName);
10808 cleanUpDoxygen();
10809 exit(0);
10810 break;
10811 case 'd':
10812 debugLabel=getArg(argc,argv,optInd);
10813 if (debugLabel.isEmpty())
10814 {
10815 devUsage();
10816 cleanUpDoxygen();
10817 exit(0);
10818 }
10819 retVal = Debug::setFlag(debugLabel);
10820 if (!retVal)
10821 {
10822 err("option \"-d\" has unknown debug specifier: \"%s\".\n",qPrint(debugLabel));
10823 devUsage();
10824 cleanUpDoxygen();
10825 exit(1);
10826 }
10827 break;
10828 case 'x':
10829 diffList=TRUE;
10830 break;
10831 case 's':
10832 shortList=TRUE;
10833 break;
10834 case 'u':
10835 updateConfig=TRUE;
10836 break;
10837 case 'e':
10838 formatName=getArg(argc,argv,optInd);
10839 if (formatName.isEmpty())
10840 {
10841 err("option \"-e\" is missing format specifier rtf.\n");
10842 cleanUpDoxygen();
10843 exit(1);
10844 }
10845 if (qstricmp(formatName.data(),"rtf")==0)
10846 {
10847 if (optInd+1>=argc)
10848 {
10849 err("option \"-e rtf\" is missing an extensions file name\n");
10850 cleanUpDoxygen();
10851 exit(1);
10852 }
10853 std::ofstream f;
10854 if (openOutputFile(argv[optInd+1],f))
10855 {
10856 TextStream t(&f);
10857 RTFGenerator::writeExtensionsFile(t);
10858 }
10859 cleanUpDoxygen();
10860 exit(0);
10861 }
10862 err("option \"-e\" has invalid format specifier.\n");
10863 cleanUpDoxygen();
10864 exit(1);
10865 break;
10866 case 'f':
10867 listName=getArg(argc,argv,optInd);
10868 if (listName.isEmpty())
10869 {
10870 err("option \"-f\" is missing list specifier.\n");
10871 cleanUpDoxygen();
10872 exit(1);
10873 }
10874 if (qstricmp(listName.data(),"emoji")==0)
10875 {
10876 if (optInd+1>=argc)
10877 {
10878 err("option \"-f emoji\" is missing an output file name\n");
10879 cleanUpDoxygen();
10880 exit(1);
10881 }
10882 std::ofstream f;
10883 if (openOutputFile(argv[optInd+1],f))
10884 {
10885 TextStream t(&f);
10886 EmojiEntityMapper::instance()->writeEmojiFile(t);
10887 }
10888 cleanUpDoxygen();
10889 exit(0);
10890 }
10891 err("option \"-f\" has invalid list specifier.\n");
10892 cleanUpDoxygen();
10893 exit(1);
10894 break;
10895 case 'w':
10896 formatName=getArg(argc,argv,optInd);
10897 if (formatName.isEmpty())
10898 {
10899 err("option \"-w\" is missing format specifier rtf, html or latex\n");
10900 cleanUpDoxygen();
10901 exit(1);
10902 }
10903 if (qstricmp(formatName.data(),"rtf")==0)
10904 {
10905 if (optInd+1>=argc)
10906 {
10907 err("option \"-w rtf\" is missing a style sheet file name\n");
10908 cleanUpDoxygen();
10909 exit(1);
10910 }
10911 std::ofstream f;
10912 if (openOutputFile(argv[optInd+1],f))
10913 {
10914 TextStream t(&f);
10915 RTFGenerator::writeStyleSheetFile(t);
10916 }
10917 cleanUpDoxygen();
10918 exit(1);
10919 }
10920 else if (qstricmp(formatName.data(),"html")==0)
10921 {
10922 Config::init();
10923 if (optInd+4<argc || FileInfo("Doxyfile").exists())
10924 // explicit config file mentioned or default found on disk
10925 {
10926 QCString df = optInd+4<argc ? argv[optInd+4] : QCString("Doxyfile");
10927 if (!Config::parse(df)) // parse the config file
10928 {
10929 err("error opening or reading configuration file %s!\n",argv[optInd+4]);
10930 cleanUpDoxygen();
10931 exit(1);
10932 }
10933 }
10934 if (optInd+3>=argc)
10935 {
10936 err("option \"-w html\" does not have enough arguments\n");
10937 cleanUpDoxygen();
10938 exit(1);
10939 }
10940 Config::postProcess(TRUE);
10941 Config::updateObsolete();
10942 Config::checkAndCorrect(Config_getBool(QUIET), false);
10943
10944 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
10945
10946 std::ofstream f;
10947 if (openOutputFile(argv[optInd+1],f))
10948 {
10949 TextStream t(&f);
10950 HtmlGenerator::writeHeaderFile(t, argv[optInd+3]);
10951 }
10952 f.close();
10953 if (openOutputFile(argv[optInd+2],f))
10954 {
10955 TextStream t(&f);
10956 HtmlGenerator::writeFooterFile(t);
10957 }
10958 f.close();
10959 if (openOutputFile(argv[optInd+3],f))
10960 {
10961 TextStream t(&f);
10962 HtmlGenerator::writeStyleSheetFile(t);
10963 }
10964 cleanUpDoxygen();
10965 exit(0);
10966 }
10967 else if (qstricmp(formatName.data(),"latex")==0)
10968 {
10969 Config::init();
10970 if (optInd+4<argc || FileInfo("Doxyfile").exists())
10971 {
10972 QCString df = optInd+4<argc ? argv[optInd+4] : QCString("Doxyfile");
10973 if (!Config::parse(df))
10974 {
10975 err("error opening or reading configuration file %s!\n",argv[optInd+4]);
10976 cleanUpDoxygen();
10977 exit(1);
10978 }
10979 }
10980 if (optInd+3>=argc)
10981 {
10982 err("option \"-w latex\" does not have enough arguments\n");
10983 cleanUpDoxygen();
10984 exit(1);
10985 }
10986 Config::postProcess(TRUE);
10987 Config::updateObsolete();
10988 Config::checkAndCorrect(Config_getBool(QUIET), false);
10989
10990 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
10991
10992 std::ofstream f;
10993 if (openOutputFile(argv[optInd+1],f))
10994 {
10995 TextStream t(&f);
10996 LatexGenerator::writeHeaderFile(t);
10997 }
10998 f.close();
10999 if (openOutputFile(argv[optInd+2],f))
11000 {
11001 TextStream t(&f);
11002 LatexGenerator::writeFooterFile(t);
11003 }
11004 f.close();
11005 if (openOutputFile(argv[optInd+3],f))
11006 {
11007 TextStream t(&f);
11008 LatexGenerator::writeStyleSheetFile(t);
11009 }
11010 cleanUpDoxygen();
11011 exit(0);
11012 }
11013 else
11014 {
11015 err("Illegal format specifier \"%s\": should be one of rtf, html or latex\n",qPrint(formatName));
11016 cleanUpDoxygen();
11017 exit(1);
11018 }
11019 break;
11020 case 'm':
11021 g_dumpSymbolMap = TRUE;
11022 break;
11023 case 'v':
11024 version(false);
11025 cleanUpDoxygen();
11026 exit(0);
11027 break;
11028 case 'V':
11029 version(true);
11030 cleanUpDoxygen();
11031 exit(0);
11032 break;
11033 case '-':
11034 if (qstrcmp(&argv[optInd][2],"help")==0)
11035 {
11036 usage(argv[0],versionString);
11037 exit(0);
11038 }
11039 else if (qstrcmp(&argv[optInd][2],"version")==0)
11040 {
11041 version(false);
11042 cleanUpDoxygen();
11043 exit(0);
11044 }
11045 else if ((qstrcmp(&argv[optInd][2],"Version")==0) ||
11046 (qstrcmp(&argv[optInd][2],"VERSION")==0))
11047 {
11048 version(true);
11049 cleanUpDoxygen();
11050 exit(0);
11051 }
11052 else
11053 {
11054 err("Unknown option \"-%s\"\n",&argv[optInd][1]);
11055 usage(argv[0],versionString);
11056 exit(1);
11057 }
11058 break;
11059 case 'b':
11060 setvbuf(stdout,NULL,_IONBF,0);
11061 break;
11062 case 'q':
11063 quiet = true;
11064 break;
11065 case 'T':
11066 msg("Warning: this option activates output generation via Django like template files. "
11067 "This option is scheduled for doxygen 2.0, is currently incomplete and highly experimental! "
11068 "Only use if you are a doxygen developer\n");
11069 g_useOutputTemplate=TRUE;
11070 break;
11071 case 'h':
11072 case '?':
11073 usage(argv[0],versionString);
11074 exit(0);
11075 break;
11076 default:
11077 err("Unknown option \"-%c\"\n",argv[optInd][1]);
11078 usage(argv[0],versionString);
11079 exit(1);
11080 }
11081 optInd++;
11082 }
11083
11084 /**************************************************************************
11085 * Parse or generate the config file *
11086 **************************************************************************/
11087
11088 Config::init();
11089
11090 FileInfo configFileInfo1("Doxyfile"),configFileInfo2("doxyfile");
11091 if (optInd>=argc)
11092 {
11093 if (configFileInfo1.exists())
11094 {
11095 configName="Doxyfile";
11096 }
11097 else if (configFileInfo2.exists())
11098 {
11099 configName="doxyfile";
11100 }
11101 else if (genConfig)
11102 {
11103 configName="Doxyfile";
11104 }
11105 else
11106 {
11107 err("Doxyfile not found and no input file specified!\n");
11108 usage(argv[0],versionString);
11109 exit(1);
11110 }
11111 }
11112 else
11113 {
11114 FileInfo fi(argv[optInd]);
11115 if (fi.exists() || qstrcmp(argv[optInd],"-")==0 || genConfig)
11116 {
11117 configName=argv[optInd];
11118 }
11119 else
11120 {
11121 err("configuration file %s not found!\n",argv[optInd]);
11122 usage(argv[0],versionString);
11123 exit(1);
11124 }
11125 }
11126
11127 if (genConfig && g_useOutputTemplate)
11128 {
11129 generateTemplateFiles("templates");
11130 cleanUpDoxygen();
11131 exit(0);
11132 }
11133
11134 if (genConfig)
11135 {
11136 generateConfigFile(configName,shortList);
11137 cleanUpDoxygen();
11138 exit(0);
11139 }
11140
11141 if (!Config::parse(configName,updateConfig))
11142 {
11143 err("could not open or read configuration file %s!\n",qPrint(configName));
11144 cleanUpDoxygen();
11145 exit(1);
11146 }
11147
11148 if (diffList)
11149 {
11150 Config::updateObsolete();
11151 compareDoxyfile();
11152 cleanUpDoxygen();
11153 exit(0);
11154 }
11155
11156 if (updateConfig)
11157 {
11158 Config::updateObsolete();
11159 generateConfigFile(configName,shortList,TRUE);
11160 cleanUpDoxygen();
11161 exit(0);
11162 }
11163
11164 /* Perlmod wants to know the path to the config file.*/
11165 FileInfo configFileInfo(configName.str());
11166 setPerlModDoxyfile(configFileInfo.absFilePath());
11167
11168 /* handle -q option */
11169 if (quiet) Config_updateBool(QUIET,TRUE);
11170 }
11171
11172 /** check and resolve config options */
checkConfiguration()11173 void checkConfiguration()
11174 {
11175
11176 Config::postProcess(FALSE);
11177 Config::updateObsolete();
11178 Config::checkAndCorrect(Config_getBool(QUIET), true);
11179 initWarningFormat();
11180 }
11181
11182 /** adjust globals that depend on configuration settings. */
adjustConfiguration()11183 void adjustConfiguration()
11184 {
11185 Doxygen::globalScope = createNamespaceDef("<globalScope>",1,1,"<globalScope>");
11186 Doxygen::inputNameLinkedMap = new FileNameLinkedMap;
11187 Doxygen::includeNameLinkedMap = new FileNameLinkedMap;
11188 Doxygen::exampleNameLinkedMap = new FileNameLinkedMap;
11189 Doxygen::imageNameLinkedMap = new FileNameLinkedMap;
11190 Doxygen::dotFileNameLinkedMap = new FileNameLinkedMap;
11191 Doxygen::mscFileNameLinkedMap = new FileNameLinkedMap;
11192 Doxygen::diaFileNameLinkedMap = new FileNameLinkedMap;
11193
11194 setTranslator(Config_getEnum(OUTPUT_LANGUAGE));
11195
11196 /* Set the global html file extension. */
11197 Doxygen::htmlFileExtension = Config_getString(HTML_FILE_EXTENSION);
11198
11199
11200 Doxygen::parseSourcesNeeded = Config_getBool(CALL_GRAPH) ||
11201 Config_getBool(CALLER_GRAPH) ||
11202 Config_getBool(REFERENCES_RELATION) ||
11203 Config_getBool(REFERENCED_BY_RELATION);
11204
11205 /**************************************************************************
11206 * Add custom extension mappings
11207 **************************************************************************/
11208
11209 const StringVector &extMaps = Config_getList(EXTENSION_MAPPING);
11210 for (const auto &mapping : extMaps)
11211 {
11212 QCString mapStr = mapping.c_str();
11213 int i=mapStr.find('=');
11214 if (i==-1)
11215 {
11216 continue;
11217 }
11218 else
11219 {
11220 QCString ext = mapStr.left(i).stripWhiteSpace().lower();
11221 QCString language = mapStr.mid(i+1).stripWhiteSpace().lower();
11222 if (ext.isEmpty() || language.isEmpty())
11223 {
11224 continue;
11225 }
11226
11227 if (!updateLanguageMapping(ext,language))
11228 {
11229 err("Failed to map file extension '%s' to unsupported language '%s'.\n"
11230 "Check the EXTENSION_MAPPING setting in the config file.\n",
11231 qPrint(ext),qPrint(language));
11232 }
11233 else
11234 {
11235 msg("Adding custom extension mapping: '%s' will be treated as language '%s'\n",
11236 qPrint(ext),qPrint(language));
11237 }
11238 }
11239 }
11240
11241 // add predefined macro name to a dictionary
11242 const StringVector &expandAsDefinedList =Config_getList(EXPAND_AS_DEFINED);
11243 for (const auto &s : expandAsDefinedList)
11244 {
11245 Doxygen::expandAsDefinedSet.insert(s.c_str());
11246 }
11247
11248 // read aliases and store them in a dictionary
11249 readAliases();
11250
11251 // store number of spaces in a tab into Doxygen::spaces
11252 int tabSize = Config_getInt(TAB_SIZE);
11253 Doxygen::spaces.resize(tabSize+1);
11254 int sp;for (sp=0;sp<tabSize;sp++) Doxygen::spaces.at(sp)=' ';
11255 Doxygen::spaces.at(tabSize)='\0';
11256 }
11257
11258 #ifdef HAS_SIGNALS
stopDoxygen(int)11259 static void stopDoxygen(int)
11260 {
11261 signal(SIGINT,SIG_DFL); // Re-register signal handler for default action
11262 Dir thisDir;
11263 msg("Cleaning up...\n");
11264 if (!Doxygen::filterDBFileName.isEmpty())
11265 {
11266 thisDir.remove(Doxygen::filterDBFileName.str());
11267 }
11268 killpg(0,SIGINT);
11269 cleanUpDoxygen();
11270 exit(1);
11271 }
11272 #endif
11273
writeTagFile()11274 static void writeTagFile()
11275 {
11276 QCString generateTagFile = Config_getString(GENERATE_TAGFILE);
11277 if (generateTagFile.isEmpty()) return;
11278
11279 std::ofstream f(generateTagFile.str(),std::ofstream::out | std::ofstream::binary);
11280 if (!f.is_open())
11281 {
11282 err("cannot open tag file %s for writing\n",
11283 qPrint(generateTagFile)
11284 );
11285 return;
11286 }
11287 TextStream tagFile(&f);
11288 tagFile << "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>\n";
11289 tagFile << "<tagfile doxygen_version=\"" << getDoxygenVersion() << "\"";
11290 if (strlen(getGitVersion())>0)
11291 {
11292 tagFile << " doxygen_gitid=\"" << getGitVersion() << "\"";
11293 }
11294 tagFile << ">\n";
11295
11296 // for each file
11297 for (const auto &fn : *Doxygen::inputNameLinkedMap)
11298 {
11299 for (const auto &fd : *fn)
11300 {
11301 if (fd->isLinkableInProject()) fd->writeTagFile(tagFile);
11302 }
11303 }
11304 // for each class
11305 for (const auto &cd : *Doxygen::classLinkedMap)
11306 {
11307 ClassDefMutable *cdm = toClassDefMutable(cd.get());
11308 if (cdm && cdm->isLinkableInProject())
11309 {
11310 cdm->writeTagFile(tagFile);
11311 }
11312 }
11313 // for each concept
11314 for (const auto &cd : *Doxygen::conceptLinkedMap)
11315 {
11316 ConceptDefMutable *cdm = toConceptDefMutable(cd.get());
11317 if (cdm && cdm->isLinkableInProject())
11318 {
11319 cdm->writeTagFile(tagFile);
11320 }
11321 }
11322 // for each namespace
11323 for (const auto &nd : *Doxygen::namespaceLinkedMap)
11324 {
11325 NamespaceDefMutable *ndm = toNamespaceDefMutable(nd.get());
11326 if (ndm && nd->isLinkableInProject())
11327 {
11328 ndm->writeTagFile(tagFile);
11329 }
11330 }
11331 // for each group
11332 for (const auto &gd : *Doxygen::groupLinkedMap)
11333 {
11334 if (gd->isLinkableInProject()) gd->writeTagFile(tagFile);
11335 }
11336 // for each page
11337 for (const auto &pd : *Doxygen::pageLinkedMap)
11338 {
11339 if (pd->isLinkableInProject()) pd->writeTagFile(tagFile);
11340 }
11341 if (Doxygen::mainPage) Doxygen::mainPage->writeTagFile(tagFile);
11342
11343 tagFile << "</tagfile>\n";
11344 }
11345
exitDoxygen()11346 static void exitDoxygen()
11347 {
11348 if (!g_successfulRun) // premature exit
11349 {
11350 Dir thisDir;
11351 msg("Exiting...\n");
11352 if (!Doxygen::filterDBFileName.isEmpty())
11353 {
11354 thisDir.remove(Doxygen::filterDBFileName.str());
11355 }
11356 }
11357 }
11358
createOutputDirectory(const QCString & baseDirName,const QCString & formatDirName,const char * defaultDirName)11359 static QCString createOutputDirectory(const QCString &baseDirName,
11360 const QCString &formatDirName,
11361 const char *defaultDirName)
11362 {
11363 QCString result = formatDirName;
11364 if (result.isEmpty())
11365 {
11366 result = baseDirName + defaultDirName;
11367 }
11368 else if (formatDirName[0]!='/' && (formatDirName.length()==1 || formatDirName[1]!=':'))
11369 {
11370 result.prepend(baseDirName+"/");
11371 }
11372 Dir formatDir(result.str());
11373 if (!formatDir.exists() && !formatDir.mkdir(result.str()))
11374 {
11375 err("Could not create output directory %s\n", qPrint(result));
11376 cleanUpDoxygen();
11377 exit(1);
11378 }
11379 return result;
11380 }
11381
getQchFileName()11382 static QCString getQchFileName()
11383 {
11384 QCString const & qchFile = Config_getString(QCH_FILE);
11385 if (!qchFile.isEmpty())
11386 {
11387 return qchFile;
11388 }
11389
11390 QCString const & projectName = Config_getString(PROJECT_NAME);
11391 QCString const & versionText = Config_getString(PROJECT_NUMBER);
11392
11393 return QCString("../qch/")
11394 + (projectName.isEmpty() ? QCString("index") : projectName)
11395 + (versionText.isEmpty() ? QCString("") : QCString("-") + versionText)
11396 + QCString(".qch");
11397 }
11398
searchInputFiles()11399 void searchInputFiles()
11400 {
11401 StringUnorderedSet killSet;
11402
11403 const StringVector &exclPatterns = Config_getList(EXCLUDE_PATTERNS);
11404 bool alwaysRecursive = Config_getBool(RECURSIVE);
11405 StringUnorderedSet excludeNameSet;
11406
11407 // gather names of all files in the include path
11408 g_s.begin("Searching for include files...\n");
11409 killSet.clear();
11410 const StringVector &includePathList = Config_getList(INCLUDE_PATH);
11411 for (const auto &s : includePathList)
11412 {
11413 size_t plSize = Config_getList(INCLUDE_FILE_PATTERNS).size();
11414 const StringVector &pl = plSize==0 ? Config_getList(FILE_PATTERNS) :
11415 Config_getList(INCLUDE_FILE_PATTERNS);
11416 readFileOrDirectory(s.c_str(), // s
11417 Doxygen::includeNameLinkedMap, // fnDict
11418 0, // exclSet
11419 &pl, // patList
11420 &exclPatterns, // exclPatList
11421 0, // resultList
11422 0, // resultSet
11423 alwaysRecursive, // recursive
11424 TRUE, // errorIfNotExist
11425 &killSet); // killSet
11426 }
11427 g_s.end();
11428
11429 g_s.begin("Searching for example files...\n");
11430 killSet.clear();
11431 const StringVector &examplePathList = Config_getList(EXAMPLE_PATH);
11432 for (const auto &s : examplePathList)
11433 {
11434 readFileOrDirectory(s.c_str(), // s
11435 Doxygen::exampleNameLinkedMap, // fnDict
11436 0, // exclSet
11437 &Config_getList(EXAMPLE_PATTERNS), // patList
11438 0, // exclPatList
11439 0, // resultList
11440 0, // resultSet
11441 (alwaysRecursive || Config_getBool(EXAMPLE_RECURSIVE)), // recursive
11442 TRUE, // errorIfNotExist
11443 &killSet); // killSet
11444 }
11445 g_s.end();
11446
11447 g_s.begin("Searching for images...\n");
11448 killSet.clear();
11449 const StringVector &imagePathList=Config_getList(IMAGE_PATH);
11450 for (const auto &s : imagePathList)
11451 {
11452 readFileOrDirectory(s.c_str(), // s
11453 Doxygen::imageNameLinkedMap, // fnDict
11454 0, // exclSet
11455 0, // patList
11456 0, // exclPatList
11457 0, // resultList
11458 0, // resultSet
11459 alwaysRecursive, // recursive
11460 TRUE, // errorIfNotExist
11461 &killSet); // killSet
11462 }
11463 g_s.end();
11464
11465 g_s.begin("Searching for dot files...\n");
11466 killSet.clear();
11467 const StringVector &dotFileList=Config_getList(DOTFILE_DIRS);
11468 for (const auto &s : dotFileList)
11469 {
11470 readFileOrDirectory(s.c_str(), // s
11471 Doxygen::dotFileNameLinkedMap, // fnDict
11472 0, // exclSet
11473 0, // patList
11474 0, // exclPatList
11475 0, // resultList
11476 0, // resultSet
11477 alwaysRecursive, // recursive
11478 TRUE, // errorIfNotExist
11479 &killSet); // killSet
11480 }
11481 g_s.end();
11482
11483 g_s.begin("Searching for msc files...\n");
11484 killSet.clear();
11485 const StringVector &mscFileList=Config_getList(MSCFILE_DIRS);
11486 for (const auto &s : mscFileList)
11487 {
11488 readFileOrDirectory(s.c_str(), // s
11489 Doxygen::mscFileNameLinkedMap, // fnDict
11490 0, // exclSet
11491 0, // patList
11492 0, // exclPatList
11493 0, // resultList
11494 0, // resultSet
11495 alwaysRecursive, // recursive
11496 TRUE, // errorIfNotExist
11497 &killSet); // killSet
11498 }
11499 g_s.end();
11500
11501 g_s.begin("Searching for dia files...\n");
11502 killSet.clear();
11503 const StringVector &diaFileList=Config_getList(DIAFILE_DIRS);
11504 for (const auto &s : diaFileList)
11505 {
11506 readFileOrDirectory(s.c_str(), // s
11507 Doxygen::diaFileNameLinkedMap, // fnDict
11508 0, // exclSet
11509 0, // patList
11510 0, // exclPatList
11511 0, // resultList
11512 0, // resultSet
11513 alwaysRecursive, // recursive
11514 TRUE, // errorIfNotExist
11515 &killSet); // killSet
11516 }
11517 g_s.end();
11518
11519 g_s.begin("Searching for files to exclude\n");
11520 const StringVector &excludeList = Config_getList(EXCLUDE);
11521 for (const auto &s : excludeList)
11522 {
11523 readFileOrDirectory(s.c_str(), // s
11524 0, // fnDict
11525 0, // exclSet
11526 &Config_getList(FILE_PATTERNS), // patList
11527 0, // exclPatList
11528 0, // resultList
11529 &excludeNameSet, // resultSet
11530 alwaysRecursive, // recursive
11531 FALSE); // errorIfNotExist
11532 }
11533 g_s.end();
11534
11535 /**************************************************************************
11536 * Determine Input Files *
11537 **************************************************************************/
11538
11539 g_s.begin("Searching INPUT for files to process...\n");
11540 killSet.clear();
11541 Doxygen::inputPaths.clear();
11542 const StringVector &inputList=Config_getList(INPUT);
11543 for (const auto &s : inputList)
11544 {
11545 QCString path=s.c_str();
11546 uint l = path.length();
11547 if (l>0)
11548 {
11549 // strip trailing slashes
11550 if (path.at(l-1)=='\\' || path.at(l-1)=='/') path=path.left(l-1);
11551
11552 readFileOrDirectory(
11553 path, // s
11554 Doxygen::inputNameLinkedMap, // fnDict
11555 &excludeNameSet, // exclSet
11556 &Config_getList(FILE_PATTERNS), // patList
11557 &exclPatterns, // exclPatList
11558 &g_inputFiles, // resultList
11559 0, // resultSet
11560 alwaysRecursive, // recursive
11561 TRUE, // errorIfNotExist
11562 &killSet, // killSet
11563 &Doxygen::inputPaths); // paths
11564 }
11565 }
11566
11567 // Sort the FileDef objects by full path to get a predictable ordering over multiple runs
11568 std::sort(Doxygen::inputNameLinkedMap->begin(),
11569 Doxygen::inputNameLinkedMap->end(),
11570 [](const auto &f1,const auto &f2)
11571 {
11572 return qstricmp(f1->fullName(),f2->fullName())<0;
11573 });
11574 for (auto &fileName : *Doxygen::inputNameLinkedMap)
11575 {
11576 if (fileName->size()>1)
11577 {
11578 std::sort(fileName->begin(),fileName->end(),[](const auto &f1,const auto &f2)
11579 {
11580 return qstricmp(f1->absFilePath(),f2->absFilePath())<0;
11581 });
11582 }
11583 }
11584 g_s.end();
11585 }
11586
11587
parseInput()11588 void parseInput()
11589 {
11590 atexit(exitDoxygen);
11591
11592 #if USE_LIBCLANG
11593 Doxygen::clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING);
11594 #endif
11595
11596 // we would like to show the versionString earlier, but we first have to handle the configuration file
11597 // to know the value of the QUIET setting.
11598 QCString versionString = getFullVersion();
11599 msg("Doxygen version used: %s\n",qPrint(versionString));
11600
11601 /**************************************************************************
11602 * Make sure the output directory exists
11603 **************************************************************************/
11604 QCString outputDirectory = Config_getString(OUTPUT_DIRECTORY);
11605 if (outputDirectory.isEmpty())
11606 {
11607 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,Dir::currentDirPath().c_str());
11608 }
11609 else
11610 {
11611 Dir dir(outputDirectory.str());
11612 if (!dir.exists())
11613 {
11614 dir.setPath(Dir::currentDirPath());
11615 if (!dir.mkdir(outputDirectory.str()))
11616 {
11617 err("tag OUTPUT_DIRECTORY: Output directory '%s' does not "
11618 "exist and cannot be created\n",qPrint(outputDirectory));
11619 cleanUpDoxygen();
11620 exit(1);
11621 }
11622 else
11623 {
11624 msg("Notice: Output directory '%s' does not exist. "
11625 "I have created it for you.\n", qPrint(outputDirectory));
11626 }
11627 dir.setPath(outputDirectory.str());
11628 }
11629 outputDirectory = Config_updateString(OUTPUT_DIRECTORY,dir.absPath().c_str());
11630 }
11631
11632 /**************************************************************************
11633 * Initialize global lists and dictionaries
11634 **************************************************************************/
11635
11636 // also scale lookup cache with SYMBOL_CACHE_SIZE
11637 int cacheSize = Config_getInt(LOOKUP_CACHE_SIZE);
11638 if (cacheSize<0) cacheSize=0;
11639 if (cacheSize>9) cacheSize=9;
11640 uint lookupSize = 65536 << cacheSize;
11641 Doxygen::lookupCache = new Cache<std::string,LookupInfo>(lookupSize);
11642
11643 #ifdef HAS_SIGNALS
11644 signal(SIGINT, stopDoxygen);
11645 #endif
11646
11647 uint pid = Portable::pid();
11648 Doxygen::filterDBFileName.sprintf("doxygen_filterdb_%d.tmp",pid);
11649 Doxygen::filterDBFileName.prepend(outputDirectory+"/");
11650
11651 /**************************************************************************
11652 * Check/create output directories *
11653 **************************************************************************/
11654
11655 QCString htmlOutput;
11656 bool generateHtml = Config_getBool(GENERATE_HTML);
11657 if (generateHtml || g_useOutputTemplate /* TODO: temp hack */)
11658 {
11659 htmlOutput = createOutputDirectory(outputDirectory,Config_getString(HTML_OUTPUT),"/html");
11660 Config_updateString(HTML_OUTPUT,htmlOutput);
11661
11662 // add HTML indexers that are enabled
11663 bool generateHtmlHelp = Config_getBool(GENERATE_HTMLHELP);
11664 bool generateEclipseHelp = Config_getBool(GENERATE_ECLIPSEHELP);
11665 bool generateQhp = Config_getBool(GENERATE_QHP);
11666 bool generateTreeView = Config_getBool(GENERATE_TREEVIEW);
11667 bool generateDocSet = Config_getBool(GENERATE_DOCSET);
11668 if (generateEclipseHelp) Doxygen::indexList->addIndex<EclipseHelp>();
11669 if (generateHtmlHelp) Doxygen::indexList->addIndex<HtmlHelp>();
11670 if (generateQhp) Doxygen::indexList->addIndex<Qhp>();
11671 if (generateTreeView) Doxygen::indexList->addIndex<FTVHelp>(TRUE);
11672 if (generateDocSet) Doxygen::indexList->addIndex<DocSets>();
11673 Doxygen::indexList->initialize();
11674 }
11675
11676 QCString docbookOutput;
11677 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
11678 if (generateDocbook)
11679 {
11680 docbookOutput = createOutputDirectory(outputDirectory,Config_getString(DOCBOOK_OUTPUT),"/docbook");
11681 Config_updateString(DOCBOOK_OUTPUT,docbookOutput);
11682 }
11683
11684 QCString xmlOutput;
11685 bool generateXml = Config_getBool(GENERATE_XML);
11686 if (generateXml)
11687 {
11688 xmlOutput = createOutputDirectory(outputDirectory,Config_getString(XML_OUTPUT),"/xml");
11689 Config_updateString(XML_OUTPUT,xmlOutput);
11690 }
11691
11692 QCString latexOutput;
11693 bool generateLatex = Config_getBool(GENERATE_LATEX);
11694 if (generateLatex)
11695 {
11696 latexOutput = createOutputDirectory(outputDirectory,Config_getString(LATEX_OUTPUT), "/latex");
11697 Config_updateString(LATEX_OUTPUT,latexOutput);
11698 }
11699
11700 QCString rtfOutput;
11701 bool generateRtf = Config_getBool(GENERATE_RTF);
11702 if (generateRtf)
11703 {
11704 rtfOutput = createOutputDirectory(outputDirectory,Config_getString(RTF_OUTPUT),"/rtf");
11705 Config_updateString(RTF_OUTPUT,rtfOutput);
11706 }
11707
11708 QCString manOutput;
11709 bool generateMan = Config_getBool(GENERATE_MAN);
11710 if (generateMan)
11711 {
11712 manOutput = createOutputDirectory(outputDirectory,Config_getString(MAN_OUTPUT),"/man");
11713 Config_updateString(MAN_OUTPUT,manOutput);
11714 }
11715
11716 #if USE_SQLITE3
11717 QCString sqlOutput;
11718 bool generateSql = Config_getBool(GENERATE_SQLITE3);
11719 if (generateSql)
11720 {
11721 sqlOutput = createOutputDirectory(outputDirectory,Config_getString(SQLITE3_OUTPUT),"/sqlite3");
11722 Config_updateString(SQLITE3_OUTPUT,sqlOutput);
11723 }
11724 #endif
11725
11726 if (Config_getBool(HAVE_DOT))
11727 {
11728 QCString curFontPath = Config_getString(DOT_FONTPATH);
11729 if (curFontPath.isEmpty())
11730 {
11731 Portable::getenv("DOTFONTPATH");
11732 QCString newFontPath = ".";
11733 if (!curFontPath.isEmpty())
11734 {
11735 newFontPath+=Portable::pathListSeparator();
11736 newFontPath+=curFontPath;
11737 }
11738 Portable::setenv("DOTFONTPATH",qPrint(newFontPath));
11739 }
11740 else
11741 {
11742 Portable::setenv("DOTFONTPATH",qPrint(curFontPath));
11743 }
11744 }
11745
11746
11747
11748 /**************************************************************************
11749 * Handle layout file *
11750 **************************************************************************/
11751
11752 LayoutDocManager::instance().init();
11753 QCString layoutFileName = Config_getString(LAYOUT_FILE);
11754 bool defaultLayoutUsed = FALSE;
11755 if (layoutFileName.isEmpty())
11756 {
11757 layoutFileName = Config_updateString(LAYOUT_FILE,"DoxygenLayout.xml");
11758 defaultLayoutUsed = TRUE;
11759 }
11760
11761 FileInfo fi(layoutFileName.str());
11762 if (fi.exists())
11763 {
11764 msg("Parsing layout file %s...\n",qPrint(layoutFileName));
11765 LayoutDocManager::instance().parse(layoutFileName);
11766 }
11767 else if (!defaultLayoutUsed)
11768 {
11769 warn_uncond("failed to open layout file '%s' for reading!\n",qPrint(layoutFileName));
11770 }
11771
11772 /**************************************************************************
11773 * Read and preprocess input *
11774 **************************************************************************/
11775
11776 // prevent search in the output directories
11777 StringVector exclPatterns = Config_getList(EXCLUDE_PATTERNS);
11778 if (generateHtml) exclPatterns.push_back(htmlOutput.str());
11779 if (generateDocbook) exclPatterns.push_back(docbookOutput.str());
11780 if (generateXml) exclPatterns.push_back(xmlOutput.str());
11781 if (generateLatex) exclPatterns.push_back(latexOutput.str());
11782 if (generateRtf) exclPatterns.push_back(rtfOutput.str());
11783 if (generateMan) exclPatterns.push_back(manOutput.str());
11784 Config_updateList(EXCLUDE_PATTERNS,exclPatterns);
11785
11786 searchInputFiles();
11787
11788 // Notice: the order of the function calls below is very important!
11789
11790 if (Config_getBool(GENERATE_HTML) && !Config_getBool(USE_MATHJAX))
11791 {
11792 FormulaManager::instance().readFormulas(Config_getString(HTML_OUTPUT));
11793 }
11794 if (Config_getBool(GENERATE_RTF))
11795 {
11796 // in case GENERRATE_HTML is set we just have to compare, both repositories should be identical
11797 FormulaManager::instance().readFormulas(Config_getString(RTF_OUTPUT),
11798 Config_getBool(GENERATE_HTML) &&
11799 !Config_getBool(USE_MATHJAX));
11800 }
11801 if (Config_getBool(GENERATE_DOCBOOK))
11802 {
11803 // in case GENERRATE_HTML is set we just have to compare, both repositories should be identical
11804 FormulaManager::instance().readFormulas(Config_getString(DOCBOOK_OUTPUT),
11805 (Config_getBool(GENERATE_HTML) &&
11806 !Config_getBool(USE_MATHJAX)) ||
11807 Config_getBool(GENERATE_RTF));
11808 }
11809
11810 /**************************************************************************
11811 * Handle Tag Files *
11812 **************************************************************************/
11813
11814 std::shared_ptr<Entry> root = std::make_shared<Entry>();
11815 msg("Reading and parsing tag files\n");
11816
11817 const StringVector &tagFileList = Config_getList(TAGFILES);
11818 for (const auto &s : tagFileList)
11819 {
11820 readTagFile(root,s.c_str());
11821 }
11822
11823 /**************************************************************************
11824 * Parse source files *
11825 **************************************************************************/
11826
11827 addSTLSupport(root);
11828
11829 g_s.begin("Parsing files\n");
11830 if (Config_getInt(NUM_PROC_THREADS)==1)
11831 {
11832 parseFilesSingleThreading(root);
11833 }
11834 else
11835 {
11836 parseFilesMultiThreading(root);
11837 }
11838 g_s.end();
11839
11840 /**************************************************************************
11841 * Gather information *
11842 **************************************************************************/
11843
11844 g_s.begin("Building macro definition list...\n");
11845 buildDefineList();
11846 g_s.end();
11847
11848 g_s.begin("Building group list...\n");
11849 buildGroupList(root.get());
11850 organizeSubGroups(root.get());
11851 g_s.end();
11852
11853 g_s.begin("Building directory list...\n");
11854 buildDirectories();
11855 findDirDocumentation(root.get());
11856 g_s.end();
11857
11858 g_s.begin("Building namespace list...\n");
11859 buildNamespaceList(root.get());
11860 findUsingDirectives(root.get());
11861 g_s.end();
11862
11863 g_s.begin("Building file list...\n");
11864 buildFileList(root.get());
11865 g_s.end();
11866
11867 g_s.begin("Building class list...\n");
11868 buildClassList(root.get());
11869 g_s.end();
11870
11871 g_s.begin("Building concept list...\n");
11872 buildConceptList(root.get());
11873 g_s.end();
11874
11875 // build list of using declarations here (global list)
11876 buildListOfUsingDecls(root.get());
11877 g_s.end();
11878
11879 g_s.begin("Computing nesting relations for classes...\n");
11880 resolveClassNestingRelations();
11881 g_s.end();
11882 // 1.8.2-20121111: no longer add nested classes to the group as well
11883 //distributeClassGroupRelations();
11884
11885 // calling buildClassList may result in cached relations that
11886 // become invalid after resolveClassNestingRelations(), that's why
11887 // we need to clear the cache here
11888 Doxygen::lookupCache->clear();
11889 // we don't need the list of using declaration anymore
11890 g_usingDeclarations.clear();
11891
11892 g_s.begin("Associating documentation with classes...\n");
11893 buildClassDocList(root.get());
11894 g_s.end();
11895
11896 g_s.begin("Associating documentation with concepts...\n");
11897 buildConceptDocList(root.get());
11898 distributeConceptGroups();
11899 g_s.end();
11900
11901 g_s.begin("Building example list...\n");
11902 buildExampleList(root.get());
11903 g_s.end();
11904
11905 g_s.begin("Searching for enumerations...\n");
11906 findEnums(root.get());
11907 g_s.end();
11908
11909 // Since buildVarList calls isVarWithConstructor
11910 // and this calls getResolvedClass we need to process
11911 // typedefs first so the relations between classes via typedefs
11912 // are properly resolved. See bug 536385 for an example.
11913 g_s.begin("Searching for documented typedefs...\n");
11914 buildTypedefList(root.get());
11915 g_s.end();
11916
11917 if (Config_getBool(OPTIMIZE_OUTPUT_SLICE))
11918 {
11919 g_s.begin("Searching for documented sequences...\n");
11920 buildSequenceList(root.get());
11921 g_s.end();
11922
11923 g_s.begin("Searching for documented dictionaries...\n");
11924 buildDictionaryList(root.get());
11925 g_s.end();
11926 }
11927
11928 g_s.begin("Searching for members imported via using declarations...\n");
11929 // this should be after buildTypedefList in order to properly import
11930 // used typedefs
11931 findUsingDeclarations(root.get(),TRUE); // do for python packages first
11932 findUsingDeclarations(root.get(),FALSE); // then the rest
11933 g_s.end();
11934
11935 g_s.begin("Searching for included using directives...\n");
11936 findIncludedUsingDirectives();
11937 g_s.end();
11938
11939 g_s.begin("Searching for documented variables...\n");
11940 buildVarList(root.get());
11941 g_s.end();
11942
11943 g_s.begin("Building interface member list...\n");
11944 buildInterfaceAndServiceList(root.get()); // UNO IDL
11945
11946 g_s.begin("Building member list...\n"); // using class info only !
11947 buildFunctionList(root.get());
11948 g_s.end();
11949
11950 g_s.begin("Searching for friends...\n");
11951 findFriends();
11952 g_s.end();
11953
11954 g_s.begin("Searching for documented defines...\n");
11955 findDefineDocumentation(root.get());
11956 g_s.end();
11957
11958 g_s.begin("Computing class inheritance relations...\n");
11959 findClassEntries(root.get());
11960 findInheritedTemplateInstances();
11961 g_s.end();
11962
11963 g_s.begin("Computing class usage relations...\n");
11964 findUsedTemplateInstances();
11965 g_s.end();
11966
11967 if (Config_getBool(INLINE_SIMPLE_STRUCTS))
11968 {
11969 g_s.begin("Searching for tag less structs...\n");
11970 findTagLessClasses();
11971 g_s.end();
11972 }
11973
11974 g_s.begin("Flushing cached template relations that have become invalid...\n");
11975 flushCachedTemplateRelations();
11976 g_s.end();
11977
11978 g_s.begin("Computing class relations...\n");
11979 computeTemplateClassRelations();
11980 flushUnresolvedRelations();
11981 if (Config_getBool(OPTIMIZE_OUTPUT_VHDL))
11982 {
11983 VhdlDocGen::computeVhdlComponentRelations();
11984 }
11985 computeClassRelations();
11986 g_classEntries.clear();
11987 g_s.end();
11988
11989 g_s.begin("Add enum values to enums...\n");
11990 addEnumValuesToEnums(root.get());
11991 findEnumDocumentation(root.get());
11992 g_s.end();
11993
11994 g_s.begin("Searching for member function documentation...\n");
11995 findObjCMethodDefinitions(root.get());
11996 findMemberDocumentation(root.get()); // may introduce new members !
11997 findUsingDeclImports(root.get()); // may introduce new members !
11998
11999 transferRelatedFunctionDocumentation();
12000 transferFunctionDocumentation();
12001 g_s.end();
12002
12003 // moved to after finding and copying documentation,
12004 // as this introduces new members see bug 722654
12005 g_s.begin("Creating members for template instances...\n");
12006 createTemplateInstanceMembers();
12007 g_s.end();
12008
12009 g_s.begin("Building page list...\n");
12010 buildPageList(root.get());
12011 g_s.end();
12012
12013 g_s.begin("Search for main page...\n");
12014 findMainPage(root.get());
12015 findMainPageTagFiles(root.get());
12016 g_s.end();
12017
12018 g_s.begin("Computing page relations...\n");
12019 computePageRelations(root.get());
12020 checkPageRelations();
12021 g_s.end();
12022
12023 g_s.begin("Determining the scope of groups...\n");
12024 findGroupScope(root.get());
12025 g_s.end();
12026
12027 auto memberNameComp = [](const MemberNameLinkedMap::Ptr &n1,const MemberNameLinkedMap::Ptr &n2)
12028 {
12029 return qstricmp(n1->memberName().data()+getPrefixIndex(n1->memberName()),
12030 n2->memberName().data()+getPrefixIndex(n2->memberName())
12031 )<0;
12032 };
12033
12034 auto classComp = [](const ClassLinkedMap::Ptr &c1,const ClassLinkedMap::Ptr &c2)
12035 {
12036 if (Config_getBool(SORT_BY_SCOPE_NAME))
12037 {
12038 return qstricmp(c1->name(), c2->name())<0;
12039 }
12040 else
12041 {
12042 int i = qstricmp(c1->className(), c2->className());
12043 return i==0 ? qstricmp(c1->name(), c2->name())<0 : i<0;
12044 }
12045 };
12046
12047 auto namespaceComp = [](const NamespaceLinkedMap::Ptr &n1,const NamespaceLinkedMap::Ptr &n2)
12048 {
12049 return qstricmp(n1->name(),n2->name())<0;
12050 };
12051
12052 auto conceptComp = [](const ConceptLinkedMap::Ptr &c1,const ConceptLinkedMap::Ptr &c2)
12053 {
12054 return qstricmp(c1->name(),c2->name())<0;
12055 };
12056
12057 g_s.begin("Sorting lists...\n");
12058 std::sort(Doxygen::memberNameLinkedMap->begin(),
12059 Doxygen::memberNameLinkedMap->end(),
12060 memberNameComp);
12061 std::sort(Doxygen::functionNameLinkedMap->begin(),
12062 Doxygen::functionNameLinkedMap->end(),
12063 memberNameComp);
12064 std::sort(Doxygen::hiddenClassLinkedMap->begin(),
12065 Doxygen::hiddenClassLinkedMap->end(),
12066 classComp);
12067 std::sort(Doxygen::classLinkedMap->begin(),
12068 Doxygen::classLinkedMap->end(),
12069 classComp);
12070 std::sort(Doxygen::conceptLinkedMap->begin(),
12071 Doxygen::conceptLinkedMap->end(),
12072 conceptComp);
12073 std::sort(Doxygen::namespaceLinkedMap->begin(),
12074 Doxygen::namespaceLinkedMap->end(),
12075 namespaceComp);
12076 g_s.end();
12077
12078 g_s.begin("Determining which enums are documented\n");
12079 findDocumentedEnumValues();
12080 g_s.end();
12081
12082 g_s.begin("Computing member relations...\n");
12083 mergeCategories();
12084 computeMemberRelations();
12085 g_s.end();
12086
12087 g_s.begin("Building full member lists recursively...\n");
12088 buildCompleteMemberLists();
12089 g_s.end();
12090
12091 g_s.begin("Adding members to member groups.\n");
12092 addMembersToMemberGroup();
12093 g_s.end();
12094
12095 if (Config_getBool(DISTRIBUTE_GROUP_DOC))
12096 {
12097 g_s.begin("Distributing member group documentation.\n");
12098 distributeMemberGroupDocumentation();
12099 g_s.end();
12100 }
12101
12102 g_s.begin("Computing member references...\n");
12103 computeMemberReferences();
12104 g_s.end();
12105
12106 if (Config_getBool(INHERIT_DOCS))
12107 {
12108 g_s.begin("Inheriting documentation...\n");
12109 inheritDocumentation();
12110 g_s.end();
12111 }
12112
12113 // compute the shortest possible names of all files
12114 // without losing the uniqueness of the file names.
12115 g_s.begin("Generating disk names...\n");
12116 generateDiskNames();
12117 g_s.end();
12118
12119 g_s.begin("Adding source references...\n");
12120 addSourceReferences();
12121 g_s.end();
12122
12123 g_s.begin("Adding xrefitems...\n");
12124 addListReferences();
12125 generateXRefPages();
12126 g_s.end();
12127
12128 g_s.begin("Sorting member lists...\n");
12129 sortMemberLists();
12130 g_s.end();
12131
12132 g_s.begin("Setting anonymous enum type...\n");
12133 setAnonymousEnumType();
12134 g_s.end();
12135
12136 if (Config_getBool(DIRECTORY_GRAPH))
12137 {
12138 g_s.begin("Computing dependencies between directories...\n");
12139 computeDirDependencies();
12140 g_s.end();
12141 }
12142
12143 g_s.begin("Generating citations page...\n");
12144 CitationManager::instance().generatePage();
12145 g_s.end();
12146
12147 g_s.begin("Counting members...\n");
12148 countMembers();
12149 g_s.end();
12150
12151 g_s.begin("Counting data structures...\n");
12152 countDataStructures();
12153 g_s.end();
12154
12155 g_s.begin("Resolving user defined references...\n");
12156 resolveUserReferences();
12157 g_s.end();
12158
12159 g_s.begin("Finding anchors and sections in the documentation...\n");
12160 findSectionsInDocumentation();
12161 g_s.end();
12162
12163 g_s.begin("Transferring function references...\n");
12164 transferFunctionReferences();
12165 g_s.end();
12166
12167 g_s.begin("Combining using relations...\n");
12168 combineUsingRelations();
12169 g_s.end();
12170
12171 g_s.begin("Adding members to index pages...\n");
12172 addMembersToIndex();
12173 addToIndices();
12174 g_s.end();
12175
12176 g_s.begin("Correcting members for VHDL...\n");
12177 vhdlCorrectMemberProperties();
12178 g_s.end();
12179
12180 g_s.begin("Computing tooltip texts...\n");
12181 computeTooltipTexts();
12182 g_s.end();
12183
12184 if (Config_getBool(SORT_GROUP_NAMES))
12185 {
12186 std::sort(Doxygen::groupLinkedMap->begin(),
12187 Doxygen::groupLinkedMap->end(),
12188 [](const auto &g1,const auto &g2)
12189 { return g1->groupTitle() < g2->groupTitle(); });
12190
12191 for (const auto &gd : *Doxygen::groupLinkedMap)
12192 {
12193 gd->sortSubGroups();
12194 }
12195 }
12196
12197 }
12198
generateOutput()12199 void generateOutput()
12200 {
12201 /**************************************************************************
12202 * Initialize output generators *
12203 **************************************************************************/
12204
12205 /// add extra languages for which we can only produce syntax highlighted code
12206 addCodeOnlyMappings();
12207
12208 //// dump all symbols
12209 if (g_dumpSymbolMap)
12210 {
12211 dumpSymbolMap();
12212 exit(0);
12213 }
12214
12215 initSearchIndexer();
12216
12217 bool generateHtml = Config_getBool(GENERATE_HTML);
12218 bool generateLatex = Config_getBool(GENERATE_LATEX);
12219 bool generateMan = Config_getBool(GENERATE_MAN);
12220 bool generateRtf = Config_getBool(GENERATE_RTF);
12221 bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
12222
12223
12224 g_outputList = new OutputList;
12225 if (generateHtml)
12226 {
12227 g_outputList->add<HtmlGenerator>();
12228 HtmlGenerator::init();
12229 HtmlGenerator::writeTabData();
12230 }
12231 if (generateLatex)
12232 {
12233 g_outputList->add<LatexGenerator>();
12234 LatexGenerator::init();
12235 }
12236 if (generateDocbook)
12237 {
12238 g_outputList->add<DocbookGenerator>();
12239 DocbookGenerator::init();
12240 }
12241 if (generateMan)
12242 {
12243 g_outputList->add<ManGenerator>();
12244 ManGenerator::init();
12245 }
12246 if (generateRtf)
12247 {
12248 g_outputList->add<RTFGenerator>();
12249 RTFGenerator::init();
12250 }
12251 if (Config_getBool(USE_HTAGS))
12252 {
12253 Htags::useHtags = TRUE;
12254 QCString htmldir = Config_getString(HTML_OUTPUT);
12255 if (!Htags::execute(htmldir))
12256 err("USE_HTAGS is YES but htags(1) failed. \n");
12257 else if (!Htags::loadFilemap(htmldir))
12258 err("htags(1) ended normally but failed to load the filemap. \n");
12259 }
12260
12261 /**************************************************************************
12262 * Generate documentation *
12263 **************************************************************************/
12264
12265 g_s.begin("Generating style sheet...\n");
12266 //printf("writing style info\n");
12267 g_outputList->writeStyleInfo(0); // write first part
12268 g_s.end();
12269
12270 static bool searchEngine = Config_getBool(SEARCHENGINE);
12271 static bool serverBasedSearch = Config_getBool(SERVER_BASED_SEARCH);
12272
12273 g_s.begin("Generating search indices...\n");
12274 if (searchEngine && !serverBasedSearch && (generateHtml || g_useOutputTemplate))
12275 {
12276 createJavaScriptSearchIndex();
12277 }
12278
12279 // generate search indices (need to do this before writing other HTML
12280 // pages as these contain a drop down menu with options depending on
12281 // what categories we find in this function.
12282 if (generateHtml && searchEngine)
12283 {
12284 QCString searchDirName = Config_getString(HTML_OUTPUT)+"/search";
12285 Dir searchDir(searchDirName.str());
12286 if (!searchDir.exists() && !searchDir.mkdir(searchDirName.str()))
12287 {
12288 term("Could not create search results directory '%s' $PWD='%s'\n",
12289 qPrint(searchDirName),Dir::currentDirPath().c_str());
12290 }
12291 HtmlGenerator::writeSearchData(searchDirName);
12292 if (!serverBasedSearch) // client side search index
12293 {
12294 writeJavaScriptSearchIndex();
12295 }
12296 }
12297 g_s.end();
12298
12299 // copy static stuff
12300 if (generateHtml)
12301 {
12302 FTVHelp::generateTreeViewImages();
12303 copyStyleSheet();
12304 copyLogo(Config_getString(HTML_OUTPUT));
12305 copyExtraFiles(Config_getList(HTML_EXTRA_FILES),"HTML_EXTRA_FILES",Config_getString(HTML_OUTPUT));
12306 }
12307 if (generateLatex)
12308 {
12309 copyLatexStyleSheet();
12310 copyLogo(Config_getString(LATEX_OUTPUT));
12311 copyExtraFiles(Config_getList(LATEX_EXTRA_FILES),"LATEX_EXTRA_FILES",Config_getString(LATEX_OUTPUT));
12312 }
12313 if (generateDocbook)
12314 {
12315 copyLogo(Config_getString(DOCBOOK_OUTPUT));
12316 }
12317 if (generateRtf)
12318 {
12319 copyLogo(Config_getString(RTF_OUTPUT));
12320 }
12321
12322 const FormulaManager &fm = FormulaManager::instance();
12323 if (fm.hasFormulas() && generateHtml
12324 && !Config_getBool(USE_MATHJAX))
12325 {
12326 g_s.begin("Generating images for formulas in HTML...\n");
12327 fm.generateImages(Config_getString(HTML_OUTPUT), Config_getEnum(HTML_FORMULA_FORMAT)==HTML_FORMULA_FORMAT_t::svg ?
12328 FormulaManager::Format::Vector : FormulaManager::Format::Bitmap, FormulaManager::HighDPI::On);
12329 g_s.end();
12330 }
12331 if (fm.hasFormulas() && generateRtf)
12332 {
12333 g_s.begin("Generating images for formulas in RTF...\n");
12334 fm.generateImages(Config_getString(RTF_OUTPUT),FormulaManager::Format::Bitmap);
12335 g_s.end();
12336 }
12337
12338 if (fm.hasFormulas() && generateDocbook)
12339 {
12340 g_s.begin("Generating images for formulas in Docbook...\n");
12341 fm.generateImages(Config_getString(DOCBOOK_OUTPUT),FormulaManager::Format::Bitmap);
12342 g_s.end();
12343 }
12344
12345 g_s.begin("Generating example documentation...\n");
12346 generateExampleDocs();
12347 g_s.end();
12348
12349 warn_flush();
12350
12351 g_s.begin("Generating file sources...\n");
12352 generateFileSources();
12353 g_s.end();
12354
12355 g_s.begin("Generating file documentation...\n");
12356 generateFileDocs();
12357 g_s.end();
12358
12359 g_s.begin("Generating page documentation...\n");
12360 generatePageDocs();
12361 g_s.end();
12362
12363 g_s.begin("Generating group documentation...\n");
12364 generateGroupDocs();
12365 g_s.end();
12366
12367 g_s.begin("Generating class documentation...\n");
12368 generateClassDocs();
12369 g_s.end();
12370
12371 g_s.begin("Generating concept documentation...\n");
12372 generateConceptDocs();
12373 g_s.end();
12374
12375 g_s.begin("Generating namespace index...\n");
12376 generateNamespaceDocs();
12377 g_s.end();
12378
12379 if (Config_getBool(GENERATE_LEGEND))
12380 {
12381 g_s.begin("Generating graph info page...\n");
12382 writeGraphInfo(*g_outputList);
12383 g_s.end();
12384 }
12385
12386 g_s.begin("Generating directory documentation...\n");
12387 generateDirDocs(*g_outputList);
12388 g_s.end();
12389
12390 if (g_outputList->size()>0)
12391 {
12392 writeIndexHierarchy(*g_outputList);
12393 }
12394
12395 g_s.begin("finalizing index lists...\n");
12396 Doxygen::indexList->finalize();
12397 g_s.end();
12398
12399 g_s.begin("writing tag file...\n");
12400 writeTagFile();
12401 g_s.end();
12402
12403 if (Config_getBool(GENERATE_XML))
12404 {
12405 g_s.begin("Generating XML output...\n");
12406 Doxygen::generatingXmlOutput=TRUE;
12407 generateXML();
12408 Doxygen::generatingXmlOutput=FALSE;
12409 g_s.end();
12410 }
12411 #if USE_SQLITE3
12412 if (Config_getBool(GENERATE_SQLITE3))
12413 {
12414 g_s.begin("Generating SQLITE3 output...\n");
12415 generateSqlite3();
12416 g_s.end();
12417 }
12418 #endif
12419
12420 if (Config_getBool(GENERATE_AUTOGEN_DEF))
12421 {
12422 g_s.begin("Generating AutoGen DEF output...\n");
12423 generateDEF();
12424 g_s.end();
12425 }
12426 if (Config_getBool(GENERATE_PERLMOD))
12427 {
12428 g_s.begin("Generating Perl module output...\n");
12429 generatePerlMod();
12430 g_s.end();
12431 }
12432 if (generateHtml && searchEngine && serverBasedSearch)
12433 {
12434 g_s.begin("Generating search index\n");
12435 if (Doxygen::searchIndex->kind()==SearchIndexIntf::Internal) // write own search index
12436 {
12437 HtmlGenerator::writeSearchPage();
12438 Doxygen::searchIndex->write(Config_getString(HTML_OUTPUT)+"/search/search.idx");
12439 }
12440 else // write data for external search index
12441 {
12442 HtmlGenerator::writeExternalSearchPage();
12443 QCString searchDataFile = Config_getString(SEARCHDATA_FILE);
12444 if (searchDataFile.isEmpty())
12445 {
12446 searchDataFile="searchdata.xml";
12447 }
12448 if (!Portable::isAbsolutePath(searchDataFile.data()))
12449 {
12450 searchDataFile.prepend(Config_getString(OUTPUT_DIRECTORY)+"/");
12451 }
12452 Doxygen::searchIndex->write(searchDataFile);
12453 }
12454 g_s.end();
12455 }
12456
12457 if (g_useOutputTemplate)
12458 {
12459 g_s.begin("Generating output via template engine...\n");
12460 generateOutputViaTemplate();
12461 g_s.end();
12462 }
12463
12464 warn_flush();
12465
12466 if (generateRtf)
12467 {
12468 g_s.begin("Combining RTF output...\n");
12469 if (!RTFGenerator::preProcessFileInplace(Config_getString(RTF_OUTPUT),"refman.rtf"))
12470 {
12471 err("An error occurred during post-processing the RTF files!\n");
12472 }
12473 g_s.end();
12474 }
12475
12476 warn_flush();
12477
12478 g_s.begin("Running plantuml with JAVA...\n");
12479 PlantumlManager::instance().run();
12480 g_s.end();
12481
12482 warn_flush();
12483
12484 if (Config_getBool(HAVE_DOT))
12485 {
12486 g_s.begin("Running dot...\n");
12487 DotManager::instance()->run();
12488 g_s.end();
12489 }
12490
12491 if (generateHtml &&
12492 Config_getBool(GENERATE_HTMLHELP) &&
12493 !Config_getString(HHC_LOCATION).isEmpty())
12494 {
12495 g_s.begin("Running html help compiler...\n");
12496 std::string oldDir = Dir::currentDirPath();
12497 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
12498 Portable::setShortDir();
12499 Portable::sysTimerStart();
12500 if (Portable::system(Config_getString(HHC_LOCATION).data(), "index.hhp", Debug::isFlagSet(Debug::ExtCmd))!=1)
12501 {
12502 err("failed to run html help compiler on index.hhp\n");
12503 }
12504 Portable::sysTimerStop();
12505 Dir::setCurrent(oldDir);
12506 g_s.end();
12507 }
12508
12509 warn_flush();
12510
12511 if ( generateHtml &&
12512 Config_getBool(GENERATE_QHP) &&
12513 !Config_getString(QHG_LOCATION).isEmpty())
12514 {
12515 g_s.begin("Running qhelpgenerator...\n");
12516 QCString qhpFileName = Qhp::getQhpFileName();
12517 QCString qchFileName = getQchFileName();
12518
12519 QCString args = QCString().sprintf("%s -o \"%s\"", qPrint(qhpFileName), qPrint(qchFileName));
12520 std::string oldDir = Dir::currentDirPath();
12521 Dir::setCurrent(Config_getString(HTML_OUTPUT).str());
12522 Portable::sysTimerStart();
12523 if (Portable::system(Config_getString(QHG_LOCATION).data(), args.data(), FALSE))
12524 {
12525 err("failed to run qhelpgenerator on index.qhp\n");
12526 }
12527 Portable::sysTimerStop();
12528 Dir::setCurrent(oldDir);
12529 g_s.end();
12530 }
12531
12532 g_outputList->cleanup();
12533
12534 int cacheParam;
12535 msg("lookup cache used %zu/%zu hits=%" PRIu64 " misses=%" PRIu64 "\n",
12536 Doxygen::lookupCache->size(),
12537 Doxygen::lookupCache->capacity(),
12538 Doxygen::lookupCache->hits(),
12539 Doxygen::lookupCache->misses());
12540 cacheParam = computeIdealCacheParam(static_cast<size_t>(Doxygen::lookupCache->misses()*2/3)); // part of the cache is flushed, hence the 2/3 correction factor
12541 if (cacheParam>Config_getInt(LOOKUP_CACHE_SIZE))
12542 {
12543 msg("Note: based on cache misses the ideal setting for LOOKUP_CACHE_SIZE is %d at the cost of higher memory usage.\n",cacheParam);
12544 }
12545
12546 if (Debug::isFlagSet(Debug::Time))
12547 {
12548 msg("Total elapsed time: %.6f seconds\n(of which %.6f seconds waiting for external tools to finish)\n",
12549 ((double)Debug::elapsedTime()),
12550 Portable::getSysElapsedTime()
12551 );
12552 g_s.print();
12553 }
12554 else
12555 {
12556 msg("finished...\n");
12557 }
12558
12559
12560 /**************************************************************************
12561 * Start cleaning up *
12562 **************************************************************************/
12563
12564 cleanUpDoxygen();
12565
12566 finalizeSearchIndexer();
12567 Dir thisDir;
12568 thisDir.remove(Doxygen::filterDBFileName.str());
12569 finishWarnExit();
12570 Config::deinit();
12571 delete Doxygen::clangUsrMap;
12572 g_successfulRun=TRUE;
12573 }
12574