9#include <clang-c/Index.h>
10#include "clang/Tooling/CompilationDatabase.h"
11#include "clang/Tooling/Tooling.h"
48enum class DetectedLang { Cpp, ObjC, ObjCpp };
50static const char * keywordToType(
const char *keyword)
53 "break",
"case",
"catch",
"continue",
"default",
"do",
54 "else",
"finally",
"for",
"foreach",
"for each",
"goto",
55 "if",
"return",
"switch",
"throw",
"throws",
"try",
56 "while",
"@try",
"@catch",
"@finally" });
58 "bool",
"char",
"double",
"float",
"int",
"long",
"object",
59 "short",
"signed",
"unsigned",
"void",
"wchar_t",
"size_t",
60 "boolean",
"id",
"SEL",
"string",
"nullptr" });
61 if (flowKeywords.find(keyword)!=flowKeywords.end())
return "keywordflow";
62 if (typeKeywords.find(keyword)!=typeKeywords.end())
return "keywordtype";
72 Private(
const ClangParser &
p,
const FileDef *fd)
73 : parser(
p), fileDef(fd) {}
74 const ClangParser &parser;
75 const FileDef *fileDef;
76 CXIndex index =
nullptr;
77 uint32_t curToken = 0;
78 DetectedLang detectedLang = DetectedLang::Cpp;
80 std::vector<QCString> sources;
81 std::vector<CXUnsavedFile> ufs;
82 std::vector<CXCursor> cursors;
83 std::unordered_map<std::string,uint32_t> fileMapping;
84 CXTranslationUnit tu =
nullptr;
85 CXToken *tokens =
nullptr;
86 uint32_t numTokens = 0;
88 TooltipManager tooltipManager;
89 std::vector<const Definition *> foldStack;
92 const MemberDef *currentMemberDef=
nullptr;
93 uint32_t currentLine=0;
94 bool searchForBody=
FALSE;
95 bool insideBody=
FALSE;
96 uint32_t bracketCount=0;
100 : p(std::make_unique<Private>(parser,fd))
107 return p->filesInSameTU;
113 QCString fileName =
p->fileDef->absFilePath();
114 p->fileDef->getAllIncludeFilesRecursively(
p->filesInSameTU);
117 bool clangAssistedParsing =
Config_getBool(CLANG_ASSISTED_PARSING);
118 bool clangIncludeInputPaths =
Config_getBool(CLANG_ADD_INC_PATHS);
122 if (!clangAssistedParsing)
return;
124 assert(
p->index==
nullptr);
125 assert(
p->tokens==
nullptr);
126 assert(
p->numTokens==0);
127 p->index = clang_createIndex(0, 0);
130 std::vector<clang::tooling::CompileCommand> command;
131 if (
p->parser.database()!=
nullptr)
134 command =
p->parser.database()->getCompileCommands(fileName.
data());
136 std::vector<char *> argv;
137 if (!command.empty() )
139 std::vector<std::string> options = command[command.size()-1].CommandLine;
141 for (
auto option = options.begin()+1; option != options.end(); option++)
143 argv.push_back(
qstrdup(option->c_str()));
156 if (std::string(argv[argv.size() - 1]) ==
"--") {
162 for (
size_t i=0;i<clangOptions.size();i++)
164 argv.push_back(
qstrdup(clangOptions[i].c_str()));
172 if (clangIncludeInputPaths)
176 QCString inc = QCString(
"-I")+path.data();
182 for (
size_t i=0;i<includePath.size();i++)
184 QCString inc = QCString(
"-I")+includePath[i].c_str();
188 for (
size_t i=0;i<clangOptions.size();i++)
190 argv.push_back(
qstrdup(clangOptions[i].c_str()));
193 argv.push_back(
qstrdup(
"-ferror-limit=0"));
201 QCString fn = fileName.
lower();
202 if (lang==SrcLangExt::ObjC ||
p->detectedLang!=DetectedLang::Cpp)
204 if (
p->detectedLang!=DetectedLang::Cpp &&
208 p->detectedLang = DetectedLang::Cpp;
212 p->detectedLang = DetectedLang::ObjCpp;
216 p->detectedLang = DetectedLang::ObjC;
219 switch (
p->detectedLang)
221 case DetectedLang::Cpp:
224 argv.push_back(
qstrdup(
"c++-header"));
226 argv.push_back(
qstrdup(
"c++"));
228 case DetectedLang::ObjC: argv.push_back(
qstrdup(
"objective-c"));
break;
229 case DetectedLang::ObjCpp: argv.push_back(
qstrdup(
"objective-c++"));
break;
234 size_t numUnsavedFiles =
p->filesInSameTU.size()+1;
235 p->numFiles = numUnsavedFiles;
236 p->sources.resize(numUnsavedFiles);
237 p->ufs.resize(numUnsavedFiles);
238 size_t refIndent = 0;
241 p->ufs[0].Contents =
p->sources[0].data();
242 p->ufs[0].Length =
p->sources[0].length();
243 p->fileMapping.emplace(fileName.
data(),0);
245 for (
auto it =
p->filesInSameTU.begin();
246 it !=
p->filesInSameTU.end() && i<numUnsavedFiles;
249 p->fileMapping.emplace(it->c_str(),
static_cast<uint32_t
>(i));
251 p->ufs[i].Filename =
qstrdup(it->c_str());
252 p->ufs[i].Contents =
p->sources[i].data();
253 p->ufs[i].Length =
p->sources[i].length();
258 p->tu = clang_parseTranslationUnit(
p->index, fileName.
data(),
259 argv.data(),
static_cast<int>(argv.size()),
p->ufs.data(), numUnsavedFiles,
260 CXTranslationUnit_DetailedPreprocessingRecord);
263 for (i=0;i<argv.size();++i)
271 size_t n=clang_getNumDiagnostics(
p->tu);
274 CXDiagnostic diag = clang_getDiagnostic(
p->tu,
static_cast<unsigned>(i));
275 CXString
string = clang_formatDiagnostic(diag,
276 clang_defaultDiagnosticDisplayOptions());
277 err(
"{} [clang]\n",clang_getCString(
string));
278 clang_disposeString(
string);
279 clang_disposeDiagnostic(diag);
284 err(
"clang: Failed to parse translation unit {}\n",fileName);
291 bool clangAssistedParsing =
Config_getBool(CLANG_ASSISTED_PARSING);
292 if (!clangAssistedParsing)
return;
296 clang_disposeTokens(
p->tu,
p->tokens,
p->numTokens);
297 clang_disposeTranslationUnit(
p->tu);
298 clang_disposeIndex(
p->index);
299 p->fileMapping.clear();
303 for (
size_t i=0;i<
p->numFiles;i++)
305 delete[]
p->ufs[i].Filename;
319 clang_disposeTokens(
p->tu,
p->tokens,
p->numTokens);
325 if (it!=
p->fileMapping.end() && it->second <
p->numFiles)
327 uint32_t i = it->second;
329 CXSourceLocation fileBegin = clang_getLocationForOffset(
p->tu, f, 0);
330 CXSourceLocation fileEnd = clang_getLocationForOffset(
p->tu, f,
p->ufs[i].Length);
331 CXSourceRange fileRange = clang_getRange(fileBegin, fileEnd);
333 clang_tokenize(
p->tu,fileRange,&
p->tokens,&
p->numTokens);
334 p->cursors.resize(
p->numTokens);
335 clang_annotateTokens(
p->tu,
p->tokens,
p->numTokens,
p->cursors.data());
340 err(
"clang: Failed to find input file {} in mapping\n",fd->
absFilePath());
349 if (symbol==
nullptr)
return result;
350 bool clangAssistedParsing =
Config_getBool(CLANG_ASSISTED_PARSING);
351 if (!clangAssistedParsing)
return result;
353 auto getCurrentTokenLine = [
this]() -> uint32_t
356 if (
p->numTokens==0)
return 1;
358 if (
p->curToken>=
p->numTokens)
p->curToken=
p->numTokens-1;
359 CXSourceLocation start = clang_getTokenLocation(
p->tu,
p->tokens[
p->curToken]);
360 clang_getSpellingLocation(start,
nullptr, &l, &c,
nullptr);
364 int sl = strlen(symbol);
365 uint32_t l = getCurrentTokenLine();
366 while (l>=line &&
p->curToken>0)
371 l = getCurrentTokenLine();
376 l = getCurrentTokenLine();
380 while (l<=line && p->curToken<p->numTokens && !
found)
382 CXString tokenString = clang_getTokenSpelling(
p->tu,
p->tokens[
p->curToken]);
385 AUTO_TRACE_ADD(
"try to match symbol {} with token {}",symbol,clang_getCString(tokenString));
387 const char *ts = clang_getCString(tokenString);
389 int startIndex =
p->curToken;
390 if (l==line && strncmp(ts,symbol,tl)==0)
397 if (
p->curToken>=
p->numTokens)
401 l = getCurrentTokenLine();
402 clang_disposeString(tokenString);
403 tokenString = clang_getTokenSpelling(
p->tu,
p->tokens[
p->curToken]);
404 ts = clang_getCString(tokenString);
405 tl = ts ? strlen(ts) : 0;
408 while (offset<sl && ((c=symbol[offset])==
' ' || c==
'\t' || c==
'\r' || c==
'\n'))
412 if (strncmp(ts,symbol+offset,tl)!=0)
422 CXCursor c =
p->cursors[
p->curToken];
423 CXString usr = clang_getCursorUSR(c);
424 AUTO_TRACE_ADD(
"found full match {} usr='{}'",symbol,clang_getCString(usr));
425 result = clang_getCString(usr);
426 clang_disposeString(usr);
431 p->curToken = startIndex;
434 clang_disposeString(tokenString);
436 if (
p->curToken<
p->numTokens)
438 l = getCurrentTokenLine();
461 if (endLine!=-1 && startLine!=endLine &&
469 (
p->foldStack.empty() ||
p->foldStack.back()->getEndBodyLine()!=startLine))
495 p->foldStack.push_back(d);
503 while (!
p->foldStack.empty())
505 const Definition *dd =
p->foldStack.back();
509 p->foldStack.pop_back();
528 if (
p->currentMemberDef!=md)
530 p->searchForBody=
TRUE;
534 p->currentMemberDef=md;
539 line,writeLineAnchor);
543 p->currentMemberDef=
nullptr;
548 line,writeLineAnchor);
553 ol.
writeLineNumber(QCString(),QCString(),QCString(),line,writeLineAnchor);
559 ol.
writeLineNumber(QCString(),QCString(),QCString(),line,writeLineAnchor);
566 lineAnchor.
sprintf(
"l%05d",line);
574 uint32_t &line,uint32_t &column,
const char *fontClass)
577 const char *
p=text,*sp=
p;
579 bool inlineCodeFragment =
false;
584 while ((c=*
p++) && c!=
'\n') { column++; }
588 size_t l =
static_cast<size_t>(
p-sp-1);
590 std::string tmp(sp,l);
608 const FileDef *fd,uint32_t &line,uint32_t &column,
613 p->tooltipManager.addTooltip(d);
616 QCString anchor = d->
anchor();
622 bool inlineCodeFragment =
false;
629 while ((c=*
p++) && c!=
'\n') { column++; }
649 uint32_t &line,uint32_t &column,
const char *text)
651 QCString incName = text;
652 incName = incName.
mid(1,incName.
length()-2);
653 FileDef *ifd=
nullptr;
660 auto it = std::find_if(fn->begin(),
662 [&fd](
const auto &ifd)
663 { return fd->isIncluded(ifd->absFilePath()); });
664 bool found = it!=fn->end();
683 codifyLines(ol,ifd,text,line,column,
"preprocessor");
688 uint32_t &line,uint32_t &column,
const char *text)
693 for (
const auto &md : *mn)
707 uint32_t &line,uint32_t &column,
const char *text,
int tokenIndex)
709 AUTO_TRACE(
"line={} column={} text={}",line,column,text);
710 CXCursor c =
p->cursors[tokenIndex];
711 CXCursorKind cKind = clang_getCursorKind(c);
713 CXCursor r = clang_getCursorReferenced(c);
714 AUTO_TRACE_ADD(
"cursor reference kind={}",(
int)clang_getCursorKind(r));
715 if (!clang_equalCursors(r, c))
720 if (!clang_isDeclaration(cKind))
722 CXCursor t = clang_getSpecializedCursorTemplate(c);
723 AUTO_TRACE_ADD(
"cursor template kind={}",(
int)clang_getCursorKind(t));
724 if (!clang_Cursor_isNull(t) && !clang_equalCursors(t,c))
729 CXString usr = clang_getCursorUSR(c);
730 const char *usrStr = clang_getCString(usr);
733 const Definition *d =
nullptr;
739 CXCursorKind kind = clang_getCursorKind(c);
742 AUTO_TRACE_ADD(
"didn't find definition for '{}' usr='{}' kind={}",
743 text,usrStr,(
int)kind);
756 (
p->currentMemberDef!=d ||
p->currentLine<line))
767 clang_disposeString(usr);
780 else if (
p->searchForBody &&
qstrcmp(s,
";")==0)
785 if (
p->insideBody &&
qstrcmp(s,
"{")==0)
789 if (
p->insideBody &&
qstrcmp(s,
"}")==0)
792 if (
p->bracketCount<=0)
804 p->currentMemberDef=
nullptr;
809 p->foldStack.clear();
811 unsigned int line=1,column=1;
812 QCString lineNumber,lineAnchor;
813 bool inlineCodeFragment =
false;
816 for (
unsigned int i=0;i<
p->numTokens;i++)
818 CXSourceLocation start = clang_getTokenLocation(
p->tu,
p->tokens[i]);
819 unsigned int l=0, c=0;
820 clang_getSpellingLocation(start,
nullptr, &l, &c,
nullptr);
821 if (l > line) column = 1;
829 while (column<c) { ol.
codify(
" "); column++; }
830 CXString tokenString = clang_getTokenSpelling(
p->tu,
p->tokens[i]);
831 char const *s = clang_getCString(tokenString);
832 CXCursorKind cursorKind = clang_getCursorKind(
p->cursors[i]);
833 CXTokenKind tokenKind = clang_getTokenKind(
p->tokens[i]);
837 case CXToken_Keyword:
838 if (strcmp(s,
"operator")==0)
845 cursorKind==CXCursor_PreprocessingDirective ?
"preprocessor" :
849 case CXToken_Literal:
850 if (cursorKind==CXCursor_InclusionDirective)
854 else if (s[0]==
'"' || s[0]==
'\'')
863 case CXToken_Comment:
867 if (tokenKind==CXToken_Punctuation)
874 case CXCursor_PreprocessingDirective:
877 case CXCursor_MacroDefinition:
880 case CXCursor_InclusionDirective:
883 case CXCursor_MacroExpansion:
887 if (tokenKind==CXToken_Identifier ||
888 (tokenKind==CXToken_Punctuation &&
889 (cursorKind==CXCursor_DeclRefExpr ||
890 cursorKind==CXCursor_MemberRefExpr ||
891 cursorKind==CXCursor_CallExpr ||
892 cursorKind==CXCursor_ObjCMessageExpr)
909 clang_disposeString(tokenString);
914 while (!
p->foldStack.empty())
917 p->foldStack.pop_back();
920 p->tooltipManager.writeTooltips(ol);
933 db = clang::tooling::CompilationDatabase::loadFromDirectory(clangCompileDatabase.
data(), error);
934 if (!clangCompileDatabase.
isEmpty() && clangCompileDatabase!=
"0" && db==
nullptr)
937 err(
"{} using clang compilation database path of: \"{}\"\n", error, clangCompileDatabase);
941 std::unique_ptr<clang::tooling::CompilationDatabase> db;
960 return std::make_unique<ClangTUParser>(*
this,fd);
989 return std::string();
static std::mutex g_clangMutex
Wrapper for to let libclang assisted parsing.
static ClangParser * s_instance
std::unique_ptr< ClangTUParser > createTUParser(const FileDef *fd) const
const clang::tooling::CompilationDatabase * database() const
static ClangParser * instance()
Returns the one and only instance of the class.
std::unique_ptr< Private > p
StringVector filesInSameTU() const
Returns the list of files for this translation unit.
ClangTUParser(const ClangParser &parser, const FileDef *fd)
void linkIdentifier(OutputCodeList &ol, const FileDef *fd, uint32_t &line, uint32_t &column, const char *text, int tokenIndex)
void linkInclude(OutputCodeList &ol, const FileDef *fd, uint32_t &line, uint32_t &column, const char *text)
void endCodeFold(OutputCodeList &ol, uint32_t line)
void writeLineNumber(OutputCodeList &ol, const FileDef *fd, uint32_t line, bool writeLineAnchor)
void codeFolding(OutputCodeList &ol, const Definition *d, uint32_t line)
void switchToFile(const FileDef *fd)
Switches to another file within the translation unit started with start().
void parse()
Parse the file given at construction time as a translation unit This file should already be preproces...
void codifyLines(OutputCodeList &ol, const FileDef *fd, const char *text, uint32_t &line, uint32_t &column, const char *fontClass=nullptr)
void linkMacro(OutputCodeList &ol, const FileDef *fd, uint32_t &line, uint32_t &column, const char *text)
std::string lookup(uint32_t line, const char *symbol)
Looks for symbol which should be found at line.
std::unique_ptr< Private > p
void writeMultiLineCodeLink(OutputCodeList &ol, const FileDef *fd, uint32_t &line, uint32_t &column, const Definition *d, const char *text)
void detectFunctionBody(const char *s)
void writeSources(OutputCodeList &ol, const FileDef *fd)
writes the syntax highlighted source code for a file
The common base class of all entity definitions found in the sources.
virtual int getEndBodyLine() const =0
virtual bool isLinkable() const =0
virtual DefType definitionType() const =0
virtual QCString anchor() const =0
virtual QCString briefDescriptionAsTooltip() const =0
virtual int getStartDefLine() const =0
virtual QCString getReference() const =0
virtual CodeSymbolType codeSymbolType() const =0
virtual QCString getOutputFileBase() const =0
virtual const QCString & name() const =0
static StringUnorderedSet inputPaths
static FileNameLinkedMap * inputNameLinkedMap
static MemberNameLinkedMap * functionNameLinkedMap
static SearchIndexIntf searchIndex
static ClangUsrMap * clangUsrMap
A model of a file symbol.
virtual const Definition * getSourceDefinition(int lineNr) const =0
virtual const MemberDef * getSourceMember(int lineNr) const =0
virtual QCString absFilePath() const =0
const T * find(const std::string &key) const
virtual bool isDefine() const =0
virtual bool isCallable() const =0
Class representing a list of different code generators.
void codify(const QCString &s)
void writeCodeLink(CodeSymbolType type, const QCString &ref, const QCString &file, const QCString &anchor, const QCString &name, const QCString &tooltip)
void startCodeLine(int lineNr)
void startFold(int lineNr, const QCString &startMarker, const QCString &endMarker)
void writeLineNumber(const QCString &ref, const QCString &file, const QCString &anchor, int lineNumber, bool writeLineAnchor)
void startFontClass(const QCString &c)
size_t length() const
Returns the length of the string, not counting the 0-terminator.
QCString mid(size_t index, size_t len=static_cast< size_t >(-1)) const
bool endsWith(const char *s) const
bool isEmpty() const
Returns TRUE iff the string is empty.
QCString & sprintf(const char *format,...)
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
void addWord(const QCString &word, bool hiPriority)
void setCurrentDoc(const Definition *ctx, const QCString &anchor, bool isSourceFile)
#define Config_getList(name)
#define Config_getBool(name)
#define Config_getString(name)
std::unordered_set< std::string > StringUnorderedSet
std::vector< std::string > StringVector
DirIterator end(const DirIterator &) noexcept
#define AUTO_TRACE_ADD(...)
#define AUTO_TRACE_EXIT(...)
static std::mutex g_docCrossReferenceMutex
void addDocCrossReference(const MemberDef *s, const MemberDef *d)
MemberDef * toMemberDef(Definition *d)
char * qstrdup(const char *str)
void qstrfree(const char *str)
Frees the memory allocated using qstrdup().
int qstrcmp(const char *str1, const char *str2)
Web server based search engine.
Various UTF8 related helper functions.
SrcLangExt getLanguageFromFileName(const QCString &fileName, SrcLangExt defLang)
QCString fileToString(const QCString &name, bool filter, bool isSourceCode)
QCString detab(const QCString &s, size_t &refIndent)
A bunch of utility functions.