Commit 23b8a3b2 authored by Nikolai Kosjar's avatar Nikolai Kosjar

Clang: Use completion through backend process

This makes us independent of libclang crashes for completion.
Re-parsing for highlighting still happens in the Qt Creator process.

Run in verbose mode:
    qtc.clangcodemodel.ipc=true

Run tests:
    -test "ClangCodeModel"

Task-number: QTCREATORBUG-14108
Task-number: QTCREATORBUG-12819
Change-Id: Id3e95bd2afdb6508bbd1d35fddc69534a909b905
Reviewed-by: default avatarMarco Bubke <marco.bubke@theqtcompany.com>
parent 264132da
......@@ -37,7 +37,7 @@ QT_BEGIN_NAMESPACE
class QVariant;
QT_END_NAMESPACE
#include <codemodelbackendipc_global.h>
#include "codemodelbackendipc_global.h"
namespace CodeModelBackEnd {
......
......@@ -14,6 +14,7 @@ unix:QMAKE_LFLAGS += -Wl,-rpath,\'$$LLVM_LIBDIR\'
SOURCES += \
$$PWD/clangcodemodelplugin.cpp \
$$PWD/clangcompleter.cpp \
$$PWD/clangcompletioncontextanalyzer.cpp \
$$PWD/clangcompletion.cpp \
$$PWD/clangeditordocumentparser.cpp \
$$PWD/clangeditordocumentprocessor.cpp \
......@@ -21,6 +22,8 @@ SOURCES += \
$$PWD/clangprojectsettings.cpp \
$$PWD/clangprojectsettingspropertiespage.cpp \
$$PWD/clangutils.cpp \
$$PWD/codemodelbackendipcintegration.cpp \
$$PWD/completionchunkstotextconverter.cpp \
$$PWD/completionproposalsbuilder.cpp \
$$PWD/cppcreatemarkers.cpp \
$$PWD/cxprettyprinter.cpp \
......@@ -41,6 +44,7 @@ SOURCES += \
HEADERS += \
$$PWD/clangcodemodelplugin.h \
$$PWD/clangcompleter.h \
$$PWD/clangcompletioncontextanalyzer.h \
$$PWD/clangcompletion.h \
$$PWD/clangeditordocumentparser.h \
$$PWD/clangeditordocumentprocessor.h \
......@@ -49,6 +53,8 @@ HEADERS += \
$$PWD/clangprojectsettings.h \
$$PWD/clangprojectsettingspropertiespage.h \
$$PWD/clangutils.h \
$$PWD/codemodelbackendipcintegration.h \
$$PWD/completionchunkstotextconverter.h \
$$PWD/completionproposalsbuilder.h \
$$PWD/constants.h \
$$PWD/cppcreatemarkers.h \
......@@ -89,13 +95,24 @@ equals(TEST, 1) {
$$PWD/test/clang_tests_database.qrc
HEADERS += \
$$PWD/test/clangcodecompletion_test.h \
$$PWD/test/clangcompletioncontextanalyzertest.h \
$$PWD/test/completiontesthelper.h
SOURCES += \
$$PWD/test/clangcodecompletion_test.cpp \
$$PWD/test/clangcompletioncontextanalyzertest.cpp \
$$PWD/test/clangcompletion_test.cpp \
$$PWD/test/completiontesthelper.cpp
DISTFILES += \
$$PWD/test/mysource.cpp \
$$PWD/test/myheader.cpp \
$$PWD/test/completionWithProject.cpp \
$$PWD/test/memberCompletion.cpp \
$$PWD/test/doxygenKeywordsCompletion.cpp \
$$PWD/test/preprocessorKeywordsCompletion.cpp \
$$PWD/test/includeDirectiveCompletion.cpp \
$$PWD/test/cxx_regression_1.cpp \
$$PWD/test/cxx_regression_2.cpp \
$$PWD/test/cxx_regression_3.cpp \
......
......@@ -12,6 +12,12 @@ QtcPlugin {
Depends { name: "ProjectExplorer" }
Depends { name: "TextEditor" }
Depends { name: "Utils" }
Depends { name: "CodeModelBackEndIpc" }
pluginTestDepends: [
"CppEditor",
"QmakeProjectManager",
]
property bool clangCompletion: true
property bool clangHighlighting: true
......@@ -89,6 +95,10 @@ QtcPlugin {
"clangcompletion_test.cpp",
"completiontesthelper.cpp",
"completiontesthelper.h",
"clangcodecompletion_test.cpp",
"clangcodecompletion_test.h",
"clangcompletioncontextanalyzertest.cpp",
"clangcompletioncontextanalyzertest.h",
]
}
......@@ -97,6 +107,13 @@ QtcPlugin {
prefix: "test/"
fileTags: "none"
files: [
"mysource.cpp",
"myheader.h",
"completionWithProject.cpp",
"memberCompletion.cpp",
"doxygenKeywordsCompletion.cpp",
"preprocessorKeywordsCompletion.cpp",
"includeDirectiveCompletion.cpp",
"cxx_regression_1.cpp",
"cxx_regression_2.cpp",
"cxx_regression_3.cpp",
......@@ -118,6 +135,8 @@ QtcPlugin {
files: [
"clang_global.h",
"clangcompletioncontextanalyzer.cpp",
"clangcompletioncontextanalyzer.h",
"clangeditordocumentparser.cpp",
"clangeditordocumentparser.h",
"clangeditordocumentprocessor.cpp",
......@@ -133,6 +152,10 @@ QtcPlugin {
"clangprojectsettingspropertiespage.ui",
"clangutils.cpp",
"clangutils.h",
"codemodelbackendipcintegration.cpp",
"codemodelbackendipcintegration.h",
"completionchunkstotextconverter.cpp",
"completionchunkstotextconverter.h",
"constants.h",
"cxprettyprinter.cpp",
"cxprettyprinter.h",
......
QTC_PLUGIN_NAME = ClangCodeModel
QTC_LIB_DEPENDS += \
utils
utils \
codemodelbackendipc
QTC_PLUGIN_DEPENDS += \
coreplugin \
cpptools \
texteditor
QTC_TEST_DEPENDS += \
cppeditor \
qmakeprojectmanager
......@@ -34,6 +34,11 @@
#include "pchmanager.h"
#include "utils.h"
#ifdef WITH_TESTS
# include "test/clangcodecompletion_test.h"
# include "test/clangcompletioncontextanalyzertest.h"
#endif
#include <cpptools/cppmodelmanager.h>
#include <projectexplorer/projectpanelfactory.h>
......@@ -73,9 +78,8 @@ bool ClangCodeModelPlugin::initialize(const QStringList &arguments, QString *err
connect(cppModelManager, &CppTools::CppModelManager::projectPartsUpdated,
pchManager, &PchManager::onProjectPartsUpdated);
// Register ModelManagerSupport
m_modelManagerSupport.reset(new ModelManagerSupport);
cppModelManager->addModelManagerSupport(m_modelManagerSupport.data());
// Register ModelManagerSupportProvider
cppModelManager->addModelManagerSupportProvider(&m_modelManagerSupportProvider);
return true;
}
......@@ -84,5 +88,16 @@ void ClangCodeModelPlugin::extensionsInitialized()
{
}
#ifdef WITH_TESTS
QList<QObject *> ClangCodeModelPlugin::createTestObjects() const
{
return {
new Tests::ClangCodeCompletionTest,
new Tests::ClangCompletionContextAnalyzerTest
};
}
#endif
} // namespace Internal
} // namespace Clang
......@@ -54,12 +54,14 @@ public:
void extensionsInitialized();
private:
QScopedPointer<ModelManagerSupport> m_modelManagerSupport;
ModelManagerSupportProviderClang m_modelManagerSupportProvider;
#ifdef CLANG_INDEXING
QScopedPointer<ClangIndexer> m_indexer;
#endif // CLANG_INDEXING
#ifdef WITH_TESTS
QList<QObject *> createTestObjects() const;
private slots:
void test_CXX_regressions();
void test_CXX_regressions_data();
......
......@@ -32,115 +32,166 @@
#define CPPEDITOR_INTERNAL_CLANGCOMPLETION_H
#include "clangcompleter.h"
#include <cplusplus/Icons.h>
#include "codemodelbackendipcintegration.h"
#include <cpptools/cppcompletionassistprocessor.h>
#include <cpptools/cppcompletionassistprovider.h>
#include <cpptools/cppmodelmanager.h>
#include <texteditor/codeassist/assistinterface.h>
#include <texteditor/codeassist/assistproposalitem.h>
#include <texteditor/codeassist/completionassistprovider.h>
#include <texteditor/codeassist/assistinterface.h>
#include <texteditor/codeassist/ifunctionhintproposalmodel.h>
#include <codemodelbackendipc/codecompletion.h>
#include <QStringList>
#include <QTextCursor>
namespace ClangCodeModel {
namespace Internal {
using CodeCompletions = QVector<CodeModelBackEnd::CodeCompletion>;
class ClangAssistProposalModel;
class ClangCompletionAssistProvider : public CppTools::CppCompletionAssistProvider
{
Q_OBJECT
public:
ClangCompletionAssistProvider();
ClangCompletionAssistProvider(IpcCommunicator::Ptr ipcCommunicator);
IAssistProvider::RunType runType() const override;
virtual TextEditor::IAssistProcessor *createProcessor() const;
virtual TextEditor::AssistInterface *createAssistInterface(
const QString &filePath, QTextDocument *document,
TextEditor::IAssistProcessor *createProcessor() const override;
TextEditor::AssistInterface *createAssistInterface(
const QString &filePath,
const TextEditor::TextEditorWidget *textEditorWidget,
const CPlusPlus::LanguageFeatures &languageFeatures,
int position, TextEditor::AssistReason reason) const;
int position,
TextEditor::AssistReason reason) const override;
private:
ClangCodeModel::ClangCompleter::Ptr m_clangCompletionWrapper;
IpcCommunicator::Ptr m_ipcCommunicator;
};
} // namespace Internal
class ClangAssistProposalItem : public TextEditor::AssistProposalItem
{
public:
ClangAssistProposalItem() {}
bool prematurelyApplies(const QChar &c) const override;
void applyContextualContent(TextEditor::TextEditorWidget *editorWidget, int basePosition) const override;
void keepCompletionOperator(unsigned compOp) { m_completionOperator = compOp; }
bool isOverloaded() const;
void addOverload(const CodeModelBackEnd::CodeCompletion &ccr);
CodeModelBackEnd::CodeCompletion originalItem() const;
bool isCodeCompletion() const;
private:
unsigned m_completionOperator;
mutable QChar m_typedChar;
QList<CodeModelBackEnd::CodeCompletion> m_overloads;
};
class ClangFunctionHintModel : public TextEditor::IFunctionHintProposalModel
{
public:
ClangFunctionHintModel(const CodeCompletions &functionSymbols);
void reset() override {}
int size() const override { return m_functionSymbols.size(); }
QString text(int index) const override;
int activeArgument(const QString &prefix) const override;
private:
CodeCompletions m_functionSymbols;
mutable int m_currentArg;
};
class CLANG_EXPORT ClangCompletionAssistInterface: public TextEditor::AssistInterface
class ClangCompletionAssistInterface: public TextEditor::AssistInterface
{
public:
ClangCompletionAssistInterface(ClangCodeModel::ClangCompleter::Ptr clangWrapper,
QTextDocument *document,
ClangCompletionAssistInterface(ClangCodeModel::Internal::IpcCommunicator::Ptr ipcCommunicator,
const TextEditor::TextEditorWidget *textEditorWidget,
int position,
const QString &fileName,
TextEditor::AssistReason reason,
const QStringList &options,
const QList<CppTools::ProjectPart::HeaderPath> &headerPaths,
const CppTools::ProjectPart::HeaderPaths &headerPaths,
const Internal::PchInfo::Ptr &pchInfo,
const CPlusPlus::LanguageFeatures &features);
ClangCodeModel::ClangCompleter::Ptr clangWrapper() const
{ return m_clangWrapper; }
const ClangCodeModel::Internal::UnsavedFiles &unsavedFiles() const
{ return m_unsavedFiles; }
ClangCodeModel::Internal::IpcCommunicator::Ptr ipcCommunicator() const;
const ClangCodeModel::Internal::UnsavedFiles &unsavedFiles() const;
bool objcEnabled() const;
const CppTools::ProjectPart::HeaderPaths &headerPaths() const;
CPlusPlus::LanguageFeatures languageFeatures() const;
const TextEditor::TextEditorWidget *textEditorWidget() const;
const QStringList &options() const
{ return m_options; }
const QList<CppTools::ProjectPart::HeaderPath> &headerPaths() const
{ return m_headerPaths; }
CPlusPlus::LanguageFeatures languageFeatures() const
{ return m_languageFeatures; }
void setHeaderPaths(const CppTools::ProjectPart::HeaderPaths &headerPaths); // For tests
private:
ClangCodeModel::ClangCompleter::Ptr m_clangWrapper;
ClangCodeModel::Internal::IpcCommunicator::Ptr m_ipcCommunicator;
ClangCodeModel::Internal::UnsavedFiles m_unsavedFiles;
QStringList m_options;
QList<CppTools::ProjectPart::HeaderPath> m_headerPaths;
CppTools::ProjectPart::HeaderPaths m_headerPaths;
Internal::PchInfo::Ptr m_savedPchPointer;
CPlusPlus::LanguageFeatures m_languageFeatures;
const TextEditor::TextEditorWidget *m_textEditorWidget;
};
class CLANG_EXPORT ClangCompletionAssistProcessor : public CppTools::CppCompletionAssistProcessor
class ClangCompletionAssistProcessor : public CppTools::CppCompletionAssistProcessor
{
Q_DECLARE_TR_FUNCTIONS(ClangCodeModel::Internal::ClangCompletionAssistProcessor)
public:
ClangCompletionAssistProcessor();
virtual ~ClangCompletionAssistProcessor();
~ClangCompletionAssistProcessor();
TextEditor::IAssistProposal *perform(const TextEditor::AssistInterface *interface) override;
virtual TextEditor::IAssistProposal *perform(const TextEditor::AssistInterface *interface);
void asyncCompletionsAvailable(const CodeCompletions &completions);
const TextEditor::TextEditorWidget *textEditorWidget() const;
private:
int startCompletionHelper();
TextEditor::IAssistProposal *startCompletionHelper();
int startOfOperator(int pos, unsigned *kind, bool wantFunctionCall) const;
int findStartOfName(int pos = -1) const;
bool accepts() const;
TextEditor::IAssistProposal *createContentProposal();
int startCompletionInternal(const QString fileName,
unsigned line, unsigned column,
int endOfExpression);
TextEditor::IAssistProposal *createProposal() const;
bool completeInclude(const QTextCursor &cursor);
bool completeInclude(int position);
void completeIncludePath(const QString &realPath, const QStringList &suffixes);
void completePreprocessor();
bool completePreprocessorDirectives();
bool completeDoxygenKeywords();
void addCompletionItem(const QString &text,
const QIcon &icon = QIcon(),
int order = 0,
const QVariant &data = QVariant());
void sendFileContent(const QString &projectFilePath, const QByteArray &modifiedFileContent);
void sendCompletionRequest(int position, const QByteArray &modifiedFileContent);
void onCompletionsAvailable(const CodeCompletions &completions);
void onFunctionHintCompletionsAvailable(const CodeCompletions &completions);
private:
QScopedPointer<const ClangCompletionAssistInterface> m_interface;
QScopedPointer<Internal::ClangAssistProposalModel> m_model;
unsigned m_completionOperator;
enum CompletionRequestType { NormalCompletion, FunctionHintCompletion } m_sentRequestType;
QString m_functionName; // For type == Type::FunctionHintCompletion
bool m_addSnippets = false; // For type == Type::NormalCompletion
};
} // namespace Internal
} // namespace Clang
#endif // CPPEDITOR_INTERNAL_CLANGCOMPLETION_H
This diff is collapsed.
/****************************************************************************
**
** 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 CLANGCOMPLETIONCONTEXTANALYZER_H
#define CLANGCOMPLETIONCONTEXTANALYZER_H
#include <cplusplus/Token.h>
#include <QString>
namespace TextEditor { class AssistInterface; }
namespace ClangCodeModel {
namespace Internal {
class ClangCompletionContextAnalyzer
{
public:
ClangCompletionContextAnalyzer(const TextEditor::AssistInterface *assistInterface,
CPlusPlus::LanguageFeatures languageFeatures);
void analyze();
enum CompletionAction {
PassThroughToLibClang,
PassThroughToLibClangAfterLeftParen,
CompleteDoxygenKeyword,
CompleteIncludePath,
CompletePreprocessorDirective,
CompleteSignal,
CompleteSlot
};
CompletionAction completionAction() const { return m_completionAction; }
unsigned completionOperator() const { return m_completionOperator; }
int positionForProposal() const { return m_positionForProposal; }
int positionForClang() const { return m_positionForClang; }
int positionEndOfExpression() const { return m_positionEndOfExpression; }
QString functionName() const { return m_functionName; }
private:
ClangCompletionContextAnalyzer();
struct FunctionInfo { int functionNamePosition; QString functionName; };
FunctionInfo analyzeFunctionCall(int endOfExpression) const;
int findStartOfName(int position = -1) const;
int skipPrecedingWhitespace(int position) const;
int startOfOperator(int position, unsigned *kind, bool wantFunctionCall) const;
void setActionAndClangPosition(CompletionAction action, int position);
const TextEditor::AssistInterface * const m_interface; // Not owned
const CPlusPlus::LanguageFeatures m_languageFeatures; // TODO: Get from assistInterface?!
// Results
CompletionAction m_completionAction = PassThroughToLibClang;
unsigned m_completionOperator = CPlusPlus::T_EOF_SYMBOL;
int m_positionForProposal = -1;
int m_positionForClang = -1;
int m_positionEndOfExpression = -1;
QString m_functionName;
};
} // namespace Internal
} // namespace ClangCodeModel
#endif // CLANGCOMPLETIONCONTEXTANALYZER_H
......@@ -46,7 +46,9 @@ static Q_LOGGING_CATEGORY(log, "qtc.clangcodemodel.clangeditordocumentparser")
namespace {
QStringList createOptions(const QString &filePath, const CppTools::ProjectPart::Ptr &part)
QStringList createOptions(const QString &filePath,
const CppTools::ProjectPart::Ptr &part,
bool includeSpellCheck = false)
{
using namespace ClangCodeModel;
......@@ -54,7 +56,9 @@ QStringList createOptions(const QString &filePath, const CppTools::ProjectPart::
if (part.isNull())
return options;
options += QLatin1String("-fspell-checking");
if (includeSpellCheck)
options += QLatin1String("-fspell-checking");
options += ClangCodeModel::Utils::createClangOptions(part, filePath);
if (Internal::PchInfo::Ptr pchInfo = Internal::PchManager::instance()->pchInfo(part))
......@@ -91,7 +95,7 @@ void ClangEditorDocumentParser::update(CppTools::WorkingCopy workingCopy)
QMutexLocker lock2(&m_mutex);
updateProjectPart();
const QStringList options = createOptions(filePath(), projectPart());
const QStringList options = createOptions(filePath(), projectPart(), true);
qCDebug(log, "Reparse options (cmd line equivalent): %s",
commandLine(options, filePath()).toUtf8().constData());
......
......@@ -30,6 +30,8 @@
#include "clangeditordocumentprocessor.h"
#include "clangmodelmanagersupport.h"
#include "clangutils.h"
#include "cppcreatemarkers.h"
#include "diagnostic.h"
#include "pchinfo.h"
......@@ -91,9 +93,13 @@ QList<TextEditor::BlockRange> toTextEditorBlocks(
} // anonymous namespace
namespace ClangCodeModel {
namespace Internal {
ClangEditorDocumentProcessor::ClangEditorDocumentProcessor(TextEditor::TextDocument *document)
ClangEditorDocumentProcessor::ClangEditorDocumentProcessor(
ModelManagerSupportClang *modelManagerSupport,
TextEditor::TextDocument *document)
: BaseEditorDocumentProcessor(document)
, m_modelManagerSupport(modelManagerSupport)
, m_parser(document->filePath().toString())
, m_parserRevision(0)
, m_semanticHighlighter(document)
......@@ -122,6 +128,17 @@ ClangEditorDocumentProcessor::~ClangEditorDocumentProcessor()
{
m_parserWatcher.cancel();
m_parserWatcher.waitForFinished();
const CppTools::ProjectPart::Ptr projectPart = m_parser.projectPart();
QTC_ASSERT(projectPart, return);
QString projectFilePath;
if (Utils::isProjectPartValid(projectPart))
projectFilePath = projectPart->projectFile; // OK, Project Part is still loaded
QTC_ASSERT(m_modelManagerSupport, return);
m_modelManagerSupport->ipcCommunicator()->unregisterFilesForCodeCompletion(
{CodeModelBackEnd::FileContainer(filePath(), projectFilePath)});
}
void ClangEditorDocumentProcessor::run()
......@@ -188,4 +205,5 @@ void ClangEditorDocumentProcessor::onParserFinished()
m_semanticHighlighter.run();
}
} // namespace Internal
} // namespace ClangCodeModel
......@@ -38,15 +38,20 @@
#include <cpptools/semantichighlighter.h>
#include <QFutureWatcher>
#include <QPointer>
namespace ClangCodeModel {
namespace Internal {
class ModelManagerSupportClang;
class ClangEditorDocumentProcessor : public CppTools::BaseEditorDocumentProcessor
{
Q_OBJECT
public:
ClangEditorDocumentProcessor(TextEditor::TextDocument *document);
ClangEditorDocumentProcessor(ModelManagerSupportClang *modelManagerSupport,
TextEditor::TextDocument *document);
~ClangEditorDocumentProcessor();
// BaseEditorDocumentProcessor interface
......@@ -61,6 +66,8 @@ private slots:
void onParserFinished();
private:
QPointer<ModelManagerSupportClang> m_modelManagerSupport;
ClangEditorDocumentParser m_parser;