Skip to content

Conversation

@swthorn
Copy link

@swthorn swthorn commented Dec 4, 2025

This resolves issue #162 with the idea I described in the issue. When there is a conflict in names, we add a number to the end of the type.

When generating identifiers, I keep track of the used names in a HashSet. When an identifier in the same module shares the same name, we start with the number 2 and increment if the identifier is used. I pick the first identifier that is unused.

With my example in #162, this generates the following:

#[allow(
    non_camel_case_types,
    non_snake_case,
    non_upper_case_globals,
    unused,
    clippy::too_many_arguments
)]
pub mod test {
    extern crate alloc;
    use core::borrow::Borrow;
    use rasn::prelude::*;
    use std::sync::LazyLock;
    #[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
    #[rasn(delegate, identifier = "E-UTRAN-Trace-ID")]
    pub struct EUTRANTraceID(pub FixedOctetString<8usize>);
    #[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
    #[rasn(delegate, identifier = "EUTRANTraceID")]
    pub struct EUTRANTraceID2(pub FixedOctetString<8usize>);
}

Copilot AI review requested due to automatic review settings December 4, 2025 19:55
Copilot finished reviewing on behalf of swthorn December 4, 2025 19:58
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR resolves name conflicts when generating Rust bindings for ASN.1 types that have similar names but different representations (e.g., E-UTRAN-Trace-ID and EUTRANTraceID both map to EUTRANTraceID). The solution adds a numbering suffix (starting with 2) to conflicting names and tracks used names per module using a HashSet.

Key changes:

  • Introduced used_names: RefCell<HashSet<String>> to track generated identifiers within each module
  • Added get_unique_name() method to handle conflict resolution with automatic numbering
  • Created to_rust_title_case_unique() and to_rust_const_case_unique() wrapper methods
  • Updated identifier annotation logic to add ASN.1 identifier attributes when names are modified

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.

File Description
rasn-compiler/src/generator/rasn/mod.rs Adds used_names field to track generated names and clears it at the start of each module generation
rasn-compiler/src/generator/rasn/utils.rs Implements unique name generation logic and formatting code style improvements
rasn-compiler/src/generator/rasn/builder.rs Updates type generation methods to use unique name variants and adjust identifier annotation conditions

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

vec![quote!(enumerated), self.format_tag(tld.tag.as_ref(), false)];
if name.to_string() != tld.name {
let name_str = name.to_string();
if name_str != tld.name || name_str != self.to_rust_title_case(&tld.name).to_string() {
Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition name_str != self.to_rust_title_case(&tld.name).to_string() is redundant because name_str is derived from to_rust_title_case_unique(&tld.name), which internally calls to_rust_title_case(&tld.name). When there's no collision, both will be identical. The actual check needed is whether the name was modified due to a collision (i.e., whether a number was appended).

Consider storing the base name (before uniquification) and comparing against it instead:

let base_name = self.to_rust_title_case(&tld.name).to_string();
let name = self.to_rust_title_case_unique(&tld.name);
let name_str = name.to_string();
if name_str != tld.name || name_str != base_name {

This pattern is correctly implemented in format_name_and_common_annotations at line 1355-1360 in utils.rs.

Copilot uses AI. Check for mistakes.
];
if name.to_string() != tld.name {
let name_str = name.to_string();
if name_str != tld.name || name_str != self.to_rust_title_case(&tld.name).to_string() {
Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as line 604: the condition name_str != self.to_rust_title_case(&tld.name).to_string() is redundant. The check should compare name_str against the base name (before uniquification) to determine if a number was appended due to a name collision.

The correct pattern is:

let base_name = self.to_rust_title_case(&tld.name).to_string();
let name = self.to_rust_title_case_unique(&tld.name);
let name_str = name.to_string();
if name_str != tld.name || name_str != base_name {
Suggested change
if name_str != tld.name || name_str != self.to_rust_title_case(&tld.name).to_string() {
let base_name = self.to_rust_title_case(&tld.name).to_string();
if name_str != tld.name || name_str != base_name {

Copilot uses AI. Check for mistakes.
Comment on lines +782 to +785
let name_str = name.to_string();
if name_str != tld.name
|| name_str != self.to_rust_title_case(&tld.name).to_string()
{
Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as lines 604 and 649: the condition at line 784 is redundant. The check should compare name_str against the base name (before uniquification) to determine if a number was appended due to a name collision.

The correct pattern is:

let base_name = self.to_rust_title_case(&tld.name).to_string();
let name = self.to_rust_title_case_unique(&tld.name);
// ... later in the code
let name_str = name.to_string();
if name_str != tld.name || name_str != base_name {
Suggested change
let name_str = name.to_string();
if name_str != tld.name
|| name_str != self.to_rust_title_case(&tld.name).to_string()
{
let base_name = self.to_rust_title_case(&tld.name).to_string();
let name_str = name.to_string();
if name_str != base_name {

Copilot uses AI. Check for mistakes.
];
if name.to_string() != tld.name {
let name_str = name.to_string();
if name_str != tld.name || name_str != self.to_rust_title_case(&tld.name).to_string() {
Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as previous cases: the condition name_str != self.to_rust_title_case(&tld.name).to_string() is redundant. The check should compare name_str against the base name (before uniquification) to determine if a number was appended due to a name collision.

The correct pattern is:

let base_name = self.to_rust_title_case(&tld.name).to_string();
let name = self.to_rust_title_case_unique(&tld.name);
// ... later in the code
let name_str = name.to_string();
if name_str != tld.name || name_str != base_name {

Copilot uses AI. Check for mistakes.
Comment on lines +1318 to +1334
fn get_unique_name(&self, base_name: &str) -> String {
let mut used_names = self.used_names.borrow_mut();
if !used_names.contains(base_name) {
used_names.insert(base_name.to_string());
return base_name.to_string();
}

let mut counter = 2;
loop {
let candidate = format!("{base_name}{counter}");
if !used_names.contains(&candidate) {
used_names.insert(candidate.clone());
return candidate;
}
counter += 1;
}
}
Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new name conflict resolution mechanism (using get_unique_name and tracking used names in a HashSet) lacks test coverage. Given that the repository has comprehensive test coverage for other generator features (see rasn-compiler-tests/tests/ and tests in utils.rs), this new functionality should have tests to verify:

  1. Basic conflict resolution (e.g., two types with conflicting names get numbered correctly)
  2. Multiple conflicts (e.g., Name, Name2, Name3 generation)
  3. The identifier annotation is correctly added when names are modified
  4. Names are reset between module generations (via used_names.borrow_mut().clear())

Consider adding a test similar to the example in the PR description, which tests types like E-UTRAN-Trace-ID and EUTRANTraceID that resolve to the same Rust identifier.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant