forked from qt-creator/qt-creator
Debugger: pmr containers
Added support for visualization of for `std::pmr` containers (and more broadly containers with custom non-empty allocators). The gotcha with pmr allocators in particular is that unlike most allocators these are non-empty classes: they contain a pointer to a memory resource. Tested locally with CDB and GDB (MinGW). Task number: QTCREATORBUG-30224 Change-Id: If0ceb8a46905151a98f27d3e3ff11386406a9737 Reviewed-by: hjk <hjk@qt.io> Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -53,7 +53,28 @@ def qdump__std____1__list(d, value):
|
|||||||
|
|
||||||
|
|
||||||
def qdump__std____1__set(d, value):
|
def qdump__std____1__set(d, value):
|
||||||
(proxy, head, size) = value.split("ppp")
|
# Looking up the allocator template type is potentially an expensive operation.
|
||||||
|
# A better alternative would be finding out the allocator type through accessing a class member.
|
||||||
|
# However due to different implementations doing things very differently
|
||||||
|
# it is deemed acceptable.
|
||||||
|
# Specifically:
|
||||||
|
# * GCC's `_Rb_tree_impl` basically inherits from the allocator, the comparator
|
||||||
|
# and the sentinel node
|
||||||
|
# * Clang packs the allocator and the sentinel node into a compressed pair,
|
||||||
|
# so depending on whether the allocator is sized or not,
|
||||||
|
# there may or may not be a member to access
|
||||||
|
# * MSVC goes even one step further and stores the allocator and the sentinel node together
|
||||||
|
# in a compressed pair, which in turn is stored together with the comparator inside
|
||||||
|
# another compressed pair
|
||||||
|
alloc_type = value.type[2]
|
||||||
|
alloc_size = alloc_type.size()
|
||||||
|
# size of empty allocators (which are the majority) is reported as 1
|
||||||
|
# in theory there can be allocators whose size is truly 1 byte,
|
||||||
|
# in which case this will cause issues, but in practice they should be rather rare
|
||||||
|
if alloc_size > 1:
|
||||||
|
(proxy, head, alloc, size) = value.split(f'pp{{{alloc_type.name}}}p')
|
||||||
|
else:
|
||||||
|
(proxy, head, size) = value.split("ppp")
|
||||||
|
|
||||||
d.check(0 <= size and size <= 100 * 1000 * 1000)
|
d.check(0 <= size and size <= 100 * 1000 * 1000)
|
||||||
d.putItemCount(size)
|
d.putItemCount(size)
|
||||||
|
@@ -1,6 +1,25 @@
|
|||||||
# Copyright (C) 2016 The Qt Company Ltd.
|
# Copyright (C) 2016 The Qt Company Ltd.
|
||||||
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
# Disclaimers:
|
||||||
|
# 1. Looking up the allocator template type is potentially an expensive operation.
|
||||||
|
# A better alternative would be finding out the allocator type
|
||||||
|
# through accessing a class member.
|
||||||
|
# However due to different implementations doing things very differently
|
||||||
|
# it is deemed acceptable.
|
||||||
|
# Specifically:
|
||||||
|
# * GCC's `_Rb_tree_impl` basically inherits from the allocator, the comparator
|
||||||
|
# and the sentinel node
|
||||||
|
# * Clang packs the allocator and the sentinel node into a compressed pair,
|
||||||
|
# so depending on whether the allocator is sized or not,
|
||||||
|
# there may or may not be a member to access
|
||||||
|
# * MSVC goes even one step further and stores the allocator and the sentinel node together
|
||||||
|
# in a compressed pair, which in turn is stored together with the comparator inside
|
||||||
|
# another compressed pair
|
||||||
|
# 2. `size` on an empty type, which the majority of the allocators are of,
|
||||||
|
# for whatever reason reports 1. In theory there can be allocators whose type is truly 1 byte,
|
||||||
|
# in which case we will have issues, but in practice they should be rather rare.
|
||||||
|
|
||||||
from utils import DisplayFormat
|
from utils import DisplayFormat
|
||||||
from dumper import Children, SubItem, DumperBase
|
from dumper import Children, SubItem, DumperBase
|
||||||
|
|
||||||
@@ -94,7 +113,13 @@ def qdumpHelper__std__deque__libstdcxx(d, value):
|
|||||||
pcur = pfirst
|
pcur = pfirst
|
||||||
|
|
||||||
def qdumpHelper__std__deque__libcxx(d, value):
|
def qdumpHelper__std__deque__libcxx(d, value):
|
||||||
mptr, mfirst, mbegin, mend, start, size = value.split("pppptt")
|
alloc_type = value.type[1] # see disclaimer #1
|
||||||
|
alloc_size = alloc_type.size()
|
||||||
|
# see disclaimer #2
|
||||||
|
if alloc_size > 1:
|
||||||
|
mptr, mfirst, mbegin, mend, alloc, start, size = value.split(f'pppp{{{alloc_type.name}}}tt')
|
||||||
|
else:
|
||||||
|
mptr, mfirst, mbegin, mend, start, size = value.split("pppptt")
|
||||||
d.check(0 <= size and size <= 1000 * 1000 * 1000)
|
d.check(0 <= size and size <= 1000 * 1000 * 1000)
|
||||||
d.putItemCount(size)
|
d.putItemCount(size)
|
||||||
if d.isExpanded():
|
if d.isExpanded():
|
||||||
@@ -160,7 +185,11 @@ def qdumpHelper__std__deque__msvc(d, value):
|
|||||||
else:
|
else:
|
||||||
bufsize = 1
|
bufsize = 1
|
||||||
|
|
||||||
(proxy, map, mapsize, myoff, mysize) = value.split("ppppp")
|
alloc_size = value.type[1].size() # see disclaimer #1
|
||||||
|
# see disclaimer #2
|
||||||
|
offset = alloc_size if alloc_size > 1 else 0
|
||||||
|
core = d.createValue(value.address() + offset, value.type)
|
||||||
|
(proxy, map, mapsize, myoff, mysize) = core.split("ppppp")
|
||||||
|
|
||||||
d.check(0 <= mapsize and mapsize <= 1000 * 1000 * 1000)
|
d.check(0 <= mapsize and mapsize <= 1000 * 1000 * 1000)
|
||||||
d.putItemCount(mysize)
|
d.putItemCount(mysize)
|
||||||
@@ -221,10 +250,14 @@ def qdump__std__list__QNX(d, value):
|
|||||||
d.isDebugBuild = True
|
d.isDebugBuild = True
|
||||||
except Exception:
|
except Exception:
|
||||||
d.isDebugBuild = False
|
d.isDebugBuild = False
|
||||||
|
|
||||||
|
alloc_size = value.type[1].size() # see disclaimer #1
|
||||||
|
# see disclaimer #2
|
||||||
|
offset = alloc_size if alloc_size > 1 else 0
|
||||||
if d.isDebugBuild:
|
if d.isDebugBuild:
|
||||||
(proxy, head, size) = value.split("ppp")
|
offset += d.ptrSize() # _Myval2.Myproxy
|
||||||
else:
|
core = d.createValue(value.address() + offset, value.type)
|
||||||
(head, size) = value.split("pp")
|
(head, size) = core.split("pp")
|
||||||
|
|
||||||
d.putItemCount(size, 1000)
|
d.putItemCount(size, 1000)
|
||||||
|
|
||||||
@@ -331,10 +364,13 @@ def qdump_std__map__helper(d, value):
|
|||||||
d.isDebugBuild = True
|
d.isDebugBuild = True
|
||||||
except Exception:
|
except Exception:
|
||||||
d.isDebugBuild = False
|
d.isDebugBuild = False
|
||||||
|
alloc_size = value.type[3].size() # see disclaimer #1
|
||||||
|
# see disclaimer #2
|
||||||
|
offset = alloc_size if alloc_size > 1 else 0
|
||||||
if d.isDebugBuild:
|
if d.isDebugBuild:
|
||||||
(proxy, head, size) = value.split("ppp")
|
offset += d.ptrSize() # _Myval2.Myproxy
|
||||||
else:
|
core = d.createValue(value.address() + offset, value.type)
|
||||||
(head, size) = value.split("pp")
|
(head, size) = core.split("pp")
|
||||||
d.check(0 <= size and size <= 100 * 1000 * 1000)
|
d.check(0 <= size and size <= 100 * 1000 * 1000)
|
||||||
d.putItemCount(size)
|
d.putItemCount(size)
|
||||||
if d.isExpanded():
|
if d.isExpanded():
|
||||||
@@ -559,10 +595,14 @@ def qdump__std__set__QNX(d, value):
|
|||||||
d.isDebugBuild = True
|
d.isDebugBuild = True
|
||||||
except Exception:
|
except Exception:
|
||||||
d.isDebugBuild = False
|
d.isDebugBuild = False
|
||||||
|
|
||||||
|
alloc_size = value.type[2].size() # see disclaimer #1
|
||||||
|
# see disclaimer #2
|
||||||
|
offset = alloc_size if alloc_size > 1 else 0
|
||||||
if d.isDebugBuild:
|
if d.isDebugBuild:
|
||||||
(proxy, head, size) = value.split("ppp")
|
offset += d.ptrSize() # _Myval2.Myproxy
|
||||||
else:
|
core = d.createValue(value.address() + offset, value.type)
|
||||||
(head, size) = value.split("pp")
|
(head, size) = core.split("pp")
|
||||||
d.check(0 <= size and size <= 100 * 1000 * 1000)
|
d.check(0 <= size and size <= 100 * 1000 * 1000)
|
||||||
d.putItemCount(size)
|
d.putItemCount(size)
|
||||||
if d.isExpanded():
|
if d.isExpanded():
|
||||||
@@ -840,10 +880,14 @@ def qdump__std__unordered_map(d, value):
|
|||||||
d.isDebugBuild = True
|
d.isDebugBuild = True
|
||||||
except Exception:
|
except Exception:
|
||||||
d.isDebugBuild = False
|
d.isDebugBuild = False
|
||||||
|
|
||||||
|
alloc_size = value.type[4].size() # see disclaimer #1
|
||||||
|
# see disclaimer #2
|
||||||
|
offset = alloc_size if alloc_size > 1 else 0
|
||||||
if d.isDebugBuild:
|
if d.isDebugBuild:
|
||||||
(_, start, size) = _list.split("ppp")
|
offset += d.ptrSize() # _Myval2.Myproxy
|
||||||
else:
|
core = d.createValue(_list.address() + offset, _list.type)
|
||||||
(start, size) = _list.split("pp")
|
(start, size) = core.split("pp")
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
# gcc ~= 4.7
|
# gcc ~= 4.7
|
||||||
@@ -1083,12 +1127,16 @@ def qdumpHelper__std__vector__msvc(d, value):
|
|||||||
d.isDebugBuild = True
|
d.isDebugBuild = True
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
d.isDebugBuild = False
|
d.isDebugBuild = False
|
||||||
|
|
||||||
|
alloc_size = value.type[1].size() # see disclaimer #1
|
||||||
|
# see disclaimer #2
|
||||||
|
offset = alloc_size if alloc_size > 1 else 0
|
||||||
if d.isDebugBuild:
|
if d.isDebugBuild:
|
||||||
proxy1, proxy2, start, finish, alloc, size = value.split("pppppi")
|
offset += 2 * d.ptrSize() # _Myproxy and _MyVal2._Myproxy
|
||||||
else:
|
core = d.createValue(value.address() + offset, value.type)
|
||||||
start, finish, alloc, size = value.split("pppi")
|
first, last, end, size = core.split("pppi")
|
||||||
d.check(0 <= size and size <= 1000 * 1000 * 1000)
|
d.check(0 <= size and size <= 1000 * 1000 * 1000)
|
||||||
qdumpHelper__std__vector__bool(d, start, size, inner_type)
|
qdumpHelper__std__vector__bool(d, first, size, inner_type)
|
||||||
else:
|
else:
|
||||||
if d.isDebugBuild is None:
|
if d.isDebugBuild is None:
|
||||||
try:
|
try:
|
||||||
@@ -1096,13 +1144,17 @@ def qdumpHelper__std__vector__msvc(d, value):
|
|||||||
d.isDebugBuild = True
|
d.isDebugBuild = True
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
d.isDebugBuild = False
|
d.isDebugBuild = False
|
||||||
|
|
||||||
|
alloc_size = value.type[1].size() # see disclaimer #1
|
||||||
|
# see disclaimer #2
|
||||||
|
offset = alloc_size if alloc_size > 1 else 0
|
||||||
if d.isDebugBuild:
|
if d.isDebugBuild:
|
||||||
proxy, start, finish, alloc = value.split("pppp")
|
offset += d.ptrSize() # _MyVal2._Myproxy
|
||||||
else:
|
core = d.createValue(value.address() + offset, value.type)
|
||||||
start, finish, alloc = value.split("ppp")
|
first, last, end = core.split(f'ppp')
|
||||||
size = (finish - start) // inner_type.size()
|
size = (last - first) // inner_type.size()
|
||||||
d.check(0 <= size and size <= 1000 * 1000 * 1000)
|
d.check(0 <= size and size <= 1000 * 1000 * 1000)
|
||||||
qdumpHelper__std__vector__nonbool(d, start, finish, alloc, inner_type)
|
qdumpHelper__std__vector__nonbool(d, first, last, end, inner_type)
|
||||||
|
|
||||||
|
|
||||||
def qform__std____debug__vector():
|
def qform__std____debug__vector():
|
||||||
|
@@ -87,6 +87,199 @@ static inline QString fixNestedTemplates(QString s)
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void simplifyAllocator(
|
||||||
|
const QString &typeIn,
|
||||||
|
const QString &allocatorTemplateHead,
|
||||||
|
const QString &containerTypePrefix,
|
||||||
|
const bool isLibCpp,
|
||||||
|
QString &type)
|
||||||
|
{
|
||||||
|
const int allocatorTemplateHeadLength = allocatorTemplateHead.length();
|
||||||
|
int start = type.indexOf(allocatorTemplateHead);
|
||||||
|
if (start != -1) {
|
||||||
|
// search for matching '>'
|
||||||
|
int pos;
|
||||||
|
int level = 0;
|
||||||
|
for (pos = start + allocatorTemplateHeadLength - 1; pos < type.size(); ++pos) {
|
||||||
|
int c = type.at(pos).unicode();
|
||||||
|
if (c == '<') {
|
||||||
|
++level;
|
||||||
|
} else if (c == '>') {
|
||||||
|
--level;
|
||||||
|
if (level == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const QString alloc = fixNestedTemplates(type.mid(start, pos + 1 - start).trimmed());
|
||||||
|
const QString inner = fixNestedTemplates(
|
||||||
|
alloc.mid(allocatorTemplateHeadLength, alloc.size() - allocatorTemplateHeadLength - 1)
|
||||||
|
.trimmed());
|
||||||
|
|
||||||
|
const QString allocEsc = QRegularExpression::escape(alloc);
|
||||||
|
const QString innerEsc = QRegularExpression::escape(inner);
|
||||||
|
if (inner == "char") { // std::string
|
||||||
|
simplifyStdString("char", "string", &type);
|
||||||
|
} else if (inner == "wchar_t") { // std::wstring
|
||||||
|
simplifyStdString("wchar_t", "wstring", &type);
|
||||||
|
} else if (inner == "unsigned short") { // std::wstring/MSVC
|
||||||
|
simplifyStdString("unsigned short", "wstring", &type);
|
||||||
|
}
|
||||||
|
// std::vector, std::deque, std::list
|
||||||
|
QRegularExpression re1(QString::fromLatin1("(vector|(forward_)?list|deque)<%1, ?%2\\s*>")
|
||||||
|
.arg(innerEsc, allocEsc));
|
||||||
|
QTC_ASSERT(re1.isValid(), return);
|
||||||
|
QRegularExpressionMatch match = re1.match(type);
|
||||||
|
if (match.hasMatch())
|
||||||
|
type.replace(
|
||||||
|
match.captured(),
|
||||||
|
QString::fromLatin1("%1%2<%3>").arg(containerTypePrefix, match.captured(1), inner));
|
||||||
|
|
||||||
|
// std::stack
|
||||||
|
QRegularExpression stackRE(
|
||||||
|
QString::fromLatin1("stack<%1, ?std::deque<%2> >").arg(innerEsc, innerEsc));
|
||||||
|
QTC_ASSERT(stackRE.isValid(), return);
|
||||||
|
match = stackRE.match(type);
|
||||||
|
if (match.hasMatch())
|
||||||
|
type.replace(
|
||||||
|
match.captured(),
|
||||||
|
QString::fromLatin1("%1stack<%2>").arg(containerTypePrefix, inner));
|
||||||
|
|
||||||
|
// std::hash<char>
|
||||||
|
QRegularExpression hashCharRE(
|
||||||
|
QString::fromLatin1("hash<char, std::char_traits<char>, ?%1\\s*?>").arg(allocEsc));
|
||||||
|
QTC_ASSERT(hashCharRE.isValid(), return);
|
||||||
|
match = hashCharRE.match(type);
|
||||||
|
if (match.hasMatch())
|
||||||
|
type.replace(match.captured(), QString::fromLatin1("hash<char>"));
|
||||||
|
|
||||||
|
// std::set
|
||||||
|
QRegularExpression setRE(QString::fromLatin1("(multi)?set<%1, ?std::less<%2>, ?%3\\s*?>")
|
||||||
|
.arg(innerEsc, innerEsc, allocEsc));
|
||||||
|
QTC_ASSERT(setRE.isValid(), return);
|
||||||
|
match = setRE.match(type);
|
||||||
|
if (match.hasMatch())
|
||||||
|
type.replace(
|
||||||
|
match.captured(),
|
||||||
|
QString::fromLatin1("%1%2set<%3>")
|
||||||
|
.arg(containerTypePrefix, match.captured(1), inner));
|
||||||
|
|
||||||
|
// std::unordered_set
|
||||||
|
QRegularExpression unorderedSetRE(
|
||||||
|
QString::fromLatin1(
|
||||||
|
"unordered_(multi)?set<%1, ?std::hash<%2>, ?std::equal_to<%3>, ?%4\\s*?>")
|
||||||
|
.arg(innerEsc, innerEsc, innerEsc, allocEsc));
|
||||||
|
QTC_ASSERT(unorderedSetRE.isValid(), return);
|
||||||
|
match = unorderedSetRE.match(type);
|
||||||
|
if (match.hasMatch())
|
||||||
|
type.replace(
|
||||||
|
match.captured(),
|
||||||
|
QString::fromLatin1("%1unordered_%2set<%3>")
|
||||||
|
.arg(containerTypePrefix, match.captured(1), inner));
|
||||||
|
|
||||||
|
// boost::unordered_set
|
||||||
|
QRegularExpression boostUnorderedSetRE(
|
||||||
|
QString::fromLatin1("unordered_set<%1, ?boost::hash<%2>, ?std::equal_to<%3>, ?%4\\s*?>")
|
||||||
|
.arg(innerEsc, innerEsc, innerEsc, allocEsc));
|
||||||
|
QTC_ASSERT(boostUnorderedSetRE.isValid(), return);
|
||||||
|
match = boostUnorderedSetRE.match(type);
|
||||||
|
if (match.hasMatch())
|
||||||
|
type.replace(match.captured(), QString::fromLatin1("unordered_set<%1>").arg(inner));
|
||||||
|
|
||||||
|
// std::map
|
||||||
|
if (inner.startsWith("std::pair<")) {
|
||||||
|
// search for outermost ',', split key and value
|
||||||
|
int pos;
|
||||||
|
int level = 0;
|
||||||
|
for (pos = 10; pos < inner.size(); ++pos) {
|
||||||
|
int c = inner.at(pos).unicode();
|
||||||
|
if (c == '<')
|
||||||
|
++level;
|
||||||
|
else if (c == '>')
|
||||||
|
--level;
|
||||||
|
else if (c == ',' && level == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const QString key = chopConst(inner.mid(10, pos - 10));
|
||||||
|
const QString keyEsc = QRegularExpression::escape(key);
|
||||||
|
// Get value: MSVC: 'pair<a const ,b>', gcc: 'pair<const a, b>'
|
||||||
|
if (inner.at(++pos) == ' ')
|
||||||
|
pos++;
|
||||||
|
const QString value = inner.mid(pos, inner.size() - pos - 1).trimmed();
|
||||||
|
const QString valueEsc = QRegularExpression::escape(value);
|
||||||
|
QRegularExpression mapRE1(
|
||||||
|
QString::fromLatin1("(multi)?map<%1, ?%2, ?std::less<%3 ?>, ?%4\\s*?>")
|
||||||
|
.arg(keyEsc, valueEsc, keyEsc, allocEsc));
|
||||||
|
QTC_ASSERT(mapRE1.isValid(), return);
|
||||||
|
match = mapRE1.match(type);
|
||||||
|
if (match.hasMatch()) {
|
||||||
|
type.replace(
|
||||||
|
match.captured(),
|
||||||
|
QString::fromLatin1("%1%2map<%3, %4>")
|
||||||
|
.arg(containerTypePrefix, match.captured(1), key, value));
|
||||||
|
} else {
|
||||||
|
QRegularExpression mapRE2(
|
||||||
|
QString::fromLatin1(
|
||||||
|
"(multi)?map<const %1, ?%2, ?std::less<const %3>, ?%4\\s*?>")
|
||||||
|
.arg(keyEsc, valueEsc, keyEsc, allocEsc));
|
||||||
|
match = mapRE2.match(type);
|
||||||
|
if (match.hasMatch())
|
||||||
|
type.replace(
|
||||||
|
match.captured(),
|
||||||
|
QString::fromLatin1("%1%2map<const %3, %4>")
|
||||||
|
.arg(containerTypePrefix, match.captured(1), key, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// std::unordered_map
|
||||||
|
if (inner.startsWith("std::pair<")) {
|
||||||
|
// search for outermost ',', split key and value
|
||||||
|
int pos;
|
||||||
|
int level = 0;
|
||||||
|
for (pos = 10; pos < inner.size(); ++pos) {
|
||||||
|
int c = inner.at(pos).unicode();
|
||||||
|
if (c == '<')
|
||||||
|
++level;
|
||||||
|
else if (c == '>')
|
||||||
|
--level;
|
||||||
|
else if (c == ',' && level == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const QString key = chopConst(inner.mid(10, pos - 10));
|
||||||
|
const QString keyEsc = QRegularExpression::escape(key);
|
||||||
|
// Get value: MSVC: 'pair<a const ,b>', gcc: 'pair<const a, b>'
|
||||||
|
if (inner.at(++pos) == ' ')
|
||||||
|
pos++;
|
||||||
|
const QString value = inner.mid(pos, inner.size() - pos - 1).trimmed();
|
||||||
|
const QString valueEsc = QRegularExpression::escape(value);
|
||||||
|
QRegularExpression mapRE1(
|
||||||
|
QString::fromLatin1("unordered_(multi)?map<%1, ?%2, ?std::hash<%3 ?>, "
|
||||||
|
"?std::equal_to<%4 ?>, ?%5\\s*?>")
|
||||||
|
.arg(keyEsc, valueEsc, keyEsc, keyEsc, allocEsc));
|
||||||
|
QTC_ASSERT(mapRE1.isValid(), return);
|
||||||
|
match = mapRE1.match(type);
|
||||||
|
if (match.hasMatch())
|
||||||
|
type.replace(
|
||||||
|
match.captured(),
|
||||||
|
QString::fromLatin1("%1unordered_%2map<%3, %4>")
|
||||||
|
.arg(containerTypePrefix, match.captured(1), key, value));
|
||||||
|
|
||||||
|
if (isLibCpp) {
|
||||||
|
QRegularExpression mapRE2(
|
||||||
|
QString::fromLatin1("unordered_(multi)?map<std::string, ?%1, "
|
||||||
|
"?std::hash<char>, ?std::equal_to<std::string>, ?%2\\s*?>")
|
||||||
|
.arg(valueEsc, allocEsc));
|
||||||
|
QTC_ASSERT(mapRE2.isValid(), return);
|
||||||
|
match = mapRE2.match(type);
|
||||||
|
if (match.hasMatch())
|
||||||
|
type.replace(
|
||||||
|
match.captured(),
|
||||||
|
QString::fromLatin1("%1unordered_%2map<std::string, %3>")
|
||||||
|
.arg(containerTypePrefix, match.captured(1), value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QString simplifyType(const QString &typeIn)
|
QString simplifyType(const QString &typeIn)
|
||||||
{
|
{
|
||||||
QString type = typeIn;
|
QString type = typeIn;
|
||||||
@@ -140,153 +333,10 @@ QString simplifyType(const QString &typeIn)
|
|||||||
type.replace(match.captured(), match.captured(1));
|
type.replace(match.captured(), match.captured(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Anything with a std::allocator
|
// Fix e.g. `std::vector<T, std::allocator<T>> -> std::vector<T>`
|
||||||
int start = type.indexOf("std::allocator<");
|
simplifyAllocator(typeIn, "std::allocator<", "", isLibCpp, type);
|
||||||
if (start != -1) {
|
// Fix e.g. `std::vector<T, std::pmr::polynorphic_allocator<T>> -> std::pmr::vector<T>`
|
||||||
// search for matching '>'
|
simplifyAllocator(typeIn, "std::pmr::polymorphic_allocator<", "pmr::", isLibCpp, type);
|
||||||
int pos;
|
|
||||||
int level = 0;
|
|
||||||
for (pos = start + 12; pos < type.size(); ++pos) {
|
|
||||||
int c = type.at(pos).unicode();
|
|
||||||
if (c == '<') {
|
|
||||||
++level;
|
|
||||||
} else if (c == '>') {
|
|
||||||
--level;
|
|
||||||
if (level == 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const QString alloc = fixNestedTemplates(type.mid(start, pos + 1 - start).trimmed());
|
|
||||||
const QString inner = fixNestedTemplates(alloc.mid(15, alloc.size() - 16).trimmed());
|
|
||||||
|
|
||||||
const QString allocEsc = QRegularExpression::escape(alloc);
|
|
||||||
const QString innerEsc = QRegularExpression::escape(inner);
|
|
||||||
if (inner == "char") { // std::string
|
|
||||||
simplifyStdString("char", "string", &type);
|
|
||||||
} else if (inner == "wchar_t") { // std::wstring
|
|
||||||
simplifyStdString("wchar_t", "wstring", &type);
|
|
||||||
} else if (inner == "unsigned short") { // std::wstring/MSVC
|
|
||||||
simplifyStdString("unsigned short", "wstring", &type);
|
|
||||||
}
|
|
||||||
// std::vector, std::deque, std::list
|
|
||||||
QRegularExpression re1(QString::fromLatin1("(vector|list|deque)<%1, ?%2\\s*>").arg(innerEsc, allocEsc));
|
|
||||||
QTC_ASSERT(re1.isValid(), return typeIn);
|
|
||||||
QRegularExpressionMatch match = re1.match(type);
|
|
||||||
if (match.hasMatch())
|
|
||||||
type.replace(match.captured(), QString::fromLatin1("%1<%2>").arg(match.captured(1), inner));
|
|
||||||
|
|
||||||
// std::stack
|
|
||||||
QRegularExpression stackRE(QString::fromLatin1("stack<%1, ?std::deque<%2> >").arg(innerEsc, innerEsc));
|
|
||||||
QTC_ASSERT(stackRE.isValid(), return typeIn);
|
|
||||||
match = stackRE.match(type);
|
|
||||||
if (match.hasMatch())
|
|
||||||
type.replace(match.captured(), QString::fromLatin1("stack<%1>").arg(inner));
|
|
||||||
|
|
||||||
// std::hash<char>
|
|
||||||
QRegularExpression hashCharRE(QString::fromLatin1("hash<char, std::char_traits<char>, ?%1\\s*?>").arg(allocEsc));
|
|
||||||
QTC_ASSERT(hashCharRE.isValid(), return typeIn);
|
|
||||||
match = hashCharRE.match(type);
|
|
||||||
if (match.hasMatch())
|
|
||||||
type.replace(match.captured(), QString::fromLatin1("hash<char>"));
|
|
||||||
|
|
||||||
// std::set
|
|
||||||
QRegularExpression setRE(QString::fromLatin1("set<%1, ?std::less<%2>, ?%3\\s*?>").arg(innerEsc, innerEsc, allocEsc));
|
|
||||||
QTC_ASSERT(setRE.isValid(), return typeIn);
|
|
||||||
match = setRE.match(type);
|
|
||||||
if (match.hasMatch())
|
|
||||||
type.replace(match.captured(), QString::fromLatin1("set<%1>").arg(inner));
|
|
||||||
|
|
||||||
// std::unordered_set
|
|
||||||
QRegularExpression unorderedSetRE(QString::fromLatin1("unordered_(multi)?set<%1, ?std::hash<%2>, ?std::equal_to<%3>, ?%4\\s*?>")
|
|
||||||
.arg(innerEsc, innerEsc, innerEsc, allocEsc));
|
|
||||||
QTC_ASSERT(unorderedSetRE.isValid(), return typeIn);
|
|
||||||
match = unorderedSetRE.match(type);
|
|
||||||
if (match.hasMatch())
|
|
||||||
type.replace(match.captured(), QString::fromLatin1("unordered_%1set<%2>").arg(match.captured(1), inner));
|
|
||||||
|
|
||||||
// boost::unordered_set
|
|
||||||
QRegularExpression boostUnorderedSetRE(QString::fromLatin1("unordered_set<%1, ?boost::hash<%2>, ?std::equal_to<%3>, ?%4\\s*?>")
|
|
||||||
.arg(innerEsc, innerEsc, innerEsc, allocEsc));
|
|
||||||
QTC_ASSERT(boostUnorderedSetRE.isValid(), return typeIn);
|
|
||||||
match = boostUnorderedSetRE.match(type);
|
|
||||||
if (match.hasMatch())
|
|
||||||
type.replace(match.captured(), QString::fromLatin1("unordered_set<%1>").arg(inner));
|
|
||||||
|
|
||||||
// std::map
|
|
||||||
if (inner.startsWith("std::pair<")) {
|
|
||||||
// search for outermost ',', split key and value
|
|
||||||
int pos;
|
|
||||||
int level = 0;
|
|
||||||
for (pos = 10; pos < inner.size(); ++pos) {
|
|
||||||
int c = inner.at(pos).unicode();
|
|
||||||
if (c == '<')
|
|
||||||
++level;
|
|
||||||
else if (c == '>')
|
|
||||||
--level;
|
|
||||||
else if (c == ',' && level == 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const QString key = chopConst(inner.mid(10, pos - 10));
|
|
||||||
const QString keyEsc = QRegularExpression::escape(key);
|
|
||||||
// Get value: MSVC: 'pair<a const ,b>', gcc: 'pair<const a, b>'
|
|
||||||
if (inner.at(++pos) == ' ')
|
|
||||||
pos++;
|
|
||||||
const QString value = inner.mid(pos, inner.size() - pos - 1).trimmed();
|
|
||||||
const QString valueEsc = QRegularExpression::escape(value);
|
|
||||||
QRegularExpression mapRE1(QString::fromLatin1("map<%1, ?%2, ?std::less<%3 ?>, ?%4\\s*?>")
|
|
||||||
.arg(keyEsc, valueEsc, keyEsc, allocEsc));
|
|
||||||
QTC_ASSERT(mapRE1.isValid(), return typeIn);
|
|
||||||
match = mapRE1.match(type);
|
|
||||||
if (match.hasMatch()) {
|
|
||||||
type.replace(match.captured(), QString::fromLatin1("map<%1, %2>").arg(key, value));
|
|
||||||
} else {
|
|
||||||
QRegularExpression mapRE2(QString::fromLatin1("map<const %1, ?%2, ?std::less<const %3>, ?%4\\s*?>")
|
|
||||||
.arg(keyEsc, valueEsc, keyEsc, allocEsc));
|
|
||||||
match = mapRE2.match(type);
|
|
||||||
if (match.hasMatch())
|
|
||||||
type.replace(match.captured(), QString::fromLatin1("map<const %1, %2>").arg(key, value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// std::unordered_map
|
|
||||||
if (inner.startsWith("std::pair<")) {
|
|
||||||
// search for outermost ',', split key and value
|
|
||||||
int pos;
|
|
||||||
int level = 0;
|
|
||||||
for (pos = 10; pos < inner.size(); ++pos) {
|
|
||||||
int c = inner.at(pos).unicode();
|
|
||||||
if (c == '<')
|
|
||||||
++level;
|
|
||||||
else if (c == '>')
|
|
||||||
--level;
|
|
||||||
else if (c == ',' && level == 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const QString key = chopConst(inner.mid(10, pos - 10));
|
|
||||||
const QString keyEsc = QRegularExpression::escape(key);
|
|
||||||
// Get value: MSVC: 'pair<a const ,b>', gcc: 'pair<const a, b>'
|
|
||||||
if (inner.at(++pos) == ' ')
|
|
||||||
pos++;
|
|
||||||
const QString value = inner.mid(pos, inner.size() - pos - 1).trimmed();
|
|
||||||
const QString valueEsc = QRegularExpression::escape(value);
|
|
||||||
QRegularExpression mapRE1(QString::fromLatin1("unordered_(multi)?map<%1, ?%2, ?std::hash<%3 ?>, ?std::equal_to<%4 ?>, ?%5\\s*?>")
|
|
||||||
.arg(keyEsc, valueEsc, keyEsc, keyEsc, allocEsc));
|
|
||||||
QTC_ASSERT(mapRE1.isValid(), return typeIn);
|
|
||||||
match = mapRE1.match(type);
|
|
||||||
if (match.hasMatch())
|
|
||||||
type.replace(match.captured(), QString::fromLatin1("unordered_%1map<%2, %3>").arg(match.captured(1), key, value));
|
|
||||||
|
|
||||||
if (isLibCpp) {
|
|
||||||
QRegularExpression mapRE2(QString::fromLatin1("unordered_map<std::string, ?%1, "
|
|
||||||
"?std::hash<char>, ?std::equal_to<std::string>, ?%2\\s*?>")
|
|
||||||
.arg(valueEsc, allocEsc));
|
|
||||||
QTC_ASSERT(mapRE2.isValid(), return typeIn);
|
|
||||||
match = mapRE2.match(type);
|
|
||||||
if (match.hasMatch())
|
|
||||||
type.replace(match.captured(), QString::fromLatin1("unordered_map<std::string, %2>").arg(value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // with std::allocator
|
|
||||||
}
|
}
|
||||||
type.replace('@', " *");
|
type.replace('@', " *");
|
||||||
type.replace(" >", ">");
|
type.replace(" >", ">");
|
||||||
|
@@ -4838,7 +4838,7 @@ void tst_Dumpers::dumper_data()
|
|||||||
"};\n\n"
|
"};\n\n"
|
||||||
|
|
||||||
"std::deque<MyItem> deq;\n"
|
"std::deque<MyItem> deq;\n"
|
||||||
"for (int i = 0; i < 100; ++i)\n"
|
"for (uint16_t i = 0; i < 100; ++i)\n"
|
||||||
" deq.push_back({i, i});\n",
|
" deq.push_back({i, i});\n",
|
||||||
|
|
||||||
"&deq")
|
"&deq")
|
||||||
@@ -8684,6 +8684,96 @@ void tst_Dumpers::dumper_data()
|
|||||||
+ Check{"err_class", "Unexpected", "tl::expected<ErrCode, MyClass>"}
|
+ Check{"err_class", "Unexpected", "tl::expected<ErrCode, MyClass>"}
|
||||||
+ Check{"err_class.inner.m_x", "10", "int"};
|
+ Check{"err_class.inner.m_x", "10", "int"};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
QTest::newRow("QTCREATORBUG-30224-StdLibPmrContainers") << Data{
|
||||||
|
R"(
|
||||||
|
#include <array>
|
||||||
|
#include <deque>
|
||||||
|
#include <forward_list>
|
||||||
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <vector>
|
||||||
|
)",
|
||||||
|
R"(
|
||||||
|
std::pmr::deque<int> d;
|
||||||
|
std::pmr::forward_list<int> fl;
|
||||||
|
std::pmr::list<int> l;
|
||||||
|
std::pmr::set<int> s;
|
||||||
|
std::pmr::multiset<int> ms;
|
||||||
|
std::pmr::unordered_set<int> us;
|
||||||
|
std::pmr::unordered_multiset<int> ums;
|
||||||
|
std::pmr::map<int, int> m;
|
||||||
|
std::pmr::multimap<int, int> mm;
|
||||||
|
std::pmr::unordered_map<int, int> um;
|
||||||
|
std::pmr::unordered_multimap<int, int> umm;
|
||||||
|
std::pmr::vector<int> v;
|
||||||
|
|
||||||
|
constexpr auto count = 10;
|
||||||
|
for (auto i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
if (i < count / 2) {
|
||||||
|
d.push_back(i);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
d.push_front(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
fl.push_front(i);
|
||||||
|
|
||||||
|
l.push_back(i);
|
||||||
|
|
||||||
|
s.insert(i);
|
||||||
|
ms.insert(i);
|
||||||
|
us.insert(i);
|
||||||
|
ums.insert(i);
|
||||||
|
|
||||||
|
m.emplace(i, i);
|
||||||
|
mm.emplace(i, i);
|
||||||
|
um.emplace(i, i);
|
||||||
|
umm.emplace(i, i);
|
||||||
|
|
||||||
|
v.push_back(i);
|
||||||
|
}
|
||||||
|
)",
|
||||||
|
"&d, &fl, &l, &s, &ms, &us, &ums, &m, &mm, &um, &umm, &v"
|
||||||
|
}
|
||||||
|
+ Check{"d", "<10 items>", "std::pmr::deque<int>"} % NoGdbEngine
|
||||||
|
+ Check{"d", "<10 items>", "std::pmr::deque"} % GdbEngine
|
||||||
|
+ Check{"d.1", "[1]", "8", "int"}
|
||||||
|
+ Check{"fl", "<10 items>", "std::pmr::forward_list<int>"} % NoGdbEngine
|
||||||
|
+ Check{"fl", "<10 items>", "std::pmr::forward_list"} % GdbEngine
|
||||||
|
+ Check{"fl.2", "[2]", "7", "int"}
|
||||||
|
+ Check{"l", "<10 items>", "std::pmr::list<int>"} % NoGdbEngine
|
||||||
|
+ Check{"l", "<10 items>", "std::pmr::list"} % GdbEngine
|
||||||
|
+ Check{"l.3", "[3]", "3", "int"}
|
||||||
|
+ Check{"s", "<10 items>", "std::pmr::set<int>"} % NoGdbEngine
|
||||||
|
+ Check{"s", "<10 items>", "std::pmr::set"} % GdbEngine
|
||||||
|
+ Check{"s.4", "[4]", "4", "int"}
|
||||||
|
+ Check{"ms", "<10 items>", "std::pmr::multiset<int>"} % NoGdbEngine
|
||||||
|
+ Check{"ms", "<10 items>", "std::pmr::multiset"} % GdbEngine
|
||||||
|
+ Check{"ms.4", "[4]", "4", "int"}
|
||||||
|
+ Check{"us", "<10 items>", "std::pmr::unordered_set<int>"} % NoGdbEngine
|
||||||
|
+ Check{"us", "<10 items>", "std::pmr::unordered_set"} % GdbEngine
|
||||||
|
+ Check{"ums", "<10 items>", "std::pmr::unordered_multiset<int>"} % NoGdbEngine
|
||||||
|
+ Check{"ums", "<10 items>", "std::pmr::unordered_multiset"} % GdbEngine
|
||||||
|
+ Check{"m", "<10 items>", "std::pmr::map<int, int>"} % NoGdbEngine
|
||||||
|
+ Check{"m", "<10 items>", "std::pmr::map"} % GdbEngine
|
||||||
|
+ Check{"m.5", "[5] 5", "5", ""}
|
||||||
|
+ Check{"mm", "<10 items>", "std::pmr::multimap<int, int>"} % NoGdbEngine
|
||||||
|
+ Check{"mm", "<10 items>", "std::pmr::multimap"} % GdbEngine
|
||||||
|
+ Check{"mm.5", "[5] 5", "5", ""}
|
||||||
|
+ Check{"um", "<10 items>", "std::pmr::unordered_map<int, int>"} % NoGdbEngine
|
||||||
|
+ Check{"um", "<10 items>", "std::pmr::unordered_map"} % GdbEngine
|
||||||
|
+ Check{"umm", "<10 items>", "std::pmr::unordered_multimap<int, int>"} % NoGdbEngine
|
||||||
|
+ Check{"umm", "<10 items>", "std::pmr::unordered_multimap"} % GdbEngine
|
||||||
|
+ Check{"v", "<10 items>", "std::pmr::vector<int>"} % NoGdbEngine
|
||||||
|
+ Check{"v", "<10 items>", "std::pmr::vector"} % GdbEngine
|
||||||
|
+ Check{"v.6", "[6]", "6", "int"};
|
||||||
|
// clang-format on
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
|
Reference in New Issue
Block a user