forked from qt-creator/qt-creator
CppEditor: Add QuickFix to generate a constructor
Change-Id: Iba2ce3bfa1a1d1a325626a21f46b485d12cbb060 Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
43
src/libs/3rdparty/cplusplus/AST.h
vendored
43
src/libs/3rdparty/cplusplus/AST.h
vendored
@@ -75,8 +75,51 @@ public:
|
|||||||
|
|
||||||
Tptr value;
|
Tptr value;
|
||||||
List *next;
|
List *next;
|
||||||
|
|
||||||
|
class ListIterator
|
||||||
|
{
|
||||||
|
List<Tptr> *iter;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ListIterator(List<Tptr> *iter)
|
||||||
|
: iter(iter)
|
||||||
|
{}
|
||||||
|
Tptr operator*() { return iter->value; }
|
||||||
|
ListIterator &operator++()
|
||||||
|
{
|
||||||
|
if (iter)
|
||||||
|
iter = iter->next;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
bool operator==(ListIterator other) { return iter == other.iter; }
|
||||||
|
bool operator!=(ListIterator other) { return iter != other.iter; }
|
||||||
|
};
|
||||||
|
ListIterator begin() { return {this}; }
|
||||||
|
ListIterator end() { return {nullptr}; }
|
||||||
|
|
||||||
|
int size() { return next ? next->size() + 1 : 1; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename Tptr>
|
||||||
|
typename List<Tptr>::ListIterator begin(List<Tptr> *list)
|
||||||
|
{
|
||||||
|
return list ? list->begin() : typename List<Tptr>::ListIterator(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Tptr>
|
||||||
|
typename List<Tptr>::ListIterator end(List<Tptr> *list)
|
||||||
|
{
|
||||||
|
return list ? list->end() : typename List<Tptr>::ListIterator(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Tptr>
|
||||||
|
int size(List<Tptr> *list)
|
||||||
|
{
|
||||||
|
if (list)
|
||||||
|
return list->size();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
class CPLUSPLUS_EXPORT AST: public Managed
|
class CPLUSPLUS_EXPORT AST: public Managed
|
||||||
{
|
{
|
||||||
AST(const AST &other);
|
AST(const AST &other);
|
||||||
|
|||||||
@@ -231,6 +231,9 @@ private slots:
|
|||||||
void test_quickfix_removeUsingNamespace_simple();
|
void test_quickfix_removeUsingNamespace_simple();
|
||||||
void test_quickfix_removeUsingNamespace_differentSymbols();
|
void test_quickfix_removeUsingNamespace_differentSymbols();
|
||||||
|
|
||||||
|
void test_quickfix_generateConstructor_data();
|
||||||
|
void test_quickfix_generateConstructor();
|
||||||
|
|
||||||
void test_quickfix_InsertVirtualMethods_data();
|
void test_quickfix_InsertVirtualMethods_data();
|
||||||
void test_quickfix_InsertVirtualMethods();
|
void test_quickfix_InsertVirtualMethods();
|
||||||
void test_quickfix_InsertVirtualMethods_implementationFile();
|
void test_quickfix_InsertVirtualMethods_implementationFile();
|
||||||
|
|||||||
@@ -7576,5 +7576,285 @@ void CppEditorPlugin::test_quickfix_removeUsingNamespace_differentSymbols()
|
|||||||
QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 0);
|
QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ConstructorLocation { Inside, Outside, CppGenNamespace, CppGenUsingDirective, CppRewriteType };
|
||||||
|
|
||||||
|
void CppEditorPlugin::test_quickfix_generateConstructor_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QByteArray>("original_header");
|
||||||
|
QTest::addColumn<QByteArray>("expected_header");
|
||||||
|
QTest::addColumn<QByteArray>("original_source");
|
||||||
|
QTest::addColumn<QByteArray>("expected_source");
|
||||||
|
QTest::addColumn<int>("location");
|
||||||
|
const int Inside = ConstructorLocation::Inside;
|
||||||
|
const int Outside = ConstructorLocation::Outside;
|
||||||
|
const int CppGenNamespace = ConstructorLocation::CppGenNamespace;
|
||||||
|
const int CppGenUsingDirective = ConstructorLocation::CppGenUsingDirective;
|
||||||
|
const int CppRewriteType = ConstructorLocation::CppRewriteType;
|
||||||
|
|
||||||
|
QByteArray header = R"--(
|
||||||
|
class@ Foo{
|
||||||
|
int test;
|
||||||
|
static int s;
|
||||||
|
};
|
||||||
|
)--";
|
||||||
|
QByteArray expected = R"--(
|
||||||
|
class Foo{
|
||||||
|
int test;
|
||||||
|
static int s;
|
||||||
|
public:
|
||||||
|
Foo(int test) : test(test)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
)--";
|
||||||
|
QTest::newRow("ignore static") << header << expected << QByteArray() << QByteArray() << Inside;
|
||||||
|
|
||||||
|
header = R"--(
|
||||||
|
class@ Foo{
|
||||||
|
CustomType test;
|
||||||
|
};
|
||||||
|
)--";
|
||||||
|
expected = R"--(
|
||||||
|
class Foo{
|
||||||
|
CustomType test;
|
||||||
|
public:
|
||||||
|
Foo(CustomType test) : test(std::move(test))
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
)--";
|
||||||
|
QTest::newRow("Move custom value types")
|
||||||
|
<< header << expected << QByteArray() << QByteArray() << Inside;
|
||||||
|
|
||||||
|
header = R"--(
|
||||||
|
class@ Foo{
|
||||||
|
int test;
|
||||||
|
protected:
|
||||||
|
Foo() = default;
|
||||||
|
};
|
||||||
|
)--";
|
||||||
|
expected = R"--(
|
||||||
|
class Foo{
|
||||||
|
int test;
|
||||||
|
public:
|
||||||
|
Foo(int test) : test(test)
|
||||||
|
{}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Foo() = default;
|
||||||
|
};
|
||||||
|
)--";
|
||||||
|
|
||||||
|
QTest::newRow("new section before existing")
|
||||||
|
<< header << expected << QByteArray() << QByteArray() << Inside;
|
||||||
|
|
||||||
|
header = R"--(
|
||||||
|
class@ Foo{
|
||||||
|
int test;
|
||||||
|
};
|
||||||
|
)--";
|
||||||
|
expected = R"--(
|
||||||
|
class Foo{
|
||||||
|
int test;
|
||||||
|
public:
|
||||||
|
Foo(int test) : test(test)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
)--";
|
||||||
|
QTest::newRow("new section at end")
|
||||||
|
<< header << expected << QByteArray() << QByteArray() << Inside;
|
||||||
|
|
||||||
|
header = R"--(
|
||||||
|
class@ Foo{
|
||||||
|
int test;
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Random comment
|
||||||
|
*/
|
||||||
|
Foo(int i, int i2);
|
||||||
|
};
|
||||||
|
)--";
|
||||||
|
expected = R"--(
|
||||||
|
class Foo{
|
||||||
|
int test;
|
||||||
|
public:
|
||||||
|
Foo(int test) : test(test)
|
||||||
|
{}
|
||||||
|
/**
|
||||||
|
* Random comment
|
||||||
|
*/
|
||||||
|
Foo(int i, int i2);
|
||||||
|
};
|
||||||
|
)--";
|
||||||
|
QTest::newRow("in section before")
|
||||||
|
<< header << expected << QByteArray() << QByteArray() << Inside;
|
||||||
|
|
||||||
|
header = R"--(
|
||||||
|
class@ Foo{
|
||||||
|
int test;
|
||||||
|
public:
|
||||||
|
Foo() = default;
|
||||||
|
};
|
||||||
|
)--";
|
||||||
|
expected = R"--(
|
||||||
|
class Foo{
|
||||||
|
int test;
|
||||||
|
public:
|
||||||
|
Foo() = default;
|
||||||
|
Foo(int test) : test(test)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
)--";
|
||||||
|
QTest::newRow("in section after")
|
||||||
|
<< header << expected << QByteArray() << QByteArray() << Inside;
|
||||||
|
|
||||||
|
const QByteArray common = R"--(
|
||||||
|
namespace N{
|
||||||
|
template<typename T>
|
||||||
|
struct vector{
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)--";
|
||||||
|
header = common + R"--(
|
||||||
|
namespace M{
|
||||||
|
enum G{g};
|
||||||
|
class@ Foo{
|
||||||
|
N::vector<G> g;
|
||||||
|
enum E{e}e;
|
||||||
|
public:
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)--";
|
||||||
|
|
||||||
|
expected = common + R"--(
|
||||||
|
namespace M{
|
||||||
|
enum G{g};
|
||||||
|
class@ Foo{
|
||||||
|
N::vector<G> g;
|
||||||
|
enum E{e}e;
|
||||||
|
public:
|
||||||
|
Foo(const N::vector<G> &g, E e);
|
||||||
|
};
|
||||||
|
|
||||||
|
Foo::Foo(const N::vector<G> &g, Foo::E e) : g(g),
|
||||||
|
e(e)
|
||||||
|
{}
|
||||||
|
|
||||||
|
}
|
||||||
|
)--";
|
||||||
|
QTest::newRow("source: right type outside class ")
|
||||||
|
<< QByteArray() << QByteArray() << header << expected << Outside;
|
||||||
|
expected = common + R"--(
|
||||||
|
namespace M{
|
||||||
|
enum G{g};
|
||||||
|
class@ Foo{
|
||||||
|
N::vector<G> g;
|
||||||
|
enum E{e}e;
|
||||||
|
public:
|
||||||
|
Foo(const N::vector<G> &g, E e);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline M::Foo::Foo(const N::vector<M::G> &g, M::Foo::E e) : g(g),
|
||||||
|
e(e)
|
||||||
|
{}
|
||||||
|
|
||||||
|
)--";
|
||||||
|
QTest::newRow("header: right type outside class ")
|
||||||
|
<< header << expected << QByteArray() << QByteArray() << Outside;
|
||||||
|
|
||||||
|
expected = common + R"--(
|
||||||
|
namespace M{
|
||||||
|
enum G{g};
|
||||||
|
class@ Foo{
|
||||||
|
N::vector<G> g;
|
||||||
|
enum E{e}e;
|
||||||
|
public:
|
||||||
|
Foo(const N::vector<G> &g, E e);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)--";
|
||||||
|
const QByteArray source = R"--(
|
||||||
|
#include "file.h"
|
||||||
|
)--";
|
||||||
|
QByteArray expected_source = R"--(
|
||||||
|
#include "file.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace M {
|
||||||
|
Foo::Foo(const N::vector<G> &g, Foo::E e) : g(g),
|
||||||
|
e(e)
|
||||||
|
{}
|
||||||
|
|
||||||
|
}
|
||||||
|
)--";
|
||||||
|
QTest::newRow("source: right type inside namespace")
|
||||||
|
<< header << expected << source << expected_source << CppGenNamespace;
|
||||||
|
|
||||||
|
expected_source = R"--(
|
||||||
|
#include "file.h"
|
||||||
|
|
||||||
|
using namespace M;
|
||||||
|
Foo::Foo(const N::vector<G> &g, Foo::E e) : g(g),
|
||||||
|
e(e)
|
||||||
|
{}
|
||||||
|
)--";
|
||||||
|
QTest::newRow("source: right type with using directive")
|
||||||
|
<< header << expected << source << expected_source << CppGenUsingDirective;
|
||||||
|
|
||||||
|
expected_source = R"--(
|
||||||
|
#include "file.h"
|
||||||
|
|
||||||
|
M::Foo::Foo(const N::vector<M::G> &g, M::Foo::E e) : g(g),
|
||||||
|
e(e)
|
||||||
|
{}
|
||||||
|
)--";
|
||||||
|
QTest::newRow("source: right type while rewritung types")
|
||||||
|
<< header << expected << source << expected_source << CppRewriteType;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CppEditorPlugin::test_quickfix_generateConstructor()
|
||||||
|
{
|
||||||
|
class TestFactory : public GenerateConstructor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TestFactory() { setTest(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
QFETCH(QByteArray, original_header);
|
||||||
|
QFETCH(QByteArray, expected_header);
|
||||||
|
QFETCH(QByteArray, original_source);
|
||||||
|
QFETCH(QByteArray, expected_source);
|
||||||
|
QFETCH(int, location);
|
||||||
|
|
||||||
|
QuickFixSettings s;
|
||||||
|
s->valueTypes << "CustomType";
|
||||||
|
using L = ConstructorLocation;
|
||||||
|
if (location == L::Inside) {
|
||||||
|
s->setterInCppFileFrom = -1;
|
||||||
|
s->setterOutsideClassFrom = -1;
|
||||||
|
} else if (location == L::Outside) {
|
||||||
|
s->setterInCppFileFrom = -1;
|
||||||
|
s->setterOutsideClassFrom = 1;
|
||||||
|
} else if (location >= L::CppGenNamespace && location <= L::CppRewriteType) {
|
||||||
|
s->setterInCppFileFrom = 1;
|
||||||
|
s->setterOutsideClassFrom = -1;
|
||||||
|
using Handling = CppQuickFixSettings::MissingNamespaceHandling;
|
||||||
|
if (location == L::CppGenNamespace)
|
||||||
|
s->cppFileNamespaceHandling = Handling::CreateMissing;
|
||||||
|
else if (location == L::CppGenUsingDirective)
|
||||||
|
s->cppFileNamespaceHandling = Handling::AddUsingDirective;
|
||||||
|
else if (location == L::CppRewriteType)
|
||||||
|
s->cppFileNamespaceHandling = Handling::RewriteType;
|
||||||
|
} else {
|
||||||
|
QFAIL("location is none of the values of the ConstructorLocation enum");
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QuickFixTestDocument::Ptr> testDocuments;
|
||||||
|
testDocuments << QuickFixTestDocument::create("file.h", original_header, expected_header);
|
||||||
|
testDocuments << QuickFixTestDocument::create("file.cpp", original_source, expected_source);
|
||||||
|
TestFactory factory;
|
||||||
|
QuickFixOperationTest(testDocuments, &factory);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace CppEditor
|
} // namespace CppEditor
|
||||||
|
|||||||
@@ -413,10 +413,12 @@ QStringList getNamespaceNames(const Symbol *symbol)
|
|||||||
|
|
||||||
// TODO: We should use the "CreateMissing" approach everywhere.
|
// TODO: We should use the "CreateMissing" approach everywhere.
|
||||||
enum class NamespaceHandling { CreateMissing, Ignore };
|
enum class NamespaceHandling { CreateMissing, Ignore };
|
||||||
InsertionLocation insertLocationForMethodDefinition(Symbol *symbol, const bool useSymbolFinder,
|
InsertionLocation insertLocationForMethodDefinition(Symbol *symbol,
|
||||||
|
const bool useSymbolFinder,
|
||||||
NamespaceHandling namespaceHandling,
|
NamespaceHandling namespaceHandling,
|
||||||
CppRefactoringChanges& refactoring,
|
const CppRefactoringChanges &refactoring,
|
||||||
const QString& fileName)
|
const QString &fileName,
|
||||||
|
QStringList *insertedNamespaces = nullptr)
|
||||||
{
|
{
|
||||||
QTC_ASSERT(symbol, return InsertionLocation());
|
QTC_ASSERT(symbol, return InsertionLocation());
|
||||||
|
|
||||||
@@ -484,6 +486,8 @@ InsertionLocation insertLocationForMethodDefinition(Symbol *symbol, const bool u
|
|||||||
prefix += "namespace " + ns + " {\n";
|
prefix += "namespace " + ns + " {\n";
|
||||||
suffix += "}\n";
|
suffix += "}\n";
|
||||||
}
|
}
|
||||||
|
if (insertedNamespaces)
|
||||||
|
*insertedNamespaces = visitor.remainingNamespaces();
|
||||||
|
|
||||||
//TODO watch for moc-includes
|
//TODO watch for moc-includes
|
||||||
|
|
||||||
@@ -3689,7 +3693,7 @@ Utils::optional<FullySpecifiedType> getFirstTemplateParameter(FullySpecifiedType
|
|||||||
|
|
||||||
QString symbolAtDifferentLocation(const CppQuickFixInterface &interface,
|
QString symbolAtDifferentLocation(const CppQuickFixInterface &interface,
|
||||||
Symbol *symbol,
|
Symbol *symbol,
|
||||||
CppRefactoringFilePtr &targetFile,
|
const CppRefactoringFilePtr &targetFile,
|
||||||
InsertionLocation targetLocation)
|
InsertionLocation targetLocation)
|
||||||
{
|
{
|
||||||
QTC_ASSERT(symbol, return QString());
|
QTC_ASSERT(symbol, return QString());
|
||||||
@@ -3713,12 +3717,20 @@ QString symbolAtDifferentLocation(const CppQuickFixInterface &interface,
|
|||||||
FullySpecifiedType typeAtDifferentLocation(const CppQuickFixInterface &interface,
|
FullySpecifiedType typeAtDifferentLocation(const CppQuickFixInterface &interface,
|
||||||
FullySpecifiedType type,
|
FullySpecifiedType type,
|
||||||
Scope *originalScope,
|
Scope *originalScope,
|
||||||
CppRefactoringFilePtr &targetFile,
|
const CppRefactoringFilePtr &targetFile,
|
||||||
InsertionLocation targetLocation)
|
InsertionLocation targetLocation,
|
||||||
|
const QStringList &newNamespaceNamesAtLoc = {})
|
||||||
{
|
{
|
||||||
Scope *scopeAtInsertPos = targetFile->cppDocument()->scopeAt(targetLocation.line(),
|
Scope *scopeAtInsertPos = targetFile->cppDocument()->scopeAt(targetLocation.line(),
|
||||||
targetLocation.column());
|
targetLocation.column());
|
||||||
|
for (const QString &nsName : newNamespaceNamesAtLoc) {
|
||||||
|
const QByteArray utf8Name = nsName.toUtf8();
|
||||||
|
Control *control = targetFile->cppDocument()->control();
|
||||||
|
const Name *name = control->identifier(utf8Name.data(), utf8Name.size());
|
||||||
|
Namespace *ns = control->newNamespace(0, name);
|
||||||
|
ns->setEnclosingScope(scopeAtInsertPos);
|
||||||
|
scopeAtInsertPos = ns;
|
||||||
|
}
|
||||||
LookupContext cppContext(targetFile->cppDocument(), interface.snapshot());
|
LookupContext cppContext(targetFile->cppDocument(), interface.snapshot());
|
||||||
ClassOrNamespace *cppCoN = cppContext.lookupType(scopeAtInsertPos);
|
ClassOrNamespace *cppCoN = cppContext.lookupType(scopeAtInsertPos);
|
||||||
if (!cppCoN)
|
if (!cppCoN)
|
||||||
@@ -3750,21 +3762,24 @@ struct ExistingGetterSetterData
|
|||||||
class GetterSetterRefactoringHelper
|
class GetterSetterRefactoringHelper
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
GetterSetterRefactoringHelper(CppQuickFixOperation *operation, const QString &fileName, Class *clazz)
|
GetterSetterRefactoringHelper(CppQuickFixOperation *operation,
|
||||||
|
const QString &fileName,
|
||||||
|
Class *clazz)
|
||||||
: m_operation(operation)
|
: m_operation(operation)
|
||||||
, m_changes(m_operation->snapshot())
|
, m_changes(m_operation->snapshot())
|
||||||
, m_locator(m_changes)
|
, m_locator(m_changes)
|
||||||
|
, m_headerFile(m_changes.file(fileName))
|
||||||
|
, m_sourceFile([&] {
|
||||||
|
QString cppFileName = correspondingHeaderOrSource(fileName, &m_isHeaderHeaderFile);
|
||||||
|
if (!m_isHeaderHeaderFile || !QFile::exists(cppFileName)) {
|
||||||
|
// there is no "source" file
|
||||||
|
return m_headerFile;
|
||||||
|
} else {
|
||||||
|
return m_changes.file(cppFileName);
|
||||||
|
}
|
||||||
|
}())
|
||||||
, m_class(clazz)
|
, m_class(clazz)
|
||||||
{
|
{}
|
||||||
m_headerFile = m_changes.file(fileName);
|
|
||||||
QString cppFileName = correspondingHeaderOrSource(fileName, &m_isHeaderHeaderFile);
|
|
||||||
if (!m_isHeaderHeaderFile || !QFile::exists(cppFileName)) {
|
|
||||||
// there is no "source" file
|
|
||||||
m_sourceFile = m_headerFile;
|
|
||||||
} else {
|
|
||||||
m_sourceFile = m_changes.file(cppFileName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void performGeneration(ExistingGetterSetterData data, int generationFlags);
|
void performGeneration(ExistingGetterSetterData data, int generationFlags);
|
||||||
|
|
||||||
@@ -3798,7 +3813,8 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasSourceFile() { return m_headerFile != m_sourceFile; }
|
bool hasSourceFile() const { return m_headerFile != m_sourceFile; }
|
||||||
|
bool isHeaderHeaderFile() const { return m_isHeaderHeaderFile; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void insertAndIndent(const RefactoringFilePtr &file,
|
void insertAndIndent(const RefactoringFilePtr &file,
|
||||||
@@ -3831,17 +3847,86 @@ protected:
|
|||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString symbolAt(Symbol *symbol, CppRefactoringFilePtr &targetFile, InsertionLocation targetLocation)
|
QString symbolAt(Symbol *symbol,
|
||||||
|
const CppRefactoringFilePtr &targetFile,
|
||||||
|
InsertionLocation targetLocation)
|
||||||
{
|
{
|
||||||
return symbolAtDifferentLocation(*m_operation, symbol, targetFile, targetLocation);
|
return symbolAtDifferentLocation(*m_operation, symbol, targetFile, targetLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
FullySpecifiedType typeAt(FullySpecifiedType type,
|
FullySpecifiedType typeAt(FullySpecifiedType type,
|
||||||
Scope *originalScope,
|
Scope *originalScope,
|
||||||
CppRefactoringFilePtr &targetFile,
|
const CppRefactoringFilePtr &targetFile,
|
||||||
InsertionLocation targetLocation)
|
InsertionLocation targetLocation,
|
||||||
|
const QStringList &newNamespaceNamesAtLoc = {})
|
||||||
{
|
{
|
||||||
return typeAtDifferentLocation(*m_operation, type, originalScope, targetFile, targetLocation);
|
return typeAtDifferentLocation(*m_operation,
|
||||||
|
type,
|
||||||
|
originalScope,
|
||||||
|
targetFile,
|
||||||
|
targetLocation,
|
||||||
|
newNamespaceNamesAtLoc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief checks if the type in the enclosing scope in the header is a value type
|
||||||
|
* @param type a type in the m_headerFile
|
||||||
|
* @param enclosingScope the enclosing scope
|
||||||
|
* @param customValueType if not nullptr set to true when value type comes
|
||||||
|
* from CppQuickFixSettings::isValueType
|
||||||
|
* @return true if it is a pointer, enum, integer, floating point, reference, custom value type
|
||||||
|
*/
|
||||||
|
bool isValueType(FullySpecifiedType type, Scope *enclosingScope, bool *customValueType = nullptr)
|
||||||
|
{
|
||||||
|
if (customValueType)
|
||||||
|
*customValueType = false;
|
||||||
|
// a type is a value type if it is one of the following
|
||||||
|
const auto isTypeValueType = [](const FullySpecifiedType &t) {
|
||||||
|
return t->isPointerType() || t->isEnumType() || t->isIntegerType() || t->isFloatType()
|
||||||
|
|| t->isReferenceType();
|
||||||
|
};
|
||||||
|
if (type->isNamedType()) {
|
||||||
|
// we need a recursive search and a lookup context
|
||||||
|
LookupContext context(m_headerFile->cppDocument(), m_changes.snapshot());
|
||||||
|
auto isValueType = [settings = m_settings,
|
||||||
|
&customValueType,
|
||||||
|
&context,
|
||||||
|
&isTypeValueType](const Name *name,
|
||||||
|
Scope *scope,
|
||||||
|
auto &isValueType) mutable -> bool {
|
||||||
|
// maybe the type is a custom value type by name
|
||||||
|
if (const Identifier *id = name->identifier()) {
|
||||||
|
if (settings->isValueType(QString::fromUtf8(id->chars(), id->size()))) {
|
||||||
|
if (customValueType)
|
||||||
|
*customValueType = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// search for the type declaration
|
||||||
|
QList<LookupItem> localLookup = context.lookup(name, scope);
|
||||||
|
for (auto &&i : localLookup) {
|
||||||
|
if (isTypeValueType(i.type()))
|
||||||
|
return true;
|
||||||
|
if (i.type()->isNamedType()) { // check if we have to search recursively
|
||||||
|
const Name *newName = i.type()->asNamedType()->name();
|
||||||
|
Scope *newScope = i.declaration()->enclosingScope();
|
||||||
|
if (newName == name && newScope == scope)
|
||||||
|
continue; // we have found the start location of the search
|
||||||
|
return isValueType(newName, newScope, isValueType);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
// start recursion
|
||||||
|
return isValueType(type->asNamedType()->name(), enclosingScope, isValueType);
|
||||||
|
}
|
||||||
|
return isTypeValueType(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isValueType(Symbol *symbol, bool *customValueType = nullptr)
|
||||||
|
{
|
||||||
|
return isValueType(symbol->type(), symbol->enclosingScope(), customValueType);
|
||||||
}
|
}
|
||||||
|
|
||||||
void addHeaderCode(InsertionPointLocator::AccessSpec spec, QString code)
|
void addHeaderCode(InsertionPointLocator::AccessSpec spec, QString code)
|
||||||
@@ -3864,31 +3949,38 @@ protected:
|
|||||||
return loc;
|
return loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
InsertionLocation sourceLocationFor(Declaration *declarationSymbol)
|
InsertionLocation sourceLocationFor(Symbol *symbol, QStringList *insertedNamespaces = nullptr)
|
||||||
{
|
{
|
||||||
if (m_sourceFileInsertionPoint.isValid())
|
if (m_sourceFileInsertionPoint.isValid())
|
||||||
return m_sourceFileInsertionPoint;
|
return m_sourceFileInsertionPoint;
|
||||||
m_sourceFileInsertionPoint = insertLocationForMethodDefinition(
|
m_sourceFileInsertionPoint
|
||||||
declarationSymbol,
|
= insertLocationForMethodDefinition(symbol,
|
||||||
false,
|
false,
|
||||||
m_settings->createMissingNamespacesinCppFile() ? NamespaceHandling::CreateMissing
|
m_settings->createMissingNamespacesinCppFile()
|
||||||
: NamespaceHandling::Ignore,
|
? NamespaceHandling::CreateMissing
|
||||||
m_changes,
|
: NamespaceHandling::Ignore,
|
||||||
m_sourceFile->fileName());
|
m_changes,
|
||||||
|
m_sourceFile->fileName(),
|
||||||
|
insertedNamespaces);
|
||||||
if (m_settings->addUsingNamespaceinCppFile()) {
|
if (m_settings->addUsingNamespaceinCppFile()) {
|
||||||
// check if we have to insert a using namespace ...
|
// check if we have to insert a using namespace ...
|
||||||
auto requiredNamespaces = getNamespaceNames(declarationSymbol->enclosingClass());
|
auto requiredNamespaces = getNamespaceNames(
|
||||||
|
symbol->isClass() ? symbol : symbol->enclosingClass());
|
||||||
NSCheckerVisitor visitor(m_sourceFile.get(),
|
NSCheckerVisitor visitor(m_sourceFile.get(),
|
||||||
requiredNamespaces,
|
requiredNamespaces,
|
||||||
m_sourceFile->position(m_sourceFileInsertionPoint.line(),
|
m_sourceFile->position(m_sourceFileInsertionPoint.line(),
|
||||||
m_sourceFileInsertionPoint.column()));
|
m_sourceFileInsertionPoint.column()));
|
||||||
visitor.accept(m_sourceFile->cppDocument()->translationUnit()->ast());
|
visitor.accept(m_sourceFile->cppDocument()->translationUnit()->ast());
|
||||||
|
if (insertedNamespaces)
|
||||||
|
insertedNamespaces->clear();
|
||||||
if (auto rns = visitor.remainingNamespaces(); !rns.empty()) {
|
if (auto rns = visitor.remainingNamespaces(); !rns.empty()) {
|
||||||
QString ns = "using namespace ";
|
QString ns = "using namespace ";
|
||||||
for (auto &n : rns) {
|
for (auto &n : rns) {
|
||||||
if (!n.isEmpty()) { // we have to ignore unnamed namespaces
|
if (!n.isEmpty()) { // we have to ignore unnamed namespaces
|
||||||
ns += n;
|
ns += n;
|
||||||
ns += "::";
|
ns += "::";
|
||||||
|
if (insertedNamespaces)
|
||||||
|
insertedNamespaces->append(n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ns.resize(ns.size() - 2); // remove last '::'
|
ns.resize(ns.size() - 2); // remove last '::'
|
||||||
@@ -3911,20 +4003,22 @@ protected:
|
|||||||
m_sourceFileCode += code;
|
m_sourceFileCode += code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
CppQuickFixOperation *const m_operation;
|
||||||
|
const CppRefactoringChanges m_changes;
|
||||||
|
const InsertionPointLocator m_locator;
|
||||||
|
const CppRefactoringFilePtr m_headerFile;
|
||||||
|
const CppRefactoringFilePtr m_sourceFile;
|
||||||
|
CppQuickFixSettings *const m_settings = CppQuickFixProjectsSettings::getQuickFixSettings(
|
||||||
|
ProjectExplorer::ProjectTree::currentProject());
|
||||||
|
Class *const m_class;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CppQuickFixOperation *m_operation;
|
|
||||||
CppRefactoringChanges m_changes;
|
|
||||||
CppRefactoringFilePtr m_headerFile;
|
|
||||||
CppRefactoringFilePtr m_sourceFile;
|
|
||||||
ChangeSet m_headerFileChangeSet;
|
ChangeSet m_headerFileChangeSet;
|
||||||
ChangeSet m_sourceFileChangeSet;
|
ChangeSet m_sourceFileChangeSet;
|
||||||
InsertionPointLocator m_locator;
|
|
||||||
QMap<InsertionPointLocator::AccessSpec, InsertionLocation> m_headerInsertionPoints;
|
QMap<InsertionPointLocator::AccessSpec, InsertionLocation> m_headerInsertionPoints;
|
||||||
InsertionLocation m_sourceFileInsertionPoint;
|
InsertionLocation m_sourceFileInsertionPoint;
|
||||||
CppQuickFixSettings *m_settings = CppQuickFixProjectsSettings::getQuickFixSettings(
|
|
||||||
ProjectExplorer::ProjectTree::currentProject());
|
|
||||||
QString m_sourceFileCode;
|
QString m_sourceFileCode;
|
||||||
Class *m_class;
|
|
||||||
QMap<InsertionPointLocator::AccessSpec, QString> m_headerFileCode;
|
QMap<InsertionPointLocator::AccessSpec, QString> m_headerFileCode;
|
||||||
bool m_isHeaderHeaderFile; // the "header" (where the class is defined) can be a source file
|
bool m_isHeaderHeaderFile; // the "header" (where the class is defined) can be a source file
|
||||||
};
|
};
|
||||||
@@ -4070,47 +4164,8 @@ void GetterSetterRefactoringHelper::performGeneration(ExistingGetterSetterData d
|
|||||||
overview.showTemplateParameters = true;
|
overview.showTemplateParameters = true;
|
||||||
|
|
||||||
// Ok... - If a type is a Named type we have to search recusive for the real type
|
// Ok... - If a type is a Named type we have to search recusive for the real type
|
||||||
const bool isValueType = [settings = m_settings,
|
const bool isValueType = this->isValueType(memberVariableType,
|
||||||
scope = data.declarationSymbol->enclosingScope(),
|
data.declarationSymbol->enclosingScope());
|
||||||
document = m_headerFile->cppDocument(),
|
|
||||||
&snapshot = m_changes.snapshot()](FullySpecifiedType type) {
|
|
||||||
// a type is a value type if it is one of the following
|
|
||||||
const auto isTypeValueType = [](const FullySpecifiedType &t) {
|
|
||||||
return t->isPointerType() || t->isEnumType() || t->isIntegerType() || t->isFloatType()
|
|
||||||
|| t->isReferenceType();
|
|
||||||
};
|
|
||||||
if (type->isNamedType()) {
|
|
||||||
// we need a recursive search and a lookup context
|
|
||||||
LookupContext context(document, snapshot);
|
|
||||||
auto isValueType = [settings, &context, &isTypeValueType](const Name *name,
|
|
||||||
Scope *scope,
|
|
||||||
auto &isValueType) mutable -> bool {
|
|
||||||
// maybe the type is a custom value type by name
|
|
||||||
if (const Identifier *id = name->identifier()) {
|
|
||||||
if (settings->isValueType(QString::fromUtf8(id->chars(), id->size())))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// search for the type declaration
|
|
||||||
QList<LookupItem> localLookup = context.lookup(name, scope);
|
|
||||||
for (auto &&i : localLookup) {
|
|
||||||
if (isTypeValueType(i.type()))
|
|
||||||
return true;
|
|
||||||
if (i.type()->isNamedType()) { // check if we have to search recursively
|
|
||||||
const Name *newName = i.type()->asNamedType()->name();
|
|
||||||
Scope *newScope = i.declaration()->enclosingScope();
|
|
||||||
if (newName == name && newScope == scope)
|
|
||||||
continue; // we have found the start location of the search
|
|
||||||
return isValueType(newName, newScope, isValueType);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
// start recursion
|
|
||||||
return isValueType(type->asNamedType()->name(), scope, isValueType);
|
|
||||||
}
|
|
||||||
return isTypeValueType(type);
|
|
||||||
}(memberVariableType);
|
|
||||||
const FullySpecifiedType parameterType = isValueType ? memberVariableType
|
const FullySpecifiedType parameterType = isValueType ? memberVariableType
|
||||||
: makeConstRef(memberVariableType);
|
: makeConstRef(memberVariableType);
|
||||||
|
|
||||||
@@ -8350,6 +8405,351 @@ void RemoveUsingNamespace::match(const CppQuickFixInterface &interface, QuickFix
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class ConstructorMemberInfo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ConstructorMemberInfo(const QString &name, Symbol *symbol)
|
||||||
|
: memberVariableName(name)
|
||||||
|
, parameterName(memberBaseName(name))
|
||||||
|
, symbol(symbol)
|
||||||
|
, type(symbol->type())
|
||||||
|
{}
|
||||||
|
|
||||||
|
QString memberVariableName;
|
||||||
|
QString parameterName;
|
||||||
|
bool init = true;
|
||||||
|
Symbol *symbol; // for the right type later
|
||||||
|
FullySpecifiedType type;
|
||||||
|
};
|
||||||
|
using ConstructorMemberCandidates = std::vector<ConstructorMemberInfo>;
|
||||||
|
|
||||||
|
class ConstructorCandidateTreeItem : public Utils::TreeItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum Column { ShouldInitColumn, MemberNameColumn, ParameterNameColumn };
|
||||||
|
ConstructorCandidateTreeItem(ConstructorMemberInfo *memberInfo)
|
||||||
|
: m_memberInfo(memberInfo)
|
||||||
|
{}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVariant data(int column, int role) const override
|
||||||
|
{
|
||||||
|
if (role == Qt::CheckStateRole && column == ShouldInitColumn)
|
||||||
|
return m_memberInfo->init ? Qt::Checked : Qt::Unchecked;
|
||||||
|
if (role == Qt::DisplayRole && column == MemberNameColumn)
|
||||||
|
return m_memberInfo->memberVariableName;
|
||||||
|
if ((role == Qt::DisplayRole || role == Qt::EditRole) && column == ParameterNameColumn)
|
||||||
|
return m_memberInfo->parameterName;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool setData(int column, const QVariant &data, int role) override
|
||||||
|
{
|
||||||
|
if (column == ShouldInitColumn && role == Qt::CheckStateRole) {
|
||||||
|
m_memberInfo->init = data.toInt() == Qt::Checked;
|
||||||
|
update();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (column == ParameterNameColumn && role == Qt::EditRole) {
|
||||||
|
m_memberInfo->parameterName = data.toString();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::ItemFlags flags(int column) const override
|
||||||
|
{
|
||||||
|
if (column == ShouldInitColumn)
|
||||||
|
return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
|
||||||
|
if (!m_memberInfo->init)
|
||||||
|
return {};
|
||||||
|
if (column == MemberNameColumn)
|
||||||
|
return Qt::ItemIsEnabled;
|
||||||
|
if (column == ParameterNameColumn)
|
||||||
|
return Qt::ItemIsEnabled | Qt::ItemIsEditable;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ConstructorMemberInfo *const m_memberInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GenerateConstructorDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_DECLARE_TR_FUNCTIONS(GenerateConstructorDialog)
|
||||||
|
public:
|
||||||
|
GenerateConstructorDialog(const ConstructorMemberCandidates &candidates)
|
||||||
|
: QDialog()
|
||||||
|
, m_candidates(candidates)
|
||||||
|
{
|
||||||
|
setWindowTitle(tr("Constructor"));
|
||||||
|
const auto model = new Utils::TreeModel<Utils::TreeItem, ConstructorCandidateTreeItem>(this);
|
||||||
|
model->setHeader(QStringList({
|
||||||
|
tr("Initialize in Constructor"),
|
||||||
|
tr("Member Name"),
|
||||||
|
tr("Parameter Name"),
|
||||||
|
}));
|
||||||
|
for (auto &candidate : m_candidates)
|
||||||
|
model->rootItem()->appendChild(new ConstructorCandidateTreeItem(&candidate));
|
||||||
|
const auto view = new QTreeView(this);
|
||||||
|
view->setModel(model);
|
||||||
|
int optimalWidth = 0;
|
||||||
|
for (int i = 0; i < model->columnCount(QModelIndex{}); ++i) {
|
||||||
|
view->resizeColumnToContents(i);
|
||||||
|
optimalWidth += view->columnWidth(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||||
|
connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||||
|
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||||
|
|
||||||
|
// setup select all/none checkbox
|
||||||
|
QCheckBox *const checkBox = new QCheckBox(tr("Initialize all members"));
|
||||||
|
checkBox->setChecked(true);
|
||||||
|
connect(checkBox, &QCheckBox::stateChanged, [model](int state) {
|
||||||
|
if (state != Qt::PartiallyChecked) {
|
||||||
|
for (int i = 0; i < model->rowCount(); ++i)
|
||||||
|
model->setData(model->index(i, ConstructorCandidateTreeItem::ShouldInitColumn),
|
||||||
|
state,
|
||||||
|
Qt::CheckStateRole);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connect(checkBox, &QCheckBox::clicked, this, [checkBox] {
|
||||||
|
if (checkBox->checkState() == Qt::PartiallyChecked)
|
||||||
|
checkBox->setCheckState(Qt::Checked);
|
||||||
|
});
|
||||||
|
connect(model, &QAbstractItemModel::dataChanged, this, [this, checkBox] {
|
||||||
|
const auto selectedCount = Utils::count(m_candidates,
|
||||||
|
[](const ConstructorMemberInfo &mi) {
|
||||||
|
return mi.init;
|
||||||
|
});
|
||||||
|
|
||||||
|
const auto state = [this, selectedCount]() {
|
||||||
|
if (selectedCount == 0)
|
||||||
|
return Qt::Unchecked;
|
||||||
|
if (static_cast<int>(m_candidates.size()) == selectedCount)
|
||||||
|
return Qt::Checked;
|
||||||
|
return Qt::PartiallyChecked;
|
||||||
|
}();
|
||||||
|
checkBox->setCheckState(state);
|
||||||
|
});
|
||||||
|
|
||||||
|
using A = InsertionPointLocator::AccessSpec;
|
||||||
|
auto accessCombo = new QComboBox;
|
||||||
|
connect(accessCombo, qOverload<int>(&QComboBox::currentIndexChanged), [this, accessCombo]() {
|
||||||
|
const auto data = accessCombo->currentData();
|
||||||
|
m_accessSpec = static_cast<A>(data.toInt());
|
||||||
|
});
|
||||||
|
for (auto a : {A::Public, A::Protected, A::Private})
|
||||||
|
accessCombo->addItem(InsertionPointLocator::accessSpecToString(a), a);
|
||||||
|
const auto row = new QHBoxLayout();
|
||||||
|
row->addWidget(new QLabel(tr("Access") + ":"));
|
||||||
|
row->addWidget(accessCombo);
|
||||||
|
row->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum));
|
||||||
|
|
||||||
|
const auto mainLayout = new QVBoxLayout(this);
|
||||||
|
mainLayout->addWidget(new QLabel(tr("Select the members "
|
||||||
|
"to be initialized in the constructor.")));
|
||||||
|
mainLayout->addLayout(row);
|
||||||
|
mainLayout->addWidget(checkBox);
|
||||||
|
mainLayout->addWidget(view);
|
||||||
|
mainLayout->addWidget(buttonBox);
|
||||||
|
int left, right;
|
||||||
|
mainLayout->getContentsMargins(&left, nullptr, &right, nullptr);
|
||||||
|
optimalWidth += left + right;
|
||||||
|
resize(optimalWidth, mainLayout->sizeHint().height());
|
||||||
|
}
|
||||||
|
|
||||||
|
ConstructorMemberCandidates candidates() const { return m_candidates; }
|
||||||
|
InsertionPointLocator::AccessSpec accessSpec() const { return m_accessSpec; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
ConstructorMemberCandidates m_candidates;
|
||||||
|
InsertionPointLocator::AccessSpec m_accessSpec;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GenerateConstructorOperation : public CppQuickFixOperation
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GenerateConstructorOperation(const CppQuickFixInterface &interface)
|
||||||
|
: CppQuickFixOperation(interface)
|
||||||
|
{
|
||||||
|
setDescription(CppQuickFixFactory::tr("Generate Constructor"));
|
||||||
|
|
||||||
|
m_classAST = astForClassOperations(interface);
|
||||||
|
if (!m_classAST)
|
||||||
|
return;
|
||||||
|
Class *const theClass = m_classAST->symbol;
|
||||||
|
if (!theClass)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Go through all members and find member variable declarations
|
||||||
|
for (auto it = theClass->memberBegin(); it != theClass->memberEnd(); ++it) {
|
||||||
|
Symbol *const s = *it;
|
||||||
|
if (!s->identifier() || !s->type() || s->type().isTypedef())
|
||||||
|
continue;
|
||||||
|
if ((s->isDeclaration() && s->type()->asFunctionType()) || s->asFunction())
|
||||||
|
continue;
|
||||||
|
if (s->isDeclaration() && (s->isPrivate() || s->isProtected()) && !s->isStatic()) {
|
||||||
|
const auto name = QString::fromUtf8(s->identifier()->chars(),
|
||||||
|
s->identifier()->size());
|
||||||
|
m_candidates.emplace_back(name, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConstructorMemberCandidates &candidates() { return m_candidates; }
|
||||||
|
bool isApplicable() const { return !m_candidates.empty(); }
|
||||||
|
|
||||||
|
void setTest(bool isTest = true) { m_test = isTest; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void perform() override
|
||||||
|
{
|
||||||
|
InsertionPointLocator::AccessSpec accessSpec = InsertionPointLocator::Public;
|
||||||
|
if (!m_test) {
|
||||||
|
GenerateConstructorDialog dlg(m_candidates);
|
||||||
|
if (dlg.exec() == QDialog::Rejected)
|
||||||
|
return;
|
||||||
|
m_candidates = dlg.candidates();
|
||||||
|
accessSpec = dlg.accessSpec();
|
||||||
|
}
|
||||||
|
if (m_candidates.empty())
|
||||||
|
return;
|
||||||
|
struct GenerateConstructorRefactoringHelper : public GetterSetterRefactoringHelper
|
||||||
|
{
|
||||||
|
const ClassSpecifierAST *m_classAST;
|
||||||
|
InsertionPointLocator::AccessSpec m_accessSpec;
|
||||||
|
GenerateConstructorRefactoringHelper(CppQuickFixOperation *operation,
|
||||||
|
const QString &fileName,
|
||||||
|
Class *clazz,
|
||||||
|
const ClassSpecifierAST *classAST,
|
||||||
|
InsertionPointLocator::AccessSpec accessSpec)
|
||||||
|
: GetterSetterRefactoringHelper(operation, fileName, clazz)
|
||||||
|
, m_classAST(classAST)
|
||||||
|
, m_accessSpec(accessSpec)
|
||||||
|
{}
|
||||||
|
void generateConstructor(ConstructorMemberCandidates candidates)
|
||||||
|
{
|
||||||
|
ConstructorMemberCandidates members = Utils::filtered(candidates,
|
||||||
|
[](const auto &mi) {
|
||||||
|
return mi.init;
|
||||||
|
});
|
||||||
|
auto constructorLocation = m_settings->determineSetterLocation(members.size());
|
||||||
|
if (constructorLocation == CppQuickFixSettings::FunctionLocation::CppFile
|
||||||
|
&& !hasSourceFile())
|
||||||
|
constructorLocation = CppQuickFixSettings::FunctionLocation::OutsideClass;
|
||||||
|
|
||||||
|
Overview overview = CppCodeStyleSettings::currentProjectCodeStyleOverview();
|
||||||
|
overview.showTemplateParameters = true;
|
||||||
|
|
||||||
|
InsertionLocation implLoc;
|
||||||
|
QString implCode;
|
||||||
|
CppRefactoringFilePtr implFile;
|
||||||
|
QString className = overview.prettyName(m_class->name());
|
||||||
|
QStringList insertedNamespaces;
|
||||||
|
if (constructorLocation == CppQuickFixSettings::FunctionLocation::CppFile) {
|
||||||
|
implLoc = sourceLocationFor(m_class, &insertedNamespaces);
|
||||||
|
implFile = m_sourceFile;
|
||||||
|
QString clazz = overview.prettyName(m_class->name());
|
||||||
|
if (m_settings->rewriteTypesinCppFile())
|
||||||
|
implCode = symbolAt(m_class, m_sourceFile, implLoc);
|
||||||
|
else
|
||||||
|
implCode = className;
|
||||||
|
implCode += "::" + className + "(";
|
||||||
|
} else if (constructorLocation
|
||||||
|
== CppQuickFixSettings::FunctionLocation::OutsideClass) {
|
||||||
|
implLoc = insertLocationForMethodDefinition(m_class,
|
||||||
|
false,
|
||||||
|
NamespaceHandling::Ignore,
|
||||||
|
m_changes,
|
||||||
|
m_headerFile->fileName(),
|
||||||
|
&insertedNamespaces);
|
||||||
|
implFile = m_headerFile;
|
||||||
|
implCode = symbolAt(m_class, m_headerFile, implLoc);
|
||||||
|
implCode += "::" + className + "(";
|
||||||
|
}
|
||||||
|
|
||||||
|
QString inClassDeclaration = overview.prettyName(m_class->name()) + "(";
|
||||||
|
QString constructorBody = members.empty() ? ") {}" : ") : ";
|
||||||
|
for (auto &member : members) {
|
||||||
|
bool customValueType;
|
||||||
|
if (isValueType(member.symbol, &customValueType))
|
||||||
|
member.type.setConst(false);
|
||||||
|
else
|
||||||
|
member.type = makeConstRef(member.type);
|
||||||
|
|
||||||
|
inClassDeclaration += overview.prettyType(member.type, member.parameterName);
|
||||||
|
inClassDeclaration += ", ";
|
||||||
|
QString param = member.parameterName;
|
||||||
|
if (customValueType)
|
||||||
|
param = "std::move(" + member.parameterName + ')';
|
||||||
|
constructorBody += member.memberVariableName + '(' + param + "),\n";
|
||||||
|
if (implFile) {
|
||||||
|
FullySpecifiedType type = typeAt(member.type,
|
||||||
|
m_class,
|
||||||
|
implFile,
|
||||||
|
implLoc,
|
||||||
|
insertedNamespaces);
|
||||||
|
implCode += overview.prettyType(type, member.parameterName) + ", ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!members.empty()) {
|
||||||
|
inClassDeclaration.resize(inClassDeclaration.length() - 2);
|
||||||
|
constructorBody.remove(constructorBody.length() - 2, 1); // ..),\n => ..)\n
|
||||||
|
constructorBody += "{}";
|
||||||
|
if (!implCode.isEmpty())
|
||||||
|
implCode.resize(implCode.length() - 2);
|
||||||
|
}
|
||||||
|
implCode += constructorBody;
|
||||||
|
|
||||||
|
if (constructorLocation == CppQuickFixSettings::FunctionLocation::InsideClass)
|
||||||
|
inClassDeclaration += constructorBody;
|
||||||
|
else
|
||||||
|
inClassDeclaration += QLatin1String(");");
|
||||||
|
|
||||||
|
TranslationUnit *tu = m_headerFile->cppDocument()->translationUnit();
|
||||||
|
insertAndIndent(m_headerFile,
|
||||||
|
m_locator.constructorDeclarationInClass(tu,
|
||||||
|
m_classAST,
|
||||||
|
m_accessSpec,
|
||||||
|
members.size()),
|
||||||
|
inClassDeclaration);
|
||||||
|
|
||||||
|
if (constructorLocation == CppQuickFixSettings::FunctionLocation::CppFile) {
|
||||||
|
addSourceFileCode(implCode);
|
||||||
|
} else if (constructorLocation
|
||||||
|
== CppQuickFixSettings::FunctionLocation::OutsideClass) {
|
||||||
|
if (isHeaderHeaderFile())
|
||||||
|
implCode.prepend("inline ");
|
||||||
|
insertAndIndent(m_headerFile, implLoc, implCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
GenerateConstructorRefactoringHelper helper(this,
|
||||||
|
currentFile()->fileName(),
|
||||||
|
m_classAST->symbol,
|
||||||
|
m_classAST,
|
||||||
|
accessSpec);
|
||||||
|
helper.generateConstructor(m_candidates);
|
||||||
|
helper.applyChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
ConstructorMemberCandidates m_candidates;
|
||||||
|
const ClassSpecifierAST *m_classAST = nullptr;
|
||||||
|
bool m_test = false;
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
void GenerateConstructor::match(const CppQuickFixInterface &interface, QuickFixOperations &result)
|
||||||
|
{
|
||||||
|
const auto op = QSharedPointer<GenerateConstructorOperation>::create(interface);
|
||||||
|
if (!op->isApplicable())
|
||||||
|
return;
|
||||||
|
op->setTest(m_test);
|
||||||
|
result << op;
|
||||||
|
}
|
||||||
|
|
||||||
void createCppQuickFixes()
|
void createCppQuickFixes()
|
||||||
{
|
{
|
||||||
new AddIncludeForUndefinedIdentifier;
|
new AddIncludeForUndefinedIdentifier;
|
||||||
@@ -8406,6 +8806,7 @@ void createCppQuickFixes()
|
|||||||
new ExtraRefactoringOperations;
|
new ExtraRefactoringOperations;
|
||||||
|
|
||||||
new RemoveUsingNamespace;
|
new RemoveUsingNamespace;
|
||||||
|
new GenerateConstructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
void destroyCppQuickFixes()
|
void destroyCppQuickFixes()
|
||||||
|
|||||||
@@ -590,5 +590,20 @@ public:
|
|||||||
void match(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
|
void match(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Generate constructor
|
||||||
|
*/
|
||||||
|
class GenerateConstructor : public CppQuickFixFactory
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
void setTest() { m_test = true; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void match(const CppQuickFixInterface &interface,
|
||||||
|
TextEditor::QuickFixOperations &result) override;
|
||||||
|
|
||||||
|
bool m_test = false;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace CppEditor
|
} // namespace CppEditor
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ static int ordering(InsertionPointLocator::AccessSpec xsSpec)
|
|||||||
struct AccessRange
|
struct AccessRange
|
||||||
{
|
{
|
||||||
int start = 0;
|
int start = 0;
|
||||||
|
int beforeStart = 0;
|
||||||
unsigned end = 0;
|
unsigned end = 0;
|
||||||
InsertionPointLocator::AccessSpec xsSpec = InsertionPointLocator::Invalid;
|
InsertionPointLocator::AccessSpec xsSpec = InsertionPointLocator::Invalid;
|
||||||
unsigned colonToken = 0;
|
unsigned colonToken = 0;
|
||||||
@@ -81,16 +82,14 @@ struct AccessRange
|
|||||||
class FindInClass: public ASTVisitor
|
class FindInClass: public ASTVisitor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FindInClass(const Document::Ptr &doc, const Class *clazz, InsertionPointLocator::AccessSpec xsSpec)
|
FindInClass(TranslationUnit *tu, const Class *clazz)
|
||||||
: ASTVisitor(doc->translationUnit())
|
: ASTVisitor(tu)
|
||||||
, _doc(doc)
|
|
||||||
, _clazz(clazz)
|
, _clazz(clazz)
|
||||||
, _xsSpec(xsSpec)
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
InsertionLocation operator()()
|
ClassSpecifierAST *operator()()
|
||||||
{
|
{
|
||||||
_result = InsertionLocation();
|
_result = nullptr;
|
||||||
|
|
||||||
AST *ast = translationUnit()->ast();
|
AST *ast = translationUnit()->ast();
|
||||||
accept(ast);
|
accept(ast);
|
||||||
@@ -107,146 +106,120 @@ protected:
|
|||||||
return true;
|
return true;
|
||||||
if (!ast->symbol || !ast->symbol->match(_clazz))
|
if (!ast->symbol || !ast->symbol->match(_clazz))
|
||||||
return true;
|
return true;
|
||||||
|
_result = ast;
|
||||||
QList<AccessRange> ranges = collectAccessRanges(
|
|
||||||
ast->member_specifier_list,
|
|
||||||
tokenKind(ast->classkey_token) == T_CLASS ? InsertionPointLocator::Private : InsertionPointLocator::Public,
|
|
||||||
ast->lbrace_token,
|
|
||||||
ast->rbrace_token);
|
|
||||||
|
|
||||||
unsigned beforeToken = 0;
|
|
||||||
bool needsLeadingEmptyLine = false;
|
|
||||||
bool needsPrefix = false;
|
|
||||||
bool needsSuffix = false;
|
|
||||||
findMatch(ranges, _xsSpec, beforeToken, needsLeadingEmptyLine, needsPrefix, needsSuffix);
|
|
||||||
|
|
||||||
int line = 0, column = 0;
|
|
||||||
getTokenStartPosition(beforeToken, &line, &column);
|
|
||||||
|
|
||||||
QString prefix;
|
|
||||||
if (needsLeadingEmptyLine)
|
|
||||||
prefix += QLatin1String("\n");
|
|
||||||
if (needsPrefix)
|
|
||||||
prefix += InsertionPointLocator::accessSpecToString(_xsSpec) + QLatin1String(":\n");
|
|
||||||
|
|
||||||
QString suffix;
|
|
||||||
if (needsSuffix)
|
|
||||||
suffix = QLatin1Char('\n');
|
|
||||||
|
|
||||||
_result = InsertionLocation(_doc->fileName(), prefix, suffix,
|
|
||||||
line, column);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void findMatch(const QList<AccessRange> &ranges,
|
|
||||||
InsertionPointLocator::AccessSpec xsSpec,
|
|
||||||
unsigned &beforeToken,
|
|
||||||
bool &needsLeadingEmptyLine,
|
|
||||||
bool &needsPrefix,
|
|
||||||
bool &needsSuffix)
|
|
||||||
{
|
|
||||||
QTC_ASSERT(!ranges.isEmpty(), return);
|
|
||||||
const int lastIndex = ranges.size() - 1;
|
|
||||||
|
|
||||||
needsLeadingEmptyLine = false;
|
|
||||||
|
|
||||||
// try an exact match, and ignore the first (default) access spec:
|
|
||||||
for (int i = lastIndex; i > 0; --i) {
|
|
||||||
const AccessRange &range = ranges.at(i);
|
|
||||||
if (range.xsSpec == xsSpec) {
|
|
||||||
beforeToken = range.end;
|
|
||||||
needsPrefix = false;
|
|
||||||
needsSuffix = (i != lastIndex);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to find a fitting access spec to insert XXX:
|
|
||||||
for (int i = lastIndex; i > 0; --i) {
|
|
||||||
const AccessRange ¤t = ranges.at(i);
|
|
||||||
|
|
||||||
if (ordering(xsSpec) > ordering(current.xsSpec)) {
|
|
||||||
beforeToken = current.end;
|
|
||||||
needsPrefix = true;
|
|
||||||
needsSuffix = (i != lastIndex);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise:
|
|
||||||
beforeToken = ranges.first().end;
|
|
||||||
needsLeadingEmptyLine = !ranges.first().isEmpty();
|
|
||||||
needsPrefix = true;
|
|
||||||
needsSuffix = (ranges.size() != 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<AccessRange> collectAccessRanges(DeclarationListAST *decls,
|
|
||||||
InsertionPointLocator::AccessSpec initialXs,
|
|
||||||
int firstRangeStart,
|
|
||||||
int lastRangeEnd) const
|
|
||||||
{
|
|
||||||
QList<AccessRange> ranges;
|
|
||||||
ranges.append(AccessRange(firstRangeStart, lastRangeEnd, initialXs, 0));
|
|
||||||
|
|
||||||
for (DeclarationListAST *iter = decls; iter; iter = iter->next) {
|
|
||||||
DeclarationAST *decl = iter->value;
|
|
||||||
|
|
||||||
if (AccessDeclarationAST *xsDecl = decl->asAccessDeclaration()) {
|
|
||||||
const unsigned token = xsDecl->access_specifier_token;
|
|
||||||
InsertionPointLocator::AccessSpec newXsSpec = initialXs;
|
|
||||||
bool isSlot = xsDecl->slots_token
|
|
||||||
&& tokenKind(xsDecl->slots_token) == T_Q_SLOTS;
|
|
||||||
|
|
||||||
switch (tokenKind(token)) {
|
|
||||||
case T_PUBLIC:
|
|
||||||
newXsSpec = isSlot ? InsertionPointLocator::PublicSlot
|
|
||||||
: InsertionPointLocator::Public;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case T_PROTECTED:
|
|
||||||
newXsSpec = isSlot ? InsertionPointLocator::ProtectedSlot
|
|
||||||
: InsertionPointLocator::Protected;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case T_PRIVATE:
|
|
||||||
newXsSpec = isSlot ? InsertionPointLocator::PrivateSlot
|
|
||||||
: InsertionPointLocator::Private;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case T_Q_SIGNALS:
|
|
||||||
newXsSpec = InsertionPointLocator::Signals;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case T_Q_SLOTS: {
|
|
||||||
newXsSpec = (InsertionPointLocator::AccessSpec)
|
|
||||||
(ranges.last().xsSpec | InsertionPointLocator::SlotBit);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newXsSpec != ranges.last().xsSpec || ranges.size() == 1) {
|
|
||||||
ranges.last().end = token;
|
|
||||||
AccessRange r(token, lastRangeEnd, newXsSpec, xsDecl->colon_token);
|
|
||||||
ranges.append(r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ranges.last().end = lastRangeEnd;
|
|
||||||
return ranges;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Document::Ptr _doc;
|
|
||||||
const Class *_clazz;
|
const Class *_clazz;
|
||||||
InsertionPointLocator::AccessSpec _xsSpec;
|
ClassSpecifierAST *_result;
|
||||||
|
|
||||||
InsertionLocation _result;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void findMatch(const QList<AccessRange> &ranges,
|
||||||
|
InsertionPointLocator::AccessSpec xsSpec,
|
||||||
|
InsertionPointLocator::Position positionInAccessSpec,
|
||||||
|
unsigned &beforeToken,
|
||||||
|
bool &needsLeadingEmptyLine,
|
||||||
|
bool &needsPrefix,
|
||||||
|
bool &needsSuffix)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(!ranges.isEmpty(), return );
|
||||||
|
const int lastIndex = ranges.size() - 1;
|
||||||
|
const bool atEnd = positionInAccessSpec == InsertionPointLocator::AccessSpecEnd;
|
||||||
|
needsLeadingEmptyLine = false;
|
||||||
|
|
||||||
|
// try an exact match, and ignore the first (default) access spec:
|
||||||
|
for (int i = lastIndex; i > 0; --i) {
|
||||||
|
const AccessRange &range = ranges.at(i);
|
||||||
|
if (range.xsSpec == xsSpec) {
|
||||||
|
beforeToken = atEnd ? range.end : range.beforeStart;
|
||||||
|
needsLeadingEmptyLine = !atEnd;
|
||||||
|
needsPrefix = false;
|
||||||
|
needsSuffix = (i != lastIndex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to find a fitting access spec to insert XXX:
|
||||||
|
for (int i = lastIndex; i > 0; --i) {
|
||||||
|
const AccessRange ¤t = ranges.at(i);
|
||||||
|
|
||||||
|
if (ordering(xsSpec) > ordering(current.xsSpec)) {
|
||||||
|
beforeToken = atEnd ? current.end : current.end - 1;
|
||||||
|
needsLeadingEmptyLine = !atEnd;
|
||||||
|
needsPrefix = true;
|
||||||
|
needsSuffix = (i != lastIndex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise:
|
||||||
|
beforeToken = atEnd ? ranges.first().end : ranges.first().end - 1;
|
||||||
|
needsLeadingEmptyLine = !ranges.first().isEmpty();
|
||||||
|
needsPrefix = true;
|
||||||
|
needsSuffix = (ranges.size() != 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<AccessRange> collectAccessRanges(const CPlusPlus::TranslationUnit *tu,
|
||||||
|
DeclarationListAST *decls,
|
||||||
|
InsertionPointLocator::AccessSpec initialXs,
|
||||||
|
int firstRangeStart,
|
||||||
|
int lastRangeEnd)
|
||||||
|
{
|
||||||
|
QList<AccessRange> ranges;
|
||||||
|
ranges.append(AccessRange(firstRangeStart, lastRangeEnd, initialXs, 0));
|
||||||
|
|
||||||
|
for (DeclarationListAST *iter = decls; iter; iter = iter->next) {
|
||||||
|
DeclarationAST *decl = iter->value;
|
||||||
|
|
||||||
|
if (AccessDeclarationAST *xsDecl = decl->asAccessDeclaration()) {
|
||||||
|
const unsigned token = xsDecl->access_specifier_token;
|
||||||
|
InsertionPointLocator::AccessSpec newXsSpec = initialXs;
|
||||||
|
bool isSlot = xsDecl->slots_token && tu->tokenKind(xsDecl->slots_token) == T_Q_SLOTS;
|
||||||
|
|
||||||
|
switch (tu->tokenKind(token)) {
|
||||||
|
case T_PUBLIC:
|
||||||
|
newXsSpec = isSlot ? InsertionPointLocator::PublicSlot
|
||||||
|
: InsertionPointLocator::Public;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case T_PROTECTED:
|
||||||
|
newXsSpec = isSlot ? InsertionPointLocator::ProtectedSlot
|
||||||
|
: InsertionPointLocator::Protected;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case T_PRIVATE:
|
||||||
|
newXsSpec = isSlot ? InsertionPointLocator::PrivateSlot
|
||||||
|
: InsertionPointLocator::Private;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case T_Q_SIGNALS:
|
||||||
|
newXsSpec = InsertionPointLocator::Signals;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case T_Q_SLOTS: {
|
||||||
|
newXsSpec = (InsertionPointLocator::AccessSpec)(ranges.last().xsSpec
|
||||||
|
| InsertionPointLocator::SlotBit);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newXsSpec != ranges.last().xsSpec || ranges.size() == 1) {
|
||||||
|
ranges.last().end = token;
|
||||||
|
AccessRange r(token, lastRangeEnd, newXsSpec, xsDecl->colon_token);
|
||||||
|
r.beforeStart = xsDecl->lastToken() - 1;
|
||||||
|
ranges.append(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ranges.last().end = lastRangeEnd;
|
||||||
|
return ranges;
|
||||||
|
}
|
||||||
|
|
||||||
} // end of anonymous namespace
|
} // end of anonymous namespace
|
||||||
|
|
||||||
InsertionLocation::InsertionLocation() = default;
|
InsertionLocation::InsertionLocation() = default;
|
||||||
@@ -301,13 +274,118 @@ InsertionLocation InsertionPointLocator::methodDeclarationInClass(
|
|||||||
{
|
{
|
||||||
const Document::Ptr doc = m_refactoringChanges.file(fileName)->cppDocument();
|
const Document::Ptr doc = m_refactoringChanges.file(fileName)->cppDocument();
|
||||||
if (doc) {
|
if (doc) {
|
||||||
FindInClass find(doc, clazz, xsSpec);
|
FindInClass find(doc->translationUnit(), clazz);
|
||||||
return find();
|
ClassSpecifierAST *classAST = find();
|
||||||
|
return methodDeclarationInClass(doc->translationUnit(), classAST, xsSpec);
|
||||||
} else {
|
} else {
|
||||||
return InsertionLocation();
|
return InsertionLocation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InsertionLocation InsertionPointLocator::methodDeclarationInClass(
|
||||||
|
const TranslationUnit *tu,
|
||||||
|
const ClassSpecifierAST *clazz,
|
||||||
|
InsertionPointLocator::AccessSpec xsSpec,
|
||||||
|
Position pos) const
|
||||||
|
{
|
||||||
|
if (!clazz)
|
||||||
|
return {};
|
||||||
|
QList<AccessRange> ranges = collectAccessRanges(tu,
|
||||||
|
clazz->member_specifier_list,
|
||||||
|
tu->tokenKind(clazz->classkey_token) == T_CLASS
|
||||||
|
? InsertionPointLocator::Private
|
||||||
|
: InsertionPointLocator::Public,
|
||||||
|
clazz->lbrace_token,
|
||||||
|
clazz->rbrace_token);
|
||||||
|
|
||||||
|
unsigned beforeToken = 0;
|
||||||
|
bool needsLeadingEmptyLine = false;
|
||||||
|
bool needsPrefix = false;
|
||||||
|
bool needsSuffix = false;
|
||||||
|
findMatch(ranges, xsSpec, pos, beforeToken, needsLeadingEmptyLine, needsPrefix, needsSuffix);
|
||||||
|
|
||||||
|
int line = 0, column = 0;
|
||||||
|
if (pos == InsertionPointLocator::AccessSpecEnd)
|
||||||
|
tu->getTokenStartPosition(beforeToken, &line, &column);
|
||||||
|
else
|
||||||
|
tu->getTokenEndPosition(beforeToken, &line, &column);
|
||||||
|
|
||||||
|
QString prefix;
|
||||||
|
if (needsLeadingEmptyLine)
|
||||||
|
prefix += QLatin1String("\n");
|
||||||
|
if (needsPrefix)
|
||||||
|
prefix += InsertionPointLocator::accessSpecToString(xsSpec) + QLatin1String(":\n");
|
||||||
|
|
||||||
|
QString suffix;
|
||||||
|
if (needsSuffix)
|
||||||
|
suffix = QLatin1Char('\n');
|
||||||
|
const QString fileName = QString::fromUtf8(tu->fileName(), tu->fileNameLength());
|
||||||
|
return InsertionLocation(fileName, prefix, suffix, line, column);
|
||||||
|
}
|
||||||
|
|
||||||
|
InsertionPointLocator::AccessSpec symbolsAccessSpec(Symbol *symbol)
|
||||||
|
{
|
||||||
|
if (symbol->isPrivate())
|
||||||
|
return InsertionPointLocator::Private;
|
||||||
|
if (symbol->isProtected())
|
||||||
|
return InsertionPointLocator::Protected;
|
||||||
|
if (symbol->isPublic())
|
||||||
|
return InsertionPointLocator::Public;
|
||||||
|
return InsertionPointLocator::Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
InsertionLocation InsertionPointLocator::constructorDeclarationInClass(
|
||||||
|
const CPlusPlus::TranslationUnit *tu,
|
||||||
|
const ClassSpecifierAST *clazz,
|
||||||
|
InsertionPointLocator::AccessSpec xsSpec,
|
||||||
|
int constructorArgumentCount) const
|
||||||
|
{
|
||||||
|
std::map<int, std::pair<DeclarationAST *, DeclarationAST *>> constructors;
|
||||||
|
for (DeclarationAST *rootDecl : clazz->member_specifier_list) {
|
||||||
|
if (SimpleDeclarationAST *ast = rootDecl->asSimpleDeclaration()) {
|
||||||
|
if (!ast->symbols)
|
||||||
|
continue;
|
||||||
|
if (symbolsAccessSpec(ast->symbols->value) != xsSpec)
|
||||||
|
continue;
|
||||||
|
if (ast->symbols->value->name() != clazz->name->name)
|
||||||
|
continue;
|
||||||
|
for (DeclaratorAST *d : ast->declarator_list) {
|
||||||
|
for (PostfixDeclaratorAST *decl : d->postfix_declarator_list) {
|
||||||
|
if (FunctionDeclaratorAST *func = decl->asFunctionDeclarator()) {
|
||||||
|
int params = 0;
|
||||||
|
if (func->parameter_declaration_clause) {
|
||||||
|
params = size(
|
||||||
|
func->parameter_declaration_clause->parameter_declaration_list);
|
||||||
|
}
|
||||||
|
auto &entry = constructors[params];
|
||||||
|
if (!entry.first)
|
||||||
|
entry.first = rootDecl;
|
||||||
|
entry.second = rootDecl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (constructors.empty())
|
||||||
|
return methodDeclarationInClass(tu, clazz, xsSpec, AccessSpecBegin);
|
||||||
|
|
||||||
|
auto iter = constructors.lower_bound(constructorArgumentCount);
|
||||||
|
if (iter == constructors.end()) {
|
||||||
|
// we have a constructor with x arguments but there are only ones with < x arguments
|
||||||
|
--iter; // select greatest one (in terms of argument count)
|
||||||
|
}
|
||||||
|
const QString fileName = QString::fromUtf8(tu->fileName(), tu->fileNameLength());
|
||||||
|
int line, column;
|
||||||
|
if (iter->first <= constructorArgumentCount) {
|
||||||
|
tu->getTokenEndPosition(iter->second.second->lastToken() - 1, &line, &column);
|
||||||
|
return InsertionLocation(fileName, "\n", "", line, column);
|
||||||
|
}
|
||||||
|
// before iter
|
||||||
|
// end pos of firstToken-1 instead of start pos of firstToken to skip leading commend
|
||||||
|
tu->getTokenEndPosition(iter->second.first->firstToken() - 1, &line, &column);
|
||||||
|
return InsertionLocation(fileName, "\n", "", line, column);
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
template <class Key, class Value>
|
template <class Key, class Value>
|
||||||
class HighestValue
|
class HighestValue
|
||||||
|
|||||||
@@ -86,6 +86,11 @@ public:
|
|||||||
};
|
};
|
||||||
static QString accessSpecToString(InsertionPointLocator::AccessSpec xsSpec);
|
static QString accessSpecToString(InsertionPointLocator::AccessSpec xsSpec);
|
||||||
|
|
||||||
|
enum Position {
|
||||||
|
AccessSpecBegin,
|
||||||
|
AccessSpecEnd,
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit InsertionPointLocator(const CppRefactoringChanges &refactoringChanges);
|
explicit InsertionPointLocator(const CppRefactoringChanges &refactoringChanges);
|
||||||
|
|
||||||
@@ -93,6 +98,16 @@ public:
|
|||||||
const CPlusPlus::Class *clazz,
|
const CPlusPlus::Class *clazz,
|
||||||
AccessSpec xsSpec) const;
|
AccessSpec xsSpec) const;
|
||||||
|
|
||||||
|
InsertionLocation methodDeclarationInClass(const CPlusPlus::TranslationUnit *tu,
|
||||||
|
const CPlusPlus::ClassSpecifierAST *clazz,
|
||||||
|
AccessSpec xsSpec,
|
||||||
|
Position positionInAccessSpec = AccessSpecEnd) const;
|
||||||
|
|
||||||
|
InsertionLocation constructorDeclarationInClass(const CPlusPlus::TranslationUnit *tu,
|
||||||
|
const CPlusPlus::ClassSpecifierAST *clazz,
|
||||||
|
AccessSpec xsSpec,
|
||||||
|
int constructorArgumentCount) const;
|
||||||
|
|
||||||
const QList<InsertionLocation> methodDefinition(
|
const QList<InsertionLocation> methodDefinition(
|
||||||
CPlusPlus::Symbol *declaration,
|
CPlusPlus::Symbol *declaration,
|
||||||
bool useSymbolFinder = true,
|
bool useSymbolFinder = true,
|
||||||
|
|||||||
Reference in New Issue
Block a user