forked from qt-creator/qt-creator
"New Class" wizard: Check custom base class for QObject parent
That is, if the user specifies a custom base class, we check whether its constructor takes a "QObject *parent" parameter, and if it does, we give the derived class one as well. This is technically a heuristic, but the pattern is pretty stable in the Qt world. Fixes: QTCREATORBUG-25156 Change-Id: Ie64440929df61cca7258d6d692c5de62970f9a65 Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
@@ -13,6 +13,8 @@ public:
|
||||
%{CN}::%{CN}(QObject *parent) : QObject(parent)%{JS: ('%{SharedDataInit}') ? ', %{SharedDataInit}' : ''}
|
||||
@elsif '%{Base}' === 'QWidget' || '%{Base}' === 'QMainWindow'
|
||||
%{CN}::%{CN}(QWidget *parent) : %{Base}(parent)%{JS: ('%{SharedDataInit}') ? ', %{SharedDataInit}' : ''}
|
||||
@elsif %{JS: Cpp.hasQObjectParent('%{Base}')}
|
||||
%{CN}::%{CN}(QObject *parent) : %{Base}(parent)%{JS: ('%{SharedDataInit}') ? ', %{SharedDataInit}' : ''}
|
||||
@else
|
||||
%{CN}::%{CN}()%{JS: ('%{SharedDataInit}') ? ' : %{SharedDataInit}' : ''}
|
||||
@endif
|
||||
|
@@ -33,7 +33,7 @@ class %{CN}
|
||||
Q_OBJECT
|
||||
@endif
|
||||
public:
|
||||
@if '%{Base}' === 'QObject'
|
||||
@if '%{Base}' === 'QObject' || %{JS: Cpp.hasQObjectParent('%{Base}')}
|
||||
explicit %{CN}(QObject *parent = nullptr);
|
||||
@elsif '%{Base}' === 'QWidget' || '%{Base}' === 'QMainWindow'
|
||||
explicit %{CN}(QWidget *parent = nullptr);
|
||||
|
@@ -42,6 +42,7 @@
|
||||
#include "cpprefactoringchanges.h"
|
||||
#include "cpprefactoringengine.h"
|
||||
#include "cppsourceprocessor.h"
|
||||
#include "cpptoolsjsextension.h"
|
||||
#include "cpptoolsplugin.h"
|
||||
#include "cpptoolsconstants.h"
|
||||
#include "cpptoolsreuse.h"
|
||||
@@ -54,6 +55,7 @@
|
||||
#include <coreplugin/documentmanager.h>
|
||||
#include <coreplugin/editormanager/editormanager.h>
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/jsexpander.h>
|
||||
#include <coreplugin/progressmanager/progressmanager.h>
|
||||
#include <coreplugin/vcsmanager.h>
|
||||
#include <cplusplus/ASTPath.h>
|
||||
@@ -575,6 +577,13 @@ CppModelManager *CppModelManager::instance()
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
void CppModelManager::registerJsExtension()
|
||||
{
|
||||
Core::JsExpander::registerGlobalObject("Cpp", [this] {
|
||||
return new CppToolsJsExtension(&d->m_locatorData);
|
||||
});
|
||||
}
|
||||
|
||||
void CppModelManager::initCppTools()
|
||||
{
|
||||
// Objects
|
||||
|
@@ -98,6 +98,8 @@ public:
|
||||
|
||||
static CppModelManager *instance();
|
||||
|
||||
void registerJsExtension();
|
||||
|
||||
// Documented in source file.
|
||||
enum ProgressNotificationMode {
|
||||
ForcedProgressNotification,
|
||||
|
@@ -26,6 +26,8 @@
|
||||
#include "cpptoolsjsextension.h"
|
||||
|
||||
#include "cppfilesettingspage.h"
|
||||
#include "cpplocatordata.h"
|
||||
#include "cppworkingcopy.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
@@ -33,9 +35,13 @@
|
||||
#include <projectexplorer/projectnodes.h>
|
||||
#include <projectexplorer/session.h>
|
||||
|
||||
#include <cplusplus/AST.h>
|
||||
#include <cplusplus/ASTPath.h>
|
||||
#include <cplusplus/Overview.h>
|
||||
#include <utils/codegeneration.h>
|
||||
#include <utils/fileutils.h>
|
||||
|
||||
#include <QElapsedTimer>
|
||||
#include <QFileInfo>
|
||||
#include <QStringList>
|
||||
#include <QTextStream>
|
||||
@@ -118,6 +124,90 @@ QString CppToolsJsExtension::closeNamespaces(const QString &klass) const
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CppToolsJsExtension::hasQObjectParent(const QString &klassName) const
|
||||
{
|
||||
// This is a synchronous function, but the look-up is potentially expensive.
|
||||
// Since it's not crucial information, we just abort if retrieving it takes too long,
|
||||
// in order not to freeze the UI.
|
||||
// TODO: Any chance to at least cache between successive invocations for the same dialog?
|
||||
// I don't see it atm...
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
static const int timeout = 5000;
|
||||
|
||||
// Find symbol.
|
||||
QList<IndexItem::Ptr> candidates;
|
||||
m_locatorData->filterAllFiles([&](const IndexItem::Ptr &item) {
|
||||
if (timer.elapsed() > timeout)
|
||||
return IndexItem::VisitorResult::Break;
|
||||
if (item->scopedSymbolName() == klassName) {
|
||||
candidates = {item};
|
||||
return IndexItem::VisitorResult::Break;
|
||||
}
|
||||
if (item->symbolName() == klassName)
|
||||
candidates << item;
|
||||
return IndexItem::VisitorResult::Recurse;
|
||||
});
|
||||
if (timer.elapsed() > timeout)
|
||||
return false;
|
||||
if (candidates.isEmpty())
|
||||
return false;
|
||||
const IndexItem::Ptr item = candidates.first();
|
||||
|
||||
// Find class in AST.
|
||||
const CPlusPlus::Snapshot snapshot = CppModelManager::instance()->snapshot();
|
||||
const WorkingCopy workingCopy = CppModelManager::instance()->workingCopy();
|
||||
QByteArray source = workingCopy.source(item->fileName());
|
||||
if (source.isEmpty()) {
|
||||
QFile file(item->fileName());
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
return false;
|
||||
source = file.readAll();
|
||||
}
|
||||
const auto doc = snapshot.preprocessedDocument(source, item->fileName());
|
||||
if (!doc)
|
||||
return false;
|
||||
doc->check();
|
||||
if (!doc->translationUnit())
|
||||
return false;
|
||||
if (timer.elapsed() > timeout)
|
||||
return false;
|
||||
CPlusPlus::ClassSpecifierAST *classSpec = nullptr;
|
||||
const QList<CPlusPlus::AST *> astPath = CPlusPlus::ASTPath(doc)(item->line(), item->column());
|
||||
for (auto it = astPath.rbegin(); it != astPath.rend(); ++it) {
|
||||
if ((classSpec = (*it)->asClassSpecifier()))
|
||||
break;
|
||||
}
|
||||
if (!classSpec)
|
||||
return false;
|
||||
|
||||
// Check whether constructor has a QObject parent parameter.
|
||||
CPlusPlus::Overview overview;
|
||||
const CPlusPlus::Class * const klass = classSpec->symbol;
|
||||
if (!klass)
|
||||
return false;
|
||||
for (auto it = klass->memberBegin(); it != klass->memberEnd(); ++it) {
|
||||
const CPlusPlus::Symbol * const member = *it;
|
||||
if (overview.prettyName(member->name()) != item->symbolName())
|
||||
continue;
|
||||
const CPlusPlus::Function *function = (*it)->asFunction();
|
||||
if (!function)
|
||||
function = member->type().type()->asFunctionType();
|
||||
if (!function)
|
||||
continue;
|
||||
for (int i = 0; i < function->argumentCount(); ++i) {
|
||||
const CPlusPlus::Symbol * const arg = function->argumentAt(i);
|
||||
const QString argName = overview.prettyName(arg->name());
|
||||
const QString argType = overview.prettyType(arg->type())
|
||||
.split("::", Qt::SkipEmptyParts).last();
|
||||
if (argName == "parent" && argType == "QObject *")
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QString CppToolsJsExtension::includeStatement(
|
||||
const QString &fullyQualifiedClassName,
|
||||
const QString &suffix,
|
||||
|
@@ -30,6 +30,8 @@
|
||||
#include <QStringList>
|
||||
|
||||
namespace CppTools {
|
||||
class CppLocatorData; // FIXME: Belongs in namespace Internal
|
||||
|
||||
namespace Internal {
|
||||
|
||||
/**
|
||||
@@ -40,7 +42,8 @@ class CppToolsJsExtension : public QObject
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CppToolsJsExtension(QObject *parent = nullptr) : QObject(parent) { }
|
||||
explicit CppToolsJsExtension(CppLocatorData *locatorData, QObject *parent = nullptr)
|
||||
: QObject(parent), m_locatorData(locatorData) { }
|
||||
|
||||
// Generate header guard:
|
||||
Q_INVOKABLE QString headerGuard(const QString &in) const;
|
||||
@@ -55,6 +58,7 @@ public:
|
||||
Q_INVOKABLE QString classToHeaderGuard(const QString &klass, const QString &extension) const;
|
||||
Q_INVOKABLE QString openNamespaces(const QString &klass) const;
|
||||
Q_INVOKABLE QString closeNamespaces(const QString &klass) const;
|
||||
Q_INVOKABLE bool hasQObjectParent(const QString &klassName) const;
|
||||
|
||||
// Find header file for class.
|
||||
Q_INVOKABLE QString includeStatement(
|
||||
@@ -63,6 +67,9 @@ public:
|
||||
const QStringList &specialClasses,
|
||||
const QString &pathOfIncludingFile
|
||||
);
|
||||
|
||||
private:
|
||||
CppLocatorData * const m_locatorData;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
@@ -33,7 +33,6 @@
|
||||
#include "cpptoolsbridge.h"
|
||||
#include "cpptoolsbridgeqtcreatorimplementation.h"
|
||||
#include "cpptoolsconstants.h"
|
||||
#include "cpptoolsjsextension.h"
|
||||
#include "cpptoolsreuse.h"
|
||||
#include "cpptoolssettings.h"
|
||||
#include "projectinfo.h"
|
||||
@@ -46,7 +45,6 @@
|
||||
#include <coreplugin/editormanager/editormanager.h>
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/idocument.h>
|
||||
#include <coreplugin/jsexpander.h>
|
||||
#include <coreplugin/vcsmanager.h>
|
||||
#include <cppeditor/cppeditorconstants.h>
|
||||
#include <extensionsystem/pluginmanager.h>
|
||||
@@ -167,7 +165,7 @@ bool CppToolsPlugin::initialize(const QStringList &arguments, QString *error)
|
||||
d = new CppToolsPluginPrivate;
|
||||
d->initialize();
|
||||
|
||||
JsExpander::registerGlobalObject<CppToolsJsExtension>("Cpp");
|
||||
CppModelManager::instance()->registerJsExtension();
|
||||
ExtensionSystem::PluginManager::addObject(&d->m_cppProjectUpdaterFactory);
|
||||
|
||||
// Menus
|
||||
|
Reference in New Issue
Block a user