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