forked from qt-creator/qt-creator
CMakeFileCompletionAssist: Fix a crash on completion
Avoid calling not-thread safe functions from inside the CMakeFileCompletionAssist::performAsync(). Move calling these functions before the asyncRun, collect the needed data inside the PerformInputData structure and pass it to the other thread instead. Fixes: QTCREATORBUG-29683 Change-Id: I56127163a47339bc45d47f3a7d2c76d518b640f7 Reviewed-by: Cristian Adam <cristian.adam@qt.io>
This commit is contained in:
@@ -23,6 +23,7 @@
|
|||||||
#include <texteditor/codeassist/genericproposal.h>
|
#include <texteditor/codeassist/genericproposal.h>
|
||||||
#include <texteditor/texteditorsettings.h>
|
#include <texteditor/texteditorsettings.h>
|
||||||
|
|
||||||
|
#include <utils/async.h>
|
||||||
#include <utils/fsengine/fileiconprovider.h>
|
#include <utils/fsengine/fileiconprovider.h>
|
||||||
#include <utils/utilsicons.h>
|
#include <utils/utilsicons.h>
|
||||||
|
|
||||||
@@ -32,12 +33,15 @@ using namespace Utils;
|
|||||||
|
|
||||||
namespace CMakeProjectManager::Internal {
|
namespace CMakeProjectManager::Internal {
|
||||||
|
|
||||||
|
class PerformInputData;
|
||||||
|
|
||||||
class CMakeFileCompletionAssist : public AsyncProcessor
|
class CMakeFileCompletionAssist : public AsyncProcessor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CMakeFileCompletionAssist();
|
CMakeFileCompletionAssist();
|
||||||
|
|
||||||
IAssistProposal *performAsync() final;
|
IAssistProposal *perform() final;
|
||||||
|
IAssistProposal *performAsync() final { return nullptr; }
|
||||||
|
|
||||||
const QIcon m_variableIcon;
|
const QIcon m_variableIcon;
|
||||||
const QIcon m_projectVariableIcon;
|
const QIcon m_projectVariableIcon;
|
||||||
@@ -51,6 +55,10 @@ public:
|
|||||||
const QIcon m_importedTargetIcon;
|
const QIcon m_importedTargetIcon;
|
||||||
|
|
||||||
TextEditor::SnippetAssistCollector m_snippetCollector;
|
TextEditor::SnippetAssistCollector m_snippetCollector;
|
||||||
|
|
||||||
|
private:
|
||||||
|
IAssistProposal *doPerform(const PerformInputData &data);
|
||||||
|
PerformInputData generatePerformInputData() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
CMakeFileCompletionAssist::CMakeFileCompletionAssist()
|
CMakeFileCompletionAssist::CMakeFileCompletionAssist()
|
||||||
@@ -242,41 +250,62 @@ static QPair<QStringList, QStringList> getLocalFunctionsAndVariables(const QByte
|
|||||||
return {functions, variables};
|
return {functions, variables};
|
||||||
}
|
}
|
||||||
|
|
||||||
IAssistProposal *CMakeFileCompletionAssist::performAsync()
|
class PerformInputData
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
CMakeKeywords keywords;
|
CMakeKeywords keywords;
|
||||||
CMakeKeywords projectKeywords;
|
CMakeKeywords projectKeywords;
|
||||||
const FilePath &filePath = interface()->filePath();
|
|
||||||
if (!filePath.isEmpty() && filePath.isFile()) {
|
|
||||||
if (auto tool = CMakeToolManager::defaultProjectOrDefaultCMakeTool())
|
|
||||||
keywords = tool->keywords();
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList buildTargets;
|
QStringList buildTargets;
|
||||||
QStringList importedTargets;
|
QStringList importedTargets;
|
||||||
QStringList findPackageVariables;
|
QStringList findPackageVariables;
|
||||||
|
};
|
||||||
|
|
||||||
|
PerformInputData CMakeFileCompletionAssist::generatePerformInputData() const
|
||||||
|
{
|
||||||
|
PerformInputData data;
|
||||||
|
|
||||||
|
const FilePath &filePath = interface()->filePath();
|
||||||
|
if (!filePath.isEmpty() && filePath.isFile()) {
|
||||||
|
if (auto tool = CMakeToolManager::defaultProjectOrDefaultCMakeTool())
|
||||||
|
data.keywords = tool->keywords();
|
||||||
|
}
|
||||||
|
|
||||||
if (auto bs = qobject_cast<CMakeBuildSystem *>(ProjectTree::currentBuildSystem())) {
|
if (auto bs = qobject_cast<CMakeBuildSystem *>(ProjectTree::currentBuildSystem())) {
|
||||||
for (const auto &target : std::as_const(bs->buildTargets()))
|
for (const auto &target : std::as_const(bs->buildTargets()))
|
||||||
if (target.targetType != TargetType::UtilityType)
|
if (target.targetType != TargetType::UtilityType)
|
||||||
buildTargets << target.title;
|
data.buildTargets << target.title;
|
||||||
projectKeywords = bs->projectKeywords();
|
data.projectKeywords = bs->projectKeywords();
|
||||||
importedTargets = bs->projectImportedTargets();
|
data.importedTargets = bs->projectImportedTargets();
|
||||||
findPackageVariables = bs->projectFindPackageVariables();
|
data.findPackageVariables = bs->projectFindPackageVariables();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
IAssistProposal *CMakeFileCompletionAssist::perform()
|
||||||
|
{
|
||||||
|
IAssistProposal *result = immediateProposal();
|
||||||
|
interface()->prepareForAsyncUse();
|
||||||
|
m_watcher.setFuture(Utils::asyncRun([this, inputData = generatePerformInputData()] {
|
||||||
|
interface()->recreateTextDocument();
|
||||||
|
return doPerform(inputData);
|
||||||
|
}));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
IAssistProposal *CMakeFileCompletionAssist::doPerform(const PerformInputData &data)
|
||||||
|
{
|
||||||
if (isInComment(interface()))
|
if (isInComment(interface()))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
const int startPos = findWordStart(interface(), interface()->position());
|
const int startPos = findWordStart(interface(), interface()->position());
|
||||||
|
|
||||||
const int functionStart = findFunctionStart(interface());
|
const int functionStart = findFunctionStart(interface());
|
||||||
const int prevFunctionEnd = findFunctionEnd(interface());
|
const int prevFunctionEnd = findFunctionEnd(interface());
|
||||||
|
|
||||||
QString functionName;
|
QString functionName;
|
||||||
if (functionStart > prevFunctionEnd) {
|
if (functionStart > prevFunctionEnd) {
|
||||||
int functionStartPos = findWordStart(interface(), functionStart);
|
const int functionStartPos = findWordStart(interface(), functionStart);
|
||||||
functionName
|
functionName = interface()->textAt(functionStartPos, functionStart - functionStartPos);
|
||||||
= interface()->textAt(functionStartPos, functionStart - functionStartPos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (interface()->reason() == IdleEditor) {
|
if (interface()->reason() == IdleEditor) {
|
||||||
@@ -296,12 +325,12 @@ IAssistProposal *CMakeFileCompletionAssist::performAsync()
|
|||||||
const QString varGenexToken = interface()->textAt(startPos - 2, 2);
|
const QString varGenexToken = interface()->textAt(startPos - 2, 2);
|
||||||
if (varGenexToken == "${" || varGenexToken == "$<") {
|
if (varGenexToken == "${" || varGenexToken == "$<") {
|
||||||
if (varGenexToken == "${") {
|
if (varGenexToken == "${") {
|
||||||
items.append(generateList(keywords.variables, m_variableIcon));
|
items.append(generateList(data.keywords.variables, m_variableIcon));
|
||||||
items.append(generateList(projectKeywords.variables, m_projectVariableIcon));
|
items.append(generateList(data.projectKeywords.variables, m_projectVariableIcon));
|
||||||
items.append(generateList(findPackageVariables, m_projectVariableIcon));
|
items.append(generateList(data.findPackageVariables, m_projectVariableIcon));
|
||||||
}
|
}
|
||||||
if (varGenexToken == "$<")
|
if (varGenexToken == "$<")
|
||||||
items.append(generateList(keywords.generatorExpressions, m_genexIcon));
|
items.append(generateList(data.keywords.generatorExpressions, m_genexIcon));
|
||||||
|
|
||||||
return new GenericProposal(startPos, items);
|
return new GenericProposal(startPos, items);
|
||||||
}
|
}
|
||||||
@@ -312,56 +341,56 @@ IAssistProposal *CMakeFileCompletionAssist::performAsync()
|
|||||||
if (functionName == "if" || functionName == "elseif" || functionName == "while"
|
if (functionName == "if" || functionName == "elseif" || functionName == "while"
|
||||||
|| functionName == "set" || functionName == "list"
|
|| functionName == "set" || functionName == "list"
|
||||||
|| functionName == "cmake_print_variables") {
|
|| functionName == "cmake_print_variables") {
|
||||||
items.append(generateList(keywords.variables, m_variableIcon));
|
items.append(generateList(data.keywords.variables, m_variableIcon));
|
||||||
items.append(generateList(projectKeywords.variables, m_projectVariableIcon));
|
items.append(generateList(data.projectKeywords.variables, m_projectVariableIcon));
|
||||||
items.append(generateList(findPackageVariables, m_projectVariableIcon));
|
items.append(generateList(data.findPackageVariables, m_projectVariableIcon));
|
||||||
items.append(generateList(localVariables, m_variableIcon));
|
items.append(generateList(localVariables, m_variableIcon));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (functionName == "if" || functionName == "elseif" || functionName == "cmake_policy")
|
if (functionName == "if" || functionName == "elseif" || functionName == "cmake_policy")
|
||||||
items.append(generateList(keywords.policies, m_variableIcon));
|
items.append(generateList(data.keywords.policies, m_variableIcon));
|
||||||
|
|
||||||
if (functionName.contains("path") || functionName.contains("file")
|
if (functionName.contains("path") || functionName.contains("file")
|
||||||
|| functionName.contains("add_executable") || functionName.contains("add_library")
|
|| functionName.contains("add_executable") || functionName.contains("add_library")
|
||||||
|| functionName == "include" || functionName == "add_subdirectory"
|
|| functionName == "include" || functionName == "add_subdirectory"
|
||||||
|| functionName == "install" || functionName == "target_sources" || functionName == "set"
|
|| functionName == "install" || functionName == "target_sources"
|
||||||
|| functionName == "list") {
|
|| functionName == "set" || functionName == "list") {
|
||||||
fileStartPos = addFilePathItems(interface(), items, startPos);
|
fileStartPos = addFilePathItems(interface(), items, startPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (functionName == "set_property" || functionName == "cmake_print_properties")
|
if (functionName == "set_property" || functionName == "cmake_print_properties")
|
||||||
items.append(generateList(keywords.properties, m_propertyIcon));
|
items.append(generateList(data.keywords.properties, m_propertyIcon));
|
||||||
|
|
||||||
if (functionName == "set_directory_properties")
|
if (functionName == "set_directory_properties")
|
||||||
items.append(generateList(keywords.directoryProperties, m_propertyIcon));
|
items.append(generateList(data.keywords.directoryProperties, m_propertyIcon));
|
||||||
if (functionName == "set_source_files_properties")
|
if (functionName == "set_source_files_properties")
|
||||||
items.append(generateList(keywords.sourceProperties, m_propertyIcon));
|
items.append(generateList(data.keywords.sourceProperties, m_propertyIcon));
|
||||||
if (functionName == "set_target_properties")
|
if (functionName == "set_target_properties")
|
||||||
items.append(generateList(keywords.targetProperties, m_propertyIcon));
|
items.append(generateList(data.keywords.targetProperties, m_propertyIcon));
|
||||||
if (functionName == "set_tests_properties")
|
if (functionName == "set_tests_properties")
|
||||||
items.append(generateList(keywords.testProperties, m_propertyIcon));
|
items.append(generateList(data.keywords.testProperties, m_propertyIcon));
|
||||||
|
|
||||||
if (functionName == "include" && !onlyFileItems())
|
if (functionName == "include" && !onlyFileItems())
|
||||||
items.append(generateList(keywords.includeStandardModules, m_moduleIcon));
|
items.append(generateList(data.keywords.includeStandardModules, m_moduleIcon));
|
||||||
if (functionName == "find_package")
|
if (functionName == "find_package")
|
||||||
items.append(generateList(keywords.findModules, m_moduleIcon));
|
items.append(generateList(data.keywords.findModules, m_moduleIcon));
|
||||||
|
|
||||||
if ((functionName.contains("target") || functionName == "install"
|
if ((functionName.contains("target") || functionName == "install"
|
||||||
|| functionName == "add_dependencies" || functionName == "set_property"
|
|| functionName == "add_dependencies" || functionName == "set_property"
|
||||||
|| functionName == "export" || functionName == "cmake_print_properties"
|
|| functionName == "export" || functionName == "cmake_print_properties"
|
||||||
|| functionName == "if" || functionName == "elseif")
|
|| functionName == "if" || functionName == "elseif")
|
||||||
&& !onlyFileItems()) {
|
&& !onlyFileItems()) {
|
||||||
items.append(generateList(buildTargets, m_targetsIcon));
|
items.append(generateList(data.buildTargets, m_targetsIcon));
|
||||||
items.append(generateList(importedTargets, m_importedTargetIcon));
|
items.append(generateList(data.importedTargets, m_importedTargetIcon));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keywords.functionArgs.contains(functionName) && !onlyFileItems()) {
|
if (data.keywords.functionArgs.contains(functionName) && !onlyFileItems()) {
|
||||||
QStringList functionSymbols = keywords.functionArgs.value(functionName);
|
const QStringList functionSymbols = data.keywords.functionArgs.value(functionName);
|
||||||
items.append(generateList(functionSymbols, m_argsIcon));
|
items.append(generateList(functionSymbols, m_argsIcon));
|
||||||
} else if (functionName.isEmpty()) {
|
} else if (functionName.isEmpty()) {
|
||||||
// On a new line we just want functions
|
// On a new line we just want functions
|
||||||
items.append(generateList(keywords.functions, m_functionIcon));
|
items.append(generateList(data.keywords.functions, m_functionIcon));
|
||||||
items.append(generateList(projectKeywords.functions, m_projectFunctionIcon));
|
items.append(generateList(data.projectKeywords.functions, m_projectFunctionIcon));
|
||||||
items.append(generateList(localFunctions, m_functionIcon));
|
items.append(generateList(localFunctions, m_functionIcon));
|
||||||
|
|
||||||
// Snippets would make more sense only for the top level suggestions
|
// Snippets would make more sense only for the top level suggestions
|
||||||
@@ -370,14 +399,14 @@ IAssistProposal *CMakeFileCompletionAssist::performAsync()
|
|||||||
// Inside an unknown function we could have variables or properties
|
// Inside an unknown function we could have variables or properties
|
||||||
fileStartPos = addFilePathItems(interface(), items, startPos);
|
fileStartPos = addFilePathItems(interface(), items, startPos);
|
||||||
if (!onlyFileItems()) {
|
if (!onlyFileItems()) {
|
||||||
items.append(generateList(keywords.variables, m_variableIcon));
|
items.append(generateList(data.keywords.variables, m_variableIcon));
|
||||||
items.append(generateList(projectKeywords.variables, m_projectVariableIcon));
|
items.append(generateList(data.projectKeywords.variables, m_projectVariableIcon));
|
||||||
items.append(generateList(localVariables, m_variableIcon));
|
items.append(generateList(localVariables, m_variableIcon));
|
||||||
items.append(generateList(findPackageVariables, m_projectVariableIcon));
|
items.append(generateList(data.findPackageVariables, m_projectVariableIcon));
|
||||||
|
|
||||||
items.append(generateList(keywords.properties, m_propertyIcon));
|
items.append(generateList(data.keywords.properties, m_propertyIcon));
|
||||||
items.append(generateList(buildTargets, m_targetsIcon));
|
items.append(generateList(data.buildTargets, m_targetsIcon));
|
||||||
items.append(generateList(importedTargets, m_importedTargetIcon));
|
items.append(generateList(data.importedTargets, m_importedTargetIcon));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class TEXTEDITOR_EXPORT AsyncProcessor : public TextEditor::IAssistProcessor
|
|||||||
public:
|
public:
|
||||||
AsyncProcessor();
|
AsyncProcessor();
|
||||||
|
|
||||||
IAssistProposal *perform() final;
|
IAssistProposal *perform() override;
|
||||||
bool running() override;
|
bool running() override;
|
||||||
void cancel() override;
|
void cancel() override;
|
||||||
|
|
||||||
@@ -23,8 +23,6 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool isCanceled() const;
|
bool isCanceled() const;
|
||||||
|
|
||||||
private:
|
|
||||||
QFutureWatcher<IAssistProposal *> m_watcher;
|
QFutureWatcher<IAssistProposal *> m_watcher;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user