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