9#include <clang-c/Index.h>
10#include "clang/Tooling/CompilationDatabase.h"
11#include "clang/Tooling/Tooling.h"
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()));
155 if (std::string(argv[argv.size() - 1]) ==
"--") {
161 for (
size_t i=0;i<clangOptions.size();i++)
163 argv.push_back(
qstrdup(clangOptions[i].c_str()));
171 if (clangIncludeInputPaths)
175 QCString inc = QCString(
"-I")+path.data();
181 for (
size_t i=0;i<includePath.size();i++)
183 QCString inc = QCString(
"-I")+includePath[i].c_str();
187 for (
size_t i=0;i<clangOptions.size();i++)
189 argv.push_back(
qstrdup(clangOptions[i].c_str()));
192 argv.push_back(
qstrdup(
"-ferror-limit=0"));
200 QCString fn = fileName.
lower();
203 if (
p->detectedLang!=DetectedLang::Cpp &&
207 p->detectedLang = DetectedLang::Cpp;
211 p->detectedLang = DetectedLang::ObjCpp;
215 p->detectedLang = DetectedLang::ObjC;
218 switch (
p->detectedLang)
220 case DetectedLang::Cpp:
223 argv.push_back(
qstrdup(
"c++-header"));
225 argv.push_back(
qstrdup(
"c++"));
227 case DetectedLang::ObjC: argv.push_back(
qstrdup(
"objective-c"));
break;
228 case DetectedLang::ObjCpp: argv.push_back(
qstrdup(
"objective-c++"));
break;
233 size_t numUnsavedFiles =
p->filesInSameTU.size()+1;
234 p->numFiles = numUnsavedFiles;
235 p->sources.resize(numUnsavedFiles);
236 p->ufs.resize(numUnsavedFiles);
237 size_t refIndent = 0;
240 p->ufs[0].Contents =
p->sources[0].data();
241 p->ufs[0].Length =
p->sources[0].length();
242 p->fileMapping.emplace(fileName.
data(),0);
244 for (
auto it =
p->filesInSameTU.begin();
245 it !=
p->filesInSameTU.end() && i<numUnsavedFiles;
248 p->fileMapping.emplace(it->c_str(),
static_cast<uint32_t
>(i));
250 p->ufs[i].Filename =
qstrdup(it->c_str());
251 p->ufs[i].Contents =
p->sources[i].data();
252 p->ufs[i].Length =
p->sources[i].length();
257 p->tu = clang_parseTranslationUnit(
p->index, fileName.
data(),
258 argv.data(),
static_cast<int>(argv.size()),
p->ufs.data(), numUnsavedFiles,
259 CXTranslationUnit_DetailedPreprocessingRecord);
262 for (i=0;i<argv.size();++i)
270 size_t n=clang_getNumDiagnostics(
p->tu);
273 CXDiagnostic diag = clang_getDiagnostic(
p->tu,
static_cast<unsigned>(i));
274 CXString
string = clang_formatDiagnostic(diag,
275 clang_defaultDiagnosticDisplayOptions());
276 err(
"%s [clang]\n",clang_getCString(
string));
277 clang_disposeString(
string);
278 clang_disposeDiagnostic(diag);
283 err(
"clang: Failed to parse translation unit %s\n",
qPrint(fileName));
290 bool clangAssistedParsing =
Config_getBool(CLANG_ASSISTED_PARSING);
291 if (!clangAssistedParsing)
return;
295 clang_disposeTokens(
p->tu,
p->tokens,
p->numTokens);
296 clang_disposeTranslationUnit(
p->tu);
297 clang_disposeIndex(
p->index);
298 p->fileMapping.clear();
302 for (
size_t i=0;i<
p->numFiles;i++)
304 delete[]
p->ufs[i].Filename;
318 clang_disposeTokens(
p->tu,
p->tokens,
p->numTokens);
324 if (it!=
p->fileMapping.end() && it->second <
p->numFiles)
326 uint32_t i = it->second;
328 CXSourceLocation fileBegin = clang_getLocationForOffset(
p->tu, f, 0);
329 CXSourceLocation fileEnd = clang_getLocationForOffset(
p->tu, f,
p->ufs[i].Length);
330 CXSourceRange fileRange = clang_getRange(fileBegin, fileEnd);
332 clang_tokenize(
p->tu,fileRange,&
p->tokens,&
p->numTokens);
333 p->cursors.resize(
p->numTokens);
334 clang_annotateTokens(
p->tu,
p->tokens,
p->numTokens,
p->cursors.data());
348 if (symbol==
nullptr)
return result;
349 bool clangAssistedParsing =
Config_getBool(CLANG_ASSISTED_PARSING);
350 if (!clangAssistedParsing)
return result;
352 auto getCurrentTokenLine = [
this]() -> uint32_t
355 if (
p->numTokens==0)
return 1;
357 if (
p->curToken>=
p->numTokens)
p->curToken=
p->numTokens-1;
358 CXSourceLocation start = clang_getTokenLocation(
p->tu,
p->tokens[
p->curToken]);
359 clang_getSpellingLocation(start,
nullptr, &l, &c,
nullptr);
363 int sl = strlen(symbol);
364 uint32_t l = getCurrentTokenLine();
365 while (l>=line &&
p->curToken>0)
370 l = getCurrentTokenLine();
375 l = getCurrentTokenLine();
379 while (l<=line && p->curToken<p->numTokens && !
found)
381 CXString tokenString = clang_getTokenSpelling(
p->tu,
p->tokens[
p->curToken]);
386 const char *ts = clang_getCString(tokenString);
388 int startIndex =
p->curToken;
389 if (l==line && strncmp(ts,symbol,tl)==0)
396 if (
p->curToken>=
p->numTokens)
400 l = getCurrentTokenLine();
401 clang_disposeString(tokenString);
402 tokenString = clang_getTokenSpelling(
p->tu,
p->tokens[
p->curToken]);
403 ts = clang_getCString(tokenString);
404 tl = ts ? strlen(ts) : 0;
407 while (offset<sl && ((c=symbol[offset])==
' ' || c==
'\t' || c==
'\r' || c==
'\n'))
411 if (strncmp(ts,symbol+offset,tl)!=0)
421 CXCursor c =
p->cursors[
p->curToken];
422 CXString usr = clang_getCursorUSR(c);
424 result = clang_getCString(usr);
425 clang_disposeString(usr);
430 p->curToken = startIndex;
433 clang_disposeString(tokenString);
435 if (
p->curToken<
p->numTokens)
437 l = getCurrentTokenLine();
460 if (endLine!=-1 && startLine!=endLine &&
468 (
p->foldStack.empty() ||
p->foldStack.back()->getEndBodyLine()!=startLine))
494 p->foldStack.push_back(d);
502 while (!
p->foldStack.empty())
504 const Definition *dd =
p->foldStack.back();
508 p->foldStack.pop_back();
527 if (
p->currentMemberDef!=md)
529 p->searchForBody=
TRUE;
533 p->currentMemberDef=md;
538 line,writeLineAnchor);
542 p->currentMemberDef=
nullptr;
547 line,writeLineAnchor);
552 ol.
writeLineNumber(QCString(),QCString(),QCString(),line,writeLineAnchor);
558 ol.
writeLineNumber(QCString(),QCString(),QCString(),line,writeLineAnchor);
565 lineAnchor.
sprintf(
"l%05d",line);
573 uint32_t &line,uint32_t &column,
const char *fontClass)
576 const char *
p=text,*sp=
p;
578 bool inlineCodeFragment =
false;
583 while ((c=*
p++) && c!=
'\n') { column++; }
587 size_t l =
static_cast<size_t>(
p-sp-1);
589 std::string tmp(sp,l);
607 const FileDef *fd,uint32_t &line,uint32_t &column,
612 p->tooltipManager.addTooltip(d);
615 QCString anchor = d->
anchor();
621 bool inlineCodeFragment =
false;
628 while ((c=*
p++) && c!=
'\n') { column++; }
648 uint32_t &line,uint32_t &column,
const char *text)
650 QCString incName = text;
651 incName = incName.
mid(1,incName.
length()-2);
652 FileDef *ifd=
nullptr;
659 auto it = std::find_if(fn->begin(),
661 [&fd](
const auto &ifd)
662 { return fd->isIncluded(ifd->absFilePath()); });
663 bool found = it!=fn->end();
682 codifyLines(ol,ifd,text,line,column,
"preprocessor");
687 uint32_t &line,uint32_t &column,
const char *text)
692 for (
const auto &md : *mn)
706 uint32_t &line,uint32_t &column,
const char *text,
int tokenIndex)
708 CXCursor c =
p->cursors[tokenIndex];
709 CXCursor r = clang_getCursorReferenced(c);
710 if (!clang_equalCursors(r, c))
714 CXCursor t = clang_getSpecializedCursorTemplate(c);
715 if (!clang_Cursor_isNull(t) && !clang_equalCursors(t,c))
719 CXString usr = clang_getCursorUSR(c);
720 const char *usrStr = clang_getCString(usr);
722 const Definition *d =
nullptr;
745 (
p->currentMemberDef!=d ||
p->currentLine<line))
756 clang_disposeString(usr);
769 else if (
p->searchForBody &&
qstrcmp(s,
";")==0)
774 if (
p->insideBody &&
qstrcmp(s,
"{")==0)
778 if (
p->insideBody &&
qstrcmp(s,
"}")==0)
781 if (
p->bracketCount<=0)
792 p->currentMemberDef=
nullptr;
797 p->foldStack.clear();
799 unsigned int line=1,column=1;
800 QCString lineNumber,lineAnchor;
801 bool inlineCodeFragment =
false;
804 for (
unsigned int i=0;i<
p->numTokens;i++)
806 CXSourceLocation start = clang_getTokenLocation(
p->tu,
p->tokens[i]);
807 unsigned int l=0, c=0;
808 clang_getSpellingLocation(start,
nullptr, &l, &c,
nullptr);
809 if (l > line) column = 1;
817 while (column<c) { ol.
codify(
" "); column++; }
818 CXString tokenString = clang_getTokenSpelling(
p->tu,
p->tokens[i]);
819 char const *s = clang_getCString(tokenString);
820 CXCursorKind cursorKind = clang_getCursorKind(
p->cursors[i]);
821 CXTokenKind tokenKind = clang_getTokenKind(
p->tokens[i]);
825 case CXToken_Keyword:
826 if (strcmp(s,
"operator")==0)
833 cursorKind==CXCursor_PreprocessingDirective ?
"preprocessor" :
837 case CXToken_Literal:
838 if (cursorKind==CXCursor_InclusionDirective)
842 else if (s[0]==
'"' || s[0]==
'\'')
851 case CXToken_Comment:
855 if (tokenKind==CXToken_Punctuation)
862 case CXCursor_PreprocessingDirective:
865 case CXCursor_MacroDefinition:
868 case CXCursor_InclusionDirective:
871 case CXCursor_MacroExpansion:
875 if (tokenKind==CXToken_Identifier ||
876 (tokenKind==CXToken_Punctuation &&
877 (cursorKind==CXCursor_DeclRefExpr ||
878 cursorKind==CXCursor_MemberRefExpr ||
879 cursorKind==CXCursor_CallExpr ||
880 cursorKind==CXCursor_ObjCMessageExpr)
897 clang_disposeString(tokenString);
902 while (!
p->foldStack.empty())
905 p->foldStack.pop_back();
908 p->tooltipManager.writeTooltips(ol);
921 db = clang::tooling::CompilationDatabase::loadFromDirectory(clangCompileDatabase.
data(), error);
922 if (!clangCompileDatabase.
isEmpty() && clangCompileDatabase!=
"0" && db==
nullptr)
925 err(
"%s using clang compilation database path of: \"%s\"\n", error.c_str(),
926 clangCompileDatabase.
data());
930 std::unique_ptr<clang::tooling::CompilationDatabase> db;
949 return std::make_unique<ClangTUParser>(*
this,fd);
978 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
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
Find an object given the key.
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
static std::mutex g_docCrossReferenceMutex
void addDocCrossReference(const MemberDef *s, const MemberDef *d)
MemberDef * toMemberDef(Definition *d)
char * qstrdup(const char *str)
Returns a copy of a string s.
void qstrfree(const char *str)
Frees the memory allocated using qstrdup().
const char * qPrint(const char *s)
int qstrcmp(const char *str1, const char *str2)
Web server based search engine.
SrcLangExt
Language as given by extension.
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.