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