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:
Andrii Semkiv
2024-11-06 16:48:11 +01:00
parent 73c5d4e5e5
commit cb23332ae5
4 changed files with 385 additions and 172 deletions

View File

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

View File

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

View File

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

View File

@@ -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[])