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,6 +53,27 @@ def qdump__std____1__list(d, value):
|
||||
|
||||
|
||||
def qdump__std____1__set(d, value):
|
||||
# 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)
|
||||
|
@@ -1,6 +1,25 @@
|
||||
# Copyright (C) 2016 The Qt Company Ltd.
|
||||
# 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 dumper import Children, SubItem, DumperBase
|
||||
|
||||
@@ -94,6 +113,12 @@ def qdumpHelper__std__deque__libstdcxx(d, value):
|
||||
pcur = pfirst
|
||||
|
||||
def qdumpHelper__std__deque__libcxx(d, value):
|
||||
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.putItemCount(size)
|
||||
@@ -160,7 +185,11 @@ def qdumpHelper__std__deque__msvc(d, value):
|
||||
else:
|
||||
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.putItemCount(mysize)
|
||||
@@ -221,10 +250,14 @@ def qdump__std__list__QNX(d, value):
|
||||
d.isDebugBuild = True
|
||||
except Exception:
|
||||
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:
|
||||
(proxy, head, size) = value.split("ppp")
|
||||
else:
|
||||
(head, size) = value.split("pp")
|
||||
offset += d.ptrSize() # _Myval2.Myproxy
|
||||
core = d.createValue(value.address() + offset, value.type)
|
||||
(head, size) = core.split("pp")
|
||||
|
||||
d.putItemCount(size, 1000)
|
||||
|
||||
@@ -331,10 +364,13 @@ def qdump_std__map__helper(d, value):
|
||||
d.isDebugBuild = True
|
||||
except Exception:
|
||||
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:
|
||||
(proxy, head, size) = value.split("ppp")
|
||||
else:
|
||||
(head, size) = value.split("pp")
|
||||
offset += d.ptrSize() # _Myval2.Myproxy
|
||||
core = d.createValue(value.address() + offset, value.type)
|
||||
(head, size) = core.split("pp")
|
||||
d.check(0 <= size and size <= 100 * 1000 * 1000)
|
||||
d.putItemCount(size)
|
||||
if d.isExpanded():
|
||||
@@ -559,10 +595,14 @@ def qdump__std__set__QNX(d, value):
|
||||
d.isDebugBuild = True
|
||||
except Exception:
|
||||
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:
|
||||
(proxy, head, size) = value.split("ppp")
|
||||
else:
|
||||
(head, size) = value.split("pp")
|
||||
offset += d.ptrSize() # _Myval2.Myproxy
|
||||
core = d.createValue(value.address() + offset, value.type)
|
||||
(head, size) = core.split("pp")
|
||||
d.check(0 <= size and size <= 100 * 1000 * 1000)
|
||||
d.putItemCount(size)
|
||||
if d.isExpanded():
|
||||
@@ -840,10 +880,14 @@ def qdump__std__unordered_map(d, value):
|
||||
d.isDebugBuild = True
|
||||
except Exception:
|
||||
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:
|
||||
(_, start, size) = _list.split("ppp")
|
||||
else:
|
||||
(start, size) = _list.split("pp")
|
||||
offset += d.ptrSize() # _Myval2.Myproxy
|
||||
core = d.createValue(_list.address() + offset, _list.type)
|
||||
(start, size) = core.split("pp")
|
||||
else:
|
||||
try:
|
||||
# gcc ~= 4.7
|
||||
@@ -1083,12 +1127,16 @@ def qdumpHelper__std__vector__msvc(d, value):
|
||||
d.isDebugBuild = True
|
||||
except RuntimeError:
|
||||
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:
|
||||
proxy1, proxy2, start, finish, alloc, size = value.split("pppppi")
|
||||
else:
|
||||
start, finish, alloc, size = value.split("pppi")
|
||||
offset += 2 * d.ptrSize() # _Myproxy and _MyVal2._Myproxy
|
||||
core = d.createValue(value.address() + offset, value.type)
|
||||
first, last, end, size = core.split("pppi")
|
||||
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:
|
||||
if d.isDebugBuild is None:
|
||||
try:
|
||||
@@ -1096,13 +1144,17 @@ def qdumpHelper__std__vector__msvc(d, value):
|
||||
d.isDebugBuild = True
|
||||
except RuntimeError:
|
||||
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:
|
||||
proxy, start, finish, alloc = value.split("pppp")
|
||||
else:
|
||||
start, finish, alloc = value.split("ppp")
|
||||
size = (finish - start) // inner_type.size()
|
||||
offset += d.ptrSize() # _MyVal2._Myproxy
|
||||
core = d.createValue(value.address() + offset, value.type)
|
||||
first, last, end = core.split(f'ppp')
|
||||
size = (last - first) // inner_type.size()
|
||||
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():
|
||||
|
@@ -87,6 +87,199 @@ static inline QString fixNestedTemplates(QString 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 type = typeIn;
|
||||
@@ -140,153 +333,10 @@ QString simplifyType(const QString &typeIn)
|
||||
type.replace(match.captured(), match.captured(1));
|
||||
}
|
||||
|
||||
// Anything with a std::allocator
|
||||
int start = type.indexOf("std::allocator<");
|
||||
if (start != -1) {
|
||||
// search for matching '>'
|
||||
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
|
||||
// Fix e.g. `std::vector<T, std::allocator<T>> -> std::vector<T>`
|
||||
simplifyAllocator(typeIn, "std::allocator<", "", isLibCpp, type);
|
||||
// Fix e.g. `std::vector<T, std::pmr::polynorphic_allocator<T>> -> std::pmr::vector<T>`
|
||||
simplifyAllocator(typeIn, "std::pmr::polymorphic_allocator<", "pmr::", isLibCpp, type);
|
||||
}
|
||||
type.replace('@', " *");
|
||||
type.replace(" >", ">");
|
||||
|
@@ -4838,7 +4838,7 @@ void tst_Dumpers::dumper_data()
|
||||
"};\n\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")
|
||||
@@ -8684,6 +8684,96 @@ void tst_Dumpers::dumper_data()
|
||||
+ Check{"err_class", "Unexpected", "tl::expected<ErrCode, MyClass>"}
|
||||
+ Check{"err_class.inner.m_x", "10", "int"};
|
||||
// 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[])
|
||||
|
Reference in New Issue
Block a user