@@ -9,7 +9,7 @@ use sway_error::{
9
9
handler:: { ErrorEmitted , Handler } ,
10
10
} ;
11
11
use sway_parse:: is_valid_identifier_or_path;
12
- use sway_types:: Span ;
12
+ use sway_types:: { Named , Span , Spanned } ;
13
13
14
14
use crate :: {
15
15
ast_elements:: type_parameter:: GenericTypeParameter ,
@@ -20,12 +20,27 @@ use crate::{
20
20
21
21
use super :: abi_str:: AbiStrContext ;
22
22
23
+ #[ derive( Clone , Debug ) ]
24
+ pub enum AbiNameDiagnosticSpan {
25
+ Attribute ( Span ) ,
26
+ Type ( Span ) ,
27
+ }
28
+
29
+ impl AbiNameDiagnosticSpan {
30
+ pub fn span ( self ) -> Span {
31
+ match self {
32
+ Self :: Attribute ( span) => span,
33
+ Self :: Type ( span) => span,
34
+ }
35
+ }
36
+ }
37
+
23
38
pub struct AbiContext < ' a > {
24
39
pub program : & ' a TyProgram ,
25
40
pub panic_occurrences : & ' a PanicOccurrences ,
26
41
pub abi_with_callpaths : bool ,
27
42
pub type_ids_to_full_type_str : HashMap < String , String > ,
28
- pub unique_names : HashSet < String > ,
43
+ pub unique_names : HashMap < String , AbiNameDiagnosticSpan > ,
29
44
}
30
45
31
46
impl AbiContext < ' _ > {
@@ -39,6 +54,40 @@ impl AbiContext<'_> {
39
54
}
40
55
}
41
56
57
+ pub fn extract_abi_name_inner ( span : & Span ) -> Option < Span > {
58
+ let text = & span. src ( ) . text ;
59
+ let full_attr: & str = & text[ span. start ( ) ..span. end ( ) ] ;
60
+
61
+ // Find the "name" key.
62
+ let name_key_pos = full_attr. find ( "name" ) ?;
63
+ let after_name = & full_attr[ name_key_pos..] ;
64
+
65
+ // Find the '=' after "name".
66
+ let eq_offset_rel = after_name. find ( '=' ) ?;
67
+ let after_eq = & after_name[ eq_offset_rel + 1 ..] ;
68
+
69
+ // Find the opening quote of the literal.
70
+ let first_quote_rel = after_eq. find ( '"' ) ?;
71
+ let ident_abs_start = span. start ( )
72
+ + name_key_pos
73
+ + eq_offset_rel
74
+ + 1 // move past '='
75
+ + first_quote_rel
76
+ + 1 ; // move past the opening '"'
77
+
78
+ // Starting at ident_abs_start, locate the closing quote.
79
+ let rest_after_ident = & text[ ident_abs_start..span. end ( ) ] ;
80
+ let second_quote_rel = rest_after_ident. find ( '"' ) ?;
81
+ let ident_abs_end = ident_abs_start + second_quote_rel;
82
+
83
+ Span :: new (
84
+ span. src ( ) . clone ( ) ,
85
+ ident_abs_start - 1 ,
86
+ ident_abs_end + 1 ,
87
+ span. source_id ( ) . cloned ( ) ,
88
+ )
89
+ }
90
+
42
91
impl TypeId {
43
92
fn get_abi_name_and_span_from_type_id (
44
93
engines : & Engines ,
@@ -73,7 +122,7 @@ impl TypeId {
73
122
}
74
123
None => Ok ( None ) ,
75
124
}
76
- } ,
125
+ }
77
126
_ => Ok ( None ) ,
78
127
}
79
128
}
@@ -107,7 +156,7 @@ impl TypeId {
107
156
108
157
if should_check_name {
109
158
let mut has_abi_name_attribute = false ;
110
- let ( name, span ) =
159
+ let ( name, attribute_span ) =
111
160
match Self :: get_abi_name_and_span_from_type_id ( engines, resolved_type_id) ? {
112
161
Some ( res) => {
113
162
has_abi_name_attribute = true ;
@@ -116,15 +165,42 @@ impl TypeId {
116
165
None => ( String :: new ( ) , Span :: dummy ( ) ) ,
117
166
} ;
118
167
119
- let inserted = ctx. unique_names . insert ( type_str. clone ( ) ) ;
168
+ let attribute_name_span =
169
+ extract_abi_name_inner ( & attribute_span) . unwrap_or ( attribute_span. clone ( ) ) ;
170
+
171
+ let type_span = match * engines. te ( ) . get ( resolved_type_id) {
172
+ TypeInfo :: Enum ( decl_id) => engines. de ( ) . get_enum ( & decl_id) . name ( ) . span ( ) ,
173
+ TypeInfo :: Struct ( decl_id) => engines. de ( ) . get_struct ( & decl_id) . name ( ) . span ( ) ,
174
+ _ => unreachable ! ( ) ,
175
+ } ;
176
+
177
+ let insert_span = if has_abi_name_attribute {
178
+ AbiNameDiagnosticSpan :: Attribute ( attribute_span. clone ( ) )
179
+ } else {
180
+ AbiNameDiagnosticSpan :: Type ( type_span. clone ( ) )
181
+ } ;
182
+
183
+ let prev_span_opt = if ctx. unique_names . contains_key ( & type_str) {
184
+ ctx. unique_names . get ( & type_str) . cloned ( )
185
+ } else {
186
+ ctx. unique_names . insert ( type_str. clone ( ) , insert_span)
187
+ } ;
188
+
120
189
if has_abi_name_attribute {
121
190
if name. is_empty ( ) || !is_valid_identifier_or_path ( name. as_str ( ) ) {
122
- err =
123
- Some ( handler. emit_err ( CompileError :: ABIInvalidName { span : span. clone ( ) } ) ) ;
191
+ err = Some ( handler. emit_err ( CompileError :: ABIInvalidName {
192
+ span : attribute_name_span. clone ( ) ,
193
+ name,
194
+ } ) ) ;
124
195
}
125
196
126
- if !inserted {
127
- err = Some ( handler. emit_err ( CompileError :: ABIDuplicateName { span } ) ) ;
197
+ if let Some ( prev_span) = prev_span_opt {
198
+ let is_attribute = matches ! ( prev_span, AbiNameDiagnosticSpan :: Attribute ( _) ) ;
199
+ err = Some ( handler. emit_err ( CompileError :: ABIDuplicateName {
200
+ span : attribute_name_span. clone ( ) ,
201
+ other_span : prev_span. span ( ) ,
202
+ is_attribute,
203
+ } ) ) ;
128
204
}
129
205
}
130
206
}
@@ -157,6 +233,47 @@ impl TypeId {
157
233
}
158
234
}
159
235
236
+ fn insert_unique_type ( ctx : & mut AbiContext , name : String , span : Span ) {
237
+ let _ = ctx
238
+ . unique_names
239
+ . insert ( name, AbiNameDiagnosticSpan :: Type ( span) ) ;
240
+ }
241
+
242
+ fn process_type_name ( ctx : & mut AbiContext , engines : & Engines , type_id : TypeId ) {
243
+ match & * engines. te ( ) . get ( type_id) {
244
+ TypeInfo :: Enum ( decl_id) => {
245
+ let enum_decl = engines. de ( ) . get_enum ( decl_id) ;
246
+ insert_unique_type (
247
+ ctx,
248
+ format ! ( "enum {}" , enum_decl. name( ) ) ,
249
+ enum_decl. name ( ) . span ( ) ,
250
+ ) ;
251
+ }
252
+ TypeInfo :: Struct ( decl_id) => {
253
+ let struct_decl = engines. de ( ) . get_struct ( decl_id) ;
254
+ insert_unique_type (
255
+ ctx,
256
+ format ! ( "struct {}" , struct_decl. name( ) ) ,
257
+ struct_decl. name ( ) . span ( ) ,
258
+ ) ;
259
+ }
260
+ TypeInfo :: Alias { name : _, ty } => process_type_name ( ctx, engines, ty. type_id ( ) ) ,
261
+ _ => { }
262
+ }
263
+ }
264
+
265
+ fn process_type_names_from_function (
266
+ ctx : & mut AbiContext ,
267
+ engines : & Engines ,
268
+ function : & TyFunctionDecl ,
269
+ ) {
270
+ process_type_name ( ctx, engines, function. return_type . type_id ( ) ) ;
271
+
272
+ for param in & function. parameters {
273
+ process_type_name ( ctx, engines, param. type_argument . type_id ( ) ) ;
274
+ }
275
+ }
276
+
160
277
pub fn generate_program_abi (
161
278
handler : & Handler ,
162
279
ctx : & mut AbiContext ,
@@ -167,6 +284,25 @@ pub fn generate_program_abi(
167
284
let decl_engine = engines. de ( ) ;
168
285
let metadata_types: & mut Vec < program_abi:: TypeMetadataDeclaration > = & mut vec ! [ ] ;
169
286
let concrete_types: & mut Vec < program_abi:: TypeConcreteDeclaration > = & mut vec ! [ ] ;
287
+
288
+ match & ctx. program . kind {
289
+ TyProgramKind :: Contract { abi_entries, .. } => {
290
+ abi_entries. iter ( ) . for_each ( |x| {
291
+ let fn_decl = decl_engine. get_function ( x) ;
292
+ process_type_names_from_function ( ctx, engines, & fn_decl) ;
293
+ } ) ;
294
+ }
295
+ TyProgramKind :: Script { main_function, .. } => {
296
+ let main_function = decl_engine. get_function ( main_function) ;
297
+ process_type_names_from_function ( ctx, engines, & main_function) ;
298
+ }
299
+ TyProgramKind :: Predicate { main_function, .. } => {
300
+ let main_function = decl_engine. get_function ( main_function) ;
301
+ process_type_names_from_function ( ctx, engines, & main_function) ;
302
+ }
303
+ TyProgramKind :: Library { .. } => { }
304
+ } ;
305
+
170
306
let mut program_abi = handler. scope ( |handler| match & ctx. program . kind {
171
307
TyProgramKind :: Contract { abi_entries, .. } => {
172
308
let functions = abi_entries
0 commit comments