Clang: Show info bar for parse errors in header files

...because those errors can lead to a substantial performance/functional
regression.

The actual diagnostics (possibly with children) are shown as details in
the info bar.

The info bar can be hidden with the "Do Not Show Again" button.
Re-enabling the info bar is possible with the new editor tool bar
button.

Change-Id: I03394ff8e3c84127946b0b791930b28a385f5a46
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Nikolai Kosjar
2016-10-04 16:23:42 +02:00
parent cb24872f43
commit 9d55d8485c
25 changed files with 458 additions and 44 deletions

View File

@@ -37,6 +37,7 @@ QDebug operator<<(QDebug debug, const DocumentAnnotationsChangedMessage &message
debug.nospace() << "DocumentAnnotationsChangedMessage("
<< message.fileContainer()
<< ", " << message.diagnostics().size()
<< ", " << !message.firstHeaderErrorDiagnostic().text().isEmpty()
<< ", " << message.highlightingMarks().size()
<< ", " << message.skippedPreprocessorRanges().size()
<< ")";
@@ -49,6 +50,7 @@ void PrintTo(const DocumentAnnotationsChangedMessage &message, ::std::ostream* o
*os << "DocumentAnnotationsChangedMessage(";
PrintTo(message.fileContainer(), os);
*os << "," << message.diagnostics().size();
*os << "," << !message.firstHeaderErrorDiagnostic().text().isEmpty();
*os << "," << message.highlightingMarks().size();
*os << "," << message.skippedPreprocessorRanges().size();
*os << ")";

View File

@@ -41,10 +41,12 @@ public:
DocumentAnnotationsChangedMessage() = default;
DocumentAnnotationsChangedMessage(const FileContainer &fileContainer,
const QVector<DiagnosticContainer> &diagnostics,
const DiagnosticContainer &firstHeaderErrorDiagnostic_,
const QVector<HighlightingMarkContainer> &highlightingMarks,
const QVector<SourceRangeContainer> &skippedPreprocessorRanges)
: fileContainer_(fileContainer),
diagnostics_(diagnostics),
firstHeaderErrorDiagnostic_(firstHeaderErrorDiagnostic_),
highlightingMarks_(highlightingMarks),
skippedPreprocessorRanges_(skippedPreprocessorRanges)
{
@@ -60,6 +62,11 @@ public:
return diagnostics_;
}
const DiagnosticContainer &firstHeaderErrorDiagnostic() const
{
return firstHeaderErrorDiagnostic_;
}
const QVector<HighlightingMarkContainer> &highlightingMarks() const
{
return highlightingMarks_;
@@ -74,6 +81,7 @@ public:
{
out << message.fileContainer_;
out << message.diagnostics_;
out << message.firstHeaderErrorDiagnostic_;
out << message.highlightingMarks_;
out << message.skippedPreprocessorRanges_;
@@ -84,6 +92,7 @@ public:
{
in >> message.fileContainer_;
in >> message.diagnostics_;
in >> message.firstHeaderErrorDiagnostic_;
in >> message.highlightingMarks_;
in >> message.skippedPreprocessorRanges_;
@@ -95,6 +104,7 @@ public:
{
return first.fileContainer_ == second.fileContainer_
&& first.diagnostics_ == second.diagnostics_
&& first.firstHeaderErrorDiagnostic_ == second.firstHeaderErrorDiagnostic_
&& first.highlightingMarks_ == second.highlightingMarks_
&& first.skippedPreprocessorRanges_ == second.skippedPreprocessorRanges_;
}
@@ -102,6 +112,7 @@ public:
private:
FileContainer fileContainer_;
QVector<DiagnosticContainer> diagnostics_;
DiagnosticContainer firstHeaderErrorDiagnostic_;
QVector<HighlightingMarkContainer> highlightingMarks_;
QVector<SourceRangeContainer> skippedPreprocessorRanges_;
};

View File

@@ -182,7 +182,9 @@ void IpcReceiver::documentAnnotationsChanged(const DocumentAnnotationsChangedMes
const QString documentProjectPartId = CppTools::CppToolsBridge::projectPartIdForFile(filePath);
if (projectPartId == documentProjectPartId) {
const quint32 documentRevision = message.fileContainer().documentRevision();
processor->updateCodeWarnings(message.diagnostics(), documentRevision);
processor->updateCodeWarnings(message.diagnostics(),
message.firstHeaderErrorDiagnostic(),
documentRevision);
processor->updateHighlighting(message.highlightingMarks(),
message.skippedPreprocessorRanges(),
documentRevision);

View File

@@ -41,6 +41,7 @@
#include <utils/proxyaction.h>
#include <utils/qtcassert.h>
#include <QFileInfo>
#include <QTextBlock>
namespace {

View File

@@ -129,9 +129,10 @@ enum IndentType { IndentDiagnostic, DoNotIndentDiagnostic };
QWidget *createDiagnosticLabel(const ClangBackEnd::DiagnosticContainer &diagnostic,
const QString &mainFilePath,
IndentType indentType = DoNotIndentDiagnostic)
IndentType indentType = DoNotIndentDiagnostic,
bool enableClickableFixits = true)
{
const bool hasFixit = !diagnostic.fixIts().isEmpty();
const bool hasFixit = enableClickableFixits ? !diagnostic.fixIts().isEmpty() : false;
const QString diagnosticText = diagnostic.text().toString().toHtmlEscaped();
const QString text = clickableLocation(mainFilePath, diagnostic.location())
+ QStringLiteral(": ")
@@ -159,15 +160,18 @@ class MainDiagnosticWidget : public QWidget
{
Q_OBJECT
public:
MainDiagnosticWidget(const ClangBackEnd::DiagnosticContainer &diagnostic)
MainDiagnosticWidget(const ClangBackEnd::DiagnosticContainer &diagnostic,
const ClangCodeModel::Internal::DisplayHints &displayHints)
{
setContentsMargins(0, 0, 0, 0);
auto *mainLayout = createLayout<QVBoxLayout>();
const ClangBackEnd::SourceLocationContainer location = diagnostic.location();
// Set up header row: category + responsible option
if (displayHints.showMainDiagnosticHeader) {
const QString category = diagnostic.category();
const QString responsibleOption = diagnostic.enableOption();
const ClangBackEnd::SourceLocationContainer location = diagnostic.location();
auto *headerLayout = createLayout<QHBoxLayout>();
headerLayout->addWidget(new QLabel(wrapInBoldTags(category)), 1);
@@ -175,9 +179,16 @@ public:
auto *responsibleOptionLabel = new QLabel(wrapInColor(responsibleOption, "gray"));
headerLayout->addWidget(responsibleOptionLabel, 0);
mainLayout->addLayout(headerLayout);
}
// Set up main row: diagnostic text
mainLayout->addWidget(createDiagnosticLabel(diagnostic, location.filePath()));
const Utf8String mainFilePath = displayHints.showFileNameInMainDiagnostic
? Utf8String()
: location.filePath();
mainLayout->addWidget(createDiagnosticLabel(diagnostic,
mainFilePath,
DoNotIndentDiagnostic,
displayHints.enableClickableFixits));
setLayout(mainLayout);
}
@@ -186,26 +197,35 @@ public:
void addChildrenToLayout(const QString &mainFilePath,
const QVector<ClangBackEnd::DiagnosticContainer>::const_iterator first,
const QVector<ClangBackEnd::DiagnosticContainer>::const_iterator last,
bool enableClickableFixits,
QLayout &boxLayout)
{
for (auto it = first; it != last; ++it)
boxLayout.addWidget(createDiagnosticLabel(*it, mainFilePath, IndentDiagnostic));
for (auto it = first; it != last; ++it) {
boxLayout.addWidget(createDiagnosticLabel(*it,
mainFilePath,
IndentDiagnostic,
enableClickableFixits));
}
}
void setupChildDiagnostics(const QString &mainFilePath,
const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
bool enableClickableFixits,
QLayout &boxLayout)
{
if (diagnostics.size() <= 10) {
addChildrenToLayout(mainFilePath, diagnostics.begin(), diagnostics.end(), boxLayout);
addChildrenToLayout(mainFilePath, diagnostics.begin(), diagnostics.end(),
enableClickableFixits, boxLayout);
} else {
addChildrenToLayout(mainFilePath, diagnostics.begin(), diagnostics.begin() + 7, boxLayout);
addChildrenToLayout(mainFilePath, diagnostics.begin(), diagnostics.begin() + 7,
enableClickableFixits, boxLayout);
auto ellipsisLabel = new QLabel(QStringLiteral("..."));
ellipsisLabel->setContentsMargins(childIndentationOnTheLeftInPixel, 0, 0, 0);
boxLayout.addWidget(ellipsisLabel);
addChildrenToLayout(mainFilePath, diagnostics.end() - 3, diagnostics.end(), boxLayout);
addChildrenToLayout(mainFilePath, diagnostics.end() - 3, diagnostics.end(),
enableClickableFixits, boxLayout);
}
}
@@ -214,13 +234,18 @@ void setupChildDiagnostics(const QString &mainFilePath,
namespace ClangCodeModel {
namespace Internal {
void addToolTipToLayout(const ClangBackEnd::DiagnosticContainer &diagnostic, QLayout *target)
void addToolTipToLayout(const ClangBackEnd::DiagnosticContainer &diagnostic,
QLayout *target,
const DisplayHints &displayHints)
{
// Set up header and text row for main diagnostic
target->addWidget(new MainDiagnosticWidget(diagnostic));
target->addWidget(new MainDiagnosticWidget(diagnostic, displayHints));
// Set up child rows for notes
setupChildDiagnostics(diagnostic.location().filePath(), diagnostic.children(), *target);
setupChildDiagnostics(diagnostic.location().filePath(),
diagnostic.children(),
displayHints.enableClickableFixits,
*target);
}
} // namespace Internal

View File

@@ -34,7 +34,15 @@ QT_END_NAMESPACE
namespace ClangCodeModel {
namespace Internal {
void addToolTipToLayout(const ClangBackEnd::DiagnosticContainer &diagnostic, QLayout *target);
struct DisplayHints {
bool showMainDiagnosticHeader = true;
bool showFileNameInMainDiagnostic = false;
bool enableClickableFixits = true;
};
void addToolTipToLayout(const ClangBackEnd::DiagnosticContainer &diagnostic,
QLayout *target,
const DisplayHints &displayHints = DisplayHints());
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -57,6 +57,8 @@
#include <utils/runextensions.h>
#include <QTextBlock>
#include <QVBoxLayout>
#include <QWidget>
namespace ClangCodeModel {
namespace Internal {
@@ -167,14 +169,21 @@ void ClangEditorDocumentProcessor::clearProjectPart()
m_projectPart.clear();
}
void ClangEditorDocumentProcessor::updateCodeWarnings(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
void ClangEditorDocumentProcessor::updateCodeWarnings(
const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
const ClangBackEnd::DiagnosticContainer &firstHeaderErrorDiagnostic,
uint documentRevision)
{
if (documentRevision == revision()) {
m_diagnosticManager.processNewDiagnostics(diagnostics);
const auto codeWarnings = m_diagnosticManager.takeExtraSelections();
const auto fixitAvailableMarkers = m_diagnosticManager.takeFixItAvailableMarkers();
emit codeWarningsUpdated(revision(), codeWarnings, fixitAvailableMarkers);
const auto creator = creatorForHeaderErrorDiagnosticWidget(firstHeaderErrorDiagnostic);
emit codeWarningsUpdated(revision(),
codeWarnings,
creator,
fixitAvailableMarkers);
}
}
namespace {
@@ -333,6 +342,38 @@ void ClangEditorDocumentProcessor::requestDocumentAnnotations(const QString &pro
m_ipcCommunicator.requestDocumentAnnotations(fileContainer);
}
static Internal::DisplayHints displayHintsForInfoBar()
{
Internal::DisplayHints displayHints;
displayHints.showMainDiagnosticHeader = false;
displayHints.showFileNameInMainDiagnostic = true;
displayHints.enableClickableFixits = false; // Tool chain headers might be changed, so disable.
return displayHints;
}
CppTools::BaseEditorDocumentProcessor::HeaderErrorDiagnosticWidgetCreator
ClangEditorDocumentProcessor::creatorForHeaderErrorDiagnosticWidget(
const ClangBackEnd::DiagnosticContainer &firstHeaderErrorDiagnostic)
{
if (firstHeaderErrorDiagnostic.text().isEmpty())
return CppTools::BaseEditorDocumentProcessor::HeaderErrorDiagnosticWidgetCreator();
return [firstHeaderErrorDiagnostic]() {
auto vbox = new QVBoxLayout;
vbox->setMargin(0);
vbox->setContentsMargins(10, 0, 0, 2);
vbox->setSpacing(2);
addToolTipToLayout(firstHeaderErrorDiagnostic, vbox, displayHintsForInfoBar());
auto widget = new QWidget;
widget->setLayout(vbox);
return widget;
};
}
static CppTools::ProjectPart projectPartForLanguageOption(CppTools::ProjectPart *projectPart)
{
if (projectPart)

View File

@@ -68,6 +68,7 @@ public:
void clearProjectPart();
void updateCodeWarnings(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
const ClangBackEnd::DiagnosticContainer &firstHeaderErrorDiagnostic,
uint documentRevision);
void updateHighlighting(const QVector<ClangBackEnd::HighlightingMarkContainer> &highlightingMarks,
const QVector<ClangBackEnd::SourceRangeContainer> &skippedPreprocessorRanges,
@@ -96,6 +97,8 @@ private:
void registerTranslationUnitForEditor(CppTools::ProjectPart *projectPart);
void updateTranslationUnitIfProjectPartExists();
void requestDocumentAnnotations(const QString &projectpartId);
HeaderErrorDiagnosticWidgetCreator creatorForHeaderErrorDiagnosticWidget(
const ClangBackEnd::DiagnosticContainer &firstHeaderErrorDiagnostic);
ClangBackEnd::FileContainer fileContainerWithArguments(CppTools::ProjectPart *projectPart) const;
ClangBackEnd::FileContainer fileContainerWithDocumentContent(const QString &projectpartId) const;

View File

@@ -84,7 +84,7 @@ void ClangTextMark::setIcon(ClangBackEnd::DiagnosticSeverity severity)
bool ClangTextMark::addToolTipContent(QLayout *target)
{
Internal::addToolTipToLayout(m_diagnostic, target);
Internal::addToolTipToLayout(m_diagnostic, target, Internal::DisplayHints());
return true;
}

View File

@@ -44,6 +44,7 @@
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/editormanager/documentmodel.h>
#include <coreplugin/infobar.h>
#include <cpptools/cppchecksymbols.h>
#include <cpptools/cppcodeformatter.h>
@@ -77,6 +78,7 @@
#include <cplusplus/FastPreprocessor.h>
#include <cplusplus/MatchingText.h>
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
#include <QApplication>
#include <QAction>
@@ -126,9 +128,13 @@ public:
QSharedPointer<FunctionDeclDefLink> m_declDefLink;
QScopedPointer<FollowSymbolUnderCursor> m_followSymbolUnderCursor;
QToolButton *m_preprocessorButton;
QToolButton *m_preprocessorButton = nullptr;
QToolButton *m_headerErrorsIndicatorButton = nullptr;
CppSelectionChanger m_cppSelectionChanger;
CppEditorWidget::HeaderErrorDiagnosticWidgetCreator m_headerErrorDiagnosticWidgetCreator;
};
CppEditorWidgetPrivate::CppEditorWidgetPrivate(CppEditorWidget *q)
@@ -139,7 +145,6 @@ CppEditorWidgetPrivate::CppEditorWidgetPrivate(CppEditorWidget *q)
, m_useSelectionsUpdater(q)
, m_declDefLinkFinder(new FunctionDeclDefLinkFinder(q))
, m_followSymbolUnderCursor(new FollowSymbolUnderCursor(q))
, m_preprocessorButton(0)
, m_cppSelectionChanger()
{
}
@@ -224,7 +229,15 @@ void CppEditorWidget::finalizeInitialization()
connect(cmd, &Command::keySequenceChanged, this, &CppEditorWidget::updatePreprocessorButtonTooltip);
updatePreprocessorButtonTooltip();
connect(d->m_preprocessorButton, &QAbstractButton::clicked, this, &CppEditorWidget::showPreProcessorWidget);
d->m_headerErrorsIndicatorButton = new QToolButton(this);
d->m_headerErrorsIndicatorButton->setIcon(Utils::Icons::WARNING_TOOLBAR.pixmap());
connect(d->m_headerErrorsIndicatorButton, &QAbstractButton::clicked,
this, &CppEditorWidget::showHeaderErrorInfoBar);
d->m_headerErrorsIndicatorButton->setEnabled(false);
insertExtraToolBarWidget(TextEditorWidget::Left, d->m_preprocessorButton);
insertExtraToolBarWidget(TextEditorWidget::Left, d->m_headerErrorsIndicatorButton);
insertExtraToolBarWidget(TextEditorWidget::Left, d->m_cppEditorOutline->widget());
}
@@ -287,6 +300,7 @@ void CppEditorWidget::onCppDocumentUpdated()
void CppEditorWidget::onCodeWarningsUpdated(unsigned revision,
const QList<QTextEdit::ExtraSelection> selections,
const HeaderErrorDiagnosticWidgetCreator &creator,
const TextEditor::RefactorMarkers &refactorMarkers)
{
if (revision != documentRevision())
@@ -294,6 +308,9 @@ void CppEditorWidget::onCodeWarningsUpdated(unsigned revision,
setExtraSelections(TextEditorWidget::CodeWarningsSelection, selections);
setRefactorMarkers(refactorMarkersWithoutClangMarkers() + refactorMarkers);
d->m_headerErrorDiagnosticWidgetCreator = creator;
updateHeaderErrorWidgets();
}
void CppEditorWidget::onIfdefedOutBlocksUpdated(unsigned revision,
@@ -304,6 +321,24 @@ void CppEditorWidget::onIfdefedOutBlocksUpdated(unsigned revision,
setIfdefedOutBlocks(ifdefedOutBlocks);
}
void CppEditorWidget::updateHeaderErrorWidgets()
{
const Id id(Constants::ERRORS_IN_HEADER_FILES);
InfoBar *infoBar = textDocument()->infoBar();
infoBar->removeInfo(id);
if (d->m_headerErrorDiagnosticWidgetCreator) {
if (infoBar->canInfoBeAdded(id)) {
addHeaderErrorInfoBarEntryAndHideIndicator();
} else {
d->m_headerErrorsIndicatorButton->setEnabled(true);
}
} else {
d->m_headerErrorsIndicatorButton->setEnabled(false);
}
}
void CppEditorWidget::findUsages()
{
if (!d->m_modelManager)
@@ -399,6 +434,25 @@ void CppEditorWidget::renameSymbolUnderCursorBuiltin()
renameUsages(); // Rename non-local symbol or macro
}
void CppEditorWidget::addHeaderErrorInfoBarEntryAndHideIndicator() const
{
InfoBarEntry info(Constants::ERRORS_IN_HEADER_FILES,
tr("<b>Warning</b>: The code model could not parse an included file, "
"which might lead to slow or incorrect code completion and "
"highlighting, for example."),
InfoBarEntry::GlobalSuppressionEnabled);
info.setDetailsWidgetCreator(d->m_headerErrorDiagnosticWidgetCreator);
info.setShowDefaultCancelButton(false);
info.setSuppressionButtonInfo([this](){
d->m_headerErrorsIndicatorButton->setEnabled(true);
});
InfoBar *infoBar = textDocument()->infoBar();
infoBar->addInfo(info);
d->m_headerErrorsIndicatorButton->setEnabled(false);
}
namespace {
QList<ProjectPart::Ptr> fetchProjectParts(CppTools::CppModelManager *modelManager,
@@ -970,5 +1024,14 @@ void CppEditorWidget::showPreProcessorWidget()
}
}
void CppEditorWidget::showHeaderErrorInfoBar()
{
const Id id(Constants::ERRORS_IN_HEADER_FILES);
QTC_CHECK(!textDocument()->infoBar()->canInfoBeAdded(id));
InfoBar::globallyUnsuppressInfo(id);
addHeaderErrorInfoBarEntryAndHideIndicator();
}
} // namespace Internal
} // namespace CppEditor

View File

@@ -31,6 +31,10 @@
#include <QScopedPointer>
namespace Core {
class InfoBarEntry;
}
namespace CppTools {
class CppEditorOutline;
class RefactoringEngineInterface;
@@ -87,6 +91,7 @@ public:
void switchDeclarationDefinition(bool inNextSplit);
void showPreProcessorWidget();
void showHeaderErrorInfoBar();
void findUsages();
void renameSymbolUnderCursor();
@@ -108,6 +113,9 @@ protected:
void slotCodeStyleSettingsChanged(const QVariant &) override;
public:
using HeaderErrorDiagnosticWidgetCreator = std::function<QWidget*()>;
private:
void updateFunctionDeclDefLink();
void updateFunctionDeclDefLinkNow();
@@ -118,10 +126,12 @@ private:
void onCodeWarningsUpdated(unsigned revision,
const QList<QTextEdit::ExtraSelection> selections,
const HeaderErrorDiagnosticWidgetCreator &creator,
const TextEditor::RefactorMarkers &refactorMarkers);
void onIfdefedOutBlocksUpdated(unsigned revision,
const QList<TextEditor::BlockRange> ifdefedOutBlocks);
void updateHeaderErrorWidgets();
void updateSemanticInfo(const CppTools::SemanticInfo &semanticInfo,
bool updateUseSelectionSynchronously = false);
void updatePreprocessorButtonTooltip();
@@ -140,6 +150,8 @@ private:
void renameSymbolUnderCursorClang();
void renameSymbolUnderCursorBuiltin();
void addHeaderErrorInfoBarEntryAndHideIndicator() const;
CppTools::ProjectPart *projectPart() const;
private:

View File

@@ -36,6 +36,7 @@ const char OPEN_DECLARATION_DEFINITION_IN_NEXT_SPLIT[] = "CppEditor.OpenDeclarat
const char RENAME_SYMBOL_UNDER_CURSOR[] = "CppEditor.RenameSymbolUnderCursor";
const char FIND_USAGES[] = "CppEditor.FindUsages";
const char OPEN_PREPROCESSOR_DIALOG[] = "CppEditor.OpenPreprocessorDialog";
const char ERRORS_IN_HEADER_FILES[] = "CppEditor.ErrorsInHeaderFiles";
const char M_REFACTORING_MENU_INSERTION_POINT[] = "CppEditor.RefactorGroup";
const char UPDATE_CODEMODEL[] = "CppEditor.UpdateCodeModel";
const char INSPECT_CPP_CODEMODEL[] = "CppEditor.InspectCppCodeModel";

View File

@@ -59,9 +59,13 @@ public:
const QByteArray &defines);
void scheduleProcessDocument();
public:
using HeaderErrorDiagnosticWidgetCreator = std::function<QWidget*()>;
signals:
void codeWarningsUpdated(unsigned contentsRevision,
const QList<QTextEdit::ExtraSelection> selections,
const HeaderErrorDiagnosticWidgetCreator &creator,
const TextEditor::RefactorMarkers &refactorMarkers);
void ifdefedOutBlocksUpdated(unsigned contentsRevision,

View File

@@ -37,6 +37,8 @@
#include <QTextEdit>
#include <functional>
namespace TextEditor {
class TextDocument;
class QuickFixOperations;
@@ -69,10 +71,15 @@ public:
virtual void editorDocumentTimerRestarted();
public:
using HeaderErrorDiagnosticWidgetCreator = std::function<QWidget*()>;
signals:
// Signal interface to implement
void codeWarningsUpdated(unsigned revision,
const QList<QTextEdit::ExtraSelection> selections,
const HeaderErrorDiagnosticWidgetCreator &creator,
const TextEditor::RefactorMarkers &refactorMarkers);
void ifdefedOutBlocksUpdated(unsigned revision,

View File

@@ -310,7 +310,10 @@ void BuiltinEditorDocumentProcessor::onCodeWarningsUpdated(
m_codeWarnings += toTextEditorSelections(codeWarnings, textDocument());
m_codeWarningsUpdated = true;
emit codeWarningsUpdated(revision(), m_codeWarnings, TextEditor::RefactorMarkers());
emit codeWarningsUpdated(revision(),
m_codeWarnings,
HeaderErrorDiagnosticWidgetCreator(),
TextEditor::RefactorMarkers());
}
SemanticInfo::Source BuiltinEditorDocumentProcessor::createSemanticInfoSource(bool force) const

View File

@@ -40,7 +40,8 @@ static RequestDocumentAnnotationsJob::AsyncResult runAsyncHelper(
RequestDocumentAnnotationsJob::AsyncResult asyncResult;
translationUnit.extractDocumentAnnotations(asyncResult.diagnostics,
translationUnit.extractDocumentAnnotations(asyncResult.firstHeaderErrorDiagnostic,
asyncResult.diagnostics,
asyncResult.highlightingMarks,
asyncResult.skippedSourceRanges);
@@ -83,6 +84,7 @@ void RequestDocumentAnnotationsJob::sendAnnotations(
{
const DocumentAnnotationsChangedMessage message(m_pinnedFileContainer,
result.diagnostics,
result.firstHeaderErrorDiagnostic,
result.highlightingMarks,
result.skippedSourceRanges);

View File

@@ -36,6 +36,7 @@ namespace ClangBackEnd {
struct RequestDocumentAnnotationsJobResult
{
ClangBackEnd::DiagnosticContainer firstHeaderErrorDiagnostic;
QVector<ClangBackEnd::DiagnosticContainer> diagnostics;
QVector<HighlightingMarkContainer> highlightingMarks;
QVector<SourceRangeContainer> skippedSourceRanges;

View File

@@ -112,11 +112,12 @@ TranslationUnit::CodeCompletionResult TranslationUnit::complete(
}
void TranslationUnit::extractDocumentAnnotations(
QVector<DiagnosticContainer> &diagnostics,
DiagnosticContainer &firstHeaderErrorDiagnostic,
QVector<DiagnosticContainer> &mainFileDiagnostics,
QVector<HighlightingMarkContainer> &highlightingMarks,
QVector<SourceRangeContainer> &skippedSourceRanges) const
{
diagnostics = mainFileDiagnostics();
extractDiagnostics(firstHeaderErrorDiagnostic, mainFileDiagnostics);
highlightingMarks = this->highlightingMarks().toHighlightingMarksContainers();
skippedSourceRanges = this->skippedSourceRanges().toSourceRangeContainers();
}
@@ -126,15 +127,6 @@ DiagnosticSet TranslationUnit::diagnostics() const
return DiagnosticSet(clang_getDiagnosticSetFromTU(m_cxTranslationUnit));
}
QVector<DiagnosticContainer> TranslationUnit::mainFileDiagnostics() const
{
const auto isMainFileDiagnostic = [this](const Diagnostic &diagnostic) {
return diagnostic.location().filePath() == m_filePath;
};
return diagnostics().toDiagnosticContainers(isMainFileDiagnostic);
}
SourceLocation TranslationUnit::sourceLocationAt(uint line,uint column) const
{
return SourceLocation(m_cxTranslationUnit, m_filePath, line, column);
@@ -193,4 +185,35 @@ SkippedSourceRanges TranslationUnit::skippedSourceRanges() const
return SkippedSourceRanges(m_cxTranslationUnit, m_filePath.constData());
}
static bool isMainFileDiagnostic(const Utf8String &mainFilePath, const Diagnostic &diagnostic)
{
return diagnostic.location().filePath() == mainFilePath;
}
static bool isHeaderErrorDiagnostic(const Utf8String &mainFilePath, const Diagnostic &diagnostic)
{
const bool isCritical = diagnostic.severity() == DiagnosticSeverity::Error
|| diagnostic.severity() == DiagnosticSeverity::Fatal;
return isCritical && diagnostic.location().filePath() != mainFilePath;
}
void TranslationUnit::extractDiagnostics(DiagnosticContainer &firstHeaderErrorDiagnostic,
QVector<DiagnosticContainer> &mainFileDiagnostics) const
{
mainFileDiagnostics.clear();
mainFileDiagnostics.reserve(int(diagnostics().size()));
bool hasFirstHeaderErrorDiagnostic = false;
for (const Diagnostic &diagnostic : diagnostics()) {
if (!hasFirstHeaderErrorDiagnostic && isHeaderErrorDiagnostic(m_filePath, diagnostic)) {
hasFirstHeaderErrorDiagnostic = true;
firstHeaderErrorDiagnostic = diagnostic.toDiagnosticContainer();
}
if (isMainFileDiagnostic(m_filePath, diagnostic))
mainFileDiagnostics.push_back(diagnostic.toDiagnosticContainer());
}
}
} // namespace ClangBackEnd

View File

@@ -76,12 +76,14 @@ public:
CodeCompletionResult complete(UnsavedFiles &unsavedFiles, uint line, uint column) const;
void extractDocumentAnnotations(QVector<DiagnosticContainer> &diagnostics,
void extractDiagnostics(DiagnosticContainer &firstHeaderErrorDiagnostic,
QVector<DiagnosticContainer> &mainFileDiagnostics) const;
void extractDocumentAnnotations(DiagnosticContainer &firstHeaderErrorDiagnostic,
QVector<DiagnosticContainer> &mainFileDiagnostics,
QVector<HighlightingMarkContainer> &highlightingMarks,
QVector<SourceRangeContainer> &skippedSourceRanges) const;
DiagnosticSet diagnostics() const;
QVector<DiagnosticContainer> mainFileDiagnostics() const;
SourceLocation sourceLocationAt(uint line, uint column) const;
SourceLocation sourceLocationAt(const Utf8String &filePath, uint line, uint column) const;

View File

@@ -45,7 +45,8 @@ static UpdateDocumentAnnotationsJob::AsyncResult runAsyncHelper(
asyncResult.updateResult = translationUnit.update(translationUnitUpdatInput);
// Collect
translationUnit.extractDocumentAnnotations(asyncResult.diagnostics,
translationUnit.extractDocumentAnnotations(asyncResult.firstHeaderErrorDiagnostic,
asyncResult.diagnostics,
asyncResult.highlightingMarks,
asyncResult.skippedSourceRanges);
@@ -95,6 +96,7 @@ void UpdateDocumentAnnotationsJob::sendAnnotations(const AsyncResult &result)
{
const DocumentAnnotationsChangedMessage message(m_pinnedFileContainer,
result.diagnostics,
result.firstHeaderErrorDiagnostic,
result.highlightingMarks,
result.skippedSourceRanges);

View File

@@ -39,6 +39,7 @@ struct UpdateDocumentAnnotationsJobResult
{
TranslationUnitUpdateResult updateResult;
ClangBackEnd::DiagnosticContainer firstHeaderErrorDiagnostic;
QVector<ClangBackEnd::DiagnosticContainer> diagnostics;
QVector<HighlightingMarkContainer> highlightingMarks;
QVector<SourceRangeContainer> skippedSourceRanges;

View File

@@ -0,0 +1,197 @@
/****************************************************************************
**
** Copyright (C) 2016 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 <clangtranslationunit.h>
#include <clangtranslationunitupdater.h>
#include <diagnosticcontainer.h>
#include <utf8string.h>
#include <clang-c/Index.h>
#include <gmock/gmock.h>
#include <gmock/gmock-matchers.h>
#include <gtest/gtest.h>
#include "gtest-qt-printing.h"
using ClangBackEnd::DiagnosticContainer;
using ClangBackEnd::TranslationUnit;
using ClangBackEnd::TranslationUnitUpdateInput;
using ClangBackEnd::TranslationUnitUpdateResult;
using testing::ContainerEq;
using testing::Eq;
namespace {
class TranslationUnit : public ::testing::Test
{
protected:
void SetUp() override;
void TearDown() override;
void parse();
void reparse();
DiagnosticContainer createDiagnostic(const QString &text,
ClangBackEnd::DiagnosticSeverity severity,
uint line,
uint column,
const QString &filePath) const;
QVector<DiagnosticContainer> diagnosticsFromMainFile() const;
QVector<DiagnosticContainer> errorDiagnosticsFromHeaders() const;
protected:
Utf8String sourceFilePath = Utf8StringLiteral(TESTDATA_DIR"/diagnostic_erroneous_source.cpp");
Utf8String headerFilePath = Utf8StringLiteral(TESTDATA_DIR"/diagnostic_erroneous_header.h");
CXIndex cxIndex = nullptr;
CXTranslationUnit cxTranslationUnit = nullptr;
::TranslationUnit translationUnit{Utf8String(), sourceFilePath, cxIndex, cxTranslationUnit};
DiagnosticContainer extractedFirstHeaderErrorDiagnostic;
QVector<DiagnosticContainer> extractedMainFileDiagnostics;
};
TEST_F(TranslationUnit, HasExpectedMainFileDiagnostics)
{
translationUnit.extractDiagnostics(extractedFirstHeaderErrorDiagnostic,
extractedMainFileDiagnostics);
ASSERT_THAT(extractedMainFileDiagnostics, ContainerEq(diagnosticsFromMainFile()));
}
TEST_F(TranslationUnit, HasExpectedMainFileDiagnosticsAfterReparse)
{
reparse();
translationUnit.extractDiagnostics(extractedFirstHeaderErrorDiagnostic,
extractedMainFileDiagnostics);
ASSERT_THAT(extractedMainFileDiagnostics, ContainerEq(diagnosticsFromMainFile()));
}
TEST_F(TranslationUnit, HasErrorDiagnosticsInHeaders)
{
translationUnit.extractDiagnostics(extractedFirstHeaderErrorDiagnostic,
extractedMainFileDiagnostics);
ASSERT_THAT(extractedFirstHeaderErrorDiagnostic,
Eq(errorDiagnosticsFromHeaders().first()));
}
TEST_F(TranslationUnit, HasErrorDiagnosticsInHeadersAfterReparse)
{
reparse();
translationUnit.extractDiagnostics(extractedFirstHeaderErrorDiagnostic,
extractedMainFileDiagnostics);
ASSERT_THAT(extractedFirstHeaderErrorDiagnostic,
Eq(errorDiagnosticsFromHeaders().first()));
}
void TranslationUnit::SetUp()
{
parse();
}
void TranslationUnit::TearDown()
{
clang_disposeTranslationUnit(cxTranslationUnit);
clang_disposeIndex(cxIndex);
}
void TranslationUnit::parse()
{
TranslationUnitUpdateInput parseInput;
parseInput.filePath = sourceFilePath;
parseInput.parseNeeded = true;
const TranslationUnitUpdateResult parseResult = translationUnit.update(parseInput);
ASSERT_TRUE(parseResult.hasParsed());
}
void TranslationUnit::reparse()
{
TranslationUnitUpdateInput parseInput;
parseInput.filePath = sourceFilePath;
parseInput.reparseNeeded = true;
const TranslationUnitUpdateResult parseResult = translationUnit.update(parseInput);
ASSERT_TRUE(parseResult.hasReparsed());
}
DiagnosticContainer TranslationUnit::createDiagnostic(const QString &text,
ClangBackEnd::DiagnosticSeverity severity,
uint line,
uint column,
const QString &filePath) const
{
return DiagnosticContainer(
text,
Utf8StringLiteral(""),
{},
severity,
{filePath, line, column},
{},
{},
{}
);
}
QVector<ClangBackEnd::DiagnosticContainer> TranslationUnit::diagnosticsFromMainFile() const
{
return {
createDiagnostic(
QStringLiteral("warning: enumeration value 'Three' not handled in switch"),
ClangBackEnd::DiagnosticSeverity::Warning,
7,
13,
sourceFilePath),
createDiagnostic(
QStringLiteral("error: void function 'g' should not return a value"),
ClangBackEnd::DiagnosticSeverity::Error,
15,
5,
sourceFilePath),
createDiagnostic(
QStringLiteral("warning: using the result of an assignment as a condition without parentheses"),
ClangBackEnd::DiagnosticSeverity::Warning,
21,
11,
sourceFilePath),
};
}
QVector<ClangBackEnd::DiagnosticContainer> TranslationUnit::errorDiagnosticsFromHeaders() const
{
return {
createDiagnostic(
QStringLiteral("error: C++ requires a type specifier for all declarations"),
ClangBackEnd::DiagnosticSeverity::Error,
11,
1,
headerFilePath),
};
}
} // anonymous namespace

View File

@@ -277,6 +277,7 @@ TEST_F(ClientServerInProcess, SendDocumentAnnotationsChangedMessage)
ClangBackEnd::DocumentAnnotationsChangedMessage message(fileContainer,
{diagnostic},
{},
{highlightingMark},
QVector<SourceRangeContainer>());

View File

@@ -186,6 +186,7 @@ TEST_F(ReadAndWriteMessageBlock, CompareDocumentAnnotationsChangedMessage)
CompareMessage(ClangBackEnd::DocumentAnnotationsChangedMessage(fileContainer,
{diagnostic},
{},
{highlightingMark},
QVector<ClangBackEnd::SourceRangeContainer>()));
}

View File

@@ -61,6 +61,7 @@ SOURCES += \
clangrequestdocumentannotationsjob-test.cpp \
clangsupportivetranslationunitinitializertest.cpp \
clangstring-test.cpp \
clangtranslationunit-test.cpp \
clangtranslationunits-test.cpp \
clangupdatedocumentannotationsjob-test.cpp \
codecompletionsextractor-test.cpp \