forked from qt-creator/qt-creator
Clang: Integrate clang's fixits as refactoring actions
They are invokable by the usual means (Alt+Enter, editor's context menu
> Refactor) plus by the context menu of the editor's left margin for the
related line.
The fixit text comes directly from libclang and is thus not translated.
We modify the text slighty by stripping the diagnostic category prefix
("note:", "error:", ...) and capitalizing the first letter.
A follow-up change should properly indicate available refactorings with
a refactoring icon in the editor's left margin.
Task-number: QTCREATORBUG-14868
Change-Id: I86157c9f824d2a9dedf19087476d02ad1e6cc854
Reviewed-by: Marco Bubke <marco.bubke@theqtcompany.com>
This commit is contained in:
@@ -27,6 +27,8 @@ SOURCES += \
|
||||
clangdiagnosticmanager.cpp \
|
||||
clangeditordocumentparser.cpp \
|
||||
clangeditordocumentprocessor.cpp \
|
||||
clangfixitoperation.cpp \
|
||||
clangfixitoperationsextractor.cpp \
|
||||
clangfunctionhintmodel.cpp \
|
||||
clangmodelmanagersupport.cpp \
|
||||
clangprojectsettings.cpp \
|
||||
@@ -67,6 +69,8 @@ HEADERS += \
|
||||
clangdiagnosticmanager.h \
|
||||
clangeditordocumentparser.h \
|
||||
clangeditordocumentprocessor.h \
|
||||
clangfixitoperation.h \
|
||||
clangfixitoperationsextractor.h \
|
||||
clangfunctionhintmodel.h \
|
||||
clang_global.h \
|
||||
clangmodelmanagersupport.h \
|
||||
|
||||
@@ -140,6 +140,10 @@ QtcPlugin {
|
||||
"clangdiagnosticfilter.h",
|
||||
"clangdiagnosticmanager.cpp",
|
||||
"clangdiagnosticmanager.h",
|
||||
"clangfixitoperation.cpp",
|
||||
"clangfixitoperation.h",
|
||||
"clangfixitoperationsextractor.cpp",
|
||||
"clangfixitoperationsextractor.h",
|
||||
"clangmodelmanagersupport.cpp",
|
||||
"clangmodelmanagersupport.h",
|
||||
"clangcodemodelplugin.cpp",
|
||||
|
||||
@@ -4,11 +4,12 @@ SOURCES += $$PWD/completionchunkstotextconverter.cpp \
|
||||
$$PWD/activationsequenceprocessor.cpp \
|
||||
$$PWD/activationsequencecontextprocessor.cpp \
|
||||
$$PWD/clangcompletioncontextanalyzer.cpp \
|
||||
$$PWD/clangdiagnosticfilter.cpp
|
||||
|
||||
$$PWD/clangdiagnosticfilter.cpp \
|
||||
$$PWD/clangfixitoperation.cpp
|
||||
|
||||
HEADERS += $$PWD/completionchunkstotextconverter.h \
|
||||
$$PWD/activationsequenceprocessor.h \
|
||||
$$PWD/activationsequencecontextprocessor.h \
|
||||
$$PWD/clangcompletioncontextanalyzer.h \
|
||||
$$PWD/clangdiagnosticfilter.h
|
||||
$$PWD/clangdiagnosticfilter.h \
|
||||
$$PWD/clangfixitoperation.h
|
||||
|
||||
@@ -61,6 +61,18 @@ filterDiagnostics(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
|
||||
return filteredDiagnostics;
|
||||
}
|
||||
|
||||
template <class Condition>
|
||||
void
|
||||
filterDiagnostics(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
|
||||
const Condition &condition,
|
||||
QVector<ClangBackEnd::DiagnosticContainer> &filteredDiagnostic)
|
||||
{
|
||||
std::copy_if(diagnostics.cbegin(),
|
||||
diagnostics.cend(),
|
||||
std::back_inserter(filteredDiagnostic),
|
||||
condition);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
namespace ClangCodeModel {
|
||||
@@ -88,6 +100,22 @@ void ClangDiagnosticFilter::filterDocumentRelatedErrors(
|
||||
m_errorDiagnostics = filterDiagnostics(diagnostics, isLocalWarning);
|
||||
}
|
||||
|
||||
void ClangDiagnosticFilter::filterFixits()
|
||||
{
|
||||
const auto hasFixIts = [] (const ClangBackEnd::DiagnosticContainer &diagnostic) {
|
||||
return diagnostic.fixIts().size() > 0;
|
||||
};
|
||||
|
||||
m_fixItdiagnostics.clear();
|
||||
filterDiagnostics(m_warningDiagnostics, hasFixIts, m_fixItdiagnostics);
|
||||
filterDiagnostics(m_errorDiagnostics, hasFixIts, m_fixItdiagnostics);
|
||||
|
||||
for (const auto &warningDiagnostic : m_warningDiagnostics)
|
||||
filterDiagnostics(warningDiagnostic.children(), hasFixIts, m_fixItdiagnostics);
|
||||
for (const auto &warningDiagnostic : m_errorDiagnostics)
|
||||
filterDiagnostics(warningDiagnostic.children(), hasFixIts, m_fixItdiagnostics);
|
||||
}
|
||||
|
||||
ClangDiagnosticFilter::ClangDiagnosticFilter(const QString &filePath)
|
||||
: m_filePath(filePath)
|
||||
{
|
||||
@@ -97,6 +125,7 @@ void ClangDiagnosticFilter::filter(const QVector<ClangBackEnd::DiagnosticContain
|
||||
{
|
||||
filterDocumentRelatedWarnings(diagnostics);
|
||||
filterDocumentRelatedErrors(diagnostics);
|
||||
filterFixits();
|
||||
}
|
||||
|
||||
QVector<ClangBackEnd::DiagnosticContainer> ClangDiagnosticFilter::takeWarnings()
|
||||
@@ -115,6 +144,14 @@ QVector<ClangBackEnd::DiagnosticContainer> ClangDiagnosticFilter::takeErrors()
|
||||
return diagnostics;
|
||||
}
|
||||
|
||||
QVector<ClangBackEnd::DiagnosticContainer> ClangDiagnosticFilter::takeFixIts()
|
||||
{
|
||||
auto diagnostics = m_fixItdiagnostics;
|
||||
m_fixItdiagnostics.clear();
|
||||
|
||||
return diagnostics;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangCodeModel
|
||||
|
||||
|
||||
@@ -47,15 +47,19 @@ public:
|
||||
|
||||
QVector<ClangBackEnd::DiagnosticContainer> takeWarnings();
|
||||
QVector<ClangBackEnd::DiagnosticContainer> takeErrors();
|
||||
QVector<ClangBackEnd::DiagnosticContainer> takeFixIts();
|
||||
|
||||
private:
|
||||
void filterDocumentRelatedWarnings(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics);
|
||||
void filterDocumentRelatedErrors(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics);
|
||||
void filterFixits();
|
||||
|
||||
private:
|
||||
const QString &m_filePath;
|
||||
|
||||
QVector<ClangBackEnd::DiagnosticContainer> m_warningDiagnostics;
|
||||
QVector<ClangBackEnd::DiagnosticContainer> m_errorDiagnostics;
|
||||
QVector<ClangBackEnd::DiagnosticContainer> m_fixItdiagnostics;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
@@ -208,6 +208,12 @@ void ClangDiagnosticManager::processNewDiagnostics(
|
||||
clearWarningsAndErrors();
|
||||
}
|
||||
|
||||
const QVector<ClangBackEnd::DiagnosticContainer> &
|
||||
ClangDiagnosticManager::diagnosticsWithFixIts() const
|
||||
{
|
||||
return m_fixItdiagnostics;
|
||||
}
|
||||
|
||||
void ClangDiagnosticManager::addClangTextMarks(
|
||||
const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics)
|
||||
{
|
||||
@@ -245,6 +251,7 @@ void ClangDiagnosticManager::filterDiagnostics(
|
||||
|
||||
m_warningDiagnostics = filter.takeWarnings();
|
||||
m_errorDiagnostics = filter.takeErrors();
|
||||
m_fixItdiagnostics = filter.takeFixIts();
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
@@ -53,6 +53,7 @@ public:
|
||||
|
||||
void processNewDiagnostics(const QVector<ClangBackEnd::DiagnosticContainer> &allDiagnostics);
|
||||
|
||||
const QVector<ClangBackEnd::DiagnosticContainer> &diagnosticsWithFixIts() const;
|
||||
QList<QTextEdit::ExtraSelection> takeExtraSelections();
|
||||
|
||||
private:
|
||||
@@ -68,6 +69,7 @@ private:
|
||||
|
||||
QVector<ClangBackEnd::DiagnosticContainer> m_warningDiagnostics;
|
||||
QVector<ClangBackEnd::DiagnosticContainer> m_errorDiagnostics;
|
||||
QVector<ClangBackEnd::DiagnosticContainer> m_fixItdiagnostics;
|
||||
QList<QTextEdit::ExtraSelection> m_extraSelections;
|
||||
std::vector<ClangTextMark> m_clangTextMarks;
|
||||
};
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
|
||||
#include "clangeditordocumentprocessor.h"
|
||||
|
||||
#include "clangfixitoperation.h"
|
||||
#include "clangfixitoperationsextractor.h"
|
||||
#include "clangmodelmanagersupport.h"
|
||||
#include "clangutils.h"
|
||||
#include "cppcreatemarkers.h"
|
||||
@@ -42,6 +44,7 @@
|
||||
#include <cpptools/cpptoolsplugin.h>
|
||||
#include <cpptools/cppworkingcopy.h>
|
||||
|
||||
#include <texteditor/convenience.h>
|
||||
#include <texteditor/fontsettings.h>
|
||||
#include <texteditor/texteditor.h>
|
||||
#include <texteditor/texteditorconstants.h>
|
||||
@@ -186,6 +189,24 @@ void ClangEditorDocumentProcessor::updateCodeWarnings(const QVector<ClangBackEnd
|
||||
}
|
||||
}
|
||||
|
||||
static int currentLine(const TextEditor::AssistInterface &assistInterface)
|
||||
{
|
||||
int line, column;
|
||||
TextEditor::Convenience::convertPosition(assistInterface.textDocument(),
|
||||
assistInterface.position(),
|
||||
&line,
|
||||
&column);
|
||||
return line;
|
||||
}
|
||||
|
||||
TextEditor::QuickFixOperations ClangEditorDocumentProcessor::extraRefactoringOperations(
|
||||
const TextEditor::AssistInterface &assistInterface)
|
||||
{
|
||||
ClangFixItOperationsExtractor extractor(m_diagnosticManager.diagnosticsWithFixIts());
|
||||
|
||||
return extractor.extract(assistInterface.fileName(), currentLine(assistInterface));
|
||||
}
|
||||
|
||||
ClangEditorDocumentProcessor *ClangEditorDocumentProcessor::get(const QString &filePath)
|
||||
{
|
||||
return qobject_cast<ClangEditorDocumentProcessor *>(BaseEditorDocumentProcessor::get(filePath));
|
||||
|
||||
@@ -73,6 +73,9 @@ public:
|
||||
void updateCodeWarnings(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
|
||||
uint documentRevision);
|
||||
|
||||
TextEditor::QuickFixOperations
|
||||
extraRefactoringOperations(const TextEditor::AssistInterface &assistInterface) override;
|
||||
|
||||
public:
|
||||
static ClangEditorDocumentProcessor *get(const QString &filePath);
|
||||
|
||||
|
||||
79
src/plugins/clangcodemodel/clangfixitoperation.cpp
Normal file
79
src/plugins/clangcodemodel/clangfixitoperation.cpp
Normal file
@@ -0,0 +1,79 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "clangfixitoperation.h"
|
||||
|
||||
#include <texteditor/refactoringchanges.h>
|
||||
|
||||
namespace ClangCodeModel {
|
||||
|
||||
ClangFixItOperation::ClangFixItOperation(const Utf8String &filePath,
|
||||
const Utf8String &fixItText,
|
||||
const QVector<ClangBackEnd::FixItContainer> &fixItContainers)
|
||||
: filePath(filePath),
|
||||
fixItText(fixItText),
|
||||
fixItContainers(fixItContainers)
|
||||
{
|
||||
}
|
||||
|
||||
int ClangFixItOperation::priority() const
|
||||
{
|
||||
return 10;
|
||||
}
|
||||
|
||||
QString ClangCodeModel::ClangFixItOperation::description() const
|
||||
{
|
||||
return QStringLiteral("Apply Fix: ") + fixItText.toString();
|
||||
}
|
||||
|
||||
void ClangFixItOperation::perform()
|
||||
{
|
||||
const TextEditor::RefactoringChanges refactoringChanges;
|
||||
TextEditor::RefactoringFilePtr refactoringFile = refactoringChanges.file(filePath.toString());
|
||||
refactoringFile->setChangeSet(changeSet());
|
||||
refactoringFile->apply();
|
||||
}
|
||||
|
||||
Utils::ChangeSet ClangFixItOperation::changeSet() const
|
||||
{
|
||||
Utils::ChangeSet changeSet;
|
||||
|
||||
for (const auto &fixItContainer : fixItContainers) {
|
||||
const auto range = fixItContainer.range();
|
||||
changeSet.replace(range.start().offset(),
|
||||
range.end().offset(),
|
||||
fixItContainer.text());
|
||||
}
|
||||
|
||||
return changeSet;
|
||||
}
|
||||
|
||||
} // namespace ClangCodeModel
|
||||
|
||||
64
src/plugins/clangcodemodel/clangfixitoperation.h
Normal file
64
src/plugins/clangcodemodel/clangfixitoperation.h
Normal file
@@ -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_CLANGFIXITOPERATION_H
|
||||
#define CLANGCODEMODEL_CLANGFIXITOPERATION_H
|
||||
|
||||
#include <texteditor/quickfix.h>
|
||||
|
||||
#include <clangbackendipc/fixitcontainer.h>
|
||||
#include <utils/changeset.h>
|
||||
|
||||
#include <QVector>
|
||||
|
||||
namespace ClangCodeModel {
|
||||
|
||||
class ClangFixItOperation : public TextEditor::QuickFixOperation
|
||||
{
|
||||
public:
|
||||
ClangFixItOperation(const Utf8String &filePath,
|
||||
const Utf8String &fixItText,
|
||||
const QVector<ClangBackEnd::FixItContainer> &fixItContainers);
|
||||
|
||||
int priority() const override;
|
||||
QString description() const override;
|
||||
void perform() override;
|
||||
|
||||
Utils::ChangeSet changeSet() const;
|
||||
|
||||
private:
|
||||
Utf8String filePath;
|
||||
Utf8String fixItText;
|
||||
QVector<ClangBackEnd::FixItContainer> fixItContainers;
|
||||
};
|
||||
|
||||
} // namespace ClangCodeModel
|
||||
|
||||
#endif // CLANGCODEMODEL_CLANGFIXITOPERATION_H
|
||||
143
src/plugins/clangcodemodel/clangfixitoperationsextractor.cpp
Normal file
143
src/plugins/clangcodemodel/clangfixitoperationsextractor.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "clangfixitoperationsextractor.h"
|
||||
|
||||
#include "clangfixitoperation.h"
|
||||
|
||||
#include <clangbackendipc/sourcerangecontainer.h>
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
using ClangBackEnd::DiagnosticContainer;
|
||||
|
||||
namespace {
|
||||
|
||||
void capitalizeText(QString &text)
|
||||
{
|
||||
text[0] = text[0].toUpper();
|
||||
}
|
||||
|
||||
void removeDiagnosticCategoryPrefix(QString &text)
|
||||
{
|
||||
// Prefixes are taken from $LLVM_SOURCE_DIR/tools/clang/lib/Frontend/TextDiagnostic.cpp,
|
||||
// function TextDiagnostic::printDiagnosticLevel (llvm-3.6.2).
|
||||
static const QStringList categoryPrefixes = {
|
||||
QStringLiteral("note"),
|
||||
QStringLiteral("remark"),
|
||||
QStringLiteral("warning"),
|
||||
QStringLiteral("error"),
|
||||
QStringLiteral("fatal error")
|
||||
};
|
||||
|
||||
foreach (const QString &prefix, categoryPrefixes) {
|
||||
const QString fullPrefix = prefix + QStringLiteral(": ");
|
||||
if (text.startsWith(fullPrefix)) {
|
||||
text.remove(0, fullPrefix.length());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString tweakedDiagnosticText(const QString &diagnosticText)
|
||||
{
|
||||
// Examples:
|
||||
// "note: use '==' to turn this assignment into an equality comparison"
|
||||
// "error: expected ';' at end of declaration"
|
||||
|
||||
QString tweakedText = diagnosticText;
|
||||
|
||||
if (!tweakedText.isEmpty()) {
|
||||
removeDiagnosticCategoryPrefix(tweakedText);
|
||||
capitalizeText(tweakedText);
|
||||
}
|
||||
|
||||
return tweakedText;
|
||||
}
|
||||
|
||||
bool isDiagnosticFromFileAtLine(const DiagnosticContainer &diagnosticContainer,
|
||||
const QString &filePath,
|
||||
int line)
|
||||
{
|
||||
const auto location = diagnosticContainer.location();
|
||||
return location.filePath().toString() == filePath
|
||||
&& location.line() == uint(line);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
namespace ClangCodeModel {
|
||||
|
||||
ClangFixItOperationsExtractor::ClangFixItOperationsExtractor(
|
||||
const QVector<DiagnosticContainer> &diagnosticContainers)
|
||||
: diagnosticContainers(diagnosticContainers)
|
||||
{
|
||||
}
|
||||
|
||||
TextEditor::QuickFixOperations
|
||||
ClangFixItOperationsExtractor::extract(const QString &filePath, int line)
|
||||
{
|
||||
foreach (const DiagnosticContainer &diagnosticContainer, diagnosticContainers)
|
||||
extractFromDiagnostic(diagnosticContainer, filePath, line);
|
||||
|
||||
return operations;
|
||||
}
|
||||
|
||||
void ClangFixItOperationsExtractor::appendFixitOperationsFromDiagnostic(
|
||||
const QString &filePath,
|
||||
const DiagnosticContainer &diagnosticContainer)
|
||||
{
|
||||
const auto fixIts = diagnosticContainer.fixIts();
|
||||
if (!fixIts.isEmpty()) {
|
||||
const QString diagnosticText = tweakedDiagnosticText(diagnosticContainer.text().toString());
|
||||
TextEditor::QuickFixOperation::Ptr operation(
|
||||
new ClangFixItOperation(filePath,
|
||||
diagnosticText,
|
||||
fixIts));
|
||||
operations.append(operation);
|
||||
}
|
||||
}
|
||||
|
||||
void ClangFixItOperationsExtractor::extractFromDiagnostic(
|
||||
const DiagnosticContainer &diagnosticContainer,
|
||||
const QString &filePath,
|
||||
int line)
|
||||
{
|
||||
if (isDiagnosticFromFileAtLine(diagnosticContainer, filePath, line)) {
|
||||
appendFixitOperationsFromDiagnostic(filePath, diagnosticContainer);
|
||||
|
||||
foreach (const auto &child, diagnosticContainer.children())
|
||||
extractFromDiagnostic(child, filePath, line);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ClangCodeModel
|
||||
61
src/plugins/clangcodemodel/clangfixitoperationsextractor.h
Normal file
61
src/plugins/clangcodemodel/clangfixitoperationsextractor.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 CLANGFIXITOPERATIONSEXTRACTOR_H
|
||||
#define CLANGFIXITOPERATIONSEXTRACTOR_H
|
||||
|
||||
#include <texteditor/quickfix.h>
|
||||
|
||||
#include <clangbackendipc/diagnosticcontainer.h>
|
||||
|
||||
namespace ClangCodeModel {
|
||||
|
||||
class ClangFixItOperationsExtractor
|
||||
{
|
||||
public:
|
||||
ClangFixItOperationsExtractor(const QVector<ClangBackEnd::DiagnosticContainer> &diagnosticContainers);
|
||||
|
||||
TextEditor::QuickFixOperations extract(const QString &filePath, int line);
|
||||
|
||||
private:
|
||||
void extractFromDiagnostic(const ClangBackEnd::DiagnosticContainer &diagnosticContainer,
|
||||
const QString &filePath,
|
||||
int line);
|
||||
void appendFixitOperationsFromDiagnostic(const QString &filePath,
|
||||
const ClangBackEnd::DiagnosticContainer &diagnosticContainer);
|
||||
|
||||
private:
|
||||
const QVector<ClangBackEnd::DiagnosticContainer> &diagnosticContainers;
|
||||
TextEditor::QuickFixOperations operations;
|
||||
};
|
||||
|
||||
} // namespace ClangCodeModel
|
||||
|
||||
#endif // CLANGFIXITOPERATIONSEXTRACTOR_H
|
||||
@@ -45,6 +45,8 @@
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QMenu>
|
||||
#include <QTextBlock>
|
||||
|
||||
using namespace ClangCodeModel;
|
||||
using namespace ClangCodeModel::Internal;
|
||||
@@ -137,6 +139,15 @@ void ModelManagerSupportClang::connectTextDocumentToUnsavedFiles(TextEditor::Tex
|
||||
Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
void ModelManagerSupportClang::connectToWidgetsMarkContextMenuRequested(QWidget *editorWidget)
|
||||
{
|
||||
const auto widget = qobject_cast<TextEditor::TextEditorWidget *>(editorWidget);
|
||||
if (widget) {
|
||||
connect(widget, &TextEditor::TextEditorWidget::markContextMenuRequested,
|
||||
this, &ModelManagerSupportClang::onTextMarkContextMenuRequested);
|
||||
}
|
||||
}
|
||||
|
||||
void ModelManagerSupportClang::onEditorOpened(Core::IEditor *editor)
|
||||
{
|
||||
QTC_ASSERT(editor, return);
|
||||
@@ -145,10 +156,13 @@ void ModelManagerSupportClang::onEditorOpened(Core::IEditor *editor)
|
||||
TextEditor::TextDocument *textDocument = qobject_cast<TextEditor::TextDocument *>(document);
|
||||
|
||||
if (textDocument && cppModelManager()->isCppEditor(editor)) {
|
||||
if (cppModelManager()->isManagedByModelManagerSupport(textDocument, QLatin1String(Constants::CLANG_MODELMANAGERSUPPORT_ID)))
|
||||
const QString clangSupportId = QLatin1String(Constants::CLANG_MODELMANAGERSUPPORT_ID);
|
||||
if (cppModelManager()->isManagedByModelManagerSupport(textDocument, clangSupportId)) {
|
||||
connectTextDocumentToTranslationUnit(textDocument);
|
||||
else
|
||||
connectToWidgetsMarkContextMenuRequested(editor->widget());
|
||||
} else {
|
||||
connectTextDocumentToUnsavedFiles(textDocument);
|
||||
}
|
||||
|
||||
// TODO: Ensure that not fully loaded documents are updated?
|
||||
}
|
||||
@@ -203,6 +217,50 @@ void ModelManagerSupportClang::onAbstractEditorSupportRemoved(const QString &fil
|
||||
}
|
||||
}
|
||||
|
||||
void addFixItsActionsToMenu(QMenu *menu, const TextEditor::QuickFixOperations &fixItOperations)
|
||||
{
|
||||
foreach (const auto &fixItOperation, fixItOperations) {
|
||||
QAction *action = menu->addAction(fixItOperation->description());
|
||||
QObject::connect(action, &QAction::triggered, [fixItOperation]() {
|
||||
fixItOperation->perform();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static int lineToPosition(const QTextDocument *textDocument, int lineNumber)
|
||||
{
|
||||
QTC_ASSERT(textDocument, return 0);
|
||||
const QTextBlock textBlock = textDocument->findBlockByLineNumber(lineNumber);
|
||||
return textBlock.isValid() ? textBlock.position() - 1 : 0;
|
||||
}
|
||||
|
||||
static TextEditor::AssistInterface createAssistInterface(TextEditor::TextEditorWidget *widget,
|
||||
int lineNumber)
|
||||
{
|
||||
return TextEditor::AssistInterface(widget->document(),
|
||||
lineToPosition(widget->document(), lineNumber),
|
||||
widget->textDocument()->filePath().toString(),
|
||||
TextEditor::IdleEditor);
|
||||
}
|
||||
|
||||
void ModelManagerSupportClang::onTextMarkContextMenuRequested(TextEditor::TextEditorWidget *widget,
|
||||
int lineNumber,
|
||||
QMenu *menu)
|
||||
{
|
||||
QTC_ASSERT(widget, return);
|
||||
QTC_ASSERT(lineNumber >= 1, return);
|
||||
QTC_ASSERT(menu, return);
|
||||
|
||||
const auto filePath = widget->textDocument()->filePath().toString();
|
||||
ClangEditorDocumentProcessor *processor = ClangEditorDocumentProcessor::get(filePath);
|
||||
if (processor) {
|
||||
const auto assistInterface = createAssistInterface(widget, lineNumber);
|
||||
const auto fixItOperations = processor->extraRefactoringOperations(assistInterface);
|
||||
|
||||
addFixItsActionsToMenu(menu, fixItOperations);
|
||||
}
|
||||
}
|
||||
|
||||
void ModelManagerSupportClang::onProjectPartsUpdated(ProjectExplorer::Project *project)
|
||||
{
|
||||
QTC_ASSERT(project, return);
|
||||
|
||||
@@ -38,7 +38,13 @@
|
||||
#include <QObject>
|
||||
#include <QScopedPointer>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QMenu;
|
||||
class QWidget;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Core { class IDocument; }
|
||||
namespace TextEditor { class TextEditorWidget; }
|
||||
|
||||
namespace ClangCodeModel {
|
||||
namespace Internal {
|
||||
@@ -76,11 +82,16 @@ private:
|
||||
void onAbstractEditorSupportContentsUpdated(const QString &filePath, const QByteArray &content);
|
||||
void onAbstractEditorSupportRemoved(const QString &filePath);
|
||||
|
||||
void onTextMarkContextMenuRequested(TextEditor::TextEditorWidget *widget,
|
||||
int lineNumber,
|
||||
QMenu *menu);
|
||||
|
||||
void onProjectPartsUpdated(ProjectExplorer::Project *project);
|
||||
void onProjectPartsRemoved(const QStringList &projectPartIds);
|
||||
|
||||
void connectTextDocumentToTranslationUnit(TextEditor::TextDocument *textDocument);
|
||||
void connectTextDocumentToUnsavedFiles(TextEditor::TextDocument *textDocument);
|
||||
void connectToWidgetsMarkContextMenuRequested(QWidget *editorWidget);
|
||||
|
||||
private:
|
||||
IpcCommunicator m_ipcCommunicator;
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/messagebox.h>
|
||||
|
||||
#include <cpptools/baseeditordocumentprocessor.h>
|
||||
#include <cpptools/cppclassesfilter.h>
|
||||
#include <cpptools/cppcodestylesettings.h>
|
||||
#include <cpptools/cpppointerdeclarationformatter.h>
|
||||
@@ -132,6 +133,8 @@ void registerQuickFixes(ExtensionSystem::IPlugin *plugIn)
|
||||
plugIn->addAutoReleasedObject(new OptimizeForLoop);
|
||||
|
||||
plugIn->addAutoReleasedObject(new EscapeStringLiteral);
|
||||
|
||||
plugIn->addAutoReleasedObject(new ExtraRefactoringOperations);
|
||||
}
|
||||
|
||||
// In the following anonymous namespace all functions are collected, which could be of interest for
|
||||
@@ -5956,5 +5959,15 @@ void ConvertQt4Connect::match(const CppQuickFixInterface &interface, QuickFixOpe
|
||||
}
|
||||
}
|
||||
|
||||
void ExtraRefactoringOperations::match(const CppQuickFixInterface &interface,
|
||||
QuickFixOperations &result)
|
||||
{
|
||||
const auto processor = CppTools::BaseEditorDocumentProcessor::get(interface.fileName());
|
||||
if (processor) {
|
||||
const auto clangFixItOperations = processor->extraRefactoringOperations(interface);
|
||||
result.append(clangFixItOperations);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace CppEditor
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#ifndef CPPQUICKFIXES_H
|
||||
#define CPPQUICKFIXES_H
|
||||
|
||||
#include "cppeditor_global.h"
|
||||
#include "cppquickfix.h"
|
||||
|
||||
#include <cpptools/cpprefactoringchanges.h>
|
||||
@@ -62,6 +63,12 @@ namespace Internal {
|
||||
|
||||
void registerQuickFixes(ExtensionSystem::IPlugin *plugIn);
|
||||
|
||||
class ExtraRefactoringOperations : public CppQuickFixFactory
|
||||
{
|
||||
public:
|
||||
void match(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
|
||||
};
|
||||
|
||||
/*!
|
||||
Adds an include for an undefined identifier or only forward declared identifier.
|
||||
|
||||
|
||||
@@ -64,6 +64,12 @@ TextEditor::TextDocument *BaseEditorDocumentProcessor::baseTextDocument() const
|
||||
return m_baseTextDocument;
|
||||
}
|
||||
|
||||
TextEditor::QuickFixOperations
|
||||
BaseEditorDocumentProcessor::extraRefactoringOperations(const TextEditor::AssistInterface &)
|
||||
{
|
||||
return TextEditor::QuickFixOperations();
|
||||
}
|
||||
|
||||
BaseEditorDocumentProcessor *BaseEditorDocumentProcessor::get(const QString &filePath)
|
||||
{
|
||||
CppModelManager *cmmi = CppModelManager::instance();
|
||||
|
||||
@@ -35,8 +35,10 @@
|
||||
#include "cppsemanticinfo.h"
|
||||
#include "cpptools_global.h"
|
||||
|
||||
#include <texteditor/codeassist/assistinterface.h>
|
||||
#include <texteditor/texteditor.h>
|
||||
#include <texteditor/textdocument.h>
|
||||
#include <texteditor/quickfix.h>
|
||||
|
||||
#include <cplusplus/CppDocument.h>
|
||||
|
||||
@@ -65,6 +67,9 @@ public:
|
||||
virtual BaseEditorDocumentParser::Ptr parser() = 0;
|
||||
virtual bool isParserRunning() const = 0;
|
||||
|
||||
virtual TextEditor::QuickFixOperations
|
||||
extraRefactoringOperations(const TextEditor::AssistInterface &assistInterface);
|
||||
|
||||
public:
|
||||
static BaseEditorDocumentProcessor *get(const QString &filePath);
|
||||
|
||||
|
||||
65
tests/unit/mockup/texteditor/quickfix.h
Normal file
65
tests/unit/mockup/texteditor/quickfix.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 TEXTEDITORQUICKFIX_H
|
||||
#define TEXTEDITORQUICKFIX_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace TextEditor {
|
||||
|
||||
class AssistInterface;
|
||||
|
||||
class QuickFixOperation
|
||||
{
|
||||
public:
|
||||
QuickFixOperation(int priority = -1)
|
||||
: _priority(priority)
|
||||
{}
|
||||
|
||||
virtual ~QuickFixOperation() {}
|
||||
|
||||
virtual int priority() const { return _priority; }
|
||||
void setPriority(int priority) { _priority = priority; }
|
||||
|
||||
virtual QString description() const { return _description; }
|
||||
void setDescription(const QString &description) { _description = description; }
|
||||
|
||||
virtual void perform() = 0;
|
||||
|
||||
private:
|
||||
int _priority = -1;
|
||||
QString _description;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // TEXTEDITORQUICKFIX_H
|
||||
|
||||
74
tests/unit/mockup/texteditor/refactoringchanges.h
Normal file
74
tests/unit/mockup/texteditor/refactoringchanges.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 REFACTORINGCHANGES_H
|
||||
#define REFACTORINGCHANGES_H
|
||||
|
||||
#include <QSharedPointer>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QString;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Utils {
|
||||
class ChangeSet;
|
||||
}
|
||||
|
||||
namespace TextEditor {
|
||||
class RefactoringChanges;
|
||||
class RefactoringFile;
|
||||
class RefactoringChangesData;
|
||||
typedef QSharedPointer<RefactoringFile> RefactoringFilePtr;
|
||||
|
||||
class RefactoringFile
|
||||
{
|
||||
public:
|
||||
void setChangeSet(const Utils::ChangeSet &) {}
|
||||
void apply() {}
|
||||
};
|
||||
|
||||
class RefactoringChanges
|
||||
{
|
||||
public:
|
||||
RefactoringChanges() {}
|
||||
virtual ~RefactoringChanges() {}
|
||||
|
||||
RefactoringFilePtr file(const QString &) const { return RefactoringFilePtr(); }
|
||||
};
|
||||
|
||||
class RefactoringChangesData
|
||||
{
|
||||
public:
|
||||
RefactoringChangesData() {}
|
||||
};
|
||||
|
||||
} // namespace TextEditor
|
||||
|
||||
#endif // REFACTORINGCHANGES_H
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
#include <clangdiagnosticfilter.h>
|
||||
#include <diagnosticcontainer.h>
|
||||
#include <fixitcontainer.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gmock/gmock-matchers.h>
|
||||
@@ -42,11 +43,20 @@ using ::testing::Contains;
|
||||
using ::testing::Not;
|
||||
|
||||
using ClangBackEnd::DiagnosticContainer;
|
||||
using ClangBackEnd::FixItContainer;
|
||||
using ClangBackEnd::SourceLocationContainer;
|
||||
using ClangBackEnd::SourceRangeContainer;
|
||||
|
||||
DiagnosticContainer createDiagnostic(const QString &text,
|
||||
ClangBackEnd::DiagnosticSeverity severity,
|
||||
const QString &filePath)
|
||||
const QString &filePath,
|
||||
QVector<DiagnosticContainer> children = QVector<DiagnosticContainer>(),
|
||||
bool addFixItContainer = false)
|
||||
{
|
||||
QVector<FixItContainer> fixIts;
|
||||
if (addFixItContainer)
|
||||
fixIts.append(FixItContainer(Utf8StringLiteral("("), {{filePath, 1, 1}, {filePath, 1, 2}}));
|
||||
|
||||
return DiagnosticContainer(
|
||||
text,
|
||||
Utf8StringLiteral(""),
|
||||
@@ -54,8 +64,8 @@ DiagnosticContainer createDiagnostic(const QString &text,
|
||||
severity,
|
||||
{filePath, 0u, 0u},
|
||||
{},
|
||||
{},
|
||||
{}
|
||||
fixIts,
|
||||
children
|
||||
);
|
||||
}
|
||||
|
||||
@@ -94,6 +104,42 @@ DiagnosticContainer errorFromMainFile()
|
||||
mainFilePath);
|
||||
}
|
||||
|
||||
DiagnosticContainer fixIt1(const QString &filePath)
|
||||
{
|
||||
return createDiagnostic(
|
||||
QStringLiteral("note: place parentheses around the assignment to silence this warning"),
|
||||
ClangBackEnd::DiagnosticSeverity::Note,
|
||||
filePath);
|
||||
}
|
||||
|
||||
DiagnosticContainer fixIt2(const QString &filePath)
|
||||
{
|
||||
return createDiagnostic(
|
||||
QStringLiteral("note: use '==' to turn this assignment into an equality comparison"),
|
||||
ClangBackEnd::DiagnosticSeverity::Note,
|
||||
filePath);
|
||||
}
|
||||
|
||||
DiagnosticContainer createDiagnosticWithFixIt(const QString &filePath)
|
||||
{
|
||||
return createDiagnostic(
|
||||
QStringLiteral("warning: using the result of an assignment as a condition without parentheses"),
|
||||
ClangBackEnd::DiagnosticSeverity::Warning,
|
||||
filePath,
|
||||
{fixIt1(filePath), fixIt2(filePath)},
|
||||
true);
|
||||
}
|
||||
|
||||
DiagnosticContainer warningFromMainFileWithFixits()
|
||||
{
|
||||
return createDiagnosticWithFixIt(mainFilePath);
|
||||
}
|
||||
|
||||
DiagnosticContainer warningFromIncludedFileWithFixits()
|
||||
{
|
||||
return createDiagnosticWithFixIt(includedFilePath);
|
||||
}
|
||||
|
||||
class ClangDiagnosticFilter : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
@@ -128,4 +174,46 @@ TEST_F(ClangDiagnosticFilter, ErrorsFromIncludedFileAreIgnored)
|
||||
ASSERT_THAT(clangDiagnosticFilter.takeErrors(), Not(Contains(errorFromIncludedFile())));
|
||||
}
|
||||
|
||||
TEST_F(ClangDiagnosticFilter, FixItsFromMainFileAreLetThrough)
|
||||
{
|
||||
clangDiagnosticFilter.filter({warningFromMainFileWithFixits()});
|
||||
|
||||
ASSERT_THAT(clangDiagnosticFilter.takeFixIts(), Contains(warningFromMainFileWithFixits()));
|
||||
}
|
||||
|
||||
TEST_F(ClangDiagnosticFilter, FixItsFromIncludedFileAreIgnored)
|
||||
{
|
||||
clangDiagnosticFilter.filter({warningFromIncludedFileWithFixits()});
|
||||
|
||||
ASSERT_THAT(clangDiagnosticFilter.takeFixIts(),
|
||||
Not(Contains(warningFromIncludedFileWithFixits())));
|
||||
}
|
||||
|
||||
TEST_F(ClangDiagnosticFilter, WarningsAreEmptyAfterTaking)
|
||||
{
|
||||
clangDiagnosticFilter.filter({warningFromMainFile()});
|
||||
|
||||
clangDiagnosticFilter.takeWarnings();
|
||||
|
||||
ASSERT_TRUE(clangDiagnosticFilter.takeWarnings().isEmpty());
|
||||
}
|
||||
|
||||
TEST_F(ClangDiagnosticFilter, ErrorsAreEmptyAfterTaking)
|
||||
{
|
||||
clangDiagnosticFilter.filter({errorFromMainFile()});
|
||||
|
||||
clangDiagnosticFilter.takeErrors();
|
||||
|
||||
ASSERT_TRUE(clangDiagnosticFilter.takeErrors().isEmpty());
|
||||
}
|
||||
|
||||
TEST_F(ClangDiagnosticFilter, FixItssAreEmptyAfterTaking)
|
||||
{
|
||||
clangDiagnosticFilter.filter({warningFromMainFileWithFixits()});
|
||||
|
||||
clangDiagnosticFilter.takeFixIts();
|
||||
|
||||
ASSERT_TRUE(clangDiagnosticFilter.takeFixIts().isEmpty());
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
136
tests/unit/unittest/clangfixitoperationtest.cpp
Normal file
136
tests/unit/unittest/clangfixitoperationtest.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "gtest/gtest.h"
|
||||
#include "gmock/gmock-matchers.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest-qt-printing.h"
|
||||
|
||||
#include <clangfixitoperation.h>
|
||||
#include <fixitcontainer.h>
|
||||
|
||||
#include <utils/changeset.h>
|
||||
|
||||
#include <QFile>
|
||||
#include <QVector>
|
||||
|
||||
using ClangBackEnd::FixItContainer;
|
||||
using ClangCodeModel::ClangFixItOperation;
|
||||
|
||||
using ::testing::PrintToString;
|
||||
|
||||
namespace {
|
||||
|
||||
QString unsavedFileContent(const QString &unsavedFilePath)
|
||||
{
|
||||
QFile unsavedFileContentFile(unsavedFilePath);
|
||||
const bool isOpen = unsavedFileContentFile.open(QFile::ReadOnly | QFile::Text);
|
||||
if (!isOpen)
|
||||
ADD_FAILURE() << "File with the unsaved content cannot be opened!";
|
||||
|
||||
return QString::fromUtf8(unsavedFileContentFile.readAll());
|
||||
}
|
||||
|
||||
MATCHER_P2(MatchText, errorText, expectedText,
|
||||
std::string(negation ? "hasn't" : "has") + " error text:\n" + PrintToString(errorText) +
|
||||
" and expected text:\n" + PrintToString(expectedText))
|
||||
{
|
||||
QString resultText = errorText;
|
||||
Utils::ChangeSet changeSet = arg.changeSet();
|
||||
changeSet.apply(&resultText);
|
||||
|
||||
if (resultText != expectedText) {
|
||||
*result_listener << "\n" << resultText.toUtf8().constData();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class ClangFixItOperation : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
Utf8String filePath;
|
||||
Utf8String diagnosticText{Utf8StringLiteral("expected ';' at end of declaration")};
|
||||
FixItContainer semicolonFixItContainer{Utf8StringLiteral(";"),
|
||||
{{filePath, 3u, 29u},
|
||||
{filePath, 3u, 29u}}};
|
||||
QString semicolonErrorFile{QStringLiteral(TESTDATA_DIR"/diagnostic_semicolon_fixit.cpp")};
|
||||
QString semicolonExpectedFile{QStringLiteral(TESTDATA_DIR"/diagnostic_semicolon_fixit_expected.cpp")};
|
||||
QString compareWarningFile{QStringLiteral(TESTDATA_DIR"/diagnostic_comparison_fixit.cpp")};
|
||||
QString compareExpected1File{QStringLiteral(TESTDATA_DIR"/diagnostic_comparison_fixit_expected1.cpp")};
|
||||
QString compareExpected2File{QStringLiteral(TESTDATA_DIR"/diagnostic_comparison_fixit_expected2.cpp")};
|
||||
FixItContainer compareFixItContainer{Utf8StringLiteral("=="),
|
||||
{{filePath, 4u, 43u},
|
||||
{filePath, 4u, 44u}}};
|
||||
FixItContainer assignmentFixItContainerParenLeft{Utf8StringLiteral("("),
|
||||
{{filePath, 4u, 41u},
|
||||
{filePath, 4u, 41u}}};
|
||||
FixItContainer assignmentFixItContainerParenRight{Utf8StringLiteral(")"),
|
||||
{{filePath, 4u, 46u},
|
||||
{filePath, 4u, 46u}}};
|
||||
};
|
||||
|
||||
TEST_F(ClangFixItOperation, Description)
|
||||
{
|
||||
::ClangFixItOperation operation(filePath, diagnosticText, {semicolonFixItContainer});
|
||||
|
||||
ASSERT_THAT(operation.description(),
|
||||
QStringLiteral("Apply Fix: expected ';' at end of declaration"));
|
||||
}
|
||||
|
||||
TEST_F(ClangFixItOperation, AppendSemicolon)
|
||||
{
|
||||
::ClangFixItOperation operation(filePath, diagnosticText, {semicolonFixItContainer});
|
||||
|
||||
ASSERT_THAT(operation, MatchText(unsavedFileContent(semicolonErrorFile),
|
||||
unsavedFileContent(semicolonExpectedFile)));
|
||||
}
|
||||
|
||||
TEST_F(ClangFixItOperation, ComparisonVersusAssignmentChooseComparison)
|
||||
{
|
||||
::ClangFixItOperation operation(filePath, diagnosticText, {compareFixItContainer});
|
||||
|
||||
ASSERT_THAT(operation, MatchText(unsavedFileContent(compareWarningFile),
|
||||
unsavedFileContent(compareExpected1File)));
|
||||
}
|
||||
|
||||
TEST_F(ClangFixItOperation, ComparisonVersusAssignmentChooseParentheses)
|
||||
{
|
||||
::ClangFixItOperation operation(filePath,
|
||||
diagnosticText,
|
||||
{assignmentFixItContainerParenLeft,
|
||||
assignmentFixItContainerParenRight});
|
||||
|
||||
ASSERT_THAT(operation, MatchText(unsavedFileContent(compareWarningFile),
|
||||
unsavedFileContent(compareExpected2File)));
|
||||
}
|
||||
|
||||
}
|
||||
5
tests/unit/unittest/data/diagnostic_comparison_fixit.cpp
Normal file
5
tests/unit/unittest/data/diagnostic_comparison_fixit.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
void function()
|
||||
{
|
||||
int i = 0;
|
||||
if (i = 3) {}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
void function()
|
||||
{
|
||||
int i = 0;
|
||||
if (i == 3) {}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
void function()
|
||||
{
|
||||
int i = 0;
|
||||
if ((i = 3)) {}
|
||||
}
|
||||
@@ -2,4 +2,10 @@ int warningInHeader()
|
||||
{
|
||||
}
|
||||
|
||||
void myfun()
|
||||
{
|
||||
int i = 0;
|
||||
if (i = 3) {}
|
||||
}
|
||||
|
||||
errorInHeader;
|
||||
|
||||
@@ -14,3 +14,11 @@ void g()
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
void function()
|
||||
{
|
||||
int i = 0;
|
||||
if (i = 3) {}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
int function()
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
@@ -80,7 +80,7 @@ protected:
|
||||
ClangBackEnd::ProjectParts projects;
|
||||
ClangBackEnd::UnsavedFiles unsavedFiles;
|
||||
ClangBackEnd::TranslationUnits translationUnits{projects, unsavedFiles};
|
||||
TranslationUnit translationUnit{Utf8StringLiteral(TESTDATA_DIR"/diagnostic_fixit.cpp"),
|
||||
TranslationUnit translationUnit{Utf8StringLiteral(TESTDATA_DIR"/diagnostic_semicolon_fixit.cpp"),
|
||||
projectPart,
|
||||
translationUnits};
|
||||
DiagnosticSet diagnosticSet{translationUnit.diagnostics()};
|
||||
@@ -101,7 +101,7 @@ TEST_F(FixIt, Text)
|
||||
|
||||
TEST_F(FixIt, Start)
|
||||
{
|
||||
ASSERT_THAT(fixIt.range().start(), IsSourceLocation(Utf8StringLiteral("diagnostic_fixit.cpp"),
|
||||
ASSERT_THAT(fixIt.range().start(), IsSourceLocation(Utf8StringLiteral("diagnostic_semicolon_fixit.cpp"),
|
||||
3u,
|
||||
13u,
|
||||
29u));
|
||||
@@ -109,7 +109,7 @@ TEST_F(FixIt, Start)
|
||||
|
||||
TEST_F(FixIt, End)
|
||||
{
|
||||
ASSERT_THAT(fixIt.range().end(), IsSourceLocation(Utf8StringLiteral("diagnostic_fixit.cpp"),
|
||||
ASSERT_THAT(fixIt.range().end(), IsSourceLocation(Utf8StringLiteral("diagnostic_semicolon_fixit.cpp"),
|
||||
3u,
|
||||
13u,
|
||||
29u));
|
||||
|
||||
@@ -24,6 +24,7 @@ SOURCES += \
|
||||
clangcodecompleteresultstest.cpp \
|
||||
clangcompletioncontextanalyzertest.cpp \
|
||||
clangdiagnosticfiltertest.cpp \
|
||||
clangfixitoperationtest.cpp \
|
||||
clangipcservertest.cpp \
|
||||
clangstringtest.cpp \
|
||||
clientserverinprocesstest.cpp \
|
||||
|
||||
Reference in New Issue
Block a user