forked from qt-creator/qt-creator
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 <nikolai.kosjar@qt.io>
This commit is contained in:
@@ -212,6 +212,8 @@ void ClangCodeModelServer::completeCode(const ClangBackEnd::CompleteCodeMessage
|
|||||||
JobRequest jobRequest = processor.createJobRequest(JobRequest::Type::CompleteCode);
|
JobRequest jobRequest = processor.createJobRequest(JobRequest::Type::CompleteCode);
|
||||||
jobRequest.line = message.line();
|
jobRequest.line = message.line();
|
||||||
jobRequest.column = message.column();
|
jobRequest.column = message.column();
|
||||||
|
jobRequest.funcNameStartLine = message.funcNameStartLine();
|
||||||
|
jobRequest.funcNameStartColumn = message.funcNameStartColumn();
|
||||||
jobRequest.ticketNumber = message.ticketNumber();
|
jobRequest.ticketNumber = message.ticketNumber();
|
||||||
|
|
||||||
processor.addJob(jobRequest);
|
processor.addJob(jobRequest);
|
||||||
|
@@ -36,12 +36,15 @@ namespace ClangBackEnd {
|
|||||||
static CompleteCodeJob::AsyncResult runAsyncHelper(const TranslationUnit &translationUnit,
|
static CompleteCodeJob::AsyncResult runAsyncHelper(const TranslationUnit &translationUnit,
|
||||||
UnsavedFiles unsavedFiles,
|
UnsavedFiles unsavedFiles,
|
||||||
quint32 line,
|
quint32 line,
|
||||||
quint32 column)
|
quint32 column,
|
||||||
|
qint32 funcNameStartLine,
|
||||||
|
qint32 funcNameStartColumn)
|
||||||
{
|
{
|
||||||
TIME_SCOPE_DURATION("CompleteCodeJobRunner");
|
TIME_SCOPE_DURATION("CompleteCodeJobRunner");
|
||||||
|
|
||||||
const TranslationUnit::CodeCompletionResult results
|
const TranslationUnit::CodeCompletionResult results
|
||||||
= translationUnit.complete(unsavedFiles, line, column);
|
= translationUnit.complete(unsavedFiles, line, column,
|
||||||
|
funcNameStartLine, funcNameStartColumn);
|
||||||
|
|
||||||
CompleteCodeJob::AsyncResult asyncResult;
|
CompleteCodeJob::AsyncResult asyncResult;
|
||||||
asyncResult.completions = results.completions;
|
asyncResult.completions = results.completions;
|
||||||
@@ -63,8 +66,12 @@ IAsyncJob::AsyncPrepareResult CompleteCodeJob::prepareAsyncRun()
|
|||||||
const UnsavedFiles unsavedFiles = *context().unsavedFiles;
|
const UnsavedFiles unsavedFiles = *context().unsavedFiles;
|
||||||
const quint32 line = jobRequest.line;
|
const quint32 line = jobRequest.line;
|
||||||
const quint32 column = jobRequest.column;
|
const quint32 column = jobRequest.column;
|
||||||
setRunner([translationUnit, unsavedFiles, line, column]() {
|
const qint32 funcNameStartLine = jobRequest.funcNameStartLine;
|
||||||
return runAsyncHelper(translationUnit, unsavedFiles, line, column);
|
const qint32 funcNameStartColumn = jobRequest.funcNameStartColumn;
|
||||||
|
setRunner([translationUnit, unsavedFiles, line, column,
|
||||||
|
funcNameStartLine, funcNameStartColumn]() {
|
||||||
|
return runAsyncHelper(translationUnit, unsavedFiles, line, column,
|
||||||
|
funcNameStartLine, funcNameStartColumn);
|
||||||
});
|
});
|
||||||
return AsyncPrepareResult{translationUnit.id()};
|
return AsyncPrepareResult{translationUnit.id()};
|
||||||
|
|
||||||
|
@@ -110,6 +110,8 @@ public:
|
|||||||
// Specific to some jobs
|
// Specific to some jobs
|
||||||
quint32 line = 0;
|
quint32 line = 0;
|
||||||
quint32 column = 0;
|
quint32 column = 0;
|
||||||
|
qint32 funcNameStartLine = -1;
|
||||||
|
qint32 funcNameStartColumn = -1;
|
||||||
quint64 ticketNumber = 0;
|
quint64 ticketNumber = 0;
|
||||||
Utf8StringVector dependentFiles;
|
Utf8StringVector dependentFiles;
|
||||||
bool resolveTarget = true;
|
bool resolveTarget = true;
|
||||||
|
@@ -119,11 +119,15 @@ bool TranslationUnit::suspend() const
|
|||||||
TranslationUnit::CodeCompletionResult TranslationUnit::complete(
|
TranslationUnit::CodeCompletionResult TranslationUnit::complete(
|
||||||
UnsavedFiles &unsavedFiles,
|
UnsavedFiles &unsavedFiles,
|
||||||
uint line,
|
uint line,
|
||||||
uint column) const
|
uint column,
|
||||||
|
int funcNameStartLine,
|
||||||
|
int funcNameStartColumn) const
|
||||||
{
|
{
|
||||||
CodeCompleter codeCompleter(*this, unsavedFiles);
|
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();
|
const CompletionCorrection correction = codeCompleter.neededCorrection();
|
||||||
|
|
||||||
return CodeCompletionResult{completions, correction};
|
return CodeCompletionResult{completions, correction};
|
||||||
|
@@ -75,7 +75,8 @@ public:
|
|||||||
TranslationUnitUpdateResult parse(const TranslationUnitUpdateInput &parseInput) const;
|
TranslationUnitUpdateResult parse(const TranslationUnitUpdateInput &parseInput) const;
|
||||||
TranslationUnitUpdateResult reparse(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,
|
void extractDiagnostics(DiagnosticContainer &firstHeaderErrorDiagnostic,
|
||||||
QVector<DiagnosticContainer> &mainFileDiagnostics) const;
|
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;
|
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);
|
filterUnknownContextResults(results, unsavedFile(), line, column);
|
||||||
tryDotArrowCorrectionIfNoResults(results, line, column);
|
tryDotArrowCorrectionIfNoResults(results, line, column);
|
||||||
|
|
||||||
@@ -97,6 +108,62 @@ CompletionCorrection CodeCompleter::neededCorrection() const
|
|||||||
return neededCorrection_;
|
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)
|
ClangCodeCompleteResults CodeCompleter::completeHelper(uint line, uint column)
|
||||||
{
|
{
|
||||||
const Utf8String nativeFilePath = FilePath::toNativeSeparators(translationUnit.filePath());
|
const Utf8String nativeFilePath = FilePath::toNativeSeparators(translationUnit.filePath());
|
||||||
|
@@ -43,7 +43,9 @@ public:
|
|||||||
CodeCompleter(const TranslationUnit &translationUnit,
|
CodeCompleter(const TranslationUnit &translationUnit,
|
||||||
const UnsavedFiles &unsavedFiles);
|
const UnsavedFiles &unsavedFiles);
|
||||||
|
|
||||||
CodeCompletions complete(uint line, uint column);
|
CodeCompletions complete(uint line, uint column,
|
||||||
|
int funcNameStartLine = -1,
|
||||||
|
int funcNameStartColumn = -1);
|
||||||
|
|
||||||
CompletionCorrection neededCorrection() const;
|
CompletionCorrection neededCorrection() const;
|
||||||
|
|
||||||
@@ -56,6 +58,10 @@ private:
|
|||||||
uint column);
|
uint column);
|
||||||
|
|
||||||
ClangCodeCompleteResults completeHelper(uint line, uint column);
|
ClangCodeCompleteResults completeHelper(uint line, uint column);
|
||||||
|
ClangCodeCompleteResults completeSmartPointerCreation(uint line,
|
||||||
|
uint column,
|
||||||
|
int funcNameStartLine,
|
||||||
|
int funcNameStartColumn);
|
||||||
ClangCodeCompleteResults completeWithArrowInsteadOfDot(uint line,
|
ClangCodeCompleteResults completeWithArrowInsteadOfDot(uint line,
|
||||||
uint column,
|
uint column,
|
||||||
uint dotPosition);
|
uint dotPosition);
|
||||||
|
@@ -188,6 +188,12 @@ protected:
|
|||||||
readFileContent(QStringLiteral("/complete_withGlobalCompletionAfterForwardDeclaredClassPointer.cpp")),
|
readFileContent(QStringLiteral("/complete_withGlobalCompletionAfterForwardDeclaredClassPointer.cpp")),
|
||||||
true
|
true
|
||||||
};
|
};
|
||||||
|
ClangBackEnd::FileContainer smartPointerCompletion{
|
||||||
|
Utf8StringLiteral(TESTDATA_DIR"/complete_smartpointer.cpp"),
|
||||||
|
projectPart.projectPartId(),
|
||||||
|
readFileContent(QStringLiteral("/complete_smartpointer.cpp")),
|
||||||
|
true
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
using CodeCompleterSlowTest = CodeCompleter;
|
using CodeCompleterSlowTest = CodeCompleter;
|
||||||
@@ -297,6 +303,33 @@ TEST_F(CodeCompleterSlowTest, FunctionInIncludedHeader)
|
|||||||
CodeCompletion::FunctionCompletionKind)));
|
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)
|
TEST_F(CodeCompleterSlowTest, FunctionInUnsavedIncludedHeader)
|
||||||
{
|
{
|
||||||
unsavedFiles.createOrUpdate({unsavedTargetHeaderFileContainer});
|
unsavedFiles.createOrUpdate({unsavedTargetHeaderFileContainer});
|
||||||
|
58
tests/unit/unittest/data/complete_smartpointer.cpp
Normal file
58
tests/unit/unittest/data/complete_smartpointer.cpp
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** 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();
|
||||||
|
}
|
Reference in New Issue
Block a user