Commit 79e94dda authored by Ivan Donchevskii's avatar Ivan Donchevskii

Clang: Workaround completion for make_unique/shared

Change unsaved file to provide constructor overloads
for std::make_unique, std::make_shared and
QSharedPointer::create

Example:
// Provide Foo constructor signatures at <Cursor>
std::make_unique<Foo>(<Cursor>

Task-number: QTCREATORBUG-18615
Change-Id: I87dd17085adf99ee498db969a3cdda5ebd973476
Reviewed-by: Nikolai Kosjar's avatarNikolai Kosjar <nikolai.kosjar@qt.io>
parent b5379d59
......@@ -212,6 +212,8 @@ void ClangCodeModelServer::completeCode(const ClangBackEnd::CompleteCodeMessage
JobRequest jobRequest = processor.createJobRequest(JobRequest::Type::CompleteCode);
jobRequest.line = message.line();
jobRequest.column = message.column();
jobRequest.funcNameStartLine = message.funcNameStartLine();
jobRequest.funcNameStartColumn = message.funcNameStartColumn();
jobRequest.ticketNumber = message.ticketNumber();
processor.addJob(jobRequest);
......
......@@ -36,12 +36,15 @@ namespace ClangBackEnd {
static CompleteCodeJob::AsyncResult runAsyncHelper(const TranslationUnit &translationUnit,
UnsavedFiles unsavedFiles,
quint32 line,
quint32 column)
quint32 column,
qint32 funcNameStartLine,
qint32 funcNameStartColumn)
{
TIME_SCOPE_DURATION("CompleteCodeJobRunner");
const TranslationUnit::CodeCompletionResult results
= translationUnit.complete(unsavedFiles, line, column);
= translationUnit.complete(unsavedFiles, line, column,
funcNameStartLine, funcNameStartColumn);
CompleteCodeJob::AsyncResult asyncResult;
asyncResult.completions = results.completions;
......@@ -63,8 +66,12 @@ IAsyncJob::AsyncPrepareResult CompleteCodeJob::prepareAsyncRun()
const UnsavedFiles unsavedFiles = *context().unsavedFiles;
const quint32 line = jobRequest.line;
const quint32 column = jobRequest.column;
setRunner([translationUnit, unsavedFiles, line, column]() {
return runAsyncHelper(translationUnit, unsavedFiles, line, column);
const qint32 funcNameStartLine = jobRequest.funcNameStartLine;
const qint32 funcNameStartColumn = jobRequest.funcNameStartColumn;
setRunner([translationUnit, unsavedFiles, line, column,
funcNameStartLine, funcNameStartColumn]() {
return runAsyncHelper(translationUnit, unsavedFiles, line, column,
funcNameStartLine, funcNameStartColumn);
});
return AsyncPrepareResult{translationUnit.id()};
......
......@@ -110,6 +110,8 @@ public:
// Specific to some jobs
quint32 line = 0;
quint32 column = 0;
qint32 funcNameStartLine = -1;
qint32 funcNameStartColumn = -1;
quint64 ticketNumber = 0;
Utf8StringVector dependentFiles;
bool resolveTarget = true;
......
......@@ -119,11 +119,15 @@ bool TranslationUnit::suspend() const
TranslationUnit::CodeCompletionResult TranslationUnit::complete(
UnsavedFiles &unsavedFiles,
uint line,
uint column) const
uint column,
int funcNameStartLine,
int funcNameStartColumn) const
{
CodeCompleter codeCompleter(*this, unsavedFiles);
const CodeCompletions completions = codeCompleter.complete(line, column);
const CodeCompletions completions = codeCompleter.complete(line, column,
funcNameStartLine,
funcNameStartColumn);
const CompletionCorrection correction = codeCompleter.neededCorrection();
return CodeCompletionResult{completions, correction};
......
......@@ -75,7 +75,8 @@ public:
TranslationUnitUpdateResult parse(const TranslationUnitUpdateInput &parseInput) const;
TranslationUnitUpdateResult reparse(const TranslationUnitUpdateInput &parseInput) const;
CodeCompletionResult complete(UnsavedFiles &unsavedFiles, uint line, uint column) const;
CodeCompletionResult complete(UnsavedFiles &unsavedFiles, uint line, uint column,
int funcNameStartLine, int funcNameStartColumn) const;
void extractDiagnostics(DiagnosticContainer &firstHeaderErrorDiagnostic,
QVector<DiagnosticContainer> &mainFileDiagnostics) const;
......
......@@ -81,11 +81,22 @@ CodeCompleter::CodeCompleter(const TranslationUnit &translationUnit,
{
}
CodeCompletions CodeCompleter::complete(uint line, uint column)
CodeCompletions CodeCompleter::complete(uint line, uint column,
int funcNameStartLine,
int funcNameStartColumn)
{
neededCorrection_ = CompletionCorrection::NoCorrection;
ClangCodeCompleteResults results = completeHelper(line, column);
// Check if we have a smart pointer completion and get proper constructor signatures in results.
// Results are empty when it's not a smart pointer or this completion failed.
ClangCodeCompleteResults results = completeSmartPointerCreation(line,
column,
funcNameStartLine,
funcNameStartColumn);
if (results.isNull() || results.isEmpty())
results = completeHelper(line, column);
filterUnknownContextResults(results, unsavedFile(), line, column);
tryDotArrowCorrectionIfNoResults(results, line, column);
......@@ -97,6 +108,62 @@ CompletionCorrection CodeCompleter::neededCorrection() const
return neededCorrection_;
}
// For given "make_unique<T>" / "make_shared<T>" / "QSharedPointer<T>::create" return "new T("
// Otherwize return empty QString
static QString tweakName(const QString &oldName)
{
QString fullName = oldName.trimmed();
if (!fullName.contains('>'))
return QString();
if (!fullName.endsWith('>')) {
// This is the class<type>::method case - remove ::method part
if (!fullName.endsWith("create") || !fullName.contains("QSharedPointer"))
return QString();
fullName = fullName.mid(0, fullName.lastIndexOf(':') - 1);
} else if (!fullName.contains("make_unique") && !fullName.contains("make_shared")) {
return QString();
}
int templateStart = fullName.indexOf('<');
QString name = fullName.mid(0, templateStart);
QString templatePart = fullName.mid(templateStart + 1,
fullName.length() - templateStart - 2);
return "new " + templatePart + "(";
}
ClangCodeCompleteResults CodeCompleter::completeSmartPointerCreation(uint line,
uint column,
int funcNameStartLine,
int funcNameStartColumn)
{
if (column <= 1 || funcNameStartLine == -1)
return ClangCodeCompleteResults();
UnsavedFile &file = unsavedFiles.unsavedFile(translationUnit.filePath());
if (!file.hasCharacterAt(line, column - 1, '('))
return ClangCodeCompleteResults();
bool ok;
const uint startPos = file.toUtf8Position(funcNameStartLine, funcNameStartColumn, &ok);
const uint endPos = file.toUtf8Position(line, column - 1, &ok);
Utf8String content = file.fileContent();
const QString oldName = content.mid(startPos, endPos - startPos);
const QString updatedName = tweakName(oldName);
if (updatedName.isEmpty())
return ClangCodeCompleteResults();
column += updatedName.length();
file.replaceAt(endPos + 1, 0, updatedName);
ClangCodeCompleteResults results = completeHelper(line, column);
if (results.isEmpty()) {
column -= updatedName.length();
file.replaceAt(endPos + 1, updatedName.length(), QString());
}
return results;
}
ClangCodeCompleteResults CodeCompleter::completeHelper(uint line, uint column)
{
const Utf8String nativeFilePath = FilePath::toNativeSeparators(translationUnit.filePath());
......
......@@ -43,7 +43,9 @@ public:
CodeCompleter(const TranslationUnit &translationUnit,
const UnsavedFiles &unsavedFiles);
CodeCompletions complete(uint line, uint column);
CodeCompletions complete(uint line, uint column,
int funcNameStartLine = -1,
int funcNameStartColumn = -1);
CompletionCorrection neededCorrection() const;
......@@ -56,6 +58,10 @@ private:
uint column);
ClangCodeCompleteResults completeHelper(uint line, uint column);
ClangCodeCompleteResults completeSmartPointerCreation(uint line,
uint column,
int funcNameStartLine,
int funcNameStartColumn);
ClangCodeCompleteResults completeWithArrowInsteadOfDot(uint line,
uint column,
uint dotPosition);
......
......@@ -188,6 +188,12 @@ protected:
readFileContent(QStringLiteral("/complete_withGlobalCompletionAfterForwardDeclaredClassPointer.cpp")),
true
};
ClangBackEnd::FileContainer smartPointerCompletion{
Utf8StringLiteral(TESTDATA_DIR"/complete_smartpointer.cpp"),
projectPart.projectPartId(),
readFileContent(QStringLiteral("/complete_smartpointer.cpp")),
true
};
};
using CodeCompleterSlowTest = CodeCompleter;
......@@ -297,6 +303,33 @@ TEST_F(CodeCompleterSlowTest, FunctionInIncludedHeader)
CodeCompletion::FunctionCompletionKind)));
}
TEST_F(CodeCompleterSlowTest, UniquePointerCompletion)
{
auto myCompleter = setupCompleter(smartPointerCompletion);
ASSERT_THAT(myCompleter.complete(55, 54, 55, 32),
Contains(IsCodeCompletion(Utf8StringLiteral("Bar"),
CodeCompletion::ConstructorCompletionKind)));
}
TEST_F(CodeCompleterSlowTest, SharedPointerCompletion)
{
auto myCompleter = setupCompleter(smartPointerCompletion);
ASSERT_THAT(myCompleter.complete(56, 55, 56, 33),
Contains(IsCodeCompletion(Utf8StringLiteral("Bar"),
CodeCompletion::ConstructorCompletionKind)));
}
TEST_F(CodeCompleterSlowTest, QSharedPointerCompletion)
{
auto myCompleter = setupCompleter(smartPointerCompletion);
ASSERT_THAT(myCompleter.complete(57, 60, 57, 32),
Contains(IsCodeCompletion(Utf8StringLiteral("Bar"),
CodeCompletion::ConstructorCompletionKind)));
}
TEST_F(CodeCompleterSlowTest, FunctionInUnsavedIncludedHeader)
{
unsavedFiles.createOrUpdate({unsavedTargetHeaderFileContainer});
......
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
namespace std {
template<class Type, class... Args>
class unique_ptr {};
template<class Type, class... Args>
class shared_ptr {};
template<class Type, class... Args>
unique_ptr<Type> make_unique(Args&&... args);
template<class Type, class... Args>
shared_ptr<Type> make_shared(Args&&... args);
} // namespace std
template<class Type>
class QSharedPointer
{
public:
template<class... Args>
static QSharedPointer<Type> create(Args&&... args);
};
class Bar
{
public:
Bar();
Bar(int, int);
};
void f2()
{
std::unique_ptr<Bar> bar = std::make_unique<Bar>();
std::shared_ptr<Bar> bar2 = std::make_shared<Bar>();
QSharedPointer<Bar> bar3 = QSharedPointer<Bar>::create();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment