1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 03:28:20 +02:00
Beef/IDEHelper/Clang/ClangHelper.cpp
2019-08-23 11:56:54 -07:00

1739 lines
No EOL
50 KiB
C++

#ifdef IDE_C_SUPPORT
#include "ClangHelper.h"
USING_NS_BF;
ClangUnsavedFiles* Beefy::gClangUnsavedFiles = NULL;
static PerfManager gClangPerfManager;
static String ToString(CXString str)
{
String result;
const char* cStr = clang_getCString(str);
if (cStr != NULL)
result = cStr;
clang_disposeString(str);
return result;
}
static void ToString(CXString str, String& outStr)
{
const char* cStr = clang_getCString(str);
if (cStr != NULL)
outStr = cStr;
else
outStr.clear();
clang_disposeString(str);
}
class ClangClassifier
{
public:
BfSourceClassifier::CharData* mCharData;
uint8 mClassifierPassId;
public:
void ModifyFlags(int startPos, int endPos, uint8 andFlags, uint8 orFlags)
{
for (int i = startPos; i < endPos; i++)
{
mCharData[i].mDisplayPassId = mClassifierPassId;
mCharData[i].mDisplayFlags = (mCharData[i].mDisplayFlags & andFlags) | orFlags;
}
}
void SetElementType(int startPos, int endPos, BfSourceElementType elementType)
{
for (int i = startPos; i < endPos; i++)
{
mCharData[i].mDisplayPassId = mClassifierPassId;
mCharData[i].mDisplayTypeId = (uint8) elementType;
}
}
};
static CXFile GetLocation(CXSourceLocation loc, int* idx)
{
CXFile file;
uint32 line;
uint32 column;
clang_getFileLocation(loc, &file, &line, &column, (uint32*)idx);
return file;
}
static CXFile GetRange(CXSourceRange range, int* startIdx, int* endIdx)
{
auto loc = clang_getRangeStart(range);
CXFile file;
uint32 line;
uint32 column;
clang_getFileLocation(loc, &file, &line, &column, (uint32*) startIdx);
loc = clang_getRangeEnd(range);
clang_getFileLocation(loc, &file, &line, &column, (uint32*) endIdx);
return file;
}
static CXFile GetCursorRange(CXCursor cursor, int* startIdx, int* endIdx)
{
auto range = clang_getCursorExtent(cursor);
return GetRange(range, startIdx, endIdx);
}
class ClangFindCall
{
public:
int mCursorIndex;
std::vector<int> mParamStarts;
CXCursor mCallDef;
bool mFoundCall;
int mSelectIdx;
int mLeftParenIdx;
public:
ClangFindCall()
{
mFoundCall = false;
mSelectIdx = 0;
mLeftParenIdx = -1;
}
CXChildVisitResult Visit(CXCursor cursor, CXCursor parent)
{
String name = ToString(clang_getCursorSpelling(cursor));
int rangeStart;
int rangeEnd;
GetCursorRange(cursor, &rangeStart, &rangeEnd);
if ((mCursorIndex >= rangeStart) && (mCursorIndex < rangeEnd))
{
if (clang_getCursorKind(cursor) == CXCursor_CallExpr)
{
String methodName = ToString(clang_getCursorSpelling(cursor));
int numArgs = clang_Cursor_getNumArguments(cursor);
if ((methodName.length() == 0) || (mCursorIndex < rangeStart + (int)methodName.length()) /*|| (numArgs < 0)*/)
{
// Cursor is just on method name
return CXChildVisit_Recurse;
}
mLeftParenIdx = rangeStart + (int)methodName.length();
mSelectIdx = 0;
mParamStarts.clear();
mFoundCall = true;
for (int argIdx = 0; argIdx < numArgs; argIdx++)
{
CXCursor argCursor = clang_Cursor_getArgument(cursor, argIdx);
String argStr = ToString(clang_getCursorSpelling(argCursor));
int argStartIdx;
int argEndIdx;
GetCursorRange(argCursor, &argStartIdx, &argEndIdx);
if ((mCursorIndex >= argStartIdx) && (mCursorIndex < argEndIdx))
mSelectIdx = argIdx;
if (argIdx == 0)
mParamStarts.push_back(argStartIdx);
//if (argIdx == numArgs - 1)
mParamStarts.push_back(argEndIdx + 1);
}
if (numArgs == 0)
{
mParamStarts.push_back(rangeStart + (int)methodName.length());
}
//
mCallDef = clang_getCursorReferenced(cursor);
if (clang_getCursorKind(mCallDef) == CXCursor_FirstInvalid)
mFoundCall = false;
}
}
return CXChildVisit_Recurse;
}
static CXChildVisitResult VisitProc(CXCursor cursor, CXCursor parent, CXClientData clientData)
{
return ((ClangFindCall*) clientData)->Visit(cursor, parent);
}
};
class ClangFindFiles
{
public:
std::set<String> mFileSet;
CXFile mPrevFile;
public:
ClangFindFiles()
{
mPrevFile = NULL;
}
void ProcessModule(CXTranslationUnit transUnit, CXModule module)
{
int numHeaders = clang_Module_getNumTopLevelHeaders(transUnit, module);
for (int headerIdx = 0; headerIdx < numHeaders; headerIdx++)
{
CXFile headerFile = clang_Module_getTopLevelHeader(transUnit, module, headerIdx);
String fileName = ToString(clang_getFileName(headerFile));
if (mFileSet.insert(fileName).second)
{
CXModule headerModule = clang_getModuleForFile(transUnit, headerFile);
ProcessModule(transUnit, headerModule);
}
}
}
CXChildVisitResult Visit(CXCursor cursor, CXCursor parent)
{
CXFile includedFile = clang_getIncludedFile(cursor);
if (includedFile != NULL)
{
String fileName = ToString(clang_getFileName(includedFile));
mFileSet.insert(fileName);
}
auto range = clang_getCursorExtent(cursor);
CXFile file;
uint32 line;
uint32 column;
uint32 offset;
clang_getFileLocation(clang_getRangeStart(range), &file, &line, &column, &offset);
if (!clang_File_isEqual(file, mPrevFile))
{
String fileName = ToString(clang_getFileName(file));
mFileSet.insert(fileName);
}
return CXChildVisit_Recurse;
}
static CXChildVisitResult VisitProc(CXCursor cursor, CXCursor parent, CXClientData clientData)
{
return ((ClangFindFiles*)clientData)->Visit(cursor, parent);
}
void InclusionProc(CXFile includedFile, CXSourceLocation* inclusionStack, unsigned includeLen)
{
String fileName = ToString(clang_getFileName(includedFile));
String outData = "### " + fileName + " : \n";
for (int i = 0; i < (int)includeLen; i++)
{
for (int j = -5; j < i; j++)
outData += " ";
CXFile file;
uint32 line;
uint32 column;
uint32 offset;
clang_getFileLocation(inclusionStack[i], &file, &line, &column, &offset);
CXString str = clang_getFileName(file);
outData += ToString(str) + "\n";
}
OutputDebugStr(outData);
mFileSet.insert(fileName);
}
static void InclusionProc(CXFile includedFile, CXSourceLocation* inclusionStack, unsigned includeLen, CXClientData clientData)
{
return ((ClangFindFiles*)clientData)->InclusionProc(includedFile, inclusionStack, includeLen);
}
};
class ClangFindNavigationData
{
public:
CXFile mWantFile;
bool mAllowEmptyMethods;
bool mAddLocation;
public:
ClangFindNavigationData()
{
mAddLocation = true;
}
CXChildVisitResult Visit(CXCursor cursor, CXCursor parent)
{
String& outString = *gTLStrReturn.Get();
CXSourceLocation sourceLoc = clang_getCursorLocation(cursor);
CXFile file;
uint32 line;
uint32 column;
uint32 offset;
clang_getFileLocation(sourceLoc, &file, &line, &column, &offset);
if ((mWantFile != NULL) && (file != mWantFile))
return CXChildVisit_Continue;
//if (!clang_Location_isFromMainFile(sourceLoc))
//return CXChildVisit_Recurse;
auto cursorKind = clang_getCursorKind(cursor);
if (cursorKind == CXCursor_MacroExpansion)
return CXChildVisit_Continue;
String defString;
if ((cursorKind == CXCursor_FunctionDecl) || (cursorKind == CXCursor_CXXMethod) || (cursorKind == CXCursor_Constructor) ||
(cursorKind == CXCursor_Destructor) || (cursorKind == CXCursor_ConversionFunction) || (cursorKind == CXCursor_FunctionTemplate))
{
if (!mAllowEmptyMethods)
{
bool hasBody = false;
clang_visitChildren(cursor, HasBodyVisitProc, &hasBody);
if (!hasBody)
return CXChildVisit_Continue;
}
String methodDefString;
methodDefString += ToString(clang_getCursorSpelling(cursor));
CXType methodType = clang_getCursorType(cursor);
methodDefString += "(";
int numArgs = clang_Cursor_getNumArguments(cursor);
for (int argIdx = 0; argIdx < numArgs; argIdx++)
{
if (argIdx > 0)
methodDefString += ", ";
CXCursor argCursor = clang_Cursor_getArgument(cursor, argIdx);
CXType paramType = clang_getCursorType(argCursor);
methodDefString += ToString(clang_getTypeSpelling(paramType));
String argName = ToString(clang_getCursorSpelling(argCursor));
if (!argName.empty())
{
char lastC = methodDefString[methodDefString.length() - 1];
if ((lastC != '*') && (lastC != ' '))
methodDefString += " ";
methodDefString += argName;
}
}
if (clang_isFunctionTypeVariadic(methodType))
{
if (numArgs > 0)
methodDefString += ", ";
methodDefString += "...";
}
methodDefString += ")";
auto checkParent = cursor;
while (true)
{
checkParent = clang_getCursorSemanticParent(checkParent);
auto checkParentKind = clang_getCursorKind(checkParent);
if ((checkParentKind == CXCursor_ClassDecl) || (checkParentKind == CXCursor_StructDecl) ||
(checkParentKind == CXCursor_Namespace) || (checkParentKind == CXCursor_UnionDecl))
{
methodDefString = ToString(clang_getCursorSpelling(checkParent)) + "::" + methodDefString;
}
// Don't fully qualify - just get the immediately preceding class name or namespace name
break;
}
defString = methodDefString;
}
if (!defString.empty())
{
/*auto range = clang_getCursorExtent(cursor);
CXFile file;
uint32 line;
uint32 column;
uint32 offset;
clang_getFileLocation(clang_getRangeStart(range), &file, &line, &column, &offset);*/
if (mAddLocation)
outString += StrFormat("%s\t%d\t%d\n", defString.c_str(), line - 1, column - 1);
else
outString = defString;
return CXChildVisit_Continue;
}
return CXChildVisit_Recurse;
}
static CXChildVisitResult HasBodyVisitProc(CXCursor cursor, CXCursor parent, CXClientData clientData)
{
bool* hasBodyPtr = (bool*)clientData;
auto cursorKind = clang_getCursorKind(cursor);
if (cursorKind == CXCursor_CompoundStmt)
{
*hasBodyPtr = true;
return CXChildVisit_Break;
}
return CXChildVisit_Recurse;
//return ((ClangFindNavigationData*)clientData)->Visit(cursor, parent);
}
static CXChildVisitResult VisitProc(CXCursor cursor, CXCursor parent, CXClientData clientData)
{
return ((ClangFindNavigationData*)clientData)->Visit(cursor, parent);
}
};
class ClangBreakCompound
{
public:
CXCursor mDefCursor;
public:
ClangBreakCompound()
{
mDefCursor = clang_getNullCursor();
}
CXChildVisitResult Visit(CXCursor cursor, CXCursor parent)
{
mDefCursor = cursor;
return CXChildVisit_Break;
}
static CXChildVisitResult HasBodyVisitProc(CXCursor cursor, CXCursor parent, CXClientData clientData)
{
bool* hasBodyPtr = (bool*)clientData;
auto cursorKind = clang_getCursorKind(cursor);
if (cursorKind == CXCursor_CompoundStmt)
{
*hasBodyPtr = true;
return CXChildVisit_Break;
}
return CXChildVisit_Recurse;
//return ((ClangFindNavigationData*)clientData)->Visit(cursor, parent);
}
static CXChildVisitResult VisitProc(CXCursor cursor, CXCursor parent, CXClientData clientData)
{
return ((ClangBreakCompound*)clientData)->Visit(cursor, parent);
}
};
/*typedef std::set<BfAutoComplete::Entry, BfAutoComplete::EntryLess> AutocompleteMap;
static bool AddEntry(AutocompleteMap& entries, const BfAutoComplete::Entry& entry, const StringImpl& filter)
{
if (filter.length() != 0)
{
if (strnicmp(filter.c_str(), entry.mDisplay, filter.length()) != 0)
return false;
}
entries.insert(entry);
return true;
}*/
static bool IsIdentifierChar(char c)
{
return (((c >= '0') && (c <= '9')) ||
((c >= 'A') && (c <= 'Z')) ||
((c >= 'a') && (c <= 'z')) ||
((c == '_')));
}
struct ThreadProtect
{
ClangHelper* mClangHelper;
ThreadProtect(ClangHelper* clangHelper)
{
mClangHelper = clangHelper;
if (clangHelper->mEntryCount == 0)
{
mClangHelper->mCurThreadId = GetCurrentThreadId();
}
else
{
BF_ASSERT(mClangHelper->mCurThreadId == GetCurrentThreadId());
}
BF_ASSERT(mClangHelper->mCurThreadId);
clangHelper->mEntryCount++;
}
~ThreadProtect()
{
BF_ASSERT(mClangHelper->mCurThreadId == GetCurrentThreadId());
mClangHelper->mEntryCount--;
if (mClangHelper->mEntryCount == 0)
{
mClangHelper->mCurThreadId = 0;
}
}
};
ClangHelper::ClangHelper()
{
//TODO: Leave on?
clang_toggleCrashRecovery(0);
mIndex = clang_createIndex(1, 1);
BfLogClang("%d Clang clang_createIndex %p\n", ::GetCurrentThreadId(), mIndex);
mIsForResolve = false;
mCurThreadId = 0;
mEntryCount = 0;
}
ClangHelper::~ClangHelper()
{
BfLogClang("%d Clang clang_disposeIndex %p\n", ::GetCurrentThreadId, mIndex);
clang_disposeIndex(mIndex);
for (auto& mapPair : mTranslationUnits)
{
ClangTranslationUnit* transUnit = &mapPair.second;
delete transUnit->mChars;
BfLogClang("%d Clang clang_disposeTranslationUnit %p\n", ::GetCurrentThreadId(), transUnit->mTransUnit);
clang_disposeTranslationUnit(transUnit->mTransUnit);
}
}
ClangTranslationUnit* ClangHelper::GetTranslationUnit(const StringImpl& fileName, const char* headerPrefix, BfSourceClassifier::CharData* charData, int charLen, const char** args, int argCount, bool forceReparse, bool noTransUnit, bool noReparse)
{
ThreadProtect threadProtect(this);
BfLogClang("%d Clang GetTranslationUnit %s\n", ::GetCurrentThreadId(), fileName.c_str());
uint32 options;
if (mIsForResolve)
{
options = CXTranslationUnit_DetailedPreprocessingRecord |
CXTranslationUnit_IncludeBriefCommentsInCodeCompletion |
CXTranslationUnit_Incomplete |
CXTranslationUnit_PrecompiledPreamble |
CXTranslationUnit_CacheCompletionResults;
}
else
{
options = CXTranslationUnit_SkipFunctionBodies |
CXTranslationUnit_PrecompiledPreamble |
CXTranslationUnit_Incomplete;
}
//options = clang_defaultEditingTranslationUnitOptions();
std::map<String, ClangTranslationUnit, StrPathCompare> transMap;
auto itr2 = transMap.find(fileName);
auto itr = mTranslationUnits.find(fileName);
if (itr == mTranslationUnits.end())
{
if (fileName[fileName.length() - 1] == 'h')
{
BF_ASSERT(headerPrefix != NULL);
}
const char* defaultArgs [] = { "-cc1", "-x", "c++", "-std=c++11", "-stdlib=libc++", "-target-cpu", "x86-64", "-target-feature", "+sse2",
"-triple", "x86_64-pc-windows-gnu", "-D__x86_64__", "-D__x86_64", "-MD"};
if (args == NULL)
{
args = defaultArgs;
argCount = BF_ARRAY_COUNT(defaultArgs);
}
ClangTranslationUnit transUnit;
bool isHeader = headerPrefix != NULL;
if (headerPrefix != NULL)
{
transUnit.mHeaderPrefix = _strdup(headerPrefix);
transUnit.mHeaderPrefixLen = (int)strlen(headerPrefix);
transUnit.mCharLen = transUnit.mHeaderPrefixLen + 1;
transUnit.mChars = new char[transUnit.mCharLen + 1];
transUnit.mChars[transUnit.mHeaderPrefixLen] = 0;
for (int i = 0; i < transUnit.mHeaderPrefixLen; i++)
transUnit.mChars[i] = headerPrefix[i];
charLen = transUnit.mHeaderPrefixLen;
}
if (charData != NULL)
{
BF_ASSERT(transUnit.mHeaderPrefixLen == 0);
transUnit.mCharLen = charLen + 256;
transUnit.mChars = new char[transUnit.mCharLen + 1];
transUnit.mChars[charLen] = 0;
for (int i = 0; i < charLen; i++)
transUnit.mChars[i] = charData[i].mChar;
}
auto mapPair = mTranslationUnits.insert(ClangTranslationUnitMap::value_type(fileName, transUnit)).first;
ClangTranslationUnit* transUnitP = &mapPair->second;
if (isHeader)
transUnitP->mTransFileName = fileName + ".cpp";
else
transUnitP->mTransFileName = fileName;
transUnitP->mRequestedFileName = fileName;
if (transUnit.mChars != NULL)
{
AutoCrit autoCrit(gClangUnsavedFiles->mCritSect);
CXUnsavedFile unsavedFile;
unsavedFile.Contents = transUnit.mChars;
unsavedFile.Length = charLen;
unsavedFile.Filename = transUnitP->mTransFileName.c_str();
BF_ASSERT(unsavedFile.Contents[unsavedFile.Length] == 0);
gClangUnsavedFiles->mUnsavedFiles.push_back(unsavedFile);
}
if (!noTransUnit)
{
transUnitP->mTransUnit = clang_parseTranslationUnit(mIndex, transUnitP->mTransFileName.c_str(), args, argCount, VecFirstRef(gClangUnsavedFiles->mUnsavedFiles), (int)gClangUnsavedFiles->mUnsavedFiles.size(), options);
BfLogClang("%d Clang clang_parseTranslationUnit %p\n", ::GetCurrentThreadId(), transUnitP->mTransUnit);
}
BfLogClang("%d Clang GetTranslationUnit Done(1) %s\n", ::GetCurrentThreadId(), fileName.c_str());
return transUnitP;
}
else
{
ClangTranslationUnit* transUnit = &itr->second;
if (charData != NULL)
{
AutoCrit autoCrit(gClangUnsavedFiles->mCritSect);
int headerPrefixLen = transUnit->mHeaderPrefixLen;
int contentCharLen = charLen + headerPrefixLen;
CXUnsavedFile* unsavedFilePtr = NULL;
unsigned long* lenPtr = NULL;
if (contentCharLen > transUnit->mCharLen)
{
char* prevChars = transUnit->mChars;
transUnit->mCharLen = contentCharLen + 256;
transUnit->mChars = new char[transUnit->mCharLen + 1];
if (headerPrefixLen > 0)
memcpy(transUnit->mChars, transUnit->mHeaderPrefix, headerPrefixLen);
if (prevChars == NULL)
{
CXUnsavedFile unsavedFile;
unsavedFile.Contents = transUnit->mChars;
unsavedFile.Length = contentCharLen;
//unsavedFile.Filename = itr->first.c_str();
unsavedFile.Filename = transUnit->mTransFileName.c_str();
gClangUnsavedFiles->mUnsavedFiles.push_back(unsavedFile);
lenPtr = &gClangUnsavedFiles->mUnsavedFiles.back().Length;
unsavedFilePtr = &gClangUnsavedFiles->mUnsavedFiles.back();
}
else
{
// Remap the new buffer in the mUnsavedFiles
bool found = false;
for (int i = 0; i < (int)gClangUnsavedFiles->mUnsavedFiles.size(); i++)
{
if (gClangUnsavedFiles->mUnsavedFiles[i].Contents == prevChars)
{
auto& unsavedFile = gClangUnsavedFiles->mUnsavedFiles[i];
unsavedFile.Contents = transUnit->mChars;
unsavedFile.Length = contentCharLen;
unsavedFilePtr = &unsavedFile;
lenPtr = &unsavedFile.Length;
found = true;
}
}
BF_ASSERT(found);
delete prevChars;
}
}
else
{
// Set the size again
bool found = false;
for (int i = 0; i < (int)gClangUnsavedFiles->mUnsavedFiles.size(); i++)
{
if (gClangUnsavedFiles->mUnsavedFiles[i].Contents == transUnit->mChars)
{
gClangUnsavedFiles->mUnsavedFiles[i].Length = contentCharLen;
lenPtr = &gClangUnsavedFiles->mUnsavedFiles[i].Length;
found = true;
}
}
BF_ASSERT(found);
}
transUnit->mChars[contentCharLen] = 0;
for (int i = 0; i < charLen; i++)
{
char c = charData[i].mChar;
if (c == 0)
{
*lenPtr = i;
break;
}
transUnit->mChars[headerPrefixLen + i] = c;
}
if (unsavedFilePtr != NULL)
{
BF_ASSERT(unsavedFilePtr->Contents[unsavedFilePtr->Length] == 0);
}
}
if (((charData != NULL) || (forceReparse)) && (transUnit->mTransUnit != NULL))
{
if (!noReparse)
{
BfLogClang("%d Clang clang_reparseTranslationUnit %p\n", GetCurrentThreadId(), transUnit->mTransUnit);
auto reparseOption = clang_defaultReparseOptions(transUnit->mTransUnit);
//reparseOption = CXReparse_None
clang_reparseTranslationUnit(transUnit->mTransUnit, (int)gClangUnsavedFiles->mUnsavedFiles.size(), VecFirstRef(gClangUnsavedFiles->mUnsavedFiles), reparseOption);
BfLogClang("%d Clang clang_reparseTranslationUnit Done %p\n", GetCurrentThreadId(), transUnit->mTransUnit);
}
}
BfLogClang("%d Clang GetTranslationUnit Done(2) %s\n", ::GetCurrentThreadId(), fileName.c_str());
return transUnit;
}
}
void ClangHelper::AddTranslationUnit(const StringImpl& fileName, const char* headerPrefix, const StringImpl& clangArgs, BfSourceClassifier::CharData* charData, int charLen)
{
// Remove any old temporary version
//TODO: Put back
//RemoveTranslationUnit(fileName);
ThreadProtect threadProtect(this);
String argsMem = clangArgs;
argsMem += "\n-c\n-x\nc++\n";
// Add Clang include path
char exePath[MAX_PATH];
GetModuleFileNameA(NULL, exePath, MAX_PATH);
char* pathPtr = exePath + strlen(exePath) - 1;
while ((*pathPtr != '\\') && (*pathPtr != '/'))
pathPtr--;
*pathPtr = 0;
argsMem += "-I";
argsMem += exePath;
argsMem += "/llvm/lib/clang/3.5.0/include";
std::vector<const char*> args;
bool isStrStart = true;
for (int i = 0; i < (int)argsMem.length(); i++)
{
if (argsMem[i] == '\n')
{
argsMem[i] = 0;
isStrStart = true;
}
else if (isStrStart)
{
args.push_back(argsMem.c_str() + i);
isStrStart = false;
}
}
auto transUnit = GetTranslationUnit(fileName, headerPrefix, charData, charLen, VecFirstRef(args), (int)args.size());
transUnit->mRefCount++;
BfLogClang("%d Clang clang_AddTranslationUnit %s %p refCount:%d\n", ::GetCurrentThreadId(), fileName.c_str(), transUnit->mTransUnit, transUnit->mRefCount);
}
typedef std::map<String, ClangTranslationUnit, StrPathCompare> ClangTranslationUnitMap2;
void ClangHelper::RemoveTranslationUnit(const StringImpl& fileName)
{
//ClangTranslationUnitMap2 map;
//map.find(fileName);
ThreadProtect threadProtect(this);
auto itr = mTranslationUnits.find(fileName);
if (itr == mTranslationUnits.end())
return;
ClangTranslationUnit* transUnit = &itr->second;
transUnit->mRefCount--;
BfLogClang("%d Clang RemoveTranslationUnit %p refCount:%d\n", ::GetCurrentThreadId(), transUnit->mTransUnit, transUnit->mRefCount);
if (transUnit->mRefCount > 0)
return;
if (transUnit->mChars != NULL)
{
bool found = false;
for (int i = 0; i < (int)gClangUnsavedFiles->mUnsavedFiles.size(); i++)
{
if (gClangUnsavedFiles->mUnsavedFiles[i].Contents == transUnit->mChars)
{
found = true;
gClangUnsavedFiles->mUnsavedFiles.erase(gClangUnsavedFiles->mUnsavedFiles.begin() + i);
i--;
}
}
BF_ASSERT(found);
delete transUnit->mChars;
delete transUnit->mHeaderPrefix;
}
if (transUnit->mTransUnit != NULL)
{
BfLogClang("%d Clang clang_disposeTranslationUnit %p\n", ::GetCurrentThreadId(), transUnit->mTransUnit);
clang_disposeTranslationUnit(transUnit->mTransUnit);
}
mTranslationUnits.erase(itr);
}
const char* ClangHelper::DetermineFilesReferenced(const StringImpl& fileName)
{
BfLogClang("%d Clang DetermineFilesReferenced %s\n", ::GetCurrentThreadId(), fileName.c_str());
gClangPerfManager.ZoneStart("Clang DetermineFilesReferenced");
ClangTranslationUnit* clangTransUnit = GetTranslationUnit(fileName, NULL, 0);
CXTranslationUnit transUnit = clangTransUnit->mTransUnit;
if (transUnit == NULL)
return NULL;
CXFile file = clang_getFile(transUnit, fileName.c_str());
mReturnString.clear();
ClangFindFiles clangFindFiles;
gClangPerfManager.ZoneStart("clang_getInclusions");
clang_getInclusions(transUnit, ClangFindFiles::InclusionProc, &clangFindFiles);
gClangPerfManager.ZoneEnd();
for (auto& fileName : clangFindFiles.mFileSet)
{
String fixedFileName = fileName;
for (int i = 0; i < (int)fixedFileName.length(); i++)
{
if (fixedFileName[i] == DIR_SEP_CHAR_ALT)
fixedFileName[i] = DIR_SEP_CHAR;
}
mReturnString += fixedFileName + "\n";
}
gClangPerfManager.ZoneEnd();
if (gClangPerfManager.mRecording)
{
gClangPerfManager.StopRecording();
gClangPerfManager.DbgPrint();
}
return mReturnString.c_str();
}
void ClangHelper::CheckErrorRange(const StringImpl& errorString, int startIdx, int endIdx, int errorLookupTextIdx)
{
if (errorLookupTextIdx == -1)
return;
if (mReturnString.length() != 0)
return;
if ((errorLookupTextIdx >= startIdx) && (errorLookupTextIdx < endIdx))
mReturnString += StrFormat("diag\t%d\t%d\t%s\n", startIdx, endIdx, errorString.c_str());
}
const char* ClangHelper::Classify(const StringImpl& fileName, BfSourceClassifier::CharData* charData, int charLen, int cursorIdx, int errorLookupTextIdx, bool ignoreErrors)
{
ThreadProtect threadProtect(this);
gClangPerfManager.StartRecording();
//OutputDebugStrF("Classify: %s\n", fileName.c_str());
BfLogClang("%d Clang Classify %s\n", ::GetCurrentThreadId(), fileName.c_str());
//Sleep(500);
ClangClassifier clangClassifier;
clangClassifier.mClassifierPassId = 2; //SourceDisplayId_FullClassify;
clangClassifier.mCharData = charData;
//clangClassifier.SetElementType(0, charLen, BfSourceElementType_Normal);
clangClassifier.ModifyFlags(0, charLen, 0, 0);
gClangPerfManager.ZoneStart("GetTranslationUnit");
//ClangTranslationUnit* clangTransUnit = GetTranslationUnit(fileName, charData, charLen);
ClangTranslationUnit* clangTransUnit = NULL;
/*if (headerFileName != NULL)
{
GetTranslationUnit(headerFileName, NULL, charData, charLen, NULL, 0, false, true);
clangTransUnit = GetTranslationUnit(fileName, NULL, 0, NULL, 0, true);
}
else*/
{
clangTransUnit = GetTranslationUnit(fileName, NULL, charData, charLen, NULL, 0, false);
}
int adjustedCursorIdx = cursorIdx + clangTransUnit->mHeaderPrefixLen;
const char* cursorFileName = clangTransUnit->mTransFileName.c_str(); //(headerFileName != NULL) ? headerFileName : fileName.c_str();
CXTranslationUnit transUnit = clangTransUnit->mTransUnit;
gClangPerfManager.ZoneEnd();
if (transUnit == NULL)
return NULL;
CXFile file = clang_getFile(transUnit, cursorFileName);
mReturnString.clear();
bool showErrors = false;
gClangPerfManager.ZoneStart("Diagnostics");
int numDiagnostics = clang_getNumDiagnostics(transUnit);
for (int diagIdx = 0; diagIdx < numDiagnostics; diagIdx++)
{
if (ignoreErrors)
break;
CXDiagnostic diag = clang_getDiagnostic(transUnit, diagIdx);
CXDiagnosticSeverity severity = clang_getDiagnosticSeverity(diag);
auto var = clang_getDiagnosticCategory(diag);
int flag = 0;
if ((severity == CXDiagnostic_Error) || (severity == CXDiagnostic_Fatal))
flag = BfSourceElementFlag_Error;
else if (severity == CXDiagnostic_Warning)
flag = BfSourceElementFlag_Warning;
if (flag != 0)
{
String errorString = ToString(clang_getDiagnosticSpelling(diag));
bool ignore = false;
if (clangTransUnit->mHeaderPrefix != NULL)
{
// A bit of a hack to filter our invalid warnings
if ((int)errorString.find("#pragma once") != -1)
{
ignore = true;
}
}
if (!ignore)
{
int rangeCount = (int)clang_getDiagnosticNumRanges(diag);
for (int rangeIdx = 0; rangeIdx < rangeCount; rangeIdx++)
{
CXSourceRange range = clang_getDiagnosticRange(diag, rangeIdx);
int startIdx;
int endIdx;
CXFile diagFile = GetRange(range, &startIdx, &endIdx);
startIdx -= clangTransUnit->mHeaderPrefixLen;
endIdx -= clangTransUnit->mHeaderPrefixLen;
String diagFileName = ToString(clang_getFileName(diagFile));
if (showErrors)
{
CXSourceLocation sourceLocation = clang_getLocationForOffset(transUnit, file, startIdx);
uint32 line;
uint32 column;
uint32 offset;
clang_getFileLocation(sourceLocation, &file, &line, &column, &offset);
OutputDebugStrF("%s:%d:%d: %s\n", diagFileName.c_str(), line, column, errorString.c_str());
}
if ((startIdx >= 0) && (diagFile == file))
{
clangClassifier.ModifyFlags(startIdx, endIdx, 0xFF, flag);
CheckErrorRange(errorString, startIdx, endIdx, errorLookupTextIdx);
}
}
if (rangeCount == 0)
{
CXSourceLocation sourceLocation = clang_getDiagnosticLocation(diag);
int startIdx;
CXFile diagFile = GetLocation(sourceLocation, &startIdx);
startIdx -= clangTransUnit->mHeaderPrefixLen;
String diagFileName = ToString(clang_getFileName(diagFile));
if (showErrors)
{
OutputDebugStrF("%s: %s\n", diagFileName.c_str(), errorString.c_str());
}
if ((startIdx >= 0) && (diagFile == file))
{
int endIdx = startIdx + 1;
while (endIdx < charLen)
{
if (!IsIdentifierChar(charData[endIdx].mChar))
break;
endIdx++;
}
clangClassifier.ModifyFlags(startIdx, endIdx, 0xFF, flag);
CheckErrorRange(errorString, startIdx, endIdx, errorLookupTextIdx);
}
}
}
}
clang_disposeDiagnostic(diag);
}
gClangPerfManager.ZoneEnd();
transUnit = transUnit;
//
CXSourceLocation sourceLocation = clang_getLocationForOffset(transUnit, file, adjustedCursorIdx);
CXCursor cursor = clang_getCursor(transUnit, sourceLocation);
String displayName = ToString(clang_getCursorDisplayName(cursor));
CXCursor parentCursor = clang_getCursorSemanticParent(cursor);
// Try to see if we're bound to a function at the current location
ClangFindCall clangFindCall;
clangFindCall.mCursorIndex = adjustedCursorIdx;
clang_visitChildren(parentCursor, clangFindCall.VisitProc, &clangFindCall);
///
if (clangFindCall.mFoundCall)
{
String methodDefString;
methodDefString += ToString(clang_getCursorSpelling(clangFindCall.mCallDef));
CXType methodType = clang_getCursorType(clangFindCall.mCallDef);
methodDefString += "(\x1";
int numArgs = clang_Cursor_getNumArguments(clangFindCall.mCallDef);
for (int argIdx = 0; argIdx < numArgs; argIdx++)
{
if (argIdx > 0)
methodDefString += ",\x1 ";
CXCursor argCursor = clang_Cursor_getArgument(clangFindCall.mCallDef, argIdx);
CXType paramType = clang_getCursorType(argCursor);
methodDefString += ToString(clang_getTypeSpelling(paramType));
String argName = ToString(clang_getCursorSpelling(argCursor));
if (!argName.empty())
{
char lastC = methodDefString[methodDefString.length() - 1];
if ((lastC != '*') && (lastC != ' '))
methodDefString += " ";
methodDefString += argName;
}
}
if (clang_isFunctionTypeVariadic(methodType))
{
if (numArgs > 0)
methodDefString += ",\x1 ";
methodDefString += "...";
}
methodDefString += "\x1)";
bool isValidCall = true;
while (clangFindCall.mLeftParenIdx < charLen)
{
char c = charData[clangFindCall.mLeftParenIdx].mChar;
if (c == '(')
break;
clangFindCall.mLeftParenIdx++;
}
// If we're to the left of the paren then don't show the invocation
if (clangFindCall.mLeftParenIdx >= adjustedCursorIdx)
isValidCall = false;
if (isValidCall)
{
mReturnString += "invoke_cur\t" + methodDefString;
mReturnString += "\n";
mReturnString += StrFormat("invokeLeftParen\t%d\n", clangFindCall.mLeftParenIdx);
}
}
CXSourceRangeList* rangeList = clang_getSkippedRanges(transUnit, file);
for (int rangeIdx = 0; rangeIdx < (int)rangeList->count; rangeIdx++)
{
int startOffset;
int endOffset;
GetRange(rangeList->ranges[rangeIdx], &startOffset, &endOffset);
startOffset = std::max(0, startOffset - clangTransUnit->mHeaderPrefixLen);
endOffset = std::max(0, endOffset - clangTransUnit->mHeaderPrefixLen);
while (startOffset < endOffset)
{
if (clangClassifier.mCharData[startOffset].mChar == '\n')
{
startOffset++;
break;
}
startOffset++;
}
while (endOffset > startOffset)
{
endOffset--;
if (clangClassifier.mCharData[endOffset].mChar == '\n')
break;
}
clangClassifier.ModifyFlags(startOffset, endOffset, 0xFF, BfSourceElementFlag_Skipped);
//clangClassifier.SetElementType(startOffset, endOffset, BfSourceElementType_Comment);
}
clang_disposeSourceRangeList(rangeList);
gClangPerfManager.ZoneStart("Tokens");
CXSourceRange range = clang_getCursorExtent(clang_getTranslationUnitCursor(transUnit));
gClangPerfManager.ZoneEnd();
if (gClangPerfManager.mRecording)
{
gClangPerfManager.StopRecording();
gClangPerfManager.DbgPrint();
}
return mReturnString.c_str();
}
void ClangHelper::DecodeCompletionString(CXCompletionString completionString, String& displayText, String& replaceText, String& chunkText)
{
auto num = clang_getNumCompletionChunks(completionString);
//char buf[256];
bool hadSymbol = false;
for (int chunkIdx = 0; chunkIdx < (int) num; chunkIdx++)
{
auto chunkKind = clang_getCompletionChunkKind(completionString, chunkIdx);
ToString(clang_getCompletionChunkText(completionString, chunkIdx), chunkText);
switch (chunkKind)
{
case CXCompletionChunk_LeftParen:
hadSymbol = true;
replaceText += "(\x1";
break;
case CXCompletionChunk_RightParen:
hadSymbol = true;
replaceText += "\x1)";
break;
case CXCompletionChunk_Comma:
replaceText += ",\x1 ";
break;
case CXCompletionChunk_LeftBracket:
case CXCompletionChunk_RightBracket:
case CXCompletionChunk_LeftBrace:
case CXCompletionChunk_RightBrace:
case CXCompletionChunk_LeftAngle:
case CXCompletionChunk_RightAngle:
case CXCompletionChunk_CurrentParameter:
case CXCompletionChunk_Colon:
case CXCompletionChunk_HorizontalSpace:
case CXCompletionChunk_VerticalSpace:
hadSymbol = true;
replaceText += chunkText;
break;
case CXCompletionChunk_TypedText:
if (!hadSymbol)
{
displayText += chunkText;
}
replaceText += chunkText;
break;
case CXCompletionChunk_Placeholder:
for (int i = 0; i < (int)chunkText.size(); i++)
if (chunkText[i] == ',')
chunkText.insert(chunkText.begin() + i + 1, '\x1');
replaceText += chunkText;
break;
case CXCompletionChunk_ResultType:
case CXCompletionChunk_Text:
case CXCompletionChunk_Informative:
case CXCompletionChunk_Equal:
/*if (description.length() > 0)
description += " ";
description += chunkText;*/
break;
case CXCompletionChunk_Optional:
{
auto subCompletionString = clang_getCompletionChunkCompletionString(completionString, chunkIdx);
DecodeCompletionString(subCompletionString, displayText, replaceText, chunkText);
}
break;
case CXCompletionChunk_SemiColon:
break;
}
}
}
const char* ClangHelper::Autocomplete(const StringImpl& fileName, BfSourceClassifier::CharData* charData, int charLen, int cursorIdx)
{
gClangPerfManager.StartRecording();
//Sleep(500);
BfLogClang("%d Clang Autocomplete %s\n", ::GetCurrentThreadId(), fileName.c_str());
static int gACCount = 0;
gACCount++;
gClangPerfManager.ZoneStart("GetTranslationUnit");
ClangTranslationUnit* clangTransUnit = NULL;
/*if (headerFileName != NULL)
{
GetTranslationUnit(headerFileName, NULL, charData, charLen, NULL, 0, false, true);
clangTransUnit = GetTranslationUnit(fileName, NULL, NULL, 0, NULL, 0, true);
}
else*/
{
bool noReparse = false;
clangTransUnit = GetTranslationUnit(fileName, NULL, charData, charLen, NULL, 0, false, false, noReparse);
}
//const char* cursorFileName = (headerFileName != NULL) ? headerFileName : fileName.c_str();
const char* cursorFileName = clangTransUnit->mTransFileName.c_str();
CXTranslationUnit transUnit = clangTransUnit->mTransUnit;
gClangPerfManager.ZoneEnd();
if (transUnit == NULL)
return NULL;
gClangPerfManager.ZoneStart("Autocomplete");
String namespaceFind;
String filter;
int startIdx = std::max(0, cursorIdx - 1);
bool isInvocation = false;
int parenPos = -1;
int lastCharIdx = -1;
while (startIdx > 0)
{
char c = charData[startIdx].mChar;
if (charData[startIdx].mDisplayTypeId == BfSourceElementType_Comment)
{
startIdx--;
continue;;
}
if ((c == '(') && (filter.length() == 0))
{
isInvocation = true;
mInvokeString.clear();
startIdx--;
cursorIdx--;
parenPos = startIdx;
while (startIdx > 0)
{
if (!isspace(charData[startIdx].mChar))
break;
startIdx--;
}
continue;
}
if (IsIdentifierChar(c))
{
filter.insert(filter.begin(), c);
lastCharIdx = startIdx;
}
else if (isspace((uint8)c))
{
if (filter.length() == 0)
return NULL;
}
else
{
if ((c == ':') && (startIdx > 0) && (charData[startIdx - 1].mChar == ':'))
{
int prevStartIdx = startIdx;
startIdx -= 2;
while (startIdx > 0)
{
char c = charData[startIdx].mChar;
if (IsIdentifierChar(c))
{
namespaceFind.insert(namespaceFind.begin(), c);
}
else
{
break;
}
startIdx--;
}
startIdx = prevStartIdx;
}
if (lastCharIdx != -1)
startIdx = lastCharIdx;
else
startIdx++;
break;
}
startIdx--;
}
int autocompletePos = startIdx;
if ((autocompletePos == 0) || (charData[autocompletePos + 1].mChar == 0))
return NULL;
char prevChar = charData[autocompletePos - 1].mChar;
//if ((prevChar == '>') || (prevChar == '.') || (prevChar)
//((isspace((uint8)charData[autocompletePos - 1].mChar)) && (isspace((uint8)charData[autocompletePos].mChar))))
int adjustedAutocompletePos = autocompletePos + clangTransUnit->mHeaderPrefixLen;
CXFile file = clang_getFile(transUnit, cursorFileName);
CXSourceLocation sourceLocation = clang_getLocationForOffset(transUnit, file, adjustedAutocompletePos);
uint32 line;
uint32 column;
uint32 offset;
clang_getFileLocation(sourceLocation, &file, &line, &column, &offset);
if (line == 0)
return NULL;
gClangPerfManager.ZoneStart("clang_codeCompleteAt");
CXCodeCompleteResults* completeResults = clang_codeCompleteAt(transUnit, cursorFileName, line, column, VecFirstRef(gClangUnsavedFiles->mUnsavedFiles), (int)gClangUnsavedFiles->mUnsavedFiles.size(),
CXCodeComplete_IncludeMacros | CXCodeComplete_IncludeCodePatterns | CXCodeComplete_IncludeBriefComments);
gClangPerfManager.ZoneEnd();
auto contexts = clang_codeCompleteGetContexts(completeResults);
//String usr = ToString(clang_codeCompleteGetContainerUSR(completeResults));
uint32 isIncomplete;
CXCursorKind cursorKind = clang_codeCompleteGetContainerKind(completeResults, &isIncomplete);
bool isMember = cursorKind != CXCursor_InvalidCode;
if (namespaceFind.length() > 0)
isMember = true;
if (!isInvocation)
{
if (isMember)
{
filter.clear();
}
else if (filter.length() > 0)
{
// Just filter on the first character
filter = filter.substr(0, 1);
}
}
/*char strChars[16] = {0};
memcpy(strChars, mUnsavedFiles[0].Contents + autocompletePos - 15, 15);
OutputDebugStrF("Autocomplete: '%s' TextBefore:'%s' Idx:%d ResultCount:%d Container:%d\n", filter.c_str(), strChars, autocompletePos, completeResults->NumResults, cursorKind);*/
if (isInvocation)
{
bool hasMethod = false;
for (int resultIdx = 0; resultIdx < (int) completeResults->NumResults; resultIdx++)
{
CXCompletionResult completeResult = completeResults->Results[resultIdx];
auto resultKind = completeResult.CursorKind;
if ((resultKind == CXCursor_CXXMethod) || (resultKind == CXCursor_FunctionDecl) || (resultKind == CXCursor_Constructor) || (resultKind == CXCursor_Destructor))
{
hasMethod = true;
break;
}
}
// This happens when we have a "new TypeName(" - we only get type completions, so we need to autocomplete on the paren
if (!hasMethod)
{
clang_disposeCodeCompleteResults(completeResults);
autocompletePos = parenPos;
sourceLocation = clang_getLocationForOffset(transUnit, file, adjustedAutocompletePos);
clang_getFileLocation(sourceLocation, &file, &line, &column, &offset);
completeResults = clang_codeCompleteAt(transUnit, cursorFileName, line, column, VecFirstRef(gClangUnsavedFiles->mUnsavedFiles), (int)gClangUnsavedFiles->mUnsavedFiles.size(),
CXCodeComplete_IncludeMacros | CXCodeComplete_IncludeCodePatterns | CXCodeComplete_IncludeBriefComments);
}
}
mReturnString.clear();
if (completeResults == NULL)
return NULL;
//AutocompleteMap entries;
AutoCompleteBase autoComplete;
if (isMember)
mReturnString += "isMember\t1\n";
String displayText;
String replaceText;
String description;
String chunkText;
for (int resultIdx = 0; resultIdx < (int) completeResults->NumResults; resultIdx++)
{
AutoPerf autoPerf("HandleResult", &gClangPerfManager);
displayText.clear();
replaceText.clear();
description.clear();
if ((!isMember) && (filter.empty()))
break; // Don't allow listing of ALL globals
CXCompletionResult completeResult = completeResults->Results[resultIdx];
auto resultKind = completeResult.CursorKind;
/*if (namespaceFind.length() != 0)
{
if (resultKind != CXCursor_Namespace)
continue;
DecodeCompletionString(completeResult.CompletionString, displayText, replaceText, chunkText);
if (displayText != namespaceFind)
continue;
}*/
//auto priority = clang_getCompletionPriority(completeResult.CompletionString);
DecodeCompletionString(completeResult.CompletionString, displayText, replaceText, chunkText);
if (namespaceFind.length() > 0)
OutputDebugStrF("%s\n", displayText.c_str());
if ((resultKind == CXCursor_CXXMethod) || (resultKind == CXCursor_FunctionDecl) || (resultKind == CXCursor_FunctionTemplate) ||
((resultKind == CXCursor_Constructor) && (isInvocation)))
{
bool isValid = true;
for (int i = 0; i < (int)displayText.size(); i++)
if (!IsIdentifierChar(displayText[i]))
isValid = false;
if (isValid)
{
if (!isInvocation)
autoComplete.AddEntry(BfAutoComplete::Entry("method", displayText), filter);
if ((isInvocation) && (displayText == filter))
{
mInvokeString += "invoke\t" + replaceText + "\n";
}
}
}
// Note: primitives are of type CXCursor_NotImplemented
if ((resultKind == CXCursor_StructDecl) || (resultKind == CXCursor_UnionDecl) || (resultKind == CXCursor_ClassDecl) ||
(resultKind == CXCursor_ClassTemplate) ||
(resultKind == CXCursor_EnumDecl) || (resultKind == CXCursor_TypedefDecl) || (resultKind == CXCursor_TypeAliasDecl) ||
(resultKind == CXCursor_UnexposedDecl) || (resultKind == CXCursor_NotImplemented))
{
if (!isInvocation)
{
autoComplete.AddEntry(BfAutoComplete::Entry("type", displayText), filter);
}
}
if (!isInvocation)
{
if (resultKind == CXCursor_VarDecl)
{
autoComplete.AddEntry(BfAutoComplete::Entry("local", displayText), filter);
}
else if (resultKind == CXCursor_ParmDecl)
{
autoComplete.AddEntry(BfAutoComplete::Entry("param", displayText), filter);
}
else if (resultKind == CXCursor_FieldDecl)
{
autoComplete.AddEntry(BfAutoComplete::Entry("field", displayText), filter);
}
else if (resultKind == CXCursor_Namespace)
{
autoComplete.AddEntry(BfAutoComplete::Entry("namespace", displayText), filter);
}
}
}
for (auto& entry : autoComplete.mEntries)
{
String entryString = String(entry.mEntryType) + "\t" + String(entry.mDisplay) + "\n";
mReturnString.append(entryString);
}
if ((isInvocation) && (!mInvokeString.empty()))
{
mInvokeString += StrFormat("invokeLeftParen\t%d\n", cursorIdx);
}
mReturnString += mInvokeString;
mInvokeString.clear();
clang_disposeCodeCompleteResults(completeResults);
gClangPerfManager.ZoneEnd();
if (gClangPerfManager.mRecording)
{
gClangPerfManager.StopRecording();
gClangPerfManager.DbgPrint();
}
return mReturnString.c_str();
}
//////////////////////////////////////////////////////////////////////////
BF_EXPORT ClangHelper* BF_CALLTYPE ClangHelper_Create(bool isForResolve)
{
if (gClangUnsavedFiles == NULL)
gClangUnsavedFiles = new ClangUnsavedFiles();
ClangHelper* clangHelper = new ClangHelper();
clangHelper->mIsForResolve = isForResolve;
return clangHelper;
}
BF_EXPORT void BF_CALLTYPE ClangHelper_Delete(ClangHelper* clangHelper)
{
delete clangHelper;
delete gClangUnsavedFiles;
gClangUnsavedFiles = NULL;
}
BF_EXPORT void BF_CALLTYPE ClangHelper_AddTranslationUnit(ClangHelper* clangHelper, const char* fileName, const char* headerPrefix, const char* clangArgs, BfSourceClassifier::CharData* charData, int charLen)
{
if (clangHelper == NULL)
return;
if (!clangHelper->mIsForResolve)
{
OutputDebugStrF("AddTranslationUnit %s\n", fileName);
//gClangPerfManager.StartRecording();
}
gClangPerfManager.ZoneStart("Clang AddTranslationUnit");
clangHelper->AddTranslationUnit(fileName, headerPrefix, clangArgs, charData, charLen);
gClangPerfManager.ZoneEnd();
}
BF_EXPORT void BF_CALLTYPE ClangHelper_RemoveTranslationUnit(ClangHelper* clangHelper, const char* fileName)
{
if (clangHelper == NULL)
return;
gClangPerfManager.ZoneStart("Clang RemoveTranslationUnit");
clangHelper->RemoveTranslationUnit(fileName);
gClangPerfManager.ZoneEnd();
}
BF_EXPORT const char* BF_CALLTYPE ClangHelper_Classify(ClangHelper* clangHelper, const char* fileName, BfSourceClassifier::CharData* charData, int charLen, int cursorIdx, int errorLookupTextIdx, bool ignoreErrors)
{
if (clangHelper == NULL)
return "";
return clangHelper->Classify(fileName, charData, charLen, cursorIdx, errorLookupTextIdx, ignoreErrors);
}
BF_EXPORT const char* BF_CALLTYPE ClangHelper_Autocomplete(ClangHelper* clangHelper, const char* fileName, BfSourceClassifier::CharData* charData, int charLen, int cursorIdx)
{
if (clangHelper == NULL)
return "";
return clangHelper->Autocomplete(fileName, charData, charLen, cursorIdx);
}
BF_EXPORT const char* BF_CALLTYPE ClangHelper_DetermineFilesReferenced(ClangHelper* clangHelper, const char* fileName)
{
if (clangHelper == NULL)
return "";
return clangHelper->DetermineFilesReferenced(fileName);
}
BF_EXPORT const char* BF_CALLTYPE ClangHelper_FindDefinition(ClangHelper* clangHelper, const char* fileName, int defPos, int& outDefLine, int& outDefColumn)
{
if (clangHelper == NULL)
return "";
outDefLine = 0;
outDefColumn = 0;
ClangTranslationUnit* clangTransUnit = clangHelper->GetTranslationUnit(fileName, NULL, 0);
CXTranslationUnit transUnit = clangTransUnit->mTransUnit;
const char* cursorFileName = clangTransUnit->mTransFileName.c_str();
CXFile file = clang_getFile(transUnit, cursorFileName);
//CXSourceLocation loc = clang_getLocation(transUnit, file, line + 1, column + 1);
int adjustedPos = defPos + clangTransUnit->mHeaderPrefixLen - 1;
CXSourceLocation loc = clang_getLocationForOffset(transUnit, file, adjustedPos);
CXCursor cursor = clang_getCursor(transUnit, loc);
if (clang_getCursorKind(cursor) == CXCursor_CompoundStmt)
{
CXSourceRange range = clang_getCursorExtent(cursor);
uint numTokens = 0;
CXToken* tokens = NULL;
clang_tokenize(transUnit, range, &tokens, &numTokens);
for (int tokenIdx = 0; tokenIdx < (int)numTokens; tokenIdx++)
{
}
clang_disposeTokens(transUnit, tokens, numTokens);
/*auto startLoc = clang_getCursorLocation(cursor);
CXFile defFile;
uint32 offset;
clang_getFileLocation(startLoc, &defFile, (uint32*)&outDefLine, (uint32*)&outDefColumn, &offset);
outDefLine--;
outDefColumn--;
ClangBreakCompound clangBreakCompound;
clang_visitChildren(cursor, ClangBreakCompound::VisitProc, &clangBreakCompound);
cursor = clangBreakCompound.mDefCursor;*/
}
String displayName = ToString(clang_getCursorDisplayName(cursor));
int numArgs = clang_Cursor_getNumArguments(cursor);
for (int argIdx = 0; argIdx < numArgs; argIdx++)
{
CXCursor argCursor = clang_Cursor_getArgument(cursor, argIdx);
}
CXFile includedFile = clang_getIncludedFile(cursor);
if (includedFile != NULL)
{
outString = ToString(clang_getFileName(includedFile));
return outString.c_str();
}
CXCursor refCursor = clang_getCursorReferenced(cursor);
//if (clang_getCursorKind(refCursor) < CXCursor_FirstInvalid)
cursor = refCursor;
CXCursor defCursor = clang_getCursorDefinition(cursor);
/*auto cursorKind = clang_getCursorKind(cursor);
if ((cursorKind == CXCursor_FunctionDecl) || (cursorKind == CXCursor_CXXMethod) || (cursorKind == CXCursor_Constructor) ||
(cursorKind == CXCursor_Destructor) || (cursorKind == CXCursor_ConversionFunction) || (cursorKind == CXCursor_FunctionTemplate))
{
//ClangFindNavigationData clangFindNavigationData;
bool hasBody = false;
clang_visitChildren(cursor, ClangFindNavigationData::HasBodyVisitProc, &hasBody);
if (hasBody)
{
defCursor = clang_getCursorLexicalParent(cursor);
}
}*/
if (!clang_Cursor_isNull(defCursor))
cursor = defCursor;
CXSourceRange cursorExtent = clang_getCursorExtent(cursor);
CXSourceLocation startLoc = clang_getRangeStart(cursorExtent);
CXFile defFile;
uint32 offset;
clang_getFileLocation(startLoc, &defFile, (uint32*)&outDefLine, (uint32*)&outDefColumn, &offset);
outDefLine--;
outDefColumn--;
if (defFile == file)
{
outString = clangTransUnit->mRequestedFileName;
// Adjust line num
for (int i = 0; i < clangTransUnit->mHeaderPrefixLen; i++)
if (clangTransUnit->mHeaderPrefix[i] == '\n')
outDefLine--;
}
else
outString = ToString(clang_getFileName(defFile));
if (outString.length() == 0)
return NULL;
return outString.c_str();
}
BF_EXPORT const char* BF_CALLTYPE ClangHelper_GetNavigationData(ClangHelper* clangHelper, const char* fileName)
{
if (clangHelper == NULL)
return "";
ClangTranslationUnit* clangTransUnit = clangHelper->GetTranslationUnit(fileName, NULL, 0);
CXTranslationUnit transUnit = clangTransUnit->mTransUnit;
outString.clear();
ClangFindNavigationData clangFindNavigationData;
clangFindNavigationData.mAllowEmptyMethods = clangTransUnit->mIsHeader;
clangFindNavigationData.mWantFile = clang_getFile(transUnit, clangTransUnit->mRequestedFileName.c_str());;
clang_visitChildren(clang_getTranslationUnitCursor(transUnit), clangFindNavigationData.VisitProc, &clangFindNavigationData);
return outString.c_str();
}
BF_EXPORT const char* BF_CALLTYPE ClangHelper_GetCurrentLocation(ClangHelper* clangHelper, const char* fileName, int defPos)
{
if (clangHelper == NULL)
return "";
ClangTranslationUnit* clangTransUnit = clangHelper->GetTranslationUnit(fileName, NULL, 0);
CXTranslationUnit transUnit = clangTransUnit->mTransUnit;
if (defPos >= clangTransUnit->mCharLen)
return ""; // Sanity check
outString.clear();
const char* cursorFileName = clangTransUnit->mTransFileName.c_str();
CXFile file = clang_getFile(transUnit, cursorFileName);
//CXSourceLocation loc = clang_getLocation(transUnit, file, line + 1, column + 1);
int adjustedPos = defPos + clangTransUnit->mHeaderPrefixLen;
CXSourceLocation loc = clang_getLocationForOffset(transUnit, file, adjustedPos);
ClangFindNavigationData clangFindNavigationData;
CXCursor cursor = clang_getCursor(transUnit, loc);
clangFindNavigationData.mAllowEmptyMethods = false;
clangFindNavigationData.mWantFile = NULL;//clang_getFile(transUnit, clangTransUnit->mRequestedFileName.c_str());;
clangFindNavigationData.mAddLocation = false;
while (!clang_Cursor_isNull(cursor))
{
auto cursorKind = clang_getCursorKind(cursor);
if ((cursorKind == CXCursor_FunctionDecl) || (cursorKind == CXCursor_CXXMethod) || (cursorKind == CXCursor_Constructor) ||
(cursorKind == CXCursor_Destructor) || (cursorKind == CXCursor_ConversionFunction) || (cursorKind == CXCursor_FunctionTemplate))
{
clangFindNavigationData.Visit(cursor, cursor);
break;
}
//cursor = clang_getCursorLexicalParent(cursor);
cursor = clang_getCursorSemanticParent(cursor);
}
return outString.c_str();
}
#endif