|
Packit Service |
50c9f2 |
#include "clangparser.h"
|
|
Packit Service |
50c9f2 |
#include "settings.h"
|
|
Packit Service |
50c9f2 |
#include <stdio.h>
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
#if USE_LIBCLANG
|
|
Packit Service |
50c9f2 |
#include <clang-c/Index.h>
|
|
Packit Service |
50c9f2 |
#include "clang/Tooling/Tooling.h"
|
|
Packit Service |
50c9f2 |
#include <qfileinfo.h>
|
|
Packit Service |
50c9f2 |
#include <stdlib.h>
|
|
Packit Service |
50c9f2 |
#include "message.h"
|
|
Packit Service |
50c9f2 |
#include "sortdict.h"
|
|
Packit Service |
50c9f2 |
#include "outputgen.h"
|
|
Packit Service |
50c9f2 |
#include "filedef.h"
|
|
Packit Service |
50c9f2 |
#include "memberdef.h"
|
|
Packit Service |
50c9f2 |
#include "doxygen.h"
|
|
Packit Service |
50c9f2 |
#include "util.h"
|
|
Packit Service |
50c9f2 |
#include "config.h"
|
|
Packit Service |
50c9f2 |
#include "growbuf.h"
|
|
Packit Service |
50c9f2 |
#include "membername.h"
|
|
Packit Service |
50c9f2 |
#include "filename.h"
|
|
Packit Service |
50c9f2 |
#include "tooltip.h"
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
static Definition *g_currentDefinition=0;
|
|
Packit Service |
50c9f2 |
static MemberDef *g_currentMemberDef=0;
|
|
Packit Service |
50c9f2 |
static uint g_currentLine=0;
|
|
Packit Service |
50c9f2 |
static bool g_searchForBody=FALSE;
|
|
Packit Service |
50c9f2 |
static bool g_insideBody=FALSE;
|
|
Packit Service |
50c9f2 |
static uint g_bracketCount=0;
|
|
Packit Service |
50c9f2 |
#endif
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
ClangParser *ClangParser::instance()
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
if (!s_instance) s_instance = new ClangParser;
|
|
Packit Service |
50c9f2 |
return s_instance;
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
ClangParser *ClangParser::s_instance = 0;
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
//--------------------------------------------------------------------------
|
|
Packit Service |
50c9f2 |
#if USE_LIBCLANG
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
class ClangParser::Private
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
public:
|
|
Packit Service |
50c9f2 |
enum DetectedLang { Detected_Cpp, Detected_ObjC, Detected_ObjCpp };
|
|
Packit Service |
50c9f2 |
Private() : tu(0), tokens(0), numTokens(0), cursors(0),
|
|
Packit Service |
50c9f2 |
ufs(0), sources(0), numFiles(0), fileMapping(257),
|
|
Packit Service |
50c9f2 |
detectedLang(Detected_Cpp)
|
|
Packit Service |
50c9f2 |
{ fileMapping.setAutoDelete(TRUE); }
|
|
Packit Service |
50c9f2 |
int getCurrentTokenLine();
|
|
Packit Service |
50c9f2 |
CXIndex index;
|
|
Packit Service |
50c9f2 |
CXTranslationUnit tu;
|
|
Packit Service |
50c9f2 |
QCString fileName;
|
|
Packit Service |
50c9f2 |
CXToken *tokens;
|
|
Packit Service |
50c9f2 |
uint numTokens;
|
|
Packit Service |
50c9f2 |
CXCursor *cursors;
|
|
Packit Service |
50c9f2 |
uint curLine;
|
|
Packit Service |
50c9f2 |
uint curToken;
|
|
Packit Service |
50c9f2 |
CXUnsavedFile *ufs;
|
|
Packit Service |
50c9f2 |
QCString *sources;
|
|
Packit Service |
50c9f2 |
uint numFiles;
|
|
Packit Service |
50c9f2 |
QDict<uint> fileMapping;
|
|
Packit Service |
50c9f2 |
DetectedLang detectedLang;
|
|
Packit Service |
50c9f2 |
};
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
static QCString detab(const QCString &s)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
static int tabSize = Config_getInt(TAB_SIZE);
|
|
Packit Service |
50c9f2 |
GrowBuf out;
|
|
Packit Service |
50c9f2 |
int size = s.length();
|
|
Packit Service |
50c9f2 |
const char *data = s.data();
|
|
Packit Service |
50c9f2 |
int i=0;
|
|
Packit Service |
50c9f2 |
int col=0;
|
|
Packit Service |
50c9f2 |
const int maxIndent=1000000; // value representing infinity
|
|
Packit Service |
50c9f2 |
int minIndent=maxIndent;
|
|
Packit Service |
50c9f2 |
while (i
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
char c = data[i++];
|
|
Packit Service |
50c9f2 |
switch(c)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
case '\t': // expand tab
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
int stop = tabSize - (col%tabSize);
|
|
Packit Service |
50c9f2 |
//printf("expand at %d stop=%d\n",col,stop);
|
|
Packit Service |
50c9f2 |
col+=stop;
|
|
Packit Service |
50c9f2 |
while (stop--) out.addChar(' ');
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
break;
|
|
Packit Service |
50c9f2 |
case '\n': // reset colomn counter
|
|
Packit Service |
50c9f2 |
out.addChar(c);
|
|
Packit Service |
50c9f2 |
col=0;
|
|
Packit Service |
50c9f2 |
break;
|
|
Packit Service |
50c9f2 |
case ' ': // increment column counter
|
|
Packit Service |
50c9f2 |
out.addChar(c);
|
|
Packit Service |
50c9f2 |
col++;
|
|
Packit Service |
50c9f2 |
break;
|
|
Packit Service |
50c9f2 |
default: // non-whitespace => update minIndent
|
|
Packit Service |
50c9f2 |
out.addChar(c);
|
|
Packit Service |
50c9f2 |
if (c<0 && i
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
out.addChar(data[i++]); // >= 2 bytes
|
|
Packit Service |
50c9f2 |
if (((uchar)c&0xE0)==0xE0 && i
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
out.addChar(data[i++]); // 3 bytes
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
if (((uchar)c&0xF0)==0xF0 && i
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
out.addChar(data[i++]); // 4 byres
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
if (col
|
|
Packit Service |
50c9f2 |
col++;
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
out.addChar(0);
|
|
Packit Service |
50c9f2 |
//printf("detab refIndent=%d\n",refIndent);
|
|
Packit Service |
50c9f2 |
return out.get();
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
/** Callback function called for each include in a translation unit */
|
|
Packit Service |
50c9f2 |
static void inclusionVisitor(CXFile includedFile,
|
|
Packit Service |
50c9f2 |
CXSourceLocation* /*inclusionStack*/,
|
|
Packit Service |
50c9f2 |
unsigned /*includeLen*/,
|
|
Packit Service |
50c9f2 |
CXClientData clientData)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
QDict<void> *fileDict = (QDict<void> *)clientData;
|
|
Packit Service |
50c9f2 |
CXString incFileName = clang_getFileName(includedFile);
|
|
Packit Service |
50c9f2 |
//printf("--- file %s includes %s\n",fileName,clang_getCString(incFileName));
|
|
Packit Service |
50c9f2 |
fileDict->insert(clang_getCString(incFileName),(void*)0x8);
|
|
Packit Service |
50c9f2 |
clang_disposeString(incFileName);
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
/** filter the \a files and only keep those that are found as include files
|
|
Packit Service |
50c9f2 |
* within the current translation unit.
|
|
Packit Service |
50c9f2 |
* @param[in,out] files The list of files to filter.
|
|
Packit Service |
50c9f2 |
*/
|
|
Packit Service |
50c9f2 |
void ClangParser::determineInputFilesInSameTu(QStrList &files)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
// put the files in this translation unit in a dictionary
|
|
Packit Service |
50c9f2 |
QDict<void> incFound(257);
|
|
Packit Service |
50c9f2 |
clang_getInclusions(p->tu,
|
|
Packit Service |
50c9f2 |
inclusionVisitor,
|
|
Packit Service |
50c9f2 |
(CXClientData)&incFound
|
|
Packit Service |
50c9f2 |
);
|
|
Packit Service |
50c9f2 |
// create a new filtered file list
|
|
Packit Service |
50c9f2 |
QStrList resultIncludes;
|
|
Packit Service |
50c9f2 |
QStrListIterator it2(files);
|
|
Packit Service |
50c9f2 |
for (it2.toFirst();it2.current();++it2)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
if (incFound.find(it2.current()))
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
resultIncludes.append(it2.current());
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
// replace the original list
|
|
Packit Service |
50c9f2 |
files=resultIncludes;
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
void ClangParser::start(const char *fileName,QStrList &filesInTranslationUnit)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
static bool clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING);
|
|
Packit Service |
50c9f2 |
static QStrList &includePath = Config_getList(INCLUDE_PATH);
|
|
Packit Service |
50c9f2 |
static QStrList clangOptions = Config_getList(CLANG_OPTIONS);
|
|
Packit Service |
50c9f2 |
static QCString clangCompileDatabase = Config_getList(CLANG_COMPILATION_DATABASE_PATH);
|
|
Packit Service |
50c9f2 |
if (!clangAssistedParsing) return;
|
|
Packit Service |
50c9f2 |
//printf("ClangParser::start(%s)\n",fileName);
|
|
Packit Service |
50c9f2 |
p->fileName = fileName;
|
|
Packit Service |
50c9f2 |
p->index = clang_createIndex(0, 0);
|
|
Packit Service |
50c9f2 |
p->curLine = 1;
|
|
Packit Service |
50c9f2 |
p->curToken = 0;
|
|
Packit Service |
50c9f2 |
QDictIterator<void> di(Doxygen::inputPaths);
|
|
Packit Service |
50c9f2 |
int argc=0;
|
|
Packit Service |
50c9f2 |
std::string error;
|
|
Packit Service |
50c9f2 |
// load a clang compilation database (https://clang.llvm.org/docs/JSONCompilationDatabase.html)
|
|
Packit Service |
50c9f2 |
// this only needs to be loaded once, and could be refactored to a higher level function
|
|
Packit Service |
50c9f2 |
static std::unique_ptr<clang::tooling::CompilationDatabase> db =
|
|
Packit Service |
50c9f2 |
clang::tooling::CompilationDatabase::loadFromDirectory(clangCompileDatabase.data(), error);
|
|
Packit Service |
50c9f2 |
int clang_option_len = 0;
|
|
Packit Service |
50c9f2 |
std::vector<clang::tooling::CompileCommand> command;
|
|
Packit Service |
50c9f2 |
if (strcmp(clangCompileDatabase, "0") != 0) {
|
|
Packit Service |
50c9f2 |
if (db == nullptr) {
|
|
Packit Service |
50c9f2 |
// user specified a path, but DB file was not found
|
|
Packit Service |
50c9f2 |
err("%s using clang compilation database path of: \"%s\"\n", error.c_str(),
|
|
Packit Service |
50c9f2 |
clangCompileDatabase.data());
|
|
Packit Service |
50c9f2 |
} else {
|
|
Packit Service |
50c9f2 |
// check if the file we are parsing is in the DB
|
|
Packit Service |
50c9f2 |
command = db->getCompileCommands(fileName);
|
|
Packit Service |
50c9f2 |
if (!command.empty() ) {
|
|
Packit Service |
50c9f2 |
// it's possible to have multiple entries for the same file, so use the last entry
|
|
Packit Service |
50c9f2 |
clang_option_len = command[command.size()-1].CommandLine.size();
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
char **argv = (char**)malloc(sizeof(char*)*(4+Doxygen::inputPaths.count()+includePath.count()+clangOptions.count()+clang_option_len));
|
|
Packit Service |
50c9f2 |
if (!command.empty() ) {
|
|
Packit Service |
50c9f2 |
std::vector<std::string> options = command[command.size()-1].CommandLine;
|
|
Packit Service |
50c9f2 |
// copy each compiler option used from the database. Skip the first which is compiler exe.
|
|
Packit Service |
50c9f2 |
for (auto option = options.begin()+1; option != options.end(); option++) {
|
|
Packit Service |
50c9f2 |
argv[argc++] = strdup(option->c_str());
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
// this extra addition to argv is accounted for as we are skipping the first entry in
|
|
Packit Service |
50c9f2 |
argv[argc++]=strdup("-w"); // finally, turn off warnings.
|
|
Packit Service |
50c9f2 |
} else {
|
|
Packit Service |
50c9f2 |
// add include paths for input files
|
|
Packit Service |
50c9f2 |
for (di.toFirst();di.current();++di,++argc)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
QCString inc = QCString("-I")+di.currentKey();
|
|
Packit Service |
50c9f2 |
argv[argc]=strdup(inc.data());
|
|
Packit Service |
50c9f2 |
//printf("argv[%d]=%s\n",argc,argv[argc]);
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
// add external include paths
|
|
Packit Service |
50c9f2 |
for (uint i=0;i
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
QCString inc = QCString("-I")+includePath.at(i);
|
|
Packit Service |
50c9f2 |
argv[argc++]=strdup(inc.data());
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
// user specified options
|
|
Packit Service |
50c9f2 |
for (uint i=0;i
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
argv[argc++]=strdup(clangOptions.at(i));
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
// extra options
|
|
Packit Service |
50c9f2 |
argv[argc++]=strdup("-ferror-limit=0");
|
|
Packit Service |
50c9f2 |
argv[argc++]=strdup("-x");
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
// Since we can be presented with a .h file that can contain C/C++ or
|
|
Packit Service |
50c9f2 |
// Objective C code and we need to configure the parser before knowing this,
|
|
Packit Service |
50c9f2 |
// we use the source file to detected the language. Detection will fail if you
|
|
Packit Service |
50c9f2 |
// pass a bunch of .h files containing ObjC code, and no sources :-(
|
|
Packit Service |
50c9f2 |
SrcLangExt lang = getLanguageFromFileName(fileName);
|
|
Packit Service |
50c9f2 |
if (lang==SrcLangExt_ObjC || p->detectedLang!=ClangParser::Private::Detected_Cpp)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
QCString fn = fileName;
|
|
Packit Service |
50c9f2 |
if (p->detectedLang==ClangParser::Private::Detected_Cpp &&
|
|
Packit Service |
50c9f2 |
(fn.right(4).lower()==".cpp" || fn.right(4).lower()==".cxx" ||
|
|
Packit Service |
50c9f2 |
fn.right(3).lower()==".cc" || fn.right(2).lower()==".c"))
|
|
Packit Service |
50c9f2 |
{ // fall back to C/C++ once we see an extension that indicates this
|
|
Packit Service |
50c9f2 |
p->detectedLang = ClangParser::Private::Detected_Cpp;
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
else if (fn.right(3).lower()==".mm") // switch to Objective C++
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
p->detectedLang = ClangParser::Private::Detected_ObjCpp;
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
else if (fn.right(2).lower()==".m") // switch to Objective C
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
p->detectedLang = ClangParser::Private::Detected_ObjC;
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
switch(p->detectedLang)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
case ClangParser::Private::Detected_Cpp:
|
|
Packit Service |
50c9f2 |
argv[argc++]=strdup("c++");
|
|
Packit Service |
50c9f2 |
break;
|
|
Packit Service |
50c9f2 |
case ClangParser::Private::Detected_ObjC:
|
|
Packit Service |
50c9f2 |
argv[argc++]=strdup("objective-c");
|
|
Packit Service |
50c9f2 |
break;
|
|
Packit Service |
50c9f2 |
case ClangParser::Private::Detected_ObjCpp:
|
|
Packit Service |
50c9f2 |
argv[argc++]=strdup("objective-c++");
|
|
Packit Service |
50c9f2 |
break;
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
// provide the input and and its dependencies as unsaved files so we can
|
|
Packit Service |
50c9f2 |
// pass the filtered versions
|
|
Packit Service |
50c9f2 |
argv[argc++]=strdup(fileName);
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
static bool filterSourceFiles = Config_getBool(FILTER_SOURCE_FILES);
|
|
Packit Service |
50c9f2 |
//printf("source %s ----------\n%s\n-------------\n\n",
|
|
Packit Service |
50c9f2 |
// fileName,p->source.data());
|
|
Packit Service |
50c9f2 |
uint numUnsavedFiles = filesInTranslationUnit.count()+1;
|
|
Packit Service |
50c9f2 |
p->numFiles = numUnsavedFiles;
|
|
Packit Service |
50c9f2 |
p->sources = new QCString[numUnsavedFiles];
|
|
Packit Service |
50c9f2 |
p->ufs = new CXUnsavedFile[numUnsavedFiles];
|
|
Packit Service |
50c9f2 |
p->sources[0] = detab(fileToString(fileName,filterSourceFiles,TRUE));
|
|
Packit Service |
50c9f2 |
p->ufs[0].Filename = strdup(fileName);
|
|
Packit Service |
50c9f2 |
p->ufs[0].Contents = p->sources[0].data();
|
|
Packit Service |
50c9f2 |
p->ufs[0].Length = p->sources[0].length();
|
|
Packit Service |
50c9f2 |
QStrListIterator it(filesInTranslationUnit);
|
|
Packit Service |
50c9f2 |
uint i=1;
|
|
Packit Service |
50c9f2 |
for (it.toFirst();it.current() && i
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
p->fileMapping.insert(it.current(),new uint(i));
|
|
Packit Service |
50c9f2 |
p->sources[i] = detab(fileToString(it.current(),filterSourceFiles,TRUE));
|
|
Packit Service |
50c9f2 |
p->ufs[i].Filename = strdup(it.current());
|
|
Packit Service |
50c9f2 |
p->ufs[i].Contents = p->sources[i].data();
|
|
Packit Service |
50c9f2 |
p->ufs[i].Length = p->sources[i].length();
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
// let libclang do the actual parsing
|
|
Packit Service |
50c9f2 |
p->tu = clang_parseTranslationUnit(p->index, 0,
|
|
Packit Service |
50c9f2 |
argv, argc, p->ufs, numUnsavedFiles,
|
|
Packit Service |
50c9f2 |
CXTranslationUnit_DetailedPreprocessingRecord);
|
|
Packit Service |
50c9f2 |
// free arguments
|
|
Packit Service |
50c9f2 |
for (int i=0;i
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
free(argv[i]);
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
free(argv);
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
if (p->tu)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
// filter out any includes not found by the clang parser
|
|
Packit Service |
50c9f2 |
determineInputFilesInSameTu(filesInTranslationUnit);
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
// show any warnings that the compiler produced
|
|
Packit Service |
50c9f2 |
for (uint i=0, n=clang_getNumDiagnostics(p->tu); i!=n; ++i)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
CXDiagnostic diag = clang_getDiagnostic(p->tu, i);
|
|
Packit Service |
50c9f2 |
CXString string = clang_formatDiagnostic(diag,
|
|
Packit Service |
50c9f2 |
clang_defaultDiagnosticDisplayOptions());
|
|
Packit Service |
50c9f2 |
err("%s [clang]\n",clang_getCString(string));
|
|
Packit Service |
50c9f2 |
clang_disposeString(string);
|
|
Packit Service |
50c9f2 |
clang_disposeDiagnostic(diag);
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
// create a source range for the given file
|
|
Packit Service |
50c9f2 |
QFileInfo fi(fileName);
|
|
Packit Service |
50c9f2 |
CXFile f = clang_getFile(p->tu, fileName);
|
|
Packit Service |
50c9f2 |
CXSourceLocation fileBegin = clang_getLocationForOffset(p->tu, f, 0);
|
|
Packit Service |
50c9f2 |
CXSourceLocation fileEnd = clang_getLocationForOffset(p->tu, f, p->ufs[0].Length);
|
|
Packit Service |
50c9f2 |
CXSourceRange fileRange = clang_getRange(fileBegin, fileEnd);
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
// produce a token stream for the file
|
|
Packit Service |
50c9f2 |
clang_tokenize(p->tu,fileRange,&p->tokens,&p->numTokens);
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
// produce cursors for each token in the stream
|
|
Packit Service |
50c9f2 |
p->cursors=new CXCursor[p->numTokens];
|
|
Packit Service |
50c9f2 |
clang_annotateTokens(p->tu,p->tokens,p->numTokens,p->cursors);
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
else
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
p->tokens = 0;
|
|
Packit Service |
50c9f2 |
p->numTokens = 0;
|
|
Packit Service |
50c9f2 |
p->cursors = 0;
|
|
Packit Service |
50c9f2 |
err("clang: Failed to parse translation unit %s\n",fileName);
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
void ClangParser::switchToFile(const char *fileName)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
if (p->tu)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
delete[] p->cursors;
|
|
Packit Service |
50c9f2 |
clang_disposeTokens(p->tu,p->tokens,p->numTokens);
|
|
Packit Service |
50c9f2 |
p->tokens = 0;
|
|
Packit Service |
50c9f2 |
p->numTokens = 0;
|
|
Packit Service |
50c9f2 |
p->cursors = 0;
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
QFileInfo fi(fileName);
|
|
Packit Service |
50c9f2 |
CXFile f = clang_getFile(p->tu, fileName);
|
|
Packit Service |
50c9f2 |
uint *pIndex=p->fileMapping.find(fileName);
|
|
Packit Service |
50c9f2 |
if (pIndex && *pIndex<p->numFiles)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
uint i=*pIndex;
|
|
Packit Service |
50c9f2 |
//printf("switchToFile %s: len=%ld\n",fileName,p->ufs[i].Length);
|
|
Packit Service |
50c9f2 |
CXSourceLocation fileBegin = clang_getLocationForOffset(p->tu, f, 0);
|
|
Packit Service |
50c9f2 |
CXSourceLocation fileEnd = clang_getLocationForOffset(p->tu, f, p->ufs[i].Length);
|
|
Packit Service |
50c9f2 |
CXSourceRange fileRange = clang_getRange(fileBegin, fileEnd);
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
clang_tokenize(p->tu,fileRange,&p->tokens,&p->numTokens);
|
|
Packit Service |
50c9f2 |
p->cursors=new CXCursor[p->numTokens];
|
|
Packit Service |
50c9f2 |
clang_annotateTokens(p->tu,p->tokens,p->numTokens,p->cursors);
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
p->curLine = 1;
|
|
Packit Service |
50c9f2 |
p->curToken = 0;
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
else
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
err("clang: Failed to find input file %s in mapping\n",fileName);
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
void ClangParser::finish()
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
static bool clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING);
|
|
Packit Service |
50c9f2 |
if (!clangAssistedParsing) return;
|
|
Packit Service |
50c9f2 |
if (p->tu)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
//printf("ClangParser::finish()\n");
|
|
Packit Service |
50c9f2 |
delete[] p->cursors;
|
|
Packit Service |
50c9f2 |
clang_disposeTokens(p->tu,p->tokens,p->numTokens);
|
|
Packit Service |
50c9f2 |
clang_disposeTranslationUnit(p->tu);
|
|
Packit Service |
50c9f2 |
clang_disposeIndex(p->index);
|
|
Packit Service |
50c9f2 |
p->fileMapping.clear();
|
|
Packit Service |
50c9f2 |
p->tokens = 0;
|
|
Packit Service |
50c9f2 |
p->numTokens = 0;
|
|
Packit Service |
50c9f2 |
p->cursors = 0;
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
for (uint i=0;i<p->numFiles;i++)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
free((void *)p->ufs[i].Filename);
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
delete[] p->ufs;
|
|
Packit Service |
50c9f2 |
delete[] p->sources;
|
|
Packit Service |
50c9f2 |
p->ufs = 0;
|
|
Packit Service |
50c9f2 |
p->sources = 0;
|
|
Packit Service |
50c9f2 |
p->numFiles = 0;
|
|
Packit Service |
50c9f2 |
p->tu = 0;
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
int ClangParser::Private::getCurrentTokenLine()
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
uint l, c;
|
|
Packit Service |
50c9f2 |
if (numTokens==0) return 1;
|
|
Packit Service |
50c9f2 |
// guard against filters that reduce the number of lines
|
|
Packit Service |
50c9f2 |
if (curToken>=numTokens) curToken=numTokens-1;
|
|
Packit Service |
50c9f2 |
CXSourceLocation start = clang_getTokenLocation(tu,tokens[curToken]);
|
|
Packit Service |
50c9f2 |
clang_getSpellingLocation(start, 0, &l, &c, 0);
|
|
Packit Service |
50c9f2 |
return l;
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
QCString ClangParser::lookup(uint line,const char *symbol)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
//printf("ClangParser::lookup(%d,%s)\n",line,symbol);
|
|
Packit Service |
50c9f2 |
QCString result;
|
|
Packit Service |
50c9f2 |
if (symbol==0) return result;
|
|
Packit Service |
50c9f2 |
static bool clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING);
|
|
Packit Service |
50c9f2 |
if (!clangAssistedParsing) return result;
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
int sl = strlen(symbol);
|
|
Packit Service |
50c9f2 |
uint l = p->getCurrentTokenLine();
|
|
Packit Service |
50c9f2 |
while (l>=line && p->curToken>0)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
if (l==line) // already at the right line
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
p->curToken--; // linear search to start of the line
|
|
Packit Service |
50c9f2 |
l = p->getCurrentTokenLine();
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
else
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
p->curToken/=2; // binary search backward
|
|
Packit Service |
50c9f2 |
l = p->getCurrentTokenLine();
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
bool found=FALSE;
|
|
Packit Service |
50c9f2 |
while (l<=line && p->curToken<p->numTokens && !found)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
CXString tokenString = clang_getTokenSpelling(p->tu, p->tokens[p->curToken]);
|
|
Packit Service |
50c9f2 |
//if (l==line)
|
|
Packit Service |
50c9f2 |
//{
|
|
Packit Service |
50c9f2 |
// printf("try to match symbol %s with token %s\n",symbol,clang_getCString(tokenString));
|
|
Packit Service |
50c9f2 |
//}
|
|
Packit Service |
50c9f2 |
const char *ts = clang_getCString(tokenString);
|
|
Packit Service |
50c9f2 |
int tl = strlen(ts);
|
|
Packit Service |
50c9f2 |
int startIndex = p->curToken;
|
|
Packit Service |
50c9f2 |
if (l==line && strncmp(ts,symbol,tl)==0) // found partial match at the correct line
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
int offset = tl;
|
|
Packit Service |
50c9f2 |
while (offset
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
//printf("found partial match\n");
|
|
Packit Service |
50c9f2 |
p->curToken++;
|
|
Packit Service |
50c9f2 |
if (p->curToken>=p->numTokens)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
break; // end of token stream
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
l = p->getCurrentTokenLine();
|
|
Packit Service |
50c9f2 |
clang_disposeString(tokenString);
|
|
Packit Service |
50c9f2 |
tokenString = clang_getTokenSpelling(p->tu, p->tokens[p->curToken]);
|
|
Packit Service |
50c9f2 |
ts = clang_getCString(tokenString);
|
|
Packit Service |
50c9f2 |
tl = ts ? strlen(ts) : 0;
|
|
Packit Service |
50c9f2 |
// skip over any spaces in the symbol
|
|
Packit Service |
50c9f2 |
char c;
|
|
Packit Service |
50c9f2 |
while (offset
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
offset++;
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
if (strncmp(ts,symbol+offset,tl)!=0) // next token matches?
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
//printf("no match '%s'<->'%s'\n",ts,symbol+offset);
|
|
Packit Service |
50c9f2 |
break; // no match
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
//printf("partial match '%s'<->'%s'\n",ts,symbol+offset);
|
|
Packit Service |
50c9f2 |
offset+=tl;
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
if (offset==sl) // symbol matches the token(s)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
CXCursor c = p->cursors[p->curToken];
|
|
Packit Service |
50c9f2 |
CXString usr = clang_getCursorUSR(c);
|
|
Packit Service |
50c9f2 |
//printf("found full match %s usr='%s'\n",symbol,clang_getCString(usr));
|
|
Packit Service |
50c9f2 |
result = clang_getCString(usr);
|
|
Packit Service |
50c9f2 |
clang_disposeString(usr);
|
|
Packit Service |
50c9f2 |
found=TRUE;
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
else // reset token cursor to start of the search
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
p->curToken = startIndex;
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
clang_disposeString(tokenString);
|
|
Packit Service |
50c9f2 |
p->curToken++;
|
|
Packit Service |
50c9f2 |
if (p->curToken<p->numTokens)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
l = p->getCurrentTokenLine();
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
//if (!found)
|
|
Packit Service |
50c9f2 |
//{
|
|
Packit Service |
50c9f2 |
// printf("Did not find symbol %s at line %d :-(\n",symbol,line);
|
|
Packit Service |
50c9f2 |
//}
|
|
Packit Service |
50c9f2 |
//else
|
|
Packit Service |
50c9f2 |
//{
|
|
Packit Service |
50c9f2 |
// printf("Found symbol %s usr=%s\n",symbol,result.data());
|
|
Packit Service |
50c9f2 |
//}
|
|
Packit Service |
50c9f2 |
return result;
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
static QCString keywordToType(const char *keyword)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
static bool init=TRUE;
|
|
Packit Service |
50c9f2 |
static QDict<void> flowKeywords(47);
|
|
Packit Service |
50c9f2 |
static QDict<void> typeKeywords(47);
|
|
Packit Service |
50c9f2 |
if (init)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
flowKeywords.insert("break",(void*)0x8);
|
|
Packit Service |
50c9f2 |
flowKeywords.insert("case",(void*)0x8);
|
|
Packit Service |
50c9f2 |
flowKeywords.insert("catch",(void*)0x8);
|
|
Packit Service |
50c9f2 |
flowKeywords.insert("continue",(void*)0x8);
|
|
Packit Service |
50c9f2 |
flowKeywords.insert("default",(void*)0x8);
|
|
Packit Service |
50c9f2 |
flowKeywords.insert("do",(void*)0x8);
|
|
Packit Service |
50c9f2 |
flowKeywords.insert("else",(void*)0x8);
|
|
Packit Service |
50c9f2 |
flowKeywords.insert("finally",(void*)0x8);
|
|
Packit Service |
50c9f2 |
flowKeywords.insert("for",(void*)0x8);
|
|
Packit Service |
50c9f2 |
flowKeywords.insert("foreach",(void*)0x8);
|
|
Packit Service |
50c9f2 |
flowKeywords.insert("for each",(void*)0x8);
|
|
Packit Service |
50c9f2 |
flowKeywords.insert("goto",(void*)0x8);
|
|
Packit Service |
50c9f2 |
flowKeywords.insert("if",(void*)0x8);
|
|
Packit Service |
50c9f2 |
flowKeywords.insert("return",(void*)0x8);
|
|
Packit Service |
50c9f2 |
flowKeywords.insert("switch",(void*)0x8);
|
|
Packit Service |
50c9f2 |
flowKeywords.insert("throw",(void*)0x8);
|
|
Packit Service |
50c9f2 |
flowKeywords.insert("throws",(void*)0x8);
|
|
Packit Service |
50c9f2 |
flowKeywords.insert("try",(void*)0x8);
|
|
Packit Service |
50c9f2 |
flowKeywords.insert("while",(void*)0x8);
|
|
Packit Service |
50c9f2 |
flowKeywords.insert("@try",(void*)0x8);
|
|
Packit Service |
50c9f2 |
flowKeywords.insert("@catch",(void*)0x8);
|
|
Packit Service |
50c9f2 |
flowKeywords.insert("@finally",(void*)0x8);
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
typeKeywords.insert("bool",(void*)0x8);
|
|
Packit Service |
50c9f2 |
typeKeywords.insert("char",(void*)0x8);
|
|
Packit Service |
50c9f2 |
typeKeywords.insert("double",(void*)0x8);
|
|
Packit Service |
50c9f2 |
typeKeywords.insert("float",(void*)0x8);
|
|
Packit Service |
50c9f2 |
typeKeywords.insert("int",(void*)0x8);
|
|
Packit Service |
50c9f2 |
typeKeywords.insert("long",(void*)0x8);
|
|
Packit Service |
50c9f2 |
typeKeywords.insert("object",(void*)0x8);
|
|
Packit Service |
50c9f2 |
typeKeywords.insert("short",(void*)0x8);
|
|
Packit Service |
50c9f2 |
typeKeywords.insert("signed",(void*)0x8);
|
|
Packit Service |
50c9f2 |
typeKeywords.insert("unsigned",(void*)0x8);
|
|
Packit Service |
50c9f2 |
typeKeywords.insert("void",(void*)0x8);
|
|
Packit Service |
50c9f2 |
typeKeywords.insert("wchar_t",(void*)0x8);
|
|
Packit Service |
50c9f2 |
typeKeywords.insert("size_t",(void*)0x8);
|
|
Packit Service |
50c9f2 |
typeKeywords.insert("boolean",(void*)0x8);
|
|
Packit Service |
50c9f2 |
typeKeywords.insert("id",(void*)0x8);
|
|
Packit Service |
50c9f2 |
typeKeywords.insert("SEL",(void*)0x8);
|
|
Packit Service |
50c9f2 |
typeKeywords.insert("string",(void*)0x8);
|
|
Packit Service |
50c9f2 |
typeKeywords.insert("nullptr",(void*)0x8);
|
|
Packit Service |
50c9f2 |
init=FALSE;
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
if (flowKeywords[keyword]) return "keywordflow";
|
|
Packit Service |
50c9f2 |
if (typeKeywords[keyword]) return "keywordtype";
|
|
Packit Service |
50c9f2 |
return "keyword";
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
static void writeLineNumber(CodeOutputInterface &ol,FileDef *fd,uint line)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
Definition *d = fd ? fd->getSourceDefinition(line) : 0;
|
|
Packit Service |
50c9f2 |
if (d && d->isLinkable())
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
g_currentDefinition=d;
|
|
Packit Service |
50c9f2 |
g_currentLine=line;
|
|
Packit Service |
50c9f2 |
MemberDef *md = fd->getSourceMember(line);
|
|
Packit Service |
50c9f2 |
if (md && md->isLinkable()) // link to member
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
if (g_currentMemberDef!=md) // new member, start search for body
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
g_searchForBody=TRUE;
|
|
Packit Service |
50c9f2 |
g_insideBody=FALSE;
|
|
Packit Service |
50c9f2 |
g_bracketCount=0;
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
g_currentMemberDef=md;
|
|
Packit Service |
50c9f2 |
ol.writeLineNumber(md->getReference(),
|
|
Packit Service |
50c9f2 |
md->getOutputFileBase(),
|
|
Packit Service |
50c9f2 |
md->anchor(),
|
|
Packit Service |
50c9f2 |
line);
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
else // link to compound
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
g_currentMemberDef=0;
|
|
Packit Service |
50c9f2 |
ol.writeLineNumber(d->getReference(),
|
|
Packit Service |
50c9f2 |
d->getOutputFileBase(),
|
|
Packit Service |
50c9f2 |
d->anchor(),
|
|
Packit Service |
50c9f2 |
line);
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
else // no link
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
ol.writeLineNumber(0,0,0,line);
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
// set search page target
|
|
Packit Service |
50c9f2 |
if (Doxygen::searchIndex)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
QCString lineAnchor;
|
|
Packit Service |
50c9f2 |
lineAnchor.sprintf("l%05d",line);
|
|
Packit Service |
50c9f2 |
ol.setCurrentDoc(fd,lineAnchor,TRUE);
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
//printf("writeLineNumber(%d) g_searchForBody=%d\n",line,g_searchForBody);
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
static void codifyLines(CodeOutputInterface &ol,FileDef *fd,const char *text,
|
|
Packit Service |
50c9f2 |
uint &line,uint &column,const char *fontClass=0)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
if (fontClass) ol.startFontClass(fontClass);
|
|
Packit Service |
50c9f2 |
const char *p=text,*sp=p;
|
|
Packit Service |
50c9f2 |
char c;
|
|
Packit Service |
50c9f2 |
bool done=FALSE;
|
|
Packit Service |
50c9f2 |
while (!done)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
sp=p;
|
|
Packit Service |
50c9f2 |
while ((c=*p++) && c!='\n') { column++; }
|
|
Packit Service |
50c9f2 |
if (c=='\n')
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
line++;
|
|
Packit Service |
50c9f2 |
int l = (int)(p-sp-1);
|
|
Packit Service |
50c9f2 |
column=l+1;
|
|
Packit Service |
50c9f2 |
char *tmp = (char*)malloc(l+1);
|
|
Packit Service |
50c9f2 |
memcpy(tmp,sp,l);
|
|
Packit Service |
50c9f2 |
tmp[l]='\0';
|
|
Packit Service |
50c9f2 |
ol.codify(tmp);
|
|
Packit Service |
50c9f2 |
free(tmp);
|
|
Packit Service |
50c9f2 |
if (fontClass) ol.endFontClass();
|
|
Packit Service |
50c9f2 |
ol.endCodeLine();
|
|
Packit Service |
50c9f2 |
ol.startCodeLine(TRUE);
|
|
Packit Service |
50c9f2 |
writeLineNumber(ol,fd,line);
|
|
Packit Service |
50c9f2 |
if (fontClass) ol.startFontClass(fontClass);
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
else
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
ol.codify(sp);
|
|
Packit Service |
50c9f2 |
done=TRUE;
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
if (fontClass) ol.endFontClass();
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
static void writeMultiLineCodeLink(CodeOutputInterface &ol,
|
|
Packit Service |
50c9f2 |
FileDef *fd,uint &line,uint &column,
|
|
Packit Service |
50c9f2 |
Definition *d,
|
|
Packit Service |
50c9f2 |
const char *text)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
static bool sourceTooltips = Config_getBool(SOURCE_TOOLTIPS);
|
|
Packit Service |
50c9f2 |
TooltipManager::instance()->addTooltip(d);
|
|
Packit Service |
50c9f2 |
QCString ref = d->getReference();
|
|
Packit Service |
50c9f2 |
QCString file = d->getOutputFileBase();
|
|
Packit Service |
50c9f2 |
QCString anchor = d->anchor();
|
|
Packit Service |
50c9f2 |
QCString tooltip;
|
|
Packit Service |
50c9f2 |
if (!sourceTooltips) // fall back to simple "title" tooltips
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
tooltip = d->briefDescriptionAsTooltip();
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
bool done=FALSE;
|
|
Packit Service |
50c9f2 |
char *p=(char *)text;
|
|
Packit Service |
50c9f2 |
while (!done)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
char *sp=p;
|
|
Packit Service |
50c9f2 |
char c;
|
|
Packit Service |
50c9f2 |
while ((c=*p++) && c!='\n') { column++; }
|
|
Packit Service |
50c9f2 |
if (c=='\n')
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
line++;
|
|
Packit Service |
50c9f2 |
*(p-1)='\0';
|
|
Packit Service |
50c9f2 |
//printf("writeCodeLink(%s,%s,%s,%s)\n",ref,file,anchor,sp);
|
|
Packit Service |
50c9f2 |
ol.writeCodeLink(ref,file,anchor,sp,tooltip);
|
|
Packit Service |
50c9f2 |
ol.endCodeLine();
|
|
Packit Service |
50c9f2 |
ol.startCodeLine(TRUE);
|
|
Packit Service |
50c9f2 |
writeLineNumber(ol,fd,line);
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
else
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
//printf("writeCodeLink(%s,%s,%s,%s)\n",ref,file,anchor,sp);
|
|
Packit Service |
50c9f2 |
ol.writeCodeLink(ref,file,anchor,sp,tooltip);
|
|
Packit Service |
50c9f2 |
done=TRUE;
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
void ClangParser::linkInclude(CodeOutputInterface &ol,FileDef *fd,
|
|
Packit Service |
50c9f2 |
uint &line,uint &column,const char *text)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
QCString incName = text;
|
|
Packit Service |
50c9f2 |
incName = incName.mid(1,incName.length()-2); // strip ".." or <..>
|
|
Packit Service |
50c9f2 |
FileDef *ifd=0;
|
|
Packit Service |
50c9f2 |
if (!incName.isEmpty())
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
FileName *fn = Doxygen::inputNameDict->find(incName);
|
|
Packit Service |
50c9f2 |
if (fn)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
bool found=false;
|
|
Packit Service |
50c9f2 |
FileNameIterator fni(*fn);
|
|
Packit Service |
50c9f2 |
// for each include name
|
|
Packit Service |
50c9f2 |
for (fni.toFirst();!found && (ifd=fni.current());++fni)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
// see if this source file actually includes the file
|
|
Packit Service |
50c9f2 |
found = fd->isIncluded(ifd->absFilePath());
|
|
Packit Service |
50c9f2 |
//printf(" include file %s found=%d\n",ifd->absFilePath().data(),found);
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
if (ifd)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
ol.writeCodeLink(ifd->getReference(),ifd->getOutputFileBase(),0,text,ifd->briefDescriptionAsTooltip());
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
else
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
codifyLines(ol,ifd,text,line,column,"preprocessor");
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
void ClangParser::linkMacro(CodeOutputInterface &ol,FileDef *fd,
|
|
Packit Service |
50c9f2 |
uint &line,uint &column,const char *text)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
MemberName *mn=Doxygen::functionNameSDict->find(text);
|
|
Packit Service |
50c9f2 |
if (mn)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
MemberNameIterator mni(*mn);
|
|
Packit Service |
50c9f2 |
MemberDef *md;
|
|
Packit Service |
50c9f2 |
for (mni.toFirst();(md=mni.current());++mni)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
if (md->isDefine())
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
writeMultiLineCodeLink(ol,fd,line,column,md,text);
|
|
Packit Service |
50c9f2 |
return;
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
codifyLines(ol,fd,text,line,column);
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
void ClangParser::linkIdentifier(CodeOutputInterface &ol,FileDef *fd,
|
|
Packit Service |
50c9f2 |
uint &line,uint &column,const char *text,int tokenIndex)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
CXCursor c = p->cursors[tokenIndex];
|
|
Packit Service |
50c9f2 |
CXCursor r = clang_getCursorReferenced(c);
|
|
Packit Service |
50c9f2 |
if (!clang_equalCursors(r, c))
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
c=r; // link to referenced location
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
CXCursor t = clang_getSpecializedCursorTemplate(c);
|
|
Packit Service |
50c9f2 |
if (!clang_Cursor_isNull(t) && !clang_equalCursors(t,c))
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
c=t; // link to template
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
CXString usr = clang_getCursorUSR(c);
|
|
Packit Service |
50c9f2 |
const char *usrStr = clang_getCString(usr);
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
Definition *d = usrStr ? Doxygen::clangUsrMap->find(usrStr) : 0;
|
|
Packit Service |
50c9f2 |
//CXCursorKind kind = clang_getCursorKind(c);
|
|
Packit Service |
50c9f2 |
//if (d==0)
|
|
Packit Service |
50c9f2 |
//{
|
|
Packit Service |
50c9f2 |
// printf("didn't find definition for '%s' usr='%s' kind=%d\n",
|
|
Packit Service |
50c9f2 |
// text,usrStr,kind);
|
|
Packit Service |
50c9f2 |
//}
|
|
Packit Service |
50c9f2 |
//else
|
|
Packit Service |
50c9f2 |
//{
|
|
Packit Service |
50c9f2 |
// printf("found definition for '%s' usr='%s' name='%s'\n",
|
|
Packit Service |
50c9f2 |
// text,usrStr,d->name().data());
|
|
Packit Service |
50c9f2 |
//}
|
|
Packit Service |
50c9f2 |
if (d && d->isLinkable())
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
if (g_insideBody &&
|
|
Packit Service |
50c9f2 |
g_currentMemberDef && d->definitionType()==Definition::TypeMember &&
|
|
Packit Service |
50c9f2 |
(g_currentMemberDef!=d || g_currentLine
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
addDocCrossReference(g_currentMemberDef,(MemberDef*)d);
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
writeMultiLineCodeLink(ol,fd,line,column,d,text);
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
else
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
codifyLines(ol,fd,text,line,column);
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
clang_disposeString(usr);
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
static void detectFunctionBody(const char *s)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
//printf("punct=%s g_searchForBody=%d g_insideBody=%d g_bracketCount=%d\n",
|
|
Packit Service |
50c9f2 |
// s,g_searchForBody,g_insideBody,g_bracketCount);
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
if (g_searchForBody && (qstrcmp(s,":")==0 || qstrcmp(s,"{")==0)) // start of 'body' (: is for constructor)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
g_searchForBody=FALSE;
|
|
Packit Service |
50c9f2 |
g_insideBody=TRUE;
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
else if (g_searchForBody && qstrcmp(s,";")==0) // declaration only
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
g_searchForBody=FALSE;
|
|
Packit Service |
50c9f2 |
g_insideBody=FALSE;
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
if (g_insideBody && qstrcmp(s,"{")==0) // increase scoping level
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
g_bracketCount++;
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
if (g_insideBody && qstrcmp(s,"}")==0) // decrease scoping level
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
g_bracketCount--;
|
|
Packit Service |
50c9f2 |
if (g_bracketCount<=0) // got outside of function body
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
g_insideBody=FALSE;
|
|
Packit Service |
50c9f2 |
g_bracketCount=0;
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
void ClangParser::writeSources(CodeOutputInterface &ol,FileDef *fd)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
TooltipManager::instance()->clearTooltips();
|
|
Packit Service |
50c9f2 |
// (re)set global parser state
|
|
Packit Service |
50c9f2 |
g_currentDefinition=0;
|
|
Packit Service |
50c9f2 |
g_currentMemberDef=0;
|
|
Packit Service |
50c9f2 |
g_currentLine=0;
|
|
Packit Service |
50c9f2 |
g_searchForBody=FALSE;
|
|
Packit Service |
50c9f2 |
g_insideBody=FALSE;
|
|
Packit Service |
50c9f2 |
g_bracketCount=0;
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
unsigned int line=1,column=1;
|
|
Packit Service |
50c9f2 |
QCString lineNumber,lineAnchor;
|
|
Packit Service |
50c9f2 |
ol.startCodeLine(TRUE);
|
|
Packit Service |
50c9f2 |
writeLineNumber(ol,fd,line);
|
|
Packit Service |
50c9f2 |
for (unsigned int i=0;i<p->numTokens;i++)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
CXSourceLocation start = clang_getTokenLocation(p->tu, p->tokens[i]);
|
|
Packit Service |
50c9f2 |
unsigned int l, c;
|
|
Packit Service |
50c9f2 |
clang_getSpellingLocation(start, 0, &l, &c, 0);
|
|
Packit Service |
50c9f2 |
if (l > line) column = 1;
|
|
Packit Service |
50c9f2 |
while (line
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
line++;
|
|
Packit Service |
50c9f2 |
ol.endCodeLine();
|
|
Packit Service |
50c9f2 |
ol.startCodeLine(TRUE);
|
|
Packit Service |
50c9f2 |
writeLineNumber(ol,fd,line);
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
while (column
|
|
Packit Service |
50c9f2 |
CXString tokenString = clang_getTokenSpelling(p->tu, p->tokens[i]);
|
|
Packit Service |
50c9f2 |
char const *s = clang_getCString(tokenString);
|
|
Packit Service |
50c9f2 |
CXCursorKind cursorKind = clang_getCursorKind(p->cursors[i]);
|
|
Packit Service |
50c9f2 |
CXTokenKind tokenKind = clang_getTokenKind(p->tokens[i]);
|
|
Packit Service |
50c9f2 |
//printf("%d:%d %s cursorKind=%d tokenKind=%d\n",line,column,s,cursorKind,tokenKind);
|
|
Packit Service |
50c9f2 |
switch (tokenKind)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
case CXToken_Keyword:
|
|
Packit Service |
50c9f2 |
if (strcmp(s,"operator")==0)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
linkIdentifier(ol,fd,line,column,s,i);
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
else
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
codifyLines(ol,fd,s,line,column,
|
|
Packit Service |
50c9f2 |
cursorKind==CXCursor_PreprocessingDirective ? "preprocessor" :
|
|
Packit Service |
50c9f2 |
keywordToType(s));
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
break;
|
|
Packit Service |
50c9f2 |
case CXToken_Literal:
|
|
Packit Service |
50c9f2 |
if (cursorKind==CXCursor_InclusionDirective)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
linkInclude(ol,fd,line,column,s);
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
else if (s[0]=='"' || s[0]=='\'')
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
codifyLines(ol,fd,s,line,column,"stringliteral");
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
else
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
codifyLines(ol,fd,s,line,column);
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
break;
|
|
Packit Service |
50c9f2 |
case CXToken_Comment:
|
|
Packit Service |
50c9f2 |
codifyLines(ol,fd,s,line,column,"comment");
|
|
Packit Service |
50c9f2 |
break;
|
|
Packit Service |
50c9f2 |
default: // CXToken_Punctuation or CXToken_Identifier
|
|
Packit Service |
50c9f2 |
if (tokenKind==CXToken_Punctuation)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
detectFunctionBody(s);
|
|
Packit Service |
50c9f2 |
//printf("punct %s: %d\n",s,cursorKind);
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
switch (cursorKind)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
case CXCursor_PreprocessingDirective:
|
|
Packit Service |
50c9f2 |
codifyLines(ol,fd,s,line,column,"preprocessor");
|
|
Packit Service |
50c9f2 |
break;
|
|
Packit Service |
50c9f2 |
case CXCursor_MacroDefinition:
|
|
Packit Service |
50c9f2 |
codifyLines(ol,fd,s,line,column,"preprocessor");
|
|
Packit Service |
50c9f2 |
break;
|
|
Packit Service |
50c9f2 |
case CXCursor_InclusionDirective:
|
|
Packit Service |
50c9f2 |
linkInclude(ol,fd,line,column,s);
|
|
Packit Service |
50c9f2 |
break;
|
|
Packit Service |
50c9f2 |
case CXCursor_MacroExpansion:
|
|
Packit Service |
50c9f2 |
linkMacro(ol,fd,line,column,s);
|
|
Packit Service |
50c9f2 |
break;
|
|
Packit Service |
50c9f2 |
default:
|
|
Packit Service |
50c9f2 |
if (tokenKind==CXToken_Identifier ||
|
|
Packit Service |
50c9f2 |
(tokenKind==CXToken_Punctuation && // for operators
|
|
Packit Service |
50c9f2 |
(cursorKind==CXCursor_DeclRefExpr ||
|
|
Packit Service |
50c9f2 |
cursorKind==CXCursor_MemberRefExpr ||
|
|
Packit Service |
50c9f2 |
cursorKind==CXCursor_CallExpr ||
|
|
Packit Service |
50c9f2 |
cursorKind==CXCursor_ObjCMessageExpr)
|
|
Packit Service |
50c9f2 |
)
|
|
Packit Service |
50c9f2 |
)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
linkIdentifier(ol,fd,line,column,s,i);
|
|
Packit Service |
50c9f2 |
if (Doxygen::searchIndex)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
ol.addWord(s,FALSE);
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
else
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
codifyLines(ol,fd,s,line,column);
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
break;
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
clang_disposeString(tokenString);
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
ol.endCodeLine();
|
|
Packit Service |
50c9f2 |
TooltipManager::instance()->writeTooltips(ol);
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
ClangParser::ClangParser()
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
p = new Private;
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
ClangParser::~ClangParser()
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
delete p;
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
//--------------------------------------------------------------------------
|
|
Packit Service |
50c9f2 |
#else // use stubbed functionality in case libclang support is disabled.
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
void ClangParser::start(const char *,QStrList &)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
void ClangParser::switchToFile(const char *)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
void ClangParser::finish()
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
QCString ClangParser::lookup(uint,const char *)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
return "";
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
void ClangParser::writeSources(CodeOutputInterface &,FileDef *)
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
ClangParser::ClangParser()
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
ClangParser::~ClangParser()
|
|
Packit Service |
50c9f2 |
{
|
|
Packit Service |
50c9f2 |
}
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
|
|
Packit Service |
50c9f2 |
#endif
|
|
Packit Service |
50c9f2 |
//--------------------------------------------------------------------------
|
|
Packit Service |
50c9f2 |
|