@@ -33,6 +33,27 @@ internal static class RustEmitter
3333 {
3434 private static readonly RustTypeProvider s_typeProvider = new RustTypeProvider ( ) ;
3535
36+ // Rust keywords that are valid C# identifiers but reserved in Rust.
37+ // C# reserved keywords (for example, 'if', 'for', 'return') are excluded
38+ // because they cannot appear as parameter names in managed assemblies.
39+ // See https://doc.rust-lang.org/reference/keywords.html
40+ private static readonly HashSet < string > s_rustKeywords = new ( StringComparer . Ordinal )
41+ {
42+ "async" , "await" , "crate" , "dyn" , "fn" , "impl" , "let" , "loop" ,
43+ "match" , "mod" , "move" , "mut" , "pub" , "self" , "Self" , "super" ,
44+ "trait" , "type" , "use" , "where" , "yield" ,
45+ // Reserved for future use
46+ "become" , "box" , "final" , "macro" , "priv" , "unsized" ,
47+ } ;
48+
49+ /// <summary>
50+ /// Returns a safe Rust identifier for the given name. If the name is a
51+ /// Rust keyword it is prefixed with <c>r#</c>; otherwise it is returned
52+ /// unchanged.
53+ /// </summary>
54+ private static string SafeRustIdentifier ( string name )
55+ => s_rustKeywords . Contains ( name ) ? $ "r#{ name } " : name ;
56+
3657 public static void Emit ( TextWriter outputStream , string assemblyName , IEnumerable < ExportedMethod > exports , IEnumerable < string > additionalCodeStatements )
3758 {
3859 // Emit preamble
@@ -114,7 +135,7 @@ public static void Emit(TextWriter outputStream, string assemblyName, IEnumerabl
114135 string delim = "" ;
115136 for ( int i = 0 ; i < export . ArgumentTypes . Length ; ++ i )
116137 {
117- var argName = export . ArgumentNames [ i ] ?? $ "arg{ i } ";
138+ var argName = SafeRustIdentifier ( export . ArgumentNames [ i ] ?? $ "arg{ i } ") ;
118139 declsig . AppendFormat ( "{0}{1}: {2}" , delim , argName , export . ArgumentTypes [ i ] ) ;
119140 callsig . AppendFormat ( "{0}{1}" , delim , argName ) ;
120141 typesig . AppendFormat ( "{0}{1}" , delim , export . ArgumentTypes [ i ] ) ;
0 commit comments