-
Notifications
You must be signed in to change notification settings - Fork 17
Resolve conflicting implementations for two similarly named types #174
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this 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()andto_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() { |
Copilot
AI
Dec 4, 2025
There was a problem hiding this comment.
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.
| ]; | ||
| 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() { |
Copilot
AI
Dec 4, 2025
There was a problem hiding this comment.
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 {| 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 { |
| let name_str = name.to_string(); | ||
| if name_str != tld.name | ||
| || name_str != self.to_rust_title_case(&tld.name).to_string() | ||
| { |
Copilot
AI
Dec 4, 2025
There was a problem hiding this comment.
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 {| 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 { |
| ]; | ||
| 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() { |
Copilot
AI
Dec 4, 2025
There was a problem hiding this comment.
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 {| 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; | ||
| } | ||
| } |
Copilot
AI
Dec 4, 2025
There was a problem hiding this comment.
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:
- Basic conflict resolution (e.g., two types with conflicting names get numbered correctly)
- Multiple conflicts (e.g.,
Name,Name2,Name3generation) - The identifier annotation is correctly added when names are modified
- 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.
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: