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:
Nikolai Kosjar
2015-08-24 18:26:09 +02:00
parent de6d7f0617
commit a7928b4b0b
32 changed files with 941 additions and 14 deletions

View File

@@ -27,6 +27,8 @@ SOURCES += \
clangdiagnosticmanager.cpp \ clangdiagnosticmanager.cpp \
clangeditordocumentparser.cpp \ clangeditordocumentparser.cpp \
clangeditordocumentprocessor.cpp \ clangeditordocumentprocessor.cpp \
clangfixitoperation.cpp \
clangfixitoperationsextractor.cpp \
clangfunctionhintmodel.cpp \ clangfunctionhintmodel.cpp \
clangmodelmanagersupport.cpp \ clangmodelmanagersupport.cpp \
clangprojectsettings.cpp \ clangprojectsettings.cpp \
@@ -67,6 +69,8 @@ HEADERS += \
clangdiagnosticmanager.h \ clangdiagnosticmanager.h \
clangeditordocumentparser.h \ clangeditordocumentparser.h \
clangeditordocumentprocessor.h \ clangeditordocumentprocessor.h \
clangfixitoperation.h \
clangfixitoperationsextractor.h \
clangfunctionhintmodel.h \ clangfunctionhintmodel.h \
clang_global.h \ clang_global.h \
clangmodelmanagersupport.h \ clangmodelmanagersupport.h \

View File

@@ -140,6 +140,10 @@ QtcPlugin {
"clangdiagnosticfilter.h", "clangdiagnosticfilter.h",
"clangdiagnosticmanager.cpp", "clangdiagnosticmanager.cpp",
"clangdiagnosticmanager.h", "clangdiagnosticmanager.h",
"clangfixitoperation.cpp",
"clangfixitoperation.h",
"clangfixitoperationsextractor.cpp",
"clangfixitoperationsextractor.h",
"clangmodelmanagersupport.cpp", "clangmodelmanagersupport.cpp",
"clangmodelmanagersupport.h", "clangmodelmanagersupport.h",
"clangcodemodelplugin.cpp", "clangcodemodelplugin.cpp",

View File

@@ -4,11 +4,12 @@ SOURCES += $$PWD/completionchunkstotextconverter.cpp \
$$PWD/activationsequenceprocessor.cpp \ $$PWD/activationsequenceprocessor.cpp \
$$PWD/activationsequencecontextprocessor.cpp \ $$PWD/activationsequencecontextprocessor.cpp \
$$PWD/clangcompletioncontextanalyzer.cpp \ $$PWD/clangcompletioncontextanalyzer.cpp \
$$PWD/clangdiagnosticfilter.cpp $$PWD/clangdiagnosticfilter.cpp \
$$PWD/clangfixitoperation.cpp
HEADERS += $$PWD/completionchunkstotextconverter.h \ HEADERS += $$PWD/completionchunkstotextconverter.h \
$$PWD/activationsequenceprocessor.h \ $$PWD/activationsequenceprocessor.h \
$$PWD/activationsequencecontextprocessor.h \ $$PWD/activationsequencecontextprocessor.h \
$$PWD/clangcompletioncontextanalyzer.h \ $$PWD/clangcompletioncontextanalyzer.h \
$$PWD/clangdiagnosticfilter.h $$PWD/clangdiagnosticfilter.h \
$$PWD/clangfixitoperation.h

View File

@@ -61,6 +61,18 @@ filterDiagnostics(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
return filteredDiagnostics; 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 } // anonymous namespace
namespace ClangCodeModel { namespace ClangCodeModel {
@@ -88,6 +100,22 @@ void ClangDiagnosticFilter::filterDocumentRelatedErrors(
m_errorDiagnostics = filterDiagnostics(diagnostics, isLocalWarning); 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) ClangDiagnosticFilter::ClangDiagnosticFilter(const QString &filePath)
: m_filePath(filePath) : m_filePath(filePath)
{ {
@@ -97,6 +125,7 @@ void ClangDiagnosticFilter::filter(const QVector<ClangBackEnd::DiagnosticContain
{ {
filterDocumentRelatedWarnings(diagnostics); filterDocumentRelatedWarnings(diagnostics);
filterDocumentRelatedErrors(diagnostics); filterDocumentRelatedErrors(diagnostics);
filterFixits();
} }
QVector<ClangBackEnd::DiagnosticContainer> ClangDiagnosticFilter::takeWarnings() QVector<ClangBackEnd::DiagnosticContainer> ClangDiagnosticFilter::takeWarnings()
@@ -115,6 +144,14 @@ QVector<ClangBackEnd::DiagnosticContainer> ClangDiagnosticFilter::takeErrors()
return diagnostics; return diagnostics;
} }
QVector<ClangBackEnd::DiagnosticContainer> ClangDiagnosticFilter::takeFixIts()
{
auto diagnostics = m_fixItdiagnostics;
m_fixItdiagnostics.clear();
return diagnostics;
}
} // namespace Internal } // namespace Internal
} // namespace ClangCodeModel } // namespace ClangCodeModel

View File

@@ -47,15 +47,19 @@ public:
QVector<ClangBackEnd::DiagnosticContainer> takeWarnings(); QVector<ClangBackEnd::DiagnosticContainer> takeWarnings();
QVector<ClangBackEnd::DiagnosticContainer> takeErrors(); QVector<ClangBackEnd::DiagnosticContainer> takeErrors();
QVector<ClangBackEnd::DiagnosticContainer> takeFixIts();
private: private:
void filterDocumentRelatedWarnings(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics); void filterDocumentRelatedWarnings(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics);
void filterDocumentRelatedErrors(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics); void filterDocumentRelatedErrors(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics);
void filterFixits();
private: private:
const QString &m_filePath; const QString &m_filePath;
QVector<ClangBackEnd::DiagnosticContainer> m_warningDiagnostics; QVector<ClangBackEnd::DiagnosticContainer> m_warningDiagnostics;
QVector<ClangBackEnd::DiagnosticContainer> m_errorDiagnostics; QVector<ClangBackEnd::DiagnosticContainer> m_errorDiagnostics;
QVector<ClangBackEnd::DiagnosticContainer> m_fixItdiagnostics;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -208,6 +208,12 @@ void ClangDiagnosticManager::processNewDiagnostics(
clearWarningsAndErrors(); clearWarningsAndErrors();
} }
const QVector<ClangBackEnd::DiagnosticContainer> &
ClangDiagnosticManager::diagnosticsWithFixIts() const
{
return m_fixItdiagnostics;
}
void ClangDiagnosticManager::addClangTextMarks( void ClangDiagnosticManager::addClangTextMarks(
const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics) const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics)
{ {
@@ -245,6 +251,7 @@ void ClangDiagnosticManager::filterDiagnostics(
m_warningDiagnostics = filter.takeWarnings(); m_warningDiagnostics = filter.takeWarnings();
m_errorDiagnostics = filter.takeErrors(); m_errorDiagnostics = filter.takeErrors();
m_fixItdiagnostics = filter.takeFixIts();
} }
} // namespace Internal } // namespace Internal

View File

@@ -53,6 +53,7 @@ public:
void processNewDiagnostics(const QVector<ClangBackEnd::DiagnosticContainer> &allDiagnostics); void processNewDiagnostics(const QVector<ClangBackEnd::DiagnosticContainer> &allDiagnostics);
const QVector<ClangBackEnd::DiagnosticContainer> &diagnosticsWithFixIts() const;
QList<QTextEdit::ExtraSelection> takeExtraSelections(); QList<QTextEdit::ExtraSelection> takeExtraSelections();
private: private:
@@ -68,6 +69,7 @@ private:
QVector<ClangBackEnd::DiagnosticContainer> m_warningDiagnostics; QVector<ClangBackEnd::DiagnosticContainer> m_warningDiagnostics;
QVector<ClangBackEnd::DiagnosticContainer> m_errorDiagnostics; QVector<ClangBackEnd::DiagnosticContainer> m_errorDiagnostics;
QVector<ClangBackEnd::DiagnosticContainer> m_fixItdiagnostics;
QList<QTextEdit::ExtraSelection> m_extraSelections; QList<QTextEdit::ExtraSelection> m_extraSelections;
std::vector<ClangTextMark> m_clangTextMarks; std::vector<ClangTextMark> m_clangTextMarks;
}; };

View File

@@ -30,6 +30,8 @@
#include "clangeditordocumentprocessor.h" #include "clangeditordocumentprocessor.h"
#include "clangfixitoperation.h"
#include "clangfixitoperationsextractor.h"
#include "clangmodelmanagersupport.h" #include "clangmodelmanagersupport.h"
#include "clangutils.h" #include "clangutils.h"
#include "cppcreatemarkers.h" #include "cppcreatemarkers.h"
@@ -42,6 +44,7 @@
#include <cpptools/cpptoolsplugin.h> #include <cpptools/cpptoolsplugin.h>
#include <cpptools/cppworkingcopy.h> #include <cpptools/cppworkingcopy.h>
#include <texteditor/convenience.h>
#include <texteditor/fontsettings.h> #include <texteditor/fontsettings.h>
#include <texteditor/texteditor.h> #include <texteditor/texteditor.h>
#include <texteditor/texteditorconstants.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) ClangEditorDocumentProcessor *ClangEditorDocumentProcessor::get(const QString &filePath)
{ {
return qobject_cast<ClangEditorDocumentProcessor *>(BaseEditorDocumentProcessor::get(filePath)); return qobject_cast<ClangEditorDocumentProcessor *>(BaseEditorDocumentProcessor::get(filePath));

View File

@@ -73,6 +73,9 @@ public:
void updateCodeWarnings(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics, void updateCodeWarnings(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
uint documentRevision); uint documentRevision);
TextEditor::QuickFixOperations
extraRefactoringOperations(const TextEditor::AssistInterface &assistInterface) override;
public: public:
static ClangEditorDocumentProcessor *get(const QString &filePath); static ClangEditorDocumentProcessor *get(const QString &filePath);

View 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

View 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

View 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

View 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

View File

@@ -45,6 +45,8 @@
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <QCoreApplication> #include <QCoreApplication>
#include <QMenu>
#include <QTextBlock>
using namespace ClangCodeModel; using namespace ClangCodeModel;
using namespace ClangCodeModel::Internal; using namespace ClangCodeModel::Internal;
@@ -137,6 +139,15 @@ void ModelManagerSupportClang::connectTextDocumentToUnsavedFiles(TextEditor::Tex
Qt::UniqueConnection); 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) void ModelManagerSupportClang::onEditorOpened(Core::IEditor *editor)
{ {
QTC_ASSERT(editor, return); QTC_ASSERT(editor, return);
@@ -145,10 +156,13 @@ void ModelManagerSupportClang::onEditorOpened(Core::IEditor *editor)
TextEditor::TextDocument *textDocument = qobject_cast<TextEditor::TextDocument *>(document); TextEditor::TextDocument *textDocument = qobject_cast<TextEditor::TextDocument *>(document);
if (textDocument && cppModelManager()->isCppEditor(editor)) { 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); connectTextDocumentToTranslationUnit(textDocument);
else connectToWidgetsMarkContextMenuRequested(editor->widget());
} else {
connectTextDocumentToUnsavedFiles(textDocument); connectTextDocumentToUnsavedFiles(textDocument);
}
// TODO: Ensure that not fully loaded documents are updated? // 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) void ModelManagerSupportClang::onProjectPartsUpdated(ProjectExplorer::Project *project)
{ {
QTC_ASSERT(project, return); QTC_ASSERT(project, return);

View File

@@ -38,7 +38,13 @@
#include <QObject> #include <QObject>
#include <QScopedPointer> #include <QScopedPointer>
QT_BEGIN_NAMESPACE
class QMenu;
class QWidget;
QT_END_NAMESPACE
namespace Core { class IDocument; } namespace Core { class IDocument; }
namespace TextEditor { class TextEditorWidget; }
namespace ClangCodeModel { namespace ClangCodeModel {
namespace Internal { namespace Internal {
@@ -76,11 +82,16 @@ private:
void onAbstractEditorSupportContentsUpdated(const QString &filePath, const QByteArray &content); void onAbstractEditorSupportContentsUpdated(const QString &filePath, const QByteArray &content);
void onAbstractEditorSupportRemoved(const QString &filePath); void onAbstractEditorSupportRemoved(const QString &filePath);
void onTextMarkContextMenuRequested(TextEditor::TextEditorWidget *widget,
int lineNumber,
QMenu *menu);
void onProjectPartsUpdated(ProjectExplorer::Project *project); void onProjectPartsUpdated(ProjectExplorer::Project *project);
void onProjectPartsRemoved(const QStringList &projectPartIds); void onProjectPartsRemoved(const QStringList &projectPartIds);
void connectTextDocumentToTranslationUnit(TextEditor::TextDocument *textDocument); void connectTextDocumentToTranslationUnit(TextEditor::TextDocument *textDocument);
void connectTextDocumentToUnsavedFiles(TextEditor::TextDocument *textDocument); void connectTextDocumentToUnsavedFiles(TextEditor::TextDocument *textDocument);
void connectToWidgetsMarkContextMenuRequested(QWidget *editorWidget);
private: private:
IpcCommunicator m_ipcCommunicator; IpcCommunicator m_ipcCommunicator;

View File

@@ -40,6 +40,7 @@
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <coreplugin/messagebox.h> #include <coreplugin/messagebox.h>
#include <cpptools/baseeditordocumentprocessor.h>
#include <cpptools/cppclassesfilter.h> #include <cpptools/cppclassesfilter.h>
#include <cpptools/cppcodestylesettings.h> #include <cpptools/cppcodestylesettings.h>
#include <cpptools/cpppointerdeclarationformatter.h> #include <cpptools/cpppointerdeclarationformatter.h>
@@ -132,6 +133,8 @@ void registerQuickFixes(ExtensionSystem::IPlugin *plugIn)
plugIn->addAutoReleasedObject(new OptimizeForLoop); plugIn->addAutoReleasedObject(new OptimizeForLoop);
plugIn->addAutoReleasedObject(new EscapeStringLiteral); plugIn->addAutoReleasedObject(new EscapeStringLiteral);
plugIn->addAutoReleasedObject(new ExtraRefactoringOperations);
} }
// In the following anonymous namespace all functions are collected, which could be of interest for // 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 Internal
} // namespace CppEditor } // namespace CppEditor

View File

@@ -31,6 +31,7 @@
#ifndef CPPQUICKFIXES_H #ifndef CPPQUICKFIXES_H
#define CPPQUICKFIXES_H #define CPPQUICKFIXES_H
#include "cppeditor_global.h"
#include "cppquickfix.h" #include "cppquickfix.h"
#include <cpptools/cpprefactoringchanges.h> #include <cpptools/cpprefactoringchanges.h>
@@ -62,6 +63,12 @@ namespace Internal {
void registerQuickFixes(ExtensionSystem::IPlugin *plugIn); 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. Adds an include for an undefined identifier or only forward declared identifier.

View File

@@ -64,6 +64,12 @@ TextEditor::TextDocument *BaseEditorDocumentProcessor::baseTextDocument() const
return m_baseTextDocument; return m_baseTextDocument;
} }
TextEditor::QuickFixOperations
BaseEditorDocumentProcessor::extraRefactoringOperations(const TextEditor::AssistInterface &)
{
return TextEditor::QuickFixOperations();
}
BaseEditorDocumentProcessor *BaseEditorDocumentProcessor::get(const QString &filePath) BaseEditorDocumentProcessor *BaseEditorDocumentProcessor::get(const QString &filePath)
{ {
CppModelManager *cmmi = CppModelManager::instance(); CppModelManager *cmmi = CppModelManager::instance();

View File

@@ -35,8 +35,10 @@
#include "cppsemanticinfo.h" #include "cppsemanticinfo.h"
#include "cpptools_global.h" #include "cpptools_global.h"
#include <texteditor/codeassist/assistinterface.h>
#include <texteditor/texteditor.h> #include <texteditor/texteditor.h>
#include <texteditor/textdocument.h> #include <texteditor/textdocument.h>
#include <texteditor/quickfix.h>
#include <cplusplus/CppDocument.h> #include <cplusplus/CppDocument.h>
@@ -65,6 +67,9 @@ public:
virtual BaseEditorDocumentParser::Ptr parser() = 0; virtual BaseEditorDocumentParser::Ptr parser() = 0;
virtual bool isParserRunning() const = 0; virtual bool isParserRunning() const = 0;
virtual TextEditor::QuickFixOperations
extraRefactoringOperations(const TextEditor::AssistInterface &assistInterface);
public: public:
static BaseEditorDocumentProcessor *get(const QString &filePath); static BaseEditorDocumentProcessor *get(const QString &filePath);

View 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

View 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

View File

@@ -30,6 +30,7 @@
#include <clangdiagnosticfilter.h> #include <clangdiagnosticfilter.h>
#include <diagnosticcontainer.h> #include <diagnosticcontainer.h>
#include <fixitcontainer.h>
#include <gmock/gmock.h> #include <gmock/gmock.h>
#include <gmock/gmock-matchers.h> #include <gmock/gmock-matchers.h>
@@ -42,11 +43,20 @@ using ::testing::Contains;
using ::testing::Not; using ::testing::Not;
using ClangBackEnd::DiagnosticContainer; using ClangBackEnd::DiagnosticContainer;
using ClangBackEnd::FixItContainer;
using ClangBackEnd::SourceLocationContainer;
using ClangBackEnd::SourceRangeContainer;
DiagnosticContainer createDiagnostic(const QString &text, DiagnosticContainer createDiagnostic(const QString &text,
ClangBackEnd::DiagnosticSeverity severity, 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( return DiagnosticContainer(
text, text,
Utf8StringLiteral(""), Utf8StringLiteral(""),
@@ -54,8 +64,8 @@ DiagnosticContainer createDiagnostic(const QString &text,
severity, severity,
{filePath, 0u, 0u}, {filePath, 0u, 0u},
{}, {},
{}, fixIts,
{} children
); );
} }
@@ -94,6 +104,42 @@ DiagnosticContainer errorFromMainFile()
mainFilePath); 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 class ClangDiagnosticFilter : public ::testing::Test
{ {
protected: protected:
@@ -128,4 +174,46 @@ TEST_F(ClangDiagnosticFilter, ErrorsFromIncludedFileAreIgnored)
ASSERT_THAT(clangDiagnosticFilter.takeErrors(), Not(Contains(errorFromIncludedFile()))); 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 } // anonymous namespace

View 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)));
}
}

View File

@@ -0,0 +1,5 @@
void function()
{
int i = 0;
if (i = 3) {}
}

View File

@@ -0,0 +1,5 @@
void function()
{
int i = 0;
if (i == 3) {}
}

View File

@@ -0,0 +1,5 @@
void function()
{
int i = 0;
if ((i = 3)) {}
}

View File

@@ -2,4 +2,10 @@ int warningInHeader()
{ {
} }
void myfun()
{
int i = 0;
if (i = 3) {}
}
errorInHeader; errorInHeader;

View File

@@ -14,3 +14,11 @@ void g()
{ {
return 3; return 3;
} }
void function()
{
int i = 0;
if (i = 3) {}
}

View File

@@ -0,0 +1,4 @@
int function()
{
return 3;
}

View File

@@ -80,7 +80,7 @@ protected:
ClangBackEnd::ProjectParts projects; ClangBackEnd::ProjectParts projects;
ClangBackEnd::UnsavedFiles unsavedFiles; ClangBackEnd::UnsavedFiles unsavedFiles;
ClangBackEnd::TranslationUnits translationUnits{projects, unsavedFiles}; ClangBackEnd::TranslationUnits translationUnits{projects, unsavedFiles};
TranslationUnit translationUnit{Utf8StringLiteral(TESTDATA_DIR"/diagnostic_fixit.cpp"), TranslationUnit translationUnit{Utf8StringLiteral(TESTDATA_DIR"/diagnostic_semicolon_fixit.cpp"),
projectPart, projectPart,
translationUnits}; translationUnits};
DiagnosticSet diagnosticSet{translationUnit.diagnostics()}; DiagnosticSet diagnosticSet{translationUnit.diagnostics()};
@@ -101,7 +101,7 @@ TEST_F(FixIt, Text)
TEST_F(FixIt, Start) 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, 3u,
13u, 13u,
29u)); 29u));
@@ -109,10 +109,10 @@ TEST_F(FixIt, Start)
TEST_F(FixIt, End) 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, 3u,
13u, 13u,
29u)); 29u));
} }
} }

View File

@@ -24,6 +24,7 @@ SOURCES += \
clangcodecompleteresultstest.cpp \ clangcodecompleteresultstest.cpp \
clangcompletioncontextanalyzertest.cpp \ clangcompletioncontextanalyzertest.cpp \
clangdiagnosticfiltertest.cpp \ clangdiagnosticfiltertest.cpp \
clangfixitoperationtest.cpp \
clangipcservertest.cpp \ clangipcservertest.cpp \
clangstringtest.cpp \ clangstringtest.cpp \
clientserverinprocesstest.cpp \ clientserverinprocesstest.cpp \