Commit 7cb06699 authored by Thomas Schöps's avatar Thomas Schöps Committed by Thomas Schöps

Simplify the implementation of ClangUtils::getRawContents() by using...

Simplify the implementation of ClangUtils::getRawContents() by using clang_getFileContents(), increase the libclang minimum version requirement accordingly, and remove code for older versions of libclang
parent 0f53e240
......@@ -40,7 +40,7 @@ else()
message(WARNING "Will not build QML/JS plugin, needs Qt >= 5.8.0")
endif()
find_package(Clang 3.8)
find_package(Clang 6.0)
set(clangSearchHint "")
if (NOT CLANG_FOUND)
set(clangSearchHint "Please install a package providing libclang. Either pass -DLLVM_ROOT=/path/to/llvm-prefix or install the 'llvm-config' command-line utility for auto-detection.")
......
......@@ -1004,7 +1004,7 @@ void Visitor::setDeclData(CXCursor cursor, MacroDefinition* decl) const
// And no way to get the actual definition text range
// Should be quite easy to expose that in libclang, though
// Let' still get some basic support for this and parse on our own, it's not that difficult
const QString contents = QString::fromUtf8(ClangUtils::getRawContents(unit, range));
const QString contents = ClangUtils::getRawContents(unit, range);
const int firstOpeningParen = contents.indexOf(QLatin1Char('('));
const int firstWhitespace = contents.indexOf(QLatin1Char(' '));
const bool isFunctionLike = (firstOpeningParen != -1) && (firstOpeningParen < firstWhitespace);
......@@ -1417,7 +1417,7 @@ RangeInRevision rangeInRevisionForUse(CXCursor cursor, CXCursorKind referencedCu
} else {
// Workaround for wrong use range returned by clang for macro expansions
const auto contents = ClangUtils::getRawContents(clang_Cursor_getTranslationUnit(cursor), useRange);
const int firstOpeningParen = contents.indexOf('(');
const int firstOpeningParen = contents.indexOf(QChar::fromLatin1('('));
if (firstOpeningParen != -1) {
range.end.column = range.start.column + firstOpeningParen;
range.end.line = range.start.line;
......
......@@ -75,8 +75,8 @@ ClangFixits fixitsForDiagnostic(CXDiagnostic diagnostic, CXTranslationUnit unit)
for (uint i = 0; i < numFixits; ++i) {
CXSourceRange range;
const QString replacementText = ClangString(clang_getDiagnosticFixIt(diagnostic, i, &range)).toString();
QByteArray original = ClangUtils::getRawContents(unit, range);
fixits << ClangFixit{replacementText, ClangRange(range).toDocumentRange(), QString(), QString::fromLocal8Bit(original)};
const auto original = ClangUtils::getRawContents(unit, range);
fixits << ClangFixit{replacementText, ClangRange(range).toDocumentRange(), QString(), original};
}
return fixits;
}
......
......@@ -236,7 +236,7 @@ void TestClangUtils::testGetRawContents()
DocumentRange documentRange{IndexedString(fileName), range};
auto cxRange = toCXRange(unit, documentRange);
const QString contents = QString::fromLatin1(ClangUtils::getRawContents(unit, cxRange));
const QString contents = ClangUtils::getRawContents(unit, cxRange);
QCOMPARE(contents, expectedContents);
}
......
......@@ -334,38 +334,21 @@ QStringList ClangUtils::templateArgumentTypes(CXCursor cursor)
return types;
}
QByteArray ClangUtils::getRawContents(CXTranslationUnit unit, CXSourceRange range)
QString ClangUtils::getRawContents(CXTranslationUnit unit, CXSourceRange range)
{
const auto rangeStart = clang_getRangeStart(range);
const auto rangeEnd = clang_getRangeEnd(range);
CXFile rangeFile;
unsigned int start, end;
clang_getFileLocation(rangeStart, nullptr, nullptr, nullptr, &start);
clang_getFileLocation(rangeStart, &rangeFile, nullptr, nullptr, &start);
clang_getFileLocation(rangeEnd, nullptr, nullptr, nullptr, &end);
QByteArray result;
const ClangTokens tokens(unit, range);
for (CXToken token : tokens) {
const auto location = ClangLocation(clang_getTokenLocation(unit, token));
unsigned int offset;
clang_getFileLocation(location, nullptr, nullptr, nullptr, &offset);
if (offset < start) // TODO: Sometimes hit, see bug 357585
return {};
const int fillCharacters = offset - start - result.size();
Q_ASSERT(fillCharacters >= 0);
if (fillCharacters < 0)
return {};
result.append(QByteArray(fillCharacters, ' '));
const auto spelling = clang_getTokenSpelling(unit, token);
result.append(clang_getCString(spelling));
clang_disposeString(spelling);
std::size_t fileSize;
const char* fileBuffer = clang_getFileContents(unit, rangeFile, &fileSize);
if (fileBuffer && start < fileSize && end <= fileSize && start < end) {
return QString::fromUtf8(fileBuffer + start, end - start);
}
// Clang always appends the full range of the last token, even if this exceeds the end of the requested range.
// Fix this.
result.chop(result.size() - (end - start));
return result;
return QString();
}
bool ClangUtils::isExplicitlyDefaultedOrDeleted(CXCursor cursor)
......@@ -374,65 +357,9 @@ bool ClangUtils::isExplicitlyDefaultedOrDeleted(CXCursor cursor)
return true;
}
#if CINDEX_VERSION_MINOR >= 34
if (clang_CXXMethod_isDefaulted(cursor)) {
return true;
}
#else
auto declCursor = clang_getCanonicalCursor(cursor);
CXTranslationUnit tu = clang_Cursor_getTranslationUnit(declCursor);
ClangTokens tokens(tu, clang_getCursorExtent(declCursor));
bool lastTokenWasDeleteOrDefault = false;
for (auto it = tokens.rbegin(), end = tokens.rend(); it != end; ++it) {
CXToken token = *it;
auto kind = clang_getTokenKind(token);
switch (kind) {
case CXToken_Comment:
break;
case CXToken_Identifier:
case CXToken_Literal:
lastTokenWasDeleteOrDefault = false;
break;
case CXToken_Punctuation: {
ClangString spelling(clang_getTokenSpelling(tu, token));
const char* spellingCStr = spelling.c_str();
if (strcmp(spellingCStr, ")") == 0) {
// a closing parent means we have reached the end of the function parameter list
// therefore this function can't be explicitly deleted/defaulted
return false;
} else if (strcmp(spellingCStr, "=") == 0) {
if (lastTokenWasDeleteOrDefault) {
return true;
}
#if CINDEX_VERSION_MINOR < 31
// HACK: on old clang versions, we don't get the default/delete
// so there, assume the function is defaulted or deleted
// when the last token is an equal sign
if (it == tokens.rbegin()) {
return true;
}
#endif
}
lastTokenWasDeleteOrDefault = false;
break;
}
case CXToken_Keyword: {
ClangString spelling(clang_getTokenSpelling(tu, token));
const char* spellingCStr = spelling.c_str();
if (strcmp(spellingCStr, "default") == 0
#if CINDEX_VERSION_MINOR < 31
|| strcmp(spellingCStr, "delete") == 0
#endif
) {
lastTokenWasDeleteOrDefault = true;
} else {
lastTokenWasDeleteOrDefault = false;
}
break;
}
}
}
#endif
return false;
}
......
......@@ -116,13 +116,9 @@ namespace ClangUtils
* @note This will return the exact textual representation of the code,
* no whitespace stripped, etc.
*
* TODO: It would better if we'd be able to just memcpy parts of the file buffer
* that's stored inside Clang (cf. llvm::MemoryBuffer for files), but libclang
* doesn't offer API for that. This implementation here is a lot more expensive.
*
* @param unit Translation unit this range is part of
*/
KDEVCLANGPRIVATE_EXPORT QByteArray getRawContents(CXTranslationUnit unit, CXSourceRange range);
KDEVCLANGPRIVATE_EXPORT QString getRawContents(CXTranslationUnit unit, CXSourceRange range);
/**
* @brief Return true if file @p file1 and file @p file2 are equal
......@@ -131,14 +127,7 @@ namespace ClangUtils
*/
inline bool isFileEqual(CXFile file1, CXFile file2)
{
#if CINDEX_VERSION_MINOR >= 28
return clang_File_isEqual(file1, file2);
#else
// note: according to the implementation of clang_File_isEqual, file1 and file2 can still be equal,
// regardless of whether file1 == file2 is true or not
// however, we didn't see any problems with pure pointer comparisons until now, so fall back to that
return file1 == file2;
#endif
}
/**
......@@ -147,8 +136,7 @@ namespace ClangUtils
*
* TODO: do we need isExplicitlyDefaulted() + isExplicitlyDeleted()?
* Currently this is only used by the implements completion to hide deleted+defaulted functions so
* we don't need to know the difference. We need to tokenize the source code because there is no
* such API in libclang so having one function to check both cases is more efficient (only tokenize once)
* we don't need to know the difference.
*/
bool isExplicitlyDefaultedOrDeleted(CXCursor cursor);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment