" << variableName << " | ";
@@ -946,7 +946,7 @@ QString BaseQtVersion::findHostBinary(HostBinaries binary) const
switch (binary) {
case Designer:
case Linguist:
- baseDir = m_mkspecValues.value(QLatin1String("QT.designer.bins"));
+ baseDir = m_mkspecValues.value("QT.designer.bins");
break;
case Uic:
case QScxmlc:
@@ -960,33 +960,32 @@ QString BaseQtVersion::findHostBinary(HostBinaries binary) const
if (baseDir.isEmpty())
return QString();
- if (!baseDir.endsWith(QLatin1Char('/')))
- baseDir += QLatin1Char('/');
+ if (!baseDir.endsWith('/'))
+ baseDir += '/';
QStringList possibleCommands;
switch (binary) {
case Designer:
if (HostOsInfo::isMacHost())
- possibleCommands << QLatin1String("Designer.app/Contents/MacOS/Designer");
+ possibleCommands << "Designer.app/Contents/MacOS/Designer";
else
- possibleCommands << HostOsInfo::withExecutableSuffix(QLatin1String("designer"));
+ possibleCommands << HostOsInfo::withExecutableSuffix("designer");
break;
case Linguist:
if (HostOsInfo::isMacHost())
- possibleCommands << QLatin1String("Linguist.app/Contents/MacOS/Linguist");
+ possibleCommands << "Linguist.app/Contents/MacOS/Linguist";
else
- possibleCommands << HostOsInfo::withExecutableSuffix(QLatin1String("linguist"));
+ possibleCommands << HostOsInfo::withExecutableSuffix("linguist");
break;
case Uic:
if (HostOsInfo::isWindowsHost()) {
- possibleCommands << QLatin1String("uic.exe");
+ possibleCommands << "uic.exe";
} else {
- possibleCommands << QLatin1String("uic-qt4") << QLatin1String("uic4")
- << QLatin1String("uic");
+ possibleCommands << "uic-qt4" << "uic4" << "uic";
}
break;
case QScxmlc:
- possibleCommands << HostOsInfo::withExecutableSuffix(QLatin1String("qscxmlc"));
+ possibleCommands << HostOsInfo::withExecutableSuffix("qscxmlc");
break;
default:
Q_ASSERT(false);
@@ -1027,7 +1026,7 @@ void BaseQtVersion::updateMkspec() const
m_mkspec = m_mkspec.relativeChildPath(baseMkspecDir);
// qDebug() << "Setting mkspec to"<values(QLatin1String("CONFIG"));
- m_qtConfigValues = evaluator->values(QLatin1String("QT_CONFIG"));
+ m_configValues = evaluator->values("CONFIG");
+ m_qtConfigValues = evaluator->values("QT_CONFIG");
m_defaultConfigIsDebugAndRelease = false;
m_frameworkBuild = false;
foreach (const QString &value, m_configValues) {
- if (value == QLatin1String("debug"))
+ if (value == "debug")
m_defaultConfigIsDebug = true;
- else if (value == QLatin1String("release"))
+ else if (value == "release")
m_defaultConfigIsDebug = false;
- else if (value == QLatin1String("build_all"))
+ else if (value == "build_all")
m_defaultConfigIsDebugAndRelease = true;
- else if (value == QLatin1String("qt_framework"))
+ else if (value == "qt_framework")
m_frameworkBuild = true;
}
- const QString designerBins = QLatin1String("QT.designer.bins");
- const QString qmlBins = QLatin1String("QT.qml.bins");
- const QString declarativeBins = QLatin1String("QT.declarative.bins");
- const QString libinfix = QLatin1String(MKSPEC_VALUE_LIBINFIX);
- const QString ns = QLatin1String(MKSPEC_VALUE_NAMESPACE);
+ const QString designerBins = "QT.designer.bins";
+ const QString qmlBins = "QT.qml.bins";
+ const QString declarativeBins = "QT.declarative.bins";
+ const QString libinfix = MKSPEC_VALUE_LIBINFIX;
+ const QString ns = MKSPEC_VALUE_NAMESPACE;
m_mkspecValues.insert(designerBins, evaluator->value(designerBins));
m_mkspecValues.insert(qmlBins, evaluator->value(qmlBins));
m_mkspecValues.insert(declarativeBins, evaluator->value(declarativeBins));
@@ -1123,11 +1122,11 @@ bool BaseQtVersion::hasMkspec(const FileName &spec) const
return true; // default spec of a Qt version
QDir mkspecDir = QDir(QDir::fromNativeSeparators(qmakeProperty("QT_HOST_DATA"))
- + QLatin1String("/mkspecs/"));
+ + "/mkspecs/");
const QString absSpec = mkspecDir.absoluteFilePath(spec.toString());
if (QFileInfo(absSpec).isDir() && QFileInfo(absSpec + "/qmake.conf").isFile())
return true;
- mkspecDir.setPath(sourcePath().toString() + QLatin1String("/mkspecs/"));
+ mkspecDir.setPath(sourcePath().toString() + "/mkspecs/");
const QString absSrcSpec = mkspecDir.absoluteFilePath(spec.toString());
return absSrcSpec != absSpec
&& QFileInfo(absSrcSpec).isDir()
@@ -1239,7 +1238,7 @@ QString BaseQtVersion::qmakeProperty(const QHash &versionInfo,
variant == PropertyVariantGet ? "/get" : "/src")))).toQString();
if (!val.isNull())
return val;
- return versionInfo.value(ProKey(QString::fromLatin1(name))).toQString();
+ return versionInfo.value(ProKey(name)).toQString();
}
QString BaseQtVersion::qmakeProperty(const QByteArray &name, PropertyVariant variant) const
@@ -1363,7 +1362,7 @@ void BaseQtVersion::populateQmlFileFinder(FileInProjectFinder *finder, const Tar
void BaseQtVersion::addToEnvironment(const Kit *k, Environment &env) const
{
Q_UNUSED(k);
- env.set(QLatin1String("QTDIR"), QDir::toNativeSeparators(qmakeProperty("QT_HOST_DATA")));
+ env.set("QTDIR", QDir::toNativeSeparators(qmakeProperty("QT_HOST_DATA")));
}
// Some Qt versions may require environment settings for qmake to work
@@ -1454,7 +1453,7 @@ static QByteArray runQmakeQuery(const FileName &binary, const Environment &env,
QProcess process;
process.setEnvironment(env.toStringList());
- process.start(binary.toString(), QStringList(QLatin1String("-query")), QIODevice::ReadOnly);
+ process.start(binary.toString(), QStringList("-query"), QIODevice::ReadOnly);
if (!process.waitForStarted()) {
*error = QCoreApplication::translate("QtVersion", "Cannot start \"%1\": %2").arg(binary.toUserOutput()).arg(process.errorString());
@@ -1521,7 +1520,7 @@ FileName BaseQtVersion::mkspecDirectoryFromVersionInfo(const QHash &versionInfo)
@@ -1533,7 +1532,7 @@ FileName BaseQtVersion::mkspecFromVersionInfo(const QHash &ve
bool qt5 = false;
QString theSpec = qmakeProperty(versionInfo, "QMAKE_XSPEC");
if (theSpec.isEmpty())
- theSpec = QLatin1String("default");
+ theSpec = "default";
else
qt5 = true;
@@ -1544,7 +1543,7 @@ FileName BaseQtVersion::mkspecFromVersionInfo(const QHash &ve
if (HostOsInfo::isWindowsHost()) {
if (!qt5) {
- QFile f2(mkspecFullPath.toString() + QLatin1String("/qmake.conf"));
+ QFile f2(mkspecFullPath.toString() + "/qmake.conf");
if (f2.exists() && f2.open(QIODevice::ReadOnly)) {
while (!f2.atEnd()) {
QByteArray line = f2.readLine();
@@ -1552,16 +1551,16 @@ FileName BaseQtVersion::mkspecFromVersionInfo(const QHash &ve
const QList &temp = line.split('=');
if (temp.size() == 2) {
QString possibleFullPath = QString::fromLocal8Bit(temp.at(1).trimmed().constData());
- if (possibleFullPath.contains(QLatin1Char('$'))) { // QTBUG-28792
- const QRegularExpression rex(QLatin1String("\\binclude\\(([^)]+)/qmake\\.conf\\)"));
+ if (possibleFullPath.contains('$')) { // QTBUG-28792
+ const QRegularExpression rex("\\binclude\\(([^)]+)/qmake\\.conf\\)");
const QRegularExpressionMatch match = rex.match(QString::fromLocal8Bit(f2.readAll()));
if (match.hasMatch()) {
- possibleFullPath = mkspecFullPath.toString() + QLatin1Char('/')
+ possibleFullPath = mkspecFullPath.toString() + '/'
+ match.captured(1);
}
}
// We sometimes get a mix of different slash styles here...
- possibleFullPath = possibleFullPath.replace(QLatin1Char('\\'), QLatin1Char('/'));
+ possibleFullPath = possibleFullPath.replace('\\', '/');
if (QFileInfo::exists(possibleFullPath)) // Only if the path exists
mkspecFullPath = FileName::fromUserInput(possibleFullPath);
}
@@ -1573,7 +1572,7 @@ FileName BaseQtVersion::mkspecFromVersionInfo(const QHash &ve
}
} else {
if (HostOsInfo::isMacHost()) {
- QFile f2(mkspecFullPath.toString() + QLatin1String("/qmake.conf"));
+ QFile f2(mkspecFullPath.toString() + "/qmake.conf");
if (f2.exists() && f2.open(QIODevice::ReadOnly)) {
while (!f2.atEnd()) {
QByteArray line = f2.readLine();
@@ -1584,7 +1583,7 @@ FileName BaseQtVersion::mkspecFromVersionInfo(const QHash &ve
if (value.contains("XCODE")) {
// we don't want to generate xcode projects...
// qDebug() << "default mkspec is xcode, falling back to g++";
- return baseMkspecDir.appendPath(QLatin1String("macx-g++"));
+ return baseMkspecDir.appendPath("macx-g++");
}
}
break;
@@ -1612,14 +1611,14 @@ FileName BaseQtVersion::sourcePath(const QHash &versionInfo)
const QString installData = qmakeProperty(versionInfo, "QT_INSTALL_PREFIX");
QString sourcePath = installData;
- QFile qmakeCache(installData + QLatin1String("/.qmake.cache"));
+ QFile qmakeCache(installData + "/.qmake.cache");
if (qmakeCache.exists() && qmakeCache.open(QIODevice::ReadOnly | QIODevice::Text)) {
QTextStream stream(&qmakeCache);
while (!stream.atEnd()) {
QString line = stream.readLine().trimmed();
- if (line.startsWith(QLatin1String("QT_SOURCE_TREE"))) {
- sourcePath = line.split(QLatin1Char('=')).at(1).trimmed();
- if (sourcePath.startsWith(QLatin1String("$$quote("))) {
+ if (line.startsWith("QT_SOURCE_TREE")) {
+ sourcePath = line.split('=').at(1).trimmed();
+ if (sourcePath.startsWith("$$quote(")) {
sourcePath.remove(0, 8);
sourcePath.chop(1);
}
@@ -1636,7 +1635,7 @@ bool BaseQtVersion::isInSourceDirectory(const Utils::FileName &filePath)
if (source.isEmpty())
return false;
QDir dir = QDir(source.toString());
- if (dir.dirName() == QLatin1String("qtbase"))
+ if (dir.dirName() == "qtbase")
dir.cdUp();
return filePath.isChildOf(dir);
}
@@ -1646,7 +1645,7 @@ bool BaseQtVersion::isSubProject(const Utils::FileName &filePath) const
const Utils::FileName &source = sourcePath();
if (!source.isEmpty()) {
QDir dir = QDir(source.toString());
- if (dir.dirName() == QLatin1String("qtbase"))
+ if (dir.dirName() == "qtbase")
dir.cdUp();
if (filePath.isChildOf(dir))
@@ -1720,7 +1719,7 @@ bool BaseQtVersion::isQtQuickCompilerSupported(QString *reason) const
}
const QString qtQuickCompilerExecutable =
- HostOsInfo::withExecutableSuffix(binPath().toString() + QLatin1String("/qtquickcompiler"));
+ HostOsInfo::withExecutableSuffix(binPath().toString() + "/qtquickcompiler");
if (!QFileInfo::exists(qtQuickCompilerExecutable)) {
if (reason)
*reason = QCoreApplication::translate("BaseQtVersion", "This Qt Version does not contain Qt Quick Compiler.");
@@ -1748,22 +1747,22 @@ FileNameList BaseQtVersion::qtCorePaths() const
foreach (const QFileInfo &info, infoList) {
const QString file = info.fileName();
if (info.isDir()
- && file.startsWith(QLatin1String("QtCore"))
- && file.endsWith(QLatin1String(".framework"))) {
+ && file.startsWith("QtCore")
+ && file.endsWith(".framework")) {
// handle Framework
FileName lib(info);
- dynamicLibs.append(lib.appendPath(file.left(file.lastIndexOf(QLatin1Char('.')))));
+ dynamicLibs.append(lib.appendPath(file.left(file.lastIndexOf('.'))));
} else if (info.isReadable()) {
- if (file.startsWith(QLatin1String("libQtCore"))
- || file.startsWith(QLatin1String("libQt5Core"))
- || file.startsWith(QLatin1String("QtCore"))
- || file.startsWith(QLatin1String("Qt5Core"))) {
- if (file.endsWith(QLatin1String(".a")) || file.endsWith(QLatin1String(".lib")))
+ if (file.startsWith("libQtCore")
+ || file.startsWith("libQt5Core")
+ || file.startsWith("QtCore")
+ || file.startsWith("Qt5Core")) {
+ if (file.endsWith(".a") || file.endsWith(".lib"))
staticLibs.append(FileName(info));
- else if (file.endsWith(QLatin1String(".dll"))
+ else if (file.endsWith(".dll")
|| file.endsWith(QString::fromLatin1(".so.") + versionString)
- || file.endsWith(QLatin1String(".so"))
- || file.endsWith(QLatin1Char('.') + versionString + QLatin1String(".dylib")))
+ || file.endsWith(".so")
+ || file.endsWith(QLatin1Char('.') + versionString + ".dylib"))
dynamicLibs.append(FileName(info));
}
}
diff --git a/src/tools/clangbackend/source/clangbackend_global.h b/src/tools/clangbackend/source/clangbackend_global.h
index e62c9a35130..a7ca7859587 100644
--- a/src/tools/clangbackend/source/clangbackend_global.h
+++ b/src/tools/clangbackend/source/clangbackend_global.h
@@ -36,4 +36,9 @@ enum class PreferredTranslationUnit
LastUninitialized,
};
+// CLANG-UPGRADE-CHECK: Remove IS_SUSPEND_SUPPORTED once we require clang >= 7.0
+#if defined(CINDEX_VERSION_HAS_PRETTYDECL_BACKPORTED) || CINDEX_VERSION_MINOR >= 47
+# define IS_PRETTY_DECL_SUPPORTED
+#endif
+
} // namespace ClangBackEnd
diff --git a/src/tools/clangbackend/source/clangbackendclangipc-source.pri b/src/tools/clangbackend/source/clangbackendclangipc-source.pri
index 3460e9d297d..154b5cc0194 100644
--- a/src/tools/clangbackend/source/clangbackendclangipc-source.pri
+++ b/src/tools/clangbackend/source/clangbackendclangipc-source.pri
@@ -29,10 +29,12 @@ HEADERS += \
$$PWD/clangreparsesupportivetranslationunitjob.h \
$$PWD/clangrequestdocumentannotationsjob.h \
$$PWD/clangrequestreferencesjob.h \
+ $$PWD/clangrequesttooltipjob.h \
$$PWD/clangresumedocumentjob.h \
$$PWD/clangstring.h \
$$PWD/clangsupportivetranslationunitinitializer.h \
$$PWD/clangsuspenddocumentjob.h \
+ $$PWD/clangtooltipinfocollector.h \
$$PWD/clangtranslationunit.h \
$$PWD/clangtranslationunits.h \
$$PWD/clangtranslationunitupdater.h \
@@ -86,8 +88,10 @@ SOURCES += \
$$PWD/clangreparsesupportivetranslationunitjob.cpp \
$$PWD/clangrequestdocumentannotationsjob.cpp \
$$PWD/clangrequestreferencesjob.cpp \
+ $$PWD/clangrequesttooltipjob.cpp \
$$PWD/clangsuspenddocumentjob.cpp \
$$PWD/clangsupportivetranslationunitinitializer.cpp \
+ $$PWD/clangtooltipinfocollector.cpp \
$$PWD/clangtranslationunit.cpp \
$$PWD/clangtranslationunits.cpp \
$$PWD/clangtranslationunitupdater.cpp \
diff --git a/src/tools/clangbackend/source/clangcodemodelserver.cpp b/src/tools/clangbackend/source/clangcodemodelserver.cpp
index 6e3a4290b80..609ac1da85c 100644
--- a/src/tools/clangbackend/source/clangcodemodelserver.cpp
+++ b/src/tools/clangbackend/source/clangcodemodelserver.cpp
@@ -258,6 +258,7 @@ static void fillJobRequest(JobRequest &jobRequest, const MessageType &message)
jobRequest.line = message.line();
jobRequest.column = message.column();
jobRequest.ticketNumber = message.ticketNumber();
+ jobRequest.textCodecName = message.fileContainer().textCodecName();
// The unsaved files might get updater later, so take the current
// revision for the request.
jobRequest.documentRevision = message.fileContainer().documentRevision();
@@ -303,6 +304,25 @@ void ClangCodeModelServer::requestFollowSymbol(const RequestFollowSymbolMessage
}
}
+void ClangCodeModelServer::requestToolTip(const RequestToolTipMessage &message)
+{
+ TIME_SCOPE_DURATION("ClangCodeModelServer::requestToolTip");
+
+ try {
+ const Document document = documents.document(message.fileContainer().filePath(),
+ message.fileContainer().projectPartId());
+ DocumentProcessor processor = documentProcessors().processor(document);
+
+ JobRequest jobRequest = processor.createJobRequest(JobRequest::Type::RequestToolTip);
+ fillJobRequest(jobRequest, message);
+
+ processor.addJob(jobRequest);
+ processor.process();
+ } catch (const std::exception &exception) {
+ qWarning() << "Error in ClangCodeModelServer::requestToolTip:" << exception.what();
+ }
+}
+
void ClangCodeModelServer::updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message)
{
qCDebug(serverLog) << "########## updateVisibleTranslationUnits";
diff --git a/src/tools/clangbackend/source/clangcodemodelserver.h b/src/tools/clangbackend/source/clangcodemodelserver.h
index 1591d25743c..98508f348c5 100644
--- a/src/tools/clangbackend/source/clangcodemodelserver.h
+++ b/src/tools/clangbackend/source/clangcodemodelserver.h
@@ -61,6 +61,7 @@ public:
void requestDocumentAnnotations(const RequestDocumentAnnotationsMessage &message) override;
void requestReferences(const RequestReferencesMessage &message) override;
void requestFollowSymbol(const RequestFollowSymbolMessage &message) override;
+ void requestToolTip(const RequestToolTipMessage &message) override;
public: // for tests
const Documents &documentsForTestOnly() const;
diff --git a/src/tools/clangbackend/source/clangjobrequest.cpp b/src/tools/clangbackend/source/clangjobrequest.cpp
index b40ea424257..e2c6f8f6d49 100644
--- a/src/tools/clangbackend/source/clangjobrequest.cpp
+++ b/src/tools/clangbackend/source/clangjobrequest.cpp
@@ -32,6 +32,7 @@
#include "clangreparsesupportivetranslationunitjob.h"
#include "clangrequestdocumentannotationsjob.h"
#include "clangrequestreferencesjob.h"
+#include "clangrequesttooltipjob.h"
#include "clangresumedocumentjob.h"
#include "clangsuspenddocumentjob.h"
#include "clangupdatedocumentannotationsjob.h"
@@ -40,6 +41,7 @@
#include
#include
#include
+#include
#include
@@ -62,6 +64,7 @@ static const char *JobRequestTypeToText(JobRequest::Type type)
RETURN_TEXT_FOR_CASE(RequestDocumentAnnotations);
RETURN_TEXT_FOR_CASE(RequestReferences);
RETURN_TEXT_FOR_CASE(FollowSymbol);
+ RETURN_TEXT_FOR_CASE(RequestToolTip);
RETURN_TEXT_FOR_CASE(SuspendDocument);
RETURN_TEXT_FOR_CASE(ResumeDocument);
}
@@ -126,6 +129,7 @@ static JobRequest::ExpirationConditions expirationConditionsForType(JobRequest::
return Conditions(Condition::AnythingChanged);
case Type::RequestReferences:
case Type::RequestDocumentAnnotations:
+ case Type::RequestToolTip:
case Type::FollowSymbol:
return Conditions(Condition::DocumentClosed)
| Conditions(Condition::DocumentRevisionChanged);
@@ -153,8 +157,10 @@ static JobRequest::RunConditions conditionsForType(JobRequest::Type type)
Conditions conditions = Conditions(Condition::DocumentUnsuspended)
| Conditions(Condition::DocumentVisible);
- if (type == Type::RequestReferences || type == Type::FollowSymbol)
+ if (type == Type::RequestReferences || type == Type::FollowSymbol
+ || type == Type::RequestToolTip) {
conditions |= Condition::CurrentDocumentRevision;
+ }
if (type != Type::UpdateDocumentAnnotations && type != Type::ParseSupportiveTranslationUnit)
conditions |= Condition::DocumentParsed;
@@ -192,6 +198,8 @@ IAsyncJob *JobRequest::createJob() const
return new RequestDocumentAnnotationsJob();
case JobRequest::Type::RequestReferences:
return new RequestReferencesJob();
+ case JobRequest::Type::RequestToolTip:
+ return new RequestToolTipJob();
case JobRequest::Type::FollowSymbol:
return new FollowSymbolJob();
case JobRequest::Type::SuspendDocument:
@@ -224,6 +232,11 @@ void JobRequest::cancelJob(ClangCodeModelClientInterface &client) const
false,
ticketNumber));
break;
+ case JobRequest::Type::RequestToolTip:
+ client.tooltip(ToolTipMessage(FileContainer(),
+ ToolTipInfo(),
+ ticketNumber));
+ break;
case JobRequest::Type::CompleteCode:
client.codeCompleted(CodeCompletedMessage(CodeCompletions(),
CompletionCorrection::NoCorrection,
diff --git a/src/tools/clangbackend/source/clangjobrequest.h b/src/tools/clangbackend/source/clangjobrequest.h
index 4b79f22792e..81c78ba9dbc 100644
--- a/src/tools/clangbackend/source/clangjobrequest.h
+++ b/src/tools/clangbackend/source/clangjobrequest.h
@@ -59,6 +59,7 @@ public:
RequestDocumentAnnotations,
RequestReferences,
FollowSymbol,
+ RequestToolTip,
SuspendDocument,
ResumeDocument,
@@ -118,6 +119,7 @@ public:
qint32 funcNameStartLine = -1;
qint32 funcNameStartColumn = -1;
quint64 ticketNumber = 0;
+ Utf8String textCodecName;
bool localReferences = false;
};
diff --git a/src/tools/clangbackend/source/clangrequesttooltipjob.cpp b/src/tools/clangbackend/source/clangrequesttooltipjob.cpp
new file mode 100644
index 00000000000..0e1249e4e8b
--- /dev/null
+++ b/src/tools/clangbackend/source/clangrequesttooltipjob.cpp
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "clangrequesttooltipjob.h"
+
+#include
+#include
+#include
+
+#include
+
+namespace ClangBackEnd {
+
+IAsyncJob::AsyncPrepareResult RequestToolTipJob::prepareAsyncRun()
+{
+ const JobRequest jobRequest = context().jobRequest;
+ QTC_ASSERT(jobRequest.type == JobRequest::Type::RequestToolTip, return AsyncPrepareResult());
+ QTC_ASSERT(acquireDocument(), return AsyncPrepareResult());
+
+ const TranslationUnit translationUnit = *m_translationUnit;
+ const UnsavedFiles unsavedFiles = *context().unsavedFiles;
+ const quint32 line = jobRequest.line;
+ const quint32 column = jobRequest.column;
+ const Utf8String textCodecName = jobRequest.textCodecName;
+ setRunner([translationUnit, unsavedFiles, line, column, textCodecName]() {
+ TIME_SCOPE_DURATION("RequestToolTipJobRunner");
+
+ UnsavedFiles theUnsavedFiles = unsavedFiles;
+ return translationUnit.tooltip(theUnsavedFiles, textCodecName, line, column);
+ });
+ return AsyncPrepareResult{translationUnit.id()};
+}
+
+void RequestToolTipJob::finalizeAsyncRun()
+{
+ if (!context().isOutdated()) {
+ const AsyncResult result = asyncResult();
+
+ context().client->tooltip(ToolTipMessage(m_pinnedFileContainer,
+ result,
+ context().jobRequest.ticketNumber));
+ }
+}
+
+} // namespace ClangBackEnd
diff --git a/src/tools/clangbackend/source/clangrequesttooltipjob.h b/src/tools/clangbackend/source/clangrequesttooltipjob.h
new file mode 100644
index 00000000000..eb121402cb2
--- /dev/null
+++ b/src/tools/clangbackend/source/clangrequesttooltipjob.h
@@ -0,0 +1,43 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include
+
+#include "clangdocumentjob.h"
+
+namespace ClangBackEnd {
+
+class RequestToolTipJob : public DocumentJob
+{
+public:
+ using AsyncResult = ToolTipInfo;
+
+ AsyncPrepareResult prepareAsyncRun() override;
+ void finalizeAsyncRun() override;
+};
+
+} // namespace ClangBackEnd
diff --git a/src/tools/clangbackend/source/clangtooltipinfocollector.cpp b/src/tools/clangbackend/source/clangtooltipinfocollector.cpp
new file mode 100644
index 00000000000..44dde1edbbc
--- /dev/null
+++ b/src/tools/clangbackend/source/clangtooltipinfocollector.cpp
@@ -0,0 +1,531 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "clangtooltipinfocollector.h"
+
+#include "clangbackend_global.h"
+#include "clangstring.h"
+#include "cursor.h"
+#include "sourcerange.h"
+#include "unsavedfiles.h"
+#include "unsavedfile.h"
+
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+#include
+
+namespace ClangBackEnd {
+
+namespace {
+
+Utf8StringVector qualificationPrefixAsVector(const Cursor &cursor)
+{
+ Utf8StringVector result;
+
+ for (Cursor parent = cursor.semanticParent();
+ parent.isValid() && (parent.kind() == CXCursor_Namespace || parent.isCompoundType());
+ parent = parent.semanticParent()) {
+ result.prepend(parent.spelling());
+ }
+
+ return result;
+}
+
+Utf8String qualificationPrefix(const Cursor &cursor)
+{
+ // TODO: Implement with qualificationPrefixAsVector()
+ Utf8String qualifiedName;
+
+ for (Cursor parent = cursor.semanticParent();
+ parent.isValid() && (parent.kind() == CXCursor_Namespace);
+ parent = parent.semanticParent()) {
+ qualifiedName = parent.spelling() + Utf8StringLiteral("::") + qualifiedName;
+ }
+
+ return qualifiedName;
+}
+
+Utf8String displayName(const Cursor &cursor)
+{
+ if (cursor.kind() == CXCursor_ClassTemplate) {
+ // TODO: The qualification should be part of the display name. Fix this in libclang.
+ return qualificationPrefix(cursor) + cursor.displayName();
+ }
+
+ return cursor.displayName();
+}
+
+Utf8String textForFunctionLike(const Cursor &cursor)
+{
+#ifdef IS_PRETTY_DECL_SUPPORTED
+ CXPrintingPolicy policy = clang_getCursorPrintingPolicy(cursor.cx());
+ clang_PrintingPolicy_setProperty(policy, CXPrintingPolicy_FullyQualifiedName, 1);
+ clang_PrintingPolicy_setProperty(policy, CXPrintingPolicy_TerseOutput, 1);
+ // Avoid printing attributes/pragmas
+ clang_PrintingPolicy_setProperty(policy, CXPrintingPolicy_PolishForDeclaration, 1);
+ clang_PrintingPolicy_setProperty(policy, CXPrintingPolicy_SuppressInitializers, 1);
+ const Utf8String prettyPrinted = ClangString(
+ clang_getCursorPrettyPrinted(cursor.cx(), policy));
+ clang_PrintingPolicy_dispose(policy);
+ return prettyPrinted;
+#else
+ // Printing function declarations with displayName() is quite limited:
+ // * result type is not included
+ // * parameter names are not included
+ // * templates in the result type are not included
+ // * no full qualification of the function name
+ return Utf8String(cursor.resultType().spelling())
+ + Utf8StringLiteral(" ")
+ + qualificationPrefix(cursor)
+ + Utf8String(cursor.displayName());
+#endif
+}
+
+Utf8String textForEnumConstantDecl(const Cursor &cursor)
+{
+ const Cursor semanticParent = cursor.semanticParent();
+ QTC_ASSERT(semanticParent.kind() == CXCursor_EnumDecl, return Utf8String());
+
+ const Type enumType = semanticParent.enumType();
+ if (enumType.isUnsigned())
+ return Utf8String::number(cursor.enumConstantUnsignedValue());
+ return Utf8String::number(cursor.enumConstantValue());
+}
+
+Utf8String textForInclusionDirective(const Cursor &cursor)
+{
+ const CXFile includedFile = cursor.includedFile();
+ const Utf8String fileName = ClangString(clang_getFileName(includedFile));
+
+ return QDir::toNativeSeparators(fileName.toString());
+}
+
+Utf8String textForAnyTypeAlias(const Cursor &cursor)
+{
+ // For a CXCursor_TypeAliasTemplateDecl the type of cursor/referenced
+ // is invalid, so we do not get the underlying type. This here solely
+ // reports the unresolved name instead of the empty string.
+ if (cursor.kind() == CXCursor_TypeAliasTemplateDecl)
+ return cursor.displayName();
+
+ return cursor.type().alias().utf8Spelling();
+}
+
+bool includeSizeForCursor(const Cursor &cursor)
+{
+ return cursor.isCompoundType()
+ || cursor.kind() == CXCursor_EnumDecl
+ || cursor.kind() == CXCursor_UnionDecl
+ || cursor.kind() == CXCursor_FieldDecl;
+}
+
+Utf8String sizeInBytes(const Cursor &cursor)
+{
+ if (includeSizeForCursor(cursor)) {
+ bool ok = false;
+ const long long size = cursor.type().sizeOf(&ok);
+ if (ok)
+ return Utf8String::number(size);
+ }
+
+ return Utf8String();
+}
+
+Cursor referencedCursor(const Cursor &cursor)
+{
+ // Query the referenced cursor directly instead of first testing with cursor.isReference().
+ // cursor.isReference() reports false for e.g. CXCursor_DeclRefExpr or CXCursor_CallExpr
+ // although it returns a valid cursor.
+ const Cursor referenced = cursor.referenced();
+ if (referenced.isValid())
+ return referenced;
+
+ const Cursor definition = cursor.definition();
+ if (definition.isValid())
+ return definition;
+
+ return cursor;
+}
+
+class ToolTipInfoCollector
+{
+public:
+ ToolTipInfoCollector(UnsavedFiles &unsavedFiles,
+ const Utf8String &textCodecName,
+ const Utf8String &mainFilePath,
+ CXTranslationUnit cxTranslationUnit);
+
+ ToolTipInfo collect(uint line, uint column) const;
+
+private:
+ Utf8String text(const Cursor &cursor, const Cursor &referenced) const;
+ Utf8String textForMacroExpansion(const Cursor &cursor) const;
+ Utf8String textForNamespaceAlias(const Cursor &cursor) const;
+
+ ToolTipInfo qDocInfo(const Cursor &cursor) const;
+
+ CXSourceLocation toCXSourceLocation(uint line, uint column) const;
+
+ UnsavedFile unsavedFile(const Utf8String &filePath) const;
+ Utf8String lineRange(const Utf8String &filePath, unsigned fromLine, unsigned toLine) const;
+
+private:
+ UnsavedFiles &m_unsavedFiles;
+ const Utf8String m_textCodecName;
+
+ const Utf8String m_mainFilePath;
+ CXTranslationUnit m_cxTranslationUnit = nullptr;
+
+};
+
+ToolTipInfoCollector::ToolTipInfoCollector(UnsavedFiles &unsavedFiles,
+ const Utf8String &textCodecName,
+ const Utf8String &mainFilePath,
+ CXTranslationUnit cxTranslationUnit)
+ : m_unsavedFiles(unsavedFiles)
+ , m_textCodecName(textCodecName)
+ , m_mainFilePath(mainFilePath)
+ , m_cxTranslationUnit(cxTranslationUnit)
+{
+}
+
+Utf8String ToolTipInfoCollector::text(const Cursor &cursor, const Cursor &referenced) const
+{
+ if (cursor.kind() == CXCursor_MacroExpansion)
+ return textForMacroExpansion(referenced);
+
+ if (referenced.kind() == CXCursor_EnumConstantDecl)
+ return textForEnumConstantDecl(referenced);
+
+ if (referenced.kind() == CXCursor_InclusionDirective)
+ return textForInclusionDirective(referenced);
+
+ if (referenced.kind() == CXCursor_Namespace)
+ return qualificationPrefix(referenced) + referenced.spelling();
+
+ if (referenced.kind() == CXCursor_NamespaceAlias)
+ return textForNamespaceAlias(referenced);
+
+ if (referenced.isAnyTypeAlias())
+ return textForAnyTypeAlias(referenced);
+
+ if (referenced.isFunctionLike())
+ return textForFunctionLike(referenced);
+
+ if (referenced.type().canonical().isBuiltinType())
+ return referenced.type().canonical().builtinTypeToString();
+
+ if (referenced.kind() == CXCursor_VarDecl)
+ return referenced.type().spelling(); // e.g. "Zii"
+
+ const Type referencedType = referenced.type();
+ if (referencedType.isValid()) {
+ // Generally, the type includes the qualification but has this limitations:
+ // * namespace aliases are not resolved
+ // * outer class of a inner template class is not included
+ // The type includes the qualification, but not resolved namespace aliases.
+
+ // For a CXType_Record, this also includes e.g. "const " as prefix.
+ return referencedType.canonical().utf8Spelling();
+ }
+
+ return displayName(referenced);
+}
+
+Utf8String ToolTipInfoCollector::textForMacroExpansion(const Cursor &cursor) const
+{
+ QTC_ASSERT(cursor.kind() == CXCursor_MacroDefinition, return Utf8String());
+
+ const SourceRange sourceRange = cursor.sourceRange();
+ const SourceLocation start = sourceRange.start();
+ const SourceLocation end = sourceRange.end();
+
+ return lineRange(start.filePath(), start.line(), end.line());
+}
+
+Utf8String ToolTipInfoCollector::textForNamespaceAlias(const Cursor &cursor) const
+{
+ // TODO: Add some libclang API to get the aliased name straight away.
+
+ CXToken *cxTokens = nullptr;
+ uint cxTokenCount = 0;
+
+ clang_tokenize(m_cxTranslationUnit, cursor.cxSourceRange(), &cxTokens, &cxTokenCount);
+
+ Utf8String aliasedName;
+ // Start at 3 in order to skip these tokens: namespace X =
+ for (uint i = 3; i < cxTokenCount; ++i)
+ aliasedName += ClangString(clang_getTokenSpelling(m_cxTranslationUnit, cxTokens[i]));
+
+ clang_disposeTokens(m_cxTranslationUnit, cxTokens, cxTokenCount);
+
+ return aliasedName;
+}
+
+static Utf8String typeName(const Type &type)
+{
+ return type.declaration().spelling();
+}
+
+static Utf8String qdocMark(const Cursor &cursor)
+{
+ if (cursor.kind() == CXCursor_ClassTemplate)
+ return cursor.spelling();
+
+ if (cursor.type().kind() == CXType_Enum
+ || cursor.type().kind() == CXType_Typedef
+ || cursor.type().kind() == CXType_Record)
+ return typeName(cursor.type());
+
+ Utf8String text = cursor.displayName();
+ if (cursor.kind() == CXCursor_FunctionDecl) {
+ // TODO: Remove this workaround by fixing this in
+ // libclang with the help of CXPrintingPolicy.
+ text.replace(Utf8StringLiteral("<>"), Utf8String());
+ }
+
+ return text;
+}
+
+static ToolTipInfo::QdocCategory qdocCategory(const Cursor &cursor)
+{
+ if (cursor.isFunctionLike())
+ return ToolTipInfo::Function;
+
+ if (cursor.kind() == CXCursor_MacroDefinition)
+ return ToolTipInfo::Macro;
+
+ if (cursor.kind() == CXCursor_EnumConstantDecl)
+ return ToolTipInfo::Enum;
+
+ if (cursor.type().kind() == CXType_Enum)
+ return ToolTipInfo::Enum;
+
+ if (cursor.kind() == CXCursor_InclusionDirective)
+ return ToolTipInfo::Brief;
+
+ // TODO: Handle CXCursor_NamespaceAlias, too?!
+ if (cursor.kind() == CXCursor_Namespace)
+ return ToolTipInfo::ClassOrNamespace;
+
+ if (cursor.isCompoundType())
+ return ToolTipInfo::ClassOrNamespace;
+
+ if (cursor.kind() == CXCursor_NamespaceAlias)
+ return ToolTipInfo::ClassOrNamespace;
+
+ if (cursor.type().kind() == CXType_Typedef)
+ return ToolTipInfo::Typedef;
+
+ if (cursor.type().kind() == CXType_Record)
+ return ToolTipInfo::ClassOrNamespace;
+
+ if (cursor.kind() == CXCursor_TypeAliasTemplateDecl)
+ return ToolTipInfo::Typedef;
+
+ if (cursor.kind() == CXCursor_ClassTemplate)
+ return ToolTipInfo::ClassOrNamespace;
+
+ return ToolTipInfo::Unknown;
+}
+
+static Utf8String name(const Cursor &cursor)
+{
+ if (cursor.type().kind() == CXType_Record || cursor.kind() == CXCursor_EnumDecl)
+ return typeName(cursor.type());
+
+ return cursor.spelling();
+}
+
+static Utf8StringVector qDocIdCandidates(const Cursor &cursor)
+{
+ Utf8StringVector components = qualificationPrefixAsVector(cursor);
+ if (components.isEmpty())
+ return { name(cursor) };
+
+ components << name(cursor);
+ Utf8StringVector result;
+ Utf8String name;
+ for (auto it = components.rbegin(); it != components.rend(); ++it) {
+ if (name.isEmpty())
+ name = *it;
+ else
+ name = *it + (Utf8StringLiteral("::") + name);
+
+ result.prepend(name);
+ }
+
+ return result;
+}
+
+// TODO: Add libclang API for this?!
+static bool isBuiltinOrPointerToBuiltin(const Type &type)
+{
+ Type theType = type;
+
+ if (theType.isBuiltinType())
+ return true;
+
+ // TODO: Simplify
+ // TODO: Test with **
+ while (theType.pointeeType().isValid()) {
+ theType = theType.pointeeType();
+ if (theType.isBuiltinType())
+ return true;
+ }
+
+ return false;
+}
+
+ToolTipInfo ToolTipInfoCollector::qDocInfo(const Cursor &cursor) const
+{
+ ToolTipInfo result;
+
+ if (isBuiltinOrPointerToBuiltin(cursor.type()))
+ return result;
+
+ result.setQdocIdCandidates(qDocIdCandidates(cursor));
+ result.setQdocMark(qdocMark(cursor));
+ result.setQdocCategory(qdocCategory(cursor));
+
+ if (cursor.type().kind() == CXType_Record) {
+ result.setQdocIdCandidates(qDocIdCandidates(cursor.type().declaration()));
+ return result;
+ }
+
+ if (cursor.kind() == CXCursor_VarDecl || cursor.kind() == CXCursor_FieldDecl) {
+ result.setQdocMark(typeName(cursor.type()));
+ // maybe template instantiation
+ if (cursor.type().kind() == CXType_Unexposed && cursor.type().canonical().kind() == CXType_Record) {
+ result.setQdocIdCandidates(qDocIdCandidates(cursor.type().canonical().declaration()));
+ result.setQdocCategory(ToolTipInfo::ClassOrNamespace);
+ return result;
+ }
+ }
+
+ // TODO: Handle also RValueReference()
+ if (cursor.type().isLValueReference()) {
+ const Cursor pointeeTypeDeclaration = cursor.type().pointeeType().declaration();
+ result.setQdocIdCandidates(qDocIdCandidates(pointeeTypeDeclaration));
+ result.setQdocMark(pointeeTypeDeclaration.spelling());
+ result.setQdocCategory(qdocCategory(pointeeTypeDeclaration));
+ return result;
+ }
+
+ return result;
+}
+
+CXSourceLocation ToolTipInfoCollector::toCXSourceLocation(uint line, uint column) const
+{
+ return clang_getLocation(m_cxTranslationUnit,
+ clang_getFile(m_cxTranslationUnit,
+ m_mainFilePath.constData()),
+ line,
+ column);
+}
+
+UnsavedFile ToolTipInfoCollector::unsavedFile(const Utf8String &filePath) const
+{
+ const UnsavedFile &unsavedFile = m_unsavedFiles.unsavedFile(filePath);
+ if (!unsavedFile.filePath().isEmpty())
+ return unsavedFile;
+
+ // Create an unsaved file with the file content from disk.
+ // TODO: Make use of clang_getFileContents() instead of reading from disk.
+ QTextCodec *codec = QTextCodec::codecForName(m_textCodecName);
+ QByteArray fileContent;
+ QString errorString;
+ using namespace Utils;
+ const TextFileFormat::ReadResult readResult
+ = TextFileFormat::readFileUTF8(filePath.toString(), codec, &fileContent, &errorString);
+ if (readResult != TextFileFormat::ReadSuccess) {
+ qWarning() << "Failed to read file" << filePath << ":" << errorString;
+ return UnsavedFile();
+ }
+
+ return UnsavedFile(filePath, Utf8String::fromByteArray(fileContent));
+}
+
+Utf8String ToolTipInfoCollector::lineRange(const Utf8String &filePath,
+ unsigned fromLine,
+ unsigned toLine) const
+{
+ if (toLine < fromLine)
+ return Utf8String();
+
+ const UnsavedFile file = unsavedFile(filePath);
+ if (file.fileContent().isEmpty())
+ return Utf8String();
+
+ return file.lineRange(fromLine, toLine);
+}
+
+ToolTipInfo ToolTipInfoCollector::collect(uint line, uint column) const
+{
+ const Cursor cursor = clang_getCursor(m_cxTranslationUnit, toCXSourceLocation(line, column));
+ if (!cursor.isValid())
+ return ToolTipInfo(); // E.g. cursor on ifdeffed out range
+
+ const Cursor referenced = referencedCursor(cursor);
+ QTC_CHECK(referenced.isValid());
+
+ ToolTipInfo info;
+ info.setText(text(cursor, referenced));
+ info.setBriefComment(referenced.briefComment());
+
+ {
+ ToolTipInfo qDocToolTipInfo = qDocInfo(referenced);
+ info.setQdocIdCandidates(qDocToolTipInfo.qdocIdCandidates());
+ info.setQdocMark(qDocToolTipInfo.qdocMark());
+ info.setQdocCategory(qDocToolTipInfo.qdocCategory());
+ }
+
+ info.setSizeInBytes(sizeInBytes(cursor));
+
+ return info;
+}
+
+} // anonymous namespace
+
+ToolTipInfo collectToolTipInfo(UnsavedFiles &unsavedFiles,
+ const Utf8String &textCodecName,
+ const Utf8String &mainFilePath,
+ CXTranslationUnit cxTranslationUnit,
+ uint line,
+ uint column)
+{
+ ToolTipInfoCollector collector(unsavedFiles, textCodecName, mainFilePath, cxTranslationUnit);
+ const ToolTipInfo info = collector.collect(line, column);
+
+ return info;
+}
+
+} // namespace ClangBackEnd
diff --git a/src/tools/clangbackend/source/clangtooltipinfocollector.h b/src/tools/clangbackend/source/clangtooltipinfocollector.h
new file mode 100644
index 00000000000..8009aa8bf4a
--- /dev/null
+++ b/src/tools/clangbackend/source/clangtooltipinfocollector.h
@@ -0,0 +1,45 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include
+
+#include
+
+#include
+
+namespace ClangBackEnd {
+
+class UnsavedFiles;
+
+ToolTipInfo collectToolTipInfo(UnsavedFiles &unsavedFiles,
+ const Utf8String &textCodecName,
+ const Utf8String &mainFilePath,
+ CXTranslationUnit cxTranslationUnit,
+ uint line,
+ uint column);
+
+} // namespace ClangBackEnd
diff --git a/src/tools/clangbackend/source/clangtranslationunit.cpp b/src/tools/clangbackend/source/clangtranslationunit.cpp
index 94827801136..da06999b614 100644
--- a/src/tools/clangbackend/source/clangtranslationunit.cpp
+++ b/src/tools/clangbackend/source/clangtranslationunit.cpp
@@ -27,6 +27,7 @@
#include "clangbackend_global.h"
#include "clangreferencescollector.h"
+#include "clangtooltipinfocollector.h"
#include "clangtranslationunitupdater.h"
#include "clangfollowsymbol.h"
#include "clangfollowsymboljob.h"
@@ -139,6 +140,20 @@ void TranslationUnit::extractDocumentAnnotations(
skippedSourceRanges = this->skippedSourceRanges().toSourceRangeContainers();
}
+
+ToolTipInfo TranslationUnit::tooltip(UnsavedFiles &unsavedFiles,
+ const Utf8String &textCodecName,
+ uint line,
+ uint column) const
+{
+ return collectToolTipInfo(unsavedFiles,
+ textCodecName,
+ filePath(),
+ m_cxTranslationUnit,
+ line,
+ column);
+}
+
ReferencesResult TranslationUnit::references(uint line, uint column, bool localReferences) const
{
return collectReferences(m_cxTranslationUnit, line, column, localReferences);
diff --git a/src/tools/clangbackend/source/clangtranslationunit.h b/src/tools/clangbackend/source/clangtranslationunit.h
index 76ba3f00551..150645cc362 100644
--- a/src/tools/clangbackend/source/clangtranslationunit.h
+++ b/src/tools/clangbackend/source/clangtranslationunit.h
@@ -41,6 +41,7 @@ class SkippedSourceRanges;
class SourceLocation;
class SourceRange;
class SourceRangeContainer;
+class ToolTipInfo;
class TranslationUnitUpdateInput;
class TranslationUnitUpdateResult;
class UnsavedFiles;
@@ -86,6 +87,10 @@ public:
ReferencesResult references(uint line, uint column, bool localReferences = false) const;
+ ToolTipInfo tooltip(UnsavedFiles &unsavedFiles,
+ const Utf8String &textCodecName,
+ uint line,
+ uint column) const;
DiagnosticSet diagnostics() const;
SourceLocation sourceLocationAt(uint line, uint column) const;
diff --git a/src/tools/clangbackend/source/clangtype.cpp b/src/tools/clangbackend/source/clangtype.cpp
index 8fc09d8c4af..970b9452c1d 100644
--- a/src/tools/clangbackend/source/clangtype.cpp
+++ b/src/tools/clangbackend/source/clangtype.cpp
@@ -84,6 +84,16 @@ bool Type::isBuiltinType() const
return cxType.kind >= CXType_FirstBuiltin && cxType.kind <= CXType_LastBuiltin;
}
+bool Type::isUnsigned() const
+{
+ return cxType.kind == CXType_UChar
+ || cxType.kind == CXType_UShort
+ || cxType.kind == CXType_UInt
+ || cxType.kind == CXType_ULong
+ || cxType.kind == CXType_ULongLong
+ || cxType.kind == CXType_UInt128;
+}
+
Utf8String Type::utf8Spelling() const
{
return ClangString(clang_getTypeSpelling(cxType));
@@ -94,6 +104,83 @@ ClangString Type::spelling() const
return ClangString(clang_getTypeSpelling(cxType));
}
+static const char *builtinTypeToText(CXTypeKind kind)
+{
+ // CLANG-UPGRADE-CHECK: Check for added built-in types.
+
+ switch (kind) {
+ case CXType_Void:
+ return "void";
+ case CXType_Bool:
+ return "bool";
+
+ // See also ${CLANG_REPOSITORY}/lib/Sema/SemaChecking.cpp - IsSameCharType().
+ case CXType_Char_U:
+ case CXType_UChar:
+ return "unsigned char";
+ case CXType_Char_S:
+ case CXType_SChar:
+ return "signed char";
+
+ case CXType_Char16:
+ return "char16_t";
+ case CXType_Char32:
+ return "char32_t";
+ case CXType_WChar:
+ return "wchar_t";
+
+ case CXType_UShort:
+ return "unsigned short";
+ case CXType_UInt:
+ return "unsigned int";
+ case CXType_ULong:
+ return "unsigned long";
+ case CXType_ULongLong:
+ return "unsigned long long";
+ case CXType_Short:
+ return "short";
+
+ case CXType_Int:
+ return "int";
+ case CXType_Long:
+ return "long";
+ case CXType_LongLong:
+ return "long long";
+
+ case CXType_Float:
+ return "float";
+ case CXType_Double:
+ return "double";
+ case CXType_LongDouble:
+ return "long double";
+
+ case CXType_NullPtr:
+ return "nullptr_t";
+
+ // https://gcc.gnu.org/onlinedocs/gcc/_005f_005fint128.html
+ case CXType_Int128: return "__int128";
+ case CXType_UInt128: return "unsigned __int128";
+
+ // https://gcc.gnu.org/onlinedocs/gcc/Floating-Types.html
+ case CXType_Float128: return "__float128";
+ // CLANG-UPGRADE-CHECK: CXType_Float16 available with >= clang-6.0:
+// case CXType_Float16: return "_Float16";
+
+ // https://www.khronos.org/registry/OpenCL/sdk/2.1/docs/man/xhtml/scalarDataTypes.html
+ case CXType_Half:
+ return "half";
+
+ default:
+ return "";
+ }
+}
+
+Utf8String Type::builtinTypeToString() const
+{
+ const char *text = builtinTypeToText(cxType.kind);
+ return Utf8String::fromByteArray(QByteArray::fromRawData(text, strlen(text)));
+}
+
int Type::argumentCount() const
{
return clang_getNumArgTypes(cxType);
@@ -129,6 +216,16 @@ Cursor Type::declaration() const
return clang_getTypeDeclaration(cxType);
}
+long long Type::sizeOf(bool *isValid) const
+{
+ const long long size = clang_Type_getSizeOf(cxType);
+ *isValid = size != CXTypeLayoutError_Invalid
+ && size != CXTypeLayoutError_Incomplete
+ && size != CXTypeLayoutError_Dependent;
+
+ return size;
+}
+
CXTypeKind Type::kind() const
{
return cxType.kind;
diff --git a/src/tools/clangbackend/source/clangtype.h b/src/tools/clangbackend/source/clangtype.h
index f26fe080f6b..6be968d3901 100644
--- a/src/tools/clangbackend/source/clangtype.h
+++ b/src/tools/clangbackend/source/clangtype.h
@@ -53,9 +53,11 @@ public:
bool isReferencingConstant() const;
bool isOutputArgument() const;
bool isBuiltinType() const;
+ bool isUnsigned() const;
Utf8String utf8Spelling() const;
ClangString spelling() const;
+ Utf8String builtinTypeToString() const;
int argumentCount() const;
Type alias() const;
@@ -66,6 +68,8 @@ public:
Cursor declaration() const;
+ long long sizeOf(bool *isValid) const;
+
CXTypeKind kind() const;
private:
diff --git a/src/tools/clangbackend/source/cursor.cpp b/src/tools/clangbackend/source/cursor.cpp
index 6ad50307cdd..41a0465e7a0 100644
--- a/src/tools/clangbackend/source/cursor.cpp
+++ b/src/tools/clangbackend/source/cursor.cpp
@@ -158,6 +158,14 @@ bool Cursor::isTemplateLike() const
Q_UNREACHABLE();
}
+bool Cursor::isAnyTypeAlias() const
+{
+ const CXCursorKind k = kind();
+ return k == CXCursor_TypeAliasDecl
+ || k == CXCursor_TypedefDecl
+ || k == CXCursor_TypeAliasTemplateDecl;
+}
+
bool Cursor::hasFinalFunctionAttribute() const
{
bool hasFinal = false;
@@ -248,11 +256,31 @@ Type Cursor::nonPointerTupe() const
return typeResult;
}
+Type Cursor::enumType() const
+{
+ return clang_getEnumDeclIntegerType(cxCursor);
+}
+
+long long Cursor::enumConstantValue() const
+{
+ return clang_getEnumConstantDeclValue(cxCursor);
+}
+
+unsigned long long Cursor::enumConstantUnsignedValue() const
+{
+ return clang_getEnumConstantDeclUnsignedValue(cxCursor);
+}
+
Cursor Cursor::specializedCursorTemplate() const
{
return clang_getSpecializedCursorTemplate(cxCursor);
}
+CXFile Cursor::includedFile() const
+{
+ return clang_getIncludedFile(cxCursor);
+}
+
SourceLocation Cursor::sourceLocation() const
{
return clang_getCursorLocation(cxCursor);
@@ -341,6 +369,11 @@ Cursor Cursor::functionBase() const
return functionBaseCursor;
}
+Type Cursor::resultType() const
+{
+ return clang_getResultType(type().cxType);
+}
+
Cursor Cursor::argument(int index) const
{
return clang_Cursor_getArgument(cxCursor, index);
@@ -398,6 +431,11 @@ CXCursorKind Cursor::kind() const
return clang_getCursorKind(cxCursor);
}
+CXCursor Cursor::cx() const
+{
+ return cxCursor;
+}
+
bool operator==(const Cursor &first, const Cursor &second)
{
return clang_equalCursors(first.cxCursor, second.cxCursor);
diff --git a/src/tools/clangbackend/source/cursor.h b/src/tools/clangbackend/source/cursor.h
index 6520366b36d..bef5a006f4f 100644
--- a/src/tools/clangbackend/source/cursor.h
+++ b/src/tools/clangbackend/source/cursor.h
@@ -67,6 +67,7 @@ public:
bool isFunctionLike() const;
bool isConstructorOrDestructor() const;
bool isTemplateLike() const;
+ bool isAnyTypeAlias() const;
bool hasFinalFunctionAttribute() const;
bool hasFinalClassAttribute() const;
bool isUnexposed() const;
@@ -81,6 +82,10 @@ public:
Type type() const;
Type nonPointerTupe() const;
+ Type enumType() const;
+
+ long long enumConstantValue() const;
+ unsigned long long enumConstantUnsignedValue() const;
SourceLocation sourceLocation() const;
CXSourceLocation cxSourceLocation() const;
@@ -92,17 +97,19 @@ public:
Cursor definition() const;
Cursor canonical() const;
- Cursor alias() const;
Cursor referenced() const;
Cursor semanticParent() const;
Cursor lexicalParent() const;
Cursor functionBaseDeclaration() const;
Cursor functionBase() const;
+ Type resultType() const;
Cursor argument(int index) const;
unsigned overloadedDeclarationsCount() const;
Cursor overloadedDeclaration(unsigned index) const;
Cursor specializedCursorTemplate() const;
+ CXFile includedFile() const;
+
void collectOutputArgumentRangesTo(
std::vector &outputArgumentRanges) const;
std::vector outputArgumentRanges() const;
@@ -112,6 +119,8 @@ public:
template
void visit(VisitorCallback visitorCallback) const;
+ CXCursor cx() const;
+
private:
CXCursor cxCursor;
};
diff --git a/src/tools/clangbackend/source/unsavedfile.cpp b/src/tools/clangbackend/source/unsavedfile.cpp
index 923d4456e8a..efcad4cad47 100644
--- a/src/tools/clangbackend/source/unsavedfile.cpp
+++ b/src/tools/clangbackend/source/unsavedfile.cpp
@@ -30,6 +30,8 @@
#include
+#include
+
namespace ClangBackEnd {
UnsavedFile::UnsavedFile()
@@ -79,6 +81,25 @@ bool UnsavedFile::hasCharacterAt(uint line, uint column, char character) const
return positionIsOk && hasCharacterAt(utf8Position, character);
}
+Utf8String UnsavedFile::lineRange(uint fromLine, uint toLine) const
+{
+ QTC_ASSERT(fromLine <= toLine, return Utf8String());
+
+ // Find start of first line
+ bool ok = false;
+ const uint fromPosition = toUtf8Position(fromLine, 1, &ok);
+ QTC_ASSERT(ok, return Utf8String());
+
+ // Find end of last line
+ uint toPosition = toUtf8Position(toLine, 1, &ok);
+ QTC_ASSERT(ok, return Utf8String());
+ const uint endPosition = uint(m_fileContent.byteSize());
+ while (toPosition < endPosition && m_fileContent.constData()[toPosition] != '\n')
+ ++toPosition;
+
+ return m_fileContent.mid(int(fromPosition), int(toPosition - fromPosition));
+}
+
bool UnsavedFile::hasCharacterAt(uint position, char character) const
{
if (position < uint(m_fileContent.byteSize()))
diff --git a/src/tools/clangbackend/source/unsavedfile.h b/src/tools/clangbackend/source/unsavedfile.h
index 3021da89b5c..325139d240d 100644
--- a/src/tools/clangbackend/source/unsavedfile.h
+++ b/src/tools/clangbackend/source/unsavedfile.h
@@ -46,6 +46,7 @@ public:
// 1-based line and column
uint toUtf8Position(uint line, uint column, bool *ok) const;
bool hasCharacterAt(uint line, uint column, char character) const;
+ Utf8String lineRange(uint fromLine, uint toLine) const;
// 0-based position
bool hasCharacterAt(uint position, char character) const;
diff --git a/tests/unit/echoserver/echoclangcodemodelserver.cpp b/tests/unit/echoserver/echoclangcodemodelserver.cpp
index 2c0fea98c93..38d2d395444 100644
--- a/tests/unit/echoserver/echoclangcodemodelserver.cpp
+++ b/tests/unit/echoserver/echoclangcodemodelserver.cpp
@@ -98,6 +98,11 @@ void EchoClangCodeModelServer::requestFollowSymbol(const RequestFollowSymbolMess
echoMessage(message);
}
+void EchoClangCodeModelServer::requestToolTip(const RequestToolTipMessage &message)
+{
+ echoMessage(message);
+}
+
void EchoClangCodeModelServer::updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message)
{
echoMessage(message);
diff --git a/tests/unit/echoserver/echoclangcodemodelserver.h b/tests/unit/echoserver/echoclangcodemodelserver.h
index 2dc8c30913a..d3452d2d321 100644
--- a/tests/unit/echoserver/echoclangcodemodelserver.h
+++ b/tests/unit/echoserver/echoclangcodemodelserver.h
@@ -47,6 +47,7 @@ public:
void requestDocumentAnnotations(const RequestDocumentAnnotationsMessage &message) override;
void requestReferences(const RequestReferencesMessage &message) override;
void requestFollowSymbol(const RequestFollowSymbolMessage &message) override;
+ void requestToolTip(const RequestToolTipMessage &message) override;
void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message) override;
private:
diff --git a/tests/unit/unittest/clangtooltipinfo-test.cpp b/tests/unit/unittest/clangtooltipinfo-test.cpp
new file mode 100644
index 00000000000..4f6c9806496
--- /dev/null
+++ b/tests/unit/unittest/clangtooltipinfo-test.cpp
@@ -0,0 +1,628 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "googletest.h"
+#include "rundocumentparse-utility.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+
+using ::ClangBackEnd::ProjectPart;
+using ::ClangBackEnd::SourceLocationContainer;
+using ::ClangBackEnd::Document;
+using ::ClangBackEnd::UnsavedFiles;
+using ::ClangBackEnd::ToolTipInfo;
+using ::ClangBackEnd::SourceRangeContainer;
+
+namespace {
+
+#define CHECK_MEMBER(actual, expected, memberName) \
+ if (actual.memberName() != expected.memberName()) { \
+ *result_listener << #memberName " is " + PrintToString(actual.memberName()) \
+ << " and not " + PrintToString(expected.memberName()); \
+ return false; \
+ }
+
+MATCHER_P(IsToolTip, expected, std::string(negation ? "isn't" : "is") + PrintToString(expected))
+{
+ CHECK_MEMBER(arg, expected, text);
+ CHECK_MEMBER(arg, expected, briefComment);
+
+ CHECK_MEMBER(arg, expected, qdocIdCandidates);
+ CHECK_MEMBER(arg, expected, qdocMark);
+ CHECK_MEMBER(arg, expected, qdocCategory);
+
+ CHECK_MEMBER(arg, expected, sizeInBytes);
+
+ return true;
+}
+
+MATCHER_P(IsQdocToolTip, expected, std::string(negation ? "isn't" : "is") + PrintToString(expected))
+{
+ CHECK_MEMBER(arg, expected, qdocIdCandidates);
+ CHECK_MEMBER(arg, expected, qdocMark);
+ CHECK_MEMBER(arg, expected, qdocCategory);
+
+ return true;
+}
+
+#undef CHECK_MEMBER
+
+struct Data {
+ ProjectPart projectPart{Utf8StringLiteral("projectPartId"), {Utf8StringLiteral("-std=c++14")}};
+ ClangBackEnd::ProjectParts projects;
+ ClangBackEnd::UnsavedFiles unsavedFiles;
+ ClangBackEnd::Documents documents{projects, unsavedFiles};
+ Document document{Utf8StringLiteral(TESTDATA_DIR "/tooltipinfo.cpp"),
+ projectPart,
+ {},
+ documents};
+ UnitTest::RunDocumentParse _1{document};
+};
+
+class ToolTipInfo : public ::testing::Test
+{
+protected:
+ ::ToolTipInfo tooltip(uint line, uint column)
+ {
+ return d->document.translationUnit().tooltip(d->unsavedFiles,
+ Utf8StringLiteral("UTF-8"),
+ line,
+ column);
+ }
+
+ static void SetUpTestCase();
+ static void TearDownTestCase();
+
+private:
+ static std::unique_ptr d;
+};
+
+TEST_F(ToolTipInfo, LocalVariableInt)
+{
+ const ::ToolTipInfo actual = tooltip(3, 5);
+
+ ASSERT_THAT(actual, IsToolTip(::ToolTipInfo(Utf8StringLiteral("int"))));
+}
+
+TEST_F(ToolTipInfo, LocalVariablePointerToConstInt)
+{
+ const ::ToolTipInfo actual = tooltip(4, 5);
+
+ ASSERT_THAT(actual, IsToolTip(::ToolTipInfo(Utf8StringLiteral("const int *"))));
+}
+
+TEST_F(ToolTipInfo, LocalParameterVariableConstRefCustomType)
+{
+ ::ToolTipInfo expected(Utf8StringLiteral("const Foo &"));
+ expected.setQdocIdCandidates({Utf8StringLiteral("Foo")});
+ expected.setQdocMark(Utf8StringLiteral("Foo"));
+ expected.setQdocCategory(::ToolTipInfo::ClassOrNamespace);
+
+ const ::ToolTipInfo actual = tooltip(12, 12);
+
+ ASSERT_THAT(actual, IsToolTip(expected));
+}
+
+TEST_F(ToolTipInfo, LocalNonParameterVariableConstRefCustomType)
+{
+ ::ToolTipInfo expected(Utf8StringLiteral("const Foo"));
+ expected.setQdocIdCandidates({Utf8StringLiteral("Foo")});
+ expected.setQdocMark(Utf8StringLiteral("Foo"));
+ expected.setQdocCategory(::ToolTipInfo::ClassOrNamespace);
+
+ const ::ToolTipInfo actual = tooltip(14, 5);
+
+ ASSERT_THAT(actual, IsToolTip(expected));
+}
+
+TEST_F(ToolTipInfo, MemberVariable)
+{
+ const ::ToolTipInfo actual = tooltip(12, 16);
+
+ ASSERT_THAT(actual, IsToolTip(::ToolTipInfo(Utf8StringLiteral("int"))));
+}
+
+TEST_F(ToolTipInfo, DISABLED_WITHOUT_PRETTYDECL_PATCH(MemberFunctionCall_QualifiedName))
+{
+ const ::ToolTipInfo actual = tooltip(21, 9);
+
+ ASSERT_THAT(actual.text(), Utf8StringLiteral("int Bar::mem()"));
+}
+
+// ChangeLog: Show extra specifiers. For functions e.g.: virtual, inline, explicit, const, volatile
+TEST_F(ToolTipInfo, DISABLED_WITHOUT_PRETTYDECL_PATCH(MemberFunctionCall_ExtraSpecifiers))
+{
+ const ::ToolTipInfo actual = tooltip(22, 9);
+
+ ASSERT_THAT(actual.text(), Utf8StringLiteral("virtual int Bar::virtualConstMem() const"));
+}
+
+TEST_F(ToolTipInfo, MemberFunctionCall_qdocIdCandidates)
+{
+ const ::ToolTipInfo actual = tooltip(21, 9);
+
+ ASSERT_THAT(actual.qdocIdCandidates(), ElementsAre(Utf8StringLiteral("Bar::mem"),
+ Utf8StringLiteral("mem")));
+}
+
+TEST_F(ToolTipInfo, MemberFunctionCall_qdocMark_FIXLIBCLANG_CHECKED)
+{
+ const ::ToolTipInfo actual = tooltip(21, 9);
+
+ ASSERT_THAT(actual.qdocMark(), Utf8StringLiteral("mem()"));
+}
+
+// TODO: Check what is really needed for qdoc before implementing this one.
+TEST_F(ToolTipInfo, DISABLED_MemberFunctionCall_qdocMark_extraSpecifiers)
+{
+ const ::ToolTipInfo actual = tooltip(22, 9);
+
+ ASSERT_THAT(actual.qdocMark(), Utf8StringLiteral("virtualConstMem() const"));
+}
+
+TEST_F(ToolTipInfo, MemberFunctionCall_qdocCategory)
+{
+ const ::ToolTipInfo actual = tooltip(21, 9);
+
+ ASSERT_THAT(actual.qdocCategory(), ::ToolTipInfo::Function);
+}
+
+// TODO: Show the template parameter type, too: "template...)"
+TEST_F(ToolTipInfo, DISABLED_WITHOUT_PRETTYDECL_PATCH(TemplateFunctionCall))
+{
+ const ::ToolTipInfo actual = tooltip(30, 5);
+
+ ASSERT_THAT(actual.text(), Utf8StringLiteral("template<> void t(int foo)"));
+}
+
+TEST_F(ToolTipInfo, TemplateFunctionCall_qdocIdCandidates)
+{
+ const ::ToolTipInfo actual = tooltip(30, 5);
+
+ ASSERT_THAT(actual.qdocIdCandidates(), ElementsAre(Utf8StringLiteral("t")));
+}
+
+TEST_F(ToolTipInfo, TemplateFunctionCall_qdocMark_FIXLIBCLANG_CHECKED)
+{
+ const ::ToolTipInfo actual = tooltip(30, 5);
+
+ ASSERT_THAT(actual.qdocMark(), Utf8StringLiteral("t(int)"));
+}
+
+TEST_F(ToolTipInfo, TemplateFunctionCall_qdocCategory)
+{
+ const ::ToolTipInfo actual = tooltip(30, 5);
+
+ ASSERT_THAT(actual.qdocCategory(), ::ToolTipInfo::Function);
+}
+
+TEST_F(ToolTipInfo, BriefComment)
+{
+ const ::ToolTipInfo actual = tooltip(41, 5);
+
+ ASSERT_THAT(actual.briefComment(), Utf8StringLiteral("This is a crazy function."));
+}
+
+TEST_F(ToolTipInfo, Enum)
+{
+ ::ToolTipInfo expected(Utf8StringLiteral("EnumType"));
+ expected.setQdocIdCandidates({Utf8StringLiteral("EnumType")});
+ expected.setQdocMark(Utf8StringLiteral("EnumType"));
+ expected.setQdocCategory(::ToolTipInfo::Enum);
+
+ const ::ToolTipInfo actual = tooltip(49, 12);
+
+ ASSERT_THAT(actual, IsToolTip(expected));
+}
+
+TEST_F(ToolTipInfo, Enumerator)
+{
+ ::ToolTipInfo expected(Utf8StringLiteral("6"));
+ expected.setQdocIdCandidates({Utf8StringLiteral("Custom")});
+ expected.setQdocMark(Utf8StringLiteral("EnumType"));
+ expected.setQdocCategory(::ToolTipInfo::Enum);
+
+ const ::ToolTipInfo actual = tooltip(49, 22);
+
+ ASSERT_THAT(actual, IsToolTip(expected));
+}
+
+TEST_F(ToolTipInfo, TemplateTypeFromParameter)
+{
+ ::ToolTipInfo expected(Utf8StringLiteral("const Baz &"));
+ expected.setQdocIdCandidates({Utf8StringLiteral("Baz")});
+ expected.setQdocMark(Utf8StringLiteral("Baz"));
+ expected.setQdocCategory(::ToolTipInfo::ClassOrNamespace);
+
+ const ::ToolTipInfo actual = tooltip(55, 25);
+
+ ASSERT_THAT(actual, IsQdocToolTip(expected));
+}
+
+TEST_F(ToolTipInfo, TemplateTypeFromNonParameter)
+{
+ ::ToolTipInfo expected(Utf8StringLiteral("Baz"));
+ expected.setQdocIdCandidates({Utf8StringLiteral("Baz")});
+ expected.setQdocMark(Utf8StringLiteral("Baz"));
+ expected.setQdocCategory(::ToolTipInfo::ClassOrNamespace);
+
+ const ::ToolTipInfo actual = tooltip(56, 19);
+
+ ASSERT_THAT(actual, IsToolTip(expected));
+}
+
+TEST_F(ToolTipInfo, IncludeDirective)
+{
+ ::ToolTipInfo expected(Utf8StringLiteral(TESTDATA_DIR"/tooltipinfo.h"));
+ expected.setQdocIdCandidates({Utf8StringLiteral("tooltipinfo.h")});
+ expected.setQdocMark(Utf8StringLiteral("tooltipinfo.h"));
+ expected.setQdocCategory(::ToolTipInfo::Brief);
+
+ const ::ToolTipInfo actual = tooltip(59, 11);
+
+ ASSERT_THAT(actual, IsToolTip(expected));
+}
+
+TEST_F(ToolTipInfo, MacroUse_WithMacroFromSameFile)
+{
+ const ::ToolTipInfo actual = tooltip(66, 5);
+
+ ASSERT_THAT(actual.text(), Utf8StringLiteral("#define MACRO_FROM_MAINFILE(x) x + 3"));
+}
+
+TEST_F(ToolTipInfo, MacroUse_WithMacroFromHeader)
+{
+ const ::ToolTipInfo actual = tooltip(67, 5);
+
+ ASSERT_THAT(actual.text(), Utf8StringLiteral("#define MACRO_FROM_HEADER(x) x + \\\n x + \\\n x"));
+}
+
+TEST_F(ToolTipInfo, MacroUse_qdoc)
+{
+ ::ToolTipInfo expected;
+ expected.setQdocIdCandidates({Utf8StringLiteral("MACRO_FROM_MAINFILE")});
+ expected.setQdocMark(Utf8StringLiteral("MACRO_FROM_MAINFILE"));
+ expected.setQdocCategory(::ToolTipInfo::Macro);
+
+ const ::ToolTipInfo actual = tooltip(66, 5);
+
+ ASSERT_THAT(actual, IsQdocToolTip(expected));
+}
+
+TEST_F(ToolTipInfo, TypeNameIntroducedByUsingDirectiveIsQualified)
+{
+ ::ToolTipInfo expected(Utf8StringLiteral("N::Muu"));
+ expected.setQdocIdCandidates({Utf8StringLiteral("N::Muu"), Utf8StringLiteral("Muu")});
+ expected.setQdocMark(Utf8StringLiteral("Muu"));
+ expected.setQdocCategory(::ToolTipInfo::ClassOrNamespace);
+
+ const ::ToolTipInfo actual = tooltip(77, 5);
+
+ ASSERT_THAT(actual, IsToolTip(expected));
+}
+
+TEST_F(ToolTipInfo, TypeNameIntroducedByUsingDirectiveOfAliasIsResolvedAndQualified)
+{
+ ::ToolTipInfo expected(Utf8StringLiteral("N::Muu"));
+ expected.setQdocIdCandidates({Utf8StringLiteral("N::Muu"), Utf8StringLiteral("Muu")});
+ expected.setQdocMark(Utf8StringLiteral("Muu"));
+ expected.setQdocCategory(::ToolTipInfo::ClassOrNamespace);
+
+ const ::ToolTipInfo actual = tooltip(82, 5);
+
+ ASSERT_THAT(actual, IsToolTip(expected));
+}
+
+TEST_F(ToolTipInfo, TypeNameIntroducedByUsingDeclarationIsQualified)
+{
+ ::ToolTipInfo expected(Utf8StringLiteral("N::Muu"));
+ expected.setQdocIdCandidates({Utf8StringLiteral("N::Muu"), Utf8StringLiteral("Muu")});
+ expected.setQdocMark(Utf8StringLiteral("Muu"));
+ expected.setQdocCategory(::ToolTipInfo::ClassOrNamespace);
+
+ const ::ToolTipInfo actual = tooltip(87, 5);
+
+ ASSERT_THAT(actual, IsToolTip(expected));
+}
+
+TEST_F(ToolTipInfo, SizeForClassDefinition)
+{
+ const ::ToolTipInfo actual = tooltip(92, 8);
+
+ ASSERT_THAT(actual.sizeInBytes(), Utf8StringLiteral("2"));
+}
+
+TEST_F(ToolTipInfo, SizeForMemberField)
+{
+ const ::ToolTipInfo actual = tooltip(95, 10);
+
+ ASSERT_THAT(actual.sizeInBytes(), Utf8StringLiteral("1"));
+}
+
+TEST_F(ToolTipInfo, SizeForEnum)
+{
+ const ::ToolTipInfo actual = tooltip(97, 12);
+
+ ASSERT_THAT(actual.sizeInBytes(), Utf8StringLiteral("4"));
+}
+
+TEST_F(ToolTipInfo, SizeForUnion)
+{
+ const ::ToolTipInfo actual = tooltip(98, 7);
+
+ ASSERT_THAT(actual.sizeInBytes(), Utf8StringLiteral("1"));
+}
+
+TEST_F(ToolTipInfo, Namespace)
+{
+ ::ToolTipInfo expected(Utf8StringLiteral("X"));
+ expected.setQdocIdCandidates({Utf8StringLiteral("X")});
+ expected.setQdocMark(Utf8StringLiteral("X"));
+ expected.setQdocCategory(::ToolTipInfo::ClassOrNamespace);
+
+ const ::ToolTipInfo actual = tooltip(106, 11);
+
+ ASSERT_THAT(actual, IsToolTip(expected));
+}
+
+TEST_F(ToolTipInfo, NamespaceQualified)
+{
+ ::ToolTipInfo expected(Utf8StringLiteral("X::Y"));
+ expected.setQdocIdCandidates({Utf8StringLiteral("X::Y"), Utf8StringLiteral("Y")});
+ expected.setQdocMark(Utf8StringLiteral("Y"));
+ expected.setQdocCategory(::ToolTipInfo::ClassOrNamespace);
+
+ const ::ToolTipInfo actual = tooltip(107, 11);
+
+ ASSERT_THAT(actual, IsToolTip(expected));
+}
+
+// TODO: Show unresolved and resolved name, for F1 try both.
+TEST_F(ToolTipInfo, TypeName_ResolveTypeDef)
+{
+ ::ToolTipInfo expected(Utf8StringLiteral("Ptr"));
+ expected.setQdocIdCandidates({Utf8StringLiteral("PtrFromTypeDef")});
+ expected.setQdocMark(Utf8StringLiteral("PtrFromTypeDef"));
+ expected.setQdocCategory(::ToolTipInfo::Typedef);
+
+ const ::ToolTipInfo actual = tooltip(122, 5);
+
+ ASSERT_THAT(actual, IsToolTip(expected));
+}
+
+// TODO: Show unresolved and resolved name, for F1 try both.
+TEST_F(ToolTipInfo, TypeName_ResolveAlias)
+{
+ ::ToolTipInfo expected(Utf8StringLiteral("Ptr"));
+ expected.setQdocIdCandidates({Utf8StringLiteral("PtrFromTypeAlias")});
+ expected.setQdocMark(Utf8StringLiteral("PtrFromTypeAlias"));
+ expected.setQdocCategory(::ToolTipInfo::Typedef);
+
+ const ::ToolTipInfo actual = tooltip(123, 5);
+
+ ASSERT_THAT(actual, IsToolTip(expected));
+}
+
+// The referenced cursor is a CXCursor_TypeAliasTemplateDecl, its type is invalid
+// and so probably clang_getTypedefDeclUnderlyingType() does not return anything useful.
+// TODO: Fix the cursor's type or add new API in libclang for querying the template type alias.
+TEST_F(ToolTipInfo, DISABLED_TypeName_ResolveTemplateTypeAlias)
+{
+ const ::ToolTipInfo actual = tooltip(124, 5);
+
+ ASSERT_THAT(actual.text(), Utf8StringLiteral("Ptr |