Skip to content

Commit f4e44fb

Browse files
committed
Implement expressive diagnostics for ABI name attribute.
1 parent 644c1c5 commit f4e44fb

File tree

6 files changed

+119
-45
lines changed

6 files changed

+119
-45
lines changed

forc-pkg/src/pkg.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1791,7 +1791,7 @@ pub fn compile(
17911791
panic_occurrences: &asm.panic_occurrences,
17921792
abi_with_callpaths: true,
17931793
type_ids_to_full_type_str: HashMap::<String, String>::new(),
1794-
unique_names: HashSet::<String>::new(),
1794+
unique_names: HashMap::<String, Span>::new(),
17951795
},
17961796
engines,
17971797
if experimental.new_encoding {

sway-core/src/abi_generation/fuel_abi.rs

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use sway_error::{
99
handler::{ErrorEmitted, Handler},
1010
};
1111
use sway_parse::is_valid_identifier_or_path;
12-
use sway_types::Span;
12+
use sway_types::{Span, Spanned};
1313

1414
use crate::{
1515
ast_elements::type_parameter::GenericTypeParameter,
@@ -25,7 +25,7 @@ pub struct AbiContext<'a> {
2525
pub panic_occurrences: &'a PanicOccurrences,
2626
pub abi_with_callpaths: bool,
2727
pub type_ids_to_full_type_str: HashMap<String, String>,
28-
pub unique_names: HashSet<String>,
28+
pub unique_names: HashMap<String, Span>,
2929
}
3030

3131
impl AbiContext<'_> {
@@ -39,6 +39,40 @@ impl AbiContext<'_> {
3939
}
4040
}
4141

42+
pub fn extract_abi_name_inner(span: &Span) -> Option<Span> {
43+
let text = &span.src().text;
44+
let full_attr: &str = &text[span.start()..span.end()];
45+
46+
// Find the "name" key.
47+
let name_key_pos = full_attr.find("name")?;
48+
let after_name = &full_attr[name_key_pos..];
49+
50+
// Find the '=' after "name".
51+
let eq_offset_rel = after_name.find('=')?;
52+
let after_eq = &after_name[eq_offset_rel + 1..];
53+
54+
// Find the opening quote of the literal.
55+
let first_quote_rel = after_eq.find('"')?;
56+
let ident_abs_start = span.start()
57+
+ name_key_pos
58+
+ eq_offset_rel
59+
+ 1 // move past '='
60+
+ first_quote_rel
61+
+ 1; // move past the opening '"'
62+
63+
// Starting at ident_abs_start, locate the closing quote.
64+
let rest_after_ident = &text[ident_abs_start..span.end()];
65+
let second_quote_rel = rest_after_ident.find('"')?;
66+
let ident_abs_end = ident_abs_start + second_quote_rel;
67+
68+
Span::new(
69+
span.src().clone(),
70+
ident_abs_start - 1,
71+
ident_abs_end + 1,
72+
span.source_id().cloned(),
73+
)
74+
}
75+
4276
impl TypeId {
4377
fn get_abi_name_and_span_from_type_id(
4478
engines: &Engines,
@@ -116,15 +150,30 @@ impl TypeId {
116150
None => (String::new(), Span::dummy()),
117151
};
118152

119-
let inserted = ctx.unique_names.insert(type_str.clone());
153+
let name_span = extract_abi_name_inner(&span).unwrap_or(span.clone());
154+
155+
let other_span = match *engines.te().get(resolved_type_id) {
156+
TypeInfo::Enum(decl_id) => engines.de().get_enum(&decl_id).span(),
157+
TypeInfo::Struct(decl_id) => engines.de().get_struct(&decl_id).span(),
158+
_ => unreachable!(),
159+
};
160+
161+
let inserted = ctx
162+
.unique_names
163+
.insert(type_str.clone(), other_span.clone());
120164
if has_abi_name_attribute {
121165
if name.is_empty() || !is_valid_identifier_or_path(name.as_str()) {
122-
err =
123-
Some(handler.emit_err(CompileError::ABIInvalidName { span: span.clone() }));
166+
err = Some(handler.emit_err(CompileError::ABIInvalidName {
167+
span: name_span.clone(),
168+
name,
169+
}));
124170
}
125171

126-
if !inserted {
127-
err = Some(handler.emit_err(CompileError::ABIDuplicateName { span }));
172+
if let Some(other_span) = inserted {
173+
err = Some(handler.emit_err(CompileError::ABIDuplicateName {
174+
span: name_span.clone(),
175+
other_span,
176+
}));
128177
}
129178
}
130179
}

sway-error/src/error.rs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,9 +1045,9 @@ pub enum CompileError {
10451045
#[error("Configurables need a function named \"abi_decode_in_place\" to be in scope.")]
10461046
ConfigurableMissingAbiDecodeInPlace { span: Span },
10471047
#[error("Invalid name found for renamed ABI type.\n")]
1048-
ABIInvalidName { span: Span },
1048+
ABIInvalidName { span: Span, name: String },
10491049
#[error("Duplicated name found for renamed ABI type.\n")]
1050-
ABIDuplicateName { span: Span },
1050+
ABIDuplicateName { span: Span, other_span: Span },
10511051
#[error("Collision detected between two different types.\n Shared hash:{hash}\n First type:{first_type}\n Second type:{second_type}")]
10521052
ABIHashCollision {
10531053
span: Span,
@@ -3087,6 +3087,32 @@ impl ToDiagnostic for CompileError {
30873087
help
30883088
},
30893089
},
3090+
ABIDuplicateName { span, other_span: other } => Diagnostic {
3091+
reason: Some(Reason::new(code(1), "Duplicated name found for renamed ABI type.".into())),
3092+
issue: Issue::error(
3093+
source_engine,
3094+
span.clone(),
3095+
String::new()
3096+
),
3097+
hints: vec![
3098+
Hint::help(
3099+
source_engine,
3100+
other.clone(),
3101+
"This is the existing attribute".into(),
3102+
)
3103+
],
3104+
help: vec!["The name must be a valid Sway identifier ".into()],
3105+
},
3106+
ABIInvalidName { span, name } => Diagnostic {
3107+
reason: Some(Reason::new(code(1), "Invalid name found for renamed ABI type.".into())),
3108+
issue: Issue::error(
3109+
source_engine,
3110+
span.clone(),
3111+
String::new()
3112+
),
3113+
hints: vec![],
3114+
help: vec![format!("The name must be a valid Sway identifier{}", if name.is_empty() { " and cannot be empty" } else { "" })],
3115+
},
30903116
_ => Diagnostic {
30913117
// TODO: Temporarily we use `self` here to achieve backward compatibility.
30923118
// In general, `self` must not be used. All the values for the formatting

test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/src/lib.sw

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ contract;
33
#[abi_name(name = "SameName")]
44
struct MyStruct {}
55

6+
#[abi_name(name = "MyStruct")]
7+
struct MyStruct0 {}
8+
69
#[abi_name(name = "SameName")]
710
struct MyStruct1 {}
811

@@ -26,6 +29,7 @@ enum MyEnum {
2629

2730
abi MyAbi {
2831
fn my_struct() -> MyStruct;
32+
fn my_struct0() -> MyStruct0;
2933
fn my_struct1() -> MyStruct1;
3034
fn my_struct2() -> MyStruct2;
3135
fn my_struct3() -> MyStruct3;
@@ -36,6 +40,7 @@ abi MyAbi {
3640

3741
impl MyAbi for Contract {
3842
fn my_struct() -> MyStruct { MyStruct{} }
43+
fn my_struct0() -> MyStruct0 { MyStruct0{} }
3944
fn my_struct1() -> MyStruct1 { MyStruct1{} }
4045
fn my_struct2() -> MyStruct2 { MyStruct2{} }
4146
fn my_struct3() -> MyStruct3 { MyStruct3{} }

test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/stdout.snap

Lines changed: 28 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,56 +7,50 @@ output:
77
Building test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names
88
Compiling library std (sway-lib-std)
99
Compiling contract attributes_invalid_abi_names (test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names)
10-
error
11-
--> test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/src/lib.sw:6:1
10+
error: Duplicated name found for renamed ABI type.
11+
--> test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/src/lib.sw:9:19
1212
|
13+
...
1314
4 | struct MyStruct {}
14-
5 |
15-
6 | #[abi_name(name = "SameName")]
16-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Duplicated name found for renamed ABI type.
17-
18-
7 | struct MyStruct1 {}
19-
8 |
15+
| ------------------ help: This is the existing attribute
16+
...
17+
9 | #[abi_name(name = "SameName")]
18+
| ^^^^^^^^^^
2019
|
20+
= help: The name must be a valid Sway identifier
2121
____
2222

23-
error
24-
--> test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/src/lib.sw:9:1
23+
error: Duplicated name found for renamed ABI type.
24+
--> test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/src/lib.sw:12:19
2525
|
26-
7 | struct MyStruct1 {}
27-
8 |
28-
9 | #[abi_name(name = "SameName")]
29-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Duplicated name found for renamed ABI type.
30-
31-
10 | struct MyStruct2 {}
26+
...
27+
10 | struct MyStruct1 {}
28+
| ------------------- help: This is the existing attribute
3229
11 |
30+
12 | #[abi_name(name = "SameName")]
31+
| ^^^^^^^^^^
3332
|
33+
= help: The name must be a valid Sway identifier
3434
____
3535

36-
error
37-
--> test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/src/lib.sw:12:1
36+
error: Invalid name found for renamed ABI type.
37+
--> test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/src/lib.sw:15:19
3838
|
39-
10 | struct MyStruct2 {}
40-
11 |
41-
12 | #[abi_name(name = "")]
42-
| ^^^^^^^^^^^^^^^^^^^^^^ Invalid name found for renamed ABI type.
43-
44-
13 | struct MyStruct3 {}
45-
14 |
39+
...
40+
15 | #[abi_name(name = "")]
41+
| ^^
4642
|
43+
= help: The name must be a valid Sway identifier and cannot be empty
4744
____
4845

49-
error
50-
--> test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/src/lib.sw:15:1
46+
error: Invalid name found for renamed ABI type.
47+
--> test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_abi_names/src/lib.sw:18:19
5148
|
52-
13 | struct MyStruct3 {}
53-
14 |
54-
15 | #[abi_name(name = "this !s n0t an identif1er")]
55-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Invalid name found for renamed ABI type.
56-
57-
16 | struct MyStruct4 {}
58-
17 |
49+
...
50+
18 | #[abi_name(name = "this !s n0t an identif1er")]
51+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
5952
|
53+
= help: The name must be a valid Sway identifier
6054
____
6155

6256
Aborting due to 4 errors.

test/src/e2e_vm_tests/test_programs/should_pass/language/attributes_abi_name/Forc.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ license = "Apache-2.0"
55
name = "attributes_abi_name"
66

77
[dependencies]
8-
std = { path = "../../../../../../../sway-lib-std-core" }
8+
std = { path = "../../../../reduced_std_libs/sway-lib-std-core" }
99

0 commit comments

Comments
 (0)