forked from qt-creator/qt-creator
CppEditor: Move ExtractLiteralAsParameter quickfix to its own files
Change-Id: Ib443b2ae2b3b07685471384b0d26c8ef9deac1b2 Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
@@ -111,6 +111,7 @@ add_qtc_plugin(CppEditor
|
|||||||
quickfixes/cppquickfixsettingswidget.cpp quickfixes/cppquickfixsettingswidget.h
|
quickfixes/cppquickfixsettingswidget.cpp quickfixes/cppquickfixsettingswidget.h
|
||||||
quickfixes/createdeclarationfromuse.cpp quickfixes/createdeclarationfromuse.h
|
quickfixes/createdeclarationfromuse.cpp quickfixes/createdeclarationfromuse.h
|
||||||
quickfixes/extractfunction.cpp quickfixes/extractfunction.h
|
quickfixes/extractfunction.cpp quickfixes/extractfunction.h
|
||||||
|
quickfixes/extractliteralasparameter.cpp quickfixes/extractliteralasparameter.h
|
||||||
quickfixes/insertfunctiondefinition.cpp quickfixes/insertfunctiondefinition.h
|
quickfixes/insertfunctiondefinition.cpp quickfixes/insertfunctiondefinition.h
|
||||||
quickfixes/logicaloperationquickfixes.cpp quickfixes/logicaloperationquickfixes.h
|
quickfixes/logicaloperationquickfixes.cpp quickfixes/logicaloperationquickfixes.h
|
||||||
quickfixes/moveclasstoownfile.cpp quickfixes/moveclasstoownfile.h
|
quickfixes/moveclasstoownfile.cpp quickfixes/moveclasstoownfile.h
|
||||||
|
@@ -251,6 +251,8 @@ QtcPlugin {
|
|||||||
"createdeclarationfromuse.h",
|
"createdeclarationfromuse.h",
|
||||||
"extractfunction.cpp",
|
"extractfunction.cpp",
|
||||||
"extractfunction.h",
|
"extractfunction.h",
|
||||||
|
"extractliteralasparameter.cpp",
|
||||||
|
"extractliteralasparameter.h",
|
||||||
"insertfunctiondefinition.cpp",
|
"insertfunctiondefinition.cpp",
|
||||||
"insertfunctiondefinition.h",
|
"insertfunctiondefinition.h",
|
||||||
"logicaloperationquickfixes.cpp",
|
"logicaloperationquickfixes.cpp",
|
||||||
|
@@ -1039,47 +1039,6 @@ void QuickfixTest::testGeneric_data()
|
|||||||
"}"
|
"}"
|
||||||
) << _();
|
) << _();
|
||||||
|
|
||||||
QTest::newRow("ExtractLiteralAsParameter_freeFunction")
|
|
||||||
<< CppQuickFixFactoryPtr(new ExtractLiteralAsParameter) << _(
|
|
||||||
"void foo(const char *a, long b = 1)\n"
|
|
||||||
"{return 1@56 + 123 + 156;}\n"
|
|
||||||
) << _(
|
|
||||||
"void foo(const char *a, long b = 1, int newParameter = 156)\n"
|
|
||||||
"{return newParameter + 123 + newParameter;}\n"
|
|
||||||
);
|
|
||||||
|
|
||||||
QTest::newRow("ExtractLiteralAsParameter_memberFunction")
|
|
||||||
<< CppQuickFixFactoryPtr(new ExtractLiteralAsParameter) << _(
|
|
||||||
"class Narf {\n"
|
|
||||||
"public:\n"
|
|
||||||
" int zort();\n"
|
|
||||||
"};\n\n"
|
|
||||||
"int Narf::zort()\n"
|
|
||||||
"{ return 15@5 + 1; }\n"
|
|
||||||
) << _(
|
|
||||||
"class Narf {\n"
|
|
||||||
"public:\n"
|
|
||||||
" int zort(int newParameter = 155);\n"
|
|
||||||
"};\n\n"
|
|
||||||
"int Narf::zort(int newParameter)\n"
|
|
||||||
"{ return newParameter + 1; }\n"
|
|
||||||
);
|
|
||||||
|
|
||||||
QTest::newRow("ExtractLiteralAsParameter_memberFunctionInline")
|
|
||||||
<< CppQuickFixFactoryPtr(new ExtractLiteralAsParameter) << _(
|
|
||||||
"class Narf {\n"
|
|
||||||
"public:\n"
|
|
||||||
" int zort()\n"
|
|
||||||
" { return 15@5 + 1; }\n"
|
|
||||||
"};\n"
|
|
||||||
) << _(
|
|
||||||
"class Narf {\n"
|
|
||||||
"public:\n"
|
|
||||||
" int zort(int newParameter = 155)\n"
|
|
||||||
" { return newParameter + 1; }\n"
|
|
||||||
"};\n"
|
|
||||||
);
|
|
||||||
|
|
||||||
QTest::newRow("ConvertFromPointer")
|
QTest::newRow("ConvertFromPointer")
|
||||||
<< CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
|
<< CppQuickFixFactoryPtr(new ConvertFromAndToPointer)
|
||||||
<< _("void foo() {\n"
|
<< _("void foo() {\n"
|
||||||
@@ -1493,145 +1452,6 @@ void QuickfixTest::testAssignToLocalVariableTemplates()
|
|||||||
QuickFixOperationTest(testDocuments, &factory);
|
QuickFixOperationTest(testDocuments, &factory);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuickfixTest::testExtractLiteralAsParameterTypeDeduction_data()
|
|
||||||
{
|
|
||||||
QTest::addColumn<QByteArray>("typeString");
|
|
||||||
QTest::addColumn<QByteArray>("literal");
|
|
||||||
QTest::newRow("int")
|
|
||||||
<< QByteArray("int ") << QByteArray("156");
|
|
||||||
QTest::newRow("unsigned int")
|
|
||||||
<< QByteArray("unsigned int ") << QByteArray("156u");
|
|
||||||
QTest::newRow("long")
|
|
||||||
<< QByteArray("long ") << QByteArray("156l");
|
|
||||||
QTest::newRow("unsigned long")
|
|
||||||
<< QByteArray("unsigned long ") << QByteArray("156ul");
|
|
||||||
QTest::newRow("long long")
|
|
||||||
<< QByteArray("long long ") << QByteArray("156ll");
|
|
||||||
QTest::newRow("unsigned long long")
|
|
||||||
<< QByteArray("unsigned long long ") << QByteArray("156ull");
|
|
||||||
QTest::newRow("float")
|
|
||||||
<< QByteArray("float ") << QByteArray("3.14159f");
|
|
||||||
QTest::newRow("double")
|
|
||||||
<< QByteArray("double ") << QByteArray("3.14159");
|
|
||||||
QTest::newRow("long double")
|
|
||||||
<< QByteArray("long double ") << QByteArray("3.14159L");
|
|
||||||
QTest::newRow("bool")
|
|
||||||
<< QByteArray("bool ") << QByteArray("true");
|
|
||||||
QTest::newRow("bool")
|
|
||||||
<< QByteArray("bool ") << QByteArray("false");
|
|
||||||
QTest::newRow("char")
|
|
||||||
<< QByteArray("char ") << QByteArray("'X'");
|
|
||||||
QTest::newRow("wchar_t")
|
|
||||||
<< QByteArray("wchar_t ") << QByteArray("L'X'");
|
|
||||||
QTest::newRow("char16_t")
|
|
||||||
<< QByteArray("char16_t ") << QByteArray("u'X'");
|
|
||||||
QTest::newRow("char32_t")
|
|
||||||
<< QByteArray("char32_t ") << QByteArray("U'X'");
|
|
||||||
QTest::newRow("const char *")
|
|
||||||
<< QByteArray("const char *") << QByteArray("\"narf\"");
|
|
||||||
QTest::newRow("const wchar_t *")
|
|
||||||
<< QByteArray("const wchar_t *") << QByteArray("L\"narf\"");
|
|
||||||
QTest::newRow("const char16_t *")
|
|
||||||
<< QByteArray("const char16_t *") << QByteArray("u\"narf\"");
|
|
||||||
QTest::newRow("const char32_t *")
|
|
||||||
<< QByteArray("const char32_t *") << QByteArray("U\"narf\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuickfixTest::testExtractLiteralAsParameterTypeDeduction()
|
|
||||||
{
|
|
||||||
QFETCH(QByteArray, typeString);
|
|
||||||
QFETCH(QByteArray, literal);
|
|
||||||
const QByteArray original = QByteArray("void foo() {return @") + literal + QByteArray(";}\n");
|
|
||||||
const QByteArray expected = QByteArray("void foo(") + typeString + QByteArray("newParameter = ")
|
|
||||||
+ literal + QByteArray(") {return newParameter;}\n");
|
|
||||||
|
|
||||||
if (literal == "3.14159") {
|
|
||||||
qWarning("Literal 3.14159 is wrongly reported as int. Skipping.");
|
|
||||||
return;
|
|
||||||
} else if (literal == "3.14159L") {
|
|
||||||
qWarning("Literal 3.14159L is wrongly reported as long. Skipping.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExtractLiteralAsParameter factory;
|
|
||||||
QuickFixOperationTest(singleDocument(original, expected), &factory);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuickfixTest::testExtractLiteralAsParameterFreeFunctionSeparateFiles()
|
|
||||||
{
|
|
||||||
QList<TestDocumentPtr> testDocuments;
|
|
||||||
QByteArray original;
|
|
||||||
QByteArray expected;
|
|
||||||
|
|
||||||
// Header File
|
|
||||||
original =
|
|
||||||
"void foo(const char *a, long b = 1);\n";
|
|
||||||
expected =
|
|
||||||
"void foo(const char *a, long b = 1, int newParameter = 156);\n";
|
|
||||||
testDocuments << CppTestDocument::create("file.h", original, expected);
|
|
||||||
|
|
||||||
// Source File
|
|
||||||
original =
|
|
||||||
"void foo(const char *a, long b)\n"
|
|
||||||
"{return 1@56 + 123 + 156;}\n";
|
|
||||||
expected =
|
|
||||||
"void foo(const char *a, long b, int newParameter)\n"
|
|
||||||
"{return newParameter + 123 + newParameter;}\n";
|
|
||||||
testDocuments << CppTestDocument::create("file.cpp", original, expected);
|
|
||||||
|
|
||||||
ExtractLiteralAsParameter factory;
|
|
||||||
QuickFixOperationTest(testDocuments, &factory);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuickfixTest::testExtractLiteralAsParameterMemberFunctionSeparateFiles()
|
|
||||||
{
|
|
||||||
QList<TestDocumentPtr> testDocuments;
|
|
||||||
QByteArray original;
|
|
||||||
QByteArray expected;
|
|
||||||
|
|
||||||
// Header File
|
|
||||||
original =
|
|
||||||
"class Narf {\n"
|
|
||||||
"public:\n"
|
|
||||||
" int zort();\n"
|
|
||||||
"};\n";
|
|
||||||
expected =
|
|
||||||
"class Narf {\n"
|
|
||||||
"public:\n"
|
|
||||||
" int zort(int newParameter = 155);\n"
|
|
||||||
"};\n";
|
|
||||||
testDocuments << CppTestDocument::create("file.h", original, expected);
|
|
||||||
|
|
||||||
// Source File
|
|
||||||
original =
|
|
||||||
"#include \"file.h\"\n\n"
|
|
||||||
"int Narf::zort()\n"
|
|
||||||
"{ return 15@5 + 1; }\n";
|
|
||||||
expected =
|
|
||||||
"#include \"file.h\"\n\n"
|
|
||||||
"int Narf::zort(int newParameter)\n"
|
|
||||||
"{ return newParameter + 1; }\n";
|
|
||||||
testDocuments << CppTestDocument::create("file.cpp", original, expected);
|
|
||||||
|
|
||||||
ExtractLiteralAsParameter factory;
|
|
||||||
QuickFixOperationTest(testDocuments, &factory);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuickfixTest::testExtractLiteralAsParameterNotTriggeringForInvalidCode()
|
|
||||||
{
|
|
||||||
QList<TestDocumentPtr> testDocuments;
|
|
||||||
QByteArray original;
|
|
||||||
original =
|
|
||||||
"T(\"test\")\n"
|
|
||||||
"{\n"
|
|
||||||
" const int i = @14;\n"
|
|
||||||
"}\n";
|
|
||||||
testDocuments << CppTestDocument::create("file.cpp", original, "");
|
|
||||||
|
|
||||||
ExtractLiteralAsParameter factory;
|
|
||||||
QuickFixOperationTest(testDocuments, &factory);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuickfixTest::testConvertToMetaMethodInvocation_data()
|
void QuickfixTest::testConvertToMetaMethodInvocation_data()
|
||||||
{
|
{
|
||||||
QTest::addColumn<QByteArray>("input");
|
QTest::addColumn<QByteArray>("input");
|
||||||
|
@@ -99,12 +99,6 @@ private slots:
|
|||||||
|
|
||||||
void testAssignToLocalVariableTemplates();
|
void testAssignToLocalVariableTemplates();
|
||||||
|
|
||||||
void testExtractLiteralAsParameterTypeDeduction_data();
|
|
||||||
void testExtractLiteralAsParameterTypeDeduction();
|
|
||||||
void testExtractLiteralAsParameterFreeFunctionSeparateFiles();
|
|
||||||
void testExtractLiteralAsParameterMemberFunctionSeparateFiles();
|
|
||||||
void testExtractLiteralAsParameterNotTriggeringForInvalidCode();
|
|
||||||
|
|
||||||
void testConvertToMetaMethodInvocation_data();
|
void testConvertToMetaMethodInvocation_data();
|
||||||
void testConvertToMetaMethodInvocation();
|
void testConvertToMetaMethodInvocation();
|
||||||
};
|
};
|
||||||
|
@@ -11,7 +11,6 @@
|
|||||||
#include "../cpppointerdeclarationformatter.h"
|
#include "../cpppointerdeclarationformatter.h"
|
||||||
#include "../cpprefactoringchanges.h"
|
#include "../cpprefactoringchanges.h"
|
||||||
#include "../cpptoolsreuse.h"
|
#include "../cpptoolsreuse.h"
|
||||||
#include "../includeutils.h"
|
|
||||||
#include "../insertionpointlocator.h"
|
#include "../insertionpointlocator.h"
|
||||||
#include "../symbolfinder.h"
|
#include "../symbolfinder.h"
|
||||||
#include "bringidentifierintoscope.h"
|
#include "bringidentifierintoscope.h"
|
||||||
@@ -24,6 +23,7 @@
|
|||||||
#include "convertstringliteral.h"
|
#include "convertstringliteral.h"
|
||||||
#include "createdeclarationfromuse.h"
|
#include "createdeclarationfromuse.h"
|
||||||
#include "extractfunction.h"
|
#include "extractfunction.h"
|
||||||
|
#include "extractliteralasparameter.h"
|
||||||
#include "insertfunctiondefinition.h"
|
#include "insertfunctiondefinition.h"
|
||||||
#include "logicaloperationquickfixes.h"
|
#include "logicaloperationquickfixes.h"
|
||||||
#include "moveclasstoownfile.h"
|
#include "moveclasstoownfile.h"
|
||||||
@@ -870,321 +870,6 @@ void CompleteSwitchCaseStatement::doMatch(const CppQuickFixInterface &interface,
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
struct ReplaceLiteralsResult
|
|
||||||
{
|
|
||||||
Token token;
|
|
||||||
QString literalText;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
class ReplaceLiterals : private ASTVisitor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ReplaceLiterals(const CppRefactoringFilePtr &file, ChangeSet *changes, T *literal)
|
|
||||||
: ASTVisitor(file->cppDocument()->translationUnit()), m_file(file), m_changes(changes),
|
|
||||||
m_literal(literal)
|
|
||||||
{
|
|
||||||
m_result.token = m_file->tokenAt(literal->firstToken());
|
|
||||||
m_literalTokenText = m_result.token.spell();
|
|
||||||
m_result.literalText = QLatin1String(m_literalTokenText);
|
|
||||||
if (m_result.token.isCharLiteral()) {
|
|
||||||
m_result.literalText.prepend(QLatin1Char('\''));
|
|
||||||
m_result.literalText.append(QLatin1Char('\''));
|
|
||||||
if (m_result.token.kind() == T_WIDE_CHAR_LITERAL)
|
|
||||||
m_result.literalText.prepend(QLatin1Char('L'));
|
|
||||||
else if (m_result.token.kind() == T_UTF16_CHAR_LITERAL)
|
|
||||||
m_result.literalText.prepend(QLatin1Char('u'));
|
|
||||||
else if (m_result.token.kind() == T_UTF32_CHAR_LITERAL)
|
|
||||||
m_result.literalText.prepend(QLatin1Char('U'));
|
|
||||||
} else if (m_result.token.isStringLiteral()) {
|
|
||||||
m_result.literalText.prepend(QLatin1Char('"'));
|
|
||||||
m_result.literalText.append(QLatin1Char('"'));
|
|
||||||
if (m_result.token.kind() == T_WIDE_STRING_LITERAL)
|
|
||||||
m_result.literalText.prepend(QLatin1Char('L'));
|
|
||||||
else if (m_result.token.kind() == T_UTF16_STRING_LITERAL)
|
|
||||||
m_result.literalText.prepend(QLatin1Char('u'));
|
|
||||||
else if (m_result.token.kind() == T_UTF32_STRING_LITERAL)
|
|
||||||
m_result.literalText.prepend(QLatin1Char('U'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ReplaceLiteralsResult apply(AST *ast)
|
|
||||||
{
|
|
||||||
ast->accept(this);
|
|
||||||
return m_result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool visit(T *ast) override
|
|
||||||
{
|
|
||||||
if (ast != m_literal
|
|
||||||
&& strcmp(m_file->tokenAt(ast->firstToken()).spell(), m_literalTokenText) != 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
int start, end;
|
|
||||||
m_file->startAndEndOf(ast->firstToken(), &start, &end);
|
|
||||||
m_changes->replace(start, end, QLatin1String("newParameter"));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const CppRefactoringFilePtr &m_file;
|
|
||||||
ChangeSet *m_changes;
|
|
||||||
T *m_literal;
|
|
||||||
const char *m_literalTokenText;
|
|
||||||
ReplaceLiteralsResult m_result;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ExtractLiteralAsParameterOp : public CppQuickFixOperation
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ExtractLiteralAsParameterOp(const CppQuickFixInterface &interface, int priority,
|
|
||||||
ExpressionAST *literal, FunctionDefinitionAST *function)
|
|
||||||
: CppQuickFixOperation(interface, priority),
|
|
||||||
m_literal(literal),
|
|
||||||
m_functionDefinition(function)
|
|
||||||
{
|
|
||||||
setDescription(Tr::tr("Extract Constant as Function Parameter"));
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FoundDeclaration
|
|
||||||
{
|
|
||||||
FunctionDeclaratorAST *ast = nullptr;
|
|
||||||
CppRefactoringFilePtr file;
|
|
||||||
};
|
|
||||||
|
|
||||||
FoundDeclaration findDeclaration(const CppRefactoringChanges &refactoring,
|
|
||||||
FunctionDefinitionAST *ast)
|
|
||||||
{
|
|
||||||
FoundDeclaration result;
|
|
||||||
Function *func = ast->symbol;
|
|
||||||
if (Class *matchingClass = isMemberFunction(context(), func)) {
|
|
||||||
// Dealing with member functions
|
|
||||||
const QualifiedNameId *qName = func->name()->asQualifiedNameId();
|
|
||||||
for (Symbol *s = matchingClass->find(qName->identifier()); s; s = s->next()) {
|
|
||||||
if (!s->name()
|
|
||||||
|| !qName->identifier()->match(s->identifier())
|
|
||||||
|| !s->type()->asFunctionType()
|
|
||||||
|| !s->type().match(func->type())
|
|
||||||
|| s->asFunction()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const FilePath declFilePath = matchingClass->filePath();
|
|
||||||
result.file = refactoring.cppFile(declFilePath);
|
|
||||||
ASTPath astPath(result.file->cppDocument());
|
|
||||||
const QList<AST *> path = astPath(s->line(), s->column());
|
|
||||||
SimpleDeclarationAST *simpleDecl = nullptr;
|
|
||||||
for (AST *node : path) {
|
|
||||||
simpleDecl = node->asSimpleDeclaration();
|
|
||||||
if (simpleDecl) {
|
|
||||||
if (simpleDecl->symbols && !simpleDecl->symbols->next) {
|
|
||||||
result.ast = functionDeclarator(simpleDecl);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (simpleDecl)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (Namespace *matchingNamespace = isNamespaceFunction(context(), func)) {
|
|
||||||
// Dealing with free functions and inline member functions.
|
|
||||||
bool isHeaderFile;
|
|
||||||
FilePath declFilePath = correspondingHeaderOrSource(filePath(), &isHeaderFile);
|
|
||||||
if (!declFilePath.exists())
|
|
||||||
return FoundDeclaration();
|
|
||||||
result.file = refactoring.cppFile(declFilePath);
|
|
||||||
if (!result.file)
|
|
||||||
return FoundDeclaration();
|
|
||||||
const LookupContext lc(result.file->cppDocument(), snapshot());
|
|
||||||
const QList<LookupItem> candidates = lc.lookup(func->name(), matchingNamespace);
|
|
||||||
for (const LookupItem &candidate : candidates) {
|
|
||||||
if (Symbol *s = candidate.declaration()) {
|
|
||||||
if (s->asDeclaration()) {
|
|
||||||
ASTPath astPath(result.file->cppDocument());
|
|
||||||
const QList<AST *> path = astPath(s->line(), s->column());
|
|
||||||
for (AST *node : path) {
|
|
||||||
SimpleDeclarationAST *simpleDecl = node->asSimpleDeclaration();
|
|
||||||
if (simpleDecl) {
|
|
||||||
result.ast = functionDeclarator(simpleDecl);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void perform() override
|
|
||||||
{
|
|
||||||
FunctionDeclaratorAST *functionDeclaratorOfDefinition
|
|
||||||
= functionDeclarator(m_functionDefinition);
|
|
||||||
const CppRefactoringChanges refactoring(snapshot());
|
|
||||||
const CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
|
|
||||||
deduceTypeNameOfLiteral(currentFile->cppDocument());
|
|
||||||
|
|
||||||
ChangeSet changes;
|
|
||||||
if (NumericLiteralAST *concreteLiteral = m_literal->asNumericLiteral()) {
|
|
||||||
m_literalInfo = ReplaceLiterals<NumericLiteralAST>(currentFile, &changes,
|
|
||||||
concreteLiteral)
|
|
||||||
.apply(m_functionDefinition->function_body);
|
|
||||||
} else if (StringLiteralAST *concreteLiteral = m_literal->asStringLiteral()) {
|
|
||||||
m_literalInfo = ReplaceLiterals<StringLiteralAST>(currentFile, &changes,
|
|
||||||
concreteLiteral)
|
|
||||||
.apply(m_functionDefinition->function_body);
|
|
||||||
} else if (BoolLiteralAST *concreteLiteral = m_literal->asBoolLiteral()) {
|
|
||||||
m_literalInfo = ReplaceLiterals<BoolLiteralAST>(currentFile, &changes,
|
|
||||||
concreteLiteral)
|
|
||||||
.apply(m_functionDefinition->function_body);
|
|
||||||
}
|
|
||||||
const FoundDeclaration functionDeclaration
|
|
||||||
= findDeclaration(refactoring, m_functionDefinition);
|
|
||||||
appendFunctionParameter(functionDeclaratorOfDefinition, currentFile, &changes,
|
|
||||||
!functionDeclaration.ast);
|
|
||||||
if (functionDeclaration.ast) {
|
|
||||||
if (currentFile->filePath() != functionDeclaration.file->filePath()) {
|
|
||||||
ChangeSet declChanges;
|
|
||||||
appendFunctionParameter(functionDeclaration.ast, functionDeclaration.file, &declChanges,
|
|
||||||
true);
|
|
||||||
functionDeclaration.file->setChangeSet(declChanges);
|
|
||||||
functionDeclaration.file->apply();
|
|
||||||
} else {
|
|
||||||
appendFunctionParameter(functionDeclaration.ast, currentFile, &changes,
|
|
||||||
true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
currentFile->setChangeSet(changes);
|
|
||||||
currentFile->apply();
|
|
||||||
QTextCursor c = currentFile->cursor();
|
|
||||||
c.setPosition(c.position() - parameterName().length());
|
|
||||||
editor()->setTextCursor(c);
|
|
||||||
editor()->renameSymbolUnderCursor();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool hasParameters(FunctionDeclaratorAST *ast) const
|
|
||||||
{
|
|
||||||
return ast->parameter_declaration_clause
|
|
||||||
&& ast->parameter_declaration_clause->parameter_declaration_list
|
|
||||||
&& ast->parameter_declaration_clause->parameter_declaration_list->value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void deduceTypeNameOfLiteral(const Document::Ptr &document)
|
|
||||||
{
|
|
||||||
TypeOfExpression typeOfExpression;
|
|
||||||
typeOfExpression.init(document, snapshot());
|
|
||||||
Overview overview;
|
|
||||||
Scope *scope = m_functionDefinition->symbol->enclosingScope();
|
|
||||||
const QList<LookupItem> items = typeOfExpression(m_literal, document, scope);
|
|
||||||
if (!items.isEmpty())
|
|
||||||
m_typeName = overview.prettyType(items.first().type());
|
|
||||||
}
|
|
||||||
|
|
||||||
static QString parameterName() { return QLatin1String("newParameter"); }
|
|
||||||
|
|
||||||
QString parameterDeclarationTextToInsert(FunctionDeclaratorAST *ast) const
|
|
||||||
{
|
|
||||||
QString str;
|
|
||||||
if (hasParameters(ast))
|
|
||||||
str = QLatin1String(", ");
|
|
||||||
str += m_typeName;
|
|
||||||
if (!m_typeName.endsWith(QLatin1Char('*')))
|
|
||||||
str += QLatin1Char(' ');
|
|
||||||
str += parameterName();
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
FunctionDeclaratorAST *functionDeclarator(SimpleDeclarationAST *ast) const
|
|
||||||
{
|
|
||||||
for (DeclaratorListAST *decls = ast->declarator_list; decls; decls = decls->next) {
|
|
||||||
FunctionDeclaratorAST * const functionDeclaratorAST = functionDeclarator(decls->value);
|
|
||||||
if (functionDeclaratorAST)
|
|
||||||
return functionDeclaratorAST;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
FunctionDeclaratorAST *functionDeclarator(DeclaratorAST *ast) const
|
|
||||||
{
|
|
||||||
for (PostfixDeclaratorListAST *pds = ast->postfix_declarator_list; pds; pds = pds->next) {
|
|
||||||
FunctionDeclaratorAST *funcdecl = pds->value->asFunctionDeclarator();
|
|
||||||
if (funcdecl)
|
|
||||||
return funcdecl;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
FunctionDeclaratorAST *functionDeclarator(FunctionDefinitionAST *ast) const
|
|
||||||
{
|
|
||||||
return functionDeclarator(ast->declarator);
|
|
||||||
}
|
|
||||||
|
|
||||||
void appendFunctionParameter(FunctionDeclaratorAST *ast, const CppRefactoringFileConstPtr &file,
|
|
||||||
ChangeSet *changes, bool addDefaultValue)
|
|
||||||
{
|
|
||||||
if (!ast)
|
|
||||||
return;
|
|
||||||
if (m_declarationInsertionString.isEmpty())
|
|
||||||
m_declarationInsertionString = parameterDeclarationTextToInsert(ast);
|
|
||||||
QString insertion = m_declarationInsertionString;
|
|
||||||
if (addDefaultValue)
|
|
||||||
insertion += QLatin1String(" = ") + m_literalInfo.literalText;
|
|
||||||
changes->insert(file->startOf(ast->rparen_token), insertion);
|
|
||||||
}
|
|
||||||
|
|
||||||
ExpressionAST *m_literal;
|
|
||||||
FunctionDefinitionAST *m_functionDefinition;
|
|
||||||
QString m_typeName;
|
|
||||||
QString m_declarationInsertionString;
|
|
||||||
ReplaceLiteralsResult m_literalInfo;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // anonymous namespace
|
|
||||||
|
|
||||||
void ExtractLiteralAsParameter::doMatch(const CppQuickFixInterface &interface,
|
|
||||||
QuickFixOperations &result)
|
|
||||||
{
|
|
||||||
const QList<AST *> &path = interface.path();
|
|
||||||
if (path.count() < 2)
|
|
||||||
return;
|
|
||||||
|
|
||||||
AST * const lastAst = path.last();
|
|
||||||
ExpressionAST *literal;
|
|
||||||
if (!((literal = lastAst->asNumericLiteral())
|
|
||||||
|| (literal = lastAst->asStringLiteral())
|
|
||||||
|| (literal = lastAst->asBoolLiteral()))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
FunctionDefinitionAST *function;
|
|
||||||
int i = path.count() - 2;
|
|
||||||
while (!(function = path.at(i)->asFunctionDefinition())) {
|
|
||||||
// Ignore literals in lambda expressions for now.
|
|
||||||
if (path.at(i)->asLambdaExpression())
|
|
||||||
return;
|
|
||||||
if (--i < 0)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
PostfixDeclaratorListAST * const declaratorList = function->declarator->postfix_declarator_list;
|
|
||||||
if (!declaratorList)
|
|
||||||
return;
|
|
||||||
if (FunctionDeclaratorAST *declarator = declaratorList->value->asFunctionDeclarator()) {
|
|
||||||
if (declarator->parameter_declaration_clause
|
|
||||||
&& declarator->parameter_declaration_clause->dot_dot_dot_token) {
|
|
||||||
// Do not handle functions with ellipsis parameter.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const int priority = path.size() - 1;
|
|
||||||
result << new ExtractLiteralAsParameterOp(interface, priority, literal, function);
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
class ConvertFromAndToPointerOp : public CppQuickFixOperation
|
class ConvertFromAndToPointerOp : public CppQuickFixOperation
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -1961,7 +1646,6 @@ void createCppQuickFixes()
|
|||||||
|
|
||||||
new ApplyDeclDefLinkChanges;
|
new ApplyDeclDefLinkChanges;
|
||||||
new ConvertFromAndToPointer;
|
new ConvertFromAndToPointer;
|
||||||
new ExtractLiteralAsParameter;
|
|
||||||
|
|
||||||
new AssignToLocalVariable;
|
new AssignToLocalVariable;
|
||||||
|
|
||||||
@@ -1979,6 +1663,7 @@ void createCppQuickFixes()
|
|||||||
registerRewriteControlStatementQuickfixes();
|
registerRewriteControlStatementQuickfixes();
|
||||||
registerRewriteCommentQuickfixes();
|
registerRewriteCommentQuickfixes();
|
||||||
registerExtractFunctionQuickfix();
|
registerExtractFunctionQuickfix();
|
||||||
|
registerExtractLiteralAsParameterQuickfix();
|
||||||
|
|
||||||
new ExtraRefactoringOperations;
|
new ExtraRefactoringOperations;
|
||||||
|
|
||||||
|
@@ -125,17 +125,6 @@ private:
|
|||||||
void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
|
void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
|
||||||
Extracts the selected constant and converts it to a parameter of the current function.
|
|
||||||
|
|
||||||
Activates on numeric, bool, character, or string literal in the function body.
|
|
||||||
*/
|
|
||||||
class ExtractLiteralAsParameter : public CppQuickFixFactory
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Converts the selected variable to a pointer if it is a stack variable or reference, or vice versa.
|
Converts the selected variable to a pointer if it is a stack variable or reference, or vice versa.
|
||||||
|
|
||||||
|
563
src/plugins/cppeditor/quickfixes/extractliteralasparameter.cpp
Normal file
563
src/plugins/cppeditor/quickfixes/extractliteralasparameter.cpp
Normal file
@@ -0,0 +1,563 @@
|
|||||||
|
// Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#include "extractliteralasparameter.h"
|
||||||
|
|
||||||
|
#include "../cppeditortr.h"
|
||||||
|
#include "../cppeditorwidget.h"
|
||||||
|
#include "../cpprefactoringchanges.h"
|
||||||
|
#include "cppquickfix.h"
|
||||||
|
#include "cppquickfixhelpers.h"
|
||||||
|
|
||||||
|
#include <cplusplus/ASTPath.h>
|
||||||
|
#include <cplusplus/Overview.h>
|
||||||
|
#include <cplusplus/TypeOfExpression.h>
|
||||||
|
|
||||||
|
#ifdef WITH_TESTS
|
||||||
|
#include "cppquickfix_test.h"
|
||||||
|
#include <QtTest>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace CPlusPlus;
|
||||||
|
using namespace Utils;
|
||||||
|
|
||||||
|
namespace CppEditor::Internal {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct ReplaceLiteralsResult
|
||||||
|
{
|
||||||
|
Token token;
|
||||||
|
QString literalText;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class ReplaceLiterals : private ASTVisitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ReplaceLiterals(const CppRefactoringFilePtr &file, ChangeSet *changes, T *literal)
|
||||||
|
: ASTVisitor(file->cppDocument()->translationUnit()), m_file(file), m_changes(changes),
|
||||||
|
m_literal(literal)
|
||||||
|
{
|
||||||
|
m_result.token = m_file->tokenAt(literal->firstToken());
|
||||||
|
m_literalTokenText = m_result.token.spell();
|
||||||
|
m_result.literalText = QLatin1String(m_literalTokenText);
|
||||||
|
if (m_result.token.isCharLiteral()) {
|
||||||
|
m_result.literalText.prepend(QLatin1Char('\''));
|
||||||
|
m_result.literalText.append(QLatin1Char('\''));
|
||||||
|
if (m_result.token.kind() == T_WIDE_CHAR_LITERAL)
|
||||||
|
m_result.literalText.prepend(QLatin1Char('L'));
|
||||||
|
else if (m_result.token.kind() == T_UTF16_CHAR_LITERAL)
|
||||||
|
m_result.literalText.prepend(QLatin1Char('u'));
|
||||||
|
else if (m_result.token.kind() == T_UTF32_CHAR_LITERAL)
|
||||||
|
m_result.literalText.prepend(QLatin1Char('U'));
|
||||||
|
} else if (m_result.token.isStringLiteral()) {
|
||||||
|
m_result.literalText.prepend(QLatin1Char('"'));
|
||||||
|
m_result.literalText.append(QLatin1Char('"'));
|
||||||
|
if (m_result.token.kind() == T_WIDE_STRING_LITERAL)
|
||||||
|
m_result.literalText.prepend(QLatin1Char('L'));
|
||||||
|
else if (m_result.token.kind() == T_UTF16_STRING_LITERAL)
|
||||||
|
m_result.literalText.prepend(QLatin1Char('u'));
|
||||||
|
else if (m_result.token.kind() == T_UTF32_STRING_LITERAL)
|
||||||
|
m_result.literalText.prepend(QLatin1Char('U'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReplaceLiteralsResult apply(AST *ast)
|
||||||
|
{
|
||||||
|
ast->accept(this);
|
||||||
|
return m_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool visit(T *ast) override
|
||||||
|
{
|
||||||
|
if (ast != m_literal
|
||||||
|
&& strcmp(m_file->tokenAt(ast->firstToken()).spell(), m_literalTokenText) != 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
int start, end;
|
||||||
|
m_file->startAndEndOf(ast->firstToken(), &start, &end);
|
||||||
|
m_changes->replace(start, end, QLatin1String("newParameter"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CppRefactoringFilePtr &m_file;
|
||||||
|
ChangeSet *m_changes;
|
||||||
|
T *m_literal;
|
||||||
|
const char *m_literalTokenText;
|
||||||
|
ReplaceLiteralsResult m_result;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ExtractLiteralAsParameterOp : public CppQuickFixOperation
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ExtractLiteralAsParameterOp(const CppQuickFixInterface &interface, int priority,
|
||||||
|
ExpressionAST *literal, FunctionDefinitionAST *function)
|
||||||
|
: CppQuickFixOperation(interface, priority),
|
||||||
|
m_literal(literal),
|
||||||
|
m_functionDefinition(function)
|
||||||
|
{
|
||||||
|
setDescription(Tr::tr("Extract Constant as Function Parameter"));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FoundDeclaration
|
||||||
|
{
|
||||||
|
FunctionDeclaratorAST *ast = nullptr;
|
||||||
|
CppRefactoringFilePtr file;
|
||||||
|
};
|
||||||
|
|
||||||
|
FoundDeclaration findDeclaration(const CppRefactoringChanges &refactoring,
|
||||||
|
FunctionDefinitionAST *ast)
|
||||||
|
{
|
||||||
|
FoundDeclaration result;
|
||||||
|
Function *func = ast->symbol;
|
||||||
|
if (Class *matchingClass = isMemberFunction(context(), func)) {
|
||||||
|
// Dealing with member functions
|
||||||
|
const QualifiedNameId *qName = func->name()->asQualifiedNameId();
|
||||||
|
for (Symbol *s = matchingClass->find(qName->identifier()); s; s = s->next()) {
|
||||||
|
if (!s->name()
|
||||||
|
|| !qName->identifier()->match(s->identifier())
|
||||||
|
|| !s->type()->asFunctionType()
|
||||||
|
|| !s->type().match(func->type())
|
||||||
|
|| s->asFunction()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FilePath declFilePath = matchingClass->filePath();
|
||||||
|
result.file = refactoring.cppFile(declFilePath);
|
||||||
|
ASTPath astPath(result.file->cppDocument());
|
||||||
|
const QList<AST *> path = astPath(s->line(), s->column());
|
||||||
|
SimpleDeclarationAST *simpleDecl = nullptr;
|
||||||
|
for (AST *node : path) {
|
||||||
|
simpleDecl = node->asSimpleDeclaration();
|
||||||
|
if (simpleDecl) {
|
||||||
|
if (simpleDecl->symbols && !simpleDecl->symbols->next) {
|
||||||
|
result.ast = functionDeclarator(simpleDecl);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (simpleDecl)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (Namespace *matchingNamespace = isNamespaceFunction(context(), func)) {
|
||||||
|
// Dealing with free functions and inline member functions.
|
||||||
|
bool isHeaderFile;
|
||||||
|
FilePath declFilePath = correspondingHeaderOrSource(filePath(), &isHeaderFile);
|
||||||
|
if (!declFilePath.exists())
|
||||||
|
return FoundDeclaration();
|
||||||
|
result.file = refactoring.cppFile(declFilePath);
|
||||||
|
if (!result.file)
|
||||||
|
return FoundDeclaration();
|
||||||
|
const LookupContext lc(result.file->cppDocument(), snapshot());
|
||||||
|
const QList<LookupItem> candidates = lc.lookup(func->name(), matchingNamespace);
|
||||||
|
for (const LookupItem &candidate : candidates) {
|
||||||
|
if (Symbol *s = candidate.declaration()) {
|
||||||
|
if (s->asDeclaration()) {
|
||||||
|
ASTPath astPath(result.file->cppDocument());
|
||||||
|
const QList<AST *> path = astPath(s->line(), s->column());
|
||||||
|
for (AST *node : path) {
|
||||||
|
SimpleDeclarationAST *simpleDecl = node->asSimpleDeclaration();
|
||||||
|
if (simpleDecl) {
|
||||||
|
result.ast = functionDeclarator(simpleDecl);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void perform() override
|
||||||
|
{
|
||||||
|
FunctionDeclaratorAST *functionDeclaratorOfDefinition
|
||||||
|
= functionDeclarator(m_functionDefinition);
|
||||||
|
const CppRefactoringChanges refactoring(snapshot());
|
||||||
|
const CppRefactoringFilePtr currentFile = refactoring.cppFile(filePath());
|
||||||
|
deduceTypeNameOfLiteral(currentFile->cppDocument());
|
||||||
|
|
||||||
|
ChangeSet changes;
|
||||||
|
if (NumericLiteralAST *concreteLiteral = m_literal->asNumericLiteral()) {
|
||||||
|
m_literalInfo = ReplaceLiterals<NumericLiteralAST>(currentFile, &changes,
|
||||||
|
concreteLiteral)
|
||||||
|
.apply(m_functionDefinition->function_body);
|
||||||
|
} else if (StringLiteralAST *concreteLiteral = m_literal->asStringLiteral()) {
|
||||||
|
m_literalInfo = ReplaceLiterals<StringLiteralAST>(currentFile, &changes,
|
||||||
|
concreteLiteral)
|
||||||
|
.apply(m_functionDefinition->function_body);
|
||||||
|
} else if (BoolLiteralAST *concreteLiteral = m_literal->asBoolLiteral()) {
|
||||||
|
m_literalInfo = ReplaceLiterals<BoolLiteralAST>(currentFile, &changes,
|
||||||
|
concreteLiteral)
|
||||||
|
.apply(m_functionDefinition->function_body);
|
||||||
|
}
|
||||||
|
const FoundDeclaration functionDeclaration
|
||||||
|
= findDeclaration(refactoring, m_functionDefinition);
|
||||||
|
appendFunctionParameter(functionDeclaratorOfDefinition, currentFile, &changes,
|
||||||
|
!functionDeclaration.ast);
|
||||||
|
if (functionDeclaration.ast) {
|
||||||
|
if (currentFile->filePath() != functionDeclaration.file->filePath()) {
|
||||||
|
ChangeSet declChanges;
|
||||||
|
appendFunctionParameter(functionDeclaration.ast, functionDeclaration.file, &declChanges,
|
||||||
|
true);
|
||||||
|
functionDeclaration.file->setChangeSet(declChanges);
|
||||||
|
functionDeclaration.file->apply();
|
||||||
|
} else {
|
||||||
|
appendFunctionParameter(functionDeclaration.ast, currentFile, &changes,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentFile->setChangeSet(changes);
|
||||||
|
currentFile->apply();
|
||||||
|
QTextCursor c = currentFile->cursor();
|
||||||
|
c.setPosition(c.position() - parameterName().length());
|
||||||
|
editor()->setTextCursor(c);
|
||||||
|
editor()->renameSymbolUnderCursor();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool hasParameters(FunctionDeclaratorAST *ast) const
|
||||||
|
{
|
||||||
|
return ast->parameter_declaration_clause
|
||||||
|
&& ast->parameter_declaration_clause->parameter_declaration_list
|
||||||
|
&& ast->parameter_declaration_clause->parameter_declaration_list->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void deduceTypeNameOfLiteral(const Document::Ptr &document)
|
||||||
|
{
|
||||||
|
TypeOfExpression typeOfExpression;
|
||||||
|
typeOfExpression.init(document, snapshot());
|
||||||
|
Overview overview;
|
||||||
|
Scope *scope = m_functionDefinition->symbol->enclosingScope();
|
||||||
|
const QList<LookupItem> items = typeOfExpression(m_literal, document, scope);
|
||||||
|
if (!items.isEmpty())
|
||||||
|
m_typeName = overview.prettyType(items.first().type());
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString parameterName() { return QLatin1String("newParameter"); }
|
||||||
|
|
||||||
|
QString parameterDeclarationTextToInsert(FunctionDeclaratorAST *ast) const
|
||||||
|
{
|
||||||
|
QString str;
|
||||||
|
if (hasParameters(ast))
|
||||||
|
str = QLatin1String(", ");
|
||||||
|
str += m_typeName;
|
||||||
|
if (!m_typeName.endsWith(QLatin1Char('*')))
|
||||||
|
str += QLatin1Char(' ');
|
||||||
|
str += parameterName();
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionDeclaratorAST *functionDeclarator(SimpleDeclarationAST *ast) const
|
||||||
|
{
|
||||||
|
for (DeclaratorListAST *decls = ast->declarator_list; decls; decls = decls->next) {
|
||||||
|
FunctionDeclaratorAST * const functionDeclaratorAST = functionDeclarator(decls->value);
|
||||||
|
if (functionDeclaratorAST)
|
||||||
|
return functionDeclaratorAST;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionDeclaratorAST *functionDeclarator(DeclaratorAST *ast) const
|
||||||
|
{
|
||||||
|
for (PostfixDeclaratorListAST *pds = ast->postfix_declarator_list; pds; pds = pds->next) {
|
||||||
|
FunctionDeclaratorAST *funcdecl = pds->value->asFunctionDeclarator();
|
||||||
|
if (funcdecl)
|
||||||
|
return funcdecl;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionDeclaratorAST *functionDeclarator(FunctionDefinitionAST *ast) const
|
||||||
|
{
|
||||||
|
return functionDeclarator(ast->declarator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void appendFunctionParameter(FunctionDeclaratorAST *ast, const CppRefactoringFileConstPtr &file,
|
||||||
|
ChangeSet *changes, bool addDefaultValue)
|
||||||
|
{
|
||||||
|
if (!ast)
|
||||||
|
return;
|
||||||
|
if (m_declarationInsertionString.isEmpty())
|
||||||
|
m_declarationInsertionString = parameterDeclarationTextToInsert(ast);
|
||||||
|
QString insertion = m_declarationInsertionString;
|
||||||
|
if (addDefaultValue)
|
||||||
|
insertion += QLatin1String(" = ") + m_literalInfo.literalText;
|
||||||
|
changes->insert(file->startOf(ast->rparen_token), insertion);
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpressionAST *m_literal;
|
||||||
|
FunctionDefinitionAST *m_functionDefinition;
|
||||||
|
QString m_typeName;
|
||||||
|
QString m_declarationInsertionString;
|
||||||
|
ReplaceLiteralsResult m_literalInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Extracts the selected constant and converts it to a parameter of the current function.
|
||||||
|
Activates on numeric, bool, character, or string literal in the function body.
|
||||||
|
*/
|
||||||
|
class ExtractLiteralAsParameter : public CppQuickFixFactory
|
||||||
|
{
|
||||||
|
#ifdef WITH_TESTS
|
||||||
|
public:
|
||||||
|
static QObject *createTest();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
void doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result) override
|
||||||
|
{
|
||||||
|
const QList<AST *> &path = interface.path();
|
||||||
|
if (path.count() < 2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
AST * const lastAst = path.last();
|
||||||
|
ExpressionAST *literal;
|
||||||
|
if (!((literal = lastAst->asNumericLiteral())
|
||||||
|
|| (literal = lastAst->asStringLiteral())
|
||||||
|
|| (literal = lastAst->asBoolLiteral()))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionDefinitionAST *function;
|
||||||
|
int i = path.count() - 2;
|
||||||
|
while (!(function = path.at(i)->asFunctionDefinition())) {
|
||||||
|
// Ignore literals in lambda expressions for now.
|
||||||
|
if (path.at(i)->asLambdaExpression())
|
||||||
|
return;
|
||||||
|
if (--i < 0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PostfixDeclaratorListAST * const declaratorList = function->declarator->postfix_declarator_list;
|
||||||
|
if (!declaratorList)
|
||||||
|
return;
|
||||||
|
if (FunctionDeclaratorAST *declarator = declaratorList->value->asFunctionDeclarator()) {
|
||||||
|
if (declarator->parameter_declaration_clause
|
||||||
|
&& declarator->parameter_declaration_clause->dot_dot_dot_token) {
|
||||||
|
// Do not handle functions with ellipsis parameter.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const int priority = path.size() - 1;
|
||||||
|
result << new ExtractLiteralAsParameterOp(interface, priority, literal, function);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef WITH_TESTS
|
||||||
|
using namespace Tests;
|
||||||
|
|
||||||
|
class ExtractLiteralAsParameterTest : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void testTypeDeduction_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QByteArray>("typeString");
|
||||||
|
QTest::addColumn<QByteArray>("literal");
|
||||||
|
QTest::newRow("int")
|
||||||
|
<< QByteArray("int ") << QByteArray("156");
|
||||||
|
QTest::newRow("unsigned int")
|
||||||
|
<< QByteArray("unsigned int ") << QByteArray("156u");
|
||||||
|
QTest::newRow("long")
|
||||||
|
<< QByteArray("long ") << QByteArray("156l");
|
||||||
|
QTest::newRow("unsigned long")
|
||||||
|
<< QByteArray("unsigned long ") << QByteArray("156ul");
|
||||||
|
QTest::newRow("long long")
|
||||||
|
<< QByteArray("long long ") << QByteArray("156ll");
|
||||||
|
QTest::newRow("unsigned long long")
|
||||||
|
<< QByteArray("unsigned long long ") << QByteArray("156ull");
|
||||||
|
QTest::newRow("float")
|
||||||
|
<< QByteArray("float ") << QByteArray("3.14159f");
|
||||||
|
QTest::newRow("double")
|
||||||
|
<< QByteArray("double ") << QByteArray("3.14159");
|
||||||
|
QTest::newRow("long double")
|
||||||
|
<< QByteArray("long double ") << QByteArray("3.14159L");
|
||||||
|
QTest::newRow("bool")
|
||||||
|
<< QByteArray("bool ") << QByteArray("true");
|
||||||
|
QTest::newRow("bool")
|
||||||
|
<< QByteArray("bool ") << QByteArray("false");
|
||||||
|
QTest::newRow("char")
|
||||||
|
<< QByteArray("char ") << QByteArray("'X'");
|
||||||
|
QTest::newRow("wchar_t")
|
||||||
|
<< QByteArray("wchar_t ") << QByteArray("L'X'");
|
||||||
|
QTest::newRow("char16_t")
|
||||||
|
<< QByteArray("char16_t ") << QByteArray("u'X'");
|
||||||
|
QTest::newRow("char32_t")
|
||||||
|
<< QByteArray("char32_t ") << QByteArray("U'X'");
|
||||||
|
QTest::newRow("const char *")
|
||||||
|
<< QByteArray("const char *") << QByteArray("\"narf\"");
|
||||||
|
QTest::newRow("const wchar_t *")
|
||||||
|
<< QByteArray("const wchar_t *") << QByteArray("L\"narf\"");
|
||||||
|
QTest::newRow("const char16_t *")
|
||||||
|
<< QByteArray("const char16_t *") << QByteArray("u\"narf\"");
|
||||||
|
QTest::newRow("const char32_t *")
|
||||||
|
<< QByteArray("const char32_t *") << QByteArray("U\"narf\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
void testTypeDeduction()
|
||||||
|
{
|
||||||
|
QFETCH(QByteArray, typeString);
|
||||||
|
QFETCH(QByteArray, literal);
|
||||||
|
const QByteArray original = QByteArray("void foo() {return @") + literal + QByteArray(";}\n");
|
||||||
|
const QByteArray expected = QByteArray("void foo(") + typeString + QByteArray("newParameter = ")
|
||||||
|
+ literal + QByteArray(") {return newParameter;}\n");
|
||||||
|
|
||||||
|
if (literal == "3.14159") {
|
||||||
|
qWarning("Literal 3.14159 is wrongly reported as int. Skipping.");
|
||||||
|
return;
|
||||||
|
} else if (literal == "3.14159L") {
|
||||||
|
qWarning("Literal 3.14159L is wrongly reported as long. Skipping.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExtractLiteralAsParameter factory;
|
||||||
|
QuickFixOperationTest(singleDocument(original, expected), &factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testFreeFunctionSeparateFiles()
|
||||||
|
{
|
||||||
|
QList<TestDocumentPtr> testDocuments;
|
||||||
|
QByteArray original;
|
||||||
|
QByteArray expected;
|
||||||
|
|
||||||
|
// Header File
|
||||||
|
original =
|
||||||
|
"void foo(const char *a, long b = 1);\n";
|
||||||
|
expected =
|
||||||
|
"void foo(const char *a, long b = 1, int newParameter = 156);\n";
|
||||||
|
testDocuments << CppTestDocument::create("file.h", original, expected);
|
||||||
|
|
||||||
|
// Source File
|
||||||
|
original =
|
||||||
|
"void foo(const char *a, long b)\n"
|
||||||
|
"{return 1@56 + 123 + 156;}\n";
|
||||||
|
expected =
|
||||||
|
"void foo(const char *a, long b, int newParameter)\n"
|
||||||
|
"{return newParameter + 123 + newParameter;}\n";
|
||||||
|
testDocuments << CppTestDocument::create("file.cpp", original, expected);
|
||||||
|
|
||||||
|
ExtractLiteralAsParameter factory;
|
||||||
|
QuickFixOperationTest(testDocuments, &factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testMemberFunctionSeparateFiles()
|
||||||
|
{
|
||||||
|
QList<TestDocumentPtr> testDocuments;
|
||||||
|
QByteArray original;
|
||||||
|
QByteArray expected;
|
||||||
|
|
||||||
|
// Header File
|
||||||
|
original =
|
||||||
|
"class Narf {\n"
|
||||||
|
"public:\n"
|
||||||
|
" int zort();\n"
|
||||||
|
"};\n";
|
||||||
|
expected =
|
||||||
|
"class Narf {\n"
|
||||||
|
"public:\n"
|
||||||
|
" int zort(int newParameter = 155);\n"
|
||||||
|
"};\n";
|
||||||
|
testDocuments << CppTestDocument::create("file.h", original, expected);
|
||||||
|
|
||||||
|
// Source File
|
||||||
|
original =
|
||||||
|
"#include \"file.h\"\n\n"
|
||||||
|
"int Narf::zort()\n"
|
||||||
|
"{ return 15@5 + 1; }\n";
|
||||||
|
expected =
|
||||||
|
"#include \"file.h\"\n\n"
|
||||||
|
"int Narf::zort(int newParameter)\n"
|
||||||
|
"{ return newParameter + 1; }\n";
|
||||||
|
testDocuments << CppTestDocument::create("file.cpp", original, expected);
|
||||||
|
|
||||||
|
ExtractLiteralAsParameter factory;
|
||||||
|
QuickFixOperationTest(testDocuments, &factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testNotTriggeringForInvalidCode()
|
||||||
|
{
|
||||||
|
QList<TestDocumentPtr> testDocuments;
|
||||||
|
QByteArray original;
|
||||||
|
original =
|
||||||
|
"T(\"test\")\n"
|
||||||
|
"{\n"
|
||||||
|
" const int i = @14;\n"
|
||||||
|
"}\n";
|
||||||
|
testDocuments << CppTestDocument::create("file.cpp", original, "");
|
||||||
|
|
||||||
|
ExtractLiteralAsParameter factory;
|
||||||
|
QuickFixOperationTest(testDocuments, &factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QByteArray>("original");
|
||||||
|
QTest::addColumn<QByteArray>("expected");
|
||||||
|
|
||||||
|
QTest::newRow("ExtractLiteralAsParameter_freeFunction")
|
||||||
|
<< QByteArray(
|
||||||
|
"void foo(const char *a, long b = 1)\n"
|
||||||
|
"{return 1@56 + 123 + 156;}\n")
|
||||||
|
<< QByteArray(
|
||||||
|
"void foo(const char *a, long b = 1, int newParameter = 156)\n"
|
||||||
|
"{return newParameter + 123 + newParameter;}\n");
|
||||||
|
QTest::newRow("ExtractLiteralAsParameter_memberFunction")
|
||||||
|
<< QByteArray(
|
||||||
|
"class Narf {\n"
|
||||||
|
"public:\n"
|
||||||
|
" int zort();\n"
|
||||||
|
"};\n\n"
|
||||||
|
"int Narf::zort()\n"
|
||||||
|
"{ return 15@5 + 1; }\n")
|
||||||
|
<< QByteArray(
|
||||||
|
"class Narf {\n"
|
||||||
|
"public:\n"
|
||||||
|
" int zort(int newParameter = 155);\n"
|
||||||
|
"};\n\n"
|
||||||
|
"int Narf::zort(int newParameter)\n"
|
||||||
|
"{ return newParameter + 1; }\n");
|
||||||
|
QTest::newRow("ExtractLiteralAsParameter_memberFunctionInline")
|
||||||
|
<< QByteArray(
|
||||||
|
"class Narf {\n"
|
||||||
|
"public:\n"
|
||||||
|
" int zort()\n"
|
||||||
|
" { return 15@5 + 1; }\n"
|
||||||
|
"};\n")
|
||||||
|
<< QByteArray(
|
||||||
|
"class Narf {\n"
|
||||||
|
"public:\n"
|
||||||
|
" int zort(int newParameter = 155)\n"
|
||||||
|
" { return newParameter + 1; }\n"
|
||||||
|
"};\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test()
|
||||||
|
{
|
||||||
|
QFETCH(QByteArray, original);
|
||||||
|
QFETCH(QByteArray, expected);
|
||||||
|
ExtractLiteralAsParameter factory;
|
||||||
|
QuickFixOperationTest(singleDocument(original, expected), &factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
QObject *ExtractLiteralAsParameter::createTest() { return new ExtractLiteralAsParameterTest; }
|
||||||
|
|
||||||
|
#endif // WITH_TESTS
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void registerExtractLiteralAsParameterQuickfix()
|
||||||
|
{
|
||||||
|
CppQuickFixFactory::registerFactory<ExtractLiteralAsParameter>();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace CppEditor::Internal
|
||||||
|
|
||||||
|
#ifdef WITH_TESTS
|
||||||
|
#include <extractliteralasparameter.moc>
|
||||||
|
#endif
|
@@ -0,0 +1,8 @@
|
|||||||
|
// Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace CppEditor::Internal {
|
||||||
|
void registerExtractLiteralAsParameterQuickfix();
|
||||||
|
} // namespace CppEditor::Internal
|
Reference in New Issue
Block a user