diff --git a/src/plugins/clangcodemodel/clangcodemodel.pro b/src/plugins/clangcodemodel/clangcodemodel.pro index e62ce4823c6..705a4c74787 100644 --- a/src/plugins/clangcodemodel/clangcodemodel.pro +++ b/src/plugins/clangcodemodel/clangcodemodel.pro @@ -23,6 +23,8 @@ SOURCES += \ clangcompletionassistprocessor.cpp \ clangcompletionassistprovider.cpp \ clangcompletioncontextanalyzer.cpp \ + clangdiagnosticfilter.cpp \ + clangdiagnosticmanager.cpp \ clangeditordocumentparser.cpp \ clangeditordocumentprocessor.cpp \ clangfunctionhintmodel.cpp \ @@ -61,6 +63,8 @@ HEADERS += \ clangcompletionassistprocessor.h \ clangcompletionassistprovider.h \ clangcompletioncontextanalyzer.h \ + clangdiagnosticfilter.h \ + clangdiagnosticmanager.h \ clangeditordocumentparser.h \ clangeditordocumentprocessor.h \ clangfunctionhintmodel.h \ diff --git a/src/plugins/clangcodemodel/clangcodemodel.qbs b/src/plugins/clangcodemodel/clangcodemodel.qbs index acd31eb882d..d27ca8e5e3b 100644 --- a/src/plugins/clangcodemodel/clangcodemodel.qbs +++ b/src/plugins/clangcodemodel/clangcodemodel.qbs @@ -136,6 +136,10 @@ QtcPlugin { "clangeditordocumentparser.h", "clangeditordocumentprocessor.cpp", "clangeditordocumentprocessor.h", + "clangdiagnosticfilter.cpp", + "clangdiagnosticfilter.h", + "clangdiagnosticmanager.cpp", + "clangdiagnosticmanager.h", "clangmodelmanagersupport.cpp", "clangmodelmanagersupport.h", "clangcodemodelplugin.cpp", diff --git a/src/plugins/clangcodemodel/clangcodemodelunittestfiles.pri b/src/plugins/clangcodemodel/clangcodemodelunittestfiles.pri index 71055941787..b4806780a30 100644 --- a/src/plugins/clangcodemodel/clangcodemodelunittestfiles.pri +++ b/src/plugins/clangcodemodel/clangcodemodelunittestfiles.pri @@ -3,9 +3,12 @@ INCLUDEPATH += $$PWD SOURCES += $$PWD/completionchunkstotextconverter.cpp \ $$PWD/activationsequenceprocessor.cpp \ $$PWD/activationsequencecontextprocessor.cpp \ - $$PWD/clangcompletioncontextanalyzer.cpp + $$PWD/clangcompletioncontextanalyzer.cpp \ + $$PWD/clangdiagnosticfilter.cpp + HEADERS += $$PWD/completionchunkstotextconverter.h \ $$PWD/activationsequenceprocessor.h \ $$PWD/activationsequencecontextprocessor.h \ - $$PWD/clangcompletioncontextanalyzer.h + $$PWD/clangcompletioncontextanalyzer.h \ + $$PWD/clangdiagnosticfilter.h diff --git a/src/plugins/clangcodemodel/clangdiagnosticfilter.cpp b/src/plugins/clangcodemodel/clangdiagnosticfilter.cpp new file mode 100644 index 00000000000..ac2aecc05a8 --- /dev/null +++ b/src/plugins/clangcodemodel/clangdiagnosticfilter.cpp @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** 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 "clangdiagnosticfilter.h" + +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(); +} + +template +QVector +filterDiagnostics(const QVector &diagnostics, + const Condition &condition) +{ + QVector filteredDiagnostics; + + std::copy_if(diagnostics.cbegin(), + diagnostics.cend(), + std::back_inserter(filteredDiagnostics), + condition); + + return filteredDiagnostics; +} + +} // anonymous namespace + +namespace ClangCodeModel { +namespace Internal { + +void ClangDiagnosticFilter::filterDocumentRelatedWarnings( + const QVector &diagnostics) +{ + const auto isLocalWarning = [this] (const ClangBackEnd::DiagnosticContainer &diagnostic) { + return isWarningOrNote(diagnostic.severity()) + && diagnostic.location().filePath() == m_filePath; + }; + + m_warningDiagnostics = filterDiagnostics(diagnostics, isLocalWarning); +} + +void ClangDiagnosticFilter::filterDocumentRelatedErrors( + const QVector &diagnostics) +{ + const auto isLocalWarning = [this] (const ClangBackEnd::DiagnosticContainer &diagnostic) { + return !isWarningOrNote(diagnostic.severity()) + && diagnostic.location().filePath() == m_filePath; + }; + + m_errorDiagnostics = filterDiagnostics(diagnostics, isLocalWarning); +} + +ClangDiagnosticFilter::ClangDiagnosticFilter(const QString &filePath) + : m_filePath(filePath) +{ +} + +void ClangDiagnosticFilter::filter(const QVector &diagnostics) +{ + filterDocumentRelatedWarnings(diagnostics); + filterDocumentRelatedErrors(diagnostics); +} + +QVector ClangDiagnosticFilter::takeWarnings() +{ + auto diagnostics = m_warningDiagnostics; + m_warningDiagnostics.clear(); + + return diagnostics; +} + +QVector ClangDiagnosticFilter::takeErrors() +{ + auto diagnostics = m_errorDiagnostics; + m_errorDiagnostics.clear(); + + return diagnostics; +} + +} // namespace Internal +} // namespace ClangCodeModel + diff --git a/src/plugins/clangcodemodel/clangdiagnosticfilter.h b/src/plugins/clangcodemodel/clangdiagnosticfilter.h new file mode 100644 index 00000000000..db62086d2fb --- /dev/null +++ b/src/plugins/clangcodemodel/clangdiagnosticfilter.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** 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_INTERNAL_CLANGDIAGNOSTICFILTER_H +#define CLANGCODEMODEL_INTERNAL_CLANGDIAGNOSTICFILTER_H + +#include + +#include + +namespace ClangCodeModel { +namespace Internal { + +class ClangDiagnosticFilter +{ +public: + ClangDiagnosticFilter(const QString &filePath); + + void filter(const QVector &diagnostics); + + QVector takeWarnings(); + QVector takeErrors(); + +private: + void filterDocumentRelatedWarnings(const QVector &diagnostics); + void filterDocumentRelatedErrors(const QVector &diagnostics); + +private: + const QString &m_filePath; + QVector m_warningDiagnostics; + QVector m_errorDiagnostics; +}; + +} // namespace Internal +} // namespace ClangCodeModel + +#endif // CLANGCODEMODEL_INTERNAL_CLANGDIAGNOSTICFILTER_H diff --git a/src/plugins/clangcodemodel/clangdiagnosticmanager.cpp b/src/plugins/clangcodemodel/clangdiagnosticmanager.cpp new file mode 100644 index 00000000000..2c68f275f6c --- /dev/null +++ b/src/plugins/clangcodemodel/clangdiagnosticmanager.cpp @@ -0,0 +1,251 @@ +/**************************************************************************** +** +** 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 "clangdiagnosticfilter.h" +#include "clangdiagnosticmanager.h" + +#include + +#include +#include + +namespace { + +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 &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; +} + +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; +} + +void addSelections(const QVector &diagnostics, + QTextDocument *textDocument, + const QTextCharFormat &mainFormat, + const QTextCharFormat &rangeFormat, + QList &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 QVector &diagnostics, + QTextDocument *textDocument, + QList &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 QVector &diagnostics, + QTextDocument *textDocument, + QList &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 ClangCodeModel { +namespace Internal { + +ClangDiagnosticManager::ClangDiagnosticManager(TextEditor::TextDocument *textDocument) + : m_textDocument(textDocument) +{ +} + +void ClangDiagnosticManager::generateTextMarks() +{ + m_clangTextMarks.clear(); + m_clangTextMarks.reserve(m_warningDiagnostics.size() + m_errorDiagnostics.size()); + + addClangTextMarks(m_warningDiagnostics); + addClangTextMarks(m_errorDiagnostics); +} + +QList ClangDiagnosticManager::takeExtraSelections() +{ + auto extraSelections = m_extraSelections; + + m_extraSelections.clear(); + + return extraSelections; +} + +void ClangDiagnosticManager::generateEditorSelections() +{ + m_extraSelections.clear(); + m_extraSelections.reserve(int(m_warningDiagnostics.size() + m_errorDiagnostics.size())); + + addWarningSelections(m_warningDiagnostics, m_textDocument->document(), m_extraSelections); + addErrorSelections(m_errorDiagnostics, m_textDocument->document(), m_extraSelections); +} + +void ClangDiagnosticManager::processNewDiagnostics( + const QVector &allDiagnostics) +{ + filterDiagnostics(allDiagnostics); + + generateTextMarks(); + generateEditorSelections(); + + clearWarningsAndErrors(); +} + +void ClangDiagnosticManager::addClangTextMarks( + const QVector &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(m_textDocument); + + m_textDocument->addMark(&textMark); + } +} + +void ClangDiagnosticManager::clearWarningsAndErrors() +{ + m_warningDiagnostics.clear(); + m_errorDiagnostics.clear(); +} + +QString ClangDiagnosticManager::filePath() const +{ + return m_textDocument->filePath().toString(); +} + +void ClangDiagnosticManager::filterDiagnostics( + const QVector &diagnostics) +{ + ClangDiagnosticFilter filter(filePath()); + filter.filter(diagnostics); + + m_warningDiagnostics = filter.takeWarnings(); + m_errorDiagnostics = filter.takeErrors(); +} + +} // namespace Internal +} // namespace ClangCodeModel diff --git a/src/plugins/clangcodemodel/clangdiagnosticmanager.h b/src/plugins/clangcodemodel/clangdiagnosticmanager.h new file mode 100644 index 00000000000..27738e89188 --- /dev/null +++ b/src/plugins/clangcodemodel/clangdiagnosticmanager.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** 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_INTERNAL_CLANGDIAGNOSTICMANAGER_H +#define CLANGCODEMODEL_INTERNAL_CLANGDIAGNOSTICMANAGER_H + +#include "clangtextmark.h" + +#include + +#include +#include +#include + +#include + +namespace TextEditor { class TextDocument; } + +namespace ClangCodeModel { +namespace Internal { + +class ClangDiagnosticManager +{ +public: + ClangDiagnosticManager(TextEditor::TextDocument *textDocument); + + void processNewDiagnostics(const QVector &allDiagnostics); + + QList takeExtraSelections(); + +private: + QString filePath() const; + void filterDiagnostics(const QVector &diagnostics); + void generateEditorSelections(); + void generateTextMarks(); + void addClangTextMarks(const QVector &diagnostics); + void clearWarningsAndErrors(); + +private: + TextEditor::TextDocument *m_textDocument; + + QVector m_warningDiagnostics; + QVector m_errorDiagnostics; + QList m_extraSelections; + std::vector m_clangTextMarks; +}; + +} // namespace Internal +} // namespace ClangCodeModel + +#endif // CLANGCODEMODEL_INTERNAL_CLANGDIAGNOSTICMANAGER_H diff --git a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp index 8467c447fa8..6ca43f9e16e 100644 --- a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp +++ b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp @@ -77,6 +77,7 @@ ClangEditorDocumentProcessor::ClangEditorDocumentProcessor( ModelManagerSupportClang *modelManagerSupport, TextEditor::TextDocument *document) : BaseEditorDocumentProcessor(document) + , m_diagnosticManager(document) , m_modelManagerSupport(modelManagerSupport) , m_parser(new ClangEditorDocumentParser(document->filePath().toString())) , m_parserRevision(0) @@ -179,7 +180,8 @@ void ClangEditorDocumentProcessor::updateCodeWarnings(const QVector -std::vector -filterDiagnostics(const QVector &diagnostics, - const Condition &condition) -{ - std::vector filteredDiagnostics; - - std::copy_if(diagnostics.cbegin(), - diagnostics.cend(), - std::back_inserter(filteredDiagnostics), - condition); - - return filteredDiagnostics; -} - -std::vector -filterInterestingWarningDiagnostics(const QVector &diagnostics, - QString &&documentFilePath) -{ - auto isLocalWarning = [documentFilePath] (const ClangBackEnd::DiagnosticContainer &diagnostic) { - return isWarningOrNote(diagnostic.severity()) - && diagnostic.location().filePath() == documentFilePath; - }; - - return filterDiagnostics(diagnostics, isLocalWarning); -} - -std::vector -filterInterestingErrorsDiagnostics(const QVector &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 &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 &diagnostics, - QTextDocument *textDocument, - const QTextCharFormat &mainFormat, - const QTextCharFormat &rangeFormat, - QList &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)); - } -} - -QTextCharFormat fontsettingsTextFormat(TextEditor::TextStyle textStyle) -{ - return TextEditor::TextEditorSettings::fontSettings().toTextCharFormat(textStyle); -} - -void addWarningSelections(const std::vector &diagnostics, - QTextDocument *textDocument, - QList &extraSelections) -{ - addSelections(diagnostics, - textDocument, - fontsettingsTextFormat(TextEditor::C_WARNING), - fontsettingsTextFormat(TextEditor::C_WARNING_CONTEXT), - extraSelections); -} - -void addErrorSelections(const std::vector &diagnostics, - QTextDocument *textDocument, - QList &extraSelections) -{ - addSelections(diagnostics, - textDocument, - fontsettingsTextFormat(TextEditor::C_ERROR), - fontsettingsTextFormat(TextEditor::C_ERROR_CONTEXT), - extraSelections); -} - -} // anonymous namespace - -QList -ClangEditorDocumentProcessor::generateDiagnosticHints(const QVector &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 extraSelections; - extraSelections.reserve(int(warningDiagnostic.size() + errorDiagnostic.size())); - - addWarningSelections(warningDiagnostic, textDocument(), extraSelections); - addErrorSelections(errorDiagnostic, textDocument(), extraSelections); - - return extraSelections; -} - -void ClangEditorDocumentProcessor::addClangTextMarks(const std::vector &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()) { diff --git a/src/plugins/clangcodemodel/clangeditordocumentprocessor.h b/src/plugins/clangcodemodel/clangeditordocumentprocessor.h index 836f41b83d5..d887c59872a 100644 --- a/src/plugins/clangcodemodel/clangeditordocumentprocessor.h +++ b/src/plugins/clangcodemodel/clangeditordocumentprocessor.h @@ -31,8 +31,8 @@ #ifndef CLANGEDITORDOCUMENTPROCESSOR_H #define CLANGEDITORDOCUMENTPROCESSOR_H +#include "clangdiagnosticmanager.h" #include "clangeditordocumentparser.h" -#include "clangtextmark.h" #include #include @@ -41,8 +41,6 @@ #include #include -#include - namespace ClangBackEnd { class DiagnosticContainer; } @@ -85,15 +83,12 @@ private slots: private: void updateProjectPartAndTranslationUnitForEditor(); void updateTranslationUnitForEditor(CppTools::ProjectPart &projectPart); - QList - generateDiagnosticHints(const QVector &diagnostics); - void addClangTextMarks(const std::vector &diagnostics); void requestDiagnostics(CppTools::ProjectPart &projectPart); void requestDiagnostics(); private: + ClangDiagnosticManager m_diagnosticManager; QPointer m_modelManagerSupport; - std::vector m_clangTextMarks; QSharedPointer m_parser; CppTools::ProjectPart::Ptr m_projectPart; QFutureWatcher m_parserWatcher; diff --git a/tests/unit/unittest/clangdiagnosticfiltertest.cpp b/tests/unit/unittest/clangdiagnosticfiltertest.cpp new file mode 100644 index 00000000000..435f4b7252a --- /dev/null +++ b/tests/unit/unittest/clangdiagnosticfiltertest.cpp @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** 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 Digia. For licensing terms and +** conditions see http://www.qt.io/licensing. 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include +#include + +#include +#include +#include +#include "gtest-qt-printing.h" + +namespace { + +using ::testing::Contains; +using ::testing::Not; + +using ClangBackEnd::DiagnosticContainer; + +DiagnosticContainer createDiagnostic(const QString &text, + ClangBackEnd::DiagnosticSeverity severity, + const QString &filePath) +{ + return DiagnosticContainer( + text, + Utf8StringLiteral(""), + {}, + severity, + {filePath, 0u, 0u}, + {}, + {}, + {} + ); +} + +const QString mainFilePath = QString::fromUtf8(TESTDATA_DIR "/diagnostic_erroneous_source.cpp"); +const QString includedFilePath = QString::fromUtf8(TESTDATA_DIR "/diagnostic_erroneous_header.cpp"); + +DiagnosticContainer warningFromIncludedFile() +{ + return createDiagnostic( + QStringLiteral("warning: control reaches end of non-void function"), + ClangBackEnd::DiagnosticSeverity::Warning, + includedFilePath); +} + +DiagnosticContainer errorFromIncludedFile() +{ + return createDiagnostic( + QStringLiteral("C++ requires a type specifier for all declarations"), + ClangBackEnd::DiagnosticSeverity::Error, + includedFilePath); +} + +DiagnosticContainer warningFromMainFile() +{ + return createDiagnostic( + QStringLiteral("warning: enumeration value 'Three' not handled in switch"), + ClangBackEnd::DiagnosticSeverity::Warning, + mainFilePath); +} + +DiagnosticContainer errorFromMainFile() +{ + return createDiagnostic( + QStringLiteral("error: void function 'g' should not return a value"), + ClangBackEnd::DiagnosticSeverity::Error, + mainFilePath); +} + +class ClangDiagnosticFilter : public ::testing::Test +{ +protected: + ClangCodeModel::Internal::ClangDiagnosticFilter clangDiagnosticFilter{mainFilePath}; +}; + +TEST_F(ClangDiagnosticFilter, WarningsFromMainFileAreLetThrough) +{ + clangDiagnosticFilter.filter({warningFromMainFile()}); + + ASSERT_THAT(clangDiagnosticFilter.takeWarnings(),Contains(warningFromMainFile())); +} + +TEST_F(ClangDiagnosticFilter, WarningsFromIncludedFileAreIgnored) +{ + clangDiagnosticFilter.filter({warningFromIncludedFile()}); + + ASSERT_THAT(clangDiagnosticFilter.takeWarnings(), Not(Contains(warningFromIncludedFile()))); +} + +TEST_F(ClangDiagnosticFilter, ErrorsFromMainFileAreLetThrough) +{ + clangDiagnosticFilter.filter({errorFromMainFile()}); + + ASSERT_THAT(clangDiagnosticFilter.takeErrors(), Contains(errorFromMainFile())); +} + +TEST_F(ClangDiagnosticFilter, ErrorsFromIncludedFileAreIgnored) +{ + clangDiagnosticFilter.filter({errorFromIncludedFile()}); + + ASSERT_THAT(clangDiagnosticFilter.takeErrors(), Not(Contains(errorFromIncludedFile()))); +} + +} // anonymous namespace diff --git a/tests/unit/unittest/data/diagnostic_erroneous_header.h b/tests/unit/unittest/data/diagnostic_erroneous_header.h new file mode 100644 index 00000000000..6dea03a732d --- /dev/null +++ b/tests/unit/unittest/data/diagnostic_erroneous_header.h @@ -0,0 +1,5 @@ +int warningInHeader() +{ +} + +errorInHeader; diff --git a/tests/unit/unittest/data/diagnostic_erroneous_source.cpp b/tests/unit/unittest/data/diagnostic_erroneous_source.cpp new file mode 100644 index 00000000000..1ab28ebef3d --- /dev/null +++ b/tests/unit/unittest/data/diagnostic_erroneous_source.cpp @@ -0,0 +1,16 @@ +#include "diagnostic_erroneous_header.h" + +enum Numbers { One, Two, Three }; + +void f(Numbers n) +{ + switch (n) { + case One: return; + case Two: return; + } +} + +void g() +{ + return 3; +} diff --git a/tests/unit/unittest/unittest.pro b/tests/unit/unittest/unittest.pro index ef5b91733b6..f971665e3d3 100644 --- a/tests/unit/unittest/unittest.pro +++ b/tests/unit/unittest/unittest.pro @@ -23,6 +23,7 @@ SOURCES += \ activationsequenceprocessortest.cpp \ clangcodecompleteresultstest.cpp \ clangcompletioncontextanalyzertest.cpp \ + clangdiagnosticfiltertest.cpp \ clangipcservertest.cpp \ clangstringtest.cpp \ clientserverinprocesstest.cpp \