9#include <clang-c/Index.h>
10#include "clang/Tooling/CompilationDatabase.h"
11#include "clang/Tooling/Tooling.h"
45static std::mutex g_docCrossReferenceMutex;
47enum class DetectedLang { Cpp, ObjC, ObjCpp };
49static const char * keywordToType(
const char *keyword)
52 "break",
"case",
"catch",
"continue",
"default",
"do",
53 "else",
"finally",
"for",
"foreach",
"for each",
"goto",
54 "if",
"return",
"switch",
"throw",
"throws",
"try",
55 "while",
"@try",
"@catch",
"@finally" });
57 "bool",
"char",
"double",
"float",
"int",
"long",
"object",
58 "short",
"signed",
"unsigned",
"void",
"wchar_t",
"size_t",
59 "boolean",
"id",
"SEL",
"string",
"nullptr" });
60 if (flowKeywords.find(keyword)!=flowKeywords.end())
return "keywordflow";
61 if (typeKeywords.find(keyword)!=typeKeywords.end())
return "keywordtype";
71 Private(
const ClangParser &
p,
const FileDef *fd)
72 : parser(
p), fileDef(fd) {}
73 const ClangParser &parser;
74 const FileDef *fileDef;
75 CXIndex index =
nullptr;
76 uint32_t curToken = 0;
77 DetectedLang detectedLang = DetectedLang::Cpp;
79 std::vector<QCString> sources;
80 std::vector<CXUnsavedFile> ufs;
81 std::vector<CXCursor> cursors;
82 std::unordered_map<std::string,uint32_t> fileMapping;
83 CXTranslationUnit tu =
nullptr;
84 CXToken *tokens =
nullptr;
85 uint32_t numTokens = 0;
87 TooltipManager tooltipManager;
88 std::vector<const Definition *> foldStack;
91 const MemberDef *currentMemberDef=
nullptr;
92 uint32_t currentLine=0;
93 bool searchForBody=
false;
94 bool insideBody=
false;
95 uint32_t bracketCount=0;
99 : p(std::make_unique<Private>(parser,fd))
106 return p->filesInSameTU;
112 QCString fileName =
p->fileDef->absFilePath();
113 p->fileDef->getAllIncludeFilesRecursively(
p->filesInSameTU);
116 bool clangAssistedParsing =
Config_getBool(CLANG_ASSISTED_PARSING);
117 bool clangIncludeInputPaths =
Config_getBool(CLANG_ADD_INC_PATHS);
121 if (!clangAssistedParsing)
return;
123 assert(
p->index==
nullptr);
124 assert(
p->tokens==
nullptr);
125 assert(
p->numTokens==0);
126 p->index = clang_createIndex(0, 0);
129 std::vector<clang::tooling::CompileCommand> command;
130 if (
p->parser.database()!=
nullptr)
133 command =
p->parser.database()->getCompileCommands(fileName.
data());
135 std::vector<char *> argv;
136 if (!command.empty() )
138 std::vector<std::string> options = command[command.size()-1].CommandLine;
140 for (
auto option = options.begin()+1; option != options.end(); option++)
142 argv.push_back(
qstrdup(option->c_str()));
156 if (argv.size()>0 &&
qstrcmp(argv[argv.size() - 1],
"--")==0) {
163 for (
size_t i=0;i<clangOptions.size();i++)
165 argv.push_back(
qstrdup(clangOptions[i].c_str()));
173 if (clangIncludeInputPaths)
177 QCString inc = QCString(
"-I")+path.data();
183 for (
size_t i=0;i<includePath.size();i++)
185 QCString inc =
"-I"+includePath[i];
189 for (
size_t i=0;i<clangOptions.size();i++)
191 argv.push_back(
qstrdup(clangOptions[i].c_str()));
194 argv.push_back(
qstrdup(
"-ferror-limit=0"));
202 QCString fn = fileName.
lower();
203 if (lang==SrcLangExt::ObjC ||
p->detectedLang!=DetectedLang::Cpp)
205 if (
p->detectedLang!=DetectedLang::Cpp &&
209 p->detectedLang = DetectedLang::Cpp;
213 p->detectedLang = DetectedLang::ObjCpp;
217 p->detectedLang = DetectedLang::ObjC;
220 switch (
p->detectedLang)
222 case DetectedLang::Cpp:
225 argv.push_back(
qstrdup(
"c++-header"));
227 argv.push_back(
qstrdup(
"c++"));
229 case DetectedLang::ObjC: argv.push_back(
qstrdup(
"objective-c"));
break;
230 case DetectedLang::ObjCpp: argv.push_back(
qstrdup(
"objective-c++"));
break;
235 size_t numUnsavedFiles =
p->filesInSameTU.size()+1;
236 p->numFiles = numUnsavedFiles;
237 p->sources.resize(numUnsavedFiles);
238 p->ufs.resize(numUnsavedFiles);
239 size_t refIndent = 0;
242 p->ufs[0].Contents =
p->sources[0].data();
243 p->ufs[0].Length =
p->sources[0].length();
244 p->fileMapping.emplace(fileName.
data(),0);
246 for (
auto it =
p->filesInSameTU.begin();
247 it !=
p->filesInSameTU.end() && i<numUnsavedFiles;
250 p->fileMapping.emplace(std::make_pair(*it,
static_cast<uint32_t
>(i)));
252 p->ufs[i].Filename =
qstrdup(it->c_str());
253 p->ufs[i].Contents =
p->sources[i].data();
254 p->ufs[i].Length =
p->sources[i].length();
259 p->tu = clang_parseTranslationUnit(
p->index, fileName.
data(),
260 argv.data(),
static_cast<int>(argv.size()),
p->ufs.data(), numUnsavedFiles,
261 CXTranslationUnit_DetailedPreprocessingRecord);
264 for (i=0;i<argv.size();++i)
272 size_t n=clang_getNumDiagnostics(
p->tu);
275 CXDiagnostic diag = clang_getDiagnostic(
p->tu,
static_cast<unsigned>(i));
276 CXString
string = clang_formatDiagnostic(diag,
277 clang_defaultDiagnosticDisplayOptions());
278 err(
"{} [clang]\n",clang_getCString(
string));
279 clang_disposeString(
string);
280 clang_disposeDiagnostic(diag);
285 err(
"clang: Failed to parse translation unit {}\n",fileName);
292 bool clangAssistedParsing =
Config_getBool(CLANG_ASSISTED_PARSING);
293 if (!clangAssistedParsing)
return;
297 clang_disposeTokens(
p->tu,
p->tokens,
p->numTokens);
298 clang_disposeTranslationUnit(
p->tu);
299 clang_disposeIndex(
p->index);
300 p->fileMapping.clear();
304 for (
size_t i=0;i<
p->numFiles;i++)
320 clang_disposeTokens(
p->tu,
p->tokens,
p->numTokens);
326 if (it!=
p->fileMapping.end() && it->second <
p->numFiles)
328 uint32_t i = it->second;
330 CXSourceLocation fileBegin = clang_getLocationForOffset(
p->tu, f, 0);
331 CXSourceLocation fileEnd = clang_getLocationForOffset(
p->tu, f,
p->ufs[i].Length);
332 CXSourceRange fileRange = clang_getRange(fileBegin, fileEnd);
334 clang_tokenize(
p->tu,fileRange,&
p->tokens,&
p->numTokens);
335 p->cursors.resize(
p->numTokens);
336 clang_annotateTokens(
p->tu,
p->tokens,
p->numTokens,
p->cursors.data());
341 err(
"clang: Failed to find input file {} in mapping\n",fd->
absFilePath());
350 if (symbol==
nullptr)
return result;
351 bool clangAssistedParsing =
Config_getBool(CLANG_ASSISTED_PARSING);
352 if (!clangAssistedParsing)
return result;
354 auto getCurrentTokenLine = [
this]() -> uint32_t
357 if (
p->numTokens==0)
return 1;
359 if (
p->curToken>=
p->numTokens)
p->curToken=
p->numTokens-1;
360 CXSourceLocation start = clang_getTokenLocation(
p->tu,
p->tokens[
p->curToken]);
361 clang_getSpellingLocation(start,
nullptr, &l, &c,
nullptr);
365 int sl = strlen(symbol);
366 uint32_t l = getCurrentTokenLine();
367 while (l>=line &&
p->curToken>0)
372 l = getCurrentTokenLine();
377 l = getCurrentTokenLine();
381 while (l<=line && p->curToken<p->numTokens && !found)
383 CXString tokenString = clang_getTokenSpelling(
p->tu,
p->tokens[
p->curToken]);
386 AUTO_TRACE_ADD(
"try to match symbol {} with token {}",symbol,clang_getCString(tokenString));
388 const char *ts = clang_getCString(tokenString);
390 int startIndex =
p->curToken;
391 if (l==line && strncmp(ts,symbol,tl)==0)
398 if (
p->curToken>=
p->numTokens)
402 l = getCurrentTokenLine();
403 clang_disposeString(tokenString);
404 tokenString = clang_getTokenSpelling(
p->tu,
p->tokens[
p->curToken]);
405 ts = clang_getCString(tokenString);
406 tl = ts ? strlen(ts) : 0;
409 while (offset<sl && ((c=symbol[offset])==
' ' || c==
'\t' || c==
'\r' || c==
'\n'))
413 if (strncmp(ts,symbol+offset,tl)!=0)
423 CXCursor c =
p->cursors[
p->curToken];
424 CXString usr = clang_getCursorUSR(c);
425 AUTO_TRACE_ADD(
"found full match {} usr='{}'",symbol,clang_getCString(usr));
426 result = clang_getCString(usr);
427 clang_disposeString(usr);
432 p->curToken = startIndex;
435 clang_disposeString(tokenString);
437 if (
p->curToken<
p->numTokens)
439 l = getCurrentTokenLine();
462 if (endLine!=-1 && startLine!=endLine &&
470 (
p->foldStack.empty() ||
p->foldStack.back()->getEndBodyLine()!=startLine))
496 p->foldStack.push_back(d);
504 while (!
p->foldStack.empty())
506 const Definition *dd =
p->foldStack.back();
510 p->foldStack.pop_back();
529 if (
p->currentMemberDef!=md)
531 p->searchForBody=
true;
535 p->currentMemberDef=md;
540 line,writeLineAnchor);
544 p->currentMemberDef=
nullptr;
549 line,writeLineAnchor);
554 ol.
writeLineNumber(QCString(),QCString(),QCString(),line,writeLineAnchor);
560 ol.
writeLineNumber(QCString(),QCString(),QCString(),line,writeLineAnchor);
567 lineAnchor.
sprintf(
"l%05d",line);
575 uint32_t &line,uint32_t &column,
const char *fontClass)
578 const char *
p=text,*sp=
p;
580 bool inlineCodeFragment =
false;
585 while ((c=*
p++) && c!=
'\n') { column++; }
589 size_t l =
static_cast<size_t>(
p-sp-1);
591 ol.
codify(QCString(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;
741 AUTO_TRACE_ADD(
"didn't find definition for '{}' usr='{}' kind={}",
742 text,usrStr,(
int)clang_getCursorKind(c));
755 (
p->currentMemberDef!=d ||
p->currentLine<line))
757 std::lock_guard<std::mutex> lock(g_docCrossReferenceMutex);
766 clang_disposeString(usr);
776 p->searchForBody=
false;
779 else if (
p->searchForBody &&
qstrcmp(s,
";")==0)
781 p->searchForBody=
false;
784 if (
p->insideBody &&
qstrcmp(s,
"{")==0)
788 if (
p->insideBody &&
qstrcmp(s,
"}")==0)
791 if (
p->bracketCount<=0)
803 p->currentMemberDef=
nullptr;
805 p->searchForBody=
false;
808 p->foldStack.clear();
810 unsigned int line=1,column=1;
811 QCString lineNumber,lineAnchor;
812 bool inlineCodeFragment =
false;
815 for (
unsigned int i=0;i<
p->numTokens;i++)
817 CXSourceLocation start = clang_getTokenLocation(
p->tu,
p->tokens[i]);
818 unsigned int l=0, c=0;
819 clang_getSpellingLocation(start,
nullptr, &l, &c,
nullptr);
820 if (l > line) column = 1;
828 while (column<c) { ol.
codify(
" "); column++; }
829 CXString tokenString = clang_getTokenSpelling(
p->tu,
p->tokens[i]);
830 char const *s = clang_getCString(tokenString);
831 CXCursorKind cursorKind = clang_getCursorKind(
p->cursors[i]);
832 CXTokenKind tokenKind = clang_getTokenKind(
p->tokens[i]);
836 case CXToken_Keyword:
837 if (strcmp(s,
"operator")==0)
844 cursorKind==CXCursor_PreprocessingDirective ?
"preprocessor" :
848 case CXToken_Literal:
849 if (cursorKind==CXCursor_InclusionDirective)
853 else if (s[0]==
'"' || s[0]==
'\'')
862 case CXToken_Comment:
866 if (tokenKind==CXToken_Punctuation)
873 case CXCursor_PreprocessingDirective:
876 case CXCursor_MacroDefinition:
879 case CXCursor_InclusionDirective:
882 case CXCursor_MacroExpansion:
886 if (tokenKind==CXToken_Identifier ||
887 (tokenKind==CXToken_Punctuation &&
888 (cursorKind==CXCursor_DeclRefExpr ||
889 cursorKind==CXCursor_MemberRefExpr ||
890 cursorKind==CXCursor_CallExpr ||
891 cursorKind==CXCursor_ObjCMessageExpr)
908 clang_disposeString(tokenString);
913 while (!
p->foldStack.empty())
916 p->foldStack.pop_back();
919 p->tooltipManager.writeTooltips(ol);
932 db = clang::tooling::CompilationDatabase::loadFromDirectory(clangCompileDatabase.
data(), error);
933 if (!clangCompileDatabase.
isEmpty() && clangCompileDatabase!=
"0" && db==
nullptr)
936 err(
"{} using clang compilation database path of: \"{}\"\n", error, clangCompileDatabase);
940 std::unique_ptr<clang::tooling::CompilationDatabase> db;
959 return std::make_unique<ClangTUParser>(*
this,fd);
988 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(...)
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.