aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Mihelich <cmihelic@google.com>2024-05-22 07:01:19 -0700
committerCopybara-Service <copybara-worker@google.com>2024-05-22 07:02:25 -0700
commitaaed9b4ab49047868285fe2fa0467e24790c4418 (patch)
treea0e3e519e8052f3f47f84022e5565d779be5913a
parente7f1a950e97b805d634909124fa4c75b690d0475 (diff)
downloadabseil-aaed9b4ab49047868285fe2fa0467e24790c4418.tar.gz
abseil-aaed9b4ab49047868285fe2fa0467e24790c4418.tar.bz2
abseil-aaed9b4ab49047868285fe2fa0467e24790c4418.zip
Recognize fn-type and lifetimes in Rust demangling.
PiperOrigin-RevId: 636152885 Change-Id: If545903854ea39cc4b5c51c88cd555072d27d89e
-rw-r--r--absl/debugging/internal/demangle_rust.cc72
-rw-r--r--absl/debugging/internal/demangle_rust_test.cc56
2 files changed, 124 insertions, 4 deletions
diff --git a/absl/debugging/internal/demangle_rust.cc b/absl/debugging/internal/demangle_rust.cc
index 83fec2d9..b8cc1803 100644
--- a/absl/debugging/internal/demangle_rust.cc
+++ b/absl/debugging/internal/demangle_rust.cc
@@ -260,12 +260,12 @@ class RustSymbolParser {
if (Eat('T')) goto tuple_type;
if (Eat('R')) {
if (!Emit("&")) return false;
- if (Eat('L')) return false; // lifetime not yet implemented
+ if (!ParseOptionalLifetime()) return false;
goto type;
}
if (Eat('Q')) {
if (!Emit("&mut ")) return false;
- if (Eat('L')) return false; // lifetime not yet implemented
+ if (!ParseOptionalLifetime()) return false;
goto type;
}
if (Eat('P')) {
@@ -276,7 +276,7 @@ class RustSymbolParser {
if (!Emit("*mut ")) return false;
goto type;
}
- if (Eat('F')) return false; // fn-type not yet implemented
+ if (Eat('F')) goto fn_type;
if (Eat('D')) return false; // dyn-trait-type not yet implemented
if (Eat('B')) goto type_backref;
goto path;
@@ -328,6 +328,27 @@ class RustSymbolParser {
--silence_depth_;
continue;
+ // fn-type -> F fn-sig (F already consumed)
+ // fn-sig -> binder? U? (K abi)? type* E type
+ // abi -> C | undisambiguated-identifier
+ //
+ // We follow the C++ demangler in suppressing details of function
+ // signatures. Every function type is rendered "fn...".
+ fn_type:
+ if (!Emit("fn...")) return false;
+ ++silence_depth_;
+ if (!ParseOptionalBinder()) return false;
+ (void)Eat('U');
+ if (Eat('K')) {
+ if (!Eat('C') && !ParseUndisambiguatedIdentifier()) return false;
+ }
+ while (!Eat('E')) {
+ ABSL_DEMANGLER_RECURSE(type, kContinueParameterList);
+ }
+ ABSL_DEMANGLER_RECURSE(type, kFinishFn);
+ --silence_depth_;
+ continue;
+
// const -> type const-data | p | backref
//
// const is a C++ keyword, so we use the label `constant` instead.
@@ -386,7 +407,10 @@ class RustSymbolParser {
// generic-arg -> lifetime | type | K const
generic_arg:
- if (Eat('L')) return false; // lifetime not yet implemented
+ if (Peek() == 'L') {
+ if (!ParseOptionalLifetime()) return false;
+ continue;
+ }
if (Eat('K')) goto constant;
goto type;
@@ -451,6 +475,8 @@ class RustSymbolParser {
kAfterSecondTupleElement,
kAfterThirdTupleElement,
kAfterSubsequentTupleElement,
+ kContinueParameterList,
+ kFinishFn,
kConstData,
kBeginGenericArgList,
kContinueGenericArgList,
@@ -612,6 +638,20 @@ class RustSymbolParser {
int disambiguator = 0;
if (!ParseDisambiguator(disambiguator)) return false;
+ return ParseUndisambiguatedIdentifier(uppercase_namespace, disambiguator);
+ }
+
+ // Consumes from the input an identifier with no preceding disambiguator,
+ // returning true on success.
+ //
+ // When ParseIdentifier calls this, it passes the N<namespace> character and
+ // disambiguator value so that "{closure#42}" and similar forms can be
+ // rendered correctly.
+ //
+ // At other appearances of undisambiguated-identifier in the grammar, this
+ // treatment is not applicable, and the call site omits both arguments.
+ ABSL_MUST_USE_RESULT bool ParseUndisambiguatedIdentifier(
+ char uppercase_namespace = '\0', int disambiguator = 0) {
// undisambiguated-identifier -> u? decimal-number _? bytes
const bool is_punycoded = Eat('u');
if (!IsDigit(Peek())) return false;
@@ -686,6 +726,30 @@ class RustSymbolParser {
return true;
}
+ // Consumes a binder of higher-ranked lifetimes if one is present. On success
+ // returns true and discards the encoded lifetime count. On parse failure
+ // returns false.
+ ABSL_MUST_USE_RESULT bool ParseOptionalBinder() {
+ // binder -> G base-62-number
+ if (!Eat('G')) return true;
+ int ignored_binding_count;
+ return ParseBase62Number(ignored_binding_count);
+ }
+
+ // Consumes a lifetime if one is present.
+ //
+ // On success returns true and discards the lifetime index. We do not print
+ // or even range-check lifetimes because they are a finer detail than other
+ // things we omit from output, such as the entire contents of generic-args.
+ //
+ // On parse failure returns false.
+ ABSL_MUST_USE_RESULT bool ParseOptionalLifetime() {
+ // lifetime -> L base-62-number
+ if (!Eat('L')) return true;
+ int ignored_de_bruijn_index;
+ return ParseBase62Number(ignored_de_bruijn_index);
+ }
+
// Pushes ns onto the namespace stack and returns true if the stack is not
// full, else returns false.
ABSL_MUST_USE_RESULT bool PushNamespace(char ns) {
diff --git a/absl/debugging/internal/demangle_rust_test.cc b/absl/debugging/internal/demangle_rust_test.cc
index 453d725b..34b78134 100644
--- a/absl/debugging/internal/demangle_rust_test.cc
+++ b/absl/debugging/internal/demangle_rust_test.cc
@@ -478,6 +478,62 @@ TEST(DemangleRust, TraitImplWithNonpathSelfType) {
"<&i32 as my_crate::my_trait>::my_func");
}
+TEST(DemangleRust, ThunkType) {
+ EXPECT_DEMANGLING("_RNvYFEuNtC1c1t1f", // <fn() as c::t>::f
+ "<fn... as c::t>::f");
+}
+
+TEST(DemangleRust, NontrivialFunctionReturnType) {
+ EXPECT_DEMANGLING(
+ "_RNvYFERTlmENtC1c1t1f", // <fn() -> &(i32, u32) as c::t>::f
+ "<fn... as c::t>::f");
+}
+
+TEST(DemangleRust, OneParameterType) {
+ EXPECT_DEMANGLING("_RNvYFlEuNtC1c1t1f", // <fn(i32) as c::t>::f
+ "<fn... as c::t>::f");
+}
+
+TEST(DemangleRust, TwoParameterTypes) {
+ EXPECT_DEMANGLING("_RNvYFlmEuNtC1c1t1f", // <fn(i32, u32) as c::t>::f
+ "<fn... as c::t>::f");
+}
+
+TEST(DemangleRust, ExternC) {
+ EXPECT_DEMANGLING("_RNvYFKCEuNtC1c1t1f", // <extern "C" fn() as c::t>>::f
+ "<fn... as c::t>::f");
+}
+
+TEST(DemangleRust, ExternOther) {
+ EXPECT_DEMANGLING(
+ "_RNvYFK5not_CEuNtC1c1t1f", // <extern "not-C" fn() as c::t>::f
+ "<fn... as c::t>::f");
+}
+
+TEST(DemangleRust, Unsafe) {
+ EXPECT_DEMANGLING("_RNvYFUEuNtC1c1t1f", // <unsafe fn() as c::t>::f
+ "<fn... as c::t>::f");
+}
+
+TEST(DemangleRust, Binder) {
+ EXPECT_DEMANGLING(
+ // <for<'a> fn(&'a i32) -> &'a i32 as c::t>::f
+ "_RNvYFG_RL0_lEB5_NtC1c1t1f",
+ "<fn... as c::t>::f");
+}
+
+TEST(DemangleRust, AllFnSigFeaturesInOrder) {
+ EXPECT_DEMANGLING(
+ // <for<'a> unsafe extern "C" fn(&'a i32) -> &'a i32 as c::t>::f
+ "_RNvYFG_UKCRL0_lEB8_NtC1c1t1f",
+ "<fn... as c::t>::f");
+}
+
+TEST(DemangleRust, LifetimeInGenericArgs) {
+ EXPECT_DEMANGLING("_RINvC1c1fINtB2_1sL_EE", // c::f::<c::s::<'_>>
+ "c::f::<>");
+}
+
} // namespace
} // namespace debugging_internal
ABSL_NAMESPACE_END