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