forked from qt-creator/qt-creator
Clang: Add diagnostics
Diagnostics are now moved to the clang backend process. Fixits are supported too. Change-Id: I20faacf466bbf78dec479220c3d7b336a47bc453 Reviewed-by: Nikolai Kosjar <nikolai.kosjar@theqtcompany.com>
This commit is contained in:
@@ -31,6 +31,7 @@
|
||||
#include "clangbackendipcintegration.h"
|
||||
|
||||
#include "clangcompletionassistprocessor.h"
|
||||
#include "clangeditordocumentprocessor.h"
|
||||
#include "clangmodelmanagersupport.h"
|
||||
#include "clangutils.h"
|
||||
#include "pchmanager.h"
|
||||
@@ -46,6 +47,8 @@
|
||||
#include <texteditor/codeassist/iassistprocessor.h>
|
||||
#include <texteditor/texteditor.h>
|
||||
|
||||
#include <clangbackendipc/diagnosticschangedmessage.h>
|
||||
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
@@ -57,6 +60,8 @@
|
||||
#include <clangbackendipc/cmbunregistertranslationunitsforcodecompletionmessage.h>
|
||||
#include <clangbackendipc/cmbunregisterprojectsforcodecompletionmessage.h>
|
||||
#include <clangbackendipc/cmbmessages.h>
|
||||
#include <clangbackendipc/requestdiagnosticsmessage.h>
|
||||
#include <clangbackendipc/filecontainer.h>
|
||||
#include <clangbackendipc/projectpartsdonotexistmessage.h>
|
||||
#include <clangbackendipc/translationunitdoesnotexistmessage.h>
|
||||
|
||||
@@ -152,6 +157,20 @@ void IpcReceiver::codeCompleted(const CodeCompletedMessage &message)
|
||||
}
|
||||
}
|
||||
|
||||
void IpcReceiver::diagnosticsChanged(const DiagnosticsChangedMessage &message)
|
||||
{
|
||||
qCDebug(log) << "<<< DiagnosticsChangedMessage with" << message.diagnostics().size() << "items";
|
||||
|
||||
auto processor = ClangEditorDocumentProcessor::get(message.file().filePath());
|
||||
|
||||
if (processor && processor->projectPart()) {
|
||||
const QString diagnosticsProjectPartId = message.file().projectPartId();
|
||||
const QString documentProjectPartId = processor->projectPart()->id();
|
||||
if (diagnosticsProjectPartId == documentProjectPartId)
|
||||
processor->updateCodeWarnings(message.diagnostics(), message.documentRevision());
|
||||
}
|
||||
}
|
||||
|
||||
void IpcReceiver::translationUnitDoesNotExist(const TranslationUnitDoesNotExistMessage &message)
|
||||
{
|
||||
QTC_CHECK(!"Got TranslationUnitDoesNotExistMessage");
|
||||
@@ -177,6 +196,7 @@ public:
|
||||
void registerProjectPartsForCodeCompletion(const ClangBackEnd::RegisterProjectPartsForCodeCompletionMessage &message) override;
|
||||
void unregisterProjectPartsForCodeCompletion(const ClangBackEnd::UnregisterProjectPartsForCodeCompletionMessage &message) override;
|
||||
void completeCode(const ClangBackEnd::CompleteCodeMessage &message) override;
|
||||
void requestDiagnostics(const ClangBackEnd::RequestDiagnosticsMessage &message) override;
|
||||
|
||||
private:
|
||||
ClangBackEnd::ConnectionClient &m_connection;
|
||||
@@ -218,6 +238,12 @@ void IpcSender::completeCode(const CompleteCodeMessage &message)
|
||||
m_connection.serverProxy().completeCode(message);
|
||||
}
|
||||
|
||||
void IpcSender::requestDiagnostics(const RequestDiagnosticsMessage &message)
|
||||
{
|
||||
QTC_CHECK(m_connection.isConnected());
|
||||
m_connection.serverProxy().requestDiagnostics(message);
|
||||
}
|
||||
|
||||
IpcCommunicator::IpcCommunicator()
|
||||
: m_connection(&m_ipcReceiver)
|
||||
, m_ipcSender(new IpcSender(m_connection))
|
||||
@@ -300,6 +326,7 @@ static QStringList projectPartMessageLine(const CppTools::ProjectPart::Ptr &proj
|
||||
CppTools::ProjectFile::Unclassified); // No language option
|
||||
if (PchInfo::Ptr pchInfo = PchManager::instance()->pchInfo(projectPart))
|
||||
options += ClangCodeModel::Utils::createPCHInclusionOptions(pchInfo->fileName());
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
@@ -339,12 +366,16 @@ void IpcCommunicator::updateUnsavedFile(const QString &filePath, const QByteArra
|
||||
const bool hasUnsavedContent = true;
|
||||
|
||||
// TODO: Send new only if changed
|
||||
registerFilesForCodeCompletion({
|
||||
ClangBackEnd::FileContainer(filePath,
|
||||
projectPartId,
|
||||
Utf8String::fromByteArray(contents),
|
||||
hasUnsavedContent)
|
||||
});
|
||||
registerFilesForCodeCompletion({{filePath,
|
||||
projectPartId,
|
||||
Utf8String::fromByteArray(contents),
|
||||
hasUnsavedContent}});
|
||||
}
|
||||
|
||||
void IpcCommunicator::requestDiagnostics(const FileContainer &fileContainer, uint documentRevision)
|
||||
{
|
||||
registerFilesForCodeCompletion({fileContainer});
|
||||
m_ipcSender->requestDiagnostics({fileContainer, documentRevision});
|
||||
}
|
||||
|
||||
void IpcCommunicator::updateUnsavedFileIfNotCurrentDocument(Core::IDocument *document)
|
||||
|
||||
@@ -47,6 +47,10 @@ class IEditor;
|
||||
class IDocument;
|
||||
}
|
||||
|
||||
namespace ClangBackEnd {
|
||||
class DiagnosticsChangedMessage;
|
||||
}
|
||||
|
||||
namespace TextEditor {
|
||||
class TextEditorWidget;
|
||||
}
|
||||
@@ -75,6 +79,7 @@ private:
|
||||
void alive() override;
|
||||
void echo(const ClangBackEnd::EchoMessage &message) override;
|
||||
void codeCompleted(const ClangBackEnd::CodeCompletedMessage &message) override;
|
||||
void diagnosticsChanged(const ClangBackEnd::DiagnosticsChangedMessage &message) override;
|
||||
|
||||
void translationUnitDoesNotExist(const ClangBackEnd::TranslationUnitDoesNotExistMessage &message) override;
|
||||
void projectPartsDoNotExist(const ClangBackEnd::ProjectPartsDoNotExistMessage &message) override;
|
||||
@@ -95,6 +100,7 @@ public:
|
||||
virtual void registerProjectPartsForCodeCompletion(const ClangBackEnd::RegisterProjectPartsForCodeCompletionMessage &message) = 0;
|
||||
virtual void unregisterProjectPartsForCodeCompletion(const ClangBackEnd::UnregisterProjectPartsForCodeCompletionMessage &message) = 0;
|
||||
virtual void completeCode(const ClangBackEnd::CompleteCodeMessage &message) = 0;
|
||||
virtual void requestDiagnostics(const ClangBackEnd::RequestDiagnosticsMessage &message) = 0;
|
||||
};
|
||||
|
||||
class IpcCommunicator : public QObject
|
||||
@@ -123,6 +129,7 @@ public:
|
||||
void updateUnsavedFileIfNotCurrentDocument(Core::IDocument *document);
|
||||
void updateUnsavedFileFromCppEditorDocument(const QString &filePath);
|
||||
void updateUnsavedFile(const QString &filePath, const QByteArray &contents);
|
||||
void requestDiagnostics(const ClangBackEnd::FileContainer &fileContainer, uint documentRevision);
|
||||
|
||||
public: // for tests
|
||||
IpcSenderInterface *setIpcSender(IpcSenderInterface *ipcSender);
|
||||
|
||||
@@ -29,6 +29,7 @@ SOURCES += \
|
||||
clangmodelmanagersupport.cpp \
|
||||
clangprojectsettings.cpp \
|
||||
clangprojectsettingspropertiespage.cpp \
|
||||
clangtextmark.cpp \
|
||||
clangutils.cpp \
|
||||
completionchunkstotextconverter.cpp \
|
||||
cppcreatemarkers.cpp \
|
||||
@@ -67,6 +68,7 @@ HEADERS += \
|
||||
clangmodelmanagersupport.h \
|
||||
clangprojectsettings.h \
|
||||
clangprojectsettingspropertiespage.h \
|
||||
clangtextmark.h \
|
||||
clangutils.h \
|
||||
completionchunkstotextconverter.h \
|
||||
constants.h \
|
||||
|
||||
@@ -145,6 +145,8 @@ QtcPlugin {
|
||||
"clangprojectsettingspropertiespage.cpp",
|
||||
"clangprojectsettingspropertiespage.h",
|
||||
"clangprojectsettingspropertiespage.ui",
|
||||
"clangtextmark.cpp",
|
||||
"clangtextmark.h",
|
||||
"clangutils.cpp",
|
||||
"clangutils.h",
|
||||
"clangbackendipcintegration.cpp",
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "clangcodemodelplugin.h"
|
||||
|
||||
#include "clangprojectsettingspropertiespage.h"
|
||||
#include "constants.h"
|
||||
#include "pchmanager.h"
|
||||
#include "utils.h"
|
||||
|
||||
@@ -44,9 +45,19 @@
|
||||
#include <projectexplorer/projectexplorer.h>
|
||||
#include <projectexplorer/session.h>
|
||||
|
||||
#include <texteditor/textmark.h>
|
||||
|
||||
namespace ClangCodeModel {
|
||||
namespace Internal {
|
||||
|
||||
void initializeTextMarks()
|
||||
{
|
||||
TextEditor::TextMark::setCategoryColor(Core::Id(Constants::CLANG_WARNING),
|
||||
Utils::Theme::ProjectExplorer_TaskWarn_TextMarkColor);
|
||||
TextEditor::TextMark::setCategoryColor(Core::Id(Constants::CLANG_ERROR),
|
||||
Utils::Theme::ProjectExplorer_TaskError_TextMarkColor);
|
||||
}
|
||||
|
||||
bool ClangCodeModelPlugin::initialize(const QStringList &arguments, QString *errorMessage)
|
||||
{
|
||||
Q_UNUSED(arguments)
|
||||
|
||||
@@ -669,11 +669,10 @@ void ClangCompletionAssistProcessor::sendFileContent(const QString &projectPartI
|
||||
const UnsavedFileContentInfo info = unsavedFileContent(customFileContent);
|
||||
|
||||
IpcCommunicator &ipcCommunicator = m_interface->ipcCommunicator();
|
||||
ipcCommunicator.registerFilesForCodeCompletion(
|
||||
{ClangBackEnd::FileContainer(m_interface->fileName(),
|
||||
projectPartId,
|
||||
Utf8String::fromByteArray(info.unsavedContent),
|
||||
info.isDocumentModified)});
|
||||
ipcCommunicator.registerFilesForCodeCompletion({{m_interface->fileName(),
|
||||
projectPartId,
|
||||
Utf8String::fromByteArray(info.unsavedContent),
|
||||
info.isDocumentModified}});
|
||||
}
|
||||
|
||||
void ClangCompletionAssistProcessor::sendCompletionRequest(int position,
|
||||
@@ -686,7 +685,11 @@ void ClangCompletionAssistProcessor::sendCompletionRequest(int position,
|
||||
const QString filePath = m_interface->fileName();
|
||||
const QString projectPartId = Utils::projectPartIdForFile(filePath);
|
||||
sendFileContent(projectPartId, customFileContent);
|
||||
m_interface->ipcCommunicator().completeCode(this, filePath, line, column, projectPartId);
|
||||
m_interface->ipcCommunicator().completeCode(this,
|
||||
filePath,
|
||||
uint(line),
|
||||
uint(column),
|
||||
projectPartId);
|
||||
}
|
||||
|
||||
TextEditor::IAssistProposal *ClangCompletionAssistProcessor::createProposal() const
|
||||
|
||||
@@ -36,43 +36,24 @@
|
||||
#include "diagnostic.h"
|
||||
#include "pchinfo.h"
|
||||
|
||||
#include <diagnosticcontainer.h>
|
||||
#include <sourcelocationcontainer.h>
|
||||
|
||||
#include <cpptools/cpptoolsplugin.h>
|
||||
#include <cpptools/cppworkingcopy.h>
|
||||
|
||||
#include <texteditor/texteditor.h>
|
||||
|
||||
#include <cplusplus/CppDocument.h>
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/QtConcurrentTools>
|
||||
|
||||
#include <QTextBlock>
|
||||
|
||||
namespace {
|
||||
|
||||
typedef CPlusPlus::Document::DiagnosticMessage CppToolsDiagnostic;
|
||||
QList<CppToolsDiagnostic> toCppToolsDiagnostics(
|
||||
const QString &filePath,
|
||||
const QList<ClangCodeModel::Diagnostic> &diagnostics)
|
||||
{
|
||||
using namespace ClangCodeModel;
|
||||
|
||||
QList<CppToolsDiagnostic> converted;
|
||||
foreach (const ClangCodeModel::Diagnostic &d, diagnostics) {
|
||||
if (d.location().fileName() != filePath)
|
||||
continue;
|
||||
|
||||
// TODO: retrieve fix-its for this diagnostic
|
||||
|
||||
int level;
|
||||
switch (d.severity()) {
|
||||
case Diagnostic::Fatal: level = CppToolsDiagnostic::Fatal; break;
|
||||
case Diagnostic::Error: level = CppToolsDiagnostic::Error; break;
|
||||
case Diagnostic::Warning: level = CppToolsDiagnostic::Warning; break;
|
||||
default: continue;
|
||||
}
|
||||
converted.append(CppToolsDiagnostic(level, d.location().fileName(), d.location().line(),
|
||||
d.location().column(), d.spelling(), d.length()));
|
||||
}
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
QList<TextEditor::BlockRange> toTextEditorBlocks(
|
||||
const QList<ClangCodeModel::SemanticMarker::Range> &ranges)
|
||||
@@ -135,6 +116,8 @@ ClangEditorDocumentProcessor::~ClangEditorDocumentProcessor()
|
||||
|
||||
void ClangEditorDocumentProcessor::run()
|
||||
{
|
||||
requestDiagnostics();
|
||||
|
||||
// Run clang parser
|
||||
disconnect(&m_parserWatcher, &QFutureWatcher<void>::finished,
|
||||
this, &ClangEditorDocumentProcessor::onParserFinished);
|
||||
@@ -189,6 +172,15 @@ CppTools::ProjectPart::Ptr ClangEditorDocumentProcessor::projectPart() const
|
||||
return m_projectPart;
|
||||
}
|
||||
|
||||
void ClangEditorDocumentProcessor::updateCodeWarnings(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
|
||||
uint documentRevision)
|
||||
{
|
||||
if (documentRevision == revision()) {
|
||||
const auto codeWarnings = generateDiagnosticHints(diagnostics);
|
||||
emit codeWarningsUpdated(revision(), codeWarnings);
|
||||
}
|
||||
}
|
||||
|
||||
ClangEditorDocumentProcessor *ClangEditorDocumentProcessor::get(const QString &filePath)
|
||||
{
|
||||
return qobject_cast<ClangEditorDocumentProcessor *>(BaseEditorDocumentProcessor::get(filePath));
|
||||
@@ -200,6 +192,8 @@ void ClangEditorDocumentProcessor::updateProjectPartAndTranslationUnitForComplet
|
||||
QTC_ASSERT(projectPart, return);
|
||||
|
||||
updateTranslationUnitForCompletion(*projectPart.data());
|
||||
requestDiagnostics(*projectPart.data());
|
||||
|
||||
m_projectPart = projectPart;
|
||||
}
|
||||
|
||||
@@ -212,11 +206,6 @@ void ClangEditorDocumentProcessor::onParserFinished()
|
||||
const auto ifdefoutBlocks = toTextEditorBlocks(m_parser.ifdefedOutBlocks());
|
||||
emit ifdefedOutBlocksUpdated(revision(), ifdefoutBlocks);
|
||||
|
||||
// Emit code warnings
|
||||
const auto diagnostics = toCppToolsDiagnostics(filePath(), m_parser.diagnostics());
|
||||
const auto codeWarnings = toTextEditorSelections(diagnostics, textDocument());
|
||||
emit codeWarningsUpdated(revision(), codeWarnings);
|
||||
|
||||
// Run semantic highlighter
|
||||
m_semanticHighlighter.run();
|
||||
|
||||
@@ -229,23 +218,261 @@ void ClangEditorDocumentProcessor::onProjectPartsRemoved(const QStringList &proj
|
||||
m_projectPart.clear();
|
||||
}
|
||||
|
||||
void ClangEditorDocumentProcessor::updateTranslationUnitForCompletion(
|
||||
CppTools::ProjectPart &projectPart)
|
||||
void ClangEditorDocumentProcessor::updateTranslationUnitForCompletion(CppTools::ProjectPart &projectPart)
|
||||
{
|
||||
QTC_ASSERT(m_modelManagerSupport, return);
|
||||
IpcCommunicator &ipcCommunicator = m_modelManagerSupport->ipcCommunicator();
|
||||
|
||||
if (m_projectPart) {
|
||||
if (projectPart.id() != m_projectPart->id()) {
|
||||
auto container1 = {ClangBackEnd::FileContainer(filePath(), m_projectPart->id())};
|
||||
ipcCommunicator.unregisterFilesForCodeCompletion(container1);
|
||||
auto container1 = ClangBackEnd::FileContainer(filePath(), m_projectPart->id());
|
||||
ipcCommunicator.unregisterFilesForCodeCompletion({container1});
|
||||
|
||||
auto container2 = {ClangBackEnd::FileContainer(filePath(), projectPart.id())};
|
||||
ipcCommunicator.registerFilesForCodeCompletion(container2);
|
||||
auto container2 = ClangBackEnd::FileContainer(filePath(), projectPart.id());
|
||||
ipcCommunicator.registerFilesForCodeCompletion({container2});
|
||||
}
|
||||
} else {
|
||||
auto container = {ClangBackEnd::FileContainer(filePath(), projectPart.id())};
|
||||
ipcCommunicator.registerFilesForCodeCompletion(container);
|
||||
auto container = ClangBackEnd::FileContainer(filePath(), projectPart.id());
|
||||
ipcCommunicator.registerFilesForCodeCompletion({container});
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool isWarningOrNote(ClangBackEnd::DiagnosticSeverity severity)
|
||||
{
|
||||
using ClangBackEnd::DiagnosticSeverity;
|
||||
switch (severity) {
|
||||
case DiagnosticSeverity::Ignored:
|
||||
case DiagnosticSeverity::Note:
|
||||
case DiagnosticSeverity::Warning: return true;
|
||||
case DiagnosticSeverity::Error:
|
||||
case DiagnosticSeverity::Fatal: return false;
|
||||
}
|
||||
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
bool isHelpfulChildDiagnostic(const ClangBackEnd::DiagnosticContainer &parentDiagnostic,
|
||||
const ClangBackEnd::DiagnosticContainer &childDiagnostic)
|
||||
{
|
||||
auto parentLocation = parentDiagnostic.location();
|
||||
auto childLocation = childDiagnostic.location();
|
||||
|
||||
return parentLocation == childLocation;
|
||||
}
|
||||
|
||||
QString diagnosticText(const ClangBackEnd::DiagnosticContainer &diagnostic)
|
||||
{
|
||||
QString text = diagnostic.category().toString()
|
||||
+ QStringLiteral(" ")
|
||||
+ diagnostic.text().toString();
|
||||
if (!diagnostic.enableOption().isEmpty()) {
|
||||
text += QStringLiteral(" (clang option: ")
|
||||
+ diagnostic.enableOption().toString()
|
||||
+ QStringLiteral(" disable with: ")
|
||||
+ diagnostic.disableOption().toString()
|
||||
+ QStringLiteral(")");
|
||||
}
|
||||
|
||||
for (auto &&childDiagnostic : diagnostic.children()) {
|
||||
if (isHelpfulChildDiagnostic(diagnostic, childDiagnostic))
|
||||
text += QStringLiteral("\n ") + childDiagnostic.text().toString();
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
template <class Condition>
|
||||
std::vector<ClangBackEnd::DiagnosticContainer>
|
||||
filterDiagnostics(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
|
||||
const Condition &condition)
|
||||
{
|
||||
std::vector<ClangBackEnd::DiagnosticContainer> filteredDiagnostics;
|
||||
|
||||
std::copy_if(diagnostics.cbegin(),
|
||||
diagnostics.cend(),
|
||||
std::back_inserter(filteredDiagnostics),
|
||||
condition);
|
||||
|
||||
return filteredDiagnostics;
|
||||
}
|
||||
|
||||
std::vector<ClangBackEnd::DiagnosticContainer>
|
||||
filterInterestingWarningDiagnostics(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
|
||||
QString &&documentFilePath)
|
||||
{
|
||||
auto isLocalWarning = [documentFilePath] (const ClangBackEnd::DiagnosticContainer &diagnostic) {
|
||||
return isWarningOrNote(diagnostic.severity())
|
||||
&& diagnostic.location().filePath() == documentFilePath;
|
||||
};
|
||||
|
||||
return filterDiagnostics(diagnostics, isLocalWarning);
|
||||
}
|
||||
|
||||
std::vector<ClangBackEnd::DiagnosticContainer>
|
||||
filterInterestingErrorsDiagnostics(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
|
||||
QString &&documentFilePath)
|
||||
{
|
||||
auto isLocalWarning = [documentFilePath] (const ClangBackEnd::DiagnosticContainer &diagnostic) {
|
||||
return !isWarningOrNote(diagnostic.severity())
|
||||
&& diagnostic.location().filePath() == documentFilePath;
|
||||
};
|
||||
|
||||
return filterDiagnostics(diagnostics, isLocalWarning);
|
||||
}
|
||||
|
||||
QTextEdit::ExtraSelection createExtraSelections(const QTextCharFormat &mainformat,
|
||||
const QTextCursor &cursor,
|
||||
const QString &diagnosticText)
|
||||
{
|
||||
QTextEdit::ExtraSelection extraSelection;
|
||||
|
||||
extraSelection.format = mainformat;
|
||||
extraSelection.cursor = cursor;
|
||||
extraSelection.format.setToolTip(diagnosticText);
|
||||
|
||||
return extraSelection;
|
||||
}
|
||||
|
||||
void addRangeSelections(const ClangBackEnd::DiagnosticContainer &diagnostic,
|
||||
QTextDocument *textDocument,
|
||||
const QTextCharFormat &rangeFormat,
|
||||
const QString &diagnosticText,
|
||||
QList<QTextEdit::ExtraSelection> &extraSelections)
|
||||
{
|
||||
for (auto &&range : diagnostic.ranges()) {
|
||||
QTextCursor cursor(textDocument);
|
||||
cursor.setPosition(int(range.start().offset()));
|
||||
cursor.setPosition(int(range.end().offset()), QTextCursor::KeepAnchor);
|
||||
|
||||
auto extraSelection = createExtraSelections(rangeFormat, cursor, diagnosticText);
|
||||
|
||||
extraSelections.push_back(std::move(extraSelection));
|
||||
}
|
||||
}
|
||||
|
||||
QTextCursor createSelectionCursor(QTextDocument *textDocument, uint position)
|
||||
{
|
||||
QTextCursor cursor(textDocument);
|
||||
cursor.setPosition(int(position));
|
||||
cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
|
||||
|
||||
if (!cursor.hasSelection()) {
|
||||
cursor.setPosition(int(position) - 1);
|
||||
cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, 2);
|
||||
}
|
||||
|
||||
return cursor;
|
||||
}
|
||||
|
||||
void addSelections(const std::vector<ClangBackEnd::DiagnosticContainer> &diagnostics,
|
||||
QTextDocument *textDocument,
|
||||
const QTextCharFormat &mainFormat,
|
||||
const QTextCharFormat &rangeFormat,
|
||||
QList<QTextEdit::ExtraSelection> &extraSelections)
|
||||
{
|
||||
for (auto &&diagnostic : diagnostics) {
|
||||
auto cursor = createSelectionCursor(textDocument, diagnostic.location().offset());
|
||||
|
||||
auto text = diagnosticText(diagnostic);
|
||||
auto extraSelection = createExtraSelections(mainFormat, cursor, text);
|
||||
|
||||
addRangeSelections(diagnostic, textDocument, rangeFormat, text, extraSelections);
|
||||
|
||||
extraSelections.push_back(std::move(extraSelection));
|
||||
}
|
||||
}
|
||||
|
||||
void addWarningSelections(const std::vector<ClangBackEnd::DiagnosticContainer> &diagnostics,
|
||||
QTextDocument *textDocument,
|
||||
QList<QTextEdit::ExtraSelection> &extraSelections)
|
||||
{
|
||||
QTextCharFormat warningFormat;
|
||||
warningFormat.setUnderlineStyle(QTextCharFormat::SingleUnderline);
|
||||
warningFormat.setUnderlineColor(QColor(180, 180, 0, 255));
|
||||
|
||||
QTextCharFormat warningRangeFormat;
|
||||
warningRangeFormat.setUnderlineStyle(QTextCharFormat::DotLine);
|
||||
warningRangeFormat.setUnderlineColor(QColor(180, 180, 0, 255));
|
||||
|
||||
addSelections(diagnostics, textDocument, warningFormat, warningRangeFormat, extraSelections);
|
||||
}
|
||||
|
||||
void addErrorSelections(const std::vector<ClangBackEnd::DiagnosticContainer> &diagnostics,
|
||||
QTextDocument *textDocument,
|
||||
QList<QTextEdit::ExtraSelection> &extraSelections)
|
||||
{
|
||||
QTextCharFormat errorFormat;
|
||||
errorFormat.setUnderlineStyle(QTextCharFormat::SingleUnderline);
|
||||
errorFormat.setUnderlineColor(QColor(255, 0, 0, 255));
|
||||
|
||||
QTextCharFormat errorRangeFormat;
|
||||
errorRangeFormat.setUnderlineStyle(QTextCharFormat::DotLine);
|
||||
errorRangeFormat.setUnderlineColor(QColor(255, 0, 0, 255));
|
||||
|
||||
addSelections(diagnostics, textDocument, errorFormat, errorRangeFormat, extraSelections);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
QList<QTextEdit::ExtraSelection>
|
||||
ClangEditorDocumentProcessor::generateDiagnosticHints(const QVector<ClangBackEnd::DiagnosticContainer> &allDiagnostics)
|
||||
{
|
||||
const auto warningDiagnostic = filterInterestingWarningDiagnostics(allDiagnostics, filePath());
|
||||
const auto errorDiagnostic = filterInterestingErrorsDiagnostics(allDiagnostics, filePath());
|
||||
|
||||
m_clangTextMarks.clear();
|
||||
m_clangTextMarks.reserve(warningDiagnostic.size() + errorDiagnostic.size());
|
||||
|
||||
addClangTextMarks(warningDiagnostic);
|
||||
addClangTextMarks(errorDiagnostic);
|
||||
|
||||
QList<QTextEdit::ExtraSelection> extraSelections;
|
||||
extraSelections.reserve(int(warningDiagnostic.size() + errorDiagnostic.size()));
|
||||
|
||||
addWarningSelections(warningDiagnostic, textDocument(), extraSelections);
|
||||
addErrorSelections(errorDiagnostic, textDocument(), extraSelections);
|
||||
|
||||
return extraSelections;
|
||||
}
|
||||
|
||||
void ClangEditorDocumentProcessor::addClangTextMarks(const std::vector<ClangBackEnd::DiagnosticContainer> &diagnostics)
|
||||
{
|
||||
QTC_ASSERT(m_clangTextMarks.size() + diagnostics.size() <= m_clangTextMarks.capacity(), return);
|
||||
|
||||
for (auto &&diagnostic : diagnostics) {
|
||||
m_clangTextMarks.emplace_back(filePath(),
|
||||
diagnostic.location().line(),
|
||||
diagnostic.severity());
|
||||
|
||||
ClangTextMark &textMark = m_clangTextMarks.back();
|
||||
|
||||
textMark.setBaseTextDocument(baseTextDocument());
|
||||
|
||||
baseTextDocument()->addMark(&textMark);
|
||||
}
|
||||
}
|
||||
|
||||
void ClangEditorDocumentProcessor::requestDiagnostics(CppTools::ProjectPart &projectPart)
|
||||
{
|
||||
if (!m_projectPart || projectPart.id() != m_projectPart->id()) {
|
||||
IpcCommunicator &ipcCommunicator = m_modelManagerSupport->ipcCommunicator();
|
||||
|
||||
ipcCommunicator.requestDiagnostics({filePath(), projectPart.id()},
|
||||
revision());
|
||||
}
|
||||
}
|
||||
|
||||
void ClangEditorDocumentProcessor::requestDiagnostics()
|
||||
{
|
||||
// Get diagnostics
|
||||
if (m_projectPart) {
|
||||
auto &ipcCommunicator = m_modelManagerSupport->ipcCommunicator();
|
||||
ipcCommunicator.requestDiagnostics({filePath(),
|
||||
m_projectPart->id(),
|
||||
baseTextDocument()->plainText(),
|
||||
true},
|
||||
revision());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#define CLANGEDITORDOCUMENTPROCESSOR_H
|
||||
|
||||
#include "clangeditordocumentparser.h"
|
||||
#include "clangtextmark.h"
|
||||
|
||||
#include <cpptools/baseeditordocumentprocessor.h>
|
||||
#include <cpptools/builtineditordocumentprocessor.h>
|
||||
@@ -40,6 +41,12 @@
|
||||
#include <QFutureWatcher>
|
||||
#include <QPointer>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace ClangBackEnd {
|
||||
class DiagnosticContainer;
|
||||
}
|
||||
|
||||
namespace ClangCodeModel {
|
||||
namespace Internal {
|
||||
|
||||
@@ -65,6 +72,9 @@ public:
|
||||
|
||||
CppTools::ProjectPart::Ptr projectPart() const;
|
||||
|
||||
void updateCodeWarnings(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
|
||||
uint documentRevision);
|
||||
|
||||
public:
|
||||
static ClangEditorDocumentProcessor *get(const QString &filePath);
|
||||
|
||||
@@ -75,9 +85,15 @@ private slots:
|
||||
private:
|
||||
void updateProjectPartAndTranslationUnitForCompletion();
|
||||
void updateTranslationUnitForCompletion(CppTools::ProjectPart &projectPart);
|
||||
QList<QTextEdit::ExtraSelection>
|
||||
generateDiagnosticHints(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics);
|
||||
void addClangTextMarks(const std::vector<ClangBackEnd::DiagnosticContainer> &diagnostics);
|
||||
void requestDiagnostics(CppTools::ProjectPart &projectPart);
|
||||
void requestDiagnostics();
|
||||
|
||||
private:
|
||||
QPointer<ModelManagerSupportClang> m_modelManagerSupport;
|
||||
|
||||
std::vector<ClangTextMark> m_clangTextMarks;
|
||||
ClangEditorDocumentParser m_parser;
|
||||
CppTools::ProjectPart::Ptr m_projectPart;
|
||||
QFutureWatcher<void> m_parserWatcher;
|
||||
|
||||
82
src/plugins/clangcodemodel/clangtextmark.cpp
Normal file
82
src/plugins/clangcodemodel/clangtextmark.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Contact: http://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 http://www.qt.io/terms-conditions. For further information
|
||||
** use the contact form at http://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or version 3 as published by the Free
|
||||
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
||||
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
||||
** following information to ensure the GNU Lesser General Public License
|
||||
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, The Qt Company gives you certain additional
|
||||
** rights. These rights are described in The Qt Company LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "clangtextmark.h"
|
||||
|
||||
#include "constants.h"
|
||||
|
||||
#include <coreplugin/coreconstants.h>
|
||||
|
||||
#include <QString>
|
||||
#include <QApplication>
|
||||
|
||||
#include <utils/tooltip/tooltip.h>
|
||||
|
||||
namespace ClangCodeModel {
|
||||
|
||||
namespace {
|
||||
|
||||
bool isWarningOrNote(ClangBackEnd::DiagnosticSeverity severity)
|
||||
{
|
||||
using ClangBackEnd::DiagnosticSeverity;
|
||||
switch (severity) {
|
||||
case DiagnosticSeverity::Ignored:
|
||||
case DiagnosticSeverity::Note:
|
||||
case DiagnosticSeverity::Warning: return true;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
Core::Id cartegoryForSeverity(ClangBackEnd::DiagnosticSeverity severity)
|
||||
{
|
||||
return isWarningOrNote(severity) ? Constants::CLANG_WARNING : Constants::CLANG_ERROR;
|
||||
}
|
||||
|
||||
const QIcon &iconForSeverity(ClangBackEnd::DiagnosticSeverity severity)
|
||||
{
|
||||
static const QIcon errorIcon{QLatin1String(Core::Constants::ICON_ERROR)};
|
||||
static const QIcon warningIcon{QLatin1String(Core::Constants::ICON_WARNING)};
|
||||
|
||||
return isWarningOrNote(severity) ? warningIcon : errorIcon;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
ClangTextMark::ClangTextMark(const QString &fileName, int lineNumber, ClangBackEnd::DiagnosticSeverity severity)
|
||||
: TextEditor::TextMark(fileName, lineNumber, cartegoryForSeverity(severity))
|
||||
{
|
||||
setPriority(TextEditor::TextMark::HighPriority);
|
||||
setIcon(iconForSeverity(severity));
|
||||
}
|
||||
|
||||
} // namespace ClangCodeModel
|
||||
|
||||
48
src/plugins/clangcodemodel/clangtextmark.h
Normal file
48
src/plugins/clangcodemodel/clangtextmark.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Contact: http://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 http://www.qt.io/terms-conditions. For further information
|
||||
** use the contact form at http://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or version 3 as published by the Free
|
||||
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
||||
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
||||
** following information to ensure the GNU Lesser General Public License
|
||||
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, The Qt Company gives you certain additional
|
||||
** rights. These rights are described in The Qt Company LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef CLANGCODEMODEL_CLANGTEXTMARK_H
|
||||
#define CLANGCODEMODEL_CLANGTEXTMARK_H
|
||||
|
||||
#include <clangbackendipc_global.h>
|
||||
|
||||
#include <texteditor/textmark.h>
|
||||
|
||||
namespace ClangCodeModel {
|
||||
|
||||
class ClangTextMark : public TextEditor::TextMark
|
||||
{
|
||||
public:
|
||||
ClangTextMark(const QString &fileName, int lineNumber, ClangBackEnd::DiagnosticSeverity severity);
|
||||
};
|
||||
|
||||
} // namespace ClangCodeModel
|
||||
|
||||
#endif // CLANGCODEMODEL_CLANGTEXTMARK_H
|
||||
@@ -135,6 +135,8 @@ public:
|
||||
optionsBuilder.addHeaderPathOptions();
|
||||
optionsBuilder.addProjectConfigFileInclude();
|
||||
|
||||
optionsBuilder.addDiagnosticOptions();
|
||||
|
||||
optionsBuilder.addExtraOptions();
|
||||
|
||||
return optionsBuilder.options();
|
||||
@@ -199,6 +201,18 @@ private:
|
||||
add(QLatin1String("-fretain-comments-from-system-headers"));
|
||||
// TODO: -Xclang -ferror-limit -Xclang 0 ?
|
||||
}
|
||||
|
||||
void addDiagnosticOptions()
|
||||
{
|
||||
add(QStringLiteral("-Weverything"));
|
||||
add(QStringLiteral("-Wno-c++98-compat"));
|
||||
add(QStringLiteral("-Wno-c++98-compat-pedantic"));
|
||||
add(QStringLiteral("-fmessage-length=0"));
|
||||
add(QStringLiteral("-fdiagnostics-show-note-include-stack"));
|
||||
add(QStringLiteral("-fmacro-backtrace-limit=0"));
|
||||
add(QStringLiteral("-fretain-comments-from-system-headers"));
|
||||
add(QStringLiteral("-ferror-limit=1000"));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -56,6 +56,8 @@ static const QLatin1Char kNewLine('\n');
|
||||
static const QLatin1Char kHorizontalTab('\t');
|
||||
|
||||
const char CLANG_MODELMANAGERSUPPORT_ID[] = "ClangCodeMode.ClangCodeMode";
|
||||
const char CLANG_ERROR[] = "Clang.Error";
|
||||
const char CLANG_WARNING[] = "Clang.Warning";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,6 +352,11 @@ QString toString(const CompleteCodeMessage &)
|
||||
return QLatin1String("CompleteCodeMessage\n");
|
||||
}
|
||||
|
||||
QString toString(const RequestDiagnosticsMessage &)
|
||||
{
|
||||
return QStringLiteral("RequestDiagnosticsMessage\n");
|
||||
}
|
||||
|
||||
class IpcSenderSpy : public IpcSenderInterface
|
||||
{
|
||||
public:
|
||||
@@ -373,6 +378,10 @@ public:
|
||||
void completeCode(const CompleteCodeMessage &message) override
|
||||
{ senderLog.append(toString(message)); }
|
||||
|
||||
void requestDiagnostics(const RequestDiagnosticsMessage &message) override
|
||||
{ senderLog.append(toString(message)); }
|
||||
|
||||
|
||||
public:
|
||||
QString senderLog;
|
||||
};
|
||||
@@ -1105,14 +1114,27 @@ void ClangCodeCompletionTest::testUpdateBackendAfterRestart()
|
||||
LogOutput(
|
||||
"RegisterTranslationUnitForCodeCompletionMessage\n"
|
||||
" Path: myheader.h ProjectPart: \n"
|
||||
"RegisterTranslationUnitForCodeCompletionMessage\n"
|
||||
" Path: myheader.h ProjectPart: \n"
|
||||
"RequestDiagnosticsMessage\n"
|
||||
"RegisterProjectPartsForCodeCompletionMessage\n"
|
||||
" ProjectPartContainer id: qt-widgets-app.pro qt-widgets-app\n"
|
||||
"RegisterTranslationUnitForCodeCompletionMessage\n"
|
||||
" Path: myheader.h ProjectPart: \n"
|
||||
"RequestDiagnosticsMessage\n"
|
||||
"RegisterTranslationUnitForCodeCompletionMessage\n"
|
||||
" Path: myheader.h ProjectPart: \n"
|
||||
"RequestDiagnosticsMessage\n"
|
||||
"RegisterTranslationUnitForCodeCompletionMessage\n"
|
||||
" Path: ui_mainwindow.h ProjectPart: \n"
|
||||
"RegisterTranslationUnitForCodeCompletionMessage\n"
|
||||
" Path: myheader.h ProjectPart: \n"
|
||||
"RegisterTranslationUnitForCodeCompletionMessage\n"
|
||||
" Path: mainwindow.cpp ProjectPart: qt-widgets-app.pro qt-widgets-app\n"
|
||||
"RegisterTranslationUnitForCodeCompletionMessage\n"
|
||||
" Path: mainwindow.cpp ProjectPart: qt-widgets-app.pro qt-widgets-app\n"
|
||||
"RequestDiagnosticsMessage\n"
|
||||
|
||||
)));
|
||||
spy.senderLog.clear();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user