Files
llvm/lldb/source/Core/DemangledNameInfo.cpp
Michael Buch 54c9ddddd1 [libcxxabi][ItaniumDemangle] Separate GtIsGt counter into more states (#166578)
Currently `OutputBuffer::GtIsGt` is used to tell us if we're inside
template arguments and have printed a '(' without a closing ')'. If so,
we don't need to quote '<' when printing it as part of a binary
expression inside a template argument. Otherwise we need to. E.g.,
```
foo<a<(b < c)>> // Quotes around binary expression needed.
```

LLDB's `TrackingOutputBuffer` has heuristics that rely on checking
whether we are inside template arguments, regardless of the current
parentheses depth. We've been using `isGtInsideTemplateArgs` for this,
but that isn't correct. Resulting in us incorrectly tracking the
basename of function like:
```
void func<(foo::Enum)1>()
```
Here `GtIsGt > 0` despite us being inside template arguments (because we
incremented it when seeing '(').

This patch adds a `isInsideTemplateArgs` API which LLDB will use to more
accurately track parts of the demangled name.

To make sure this API doesn't go untested in the actual libcxxabi
test-suite, I changed the existing `GtIsGt` logic to use it. Also
renamed the various variables/APIs involved to make it (in my opinion)
more straightforward to understand what's going on. But happy to rename
it back if people disagree.

Also adjusted LLDB to use the newly introduced API (and added a
unit-test that would previously fail).
2025-11-07 07:52:03 +00:00

250 lines
6.3 KiB
C++

//===-- DemangledNameInfo.cpp ---------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "lldb/Core/DemangledNameInfo.h"
using namespace llvm::itanium_demangle;
namespace lldb_private {
bool TrackingOutputBuffer::shouldTrack() const {
if (!isPrintingTopLevelFunctionType())
return false;
if (isInsideTemplateArgs())
return false;
if (NameInfo.ArgumentsRange.first > 0)
return false;
return true;
}
bool TrackingOutputBuffer::canFinalize() const {
if (!isPrintingTopLevelFunctionType())
return false;
if (isInsideTemplateArgs())
return false;
if (NameInfo.ArgumentsRange.first == 0)
return false;
return true;
}
void TrackingOutputBuffer::updateBasenameEnd() {
if (!shouldTrack())
return;
NameInfo.BasenameRange.second = getCurrentPosition();
}
void TrackingOutputBuffer::updateScopeStart() {
if (!shouldTrack())
return;
NameInfo.ScopeRange.first = getCurrentPosition();
}
void TrackingOutputBuffer::updateScopeEnd() {
if (!shouldTrack())
return;
NameInfo.ScopeRange.second = getCurrentPosition();
}
void TrackingOutputBuffer::finalizeArgumentEnd() {
if (!canFinalize())
return;
NameInfo.ArgumentsRange.second = getCurrentPosition();
}
void TrackingOutputBuffer::finalizeQualifiersStart() {
if (!canFinalize())
return;
NameInfo.QualifiersRange.first = getCurrentPosition();
}
void TrackingOutputBuffer::finalizeQualifiersEnd() {
if (!canFinalize())
return;
NameInfo.QualifiersRange.second = getCurrentPosition();
}
void TrackingOutputBuffer::finalizeStart() {
if (!shouldTrack())
return;
NameInfo.ArgumentsRange.first = getCurrentPosition();
// If nothing has set the end of the basename yet (for example when
// printing templates), then the beginning of the arguments is the end of
// the basename.
if (NameInfo.BasenameRange.second == 0)
NameInfo.BasenameRange.second = getCurrentPosition();
// There is something between the basename and the start of the function
// arguments. Assume those are template arguments (which *should* be true for
// C++ demangled names, but this assumption may change in the future, in
// which case this needs to be adjusted).
if (NameInfo.BasenameRange.second != NameInfo.ArgumentsRange.first)
NameInfo.TemplateArgumentsRange = {NameInfo.BasenameRange.second,
NameInfo.ArgumentsRange.first};
assert(!shouldTrack());
assert(canFinalize());
}
void TrackingOutputBuffer::finalizeEnd() {
if (!canFinalize())
return;
if (NameInfo.ScopeRange.first > NameInfo.ScopeRange.second)
NameInfo.ScopeRange.second = NameInfo.ScopeRange.first;
NameInfo.BasenameRange.first = NameInfo.ScopeRange.second;
// We call anything past the FunctionEncoding the "suffix".
// In practice this would be nodes like `DotSuffix` that wrap
// a FunctionEncoding.
NameInfo.SuffixRange.first = getCurrentPosition();
}
ScopedOverride<unsigned> TrackingOutputBuffer::enterFunctionTypePrinting() {
return {FunctionPrintingDepth, FunctionPrintingDepth + 1};
}
bool TrackingOutputBuffer::isPrintingTopLevelFunctionType() const {
return FunctionPrintingDepth == 1;
}
void TrackingOutputBuffer::printLeft(const Node &N) {
switch (N.getKind()) {
case Node::KFunctionType:
printLeftImpl(static_cast<const FunctionType &>(N));
break;
case Node::KFunctionEncoding:
printLeftImpl(static_cast<const FunctionEncoding &>(N));
break;
case Node::KNestedName:
printLeftImpl(static_cast<const NestedName &>(N));
break;
case Node::KNameWithTemplateArgs:
printLeftImpl(static_cast<const NameWithTemplateArgs &>(N));
break;
default:
OutputBuffer::printLeft(N);
}
// Keep updating suffix until we reach the end.
NameInfo.SuffixRange.second = getCurrentPosition();
}
void TrackingOutputBuffer::printRight(const Node &N) {
switch (N.getKind()) {
case Node::KFunctionType:
printRightImpl(static_cast<const FunctionType &>(N));
break;
case Node::KFunctionEncoding:
printRightImpl(static_cast<const FunctionEncoding &>(N));
break;
default:
OutputBuffer::printRight(N);
}
// Keep updating suffix until we reach the end.
NameInfo.SuffixRange.second = getCurrentPosition();
}
void TrackingOutputBuffer::printLeftImpl(const FunctionType &N) {
auto Scoped = enterFunctionTypePrinting();
OutputBuffer::printLeft(N);
}
void TrackingOutputBuffer::printRightImpl(const FunctionType &N) {
auto Scoped = enterFunctionTypePrinting();
OutputBuffer::printRight(N);
}
void TrackingOutputBuffer::printLeftImpl(const FunctionEncoding &N) {
auto Scoped = enterFunctionTypePrinting();
const Node *Ret = N.getReturnType();
if (Ret) {
printLeft(*Ret);
if (!Ret->hasRHSComponent(*this))
*this += " ";
}
updateScopeStart();
N.getName()->print(*this);
}
void TrackingOutputBuffer::printRightImpl(const FunctionEncoding &N) {
auto Scoped = enterFunctionTypePrinting();
finalizeStart();
printOpen();
N.getParams().printWithComma(*this);
printClose();
finalizeArgumentEnd();
const Node *Ret = N.getReturnType();
if (Ret)
printRight(*Ret);
finalizeQualifiersStart();
auto CVQuals = N.getCVQuals();
auto RefQual = N.getRefQual();
auto *Attrs = N.getAttrs();
auto *Requires = N.getRequires();
if (CVQuals & QualConst)
*this += " const";
if (CVQuals & QualVolatile)
*this += " volatile";
if (CVQuals & QualRestrict)
*this += " restrict";
if (RefQual == FrefQualLValue)
*this += " &";
else if (RefQual == FrefQualRValue)
*this += " &&";
if (Attrs != nullptr)
Attrs->print(*this);
if (Requires != nullptr) {
*this += " requires ";
Requires->print(*this);
}
finalizeQualifiersEnd();
finalizeEnd();
}
void TrackingOutputBuffer::printLeftImpl(const NestedName &N) {
N.Qual->print(*this);
*this += "::";
updateScopeEnd();
N.Name->print(*this);
updateBasenameEnd();
}
void TrackingOutputBuffer::printLeftImpl(const NameWithTemplateArgs &N) {
N.Name->print(*this);
updateBasenameEnd();
N.TemplateArgs->print(*this);
}
} // namespace lldb_private