Doxygen
Loading...
Searching...
No Matches
clangparser.cpp
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * Copyright (C) 1997-2026 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// own include
17#include "clangparser.h"
18
19// standard includes
20#include <cstdio>
21#include <cstdint>
22#include <cstdlib>
23#include <vector>
24#include <mutex>
25
26// other includes
27#include "settings.h"
28
29#if USE_LIBCLANG
30// clang includes
31#include "clang/Tooling/CompilationDatabase.h"
32#include "clang/Tooling/Tooling.h"
33#include "clang-c/Index.h"
34
35// other includes
36#include "config.h"
37#include "doxygen.h"
38#include "filedef.h"
39#include "filename.h"
40#include "memberdef.h"
41#include "membername.h"
42#include "message.h"
43#include "outputgen.h"
44#include "outputlist.h"
45#include "searchindex.h"
46#include "tooltip.h"
47#include "trace.h"
48#include "utf8.h"
49#include "util.h"
50#endif
51
52//--------------------------------------------------------------------------
53
54static std::mutex g_clangMutex;
55
57{
58 std::lock_guard<std::mutex> lock(g_clangMutex);
59 if (s_instance==nullptr) s_instance = new ClangParser;
60 return s_instance;
61}
62
64
65//--------------------------------------------------------------------------
66#if USE_LIBCLANG
67
68static std::mutex g_docCrossReferenceMutex;
69
70enum class DetectedLang { Cpp, ObjC, ObjCpp };
71
72static const char * keywordToType(const char *keyword)
73{
74 static const StringUnorderedSet flowKeywords({
75 "break", "case", "catch", "continue", "default", "do",
76 "else", "finally", "for", "foreach", "for each", "goto",
77 "if", "return", "switch", "throw", "throws", "try",
78 "while", "@try", "@catch", "@finally" });
79 static const StringUnorderedSet typeKeywords({
80 "bool", "char", "double", "float", "int", "long", "object",
81 "short", "signed", "unsigned", "void", "wchar_t", "size_t",
82 "boolean", "id", "SEL", "string", "nullptr" });
83 if (flowKeywords.find(keyword)!=flowKeywords.end()) return "keywordflow";
84 if (typeKeywords.find(keyword)!=typeKeywords.end()) return "keywordtype";
85 return "keyword";
86}
87
88
89//--------------------------------------------------------------------------
90
92{
93 public:
94 Private(const ClangParser &p,const FileDef *fd)
95 : parser(p), fileDef(fd) {}
96 const ClangParser &parser;
97 const FileDef *fileDef;
98 CXIndex index = nullptr;
99 uint32_t curToken = 0;
100 DetectedLang detectedLang = DetectedLang::Cpp;
101 size_t numFiles = 0;
102 std::vector<DString> sources;
103 std::vector<CXUnsavedFile> ufs;
104 std::vector<CXCursor> cursors;
105 std::unordered_map<std::string,uint32_t> fileMapping;
106 CXTranslationUnit tu = nullptr;
107 CXToken *tokens = nullptr;
108 uint32_t numTokens = 0;
110 TooltipManager tooltipManager;
111 std::vector<const Definition *> foldStack;
112
113 // state while parsing sources
114 const MemberDef *currentMemberDef=nullptr;
115 uint32_t currentLine=0;
116 bool searchForBody=false;
117 bool insideBody=false;
118 uint32_t bracketCount=0;
119};
120
121ClangTUParser::ClangTUParser(const ClangParser &parser,const FileDef *fd)
122 : p(std::make_unique<Private>(parser,fd))
123{
124 //printf("ClangTUParser::ClangTUParser() this=%p\n",this);
125}
126
128{
129 return p->filesInSameTU;
130}
131
133{
134 //printf("ClangTUParser::parse() this=%p\n",this);
135 DString fileName = p->fileDef->absFilePath();
136 p->fileDef->getAllIncludeFilesRecursively(p->filesInSameTU);
137 //printf("ClangTUParser::ClangTUParser(fileName=%s,#filesInSameTU=%d)\n",
138 // qPrint(fileName),(int)p->filesInSameTU.size());
139 bool clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING);
140 bool clangIncludeInputPaths = Config_getBool(CLANG_ADD_INC_PATHS);
141 bool filterSourceFiles = Config_getBool(FILTER_SOURCE_FILES);
142 StringVector includePath = Config_getList(INCLUDE_PATH);
143 StringVector clangOptions = Config_getList(CLANG_OPTIONS);
144 if (!clangAssistedParsing) return;
145 //printf("ClangParser::start(%s)\n",fileName);
146 ASSERT(p->index==nullptr);
147 ASSERT(p->tokens==nullptr);
148 ASSERT(p->numTokens==0);
149 p->index = clang_createIndex(0, 0);
150 p->curToken = 0;
151 p->cursors.clear();
152 std::vector<clang::tooling::CompileCommand> command;
153 if (p->parser.database()!=nullptr)
154 {
155 // check if the file we are parsing is in the DB
156 command = p->parser.database()->getCompileCommands(fileName.data());
157 }
158 std::vector<char *> argv;
159 if (!command.empty() )
160 {
161 std::vector<std::string> options = command[command.size()-1].CommandLine;
162 // copy each compiler option used from the database. Skip the first which is compiler exe.
163 for (auto option = options.begin()+1; option != options.end(); option++)
164 {
165 argv.push_back(dstrdup(option->c_str()));
166 }
167 // The last compile command (last entry of argv) should be the filename of the source
168 // file to parse. It does not matter to clang_parseTranslationUnit below if we pass the file name
169 // separately in its second argument or if we just pass it a nullptr as the second
170 // argument and pass the file name with the other compile commands.
171 // However, in some cases (e.g., starting from Clang 14, if we are parsing a header file, see
172 // https://github.com/doxygen/doxygen/issues/10733), the compile commands returned by
173 // getCompileCommands include a "--" as second to last argument (which is supposed to make it
174 // easier to parse the argument list). If we pass this "--" to clang_parseTranslationUnit below,
175 // it returns an error. To avoid this, we remove the file name argument (and the "--" if present)
176 // from argv and pass the file name separately.
177 dstrfree(argv.back());
178 argv.pop_back(); // remove file name
179 if (argv.size()>0 && dstrcmp(argv[argv.size() - 1],"--")==0) {
180 // remove '--' from argv
181 dstrfree(argv.back());
182 argv.pop_back();
183 }
184
185 // user specified options
186 for (size_t i=0;i<clangOptions.size();i++)
187 {
188 argv.push_back(dstrdup(clangOptions[i].c_str()));
189 }
190 // this extra addition to argv is accounted for as we are skipping the first entry in
191 argv.push_back(dstrdup("-w")); // finally, turn off warnings.
192 }
193 else
194 {
195 // add include paths for input files
196 if (clangIncludeInputPaths)
197 {
198 for (const std::string &path : Doxygen::inputPaths)
199 {
200 DString inc = DString("-I")+path.data();
201 argv.push_back(dstrdup(inc.data()));
202 //printf("argv[%d]=%s\n",argc,argv[argc]);
203 }
204 }
205 // add external include paths
206 for (size_t i=0;i<includePath.size();i++)
207 {
208 DString inc = "-I"+includePath[i];
209 argv.push_back(dstrdup(inc.data()));
210 }
211 // user specified options
212 for (size_t i=0;i<clangOptions.size();i++)
213 {
214 argv.push_back(dstrdup(clangOptions[i].c_str()));
215 }
216 // extra options
217 argv.push_back(dstrdup("-ferror-limit=0"));
218 argv.push_back(dstrdup("-x"));
219
220 // Since we can be presented with a .h file that can contain C/C++ or
221 // Objective C code and we need to configure the parser before knowing this,
222 // we use the source file to detected the language. Detection will fail if you
223 // pass a bunch of .h files containing ObjC code, and no sources :-(
224 SrcLangExt lang = getLanguageFromFileName(fileName);
225 DString fn = fileName.lower();
226 if (lang==SrcLangExt::ObjC || p->detectedLang!=DetectedLang::Cpp)
227 {
228 if (p->detectedLang!=DetectedLang::Cpp &&
229 (fn.endsWith(".cpp") || fn.endsWith(".cxx") ||
230 fn.endsWith(".cc") || fn.endsWith(".c")))
231 { // fall back to C/C++ once we see an extension that indicates this
232 p->detectedLang = DetectedLang::Cpp;
233 }
234 else if (fn.endsWith(".mm")) // switch to Objective C++
235 {
236 p->detectedLang = DetectedLang::ObjCpp;
237 }
238 else if (fn.endsWith(".m")) // switch to Objective C
239 {
240 p->detectedLang = DetectedLang::ObjC;
241 }
242 }
243 switch (p->detectedLang)
244 {
245 case DetectedLang::Cpp:
246 if (fn.endsWith(".hpp") || fn.endsWith(".hxx") ||
247 fn.endsWith(".hh") || fn.endsWith(".h"))
248 argv.push_back(dstrdup("c++-header"));
249 else
250 argv.push_back(dstrdup("c++"));
251 break;
252 case DetectedLang::ObjC: argv.push_back(dstrdup("objective-c")); break;
253 case DetectedLang::ObjCpp: argv.push_back(dstrdup("objective-c++")); break;
254 }
255 }
256 //printf("source %s ----------\n%s\n-------------\n\n",
257 // fileName,p->source.data());
258 size_t numUnsavedFiles = p->filesInSameTU.size()+1;
259 p->numFiles = numUnsavedFiles;
260 p->sources.resize(numUnsavedFiles);
261 p->ufs.resize(numUnsavedFiles);
262 size_t refIndent = 0;
263 p->sources[0] = detab(fileToString(fileName,filterSourceFiles,true),refIndent);
264 p->ufs[0].Filename = dstrdup(fileName.data());
265 p->ufs[0].Contents = p->sources[0].data();
266 p->ufs[0].Length = p->sources[0].length();
267 p->fileMapping.emplace(fileName.data(),0);
268 size_t i=1;
269 for (auto it = p->filesInSameTU.begin();
270 it != p->filesInSameTU.end() && i<numUnsavedFiles;
271 ++it, i++)
272 {
273 p->fileMapping.emplace(std::make_pair(*it,static_cast<uint32_t>(i)));
274 p->sources[i] = detab(fileToString(DString(*it),filterSourceFiles,true),refIndent);
275 p->ufs[i].Filename = dstrdup(it->c_str());
276 p->ufs[i].Contents = p->sources[i].data();
277 p->ufs[i].Length = p->sources[i].length();
278 }
279
280 // let libclang do the actual parsing
281 //for (i=0;i<argv.size();i++) printf("Argument %d: %s\n",i,argv[i]);
282 p->tu = clang_parseTranslationUnit(p->index, fileName.data(),
283 argv.data(), static_cast<int>(argv.size()), p->ufs.data(), numUnsavedFiles,
284 CXTranslationUnit_DetailedPreprocessingRecord);
285 //printf(" tu=%p\n",p->tu);
286 // free arguments
287 for (i=0;i<argv.size();++i)
288 {
289 dstrfree(argv[i]);
290 }
291
292 if (p->tu)
293 {
294 // show any warnings that the compiler produced
295 size_t n=clang_getNumDiagnostics(p->tu);
296 for (i=0; i!=n; ++i)
297 {
298 CXDiagnostic diag = clang_getDiagnostic(p->tu, static_cast<unsigned>(i));
299 CXString string = clang_formatDiagnostic(diag,
300 clang_defaultDiagnosticDisplayOptions());
301 err("{} [clang]\n",clang_getCString(string));
302 clang_disposeString(string);
303 clang_disposeDiagnostic(diag);
304 }
305 }
306 else
307 {
308 err("clang: Failed to parse translation unit {}\n",fileName);
309 }
310}
311
313{
314 //printf("ClangTUParser::~ClangTUParser() this=%p\n",this);
315 bool clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING);
316 if (!clangAssistedParsing) return;
317 if (p->tu)
318 {
319 p->cursors.clear();
320 clang_disposeTokens(p->tu,p->tokens,p->numTokens);
321 clang_disposeTranslationUnit(p->tu);
322 clang_disposeIndex(p->index);
323 p->fileMapping.clear();
324 p->tokens = nullptr;
325 p->numTokens = 0;
326 }
327 for (size_t i=0;i<p->numFiles;i++)
328 {
329 dstrfree(p->ufs[i].Filename);
330 }
331 p->ufs.clear();
332 p->sources.clear();
333 p->numFiles = 0;
334 p->tu = nullptr;
335}
336
338{
339 //printf("ClangTUParser::switchToFile(%s) this=%p\n",qPrint(fd->absFilePath()),this);
340 if (p->tu)
341 {
342 p->cursors.clear();
343 clang_disposeTokens(p->tu,p->tokens,p->numTokens);
344 p->tokens = nullptr;
345 p->numTokens = 0;
346
347 CXFile f = clang_getFile(p->tu, fd->absFilePath().data());
348 auto it = p->fileMapping.find(fd->absFilePath().data());
349 if (it!=p->fileMapping.end() && it->second < p->numFiles)
350 {
351 uint32_t i = it->second;
352 //printf("switchToFile %s: len=%ld\n",fileName,p->ufs[i].Length);
353 CXSourceLocation fileBegin = clang_getLocationForOffset(p->tu, f, 0);
354 CXSourceLocation fileEnd = clang_getLocationForOffset(p->tu, f, p->ufs[i].Length);
355 CXSourceRange fileRange = clang_getRange(fileBegin, fileEnd);
356
357 clang_tokenize(p->tu,fileRange,&p->tokens,&p->numTokens);
358 p->cursors.resize(p->numTokens);
359 clang_annotateTokens(p->tu,p->tokens,p->numTokens,p->cursors.data());
360 p->curToken = 0;
361 }
362 else
363 {
364 err("clang: Failed to find input file {} in mapping\n",fd->absFilePath());
365 }
366 }
367}
368
369std::string ClangTUParser::lookup(uint32_t line,const char *symbol)
370{
371 AUTO_TRACE("line={},symbol={}",line,symbol);
372 std::string result;
373 if (symbol==nullptr) return result;
374 bool clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING);
375 if (!clangAssistedParsing) return result;
376
377 auto getCurrentTokenLine = [this]() -> uint32_t
378 {
379 uint32_t l=0, c=0;
380 if (p->numTokens==0) return 1;
381 // guard against filters that reduce the number of lines
382 if (p->curToken>=p->numTokens) p->curToken=p->numTokens-1;
383 CXSourceLocation start = clang_getTokenLocation(p->tu,p->tokens[p->curToken]);
384 clang_getSpellingLocation(start, nullptr, &l, &c, nullptr);
385 return l;
386 };
387
388 int sl = strlen(symbol);
389 uint32_t l = getCurrentTokenLine();
390 while (l>=line && p->curToken>0)
391 {
392 if (l==line) // already at the right line
393 {
394 p->curToken--; // linear search to start of the line
395 l = getCurrentTokenLine();
396 }
397 else
398 {
399 p->curToken/=2; // binary search backward
400 l = getCurrentTokenLine();
401 }
402 }
403 bool found=false;
404 while (l<=line && p->curToken<p->numTokens && !found)
405 {
406 CXString tokenString = clang_getTokenSpelling(p->tu, p->tokens[p->curToken]);
407 if (l==line)
408 {
409 AUTO_TRACE_ADD("try to match symbol {} with token {}",symbol,clang_getCString(tokenString));
410 }
411 const char *ts = clang_getCString(tokenString);
412 int tl = strlen(ts);
413 int startIndex = p->curToken;
414 if (l==line && strncmp(ts,symbol,tl)==0) // found partial match at the correct line
415 {
416 int offset = tl;
417 while (offset<sl) // symbol spans multiple tokens
418 {
419 //printf("found partial match\n");
420 p->curToken++;
421 if (p->curToken>=p->numTokens)
422 {
423 break; // end of token stream
424 }
425 l = getCurrentTokenLine();
426 clang_disposeString(tokenString);
427 tokenString = clang_getTokenSpelling(p->tu, p->tokens[p->curToken]);
428 ts = clang_getCString(tokenString);
429 tl = ts ? strlen(ts) : 0;
430 // skip over any spaces in the symbol
431 char c = 0;
432 while (offset<sl && ((c=symbol[offset])==' ' || c=='\t' || c=='\r' || c=='\n'))
433 {
434 offset++;
435 }
436 if (strncmp(ts,symbol+offset,tl)!=0) // next token matches?
437 {
438 //printf("no match '%s'<->'%s'\n",ts,symbol+offset);
439 break; // no match
440 }
441 AUTO_TRACE_ADD("partial match '{}'<->'{}'",ts,symbol+offset);
442 offset+=tl;
443 }
444 if (offset==sl) // symbol matches the token(s)
445 {
446 CXCursor c = p->cursors[p->curToken];
447 CXString usr = clang_getCursorUSR(c);
448 AUTO_TRACE_ADD("found full match {} usr='{}'",symbol,clang_getCString(usr));
449 result = clang_getCString(usr);
450 clang_disposeString(usr);
451 found=true;
452 }
453 else // reset token cursor to start of the search
454 {
455 p->curToken = startIndex;
456 }
457 }
458 clang_disposeString(tokenString);
459 p->curToken++;
460 if (p->curToken<p->numTokens)
461 {
462 l = getCurrentTokenLine();
463 }
464 }
465 if (!found)
466 {
467 AUTO_TRACE_EXIT("Did not find symbol {} at line {} :-(",symbol,line);
468 }
469 else
470 {
471 AUTO_TRACE_EXIT("Found symbol {} usr={}",symbol,result);
472 }
473 return result;
474}
475
476void ClangTUParser::codeFolding(OutputCodeList &ol,const Definition *d,uint32_t line)
477{
478 if (Config_getBool(HTML_CODE_FOLDING))
479 {
480 endCodeFold(ol,line);
481 if (d)
482 {
483 int startLine = d->getStartDefLine();
484 int endLine = d->getEndBodyLine();
485 if (endLine!=-1 && startLine!=endLine &&
486 // since the end of a section is closed after the last line, we need to avoid starting a
487 // new section if the previous section ends at the same line, i.e. something like
488 // struct X {
489 // ...
490 // }; struct S { <- start of S and end of X at the same line
491 // ...
492 // };
493 (p->foldStack.empty() || p->foldStack.back()->getEndBodyLine()!=startLine))
494 {
496 {
497 const MemberDef *md = toMemberDef(d);
498 if (md && md->isDefine())
499 {
500 ol.startFold(line,"",""); // #define X ...
501 }
502 else if (md && md->isCallable())
503 {
504 ol.startFold(line,"{","}"); // func() { ... }
505 }
506 else
507 {
508 ol.startFold(line,"{","};"); // enum X { ... }
509 }
510 }
512 {
513 ol.startFold(line,"{","};"); // class X { ... };
514 }
515 else
516 {
517 ol.startFold(line,"{","}"); // namespace X {...}
518 }
519 p->foldStack.push_back(d);
520 }
521 }
522 }
523}
524
525void ClangTUParser::endCodeFold(OutputCodeList &ol,uint32_t line)
526{
527 while (!p->foldStack.empty())
528 {
529 const Definition *dd = p->foldStack.back();
530 if (dd->getEndBodyLine()+1==static_cast<int>(line))
531 {
532 ol.endFold();
533 p->foldStack.pop_back();
534 }
535 else
536 {
537 break;
538 }
539 }
540}
541
542void ClangTUParser::writeLineNumber(OutputCodeList &ol,const FileDef *fd,uint32_t line,bool writeLineAnchor)
543{
544 const Definition *d = fd ? fd->getSourceDefinition(line) : nullptr;
545 if (d)
546 {
547 p->currentLine=line;
548 const MemberDef *md = fd->getSourceMember(line);
549 //printf("writeLineNumber(%p,line=%d)\n",(void*)md,line);
550 if (md && md->isLinkable()) // link to member
551 {
552 if (p->currentMemberDef!=md) // new member, start search for body
553 {
554 p->searchForBody=true;
555 p->insideBody=false;
556 p->bracketCount=0;
557 }
558 p->currentMemberDef=md;
559 codeFolding(ol,md,line);
561 md->getOutputFileBase(),
562 md->anchor(),
563 line,writeLineAnchor);
564 }
565 else if (d->isLinkable()) // link to compound
566 {
567 p->currentMemberDef=nullptr;
568 codeFolding(ol,d,line);
571 d->anchor(),
572 line,writeLineAnchor);
573 }
574 else // no link
575 {
576 codeFolding(ol,nullptr,line);
577 ol.writeLineNumber(DString(),DString(),DString(),line,writeLineAnchor);
578 }
579 }
580 else // no link
581 {
582 codeFolding(ol,nullptr,line);
583 ol.writeLineNumber(DString(),DString(),DString(),line,writeLineAnchor);
584 }
585
586 // set search page target
587 if (Doxygen::searchIndex.enabled())
588 {
589 DString lineAnchor;
590 lineAnchor.sprintf("l%05d",line);
591 Doxygen::searchIndex.setCurrentDoc(fd,lineAnchor,true);
592 }
593
594 //printf("writeLineNumber(%d) g_searchForBody=%d\n",line,g_searchForBody);
595}
596
597void ClangTUParser::codifyLines(OutputCodeList &ol,const FileDef *fd,const char *text,
598 uint32_t &line,uint32_t &column,const char *fontClass)
599{
600 if (fontClass) ol.startFontClass(fontClass);
601 const char *p=text,*sp=p;
602 char c = 0;
603 bool inlineCodeFragment = false;
604 bool done=false;
605 while (!done)
606 {
607 sp=p;
608 while ((c=*p++) && c!='\n') { column++; }
609 if (c=='\n')
610 {
611 line++;
612 size_t l = static_cast<size_t>(p-sp-1);
613 column=l+1;
614 ol.codify(DString(sp,l));
615 if (fontClass) ol.endFontClass();
616 ol.endCodeLine();
617 writeLineNumber(ol,fd,line,inlineCodeFragment);
618 ol.startCodeLine(line);
619 if (fontClass) ol.startFontClass(fontClass);
620 }
621 else
622 {
623 ol.codify(sp);
624 done=true;
625 }
626 }
627 if (fontClass) ol.endFontClass();
628}
629
631 const FileDef *fd,uint32_t &line,uint32_t &column,
632 const Definition *d,
633 const char *text)
634{
635 bool sourceTooltips = Config_getBool(SOURCE_TOOLTIPS);
636 p->tooltipManager.addTooltip(d);
637 DString ref = d->getReference();
638 DString file = d->getOutputFileBase();
639 DString anchor = d->anchor();
640 DString tooltip;
641 if (!sourceTooltips) // fall back to simple "title" tooltips
642 {
643 tooltip = d->briefDescriptionAsTooltip();
644 }
645 bool inlineCodeFragment = false;
646 bool done=false;
647 const char *p=text;
648 while (!done)
649 {
650 const char *sp=p;
651 char c = 0;
652 while ((c=*p++) && c!='\n') { column++; }
653 if (c=='\n')
654 {
655 line++;
656 //printf("writeCodeLink(%s,%s,%s,%s)\n",ref,file,anchor,sp);
657 ol.writeCodeLink(d->codeSymbolType(),ref,file,anchor,DString(sp,p-sp-1),tooltip);
658 ol.endCodeLine();
659 writeLineNumber(ol,fd,line,inlineCodeFragment);
660 ol.startCodeLine(line);
661 }
662 else
663 {
664 //printf("writeCodeLink(%s,%s,%s,%s)\n",ref,file,anchor,sp);
665 ol.writeCodeLink(d->codeSymbolType(),ref,file,anchor,sp,tooltip);
666 done=true;
667 }
668 }
669}
670
672 uint32_t &line,uint32_t &column,const char *text)
673{
674 DString incName = text;
675 incName = incName.mid(1,incName.length()-2); // strip ".." or <..>
676 FileDef *ifd=nullptr;
677 if (!incName.empty())
678 {
679 FileName *fn = Doxygen::inputNameLinkedMap->find(incName);
680 if (fn)
681 {
682 // see if this source file actually includes the file
683 auto it = std::find_if(fn->begin(),
684 fn->end(),
685 [&fd](const auto &ifd)
686 { return fd->isIncluded(ifd->absFilePath()); });
687 bool found = it!=fn->end();
688 if (found)
689 {
690 //printf(" include file %s found=%d\n",(*it)->absFilePath().data(),found);
691 ifd = it->get();
692 }
693 }
694 }
695 if (ifd)
696 {
698 ifd->getReference(),
699 ifd->getOutputFileBase(),
700 DString(),
701 text,
703 }
704 else
705 {
706 codifyLines(ol,ifd,text,line,column,"preprocessor");
707 }
708}
709
711 uint32_t &line,uint32_t &column,const char *text)
712{
713 MemberName *mn=Doxygen::functionNameLinkedMap->find(text);
714 if (mn)
715 {
716 for (const auto &md : *mn)
717 {
718 if (md->isDefine())
719 {
720 writeMultiLineCodeLink(ol,fd,line,column,md.get(),text);
721 return;
722 }
723 }
724 }
725 codifyLines(ol,fd,text,line,column);
726}
727
728
730 uint32_t &line,uint32_t &column,const char *text,int tokenIndex)
731{
732 AUTO_TRACE("line={} column={} text={}",line,column,text);
733 CXCursor c = p->cursors[tokenIndex];
734 CXCursorKind cKind = clang_getCursorKind(c);
735 AUTO_TRACE_ADD("cursor kind={}",(int)cKind);
736 CXCursor r = clang_getCursorReferenced(c);
737 AUTO_TRACE_ADD("cursor reference kind={}",(int)clang_getCursorKind(r));
738 if (!clang_equalCursors(r, c))
739 {
740 AUTO_TRACE_ADD("link to referenced location");
741 c=r; // link to referenced location
742 }
743 if (!clang_isDeclaration(cKind))
744 {
745 CXCursor t = clang_getSpecializedCursorTemplate(c);
746 AUTO_TRACE_ADD("cursor template kind={}",(int)clang_getCursorKind(t));
747 if (!clang_Cursor_isNull(t) && !clang_equalCursors(t,c))
748 {
749 c=t; // link to template
750 }
751 }
752 CXString usr = clang_getCursorUSR(c);
753 const char *usrStr = clang_getCString(usr);
754 AUTO_TRACE_ADD("usr={}",usrStr);
755
756 const Definition *d = nullptr;
757 auto kv = Doxygen::clangUsrMap->find(usrStr);
758 if (kv!=Doxygen::clangUsrMap->end())
759 {
760 d = kv->second;
761 }
762 if (d==0)
763 {
764 AUTO_TRACE_ADD("didn't find definition for '{}' usr='{}' kind={}",
765 text,usrStr,(int)clang_getCursorKind(c));
766 }
767 else
768 {
769 AUTO_TRACE_ADD("found definition for '{}' usr='{}' name='{}'",
770 text,usrStr,d->name().data());
771 }
772
773 if (d && d->isLinkable())
774 {
775 //printf("linkIdentifier(%s) p->insideBody=%d p->currentMemberDef=%p\n",text,p->insideBody,(void*)p->currentMemberDef);
776 if (p->insideBody &&
777 p->currentMemberDef && d->definitionType()==Definition::TypeMember &&
778 (p->currentMemberDef!=d || p->currentLine<line)) // avoid self-reference
779 {
780 std::lock_guard<std::mutex> lock(g_docCrossReferenceMutex);
781 addDocCrossReference(p->currentMemberDef,toMemberDef(d));
782 }
783 writeMultiLineCodeLink(ol,fd,line,column,d,text);
784 }
785 else
786 {
787 codifyLines(ol,fd,text,line,column);
788 }
789 clang_disposeString(usr);
790}
791
792void ClangTUParser::detectFunctionBody(const char *s)
793{
794 //printf("punct=%s g_searchForBody=%d g_insideBody=%d g_bracketCount=%d\n",
795 // s,g_searchForBody,g_insideBody,g_bracketCount);
796
797 if (p->searchForBody && (dstrcmp(s,":")==0 || dstrcmp(s,"{")==0)) // start of 'body' (: is for constructor)
798 {
799 p->searchForBody=false;
800 p->insideBody=true;
801 }
802 else if (p->searchForBody && dstrcmp(s,";")==0) // declaration only
803 {
804 p->searchForBody=false;
805 p->insideBody=false;
806 }
807 if (p->insideBody && dstrcmp(s,"{")==0) // increase scoping level
808 {
809 p->bracketCount++;
810 }
811 if (p->insideBody && dstrcmp(s,"}")==0) // decrease scoping level
812 {
813 p->bracketCount--;
814 if (p->bracketCount<=0) // got outside of function body
815 {
816 p->insideBody=false;
817 p->bracketCount=0;
818 }
819 }
820}
821
823{
824 AUTO_TRACE("file={}",fd->name());
825 // (re)set global parser state
826 p->currentMemberDef=nullptr;
827 p->currentLine=0;
828 p->searchForBody=false;
829 p->insideBody=false;
830 p->bracketCount=0;
831 p->foldStack.clear();
832
833 unsigned int line=1,column=1;
834 DString lineNumber,lineAnchor;
835 bool inlineCodeFragment = false;
836 writeLineNumber(ol,fd,line,!inlineCodeFragment);
837 ol.startCodeLine(line);
838 for (unsigned int i=0;i<p->numTokens;i++)
839 {
840 CXSourceLocation start = clang_getTokenLocation(p->tu, p->tokens[i]);
841 unsigned int l=0, c=0;
842 clang_getSpellingLocation(start, nullptr, &l, &c, nullptr);
843 if (l > line) column = 1;
844 while (line<l)
845 {
846 line++;
847 ol.endCodeLine();
848 writeLineNumber(ol,fd,line,!inlineCodeFragment);
849 ol.startCodeLine(line);
850 }
851 while (column<c) { ol.codify(" "); column++; }
852 CXString tokenString = clang_getTokenSpelling(p->tu, p->tokens[i]);
853 char const *s = clang_getCString(tokenString);
854 CXCursorKind cursorKind = clang_getCursorKind(p->cursors[i]);
855 CXTokenKind tokenKind = clang_getTokenKind(p->tokens[i]);
856 //printf("%d:%d %s cursorKind=%d tokenKind=%d\n",line,column,s,cursorKind,tokenKind);
857 switch (tokenKind)
858 {
859 case CXToken_Keyword:
860 if (strcmp(s,"operator")==0)
861 {
862 linkIdentifier(ol,fd,line,column,s,i);
863 }
864 else
865 {
866 codifyLines(ol,fd,s,line,column,
867 cursorKind==CXCursor_PreprocessingDirective ? "preprocessor" :
868 keywordToType(s));
869 }
870 break;
871 case CXToken_Literal:
872 if (cursorKind==CXCursor_InclusionDirective)
873 {
874 linkInclude(ol,fd,line,column,s);
875 }
876 else if (s[0]=='"' || s[0]=='\'')
877 {
878 codifyLines(ol,fd,s,line,column,"stringliteral");
879 }
880 else
881 {
882 codifyLines(ol,fd,s,line,column);
883 }
884 break;
885 case CXToken_Comment:
886 codifyLines(ol,fd,s,line,column,"comment");
887 break;
888 default: // CXToken_Punctuation or CXToken_Identifier
889 if (tokenKind==CXToken_Punctuation)
890 {
892 //printf("punct %s: %d\n",s,cursorKind);
893 }
894 switch (cursorKind)
895 {
896 case CXCursor_PreprocessingDirective:
897 codifyLines(ol,fd,s,line,column,"preprocessor");
898 break;
899 case CXCursor_MacroDefinition:
900 codifyLines(ol,fd,s,line,column,"preprocessor");
901 break;
902 case CXCursor_InclusionDirective:
903 linkInclude(ol,fd,line,column,s);
904 break;
905 case CXCursor_MacroExpansion:
906 linkMacro(ol,fd,line,column,s);
907 break;
908 default:
909 if (tokenKind==CXToken_Identifier ||
910 (tokenKind==CXToken_Punctuation && // for operators
911 (cursorKind==CXCursor_DeclRefExpr ||
912 cursorKind==CXCursor_MemberRefExpr ||
913 cursorKind==CXCursor_CallExpr ||
914 cursorKind==CXCursor_ObjCMessageExpr)
915 )
916 )
917 {
918 linkIdentifier(ol,fd,line,column,s,i);
919 if (Doxygen::searchIndex.enabled())
920 {
922 }
923 }
924 else
925 {
926 codifyLines(ol,fd,s,line,column);
927 }
928 break;
929 }
930 }
931 clang_disposeString(tokenString);
932 }
933 ol.endCodeLine();
934 if (Config_getBool(HTML_CODE_FOLDING))
935 {
936 while (!p->foldStack.empty())
937 {
938 ol.endFold();
939 p->foldStack.pop_back();
940 }
941 }
942 p->tooltipManager.writeTooltips(ol);
943}
944
945//--------------------------------------------------------------------------
946
948{
949 public:
950 Private()
951 {
952 std::string error;
953 DString clangCompileDatabase = Config_getString(CLANG_DATABASE_PATH);
954 // load a clang compilation database (https://clang.llvm.org/docs/JSONCompilationDatabase.html)
955 db = clang::tooling::CompilationDatabase::loadFromDirectory(clangCompileDatabase.data(), error);
956 if (!clangCompileDatabase.empty() && clangCompileDatabase!="0" && db==nullptr)
957 {
958 // user specified a path, but DB file was not found
959 err("{} using clang compilation database path of: \"{}\"\n", error, clangCompileDatabase);
960 }
961 }
962
963 std::unique_ptr<clang::tooling::CompilationDatabase> db;
964};
965
966const clang::tooling::CompilationDatabase *ClangParser::database() const
967{
968 return p->db.get();
969}
970
971ClangParser::ClangParser() : p(std::make_unique<Private>())
972{
973}
974
976{
977}
978
979std::unique_ptr<ClangTUParser> ClangParser::createTUParser(const FileDef *fd) const
980{
981 //printf("ClangParser::createTUParser()\n");
982 return std::make_unique<ClangTUParser>(*this,fd);
983}
984
985
986//--------------------------------------------------------------------------
987#else // use stubbed functionality in case libclang support is disabled.
988
990{
991};
992
994{
995}
996
998{
999}
1000
1002{
1003}
1004
1008
1009std::string ClangTUParser::lookup(uint32_t,const char *)
1010{
1011 return std::string();
1012}
1013
1015{
1016};
1017
1019{
1020}
1021
1025
1026std::unique_ptr<ClangTUParser> ClangParser::createTUParser(const FileDef *) const
1027{
1028 return nullptr;
1029}
1030
1031#endif
1032//--------------------------------------------------------------------------
1033
static std::mutex g_clangMutex
Wrapper for to let libclang assisted parsing.
Definition clangparser.h:80
static ClangParser * s_instance
Definition clangparser.h:94
std::unique_ptr< ClangTUParser > createTUParser(const FileDef *fd) const
virtual ~ClangParser()
const clang::tooling::CompilationDatabase * database() const
static ClangParser * instance()
Returns the one and only instance of the class.
std::unique_ptr< Private > p
Definition clangparser.h:90
StringVector filesInSameTU() const
Returns the list of files for this translation unit.
ClangTUParser(const ClangParser &parser, const FileDef *fd)
void linkIdentifier(OutputCodeList &ol, const FileDef *fd, uint32_t &line, uint32_t &column, const char *text, int tokenIndex)
void linkInclude(OutputCodeList &ol, const FileDef *fd, uint32_t &line, uint32_t &column, const char *text)
void endCodeFold(OutputCodeList &ol, uint32_t line)
void writeLineNumber(OutputCodeList &ol, const FileDef *fd, uint32_t line, bool writeLineAnchor)
void codeFolding(OutputCodeList &ol, const Definition *d, uint32_t line)
void switchToFile(const FileDef *fd)
Switches to another file within the translation unit started with start().
void parse()
Parse the file given at construction time as a translation unit This file should already be preproces...
virtual ~ClangTUParser()
void codifyLines(OutputCodeList &ol, const FileDef *fd, const char *text, uint32_t &line, uint32_t &column, const char *fontClass=nullptr)
void linkMacro(OutputCodeList &ol, const FileDef *fd, uint32_t &line, uint32_t &column, const char *text)
std::string lookup(uint32_t line, const char *symbol)
Looks for symbol which should be found at line.
std::unique_ptr< Private > p
Definition clangparser.h:75
void writeMultiLineCodeLink(OutputCodeList &ol, const FileDef *fd, uint32_t &line, uint32_t &column, const Definition *d, const char *text)
void detectFunctionBody(const char *s)
void writeSources(OutputCodeList &ol, const FileDef *fd)
writes the syntax highlighted source code for a file
DString mid(size_t index, size_t len=npos) const
Definition dstring.h:318
DString lower() const
Definition dstring.h:326
bool empty() const
Returns true iff the string is empty (std::string compatible alias for isEmpty()).
Definition dstring.h:148
DString & sprintf(const char *format,...)
Definition dstring.cpp:34
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition dstring.h:157
bool endsWith(const char *s) const
Definition dstring.h:617
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition dstring.h:151
The common base class of all entity definitions found in the sources.
Definition definition.h:77
virtual int getEndBodyLine() const =0
virtual bool isLinkable() const =0
virtual DefType definitionType() const =0
virtual const DString & name() const =0
virtual DString briefDescriptionAsTooltip() const =0
virtual DString anchor() const =0
virtual int getStartDefLine() const =0
virtual DString getReference() const =0
virtual CodeSymbolType codeSymbolType() const =0
virtual DString getOutputFileBase() const =0
static StringUnorderedSet inputPaths
Definition doxygen.h:96
static FileNameLinkedMap * inputNameLinkedMap
Definition doxygen.h:97
static MemberNameLinkedMap * functionNameLinkedMap
Definition doxygen.h:105
static SearchIndexIntf searchIndex
Definition doxygen.h:117
static ClangUsrMap * clangUsrMap
Definition doxygen.h:119
A model of a file symbol.
Definition filedef.h:97
virtual const Definition * getSourceDefinition(int lineNr) const =0
virtual const MemberDef * getSourceMember(int lineNr) const =0
virtual DString absFilePath() const =0
const T * find(const std::string &key) const
Definition linkedmap.h:47
virtual bool isDefine() const =0
virtual bool isCallable() const =0
Class representing a list of different code generators.
Definition outputlist.h:162
void writeLineNumber(const DString &ref, const DString &file, const DString &anchor, int lineNumber, bool writeLineAnchor)
Definition outputlist.h:253
void endCodeLine()
Definition outputlist.h:264
void startFold(int lineNr, const DString &startMarker, const DString &endMarker)
Definition outputlist.h:282
void writeCodeLink(CodeSymbolType type, const DString &ref, const DString &file, const DString &anchor, const DString &name, const DString &tooltip)
Definition outputlist.h:247
void endFontClass()
Definition outputlist.h:270
void startFontClass(const DString &c)
Definition outputlist.h:267
void startCodeLine(int lineNr)
Definition outputlist.h:261
void codify(const DString &s)
Definition outputlist.h:232
void addWord(const DString &word, bool hiPriority)
void setCurrentDoc(const Definition *ctx, const DString &anchor, bool isSourceFile)
#define Config_getList(name)
Definition config.h:38
#define Config_getBool(name)
Definition config.h:33
#define Config_getString(name)
Definition config.h:32
std::unordered_set< std::string > StringUnorderedSet
Definition containers.h:29
std::vector< std::string > StringVector
Definition containers.h:33
DirIterator end(const DirIterator &) noexcept
Definition dir.cpp:181
#define AUTO_TRACE_ADD(...)
Definition docnode.cpp:54
#define AUTO_TRACE(...)
Definition docnode.cpp:53
#define AUTO_TRACE_EXIT(...)
Definition docnode.cpp:55
void dstrfree(const char *str)
Frees the memory allocated using dstrdup().
Definition dstring.cpp:431
char * dstrdup(const char *str)
Definition dstring.cpp:424
int dstrcmp(const char *str1, const char *str2)
Definition dstring.h:50
void addDocCrossReference(const MemberDef *s, const MemberDef *d)
MemberDef * toMemberDef(Definition *d)
#define err(fmt,...)
Definition message.h:127
#define ASSERT(x)
Definition message.h:142
Definition dstring.h:913
Web server based search engine.
SrcLangExt
Definition types.h:207
Various UTF8 related helper functions.
DString detab(const DString &s, size_t &refIndent)
Definition util.cpp:5179
SrcLangExt getLanguageFromFileName(const DString &fileName, SrcLangExt defLang)
Definition util.cpp:4168
DString fileToString(const DString &name, bool filter, bool isSourceCode)
Definition util.cpp:1053
A bunch of utility functions.