diff --git a/src/plugins/debugger/cdb/cdbdumperhelper.cpp b/src/plugins/debugger/cdb/cdbdumperhelper.cpp index 77f3f798594..52b4558772b 100644 --- a/src/plugins/debugger/cdb/cdbdumperhelper.cpp +++ b/src/plugins/debugger/cdb/cdbdumperhelper.cpp @@ -341,6 +341,8 @@ void CdbDumperInitThread ::run() CdbDumperHelper::CdbDumperHelper(DebuggerManager *manager, CdbComInterfaces *cif) : m_tryInjectLoad(true), + m_msgDisabled(QLatin1String("Dumpers are disabled")), + m_msgNotInScope(QLatin1String("Data not in scope")), m_state(NotLoaded), m_manager(manager), m_cif(cif), @@ -648,8 +650,12 @@ CdbDumperHelper::DumpResult CdbDumperHelper::dumpTypeI(const WatchData &wd, bool { errorMessage->clear(); // Check failure cache and supported types - if (m_state == Disabled) { - *errorMessage = QLatin1String("Dumpers are disabled"); + if (m_state == Disabled) { + *errorMessage =m_msgDisabled; + return DumpNotHandled; + } + if (wd.error) { + *errorMessage =m_msgNotInScope; return DumpNotHandled; } if (m_failedTypes.contains(wd.type)) { diff --git a/src/plugins/debugger/cdb/cdbdumperhelper.h b/src/plugins/debugger/cdb/cdbdumperhelper.h index 4035d03ee28..be326768df6 100644 --- a/src/plugins/debugger/cdb/cdbdumperhelper.h +++ b/src/plugins/debugger/cdb/cdbdumperhelper.h @@ -134,6 +134,8 @@ private: static bool writeToDebuggee(CIDebugDataSpaces *ds, const QByteArray &buffer, quint64 address, QString *errorMessage); const bool m_tryInjectLoad; + const QString m_msgDisabled; + const QString m_msgNotInScope; State m_state; DebuggerManager *m_manager; CdbComInterfaces *m_cif; diff --git a/src/plugins/debugger/cdb/cdbstackframecontext.cpp b/src/plugins/debugger/cdb/cdbstackframecontext.cpp index d97a968768c..f64f0366a1d 100644 --- a/src/plugins/debugger/cdb/cdbstackframecontext.cpp +++ b/src/plugins/debugger/cdb/cdbstackframecontext.cpp @@ -217,7 +217,7 @@ bool WatchHandleDumperInserter::expandPointerToDumpable(const WatchData &wd, QSt bool handled = false; do { - if (!isPointerType(wd.type)) + if (wd.error || !isPointerType(wd.type)) break; const int classPos = wd.value.indexOf(" class "); if (classPos == -1) @@ -396,9 +396,9 @@ bool CdbStackFrameContext::editorToolTip(const QString &iname, *errorMessage = QString::fromLatin1("%1 not found.").arg(iname); return false; } - const WatchData wd = m_symbolContext->symbolAt(index); // Check dumpers. Should actually be just one item. - if (m_useDumpers && m_dumper->state() != CdbDumperHelper::Disabled) { + const WatchData wd = m_symbolContext->watchDataAt(index); + if (m_useDumpers && !wd.error && m_dumper->state() != CdbDumperHelper::Disabled) { QList result; if (CdbDumperHelper::DumpOk == m_dumper->dumpType(wd, false, &result, errorMessage)) { foreach (const WatchData &dwd, result) { diff --git a/src/plugins/debugger/cdb/cdbstacktracecontext.cpp b/src/plugins/debugger/cdb/cdbstacktracecontext.cpp index 0a338f9fdcc..7a1448d708f 100644 --- a/src/plugins/debugger/cdb/cdbstacktracecontext.cpp +++ b/src/plugins/debugger/cdb/cdbstacktracecontext.cpp @@ -33,8 +33,11 @@ #include "cdbsymbolgroupcontext.h" #include "cdbdebugengine_p.h" #include "cdbdumperhelper.h" +#include "debuggeractions.h" +#include "debuggermanager.h" #include +#include #include namespace Debugger { @@ -160,7 +163,13 @@ CdbStackFrameContext *CdbStackTraceContext::frameContextAt(int index, QString *e *errorMessage = msgFrameContextFailed(index, m_frames.at(index), *errorMessage); return 0; } - CdbSymbolGroupContext *sc = CdbSymbolGroupContext::create(QLatin1String("local"), sg, errorMessage); + // Exclude unitialized variables if desired + QStringList uninitializedVariables; + if (theDebuggerAction(UseCodeModel)->isChecked()) { + const StackFrame &frame = m_frames.at(index); + getUninitializedVariables(DebuggerManager::instance()->cppCodeModelSnapshot(), frame.function, frame.file, frame.line, &uninitializedVariables); + } + CdbSymbolGroupContext *sc = CdbSymbolGroupContext::create(QLatin1String("local"), sg, uninitializedVariables, errorMessage); if (!sc) { *errorMessage = msgFrameContextFailed(index, m_frames.at(index), *errorMessage); return 0; diff --git a/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp b/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp index f58f1f74527..2efd3672b96 100644 --- a/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp +++ b/src/plugins/debugger/cdb/cdbsymbolgroupcontext.cpp @@ -39,6 +39,9 @@ enum { debug = 0 }; enum { debugInternalDumpers = 0 }; +// name separator for shadowed variables +static const char iNameShadowDelimiter = '#'; + static inline QString msgSymbolNotFound(const QString &s) { return QString::fromLatin1("The symbol '%1' could not be found.").arg(s); @@ -83,6 +86,14 @@ QTextStream &operator<<(QTextStream &str, const DEBUG_SYMBOL_PARAMETERS &p) return str; } +static inline QString hexSymbolOffset(CIDebugSymbolGroup *sg, unsigned long index) +{ + ULONG64 rc = 0; + if (FAILED(sg->GetSymbolOffset(index, &rc))) + rc = 0; + return QLatin1String("0x") + QString::number(rc, 16); +} + // A helper function to extract a string value from a member function of // IDebugSymbolGroup2 taking the symbol index and a character buffer. // Pass in the the member function as '&IDebugSymbolGroup2::GetSymbolNameWide' @@ -129,12 +140,15 @@ static inline CdbSymbolGroupContext::SymbolState getSymbolState(const DEBUG_SYMB } CdbSymbolGroupContext::CdbSymbolGroupContext(const QString &prefix, - CIDebugSymbolGroup *symbolGroup) : + CIDebugSymbolGroup *symbolGroup, + const QStringList &uninitializedVariables) : m_prefix(prefix), m_nameDelimiter(QLatin1Char('.')), + m_uninitializedVariables(uninitializedVariables.toSet()), m_symbolGroup(symbolGroup), m_unnamedSymbolNumber(1) { + } CdbSymbolGroupContext::~CdbSymbolGroupContext() @@ -144,9 +158,10 @@ CdbSymbolGroupContext::~CdbSymbolGroupContext() CdbSymbolGroupContext *CdbSymbolGroupContext::create(const QString &prefix, CIDebugSymbolGroup *symbolGroup, + const QStringList &uninitializedVariables, QString *errorMessage) { - CdbSymbolGroupContext *rc = new CdbSymbolGroupContext(prefix, symbolGroup); + CdbSymbolGroupContext *rc = new CdbSymbolGroupContext(prefix, symbolGroup, uninitializedVariables); if (!rc->init(errorMessage)) { delete rc; return 0; @@ -173,28 +188,36 @@ bool CdbSymbolGroupContext::init(QString *errorMessage) *errorMessage = QString::fromLatin1("In %1: %2 (%3 symbols)").arg(QLatin1String(Q_FUNC_INFO), msgComFailed("GetSymbolParameters", hr)).arg(count); return false; } - populateINameIndexMap(m_prefix, DEBUG_ANY_ID, 0, count); + populateINameIndexMap(m_prefix, DEBUG_ANY_ID, count); } if (debug) - qDebug() << Q_FUNC_INFO << '\n'<< toString(); + qDebug() << Q_FUNC_INFO << '\n'<< toString(true); return true; } +/* Make the entries for iname->index mapping. We might encounter + * already expanded subitems when doing it for top-level ('this'-pointers), + * recurse in that case, (skip over expanded children). + * Loop backwards to detect shadowed variables in the order the +/* debugger expects them: +\code +int x; // Occurrence (1), should be reported as "x " +if (true) { + int x = 5; (2) // Occurrence (2), should be reported as "x" +} +\endcode + * The order in the symbol group is (1),(2). Give them an iname of + * #, which will be split apart for display. */ + void CdbSymbolGroupContext::populateINameIndexMap(const QString &prefix, unsigned long parentId, - unsigned long start, unsigned long count) + unsigned long end) { - // Make the entries for iname->index mapping. We might encounter - // already expanded subitems when doing it for top-level, recurse in that case. const QString symbolPrefix = prefix + m_nameDelimiter; if (debug) - qDebug() << Q_FUNC_INFO << '\n'<< symbolPrefix << start << count; - const unsigned long end = m_symbolParameters.size(); - unsigned long seenChildren = 0; - // Skip over expanded children - for (unsigned long i = start; i < end && seenChildren < count; i++) { + qDebug() << Q_FUNC_INFO << '\n'<< symbolPrefix << parentId << end; + for (unsigned long i = end - 1; ; i--) { const DEBUG_SYMBOL_PARAMETERS &p = m_symbolParameters.at(i); if (parentId == p.ParentSymbol) { - seenChildren++; // "__formal" occurs when someone writes "void foo(int /* x */)..." static const QString unnamedFormalParameter = QLatin1String("__formal"); QString symbolName = getSymbolString(m_symbolGroup, &IDebugSymbolGroup2::GetSymbolNameWide, i); @@ -203,11 +226,21 @@ void CdbSymbolGroupContext::populateINameIndexMap(const QString &prefix, unsigne symbolName += QString::number(m_unnamedSymbolNumber++); symbolName += QLatin1Char('>'); } - const QString name = symbolPrefix + symbolName; + // Find a unique name in case the variable is shadowed by + // an existing one + const QString namePrefix = symbolPrefix + symbolName; + QString name = namePrefix; + for (int n = 1; m_inameIndexMap.contains(name); n++) { + name.truncate(namePrefix.size()); + name += QLatin1Char(iNameShadowDelimiter); + name += QString::number(n); + } m_inameIndexMap.insert(name, i); if (getSymbolState(p) == ExpandedSymbol) - populateINameIndexMap(name, i, i + 1, p.SubElements); + populateINameIndexMap(name, i, i + 1 + p.SubElements); } + if (i == 0 || i == parentId) + break; } } @@ -223,7 +256,10 @@ QString CdbSymbolGroupContext::toString(bool verbose) const str << " "; str << getSymbolString(m_symbolGroup, &IDebugSymbolGroup2::GetSymbolNameWide, i); if (p.Flags & DEBUG_SYMBOL_IS_LOCAL) - str << " '" << getSymbolString(m_symbolGroup, &IDebugSymbolGroup2::GetSymbolTypeNameWide, i); + str << " '" << getSymbolString(m_symbolGroup, &IDebugSymbolGroup2::GetSymbolTypeNameWide, i) << '\''; + str << " Address: " << hexSymbolOffset(m_symbolGroup, i); + if (verbose) + str << " '" << getSymbolString(m_symbolGroup, &IDebugSymbolGroup2::GetSymbolValueTextWide, i) << '\''; str << p << '\n'; } if (verbose) { @@ -348,7 +384,7 @@ bool CdbSymbolGroupContext::expandSymbol(const QString &prefix, unsigned long in if (it.value() > index) it.value() += newSymbolCount; // insert the new symbols - populateINameIndexMap(prefix, index, index + 1, newSymbolCount); + populateINameIndexMap(prefix, index, index + 1 + newSymbolCount); if (debug > 1) qDebug() << '<' << Q_FUNC_INFO << '\n' << prefix << index << '\n' << toString(); return true; @@ -365,14 +401,6 @@ QString CdbSymbolGroupContext::symbolINameAt(unsigned long index) const return m_inameIndexMap.key(index); } -static inline QString hexSymbolOffset(CIDebugSymbolGroup *sg, unsigned long index) -{ - ULONG64 rc = 0; - if (FAILED(sg->GetSymbolOffset(index, &rc))) - rc = 0; - return QLatin1String("0x") + QString::number(rc, 16); -} - // check for "0x000", "0x000 class X" static inline bool isNullPointer(const WatchData &wd) { @@ -409,19 +437,35 @@ static inline QString fixValue(const QString &value) return removeInnerTemplateType(value); } -WatchData CdbSymbolGroupContext::symbolAt(unsigned long index) const +WatchData CdbSymbolGroupContext::watchDataAt(unsigned long index) const { WatchData wd; wd.iname = symbolINameAt(index); wd.exp = wd.iname; + // Determine name from iname and format shadowed variables correctly + // as ", see populateINameIndexMap(). const int lastDelimiterPos = wd.iname.lastIndexOf(m_nameDelimiter); + QString name = lastDelimiterPos == -1 ? wd.iname : wd.iname.mid(lastDelimiterPos + 1); + int shadowedNumber = 0; + const int shadowedPos = name.lastIndexOf(QLatin1Char(iNameShadowDelimiter)); + if (shadowedPos != -1) { + shadowedNumber = name.mid(shadowedPos + 1).toInt(); + name.truncate(shadowedPos); + } // For class hierarchies, we get sometimes complicated std::template types here. - // Remove them for display - wd.name = removeInnerTemplateType(lastDelimiterPos == -1 ? wd.iname : wd.iname.mid(lastDelimiterPos + 1)); + // (std::map extends std::tree<>... Remove them for display only. + const QString fullShadowedName = WatchData::shadowedName(name, shadowedNumber); + wd.name = WatchData::shadowedName(removeInnerTemplateType(name), shadowedNumber); wd.addr = hexSymbolOffset(m_symbolGroup, index); - const QString type = getSymbolString(m_symbolGroup, &IDebugSymbolGroup2::GetSymbolTypeNameWide, index); + const QString type = getSymbolString(m_symbolGroup, &IDebugSymbolGroup2::GetSymbolTypeNameWide, index); + wd.setType(type); + // Check for unitialized variables at level 0 only. + const DEBUG_SYMBOL_PARAMETERS &p = m_symbolParameters.at(index); + if (p.ParentSymbol == DEBUG_ANY_ID && m_uninitializedVariables.contains(fullShadowedName)) { + wd.setError(WatchData::msgNotInScope()); + return wd; + } const QString value = getSymbolString(m_symbolGroup, &IDebugSymbolGroup2::GetSymbolValueTextWide, index); - wd.setType(type); wd.setValue(fixValue(value)); wd.setChildrenNeeded(); // compensate side effects of above setters // Figure out children. The SubElement is only a guess unless the symbol, @@ -429,7 +473,7 @@ WatchData CdbSymbolGroupContext::symbolAt(unsigned long index) const // If the symbol has children (expanded or not), we leave the 'Children' flag // in 'needed' state. Suppress 0-pointers right ("0x000 class X") // here as they only lead to children with memory access errors. - const bool hasChildren = m_symbolParameters.at(index).SubElements && !isNullPointer(wd); + const bool hasChildren = p.SubElements && !isNullPointer(wd); wd.setHasChildren(hasChildren); if (debug > 1) qDebug() << Q_FUNC_INFO << index << '\n' << wd.toString(); @@ -438,7 +482,7 @@ WatchData CdbSymbolGroupContext::symbolAt(unsigned long index) const WatchData CdbSymbolGroupContext::dumpSymbolAt(CIDebugDataSpaces *ds, unsigned long index) { - WatchData rc = symbolAt(index); + WatchData rc = watchDataAt(index); dump(ds, &rc); return rc; } diff --git a/src/plugins/debugger/cdb/cdbsymbolgroupcontext.h b/src/plugins/debugger/cdb/cdbsymbolgroupcontext.h index c2770fb2039..2db042423f0 100644 --- a/src/plugins/debugger/cdb/cdbsymbolgroupcontext.h +++ b/src/plugins/debugger/cdb/cdbsymbolgroupcontext.h @@ -69,12 +69,14 @@ class CdbSymbolGroupContext { Q_DISABLE_COPY(CdbSymbolGroupContext); explicit CdbSymbolGroupContext(const QString &prefix, - CIDebugSymbolGroup *symbolGroup); + CIDebugSymbolGroup *symbolGroup, + const QStringList &uninitializedVariables = QStringList()); public: ~CdbSymbolGroupContext(); - static CdbSymbolGroupContext *create(const QString &prefix, + static CdbSymbolGroupContext *create(const QString &prefix, CIDebugSymbolGroup *symbolGroup, + const QStringList &uninitializedVariables, QString *errorMessage); QString prefix() const { return m_prefix; } @@ -118,7 +120,7 @@ public: int dumpedOwner, OutputIterator it, QString *errorMessage); - WatchData symbolAt(unsigned long index) const; + WatchData watchDataAt(unsigned long index) const; // Run the internal dumpers on the symbol WatchData dumpSymbolAt(CIDebugDataSpaces *ds, unsigned long index); @@ -155,7 +157,7 @@ private: unsigned long *parentId, QString *errorMessage); bool expandSymbol(const QString &prefix, unsigned long index, QString *errorMessage); - void populateINameIndexMap(const QString &prefix, unsigned long parentId, unsigned long start, unsigned long count); + void populateINameIndexMap(const QString &prefix, unsigned long parentId, unsigned long end); QString symbolINameAt(unsigned long index) const; int dumpQString(CIDebugDataSpaces *ds, WatchData *wd); @@ -166,6 +168,7 @@ private: const QString m_prefix; const QChar m_nameDelimiter; + const QSet m_uninitializedVariables; CIDebugSymbolGroup *m_symbolGroup; NameIndexMap m_inameIndexMap; diff --git a/src/plugins/debugger/cdb/cdbsymbolgroupcontext_tpl.h b/src/plugins/debugger/cdb/cdbsymbolgroupcontext_tpl.h index 0b143207952..fe33e802b6c 100644 --- a/src/plugins/debugger/cdb/cdbsymbolgroupcontext_tpl.h +++ b/src/plugins/debugger/cdb/cdbsymbolgroupcontext_tpl.h @@ -61,7 +61,7 @@ bool CdbSymbolGroupContext::getDumpChildSymbols(CIDebugDataSpaces *ds, const QSt for (int s = start; s < m_symbolParameters.size(); ++s) { const DEBUG_SYMBOL_PARAMETERS &p = m_symbolParameters.at(s); if (p.ParentSymbol == parentId && isSymbolDisplayable(p)) { - WatchData wd = symbolAt(s); + WatchData wd = watchDataAt(s); // Run internal dumper, mark ownership if (ds) { switch (dump(ds, &wd)) { diff --git a/src/plugins/debugger/debuggeractions.cpp b/src/plugins/debugger/debuggeractions.cpp index a2c1da01721..4a0b0f783eb 100644 --- a/src/plugins/debugger/debuggeractions.cpp +++ b/src/plugins/debugger/debuggeractions.cpp @@ -229,6 +229,13 @@ DebuggerSettings *DebuggerSettings::instance() item->setValue(false); instance->insertItem(DebugDebuggingHelpers, item); + item = new SavedAction(instance); + item->setSettingsKey(debugModeGroup, QLatin1String("UseCodeModel")); + item->setText(tr("Use code model")); + item->setCheckable(true); + item->setDefaultValue(false); + item->setValue(false); + instance->insertItem(UseCodeModel, item); item = new SavedAction(instance); item->setText(tr("Recheck debugging helper availability")); diff --git a/src/plugins/debugger/debuggeractions.h b/src/plugins/debugger/debuggeractions.h index ffc334b268f..234cf21731c 100644 --- a/src/plugins/debugger/debuggeractions.h +++ b/src/plugins/debugger/debuggeractions.h @@ -85,6 +85,8 @@ enum DebuggerActionCode UseCustomDebuggingHelperLocation, CustomDebuggingHelperLocation, DebugDebuggingHelpers, + + UseCodeModel, UseToolTipsInMainEditor, UseToolTipsInLocalsView, diff --git a/src/plugins/debugger/debuggermanager.cpp b/src/plugins/debugger/debuggermanager.cpp index d7d9cf31457..c7f541293fc 100644 --- a/src/plugins/debugger/debuggermanager.cpp +++ b/src/plugins/debugger/debuggermanager.cpp @@ -61,6 +61,8 @@ #include #include #include +#include +#include #include #include @@ -303,6 +305,8 @@ struct DebuggerManagerPrivate IDebuggerEngine *m_engine; DebuggerState m_state; + + CPlusPlus::Snapshot m_codeModelSnapshot; }; DebuggerManager *DebuggerManagerPrivate::instance = 0; @@ -623,6 +627,18 @@ WatchHandler *DebuggerManager::watchHandler() const return d->m_watchHandler; } +const CPlusPlus::Snapshot &DebuggerManager::cppCodeModelSnapshot() const +{ + if (d->m_codeModelSnapshot.isEmpty() && theDebuggerAction(UseCodeModel)->isChecked()) + d->m_codeModelSnapshot = CppTools::CppModelManagerInterface::instance()->snapshot(); + return d->m_codeModelSnapshot; +} + +void DebuggerManager::clearCppCodeModelSnapshot() +{ + d->m_codeModelSnapshot.clear(); +} + SourceFilesWindow *DebuggerManager::sourceFileWindow() const { return d->m_sourceFilesWindow; @@ -1026,6 +1042,7 @@ void DebuggerManager::exitDebugger() // in turn will handle the cleanup. if (d->m_engine && state() != DebuggerNotReady) d->m_engine->exitDebugger(); + d->m_codeModelSnapshot.clear(); } DebuggerStartParametersPtr DebuggerManager::startParameters() const diff --git a/src/plugins/debugger/debuggermanager.h b/src/plugins/debugger/debuggermanager.h index 76869b379e2..bc82ec187ba 100644 --- a/src/plugins/debugger/debuggermanager.h +++ b/src/plugins/debugger/debuggermanager.h @@ -59,6 +59,10 @@ namespace TextEditor { class ITextEditor; } +namespace CPlusPlus { + class Snapshot; +} + namespace Debugger { namespace Internal { @@ -180,6 +184,8 @@ public: QString *settingsCategory = 0, QString *settingsPage = 0) const; + const CPlusPlus::Snapshot &cppCodeModelSnapshot() const; + static DebuggerManager *instance(); public slots: @@ -232,6 +238,7 @@ public slots: void setRegisterValue(int nr, const QString &value); void showStatusMessage(const QString &msg, int timeout = -1); // -1 forever + void clearCppCodeModelSnapshot(); public slots: // FIXME void showDebuggerOutput(const QString &msg) @@ -267,7 +274,8 @@ private: Internal::ThreadsHandler *threadsHandler() const; Internal::WatchHandler *watchHandler() const; Internal::SourceFilesWindow *sourceFileWindow() const; - QWidget *threadsWindow() const; + QWidget *threadsWindow() const; + Internal::DebuggerManagerActions debuggerManagerActions() const; void notifyInferiorStopped(); diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index acd19909d10..c0d60ba6a75 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -373,6 +373,9 @@ QWidget *DebuggingHelperOptionPage::createPage(QWidget *parent) m_group.insert(theDebuggerAction(CustomDebuggingHelperLocation), m_ui.dumperLocationChooser); + m_group.insert(theDebuggerAction(UseCodeModel), + m_ui.checkBoxUseCodeModel); + #ifdef QT_DEBUG m_group.insert(theDebuggerAction(DebugDebuggingHelpers), m_ui.checkBoxDebugDebuggingHelpers); diff --git a/src/plugins/debugger/dumperoptionpage.ui b/src/plugins/debugger/dumperoptionpage.ui index 1b6bb8ed505..e63355068de 100644 --- a/src/plugins/debugger/dumperoptionpage.ui +++ b/src/plugins/debugger/dumperoptionpage.ui @@ -6,7 +6,7 @@ 0 0 - 403 + 432 434 @@ -83,10 +83,20 @@ - + + + + + Makes use of Qt Creator's code model to find out if a variable has already been assigned a value at the point the debugger interrupts. + + + Use code model + + + diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index acb1d1ac88e..87d6900a301 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -89,10 +89,6 @@ #endif #include -// FIXME: temporary hack to evalaute tbreak based step-over behaviour -static QString lastFile; -static int lastLine; - namespace Debugger { namespace Internal { @@ -1233,9 +1229,9 @@ void GdbEngine::handleStop2(const GdbResponse &response) void GdbEngine::handleStop2(const GdbMi &data) { // Sometimes we get some interesting extra information. Grab it. - GdbMi frame = data.findChild("frame"); - GdbMi shortName = frame.findChild("file"); - GdbMi fullName = frame.findChild("fullname"); + const GdbMi gdbmiFrame = data.findChild("frame"); + GdbMi shortName = gdbmiFrame.findChild("file"); + GdbMi fullName = gdbmiFrame.findChild("fullname"); if (shortName.isValid() && fullName.isValid()) { QString file = QFile::decodeName(shortName.data()); QString full = QFile::decodeName(fullName.data()); @@ -1246,16 +1242,17 @@ void GdbEngine::handleStop2(const GdbMi &data) } // Quick shot: Jump to stack frame #0. - if (frame.isValid()) { - const StackFrame f = parseStackFrame(frame, 0); - gotoLocation(f, true); + StackFrame frame; + if (gdbmiFrame.isValid()) { + frame = parseStackFrame(gdbmiFrame, 0); + gotoLocation(frame, true); } // // Stack // manager()->stackHandler()->setCurrentIndex(0); - updateLocals(); // Quick shot + updateLocals(qVariantFromValue(frame)); // Quick shot reloadStack(false); @@ -2583,11 +2580,6 @@ void GdbEngine::setToolTipExpression(const QPoint &mousePos, // ////////////////////////////////////////////////////////////////////// -//: Variable -static const QString strNotInScope = - QCoreApplication::translate("Debugger::Internal::GdbEngine", ""); - - static void setWatchDataValue(WatchData &data, const GdbMi &mi, int encoding = 0) { @@ -2804,8 +2796,8 @@ void GdbEngine::updateSubItem(const WatchData &data0) qDebug() << "FIXME: GdbEngine::updateSubItem:" << data.toString() << "should not happen"; #else - data.setType(strNotInScope); - data.setValue(strNotInScope); + data.setType(WatchData::msgNotInScope()); + data.setValue(WatchData::msgNotInScope()); data.setHasChildren(false); insertData(data); return; @@ -3139,7 +3131,7 @@ void GdbEngine::handleVarCreate(const GdbResponse &response) } else { data.setError(QString::fromLocal8Bit(response.data.findChild("msg").data())); if (data.isWatcher()) { - data.value = strNotInScope; + data.value = WatchData::msgNotInScope(); data.type = _(" "); data.setAllUnneeded(); data.setHasChildren(false); @@ -3213,7 +3205,7 @@ void GdbEngine::handleDebuggingHelperValue2(const GdbResponse &response) GdbMi contents; if (!parseConsoleStream(response, &contents)) { - data.setError(strNotInScope); + data.setError(WatchData::msgNotInScope()); insertData(data); return; } @@ -3306,7 +3298,7 @@ void GdbEngine::handleDebuggingHelperValue3(const GdbResponse &response) // << " STREAM:" << out; if (list.isEmpty()) { //: Value for variable - data.setError(strNotInScope); + data.setError(WatchData::msgNotInScope()); data.setAllUnneeded(); insertData(data); } else if (data.type == __("QString") @@ -3351,19 +3343,19 @@ void GdbEngine::handleDebuggingHelperValue3(const GdbResponse &response) } } else { //: Value for variable - data.setError(strNotInScope); + data.setError(WatchData::msgNotInScope()); data.setAllUnneeded(); insertData(data); } } else { WatchData data = response.cookie.value(); - data.setError(strNotInScope); + data.setError(WatchData::msgNotInScope()); data.setAllUnneeded(); insertData(data); } } -void GdbEngine::updateLocals() +void GdbEngine::updateLocals(const QVariant &cookie) { m_pendingRequests = 0; m_processedNames.clear(); @@ -3393,7 +3385,7 @@ void GdbEngine::updateLocals() postCommand(cmd, WatchUpdate, CB(handleStackListArguments)); // '2' is 'list with type and value' postCommand(_("-stack-list-locals 2"), WatchUpdate, - CB(handleStackListLocals)); // stage 2/2 + CB(handleStackListLocals), cookie); // stage 2/2 } } @@ -3489,91 +3481,102 @@ void GdbEngine::handleStackListLocals(const GdbResponse &response) // There could be shadowed variables QList locals = response.data.findChild("locals").children(); locals += m_currentFunctionArgs; - - setLocals(locals); + QMap seen; + // If desired, retrieve list of uninitialized variables looking at + // the current frame. This is invoked first time after a stop from + // handleStop2, which passes on the frame as cookie. The whole stack + // is not known at this point. + QStringList uninitializedVariables; + if (theDebuggerAction(UseCodeModel)->isChecked()) { + const StackFrame frame = qVariantCanConvert(response.cookie) ? + qVariantValue(response.cookie) : + m_manager->stackHandler()->currentFrame(); + if (frame.isUsable()) + getUninitializedVariables(m_manager->cppCodeModelSnapshot(), + frame.function, frame.file, frame.line, + &uninitializedVariables); + } + QList list; + foreach (const GdbMi &item, locals) + list.push_back(localVariable(item, uninitializedVariables, &seen)); + manager()->watchHandler()->insertBulkData(list); manager()->watchHandler()->updateWatchers(); } -void GdbEngine::setLocals(const QList &locals) +// Parse a local variable from GdbMi +WatchData GdbEngine::localVariable(const GdbMi &item, + const QStringList &uninitializedVariables, + QMap *seen) { - //qDebug() << m_varToType; - QMap seen; - - QList list; - foreach (const GdbMi &item, locals) { - // Local variables of inlined code are reported as - // 26^done,locals={varobj={exp="this",value="",name="var4",exp="this", - // numchild="1",type="const QtSharedPointer::Basic 1) - continue; - QByteArray name = item.findChild("exp").data(); - #else - QByteArray name = item.findChild("name").data(); - #endif - int n = seen.value(name); - if (n) { - seen[name] = n + 1; - WatchData data; - QString nam = _(name); - data.iname = _("local.") + nam + QString::number(n + 1); - //: Variable %1 is the variable name, %2 is a simple count - data.name = tr("%1 ").arg(nam).arg(n); + // Local variables of inlined code are reported as + // 26^done,locals={varobj={exp="this",value="",name="var4",exp="this", + // numchild="1",type="const QtSharedPointer::Basic 1) + continue; + QByteArray name = item.findChild("exp").data(); +#else + QByteArray name = item.findChild("name").data(); +#endif + const QMap::iterator it = seen->find(name); + if (it != seen->end()) { + const int n = it.value(); + ++(it.value()); + WatchData data; + QString nam = _(name); + data.iname = _("local.") + nam + QString::number(n + 1); + //: Variable %1 is the variable name, %2 is a simple count + data.name = WatchData::shadowedName(nam, n); + if (uninitializedVariables.contains(data.name)) { + data.setError(WatchData::msgNotInScope()); + return data; + } + //: Type of local variable or parameter shadowed by another + //: variable of the same name in a nested block. + setWatchDataValue(data, item.findChild("value")); + data.setType(GdbEngine::tr("")); + data.setHasChildren(false); + return data; + } + seen->insert(name, 1); + WatchData data; + QString nam = _(name); + data.iname = _("local.") + nam; + data.name = nam; + data.exp = nam; + data.framekey = m_currentFrame + data.name; + setWatchDataType(data, item.findChild("type")); + if (uninitializedVariables.contains(data.name)) { + data.setError(WatchData::msgNotInScope()); + return data; + } + if (isSynchroneous()) { + setWatchDataValue(data, item.findChild("value"), + item.findChild("valueencoded").data().toInt()); + // We know that the complete list of children is + // somewhere in the response. + data.setChildrenUnneeded(); + } else { + // set value only directly if it is simple enough, otherwise + // pass through the insertData() machinery + if (isIntOrFloatType(data.type) || isPointerType(data.type)) + setWatchDataValue(data, item.findChild("value")); + if (isSymbianIntType(data.type)) { setWatchDataValue(data, item.findChild("value")); - //: Type of local variable or parameter shadowed by another - //variable of the same name in a nested block - data.setType(tr("")); data.setHasChildren(false); - list.append(data); - } else { - seen[name] = 1; - WatchData data; - QString nam = _(name); - data.iname = _("local.") + nam; - data.name = nam; - data.exp = nam; - data.framekey = m_currentFrame + data.name; - setWatchDataType(data, item.findChild("type")); - if (isSynchroneous()) { - setWatchDataValue(data, item.findChild("value"), - item.findChild("valueencoded").data().toInt()); - // We know that the complete list of children is - // somewhere in the response. - data.setChildrenUnneeded(); - } else { - // set value only directly if it is simple enough, otherwise - // pass through the insertData() machinery - if (isIntOrFloatType(data.type) || isPointerType(data.type)) - setWatchDataValue(data, item.findChild("value")); - if (isSymbianIntType(data.type)) { - setWatchDataValue(data, item.findChild("value")); - data.setHasChildren(false); - } - } - - // Let's be a bit more bold: - //if (!hasDebuggingHelperForType(data.type)) { - // QByteArray value = item.findChild("value").data(); - // if (!value.isEmpty() && value != "{...}") - // data.setValue(decodeData(value, 0)); - //} - if (!manager()->watchHandler()->isExpandedIName(data.iname)) - data.setChildrenUnneeded(); - if (isPointerType(data.type) || data.name == __("this")) - data.setHasChildren(true); - if (0 && m_varToType.contains(data.framekey)) { - qDebug() << "RE-USING" << m_varToType.value(data.framekey); - data.setType(m_varToType.value(data.framekey)); - } - list.append(data); } } - manager()->watchHandler()->insertBulkData(list); + + if (!m_manager->watchHandler()->isExpandedIName(data.iname)) + data.setChildrenUnneeded(); + if (isPointerType(data.type) || data.name == __("this")) + data.setHasChildren(true); + return data; } void GdbEngine::insertData(const WatchData &data0) @@ -4089,9 +4092,7 @@ void GdbEngine::handleFetchDisassemblerByAddress0(const GdbResponse &response) void GdbEngine::gotoLocation(const StackFrame &frame, bool setMarker) { - lastFile = frame.file; - lastLine = frame.line; - //qDebug() << "GOTO " << frame.toString() << setMarker; + // qDebug() << "GOTO " << frame << setMarker; m_manager->gotoLocation(frame, setMarker); } @@ -4291,4 +4292,3 @@ IDebuggerEngine *createGdbEngine(DebuggerManager *manager) Q_DECLARE_METATYPE(Debugger::Internal::MemoryAgentCookie); Q_DECLARE_METATYPE(Debugger::Internal::DisassemblerAgentCookie); Q_DECLARE_METATYPE(Debugger::Internal::GdbMi); - diff --git a/src/plugins/debugger/gdb/gdbengine.h b/src/plugins/debugger/gdb/gdbengine.h index 99f59fb63d1..08e873b2d4f 100644 --- a/src/plugins/debugger/gdb/gdbengine.h +++ b/src/plugins/debugger/gdb/gdbengine.h @@ -231,7 +231,7 @@ private: void setTokenBarrier(); void updateAll(); - void updateLocals(); + void updateLocals(const QVariant &cookie = QVariant()); void gdbInputAvailable(int channel, const QString &msg) { m_manager->showDebuggerInput(channel, msg); } @@ -399,7 +399,9 @@ private: const WatchData &parent); void setWatchDataType(WatchData &data, const GdbMi &mi); void setWatchDataDisplayedType(WatchData &data, const GdbMi &mi); - void setLocals(const QList &locals); + inline WatchData localVariable(const GdbMi &item, + const QStringList &uninitializedVariables, + QMap *seen); void connectDebuggingHelperActions(); void disconnectDebuggingHelperActions(); AbstractGdbAdapter *createAdapter(const DebuggerStartParametersPtr &dp); diff --git a/src/plugins/debugger/stackframe.h b/src/plugins/debugger/stackframe.h index 3a0e415e1f2..8e5771f7060 100644 --- a/src/plugins/debugger/stackframe.h +++ b/src/plugins/debugger/stackframe.h @@ -33,12 +33,17 @@ #include #include +QT_BEGIN_NAMESPACE +class QDebug; +QT_END_NAMESPACE + namespace Debugger { namespace Internal { struct StackFrame { StackFrame(); + void clear(); bool isUsable() const; QString toToolTip() const; QString toString() const; @@ -52,6 +57,8 @@ struct StackFrame QString address; }; +QDebug operator<<(QDebug d, const StackFrame &); + } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/stackhandler.cpp b/src/plugins/debugger/stackhandler.cpp index 2d9cbea92d7..ec47a02b159 100644 --- a/src/plugins/debugger/stackhandler.cpp +++ b/src/plugins/debugger/stackhandler.cpp @@ -37,12 +37,23 @@ #include #include -using namespace Debugger::Internal; +namespace Debugger { +namespace Internal { StackFrame::StackFrame() : level(0), line(0) {} +void StackFrame::clear() +{ + line = level = 0; + function.clear(); + file.clear(); + from.clear(); + to.clear(); + address.clear(); +} + bool StackFrame::isUsable() const { return !file.isEmpty() && QFileInfo(file).isReadable(); @@ -52,12 +63,12 @@ QString StackFrame::toString() const { QString res; QTextStream str(&res); - str << StackHandler::tr("Address:") << " " << address << " " - << StackHandler::tr("Function:") << " " << function << " " - << StackHandler::tr("File:") << " " << file << " " - << StackHandler::tr("Line:") << " " << line << " " - << StackHandler::tr("From:") << " " << from << " " - << StackHandler::tr("To:") << " " << to; + str << StackHandler::tr("Address:") << ' ' << address << ' ' + << StackHandler::tr("Function:") << ' ' << function << ' ' + << StackHandler::tr("File:") << ' ' << file << ' ' + << StackHandler::tr("Line:") << ' ' << line << ' ' + << StackHandler::tr("From:") << ' ' << from << ' ' + << StackHandler::tr("To:") << ' ' << to; return res; } @@ -76,6 +87,23 @@ QString StackFrame::toToolTip() const return res; } +QDebug operator<<(QDebug d, const StackFrame &f) +{ + QString res; + QTextStream str(&res); + str << "level=" << f.level << " address=" << f.address; + if (!f.function.isEmpty()) + str << ' ' << f.function; + if (!f.file.isEmpty()) + str << ' ' << f.file << ':' << f.line; + if (!f.from.isEmpty()) + str << " from=" << f.from; + if (!f.to.isEmpty()) + str << " to=" << f.to; + d.nospace() << res; + return d; +} + //////////////////////////////////////////////////////////////////////// // // StackHandler @@ -379,3 +407,5 @@ void ThreadsHandler::notifyRunning() it->notifyRunning(); emit dataChanged(index(0, 1), index(m_threads.size()- 1, ColumnCount - 1)); } +} // namespace Internal +} // namespace Debugger diff --git a/src/plugins/debugger/watchhandler.cpp b/src/plugins/debugger/watchhandler.cpp index a0f524abcab..788c4b54b36 100644 --- a/src/plugins/debugger/watchhandler.cpp +++ b/src/plugins/debugger/watchhandler.cpp @@ -105,6 +105,7 @@ WatchData::WatchData() : generation(-1), valueEnabled(true), valueEditable(true), + error(false), source(0), state(InitialState), changed(false) @@ -127,7 +128,8 @@ bool WatchData::isEqual(const WatchData &other) const && framekey == other.framekey && hasChildren == other.hasChildren && valueEnabled == other.valueEnabled - && valueEditable == other.valueEditable; + && valueEditable == other.valueEditable + && error == other.error; } void WatchData::setError(const QString &msg) @@ -137,6 +139,7 @@ void WatchData::setError(const QString &msg) setHasChildren(false); valueEnabled = false; valueEditable = false; + error = true; } void WatchData::setValue(const QString &value0) @@ -232,6 +235,8 @@ QString WatchData::toString() const str << "iname=\"" << iname << doubleQuoteComma; if (!name.isEmpty() && name != iname) str << "name=\"" << name << doubleQuoteComma; + if (error) + str << "error,"; if (!addr.isEmpty()) str << "addr=\"" << addr << doubleQuoteComma; if (!exp.isEmpty()) @@ -310,6 +315,19 @@ QString WatchData::toToolTip() const return res; } +QString WatchData::msgNotInScope() +{ + static const QString rc = QCoreApplication::translate("Debugger::Internal::WatchData", ""); + return rc; +} + +QString WatchData::shadowedName(const QString &name, int seen) +{ + if (seen <= 0) + return name; + return QCoreApplication::translate("Debugger::Internal::WatchData", "%1 ").arg(name).arg(seen); +} + /////////////////////////////////////////////////////////////////////// // // WatchModel diff --git a/src/plugins/debugger/watchhandler.h b/src/plugins/debugger/watchhandler.h index dc426f4e22d..155d1de4116 100644 --- a/src/plugins/debugger/watchhandler.h +++ b/src/plugins/debugger/watchhandler.h @@ -117,6 +117,9 @@ public: bool isEqual(const WatchData &other) const; + static QString msgNotInScope(); + static QString shadowedName(const QString &name, int seen); + public: QString iname; // internal name sth like 'local.baz.public.a' QString exp; // the expression @@ -135,6 +138,7 @@ public: int generation; // when updated? bool valueEnabled; // value will be greyed out or not bool valueEditable; // value will be editable + bool error; private: diff --git a/src/plugins/debugger/watchutils.cpp b/src/plugins/debugger/watchutils.cpp index 6fe9256b744..e7d52147aa9 100644 --- a/src/plugins/debugger/watchutils.cpp +++ b/src/plugins/debugger/watchutils.cpp @@ -43,6 +43,9 @@ #include #include +#include +#include +#include #include @@ -51,6 +54,7 @@ #include #include #include +#include #include #include @@ -59,6 +63,78 @@ #include enum { debug = 0 }; + +// Debug helpers for code model. @todo: Move to some CppTools library? +namespace CPlusPlus { + +static void debugCppSymbolRecursion(QTextStream &str, const Overview &o, + const Symbol &s, bool doRecurse = true, + int recursion = 0) +{ + for (int i = 0; i < recursion; i++) + str << " "; + str << "Symbol: " << o.prettyName(s.name()) << " at line " << s.line(); + if (s.isFunction()) + str << " function"; + if (s.isClass()) + str << " class"; + if (s.isDeclaration()) + str << " declaration"; + if (s.isBlock()) + str << " block"; + if (doRecurse && s.isScopedSymbol()) { + const ScopedSymbol *scoped = s.asScopedSymbol(); + const int size = scoped->memberCount(); + str << " scoped symbol of " << size << '\n'; + for (int m = 0; m < size; m++) + debugCppSymbolRecursion(str, o, *scoped->memberAt(m), true, recursion + 1); + } else { + str << '\n'; + } +} + +QDebug operator<<(QDebug d, const Symbol &s) +{ + QString output; + CPlusPlus::Overview o; + QTextStream str(&output); + debugCppSymbolRecursion(str, o, s, true, 0); + d.nospace() << output; + return d; +} + +QDebug operator<<(QDebug d, const Scope &scope) +{ + QString output; + Overview o; + QTextStream str(&output); + const int size = scope.symbolCount(); + str << "Scope of " << size; + if (scope.isNamespaceScope()) + str << " namespace"; + if (scope.isClassScope()) + str << " class"; + if (scope.isEnumScope()) + str << " enum"; + if (scope.isBlockScope()) + str << " block"; + if (scope.isFunctionScope()) + str << " function"; + if (scope.isPrototypeScope()) + str << " prototype"; + if (const Symbol *owner = scope.owner()) { + str << " owner: "; + debugCppSymbolRecursion(str, o, *owner, false, 0); + } else { + str << " 0-owner\n"; + } + for (int s = 0; s < size; s++) + debugCppSymbolRecursion(str, o, *scope.symbolAt(s), true, 2); + d.nospace() << output; + return d; +} +} // namespace CPlusPlus + namespace Debugger { namespace Internal { @@ -217,6 +293,133 @@ QString stripPointerType(QString type) return type; } +/* getUninitializedVariables(): Get variables that are not initialized + * at a certain line of a function from the code model to be able to + * indicate them as not in scope in the locals view. + * Find document + function in the code model, do a double check and + * collect declarative symbols that are in the function past or on + * the current line. blockRecursion() recurses up the scopes + * and collect symbols declared past or on the current line. + * Recursion goes up from the innermost scope, keeping a map + * of occurrences seen, to be able to derive the names of + * shadowed variables as the debugger sees them: +\code +int x; // Occurrence (1), should be reported as "x " +if (true) { + int x = 5; (2) // Occurrence (2), should be reported as "x" +} +\endcode + */ + +typedef QHash SeenHash; + +static void blockRecursion(const CPlusPlus::Overview &overview, + const CPlusPlus::Scope *scope, + unsigned line, + QStringList *uninitializedVariables, + SeenHash *seenHash, + int level = 0) +{ + const int size = scope->symbolCount(); + for (int s = 0; s < size; s++){ + const CPlusPlus::Symbol *symbol = scope->symbolAt(s); + if (symbol->isDeclaration()) { + // Find out about shadowed symbols by bookkeeping + // the already seen occurrences in a hash. + const QString name = overview.prettyName(symbol->name()); + SeenHash::iterator it = seenHash->find(name); + if (it == seenHash->end()) { + it = seenHash->insert(name, 0); + } else { + ++(it.value()); + } + // Is the declaration on or past the current line, that is, + // the variable not initialized. + if (symbol->line() >= line) + uninitializedVariables->push_back(WatchData::shadowedName(name, it.value())); + } + } + // Next block scope. + if (const CPlusPlus::Scope *enclosingScope = scope->enclosingBlockScope()) + blockRecursion(overview, enclosingScope, line, uninitializedVariables, seenHash, level + 1); +} + +// Inline helper with integer error return codes. +static inline +int getUninitializedVariablesI(const CPlusPlus::Snapshot &snapshot, + const QString &functionName, + const QString &file, + int line, + QStringList *uninitializedVariables) +{ + uninitializedVariables->clear(); + // Find document + if (snapshot.empty() || functionName.isEmpty() || file.isEmpty() || line < 1) + return 1; + const CPlusPlus::Snapshot::ConstIterator docIt = snapshot.constFind(file); + if (docIt == snapshot.constEnd()) + return 2; + const CPlusPlus::Document::Ptr doc = docIt.value(); + // Look at symbol at line and find its function. Either it is the + // function itself or some expression/variable. + const CPlusPlus::Symbol *symbolAtLine = doc->findSymbolAt(line, 0); + if (!symbolAtLine) + return 4; + // First figure out the function to do a safety name check. + const CPlusPlus::Function *function = 0; + const bool hitFunction = symbolAtLine->isFunction(); + if (hitFunction) { + function = symbolAtLine->asFunction(); + } else { + if (CPlusPlus::Scope *functionScope = symbolAtLine->enclosingFunctionScope()) + function = functionScope->owner()->asFunction(); + } + if (!function) + return 7; + // Compare function names with a bit off fuzz, + // skipping modules from a CDB symbol "lib!foo" or namespaces + // that the code model does not show at this point + CPlusPlus::Overview overview; + const QString name = overview.prettyName(function->name()); + if (!functionName.endsWith(name)) + return 11; + if (functionName.size() > name.size()) { + const char previousChar = functionName.at(functionName.size() - name.size() - 1).toLatin1(); + if (previousChar != ':' && previousChar != '!' ) + return 11; + } + // Starting from the innermost block scope, collect + // declarations. + const CPlusPlus::Scope *innerMostScope = hitFunction ? + symbolAtLine->asFunction()->members() : + symbolAtLine->enclosingBlockScope(); + if (!innerMostScope) + return 15; + SeenHash seenHash; + blockRecursion(overview, innerMostScope, line, uninitializedVariables, &seenHash); + return 0; +} + +bool getUninitializedVariables(const CPlusPlus::Snapshot &snapshot, + const QString &function, + const QString &file, + int line, + QStringList *uninitializedVariables) +{ + const int rc = getUninitializedVariablesI(snapshot, function, file, line, uninitializedVariables); + if (debug) { + QString msg; + QTextStream str(&msg); + str << "getUninitializedVariables() " << function << ' ' << file << ':' << line + << " returns (int) " << rc << " '" + << uninitializedVariables->join(QString(QLatin1Char(','))) << '\''; + if (rc) + str << " of " << snapshot.keys().size() << " documents"; + qDebug() << msg; + } + return rc == 0; +} + QString gdbQuoteTypes(const QString &type) { // gdb does not understand sizeof(Core::IFile*). diff --git a/src/plugins/debugger/watchutils.h b/src/plugins/debugger/watchutils.h index 9cbb6594813..c7cf4bfdbed 100644 --- a/src/plugins/debugger/watchutils.h +++ b/src/plugins/debugger/watchutils.h @@ -45,6 +45,10 @@ namespace Core { class IEditor; } +namespace CPlusPlus { + class Snapshot; +} + namespace Debugger { namespace Internal { @@ -89,6 +93,15 @@ QString cppExpressionAt(TextEditor::ITextEditor *editor, int pos, // Decode string data as returned by the dumper helpers. QString decodeData(const QByteArray &baIn, int encoding); +// Get variables that are not initialized at a certain line +// of a function from the code model. Shadowed variables will +// be reported using the debugger naming conventions '' +bool getUninitializedVariables(const CPlusPlus::Snapshot &snapshot, + const QString &function, + const QString &file, + int line, + QStringList *uninitializedVariables); + /* Attempt to put common code of the dumper handling into a helper * class. * "Custom dumper" is a library compiled against the current diff --git a/src/plugins/debugger/watchwindow.cpp b/src/plugins/debugger/watchwindow.cpp index 5cf8f4ff534..ac4b6a74033 100644 --- a/src/plugins/debugger/watchwindow.cpp +++ b/src/plugins/debugger/watchwindow.cpp @@ -253,10 +253,11 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev) QAction *actInsertNewWatchItem = menu.addAction(tr("Insert new watch item")); QAction *actSelectWidgetToWatch = menu.addAction(tr("Select widget to watch")); + const bool actionsEnabled = m_manager->debuggerActionsEnabled(); const QString address = model()->data(mi0, AddressRole).toString(); QAction *actWatchKnownMemory = 0; QAction *actWatchUnknownMemory = new QAction(tr("Open memory editor..."), &menu); - actWatchUnknownMemory->setEnabled(m_manager->debuggerActionsEnabled()); + actWatchUnknownMemory->setEnabled(actionsEnabled); if (!address.isEmpty()) actWatchKnownMemory = new QAction(tr("Open memory editor at %1").arg(address), &menu); @@ -276,7 +277,9 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev) menu.addAction(theDebuggerAction(RecheckDebuggingHelpers)); menu.addAction(theDebuggerAction(UseDebuggingHelpers)); - + QAction *actClearCodeModelSnapshot = new QAction(tr("Refresh code model snapshot"), &menu); + actClearCodeModelSnapshot->setEnabled(actionsEnabled && theDebuggerAction(UseCodeModel)->isChecked()); + menu.addAction(actClearCodeModelSnapshot); menu.addSeparator(); menu.addAction(theDebuggerAction(UseToolTipsInLocalsView)); @@ -311,6 +314,8 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev) } else if (act == actSelectWidgetToWatch) { grabMouse(Qt::CrossCursor); m_grabbing = true; + } else if (act == actClearCodeModelSnapshot) { + m_manager->clearCppCodeModelSnapshot(); } else { for (int i = 0; i != alternativeFormats.size(); ++i) { if (act == typeFormatActions.at(i))