Summary
Code review identified 12 potential bugs across the RustCall.jl source code.
1. Nested generics regex failure in _parse_fn_arg_types (generics.jl:755)
File: src/generics.jl:755
fn_pattern = Regex("fn\\s+$(func_name)\\s*(?:<[^{]*?>)?\s*\(")
<[^{]*?> uses non-greedy matching which fails on deeply nested generics like Vec<Option<T>>. The > inside Option< would incorrectly match before the closing > of Vec<. This violates the project's own documented rule: "Never use regex alone to match angle brackets (< >) in Rust code."
2. Incorrect bracket depth tracking for [ and > (structs.jl:64-68, julia_functions.jl:206-215, ruststr.jl:1003-1031)
Files: src/structs.jl:64-68, src/julia_functions.jl:206-215, src/ruststr.jl:1003-1031
elseif c == '>'
angle_depth = max(0, angle_depth - 1)
elseif c == ']'
bracket_depth = max(0, bracket_depth - 1)
Using max(0, ...) for bracket depth clamping causes [-delimited brackets and >-delimited brackets to be tracked independently of each other. This means a < inside an array literal [a < b] would still increment angle_depth, and a > inside [a < b] would decrement it, leading to incorrect depth tracking when arrays contain comparison operators.
3. Unusual return value from drop! fallback (types.jl:837-843)
File: src/types.jl:837-843
function drop!(x::Union{RustBox, RustRc, RustArc, RustVec})
lock(x.drop_lock) do
x.dropped = true
x.ptr = C_NULL
end
return nothing
end
Julia convention for !-suffixed mutation functions is to return nothing (not the tuple (nothing)). While this works due to implicit tuple return, it is non-idiomatic.
4. String literal brace counting causes false positives (exceptions.jl:559-565)
File: src/exceptions.jl:559-565
open_braces = count(c -> c == '{', source_code)
close_braces = count(c -> c == '}', source_code)
if open_braces > close_braces
push!(suggestions, "Found $(open_braces - close_braces) more opening brace(s)...")
Brace counting includes braces inside string literals like s = "{test}", causing false positive suggestions for mismatched braces when none exist.
5. RustStr incorrectly mapped to Cstring in codegen (codegen.jl:125)
File: src/codegen.jl:125
julia_to_c_type(::Type{RustStr}) = Cstring
RustStr is a fat pointer (ptr + len) representing &str. Mapping it to Cstring (null-terminated string) loses the length information and causes ABI mismatch for FFI calls expecting &str.
6. Generic return type inference is oversimplified (generics.jl:973-1003)
File: src/generics.jl:973-1003
ret_type = length(type_params) > 0 ? first(values(type_params)) : Any
The first type parameter is assumed as the return type. For functions like fn transform<T, U>(x: T) -> U, this returns T instead of U, causing type mismatches.
7. readdir vs walkdir inconsistency in cache size calculation (cache.jl:459-464, cache.jl:485)
File: src/cache.jl:485
for file in readdir(cache_dir)
if endswith(file, lib_ext)
list_cached_libraries uses readdir (non-recursive), while get_cache_size uses walkdir (recursive). This means .dylib files in subdirectories of the cache would be counted by get_cache_size but missed by list_cached_libraries.
8. _count_trailing_backslashes uses unsafe indexing (dependencies.jl:288)
File: src/dependencies.jl:288
for i in lastindex(s):-1:firstindex(s)
s[i] == '\\' || break
Using linear index iteration on multi-byte strings is unsafe. Should use prevind-based iteration or iterate bytes instead.
9. lib_name / cache key mismatch (ruststr.jl:239-244, ruststr.jl:273-278)
File: src/ruststr.jl:239-244
code_hash = stable_content_hash(wrapped_code)[1:16]
lib_name = "rust_$(code_hash)"
The library registration key uses the first 16 hex chars of a stable content hash. The cache key uses a different computation path. If the compilation pipeline changes between cache save/load, the lookup could fail while the cache file exists.
10. found_libs duplicate potential in fallback search (ruststr.jl:86-106)
File: src/ruststr.jl:86-106
The fallback search in get_function_pointer doesn't guard against the same library being queried multiple times if RUST_LIBRARIES contains duplicate handles (through different keys pointing to the same library).
Expected Behavior
- Regex-based angle bracket matching should be replaced with bracket-counting in all Rust code parsing
- String literals must be skipped when counting braces/special characters
RustStr should map to a fat pointer type (Cstruct with ptr + len), not Cstring
- Generic return type inference should parse the actual return type from function signature, not use first type param
readdir calls in cache code should use walkdir for recursive search
- String indexing should use character-safe iteration with
prevind/nextind
Related Files
src/structs.jl
src/julia_functions.jl
src/ruststr.jl
src/codegen.jl
src/generics.jl
src/cache.jl
src/exceptions.jl
src/dependencies.jl
src/types.jl
Summary
Code review identified 12 potential bugs across the RustCall.jl source code.
1. Nested generics regex failure in
_parse_fn_arg_types(generics.jl:755)File:
src/generics.jl:755<[^{]*?>uses non-greedy matching which fails on deeply nested generics likeVec<Option<T>>. The>insideOption<would incorrectly match before the closing>ofVec<. This violates the project's own documented rule: "Never use regex alone to match angle brackets (< >) in Rust code."2. Incorrect bracket depth tracking for
[and>(structs.jl:64-68, julia_functions.jl:206-215, ruststr.jl:1003-1031)Files:
src/structs.jl:64-68,src/julia_functions.jl:206-215,src/ruststr.jl:1003-1031Using
max(0, ...)for bracket depth clamping causes[-delimited brackets and>-delimited brackets to be tracked independently of each other. This means a<inside an array literal[a < b]would still incrementangle_depth, and a>inside[a < b]would decrement it, leading to incorrect depth tracking when arrays contain comparison operators.3. Unusual return value from
drop!fallback (types.jl:837-843)File:
src/types.jl:837-843Julia convention for
!-suffixed mutation functions is to return nothing (not the tuple(nothing)). While this works due to implicit tuple return, it is non-idiomatic.4. String literal brace counting causes false positives (exceptions.jl:559-565)
File:
src/exceptions.jl:559-565Brace counting includes braces inside string literals like
s = "{test}", causing false positive suggestions for mismatched braces when none exist.5.
RustStrincorrectly mapped toCstringin codegen (codegen.jl:125)File:
src/codegen.jl:125RustStris a fat pointer (ptr + len) representing&str. Mapping it toCstring(null-terminated string) loses the length information and causes ABI mismatch for FFI calls expecting&str.6. Generic return type inference is oversimplified (generics.jl:973-1003)
File:
src/generics.jl:973-1003The first type parameter is assumed as the return type. For functions like
fn transform<T, U>(x: T) -> U, this returnsTinstead ofU, causing type mismatches.7.
readdirvswalkdirinconsistency in cache size calculation (cache.jl:459-464, cache.jl:485)File:
src/cache.jl:485list_cached_librariesusesreaddir(non-recursive), whileget_cache_sizeuseswalkdir(recursive). This means.dylibfiles in subdirectories of the cache would be counted byget_cache_sizebut missed bylist_cached_libraries.8.
_count_trailing_backslashesuses unsafe indexing (dependencies.jl:288)File:
src/dependencies.jl:288Using linear index iteration on multi-byte strings is unsafe. Should use
prevind-based iteration or iterate bytes instead.9.
lib_name/ cache key mismatch (ruststr.jl:239-244, ruststr.jl:273-278)File:
src/ruststr.jl:239-244The library registration key uses the first 16 hex chars of a stable content hash. The cache key uses a different computation path. If the compilation pipeline changes between cache save/load, the lookup could fail while the cache file exists.
10.
found_libsduplicate potential in fallback search (ruststr.jl:86-106)File:
src/ruststr.jl:86-106The fallback search in
get_function_pointerdoesn't guard against the same library being queried multiple times ifRUST_LIBRARIEScontains duplicate handles (through different keys pointing to the same library).Expected Behavior
RustStrshould map to a fat pointer type (Cstruct with ptr + len), notCstringreaddircalls in cache code should usewalkdirfor recursive searchprevind/nextindRelated Files
src/structs.jlsrc/julia_functions.jlsrc/ruststr.jlsrc/codegen.jlsrc/generics.jlsrc/cache.jlsrc/exceptions.jlsrc/dependencies.jlsrc/types.jl