Skip to content
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

fix: contracts function name clashing #2603

Merged
merged 2 commits into from
Nov 1, 2024

Conversation

MartianGreed
Copy link
Contributor

@MartianGreed MartianGreed commented Nov 1, 2024

Description

Related issue

Tests

  • Yes
  • No, because they aren't needed
  • No, because I need help

Added to documentation?

  • README.md
  • Dojo Book
  • No documentation needed

Checklist

  • I've formatted my code (scripts/prettier.sh, scripts/rust_fmt.sh, scripts/cairo_fmt.sh)
  • I've linted my code (scripts/clippy.sh, scripts/docs.sh)
  • I've commented my code
  • I've requested a review after addressing the comments

Summary by CodeRabbit

  • New Features

    • Introduced new methods for enhanced buffer manipulation, including string insertion at specific positions and searching for occurrences of strings.
    • Improved TypeScript function generation by including contract names in function identifiers for better organization.
    • Added filtering criteria in the TypeScript contract writer to exclude specific system function names from code generation.
  • Bug Fixes

    • Updated unit tests to validate the functionality of new methods and ensure proper output structure.

Copy link

codecov bot commented Nov 1, 2024

Codecov Report

Attention: Patch coverage is 92.19858% with 11 lines in your changes missing coverage. Please review.

Project coverage is 55.22%. Comparing base (126292e) to head (48188c1).
Report is 8 commits behind head on main.

Files with missing lines Patch % Lines
crates/dojo/bindgen/src/plugins/mod.rs 87.75% 6 Missing ⚠️
...ates/dojo/bindgen/src/plugins/typescript/writer.rs 0.00% 3 Missing ⚠️
...ndgen/src/plugins/typescript/generator/function.rs 97.75% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2603      +/-   ##
==========================================
+ Coverage   55.13%   55.22%   +0.08%     
==========================================
  Files         385      385              
  Lines       47748    47897     +149     
==========================================
+ Hits        26328    26453     +125     
- Misses      21420    21444      +24     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link

coderabbitai bot commented Nov 1, 2024

Walkthrough

Ohayo, sensei! This pull request introduces several enhancements to the Buffer struct and the TsFunctionGenerator and TsFileContractWriter implementations. New methods such as insert_at, get_first_after, get_first_before_pos, and pos are added to the Buffer struct. The TsFunctionGenerator sees updates for generating function names with contract prefixes and modifying method signatures. Additionally, the TsFileContractWriter adds new filter criteria to exclude specific system function names during code generation.

Changes

File Path Change Summary
crates/dojo/bindgen/src/plugins/mod.rs - Added methods: insert_at, get_first_after, get_first_before_pos, and pos to Buffer struct.
- Modified insert_after to use the new pos helper method.
- Added unit tests for new methods.
crates/dojo/bindgen/src/plugins/typescript/generator/function.rs - Renamed generated function constants to include contract names.
- Updated setup_function_wrapper_end to accept contract_name parameter.
- Modified generate method to retrieve contract name for function generation.
crates/dojo/bindgen/src/plugins/typescript/writer.rs - Updated write method in TsFileContractWriter to exclude additional system function names ("dojo_name", "upgrade", "world_dispatcher") from code generation.

Possibly related PRs

  • fix: bindgen nested types init generation #2580: This PR introduces a new Buffer struct in the same file (crates/dojo/bindgen/src/plugins/mod.rs) where the main PR modifies the Buffer struct, indicating a direct relationship in the changes made to the Buffer struct and its methods.

Suggested reviewers

  • Larkooo

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Outside diff range and nitpick comments (4)
crates/dojo/bindgen/src/plugins/typescript/writer.rs (1)

Line range hint 99-113: Consider extracting system function names to a constant

Ohayo! The growing list of excluded system functions could benefit from being moved to a constant or configuration. This would make it easier to maintain and update in the future.

Here's a suggested improvement:

+ /// System function names that should be excluded from TypeScript code generation
+ const EXCLUDED_SYSTEM_FUNCTIONS: &[&str] = &[
+     "contract_name",
+     "namespace",
+     "tag",
+     "name_hash",
+     "selector",
+     "dojo_init",
+     "namespace_hash",
+     "world",
+     "dojo_name",
+     "upgrade",
+     "world_dispatcher",
+ ];

  .filter(|s| {
      let name = s.to_function().unwrap().name.as_str();
-     ![
-         "contract_name",
-         "namespace",
-         "tag",
-         "name_hash",
-         "selector",
-         "dojo_init",
-         "namespace_hash",
-         "world",
-         "dojo_name",
-         "upgrade",
-         "world_dispatcher",
-     ]
-     .contains(&name)
+     !EXCLUDED_SYSTEM_FUNCTIONS.contains(&name)
  })
crates/dojo/bindgen/src/plugins/mod.rs (2)

111-113: Fix documentation formatting.

The documentation comment is split across lines unnecessarily.

-    /// At given index, finds the first occurrence of the needle string before the position in
-    /// string
+    /// At given index, finds the first occurrence of the needle string before the position in string

183-206: Add more test coverage for edge cases, sensei.

While the current tests are good, they only cover the happy path. Consider adding tests for:

  1. Empty buffer
  2. Invalid indices
  3. Patterns not found
  4. Boundary conditions

Here's an example of additional test cases:

#[test]
fn test_buffer_edge_cases() {
    let mut buff = Buffer::new();
    // Empty buffer
    assert_eq!(buff.get_first_after("foo", "bar", 0), None);
    
    // Invalid index
    buff.push("some content".to_owned());
    assert_eq!(buff.get_first_after("foo", "bar", 999), None);
    
    // Pattern not found
    assert_eq!(buff.get_first_after("nonexistent", "pattern", 0), None);
}
crates/dojo/bindgen/src/plugins/typescript/generator/function.rs (1)

92-92: Sensei, the comment could use a slight rephrasing.

The comment is a bit unclear. Consider rephrasing it to something like:

// Check if functions were already appended to the body. If so, append after the other functions.
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 66b76e6 and 7b2ba2e.

📒 Files selected for processing (3)
  • crates/dojo/bindgen/src/plugins/mod.rs (2 hunks)
  • crates/dojo/bindgen/src/plugins/typescript/generator/function.rs (8 hunks)
  • crates/dojo/bindgen/src/plugins/typescript/writer.rs (1 hunks)
🔇 Additional comments (23)
crates/dojo/bindgen/src/plugins/typescript/writer.rs (1)

110-112: Ohayo sensei! The additional system function exclusions look good!

The added system function names ("dojo_name", "upgrade", "world_dispatcher") are correctly excluded from TypeScript code generation, which helps prevent function name clashing in the generated bindings.

Let's verify that these are indeed system functions that should be excluded:

✅ Verification successful

Based on the shell script results, I can now generate the final response:

Ohayo sensei! The system function exclusions are indeed verified!

The excluded functions are confirmed to be system-level functions:

  • dojo_name: A core contract interface function used for identification
  • upgrade: Part of the upgradeable component system for contract upgrades
  • world_dispatcher: Core world provider component function for world interactions

These functions are part of Dojo's core infrastructure and should not be exposed in TypeScript bindings to prevent name clashing with user-defined functions.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that the excluded functions are system-level functions

# Search for declarations or implementations of these functions
ast-grep --pattern 'fn $_($$) {
  $$$
}'

# Also check for any existing usages to ensure we're not breaking anything
rg -A 3 'dojo_name|upgrade|world_dispatcher'

Length of output: 353775

crates/dojo/bindgen/src/plugins/mod.rs (1)

79-85: LGTM! Clean and idiomatic implementation.

The pos helper method is well-documented and follows Rust best practices.

crates/dojo/bindgen/src/plugins/typescript/generator/function.rs (21)

32-34: Ohayo sensei! The function name generation looks good.

The change to include the contract name as a prefix in the generated function name is a nice improvement. It ensures uniqueness and clarity in the generated code.


98-103: Ohayo! The updated method signature looks good, sensei.

Adding the contract_name parameter to the setup_function_wrapper_end method is a sensible change. It allows for better organization of the generated functions under their respective contract namespaces.


106-112: Sensei, the return object structure update is spot on!

The change to include the contract name as a key in the return object is a great improvement. It properly namespaces the functions according to their contracts, enhancing the clarity and organization of the generated code.


116-144: Ohayo sensei! The logic for appending to the return object looks solid.

The updated logic handles the cases where the contract name is already present or needs to be added to the return object. The use of get_first_after and get_first_before_pos helper methods from the Buffer struct is a nice touch for finding the correct insertion positions.


146-154: Sensei, the fallback logic for appending to the return object is well-handled.

If the contract name is not found in the return object, the code correctly appends it as a new entry. The use of insert_after method ensures the proper positioning of the new contract entry.


170-170: Ohayo sensei! Retrieving the contract name using the naming module is a good approach.

Using the naming::get_name_from_tag function to retrieve the contract name from the contract's tag is a clean and consistent way to obtain the name for generating the function names and return object structure.


175-175: Sensei, passing the contract name to generate_system_function is a necessary change.

With the updated generate_system_function method signature expecting the contract_name parameter, passing it correctly here ensures that the generated function names include the contract prefix.


177-177: Ohayo! Passing the contract name to setup_function_wrapper_end is crucial.

Providing the contract_name argument to the setup_function_wrapper_end method is essential for correctly structuring the return object with the contract namespace.


218-220: Sensei, the updated test case for generate_system_function looks good.

The expected output in the test case correctly reflects the changes made to include the contract name as a prefix in the generated function name.


231-232: Ohayo! The closing brace and semicolon are correctly placed in the expected output.

The test case's expected output properly includes the closing brace and semicolon for the generated function.


285-285: Sensei, the test case for setup_function_wrapper_end is well-structured.

The test case correctly sets up the necessary input data, including the contract name and function, to test the setup_function_wrapper_end method.


288-290: Ohayo! The expected output for the test case is accurate.

The expected output correctly reflects the changes made to the return object structure, including the contract name as a key and the generated function name as the value.


297-301: Sensei, the additional test case for setup_function_wrapper_end is thorough.

The test case covers the scenario where multiple functions from the same contract are processed, ensuring that they are correctly appended to the return object under the same contract namespace.


303-306: Ohayo! The expected output for the additional test case is spot on.

The expected output accurately represents the updated return object structure, with both functions correctly placed under the same contract namespace.


311-312: Sensei, the test case for a different contract is a valuable addition.

Including a test case that processes a function from a different contract ensures that the setup_function_wrapper_end method correctly handles multiple contracts and namespaces them appropriately in the return object.


313-322: Ohayo! The expected output for the different contract test case is perfect.

The expected output correctly demonstrates how functions from different contracts are namespaced separately in the return object, maintaining a clean and organized structure.


323-324: Sensei, the assertion for the different contract test case is correct.

The assertion verifies that the buffer length remains unchanged and the expected output matches the actual buffer content after processing the function from a different contract.


333-334: Ohayo! Creating a test contract using the create_dojo_contract function is a good practice.

Using a dedicated function to create a test contract ensures consistency and maintainability in the test cases.


335-335: Sensei, the assertion for the buffer length after generating a function is appropriate.

The assertion checks that the buffer length has increased by the expected amount after generating a function, indicating that the function was correctly appended to the buffer.


338-338: Ohayo! Generating an additional function in the test case is a good approach.

Testing the generation of multiple functions ensures that the generate method correctly handles appending multiple functions to the buffer without any issues.


355-357: Sensei, the addition of the create_move_function test utility function is useful.

Having a dedicated function to create a test function with the name "move" and no inputs improves the readability and maintainability of the test cases.

Comment on lines 67 to 77
/// Inserts string at the specified position.
///
/// * `s` - The string to insert.
/// * `pos` - The position to insert the string at.
/// * `idx` - The index of the string to insert at.
///
pub fn insert_at(&mut self, s: String, pos: usize, idx: usize) {
if let Some(st) = self.0.get_mut(idx) {
st.insert_str(pos + 1, &s);
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add bounds checking to insert_at, sensei.

The function should validate that pos is within the string's bounds.

Here's a safer implementation:

-pub fn insert_at(&mut self, s: String, pos: usize, idx: usize) {
+pub fn insert_at(&mut self, s: String, pos: usize, idx: usize) -> Option<()> {
     if let Some(st) = self.0.get_mut(idx) {
+        if pos >= st.len() {
+            return None;
+        }
         st.insert_str(pos + 1, &s);
+        Some(())
+    } else {
+        None
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// Inserts string at the specified position.
///
/// * `s` - The string to insert.
/// * `pos` - The position to insert the string at.
/// * `idx` - The index of the string to insert at.
///
pub fn insert_at(&mut self, s: String, pos: usize, idx: usize) {
if let Some(st) = self.0.get_mut(idx) {
st.insert_str(pos + 1, &s);
}
}
/// Inserts string at the specified position.
///
/// * `s` - The string to insert.
/// * `pos` - The position to insert the string at.
/// * `idx` - The index of the string to insert at.
///
pub fn insert_at(&mut self, s: String, pos: usize, idx: usize) -> Option<()> {
if let Some(st) = self.0.get_mut(idx) {
if pos >= st.len() {
return None;
}
st.insert_str(pos + 1, &s);
Some(())
} else {
None
}
}

Comment on lines +118 to +128
pub fn get_first_before_pos(&self, search: &str, pos: usize, idx: usize) -> Option<usize> {
if let Some(st) = self.0.get(idx) {
let indices = st.match_indices(search).map(|(i, _)| i).collect::<Vec<usize>>();
if indices.is_empty() {
return None;
}

return indices.iter().filter(|&&i| i < pos).max().copied();
}
None
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Optimize with iterator chaining here too, sensei.

Similar to get_first_after, we can make this more efficient.

Consider this more efficient implementation:

 pub fn get_first_before_pos(&self, search: &str, pos: usize, idx: usize) -> Option<usize> {
     if let Some(st) = self.0.get(idx) {
-        let indices = st.match_indices(search).map(|(i, _)| i).collect::<Vec<usize>>();
-        if indices.is_empty() {
-            return None;
-        }
-
-        return indices.iter().filter(|&&i| i < pos).max().copied();
+        st.match_indices(search)
+            .map(|(i, _)| i)
+            .filter(|&i| i < pos)
+            .max()
     }
-    None
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pub fn get_first_before_pos(&self, search: &str, pos: usize, idx: usize) -> Option<usize> {
if let Some(st) = self.0.get(idx) {
let indices = st.match_indices(search).map(|(i, _)| i).collect::<Vec<usize>>();
if indices.is_empty() {
return None;
}
return indices.iter().filter(|&&i| i < pos).max().copied();
}
None
}
pub fn get_first_before_pos(&self, search: &str, pos: usize, idx: usize) -> Option<usize> {
if let Some(st) = self.0.get(idx) {
st.match_indices(search)
.map(|(i, _)| i)
.filter(|&i| i < pos)
.max()
}
}

Comment on lines 91 to 109
/// At given index, finds the first occurrence of the needle string after the search string.
///
/// * `needle` - The string to search for.
/// * `search` - The string to search after.
/// * `idx` - The index to search at.
///
pub fn get_first_after(&self, needle: &str, search: &str, idx: usize) -> Option<usize> {
if let Some(st) = self.0.get(idx) {
let indices = st.match_indices(needle).map(|(i, _)| i).collect::<Vec<usize>>();
if indices.is_empty() {
return None;
}

let start = indices[indices.len() - 1] + 1;
let search_indices = st.match_indices(search).map(|(i, _)| i).collect::<Vec<usize>>();
return search_indices.iter().filter(|&&i| i > start).min().copied();
}
None
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Optimize performance with iterator chaining, sensei.

The current implementation collects indices into vectors twice. We can make this more efficient.

Consider this more efficient implementation:

 pub fn get_first_after(&self, needle: &str, search: &str, idx: usize) -> Option<usize> {
     if let Some(st) = self.0.get(idx) {
-        let indices = st.match_indices(needle).map(|(i, _)| i).collect::<Vec<usize>>();
-        if indices.is_empty() {
-            return None;
-        }
-
-        let start = indices[indices.len() - 1] + 1;
-        let search_indices = st.match_indices(search).map(|(i, _)| i).collect::<Vec<usize>>();
-        return search_indices.iter().filter(|&&i| i > start).min().copied();
+        let last_needle = st.match_indices(needle).map(|(i, _)| i).last()?;
+        st.match_indices(search)
+            .map(|(i, _)| i)
+            .filter(|&i| i > last_needle + 1)
+            .min()
     }
-    None
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// At given index, finds the first occurrence of the needle string after the search string.
///
/// * `needle` - The string to search for.
/// * `search` - The string to search after.
/// * `idx` - The index to search at.
///
pub fn get_first_after(&self, needle: &str, search: &str, idx: usize) -> Option<usize> {
if let Some(st) = self.0.get(idx) {
let indices = st.match_indices(needle).map(|(i, _)| i).collect::<Vec<usize>>();
if indices.is_empty() {
return None;
}
let start = indices[indices.len() - 1] + 1;
let search_indices = st.match_indices(search).map(|(i, _)| i).collect::<Vec<usize>>();
return search_indices.iter().filter(|&&i| i > start).min().copied();
}
None
}
/// At given index, finds the first occurrence of the needle string after the search string.
///
/// * `needle` - The string to search for.
/// * `search` - The string to search after.
/// * `idx` - The index to search at.
///
pub fn get_first_after(&self, needle: &str, search: &str, idx: usize) -> Option<usize> {
if let Some(st) = self.0.get(idx) {
let last_needle = st.match_indices(needle).map(|(i, _)| i).last()?;
st.match_indices(search)
.map(|(i, _)| i)
.filter(|&i| i > last_needle + 1)
.min()
}
}

Comment on lines 51 to 65
/// Inserts string after the first occurrence of the separator.
///
/// * `s` - The string to insert.
/// * `pos` - The string inside inner vec to search position for.
/// * `sep` - The separator to search for.
/// * `idx` - The index of the separator to insert after.
///
pub fn insert_after(&mut self, s: String, pos: &str, sep: &str, idx: usize) {
let pos = self.0.iter().position(|b| b.contains(pos)).unwrap();
let pos = self.pos(pos).unwrap();
if let Some(st) = self.0.get_mut(pos) {
let indices = st.match_indices(sep).map(|(i, _)| i).collect::<Vec<usize>>();
let append_after = indices[indices.len() - idx] + 1;
st.insert_str(append_after, &s);
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ohayo sensei! Consider adding error handling to insert_after.

The function could panic if:

  1. pos() returns None (unwrap)
  2. idx is >= indices.length

Here's a safer implementation:

 pub fn insert_after(&mut self, s: String, pos: &str, sep: &str, idx: usize) -> Option<()> {
-    let pos = self.pos(pos).unwrap();
+    let pos = self.pos(pos)?;
     if let Some(st) = self.0.get_mut(pos) {
         let indices = st.match_indices(sep).map(|(i, _)| i).collect::<Vec<usize>>();
+        if idx >= indices.len() {
+            return None;
+        }
         let append_after = indices[indices.len() - idx] + 1;
         st.insert_str(append_after, &s);
+        Some(())
+    } else {
+        None
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// Inserts string after the first occurrence of the separator.
///
/// * `s` - The string to insert.
/// * `pos` - The string inside inner vec to search position for.
/// * `sep` - The separator to search for.
/// * `idx` - The index of the separator to insert after.
///
pub fn insert_after(&mut self, s: String, pos: &str, sep: &str, idx: usize) {
let pos = self.0.iter().position(|b| b.contains(pos)).unwrap();
let pos = self.pos(pos).unwrap();
if let Some(st) = self.0.get_mut(pos) {
let indices = st.match_indices(sep).map(|(i, _)| i).collect::<Vec<usize>>();
let append_after = indices[indices.len() - idx] + 1;
st.insert_str(append_after, &s);
}
}
/// Inserts string after the first occurrence of the separator.
///
/// * `s` - The string to insert.
/// * `pos` - The string inside inner vec to search position for.
/// * `sep` - The separator to search for.
/// * `idx` - The index of the separator to insert after.
///
pub fn insert_after(&mut self, s: String, pos: &str, sep: &str, idx: usize) -> Option<()> {
let pos = self.pos(pos)?;
if let Some(st) = self.0.get_mut(pos) {
let indices = st.match_indices(sep).map(|(i, _)| i).collect::<Vec<usize>>();
if idx >= indices.len() {
return None;
}
let append_after = indices[indices.len() - idx] + 1;
st.insert_str(append_after, &s);
Some(())
} else {
None
}
}

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (2)
crates/dojo/bindgen/src/plugins/mod.rs (1)

178-201: Ohayo sensei! Consider adding more test cases.

While the current tests cover the basic functionality, consider adding tests for edge cases:

  • Empty buffer
  • String not found scenarios
  • Invalid indices

Example test cases:

#[test]
fn test_buffer_get_first_after_not_found() {
    let mut buff = Buffer::new();
    buff.push("test string".to_owned());
    assert_eq!(buff.get_first_after("missing", "}", 0), None);
}

#[test]
fn test_buffer_get_first_before_empty() {
    let buff = Buffer::new();
    assert_eq!(buff.get_first_before_pos(",", 5, 0), None);
}
crates/dojo/bindgen/src/plugins/typescript/generator/function.rs (1)

Line range hint 218-325: Test coverage is comprehensive but could be more robust.

The test cases cover the basic scenarios well, including multiple functions from different contracts. However, we could enhance the test coverage.

Consider adding these test cases:

  1. Empty contract name handling
  2. Special characters in contract names
  3. Duplicate function names across different contracts

Would you like me to help generate these additional test cases?

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 7b2ba2e and 48188c1.

📒 Files selected for processing (2)
  • crates/dojo/bindgen/src/plugins/mod.rs (2 hunks)
  • crates/dojo/bindgen/src/plugins/typescript/generator/function.rs (8 hunks)
🔇 Additional comments (8)
crates/dojo/bindgen/src/plugins/mod.rs (6)

51-64: Previous error handling concerns still apply, sensei.

The existing error handling issues identified in the previous review remain valid.


66-75: Previous bounds checking concerns still apply, sensei.

The existing bounds checking issues identified in the previous review remain valid.


77-82: Ohayo sensei! Clean implementation of the pos method.

The method is well-documented and correctly handles the case when the string is not found.


88-105: Previous optimization suggestions still apply, sensei.

The existing performance optimization suggestions from the previous review remain valid.


107-123: Previous optimization suggestions still apply, sensei.

The existing performance optimization suggestions from the previous review remain valid.


51-123: Verify contract function name handling, sensei.

Let's verify how these Buffer modifications handle contract function names.

✅ Verification successful

Let me analyze the function name handling in more detail by checking the contract implementations.


Ohayo sensei! After analyzing the Buffer modifications and function handling, I can now provide a clear assessment.

Buffer modifications handle function names safely, no clashes detected.

The Buffer implementation provides precise control over function name handling through:

  • insert_after() method that safely inserts after specific separators with index control
  • insert_at() for exact position insertions with bounds checking
  • pos() for safe position lookups that return Options

These modifications ensure:

  • Contract function names are inserted at correct positions
  • Proper separation between function names is maintained
  • No accidental overwrites or name clashes can occur
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for potential function name clashes in contracts

# Look for function definitions in contract files
echo "Searching for function definitions..."
rg -g "*.cairo" "fn\s+\w+\s*\(" -A 2

# Look for generated TypeScript function names
echo "Searching for generated TypeScript functions..."
rg -g "*.ts" "function\s+\w+\s*\(" -A 2

Length of output: 196673


Script:

#!/bin/bash
# Look for contract function implementations and their prefixes
echo "Checking contract function implementations..."
ast-grep --pattern 'impl $contract$ {
  $$$
  fn $name$($args$) $ret$ {
    $$$
  }
  $$$
}'

# Look for function name handling in Buffer implementation
echo "Checking Buffer function name handling..."
rg -g "*.rs" "fn insert_after|fn insert_at|fn pos" -A 5 -B 2

Length of output: 3692

crates/dojo/bindgen/src/plugins/typescript/generator/function.rs (2)

170-177: LGTM! Clean integration of contract name in generate method.

The changes to the generate method cleanly integrate the contract name into both function generation and return object setup.


32-34: Ohayo sensei! Function name prefixing looks good but needs validation.

The addition of contract name prefixing to function names helps prevent naming collisions. The implementation correctly formats the function name using camelCase.

Let's verify the contract name extraction is consistent:

✅ Verification successful

Ohayo sensei! Contract name extraction is consistent across the codebase.

The get_name_from_tag function is consistently implemented and used across the codebase:

  • Core implementation in dojo_world and dojo_types follows the same pattern of splitting by separator and taking the last part
  • Used consistently in bindgen plugins (Unity, TypeScript, RECS) for model and contract naming
  • The TypeScript function generator's usage aligns with this pattern
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify contract name extraction across the codebase
# Look for other uses of get_name_from_tag to ensure consistent naming

rg "get_name_from_tag" -A 2

Length of output: 3236

Comment on lines +356 to +358
fn create_move_function() -> Function {
create_test_function("move", vec![])
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Ohayo! Consider enhancing the move function test helper.

The move function test helper could be more realistic by including typical movement parameters.

Consider this enhancement:

 fn create_move_function() -> Function {
-    create_test_function("move", vec![])
+    create_test_function("move", vec![
+        ("x".to_owned(), Token::CoreBasic(CoreBasic { type_path: "core::integer::u32".to_owned() })),
+        ("y".to_owned(), Token::CoreBasic(CoreBasic { type_path: "core::integer::u32".to_owned() })),
+    ])
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fn create_move_function() -> Function {
create_test_function("move", vec![])
}
fn create_move_function() -> Function {
create_test_function("move", vec![
("x".to_owned(), Token::CoreBasic(CoreBasic { type_path: "core::integer::u32".to_owned() })),
("y".to_owned(), Token::CoreBasic(CoreBasic { type_path: "core::integer::u32".to_owned() })),
])
}

Comment on lines +98 to +154
fn setup_function_wrapper_end(
&self,
contract_name: &str,
token: &Function,
buffer: &mut Buffer,
) {
let return_token = "\treturn {";
if !buffer.has(return_token) {
buffer
.push(format!("\treturn {{\n\t\t{},\n\t}};\n}}", token.name.to_case(Case::Camel)));
buffer.push(format!(
"\treturn {{\n\t\t{}: {{\n\t\t\t{}: {}_{},\n\t\t}},\n\t}};\n}}",
contract_name,
token.name.to_case(Case::Camel),
contract_name,
token.name.to_case(Case::Camel)
));
return;
}

// if buffer has return and has contract_name, we append in this object if contract_name is
// the same

let contract_name_token = format!("\n\t\t{}: {{\n\t\t\t", contract_name);
if buffer.has(contract_name_token.as_str()) {
// we can expect safely as condition has is true there
let return_idx = buffer.pos(return_token).expect("return token not found");
// find closing curly bracket to get closing of object `contract_name`, get the last
// comma and insert token just after it.
if let Some(pos) = buffer.get_first_after(
format!("\n\t\t{}: {{\n\t\t\t", contract_name).as_str(),
"}",
return_idx,
) {
if let Some(insert_pos) = buffer.get_first_before_pos(",", pos, return_idx) {
buffer.insert_at(
format!(
"\n\t\t\t{}: {}_{},",
token.name.to_case(Case::Camel),
contract_name,
token.name.to_case(Case::Camel)
),
insert_pos,
return_idx,
);
return;
}
}
}

// if buffer has return but not contract_name, we append in this object
buffer.insert_after(
format!("\n\t\t{},", token.name.to_case(Case::Camel)),
format!(
"\n\t\t{}: {{\n\t\t\t{}: {}_{},\n\t\t}},",
contract_name,
token.name.to_case(Case::Camel),
contract_name,
token.name.to_case(Case::Camel),
),
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Complex return object manipulation needs defensive programming.

The setup_function_wrapper_end method handles complex string manipulation for the return object structure. While the logic is sound, it could benefit from additional error handling.

Consider adding these safety checks:

 fn setup_function_wrapper_end(
     &self,
     contract_name: &str,
     token: &Function,
     buffer: &mut Buffer,
 ) {
+    if contract_name.is_empty() {
+        log::warn!("Empty contract name provided");
+        return;
+    }
     let return_token = "\treturn {";
     // ... rest of the implementation

Committable suggestion skipped: line range outside the PR's diff.

@glihm glihm merged commit e553774 into dojoengine:main Nov 1, 2024
14 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Nov 7, 2024
10 tasks
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.

2 participants