1 #include "clangparser.h"
6 #include <clang-c/Index.h>
7 #include "clang/Tooling/CompilationDatabase.h"
8 #include "clang/Tooling/Tooling.h"
13 #include "outputgen.h"
15 #include "memberdef.h"
20 #include "membername.h"
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;
32 ClangParser *ClangParser::instance()
34 if (!s_instance) s_instance = new ClangParser;
38 ClangParser *ClangParser::s_instance = 0;
40 //--------------------------------------------------------------------------
43 class ClangParser::Private
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();
63 QDict<uint> fileMapping;
64 DetectedLang detectedLang;
67 static QCString detab(const QCString &s)
69 static int tabSize = Config_getInt(TAB_SIZE);
71 int size = s.length();
72 const char *data = s.data();
75 const int maxIndent=1000000; // value representing infinity
76 int minIndent=maxIndent;
82 case '\t': // expand tab
84 int stop = tabSize - (col%tabSize);
85 //printf("expand at %d stop=%d\n",col,stop);
87 while (stop--) out.addChar(' ');
90 case '\n': // reset colomn counter
94 case ' ': // increment column counter
98 default: // non-whitespace => update minIndent
100 if (c<0 && i<size) // multibyte sequence
102 out.addChar(data[i++]); // >= 2 bytes
103 if (((uchar)c&0xE0)==0xE0 && i<size)
105 out.addChar(data[i++]); // 3 bytes
107 if (((uchar)c&0xF0)==0xF0 && i<size)
109 out.addChar(data[i++]); // 4 byres
112 if (col<minIndent) minIndent=col;
117 //printf("detab refIndent=%d\n",refIndent);
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)
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);
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.
138 void ClangParser::determineInputFilesInSameTu(QStrList &files)
140 // put the files in this translation unit in a dictionary
141 QDict<void> incFound(257);
142 clang_getInclusions(p->tu,
144 (CXClientData)&incFound
146 // create a new filtered file list
147 QStrList resultIncludes;
148 QStrListIterator it2(files);
149 for (it2.toFirst();it2.current();++it2)
151 if (incFound.find(it2.current()))
153 resultIncludes.append(it2.current());
156 // replace the original list
157 files=resultIncludes;
160 void ClangParser::start(const char *fileName,QStrList &filesInTranslationUnit)
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);
172 QDictIterator<void> di(Doxygen::inputPaths);
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)
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());
191 // check if the file we are parsing is in the DB
192 command = db->getCompileCommands(fileName);
193 if (!command.empty() )
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();
200 char **argv = (char**)malloc(sizeof(char*)*(4+Doxygen::inputPaths.count()+includePath.count()+clangOptions.count()+clang_option_len));
201 if (!command.empty() )
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++)
207 argv[argc++] = qstrdup(option->c_str());
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.
214 // add include paths for input files
215 for (di.toFirst();di.current();++di,++argc)
217 QCString inc = QCString("-I")+di.currentKey();
218 argv[argc]=qstrdup(inc.data());
219 //printf("argv[%d]=%s\n",argc,argv[argc]);
221 // add external include paths
222 for (uint i=0;i<includePath.count();i++)
224 QCString inc = QCString("-I")+includePath.at(i);
225 argv[argc++]=qstrdup(inc.data());
227 // user specified options
228 for (uint i=0;i<clangOptions.count();i++)
230 argv[argc++]=qstrdup(clangOptions.at(i));
233 argv[argc++]=qstrdup("-ferror-limit=0");
234 argv[argc++]=qstrdup("-x");
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)
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;
250 else if (fn.right(3).lower()==".mm") // switch to Objective C++
252 p->detectedLang = ClangParser::Private::Detected_ObjCpp;
254 else if (fn.right(2).lower()==".m") // switch to Objective C
256 p->detectedLang = ClangParser::Private::Detected_ObjC;
259 switch(p->detectedLang)
261 case ClangParser::Private::Detected_Cpp:
262 argv[argc++]=qstrdup("c++");
264 case ClangParser::Private::Detected_ObjC:
265 argv[argc++]=qstrdup("objective-c");
267 case ClangParser::Private::Detected_ObjCpp:
268 argv[argc++]=qstrdup("objective-c++");
272 // provide the input and and its dependencies as unsaved files so we can
273 // pass the filtered versions
274 argv[argc++]=qstrdup(fileName);
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);
289 for (it.toFirst();it.current() && i<numUnsavedFiles;++it,i++)
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();
298 // let libclang do the actual parsing
299 p->tu = clang_parseTranslationUnit(p->index, 0,
300 argv, argc, p->ufs, numUnsavedFiles,
301 CXTranslationUnit_DetailedPreprocessingRecord);
303 for (int i=0;i<argc;++i)
311 // filter out any includes not found by the clang parser
312 determineInputFilesInSameTu(filesInTranslationUnit);
314 // show any warnings that the compiler produced
315 for (uint i=0, n=clang_getNumDiagnostics(p->tu); i!=n; ++i)
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);
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);
332 // produce a token stream for the file
333 clang_tokenize(p->tu,fileRange,&p->tokens,&p->numTokens);
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);
344 err("clang: Failed to parse translation unit %s\n",fileName);
348 void ClangParser::switchToFile(const char *fileName)
353 clang_disposeTokens(p->tu,p->tokens,p->numTokens);
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)
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);
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);
378 err("clang: Failed to find input file %s in mapping\n",fileName);
383 void ClangParser::finish()
385 static bool clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING);
386 if (!clangAssistedParsing) return;
389 //printf("ClangParser::finish()\n");
391 clang_disposeTokens(p->tu,p->tokens,p->numTokens);
392 clang_disposeTranslationUnit(p->tu);
393 clang_disposeIndex(p->index);
394 p->fileMapping.clear();
399 for (uint i=0;i<p->numFiles;i++)
401 free((void *)p->ufs[i].Filename);
411 int ClangParser::Private::getCurrentTokenLine()
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);
422 QCString ClangParser::lookup(uint line,const char *symbol)
424 //printf("ClangParser::lookup(%d,%s)\n",line,symbol);
426 if (symbol==0) return result;
427 static bool clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING);
428 if (!clangAssistedParsing) return result;
430 int sl = strlen(symbol);
431 uint l = p->getCurrentTokenLine();
432 while (l>=line && p->curToken>0)
434 if (l==line) // already at the right line
436 p->curToken--; // linear search to start of the line
437 l = p->getCurrentTokenLine();
441 p->curToken/=2; // binary search backward
442 l = p->getCurrentTokenLine();
446 while (l<=line && p->curToken<p->numTokens && !found)
448 CXString tokenString = clang_getTokenSpelling(p->tu, p->tokens[p->curToken]);
451 // printf("try to match symbol %s with token %s\n",symbol,clang_getCString(tokenString));
453 const char *ts = clang_getCString(tokenString);
455 int startIndex = p->curToken;
456 if (l==line && strncmp(ts,symbol,tl)==0) // found partial match at the correct line
459 while (offset<sl) // symbol spans multiple tokens
461 //printf("found partial match\n");
463 if (p->curToken>=p->numTokens)
465 break; // end of token stream
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
474 while (offset<sl && ((c=symbol[offset])==' ' || c=='\t' || c=='\r' || c=='\n'))
478 if (strncmp(ts,symbol+offset,tl)!=0) // next token matches?
480 //printf("no match '%s'<->'%s'\n",ts,symbol+offset);
483 //printf("partial match '%s'<->'%s'\n",ts,symbol+offset);
486 if (offset==sl) // symbol matches the token(s)
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);
495 else // reset token cursor to start of the search
497 p->curToken = startIndex;
500 clang_disposeString(tokenString);
502 if (p->curToken<p->numTokens)
504 l = p->getCurrentTokenLine();
509 // printf("Did not find symbol %s at line %d :-(\n",symbol,line);
513 // printf("Found symbol %s usr=%s\n",symbol,result.data());
518 static QCString keywordToType(const char *keyword)
520 static bool init=TRUE;
521 static QDict<void> flowKeywords(47);
522 static QDict<void> typeKeywords(47);
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);
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);
568 if (flowKeywords[keyword]) return "keywordflow";
569 if (typeKeywords[keyword]) return "keywordtype";
573 static void writeLineNumber(CodeOutputInterface &ol,FileDef *fd,uint line)
575 Definition *d = fd ? fd->getSourceDefinition(line) : 0;
576 if (d && d->isLinkable())
578 g_currentDefinition=d;
580 MemberDef *md = fd->getSourceMember(line);
581 if (md && md->isLinkable()) // link to member
583 if (g_currentMemberDef!=md) // new member, start search for body
585 g_searchForBody=TRUE;
589 g_currentMemberDef=md;
590 ol.writeLineNumber(md->getReference(),
591 md->getOutputFileBase(),
595 else // link to compound
597 g_currentMemberDef=0;
598 ol.writeLineNumber(d->getReference(),
599 d->getOutputFileBase(),
606 ol.writeLineNumber(0,0,0,line);
609 // set search page target
610 if (Doxygen::searchIndex)
613 lineAnchor.sprintf("l%05d",line);
614 ol.setCurrentDoc(fd,lineAnchor,TRUE);
617 //printf("writeLineNumber(%d) g_searchForBody=%d\n",line,g_searchForBody);
620 static void codifyLines(CodeOutputInterface &ol,FileDef *fd,const char *text,
621 uint &line,uint &column,const char *fontClass=0)
623 if (fontClass) ol.startFontClass(fontClass);
624 const char *p=text,*sp=p;
630 while ((c=*p++) && c!='\n') { column++; }
634 int l = (int)(p-sp-1);
636 char *tmp = (char*)malloc(l+1);
641 if (fontClass) ol.endFontClass();
643 ol.startCodeLine(TRUE);
644 writeLineNumber(ol,fd,line);
645 if (fontClass) ol.startFontClass(fontClass);
653 if (fontClass) ol.endFontClass();
656 static void writeMultiLineCodeLink(CodeOutputInterface &ol,
657 FileDef *fd,uint &line,uint &column,
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();
667 if (!sourceTooltips) // fall back to simple "title" tooltips
669 tooltip = d->briefDescriptionAsTooltip();
672 char *p=(char *)text;
677 while ((c=*p++) && c!='\n') { column++; }
682 //printf("writeCodeLink(%s,%s,%s,%s)\n",ref,file,anchor,sp);
683 ol.writeCodeLink(ref,file,anchor,sp,tooltip);
685 ol.startCodeLine(TRUE);
686 writeLineNumber(ol,fd,line);
690 //printf("writeCodeLink(%s,%s,%s,%s)\n",ref,file,anchor,sp);
691 ol.writeCodeLink(ref,file,anchor,sp,tooltip);
697 void ClangParser::linkInclude(CodeOutputInterface &ol,FileDef *fd,
698 uint &line,uint &column,const char *text)
700 QCString incName = text;
701 incName = incName.mid(1,incName.length()-2); // strip ".." or <..>
703 if (!incName.isEmpty())
705 FileName *fn = Doxygen::inputNameDict->find(incName);
709 FileNameIterator fni(*fn);
710 // for each include name
711 for (fni.toFirst();!found && (ifd=fni.current());++fni)
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);
721 ol.writeCodeLink(ifd->getReference(),ifd->getOutputFileBase(),0,text,ifd->briefDescriptionAsTooltip());
725 codifyLines(ol,ifd,text,line,column,"preprocessor");
729 void ClangParser::linkMacro(CodeOutputInterface &ol,FileDef *fd,
730 uint &line,uint &column,const char *text)
732 MemberName *mn=Doxygen::functionNameSDict->find(text);
735 MemberNameIterator mni(*mn);
737 for (mni.toFirst();(md=mni.current());++mni)
741 writeMultiLineCodeLink(ol,fd,line,column,md,text);
746 codifyLines(ol,fd,text,line,column);
750 void ClangParser::linkIdentifier(CodeOutputInterface &ol,FileDef *fd,
751 uint &line,uint &column,const char *text,int tokenIndex)
753 CXCursor c = p->cursors[tokenIndex];
754 CXCursor r = clang_getCursorReferenced(c);
755 if (!clang_equalCursors(r, c))
757 c=r; // link to referenced location
759 CXCursor t = clang_getSpecializedCursorTemplate(c);
760 if (!clang_Cursor_isNull(t) && !clang_equalCursors(t,c))
762 c=t; // link to template
764 CXString usr = clang_getCursorUSR(c);
765 const char *usrStr = clang_getCString(usr);
767 Definition *d = usrStr ? Doxygen::clangUsrMap->find(usrStr) : 0;
768 //CXCursorKind kind = clang_getCursorKind(c);
771 // printf("didn't find definition for '%s' usr='%s' kind=%d\n",
772 // text,usrStr,kind);
776 // printf("found definition for '%s' usr='%s' name='%s'\n",
777 // text,usrStr,d->name().data());
779 if (d && d->isLinkable())
782 g_currentMemberDef && d->definitionType()==Definition::TypeMember &&
783 (g_currentMemberDef!=d || g_currentLine<line)) // avoid self-reference
785 addDocCrossReference(g_currentMemberDef,(MemberDef*)d);
787 writeMultiLineCodeLink(ol,fd,line,column,d,text);
791 codifyLines(ol,fd,text,line,column);
793 clang_disposeString(usr);
796 static void detectFunctionBody(const char *s)
798 //printf("punct=%s g_searchForBody=%d g_insideBody=%d g_bracketCount=%d\n",
799 // s,g_searchForBody,g_insideBody,g_bracketCount);
801 if (g_searchForBody && (qstrcmp(s,":")==0 || qstrcmp(s,"{")==0)) // start of 'body' (: is for constructor)
803 g_searchForBody=FALSE;
806 else if (g_searchForBody && qstrcmp(s,";")==0) // declaration only
808 g_searchForBody=FALSE;
811 if (g_insideBody && qstrcmp(s,"{")==0) // increase scoping level
815 if (g_insideBody && qstrcmp(s,"}")==0) // decrease scoping level
818 if (g_bracketCount<=0) // got outside of function body
826 void ClangParser::writeSources(CodeOutputInterface &ol,FileDef *fd)
828 TooltipManager::instance()->clearTooltips();
829 // (re)set global parser state
830 g_currentDefinition=0;
831 g_currentMemberDef=0;
833 g_searchForBody=FALSE;
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++)
843 CXSourceLocation start = clang_getTokenLocation(p->tu, p->tokens[i]);
845 clang_getSpellingLocation(start, 0, &l, &c, 0);
846 if (l > line) column = 1;
851 ol.startCodeLine(TRUE);
852 writeLineNumber(ol,fd,line);
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);
862 case CXToken_Keyword:
863 if (strcmp(s,"operator")==0)
865 linkIdentifier(ol,fd,line,column,s,i);
869 codifyLines(ol,fd,s,line,column,
870 cursorKind==CXCursor_PreprocessingDirective ? "preprocessor" :
874 case CXToken_Literal:
875 if (cursorKind==CXCursor_InclusionDirective)
877 linkInclude(ol,fd,line,column,s);
879 else if (s[0]=='"' || s[0]=='\'')
881 codifyLines(ol,fd,s,line,column,"stringliteral");
885 codifyLines(ol,fd,s,line,column);
888 case CXToken_Comment:
889 codifyLines(ol,fd,s,line,column,"comment");
891 default: // CXToken_Punctuation or CXToken_Identifier
892 if (tokenKind==CXToken_Punctuation)
894 detectFunctionBody(s);
895 //printf("punct %s: %d\n",s,cursorKind);
899 case CXCursor_PreprocessingDirective:
900 codifyLines(ol,fd,s,line,column,"preprocessor");
902 case CXCursor_MacroDefinition:
903 codifyLines(ol,fd,s,line,column,"preprocessor");
905 case CXCursor_InclusionDirective:
906 linkInclude(ol,fd,line,column,s);
908 case CXCursor_MacroExpansion:
909 linkMacro(ol,fd,line,column,s);
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)
921 linkIdentifier(ol,fd,line,column,s,i);
922 if (Doxygen::searchIndex)
929 codifyLines(ol,fd,s,line,column);
934 clang_disposeString(tokenString);
937 TooltipManager::instance()->writeTooltips(ol);
940 ClangParser::ClangParser()
945 ClangParser::~ClangParser()
950 //--------------------------------------------------------------------------
951 #else // use stubbed functionality in case libclang support is disabled.
953 void ClangParser::start(const char *,QStrList &)
957 void ClangParser::switchToFile(const char *)
961 void ClangParser::finish()
965 QCString ClangParser::lookup(uint,const char *)
970 void ClangParser::writeSources(CodeOutputInterface &,FileDef *)
974 ClangParser::ClangParser()
979 ClangParser::~ClangParser()
985 //--------------------------------------------------------------------------