31#include "clang/Tooling/CompilationDatabase.h"
32#include "clang/Tooling/Tooling.h"
33#include "clang-c/Index.h"
68static std::mutex g_docCrossReferenceMutex;
70enum class DetectedLang { Cpp, ObjC, ObjCpp };
72static const char * keywordToType(
const char *keyword)
75 "break",
"case",
"catch",
"continue",
"default",
"do",
76 "else",
"finally",
"for",
"foreach",
"for each",
"goto",
77 "if",
"return",
"switch",
"throw",
"throws",
"try",
78 "while",
"@try",
"@catch",
"@finally" });
80 "bool",
"char",
"double",
"float",
"int",
"long",
"object",
81 "short",
"signed",
"unsigned",
"void",
"wchar_t",
"size_t",
82 "boolean",
"id",
"SEL",
"string",
"nullptr" });
83 if (flowKeywords.find(keyword)!=flowKeywords.end())
return "keywordflow";
84 if (typeKeywords.find(keyword)!=typeKeywords.end())
return "keywordtype";
94 Private(
const ClangParser &
p,
const FileDef *fd)
95 : parser(
p), fileDef(fd) {}
96 const ClangParser &parser;
97 const FileDef *fileDef;
98 CXIndex index =
nullptr;
99 uint32_t curToken = 0;
100 DetectedLang detectedLang = DetectedLang::Cpp;
102 std::vector<DString> sources;
103 std::vector<CXUnsavedFile> ufs;
104 std::vector<CXCursor> cursors;
105 std::unordered_map<std::string,uint32_t> fileMapping;
106 CXTranslationUnit tu =
nullptr;
107 CXToken *tokens =
nullptr;
108 uint32_t numTokens = 0;
110 TooltipManager tooltipManager;
111 std::vector<const Definition *> foldStack;
114 const MemberDef *currentMemberDef=
nullptr;
115 uint32_t currentLine=0;
116 bool searchForBody=
false;
117 bool insideBody=
false;
118 uint32_t bracketCount=0;
122 : p(
std::make_unique<Private>(parser,fd))
129 return p->filesInSameTU;
135 DString fileName =
p->fileDef->absFilePath();
136 p->fileDef->getAllIncludeFilesRecursively(
p->filesInSameTU);
139 bool clangAssistedParsing =
Config_getBool(CLANG_ASSISTED_PARSING);
140 bool clangIncludeInputPaths =
Config_getBool(CLANG_ADD_INC_PATHS);
144 if (!clangAssistedParsing)
return;
149 p->index = clang_createIndex(0, 0);
152 std::vector<clang::tooling::CompileCommand> command;
153 if (
p->parser.database()!=
nullptr)
156 command =
p->parser.database()->getCompileCommands(fileName.
data());
158 std::vector<char *> argv;
159 if (!command.empty() )
161 std::vector<std::string> options = command[command.size()-1].CommandLine;
163 for (
auto option = options.begin()+1; option != options.end(); option++)
165 argv.push_back(
dstrdup(option->c_str()));
179 if (argv.size()>0 &&
dstrcmp(argv[argv.size() - 1],
"--")==0) {
186 for (
size_t i=0;i<clangOptions.size();i++)
188 argv.push_back(
dstrdup(clangOptions[i].c_str()));
196 if (clangIncludeInputPaths)
200 DString inc = DString(
"-I")+path.data();
206 for (
size_t i=0;i<includePath.size();i++)
208 DString inc =
"-I"+includePath[i];
212 for (
size_t i=0;i<clangOptions.size();i++)
214 argv.push_back(
dstrdup(clangOptions[i].c_str()));
217 argv.push_back(
dstrdup(
"-ferror-limit=0"));
225 DString fn = fileName.
lower();
226 if (lang==SrcLangExt::ObjC ||
p->detectedLang!=DetectedLang::Cpp)
228 if (
p->detectedLang!=DetectedLang::Cpp &&
232 p->detectedLang = DetectedLang::Cpp;
236 p->detectedLang = DetectedLang::ObjCpp;
240 p->detectedLang = DetectedLang::ObjC;
243 switch (
p->detectedLang)
245 case DetectedLang::Cpp:
248 argv.push_back(
dstrdup(
"c++-header"));
250 argv.push_back(
dstrdup(
"c++"));
252 case DetectedLang::ObjC: argv.push_back(
dstrdup(
"objective-c"));
break;
253 case DetectedLang::ObjCpp: argv.push_back(
dstrdup(
"objective-c++"));
break;
258 size_t numUnsavedFiles =
p->filesInSameTU.size()+1;
259 p->numFiles = numUnsavedFiles;
260 p->sources.resize(numUnsavedFiles);
261 p->ufs.resize(numUnsavedFiles);
262 size_t refIndent = 0;
265 p->ufs[0].Contents =
p->sources[0].data();
266 p->ufs[0].Length =
p->sources[0].length();
267 p->fileMapping.emplace(fileName.
data(),0);
269 for (
auto it =
p->filesInSameTU.begin();
270 it !=
p->filesInSameTU.end() && i<numUnsavedFiles;
273 p->fileMapping.emplace(std::make_pair(*it,
static_cast<uint32_t
>(i)));
275 p->ufs[i].Filename =
dstrdup(it->c_str());
276 p->ufs[i].Contents =
p->sources[i].data();
277 p->ufs[i].Length =
p->sources[i].length();
282 p->tu = clang_parseTranslationUnit(
p->index, fileName.
data(),
283 argv.data(),
static_cast<int>(argv.size()),
p->ufs.data(), numUnsavedFiles,
284 CXTranslationUnit_DetailedPreprocessingRecord);
287 for (i=0;i<argv.size();++i)
295 size_t n=clang_getNumDiagnostics(
p->tu);
298 CXDiagnostic diag = clang_getDiagnostic(
p->tu,
static_cast<unsigned>(i));
299 CXString
string = clang_formatDiagnostic(diag,
300 clang_defaultDiagnosticDisplayOptions());
301 err(
"{} [clang]\n",clang_getCString(
string));
302 clang_disposeString(
string);
303 clang_disposeDiagnostic(diag);
308 err(
"clang: Failed to parse translation unit {}\n",fileName);
315 bool clangAssistedParsing =
Config_getBool(CLANG_ASSISTED_PARSING);
316 if (!clangAssistedParsing)
return;
320 clang_disposeTokens(
p->tu,
p->tokens,
p->numTokens);
321 clang_disposeTranslationUnit(
p->tu);
322 clang_disposeIndex(
p->index);
323 p->fileMapping.clear();
327 for (
size_t i=0;i<
p->numFiles;i++)
343 clang_disposeTokens(
p->tu,
p->tokens,
p->numTokens);
349 if (it!=
p->fileMapping.end() && it->second <
p->numFiles)
351 uint32_t i = it->second;
353 CXSourceLocation fileBegin = clang_getLocationForOffset(
p->tu, f, 0);
354 CXSourceLocation fileEnd = clang_getLocationForOffset(
p->tu, f,
p->ufs[i].Length);
355 CXSourceRange fileRange = clang_getRange(fileBegin, fileEnd);
357 clang_tokenize(
p->tu,fileRange,&
p->tokens,&
p->numTokens);
358 p->cursors.resize(
p->numTokens);
359 clang_annotateTokens(
p->tu,
p->tokens,
p->numTokens,
p->cursors.data());
364 err(
"clang: Failed to find input file {} in mapping\n",fd->
absFilePath());
373 if (symbol==
nullptr)
return result;
374 bool clangAssistedParsing =
Config_getBool(CLANG_ASSISTED_PARSING);
375 if (!clangAssistedParsing)
return result;
377 auto getCurrentTokenLine = [
this]() -> uint32_t
380 if (
p->numTokens==0)
return 1;
382 if (
p->curToken>=
p->numTokens)
p->curToken=
p->numTokens-1;
383 CXSourceLocation start = clang_getTokenLocation(
p->tu,
p->tokens[
p->curToken]);
384 clang_getSpellingLocation(start,
nullptr, &l, &c,
nullptr);
388 int sl = strlen(symbol);
389 uint32_t l = getCurrentTokenLine();
390 while (l>=line &&
p->curToken>0)
395 l = getCurrentTokenLine();
400 l = getCurrentTokenLine();
404 while (l<=line && p->curToken<p->numTokens && !found)
406 CXString tokenString = clang_getTokenSpelling(
p->tu,
p->tokens[
p->curToken]);
409 AUTO_TRACE_ADD(
"try to match symbol {} with token {}",symbol,clang_getCString(tokenString));
411 const char *ts = clang_getCString(tokenString);
413 int startIndex =
p->curToken;
414 if (l==line && strncmp(ts,symbol,tl)==0)
421 if (
p->curToken>=
p->numTokens)
425 l = getCurrentTokenLine();
426 clang_disposeString(tokenString);
427 tokenString = clang_getTokenSpelling(
p->tu,
p->tokens[
p->curToken]);
428 ts = clang_getCString(tokenString);
429 tl = ts ? strlen(ts) : 0;
432 while (offset<sl && ((c=symbol[offset])==
' ' || c==
'\t' || c==
'\r' || c==
'\n'))
436 if (strncmp(ts,symbol+offset,tl)!=0)
446 CXCursor c =
p->cursors[
p->curToken];
447 CXString usr = clang_getCursorUSR(c);
448 AUTO_TRACE_ADD(
"found full match {} usr='{}'",symbol,clang_getCString(usr));
449 result = clang_getCString(usr);
450 clang_disposeString(usr);
455 p->curToken = startIndex;
458 clang_disposeString(tokenString);
460 if (
p->curToken<
p->numTokens)
462 l = getCurrentTokenLine();
485 if (endLine!=-1 && startLine!=endLine &&
493 (
p->foldStack.empty() ||
p->foldStack.back()->getEndBodyLine()!=startLine))
519 p->foldStack.push_back(d);
527 while (!
p->foldStack.empty())
529 const Definition *dd =
p->foldStack.back();
533 p->foldStack.pop_back();
552 if (
p->currentMemberDef!=md)
554 p->searchForBody=
true;
558 p->currentMemberDef=md;
563 line,writeLineAnchor);
567 p->currentMemberDef=
nullptr;
572 line,writeLineAnchor);
577 ol.
writeLineNumber(DString(),DString(),DString(),line,writeLineAnchor);
583 ol.
writeLineNumber(DString(),DString(),DString(),line,writeLineAnchor);
590 lineAnchor.
sprintf(
"l%05d",line);
598 uint32_t &line,uint32_t &column,
const char *fontClass)
601 const char *
p=text,*sp=
p;
603 bool inlineCodeFragment =
false;
608 while ((c=*
p++) && c!=
'\n') { column++; }
612 size_t l =
static_cast<size_t>(
p-sp-1);
631 const FileDef *fd,uint32_t &line,uint32_t &column,
636 p->tooltipManager.addTooltip(d);
639 DString anchor = d->
anchor();
645 bool inlineCodeFragment =
false;
652 while ((c=*
p++) && c!=
'\n') { column++; }
672 uint32_t &line,uint32_t &column,
const char *text)
674 DString incName = text;
675 incName = incName.
mid(1,incName.
length()-2);
676 FileDef *ifd=
nullptr;
677 if (!incName.
empty())
683 auto it = std::find_if(fn->begin(),
685 [&fd](
const auto &ifd)
686 { return fd->isIncluded(ifd->absFilePath()); });
687 bool found = it!=fn->end();
706 codifyLines(ol,ifd,text,line,column,
"preprocessor");
711 uint32_t &line,uint32_t &column,
const char *text)
716 for (
const auto &md : *mn)
730 uint32_t &line,uint32_t &column,
const char *text,
int tokenIndex)
732 AUTO_TRACE(
"line={} column={} text={}",line,column,text);
733 CXCursor c =
p->cursors[tokenIndex];
734 CXCursorKind cKind = clang_getCursorKind(c);
736 CXCursor r = clang_getCursorReferenced(c);
737 AUTO_TRACE_ADD(
"cursor reference kind={}",(
int)clang_getCursorKind(r));
738 if (!clang_equalCursors(r, c))
743 if (!clang_isDeclaration(cKind))
745 CXCursor t = clang_getSpecializedCursorTemplate(c);
746 AUTO_TRACE_ADD(
"cursor template kind={}",(
int)clang_getCursorKind(t));
747 if (!clang_Cursor_isNull(t) && !clang_equalCursors(t,c))
752 CXString usr = clang_getCursorUSR(c);
753 const char *usrStr = clang_getCString(usr);
756 const Definition *d =
nullptr;
764 AUTO_TRACE_ADD(
"didn't find definition for '{}' usr='{}' kind={}",
765 text,usrStr,(
int)clang_getCursorKind(c));
778 (
p->currentMemberDef!=d ||
p->currentLine<line))
780 std::lock_guard<std::mutex> lock(g_docCrossReferenceMutex);
789 clang_disposeString(usr);
799 p->searchForBody=
false;
802 else if (
p->searchForBody &&
dstrcmp(s,
";")==0)
804 p->searchForBody=
false;
807 if (
p->insideBody &&
dstrcmp(s,
"{")==0)
811 if (
p->insideBody &&
dstrcmp(s,
"}")==0)
814 if (
p->bracketCount<=0)
826 p->currentMemberDef=
nullptr;
828 p->searchForBody=
false;
831 p->foldStack.clear();
833 unsigned int line=1,column=1;
834 DString lineNumber,lineAnchor;
835 bool inlineCodeFragment =
false;
838 for (
unsigned int i=0;i<
p->numTokens;i++)
840 CXSourceLocation start = clang_getTokenLocation(
p->tu,
p->tokens[i]);
841 unsigned int l=0, c=0;
842 clang_getSpellingLocation(start,
nullptr, &l, &c,
nullptr);
843 if (l > line) column = 1;
851 while (column<c) { ol.
codify(
" "); column++; }
852 CXString tokenString = clang_getTokenSpelling(
p->tu,
p->tokens[i]);
853 char const *s = clang_getCString(tokenString);
854 CXCursorKind cursorKind = clang_getCursorKind(
p->cursors[i]);
855 CXTokenKind tokenKind = clang_getTokenKind(
p->tokens[i]);
859 case CXToken_Keyword:
860 if (strcmp(s,
"operator")==0)
867 cursorKind==CXCursor_PreprocessingDirective ?
"preprocessor" :
871 case CXToken_Literal:
872 if (cursorKind==CXCursor_InclusionDirective)
876 else if (s[0]==
'"' || s[0]==
'\'')
885 case CXToken_Comment:
889 if (tokenKind==CXToken_Punctuation)
896 case CXCursor_PreprocessingDirective:
899 case CXCursor_MacroDefinition:
902 case CXCursor_InclusionDirective:
905 case CXCursor_MacroExpansion:
909 if (tokenKind==CXToken_Identifier ||
910 (tokenKind==CXToken_Punctuation &&
911 (cursorKind==CXCursor_DeclRefExpr ||
912 cursorKind==CXCursor_MemberRefExpr ||
913 cursorKind==CXCursor_CallExpr ||
914 cursorKind==CXCursor_ObjCMessageExpr)
931 clang_disposeString(tokenString);
936 while (!
p->foldStack.empty())
939 p->foldStack.pop_back();
942 p->tooltipManager.writeTooltips(ol);
955 db = clang::tooling::CompilationDatabase::loadFromDirectory(clangCompileDatabase.
data(), error);
956 if (!clangCompileDatabase.
empty() && clangCompileDatabase!=
"0" && db==
nullptr)
959 err(
"{} using clang compilation database path of: \"{}\"\n", error, clangCompileDatabase);
963 std::unique_ptr<clang::tooling::CompilationDatabase> db;
982 return std::make_unique<ClangTUParser>(*
this,fd);
1011 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
DString mid(size_t index, size_t len=npos) const
bool empty() const
Returns true iff the string is empty (std::string compatible alias for isEmpty()).
DString & 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.
bool endsWith(const char *s) const
size_t length() const
Returns the length of the string, not counting the 0-terminator.
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 const DString & name() const =0
virtual DString briefDescriptionAsTooltip() const =0
virtual DString anchor() const =0
virtual int getStartDefLine() const =0
virtual DString getReference() const =0
virtual CodeSymbolType codeSymbolType() const =0
virtual DString getOutputFileBase() 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 DString 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 writeLineNumber(const DString &ref, const DString &file, const DString &anchor, int lineNumber, bool writeLineAnchor)
void startFold(int lineNr, const DString &startMarker, const DString &endMarker)
void writeCodeLink(CodeSymbolType type, const DString &ref, const DString &file, const DString &anchor, const DString &name, const DString &tooltip)
void startFontClass(const DString &c)
void startCodeLine(int lineNr)
void codify(const DString &s)
void addWord(const DString &word, bool hiPriority)
void setCurrentDoc(const Definition *ctx, const DString &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(...)
void dstrfree(const char *str)
Frees the memory allocated using dstrdup().
char * dstrdup(const char *str)
int dstrcmp(const char *str1, const char *str2)
void addDocCrossReference(const MemberDef *s, const MemberDef *d)
MemberDef * toMemberDef(Definition *d)
Web server based search engine.
Various UTF8 related helper functions.
DString detab(const DString &s, size_t &refIndent)
SrcLangExt getLanguageFromFileName(const DString &fileName, SrcLangExt defLang)
DString fileToString(const DString &name, bool filter, bool isSourceCode)
A bunch of utility functions.