Skip to main content

icb_server/
display_name.rs

1//! Conversions from Clang USR strings to display‑friendly identifiers.
2//!
3//! A USR (Unified Symbol Resolution) uniquely identifies a declaration in the
4//! Clang AST.  Its structure encodes namespaces, classes, function signatures
5//! and template parameters.  This module extracts the last, human‑significant
6//! segment of the USR and strips template suffix clutter.
7//!
8//! Additionally, the module provides [`cleanup_node_names`] which normalises
9//! both `name` and `usr` fields of every node in a [`CodePropertyGraph`].
10//! This is applied once after graph construction or cache loading.
11
12use icb_common::NodeKind;
13use icb_graph::graph::CodePropertyGraph;
14
15/// Returns a readable name from a raw identifier that may be a Clang USR.
16///
17/// # USR format
18///
19/// USRs follow the pattern `c:…@…@name#…`.  The last `@`‑delimited segment
20/// contains the symbol’s base name, optionally followed by `#…` encoding
21/// template arguments and qualifiers.
22///
23/// # Strategy
24///
25/// 1. If `raw` does **not** contain `@`, it is already a plain name and is
26///    returned unchanged.
27/// 2. Otherwise, the substring after the last `@` is taken.
28/// 3. Everything from the first `#` in that substring is discarded.
29///
30/// # Examples
31///
32/// ```rust
33/// # use icb_server::display_name::readable_name;
34/// assert_eq!(readable_name("c:@F@main"), "main");
35/// assert_eq!(readable_name("c:@S@MyClass@F@method"), "method");
36/// assert_eq!(
37///     readable_name("c:@S@MyClass@F@MyClass#&1$@S@MyClass#"),
38///     "MyClass"
39/// );
40/// assert_eq!(readable_name("already_clean"), "already_clean");
41/// assert_eq!(readable_name(""), "");
42/// ```
43pub fn readable_name(raw: &str) -> String {
44    if !raw.contains('@') {
45        return raw.to_string();
46    }
47
48    let after_at = raw.rsplit('@').next().unwrap_or(raw);
49    let before_hash = after_at.split('#').next().unwrap_or(after_at);
50    before_hash.to_string()
51}
52
53/// Walks all graph nodes and replaces USR‑encoded names with their
54/// human‑readable equivalents.
55///
56/// Both the `name` field (the primary display name) and, for functions and
57/// classes, the `usr` field are cleaned.  A `usr` value starting with `"c:"`
58/// is considered a raw USR and is converted; other values (e.g. file paths)
59/// are left unchanged.
60pub fn cleanup_node_names(cpg: &mut CodePropertyGraph) {
61    for node in cpg.graph.node_weights_mut() {
62        // Clean the primary display name
63        if let Some(ref name) = node.name {
64            let cleaned = readable_name(name);
65            if cleaned != *name {
66                node.name = Some(cleaned);
67            }
68        }
69
70        // For functions and classes, also clean the `usr` field if it
71        // appears to be a raw USR (starts with "c:").
72        if node.kind == NodeKind::Function || node.kind == NodeKind::Class {
73            if let Some(ref usr) = node.usr {
74                if usr.starts_with("c:") {
75                    let cleaned = readable_name(usr);
76                    if cleaned != *usr {
77                        node.usr = Some(cleaned);
78                    }
79                }
80            }
81        }
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    #[test]
90    fn test_simple_function() {
91        assert_eq!(readable_name("c:@F@main"), "main");
92    }
93
94    #[test]
95    fn test_class_method() {
96        assert_eq!(readable_name("c:@S@MyClass@F@method"), "method");
97    }
98
99    #[test]
100    fn test_constructor_template() {
101        assert_eq!(
102            readable_name("c:@S@MyClass@F@MyClass#&1$@S@MyClass#"),
103            "MyClass"
104        );
105    }
106
107    #[test]
108    fn test_already_clean() {
109        assert_eq!(readable_name("helper"), "helper");
110    }
111
112    #[test]
113    fn test_empty_string() {
114        assert_eq!(readable_name(""), "");
115    }
116
117    #[test]
118    fn test_only_at() {
119        assert_eq!(readable_name("@"), "");
120    }
121}