2022-08-19 15:59:36 +02:00
|
|
|
// Copyright (C) 2016 The Qt Company Ltd.
|
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
2013-01-24 16:33:17 +01:00
|
|
|
|
|
|
|
|
#include "sourceutils.h"
|
|
|
|
|
|
2015-07-17 16:33:25 +02:00
|
|
|
#include "debuggerinternalconstants.h"
|
|
|
|
|
#include "debuggerengine.h"
|
|
|
|
|
#include "disassemblerlines.h"
|
2013-01-24 16:33:17 +01:00
|
|
|
#include "watchdata.h"
|
|
|
|
|
#include "watchutils.h"
|
|
|
|
|
|
2021-01-18 16:30:38 +01:00
|
|
|
#include <coreplugin/editormanager/ieditor.h>
|
|
|
|
|
|
2014-09-19 17:06:26 +02:00
|
|
|
#include <cplusplus/CppDocument.h>
|
2013-01-24 16:33:17 +01:00
|
|
|
#include <cplusplus/ExpressionUnderCursor.h>
|
2021-01-18 16:30:38 +01:00
|
|
|
#include <cplusplus/LookupItem.h>
|
2013-01-24 16:33:17 +01:00
|
|
|
#include <cplusplus/Overview.h>
|
|
|
|
|
|
2021-08-30 10:58:08 +02:00
|
|
|
#include <cppeditor/abstracteditorsupport.h>
|
|
|
|
|
#include <cppeditor/cppprojectfile.h>
|
|
|
|
|
#include <cppeditor/cppmodelmanager.h>
|
2021-01-18 16:30:38 +01:00
|
|
|
|
|
|
|
|
#include <texteditor/texteditor.h>
|
|
|
|
|
#include <texteditor/textdocument.h>
|
|
|
|
|
|
2014-05-21 15:03:23 +02:00
|
|
|
#include <utils/qtcassert.h>
|
|
|
|
|
|
2013-01-24 16:33:17 +01:00
|
|
|
#include <QDebug>
|
2015-07-20 09:37:54 +02:00
|
|
|
#include <QTextDocument>
|
|
|
|
|
#include <QTextBlock>
|
2013-01-24 16:33:17 +01:00
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
|
|
|
|
|
enum { debug = 0 };
|
|
|
|
|
|
2021-08-30 10:58:08 +02:00
|
|
|
using namespace CppEditor;
|
2014-09-19 17:06:26 +02:00
|
|
|
using namespace CPlusPlus;
|
|
|
|
|
using namespace TextEditor;
|
2021-01-18 16:30:38 +01:00
|
|
|
using namespace Utils;
|
2014-09-19 17:06:26 +02:00
|
|
|
|
2013-01-24 16:33:17 +01:00
|
|
|
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();
|
2022-06-23 16:56:36 +02:00
|
|
|
if (s.asFunction())
|
2013-01-24 16:33:17 +01:00
|
|
|
str << " function";
|
2022-06-23 16:56:36 +02:00
|
|
|
if (s.asClass())
|
2013-01-24 16:33:17 +01:00
|
|
|
str << " class";
|
2022-06-23 16:56:36 +02:00
|
|
|
if (s.asDeclaration())
|
2013-01-24 16:33:17 +01:00
|
|
|
str << " declaration";
|
2022-06-23 16:56:36 +02:00
|
|
|
if (s.asBlock())
|
2013-01-24 16:33:17 +01:00
|
|
|
str << " block";
|
2022-06-23 16:56:36 +02:00
|
|
|
if (doRecurse && s.asScope()) {
|
2013-01-24 16:33:17 +01:00
|
|
|
const Scope *scoped = s.asScope();
|
|
|
|
|
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;
|
2014-09-19 17:06:26 +02:00
|
|
|
Overview o;
|
2013-01-24 16:33:17 +01:00
|
|
|
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.memberCount();
|
|
|
|
|
str << "Scope of " << size;
|
2022-06-23 16:56:36 +02:00
|
|
|
if (scope.asNamespace())
|
2013-01-24 16:33:17 +01:00
|
|
|
str << " namespace";
|
2022-06-23 16:56:36 +02:00
|
|
|
if (scope.asClass())
|
2013-01-24 16:33:17 +01:00
|
|
|
str << " class";
|
2022-06-23 16:56:36 +02:00
|
|
|
if (scope.asEnum())
|
2013-01-24 16:33:17 +01:00
|
|
|
str << " enum";
|
2022-06-23 16:56:36 +02:00
|
|
|
if (scope.asBlock())
|
2013-01-24 16:33:17 +01:00
|
|
|
str << " block";
|
2022-06-23 16:56:36 +02:00
|
|
|
if (scope.asFunction())
|
2013-01-24 16:33:17 +01:00
|
|
|
str << " function";
|
2022-06-23 16:56:36 +02:00
|
|
|
if (scope.asDeclaration())
|
2013-01-24 16:33:17 +01:00
|
|
|
str << " prototype";
|
|
|
|
|
#if 0 // ### port me
|
|
|
|
|
if (const Symbol *owner = &scope) {
|
|
|
|
|
str << " owner: ";
|
|
|
|
|
debugCppSymbolRecursion(str, o, *owner, false, 0);
|
|
|
|
|
} else {
|
|
|
|
|
str << " 0-owner\n";
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
for (int s = 0; s < size; s++)
|
|
|
|
|
debugCppSymbolRecursion(str, o, *scope.memberAt(s), true, 2);
|
|
|
|
|
d.nospace() << output;
|
|
|
|
|
return d;
|
|
|
|
|
}
|
2014-09-19 17:06:26 +02:00
|
|
|
|
2013-01-24 16:33:17 +01:00
|
|
|
} // namespace CPlusPlus
|
|
|
|
|
|
2022-07-05 15:37:08 +02:00
|
|
|
namespace Debugger::Internal {
|
2013-01-24 16:33:17 +01:00
|
|
|
|
|
|
|
|
/* 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 <shadowed 1>"
|
|
|
|
|
if (true) {
|
|
|
|
|
int x = 5; (2) // Occurrence (2), should be reported as "x"
|
|
|
|
|
}
|
|
|
|
|
\endcode
|
|
|
|
|
*/
|
|
|
|
|
|
2018-07-23 22:28:49 +02:00
|
|
|
using SeenHash = QHash<QString, int>;
|
2013-01-24 16:33:17 +01:00
|
|
|
|
2014-09-19 17:06:26 +02:00
|
|
|
static void blockRecursion(const Overview &overview,
|
|
|
|
|
const Scope *scope,
|
2019-07-24 18:40:10 +02:00
|
|
|
int line,
|
2013-01-24 16:33:17 +01:00
|
|
|
QStringList *uninitializedVariables,
|
|
|
|
|
SeenHash *seenHash,
|
|
|
|
|
int level = 0)
|
|
|
|
|
{
|
|
|
|
|
// Go backwards in case someone has identical variables in the same scope.
|
|
|
|
|
// Fixme: loop variables or similar are currently seen in the outer scope
|
|
|
|
|
for (int s = scope->memberCount() - 1; s >= 0; --s){
|
2019-09-19 16:36:43 +02:00
|
|
|
const CPlusPlus::Symbol *symbol = scope->memberAt(s);
|
2022-06-23 16:56:36 +02:00
|
|
|
if (symbol->asDeclaration()) {
|
2013-01-24 16:33:17 +01:00
|
|
|
// 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)
|
2015-12-16 17:17:38 +01:00
|
|
|
uninitializedVariables->push_back(WatchItem::shadowedName(name, it.value()));
|
2013-01-24 16:33:17 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Next block scope.
|
2014-09-19 17:06:26 +02:00
|
|
|
if (const Scope *enclosingScope = scope->enclosingBlock())
|
2013-01-24 16:33:17 +01:00
|
|
|
blockRecursion(overview, enclosingScope, line, uninitializedVariables, seenHash, level + 1);
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-25 15:37:39 +02:00
|
|
|
QStringList getUninitializedVariables(const Snapshot &snapshot,
|
|
|
|
|
const QString &functionName,
|
2021-10-26 10:48:43 +02:00
|
|
|
const FilePath &file,
|
2018-05-25 15:37:39 +02:00
|
|
|
int line)
|
2013-01-24 16:33:17 +01:00
|
|
|
{
|
2018-05-25 15:37:39 +02:00
|
|
|
QStringList result;
|
2013-01-24 16:33:17 +01:00
|
|
|
// Find document
|
|
|
|
|
if (snapshot.isEmpty() || functionName.isEmpty() || file.isEmpty() || line < 1)
|
2018-05-25 15:37:39 +02:00
|
|
|
return result;
|
2014-09-19 17:06:26 +02:00
|
|
|
const Snapshot::const_iterator docIt = snapshot.find(file);
|
2013-01-24 16:33:17 +01:00
|
|
|
if (docIt == snapshot.end())
|
2018-05-25 15:37:39 +02:00
|
|
|
return result;
|
2014-09-19 17:06:26 +02:00
|
|
|
const Document::Ptr doc = docIt.value();
|
2013-01-24 16:33:17 +01:00
|
|
|
// Look at symbol at line and find its function. Either it is the
|
|
|
|
|
// function itself or some expression/variable.
|
2019-09-19 16:36:43 +02:00
|
|
|
const CPlusPlus::Symbol *symbolAtLine = doc->lastVisibleSymbolAt(line, 0);
|
2013-01-24 16:33:17 +01:00
|
|
|
if (!symbolAtLine)
|
2018-05-25 15:37:39 +02:00
|
|
|
return result;
|
2013-01-24 16:33:17 +01:00
|
|
|
// First figure out the function to do a safety name check
|
|
|
|
|
// and the innermost scope at cursor position
|
2018-02-01 10:59:24 +01:00
|
|
|
const Function *function = nullptr;
|
|
|
|
|
const Scope *innerMostScope = nullptr;
|
2022-06-23 16:56:36 +02:00
|
|
|
if (symbolAtLine->asFunction()) {
|
2013-01-24 16:33:17 +01:00
|
|
|
function = symbolAtLine->asFunction();
|
|
|
|
|
if (function->memberCount() == 1) // Skip over function block
|
2014-09-19 17:06:26 +02:00
|
|
|
if (Block *block = function->memberAt(0)->asBlock())
|
2013-01-24 16:33:17 +01:00
|
|
|
innerMostScope = block;
|
|
|
|
|
} else {
|
2014-09-19 17:06:26 +02:00
|
|
|
if (const Scope *functionScope = symbolAtLine->enclosingFunction()) {
|
2013-01-24 16:33:17 +01:00
|
|
|
function = functionScope->asFunction();
|
2022-06-23 16:56:36 +02:00
|
|
|
innerMostScope = symbolAtLine->asBlock() ?
|
2013-01-24 16:33:17 +01:00
|
|
|
symbolAtLine->asBlock() :
|
|
|
|
|
symbolAtLine->enclosingBlock();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!function || !innerMostScope)
|
2018-05-25 15:37:39 +02:00
|
|
|
return result;
|
2013-01-24 16:33:17 +01:00
|
|
|
// 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
|
2014-09-19 17:06:26 +02:00
|
|
|
Overview overview;
|
2013-01-24 16:33:17 +01:00
|
|
|
const QString name = overview.prettyName(function->name());
|
|
|
|
|
if (!functionName.endsWith(name))
|
2018-05-25 15:37:39 +02:00
|
|
|
return result;
|
2013-01-24 16:33:17 +01:00
|
|
|
if (functionName.size() > name.size()) {
|
|
|
|
|
const char previousChar = functionName.at(functionName.size() - name.size() - 1).toLatin1();
|
|
|
|
|
if (previousChar != ':' && previousChar != '!' )
|
2018-05-25 15:37:39 +02:00
|
|
|
return result;
|
2013-01-24 16:33:17 +01:00
|
|
|
}
|
|
|
|
|
// Starting from the innermost block scope, collect declarations.
|
|
|
|
|
SeenHash seenHash;
|
2018-05-25 15:37:39 +02:00
|
|
|
blockRecursion(overview, innerMostScope, line, &result, &seenHash);
|
|
|
|
|
return result;
|
2013-01-24 16:33:17 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-19 17:06:26 +02:00
|
|
|
QString cppFunctionAt(const QString &fileName, int line, int column)
|
|
|
|
|
{
|
2014-11-15 13:33:40 +01:00
|
|
|
const Snapshot snapshot = CppModelManager::instance()->snapshot();
|
2014-09-19 17:06:26 +02:00
|
|
|
if (const Document::Ptr document = snapshot.document(fileName))
|
|
|
|
|
return document->functionAt(line, column);
|
|
|
|
|
|
|
|
|
|
return QString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-01-24 16:33:17 +01:00
|
|
|
// Return the Cpp expression, and, if desired, the function
|
2014-09-26 11:37:54 +02:00
|
|
|
QString cppExpressionAt(TextEditorWidget *editorWidget, int pos,
|
2014-11-15 13:33:40 +01:00
|
|
|
int *line, int *column, QString *function,
|
|
|
|
|
int *scopeFromLine, int *scopeToLine)
|
2013-01-24 16:33:17 +01:00
|
|
|
{
|
|
|
|
|
if (function)
|
|
|
|
|
function->clear();
|
|
|
|
|
|
2015-02-26 09:14:38 +02:00
|
|
|
const QString fileName = editorWidget->textDocument()->filePath().toString();
|
|
|
|
|
const Snapshot snapshot = CppModelManager::instance()->snapshot();
|
|
|
|
|
const Document::Ptr document = snapshot.document(fileName);
|
2014-09-19 15:26:41 +02:00
|
|
|
QTextCursor tc = editorWidget->textCursor();
|
2020-11-02 11:03:29 +01:00
|
|
|
QString expr;
|
|
|
|
|
if (tc.hasSelection() && pos >= tc.selectionStart() && pos <= tc.selectionEnd()) {
|
|
|
|
|
expr = tc.selectedText();
|
|
|
|
|
} else {
|
2013-01-24 16:33:17 +01:00
|
|
|
tc.setPosition(pos);
|
2014-09-19 15:26:41 +02:00
|
|
|
const QChar ch = editorWidget->characterAt(pos);
|
2018-10-07 22:38:47 +03:00
|
|
|
if (ch.isLetterOrNumber() || ch == '_')
|
2013-01-24 16:33:17 +01:00
|
|
|
tc.movePosition(QTextCursor::EndOfWord);
|
|
|
|
|
|
|
|
|
|
// Fetch the expression's code.
|
2015-02-26 09:14:38 +02:00
|
|
|
ExpressionUnderCursor expressionUnderCursor(document ? document->languageFeatures()
|
|
|
|
|
: LanguageFeatures::defaultFeatures());
|
2013-01-24 16:33:17 +01:00
|
|
|
expr = expressionUnderCursor(tc);
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-10 16:16:31 +01:00
|
|
|
*column = tc.positionInBlock();
|
|
|
|
|
*line = tc.blockNumber() + 1;
|
|
|
|
|
|
2015-02-26 09:14:38 +02:00
|
|
|
if (!expr.isEmpty() && document) {
|
|
|
|
|
QString func = document->functionAt(*line, *column, scopeFromLine, scopeToLine);
|
|
|
|
|
if (function)
|
|
|
|
|
*function = func;
|
2014-11-15 13:33:40 +01:00
|
|
|
}
|
2013-01-24 16:33:17 +01:00
|
|
|
|
|
|
|
|
return expr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ensure an expression can be added as side-effect
|
|
|
|
|
// free debugger expression.
|
|
|
|
|
QString fixCppExpression(const QString &expIn)
|
|
|
|
|
{
|
2013-09-03 18:48:02 +02:00
|
|
|
QString exp = expIn.trimmed();
|
2013-01-24 16:33:17 +01:00
|
|
|
// Extract the first identifier, everything else is considered
|
|
|
|
|
// too dangerous.
|
|
|
|
|
int pos1 = 0, pos2 = exp.size();
|
|
|
|
|
bool inId = false;
|
|
|
|
|
for (int i = 0; i != exp.size(); ++i) {
|
|
|
|
|
const QChar c = exp.at(i);
|
|
|
|
|
const bool isIdChar = c.isLetterOrNumber() || c.unicode() == '_';
|
|
|
|
|
if (inId && !isIdChar) {
|
|
|
|
|
pos2 = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (!inId && isIdChar) {
|
|
|
|
|
inId = true;
|
|
|
|
|
pos1 = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
exp = exp.mid(pos1, pos2 - pos1);
|
|
|
|
|
return removeObviousSideEffects(exp);
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-17 16:33:25 +02:00
|
|
|
ContextData getLocationContext(TextDocument *document, int lineNumber)
|
|
|
|
|
{
|
|
|
|
|
ContextData data;
|
|
|
|
|
QTC_ASSERT(document, return data);
|
|
|
|
|
if (document->property(Constants::OPENED_WITH_DISASSEMBLY).toBool()) {
|
2015-07-20 09:37:54 +02:00
|
|
|
QString line = document->document()->findBlockByNumber(lineNumber - 1).text();
|
|
|
|
|
DisassemblerLine l;
|
|
|
|
|
l.fromString(line);
|
|
|
|
|
if (l.address) {
|
|
|
|
|
data.type = LocationByAddress;
|
|
|
|
|
data.address = l.address;
|
|
|
|
|
} else {
|
|
|
|
|
QString fileName = document->property(Constants::DISASSEMBLER_SOURCE_FILE).toString();
|
|
|
|
|
if (!fileName.isEmpty()) {
|
|
|
|
|
// Possibly one of the "27 [1] foo = x" lines
|
2018-10-07 22:38:47 +03:00
|
|
|
int pos = line.indexOf('[');
|
2020-09-18 13:05:37 +02:00
|
|
|
int ln = line.left(pos - 1).toInt();
|
2015-07-20 09:37:54 +02:00
|
|
|
if (ln > 0) {
|
|
|
|
|
data.type = LocationByFile;
|
2020-01-02 11:31:14 +01:00
|
|
|
data.fileName = Utils::FilePath::fromString(fileName);
|
2015-07-20 09:37:54 +02:00
|
|
|
data.lineNumber = ln;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-07-17 16:33:25 +02:00
|
|
|
} else {
|
2015-07-20 09:37:54 +02:00
|
|
|
data.type = LocationByFile;
|
2020-01-02 11:31:14 +01:00
|
|
|
data.fileName = document->filePath();
|
2015-07-17 16:33:25 +02:00
|
|
|
data.lineNumber = lineNumber;
|
|
|
|
|
}
|
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-18 16:30:38 +01:00
|
|
|
//
|
|
|
|
|
// Annotations
|
|
|
|
|
//
|
|
|
|
|
class DebuggerValueMark : public TextEditor::TextMark
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
DebuggerValueMark(const FilePath &fileName, int lineNumber, const QString &value)
|
|
|
|
|
: TextMark(fileName, lineNumber, Constants::TEXT_MARK_CATEGORY_VALUE)
|
|
|
|
|
{
|
|
|
|
|
setPriority(TextEditor::TextMark::HighPriority);
|
|
|
|
|
setToolTipProvider([] { return QString(); });
|
|
|
|
|
setLineAnnotation(value);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static QList<DebuggerValueMark *> marks;
|
|
|
|
|
|
|
|
|
|
// Stolen from CPlusPlus::Document::functionAt(...)
|
|
|
|
|
static int firstRelevantLine(const Document::Ptr document, int line, int column)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(line > 0 && column > 0, return 0);
|
|
|
|
|
CPlusPlus::Symbol *symbol = document->lastVisibleSymbolAt(line, column);
|
|
|
|
|
if (!symbol)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
// Find the enclosing function scope (which might be several levels up,
|
|
|
|
|
// or we might be standing on it)
|
|
|
|
|
Scope *scope = symbol->asScope();
|
|
|
|
|
if (!scope)
|
|
|
|
|
scope = symbol->enclosingScope();
|
|
|
|
|
|
2022-06-23 16:56:36 +02:00
|
|
|
while (scope && !scope->asFunction() )
|
2021-01-18 16:30:38 +01:00
|
|
|
scope = scope->enclosingScope();
|
|
|
|
|
|
|
|
|
|
if (!scope)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return scope->line();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void setValueAnnotationsHelper(BaseTextEditor *textEditor,
|
|
|
|
|
const Location &loc,
|
|
|
|
|
QMap<QString, QString> values)
|
|
|
|
|
{
|
|
|
|
|
TextEditorWidget *widget = textEditor->editorWidget();
|
|
|
|
|
TextDocument *textDocument = widget->textDocument();
|
|
|
|
|
const FilePath filePath = loc.fileName();
|
|
|
|
|
const Snapshot snapshot = CppModelManager::instance()->snapshot();
|
|
|
|
|
const Document::Ptr cppDocument = snapshot.document(filePath.toString());
|
|
|
|
|
if (!cppDocument) // For non-C++ documents.
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const int firstLine = firstRelevantLine(cppDocument, loc.lineNumber(), 1);
|
|
|
|
|
if (firstLine < 1)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
CPlusPlus::ExpressionUnderCursor expressionUnderCursor(cppDocument->languageFeatures());
|
|
|
|
|
QTextCursor tc = widget->textCursor();
|
|
|
|
|
for (int lineNumber = loc.lineNumber(); lineNumber >= firstLine; --lineNumber) {
|
|
|
|
|
const QTextBlock block = textDocument->document()->findBlockByNumber(lineNumber - 1);
|
|
|
|
|
tc.setPosition(block.position());
|
|
|
|
|
for (; !tc.atBlockEnd(); tc.movePosition(QTextCursor::NextCharacter)) {
|
|
|
|
|
const QString expression = expressionUnderCursor(tc);
|
|
|
|
|
if (expression.isEmpty())
|
|
|
|
|
continue;
|
2021-06-24 16:05:27 +02:00
|
|
|
const QString value = escapeUnprintable(values.take(expression)); // Show value one only once.
|
2021-01-18 16:30:38 +01:00
|
|
|
if (value.isEmpty())
|
|
|
|
|
continue;
|
|
|
|
|
const QString annotation = QString("%1: %2").arg(expression, value);
|
|
|
|
|
marks.append(new DebuggerValueMark(filePath, lineNumber, annotation));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void setValueAnnotations(const Location &loc, const QMap<QString, QString> &values)
|
|
|
|
|
{
|
|
|
|
|
qDeleteAll(marks);
|
|
|
|
|
marks.clear();
|
|
|
|
|
if (values.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const QList<Core::IEditor *> editors = Core::EditorManager::visibleEditors();
|
|
|
|
|
for (Core::IEditor *editor : editors) {
|
|
|
|
|
if (auto textEditor = qobject_cast<BaseTextEditor *>(editor)) {
|
|
|
|
|
if (textEditor->textDocument()->filePath() == loc.fileName())
|
|
|
|
|
setValueAnnotationsHelper(textEditor, loc, values);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-05 15:37:08 +02:00
|
|
|
} // Debugger::Internal
|