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