From cb23332ae5024358922a28bd1d06eafe4b84a363 Mon Sep 17 00:00:00 2001 From: Andrii Semkiv Date: Wed, 6 Nov 2024 16:48:11 +0100 Subject: [PATCH] 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 Reviewed-by: David Schulz --- share/qtcreator/debugger/libcpp_stdtypes.py | 23 +- share/qtcreator/debugger/stdtypes.py | 98 ++++-- src/plugins/debugger/simplifytype.cpp | 344 +++++++++++--------- tests/auto/debugger/tst_dumpers.cpp | 92 +++++- 4 files changed, 385 insertions(+), 172 deletions(-) diff --git a/share/qtcreator/debugger/libcpp_stdtypes.py b/share/qtcreator/debugger/libcpp_stdtypes.py index 6c3e051261d..899a5be663d 100644 --- a/share/qtcreator/debugger/libcpp_stdtypes.py +++ b/share/qtcreator/debugger/libcpp_stdtypes.py @@ -53,7 +53,28 @@ def qdump__std____1__list(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.putItemCount(size) diff --git a/share/qtcreator/debugger/stdtypes.py b/share/qtcreator/debugger/stdtypes.py index f7acc264ac2..6f913fb1be2 100644 --- a/share/qtcreator/debugger/stdtypes.py +++ b/share/qtcreator/debugger/stdtypes.py @@ -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,7 +113,13 @@ def qdumpHelper__std__deque__libstdcxx(d, value): pcur = pfirst 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.putItemCount(size) if d.isExpanded(): @@ -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(): diff --git a/src/plugins/debugger/simplifytype.cpp b/src/plugins/debugger/simplifytype.cpp index 56e79ea0433..3d59e5a0929 100644 --- a/src/plugins/debugger/simplifytype.cpp +++ b/src/plugins/debugger/simplifytype.cpp @@ -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 + QRegularExpression hashCharRE( + QString::fromLatin1("hash, ?%1\\s*?>").arg(allocEsc)); + QTC_ASSERT(hashCharRE.isValid(), return); + match = hashCharRE.match(type); + if (match.hasMatch()) + type.replace(match.captured(), QString::fromLatin1("hash")); + + // 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', gcc: 'pair' + 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, ?%4\\s*?>") + .arg(keyEsc, valueEsc, keyEsc, allocEsc)); + match = mapRE2.match(type); + if (match.hasMatch()) + type.replace( + match.captured(), + QString::fromLatin1("%1%2map") + .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', gcc: 'pair' + 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::equal_to, ?%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") + .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 - QRegularExpression hashCharRE(QString::fromLatin1("hash, ?%1\\s*?>").arg(allocEsc)); - QTC_ASSERT(hashCharRE.isValid(), return typeIn); - match = hashCharRE.match(type); - if (match.hasMatch()) - type.replace(match.captured(), QString::fromLatin1("hash")); - - // 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', gcc: 'pair' - 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, ?%4\\s*?>") - .arg(keyEsc, valueEsc, keyEsc, allocEsc)); - match = mapRE2.match(type); - if (match.hasMatch()) - type.replace(match.captured(), QString::fromLatin1("map").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', gcc: 'pair' - 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::equal_to, ?%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").arg(value)); - } - } - } // with std::allocator + // Fix e.g. `std::vector> -> std::vector` + simplifyAllocator(typeIn, "std::allocator<", "", isLibCpp, type); + // Fix e.g. `std::vector> -> std::pmr::vector` + simplifyAllocator(typeIn, "std::pmr::polymorphic_allocator<", "pmr::", isLibCpp, type); } type.replace('@', " *"); type.replace(" >", ">"); diff --git a/tests/auto/debugger/tst_dumpers.cpp b/tests/auto/debugger/tst_dumpers.cpp index 45e91af5657..987a8c21ec7 100644 --- a/tests/auto/debugger/tst_dumpers.cpp +++ b/tests/auto/debugger/tst_dumpers.cpp @@ -4838,7 +4838,7 @@ void tst_Dumpers::dumper_data() "};\n\n" "std::deque 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"} + Check{"err_class.inner.m_x", "10", "int"}; // clang-format on + + // clang-format off + QTest::newRow("QTCREATORBUG-30224-StdLibPmrContainers") << Data{ + R"( + #include + #include + #include + #include + #include + #include + #include + #include + #include + )", + R"( + std::pmr::deque d; + std::pmr::forward_list fl; + std::pmr::list l; + std::pmr::set s; + std::pmr::multiset ms; + std::pmr::unordered_set us; + std::pmr::unordered_multiset ums; + std::pmr::map m; + std::pmr::multimap mm; + std::pmr::unordered_map um; + std::pmr::unordered_multimap umm; + std::pmr::vector 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"} % NoGdbEngine + + Check{"d", "<10 items>", "std::pmr::deque"} % GdbEngine + + Check{"d.1", "[1]", "8", "int"} + + Check{"fl", "<10 items>", "std::pmr::forward_list"} % NoGdbEngine + + Check{"fl", "<10 items>", "std::pmr::forward_list"} % GdbEngine + + Check{"fl.2", "[2]", "7", "int"} + + Check{"l", "<10 items>", "std::pmr::list"} % NoGdbEngine + + Check{"l", "<10 items>", "std::pmr::list"} % GdbEngine + + Check{"l.3", "[3]", "3", "int"} + + Check{"s", "<10 items>", "std::pmr::set"} % NoGdbEngine + + Check{"s", "<10 items>", "std::pmr::set"} % GdbEngine + + Check{"s.4", "[4]", "4", "int"} + + Check{"ms", "<10 items>", "std::pmr::multiset"} % NoGdbEngine + + Check{"ms", "<10 items>", "std::pmr::multiset"} % GdbEngine + + Check{"ms.4", "[4]", "4", "int"} + + Check{"us", "<10 items>", "std::pmr::unordered_set"} % NoGdbEngine + + Check{"us", "<10 items>", "std::pmr::unordered_set"} % GdbEngine + + Check{"ums", "<10 items>", "std::pmr::unordered_multiset"} % NoGdbEngine + + Check{"ums", "<10 items>", "std::pmr::unordered_multiset"} % GdbEngine + + Check{"m", "<10 items>", "std::pmr::map"} % NoGdbEngine + + Check{"m", "<10 items>", "std::pmr::map"} % GdbEngine + + Check{"m.5", "[5] 5", "5", ""} + + Check{"mm", "<10 items>", "std::pmr::multimap"} % NoGdbEngine + + Check{"mm", "<10 items>", "std::pmr::multimap"} % GdbEngine + + Check{"mm.5", "[5] 5", "5", ""} + + Check{"um", "<10 items>", "std::pmr::unordered_map"} % NoGdbEngine + + Check{"um", "<10 items>", "std::pmr::unordered_map"} % GdbEngine + + Check{"umm", "<10 items>", "std::pmr::unordered_multimap"} % NoGdbEngine + + Check{"umm", "<10 items>", "std::pmr::unordered_multimap"} % GdbEngine + + Check{"v", "<10 items>", "std::pmr::vector"} % NoGdbEngine + + Check{"v", "<10 items>", "std::pmr::vector"} % GdbEngine + + Check{"v.6", "[6]", "6", "int"}; + // clang-format on } int main(int argc, char *argv[])