Skip to content

Commit 5d3f8fc

Browse files
Reject unsupported Soroban string returns
1 parent 3d0fef6 commit 5d3f8fc

3 files changed

Lines changed: 153 additions & 2 deletions

File tree

src/sema/functions.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,6 +1062,41 @@ pub fn resolve_returns(
10621062
diagnostics.push(Diagnostic::error(r.ty.loc(), message));
10631063
success = false
10641064
}
1065+
1066+
if ns.target == Target::Soroban {
1067+
let unsupported_type = match &ty {
1068+
Type::String
1069+
if !matches!(r.storage, Some(pt::StorageLocation::Storage(_))) =>
1070+
{
1071+
let storage = r
1072+
.storage
1073+
.as_ref()
1074+
.map_or("memory", pt::StorageLocation::as_str);
1075+
Some(format!("string {storage}"))
1076+
}
1077+
Type::DynamicBytes
1078+
if !matches!(r.storage, Some(pt::StorageLocation::Storage(_))) =>
1079+
{
1080+
let storage = r
1081+
.storage
1082+
.as_ref()
1083+
.map_or("memory", pt::StorageLocation::as_str);
1084+
Some(format!("bytes {storage}"))
1085+
}
1086+
Type::Bytes(n) => Some(format!("bytes{n}")),
1087+
_ => None,
1088+
};
1089+
1090+
if let Some(unsupported_type) = unsupported_type {
1091+
diagnostics.push(Diagnostic::error(
1092+
r.loc,
1093+
format!(
1094+
"type '{unsupported_type}' is not supported as a Soroban external function return value"
1095+
),
1096+
));
1097+
success = false;
1098+
}
1099+
}
10651100
}
10661101
let ty = if !ty.can_have_data_location() {
10671102
if let Some(storage) = &r.storage {

src/sema/variables.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,24 @@ pub fn variable_decl<'a>(
497497
ns,
498498
)?;
499499

500+
if ns.target == Target::Soroban {
501+
let unsupported_type = match &param.ty {
502+
Type::DynamicBytes => Some("bytes".to_string()),
503+
Type::Bytes(n) => Some(format!("bytes{n}")),
504+
_ => None,
505+
};
506+
507+
if let Some(unsupported_type) = unsupported_type {
508+
ns.diagnostics.push(Diagnostic::error(
509+
def.ty.loc(),
510+
format!(
511+
"type '{unsupported_type}' is not supported as a Soroban public variable accessor return value"
512+
),
513+
));
514+
return ret;
515+
}
516+
}
517+
500518
if param.ty.contains_mapping(ns) {
501519
// we can't return a mapping
502520
ns.diagnostics.push(Diagnostic::decl_error(

tests/soroban_testcases/unsupported_parameters.rs

Lines changed: 100 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ fn dynamic_bytes_external_parameters_are_rejected() {
7171
fn static_bytes_external_parameters_are_rejected() {
7272
let ns = compile_soroban(
7373
r#"contract test {
74-
function len(bytes32 data) public returns (bytes32) {
75-
return data;
74+
function len(bytes32 data) public returns (uint64) {
75+
return uint64(data.length);
7676
}
7777
}"#,
7878
);
@@ -93,6 +93,104 @@ fn static_bytes_external_parameters_are_rejected() {
9393
.starts_with("2:"));
9494
}
9595

96+
#[test]
97+
fn string_external_returns_are_rejected() {
98+
let ns = compile_soroban(
99+
r#"contract test {
100+
function public_make() public returns (string memory) {
101+
return "hello";
102+
}
103+
104+
function external_make() external returns (string memory) {
105+
return "hello";
106+
}
107+
}"#,
108+
);
109+
110+
let errors = ns
111+
.diagnostics
112+
.iter()
113+
.filter(|diagnostic| diagnostic.level == Level::Error)
114+
.collect::<Vec<_>>();
115+
116+
assert_eq!(errors.len(), 2);
117+
assert!(errors.iter().all(|diagnostic| diagnostic.message
118+
== "type 'string memory' is not supported as a Soroban external function return value"));
119+
120+
let locations = errors
121+
.iter()
122+
.map(|diagnostic| ns.loc_to_string(PathDisplay::None, &diagnostic.loc))
123+
.collect::<Vec<_>>();
124+
125+
assert!(locations.iter().any(|loc| loc.starts_with("2:")));
126+
assert!(locations.iter().any(|loc| loc.starts_with("6:")));
127+
}
128+
129+
#[test]
130+
fn bytes_external_returns_are_rejected() {
131+
let ns = compile_soroban(
132+
r#"contract test {
133+
function public_make() public returns (bytes memory) {
134+
return hex"01";
135+
}
136+
137+
function external_make() external returns (bytes32) {
138+
return bytes32(uint256(1));
139+
}
140+
}"#,
141+
);
142+
143+
let errors = ns
144+
.diagnostics
145+
.iter()
146+
.filter(|diagnostic| diagnostic.level == Level::Error)
147+
.collect::<Vec<_>>();
148+
149+
assert_eq!(errors.len(), 2);
150+
assert!(errors.iter().any(|diagnostic| diagnostic.message
151+
== "type 'bytes memory' is not supported as a Soroban external function return value"));
152+
assert!(errors.iter().any(|diagnostic| diagnostic.message
153+
== "type 'bytes32' is not supported as a Soroban external function return value"));
154+
155+
let locations = errors
156+
.iter()
157+
.map(|diagnostic| ns.loc_to_string(PathDisplay::None, &diagnostic.loc))
158+
.collect::<Vec<_>>();
159+
160+
assert!(locations.iter().any(|loc| loc.starts_with("2:")));
161+
assert!(locations.iter().any(|loc| loc.starts_with("6:")));
162+
}
163+
164+
#[test]
165+
fn bytes_public_accessors_are_rejected() {
166+
let ns = compile_soroban(
167+
r#"contract test {
168+
bytes public dynamic_data;
169+
bytes32 public fixed_data;
170+
}"#,
171+
);
172+
173+
let errors = ns
174+
.diagnostics
175+
.iter()
176+
.filter(|diagnostic| diagnostic.level == Level::Error)
177+
.collect::<Vec<_>>();
178+
179+
assert_eq!(errors.len(), 2);
180+
assert!(errors.iter().any(|diagnostic| diagnostic.message
181+
== "type 'bytes' is not supported as a Soroban public variable accessor return value"));
182+
assert!(errors.iter().any(|diagnostic| diagnostic.message
183+
== "type 'bytes32' is not supported as a Soroban public variable accessor return value"));
184+
185+
let locations = errors
186+
.iter()
187+
.map(|diagnostic| ns.loc_to_string(PathDisplay::None, &diagnostic.loc))
188+
.collect::<Vec<_>>();
189+
190+
assert!(locations.iter().any(|loc| loc.starts_with("2:")));
191+
assert!(locations.iter().any(|loc| loc.starts_with("3:")));
192+
}
193+
96194
#[test]
97195
fn internal_private_dynamic_bytes_parameters_are_allowed() {
98196
let ns = compile_soroban(

0 commit comments

Comments
 (0)