CppEditor: Add QuickFix to generate a constructor

Change-Id: Iba2ce3bfa1a1d1a325626a21f46b485d12cbb060
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
Leander Schulten
2021-01-04 04:41:36 +01:00
parent d03f723060
commit c960f279e6
7 changed files with 1057 additions and 222 deletions

View File

@@ -75,7 +75,50 @@ 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
{ {

View File

@@ -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();

View File

@@ -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

View File

@@ -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_class(clazz) , m_headerFile(m_changes.file(fileName))
{ , m_sourceFile([&] {
m_headerFile = m_changes.file(fileName);
QString cppFileName = correspondingHeaderOrSource(fileName, &m_isHeaderHeaderFile); QString cppFileName = correspondingHeaderOrSource(fileName, &m_isHeaderHeaderFile);
if (!m_isHeaderHeaderFile || !QFile::exists(cppFileName)) { if (!m_isHeaderHeaderFile || !QFile::exists(cppFileName)) {
// there is no "source" file // there is no "source" file
m_sourceFile = m_headerFile; return m_headerFile;
} else { } else {
m_sourceFile = m_changes.file(cppFileName); return m_changes.file(cppFileName);
}
} }
}())
, m_class(clazz)
{}
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::CreateMissing
: NamespaceHandling::Ignore, : NamespaceHandling::Ignore,
m_changes, m_changes,
m_sourceFile->fileName()); 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()

View File

@@ -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

View File

@@ -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,39 +106,18 @@ 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, private:
const Class *_clazz;
ClassSpecifierAST *_result;
};
void findMatch(const QList<AccessRange> &ranges,
InsertionPointLocator::AccessSpec xsSpec, InsertionPointLocator::AccessSpec xsSpec,
InsertionPointLocator::Position positionInAccessSpec,
unsigned &beforeToken, unsigned &beforeToken,
bool &needsLeadingEmptyLine, bool &needsLeadingEmptyLine,
bool &needsPrefix, bool &needsPrefix,
@@ -147,14 +125,15 @@ protected:
{ {
QTC_ASSERT(!ranges.isEmpty(), return ); QTC_ASSERT(!ranges.isEmpty(), return );
const int lastIndex = ranges.size() - 1; const int lastIndex = ranges.size() - 1;
const bool atEnd = positionInAccessSpec == InsertionPointLocator::AccessSpecEnd;
needsLeadingEmptyLine = false; needsLeadingEmptyLine = false;
// try an exact match, and ignore the first (default) access spec: // try an exact match, and ignore the first (default) access spec:
for (int i = lastIndex; i > 0; --i) { for (int i = lastIndex; i > 0; --i) {
const AccessRange &range = ranges.at(i); const AccessRange &range = ranges.at(i);
if (range.xsSpec == xsSpec) { if (range.xsSpec == xsSpec) {
beforeToken = range.end; beforeToken = atEnd ? range.end : range.beforeStart;
needsLeadingEmptyLine = !atEnd;
needsPrefix = false; needsPrefix = false;
needsSuffix = (i != lastIndex); needsSuffix = (i != lastIndex);
return; return;
@@ -166,7 +145,8 @@ protected:
const AccessRange &current = ranges.at(i); const AccessRange &current = ranges.at(i);
if (ordering(xsSpec) > ordering(current.xsSpec)) { if (ordering(xsSpec) > ordering(current.xsSpec)) {
beforeToken = current.end; beforeToken = atEnd ? current.end : current.end - 1;
needsLeadingEmptyLine = !atEnd;
needsPrefix = true; needsPrefix = true;
needsSuffix = (i != lastIndex); needsSuffix = (i != lastIndex);
return; return;
@@ -174,16 +154,17 @@ protected:
} }
// otherwise: // otherwise:
beforeToken = ranges.first().end; beforeToken = atEnd ? ranges.first().end : ranges.first().end - 1;
needsLeadingEmptyLine = !ranges.first().isEmpty(); needsLeadingEmptyLine = !ranges.first().isEmpty();
needsPrefix = true; needsPrefix = true;
needsSuffix = (ranges.size() != 1); needsSuffix = (ranges.size() != 1);
} }
QList<AccessRange> collectAccessRanges(DeclarationListAST *decls, QList<AccessRange> collectAccessRanges(const CPlusPlus::TranslationUnit *tu,
DeclarationListAST *decls,
InsertionPointLocator::AccessSpec initialXs, InsertionPointLocator::AccessSpec initialXs,
int firstRangeStart, int firstRangeStart,
int lastRangeEnd) const int lastRangeEnd)
{ {
QList<AccessRange> ranges; QList<AccessRange> ranges;
ranges.append(AccessRange(firstRangeStart, lastRangeEnd, initialXs, 0)); ranges.append(AccessRange(firstRangeStart, lastRangeEnd, initialXs, 0));
@@ -194,10 +175,9 @@ protected:
if (AccessDeclarationAST *xsDecl = decl->asAccessDeclaration()) { if (AccessDeclarationAST *xsDecl = decl->asAccessDeclaration()) {
const unsigned token = xsDecl->access_specifier_token; const unsigned token = xsDecl->access_specifier_token;
InsertionPointLocator::AccessSpec newXsSpec = initialXs; InsertionPointLocator::AccessSpec newXsSpec = initialXs;
bool isSlot = xsDecl->slots_token bool isSlot = xsDecl->slots_token && tu->tokenKind(xsDecl->slots_token) == T_Q_SLOTS;
&& tokenKind(xsDecl->slots_token) == T_Q_SLOTS;
switch (tokenKind(token)) { switch (tu->tokenKind(token)) {
case T_PUBLIC: case T_PUBLIC:
newXsSpec = isSlot ? InsertionPointLocator::PublicSlot newXsSpec = isSlot ? InsertionPointLocator::PublicSlot
: InsertionPointLocator::Public; : InsertionPointLocator::Public;
@@ -218,8 +198,8 @@ protected:
break; break;
case T_Q_SLOTS: { case T_Q_SLOTS: {
newXsSpec = (InsertionPointLocator::AccessSpec) newXsSpec = (InsertionPointLocator::AccessSpec)(ranges.last().xsSpec
(ranges.last().xsSpec | InsertionPointLocator::SlotBit); | InsertionPointLocator::SlotBit);
break; break;
} }
@@ -230,6 +210,7 @@ protected:
if (newXsSpec != ranges.last().xsSpec || ranges.size() == 1) { if (newXsSpec != ranges.last().xsSpec || ranges.size() == 1) {
ranges.last().end = token; ranges.last().end = token;
AccessRange r(token, lastRangeEnd, newXsSpec, xsDecl->colon_token); AccessRange r(token, lastRangeEnd, newXsSpec, xsDecl->colon_token);
r.beforeStart = xsDecl->lastToken() - 1;
ranges.append(r); ranges.append(r);
} }
} }
@@ -239,14 +220,6 @@ protected:
return ranges; return ranges;
} }
private:
Document::Ptr _doc;
const Class *_clazz;
InsertionPointLocator::AccessSpec _xsSpec;
InsertionLocation _result;
};
} // 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

View File

@@ -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,