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