@@ -6,7 +6,10 @@ use foundry_compilers::{
6
6
} ;
7
7
use itertools:: Itertools ;
8
8
use serde_json:: Value ;
9
- use solar:: ast;
9
+ use solar:: {
10
+ ast:: { self , Span } ,
11
+ interface:: Session ,
12
+ } ;
10
13
use std:: { collections:: BTreeMap , path:: Path } ;
11
14
12
15
/// Convenient struct to hold in-line per-test configurations
@@ -16,7 +19,7 @@ pub struct NatSpec {
16
19
pub contract : String ,
17
20
/// The function annotated with the natspec. None if the natspec is contract-level.
18
21
pub function : Option < String > ,
19
- /// The line the natspec appears , in the form `row:col:length `, i.e. `10:21:122 `.
22
+ /// The line the natspec begins , in the form `line:column `, i.e. `10:21`.
20
23
pub line : String ,
21
24
/// The actual natspec comment, without slashes or block punctuation.
22
25
pub docs : String ,
@@ -30,7 +33,8 @@ impl NatSpec {
30
33
pub fn parse ( output : & ProjectCompileOutput , root : & Path ) -> Vec < Self > {
31
34
let mut natspecs: Vec < Self > = vec ! [ ] ;
32
35
33
- let solar = SolarParser :: new ( ) ;
36
+ let compiler = output. parser ( ) . solc ( ) . compiler ( ) ;
37
+ let solar = SolarParser :: new ( compiler. sess ( ) ) ;
34
38
let solc = SolcParser :: new ( ) ;
35
39
for ( id, artifact) in output. artifact_ids ( ) {
36
40
let path = id. source . as_path ( ) ;
@@ -41,7 +45,6 @@ impl NatSpec {
41
45
let contract = format ! ( "{}:{}" , path. display( ) , id. name) ;
42
46
43
47
let mut used_solar = false ;
44
- let compiler = output. parser ( ) . solc ( ) . compiler ( ) ;
45
48
compiler. enter_sequential ( |compiler| {
46
49
if let Some ( ( _, source) ) = compiler. gcx ( ) . get_ast_source ( abs_path)
47
50
&& let Some ( ast) = & source. ast
@@ -214,13 +217,13 @@ impl SolcParser {
214
217
}
215
218
}
216
219
217
- struct SolarParser {
218
- _private : ( ) ,
220
+ struct SolarParser < ' a > {
221
+ sess : & ' a Session ,
219
222
}
220
223
221
- impl SolarParser {
222
- fn new ( ) -> Self {
223
- Self { _private : ( ) }
224
+ impl < ' a > SolarParser < ' a > {
225
+ fn new ( sess : & ' a Session ) -> Self {
226
+ Self { sess }
224
227
}
225
228
226
229
fn parse_ast (
@@ -234,6 +237,7 @@ impl SolarParser {
234
237
if item. docs . is_empty ( ) {
235
238
return ;
236
239
}
240
+ let mut span = Span :: DUMMY ;
237
241
let lines = item
238
242
. docs
239
243
. iter ( )
@@ -242,6 +246,7 @@ impl SolarParser {
242
246
if !s. contains ( INLINE_CONFIG_PREFIX ) {
243
247
return None ;
244
248
}
249
+ span = if span. is_dummy ( ) { d. span } else { span. to ( d. span ) } ;
245
250
match d. kind {
246
251
ast:: CommentKind :: Line => Some ( s. trim ( ) . to_string ( ) ) ,
247
252
ast:: CommentKind :: Block => Some (
@@ -257,8 +262,6 @@ impl SolarParser {
257
262
if lines. is_empty ( ) {
258
263
return ;
259
264
}
260
- let span =
261
- item. docs . iter ( ) . map ( |doc| doc. span ) . reduce ( |a, b| a. to ( b) ) . unwrap_or_default ( ) ;
262
265
natspecs. push ( NatSpec {
263
266
contract : contract_id. to_string ( ) ,
264
267
function : if let ast:: ItemKind :: Function ( f) = & item. kind {
@@ -271,7 +274,10 @@ impl SolarParser {
271
274
} else {
272
275
None
273
276
} ,
274
- line : format ! ( "{}:{}:0" , span. lo( ) . 0 , span. hi( ) . 0 ) ,
277
+ line : {
278
+ let ( _, loc) = self . sess . source_map ( ) . span_to_location_info ( span) ;
279
+ format ! ( "{}:{}" , loc. lo. line, loc. lo. col. 0 + 1 )
280
+ } ,
275
281
docs : lines,
276
282
} ) ;
277
283
} ;
@@ -298,12 +304,10 @@ impl SolarParser {
298
304
mod tests {
299
305
use super :: * ;
300
306
use serde_json:: json;
307
+ use snapbox:: { assert_data_eq, str} ;
301
308
use solar:: parse:: {
302
309
Parser ,
303
- ast:: {
304
- Arena ,
305
- interface:: { self , Session } ,
306
- } ,
310
+ ast:: { Arena , interface} ,
307
311
} ;
308
312
309
313
fn parse ( natspecs : & mut Vec < NatSpec > , src : & str , contract_id : & str , contract_name : & str ) {
@@ -315,6 +319,7 @@ mod tests {
315
319
let sess = Session :: builder ( )
316
320
. with_silent_emitter ( Some ( "Inline config parsing failed" . to_string ( ) ) )
317
321
. build ( ) ;
322
+ let solar = SolarParser :: new ( & sess) ;
318
323
let _ = sess. enter ( || -> interface:: Result < ( ) > {
319
324
let arena = Arena :: new ( ) ;
320
325
@@ -327,7 +332,7 @@ mod tests {
327
332
328
333
let source_unit = parser. parse_file ( ) . map_err ( |e| e. emit ( ) ) ?;
329
334
330
- SolarParser :: new ( ) . parse_ast ( natspecs, & source_unit, contract_id, contract_name) ;
335
+ solar . parse_ast ( natspecs, & source_unit, contract_id, contract_name) ;
331
336
332
337
Ok ( ( ) )
333
338
} ) ;
@@ -388,40 +393,45 @@ function f2() {} /** forge-config: default.fuzz.runs = 800 */ function f3() {}
388
393
}
389
394
" ;
390
395
let mut natspecs = vec ! [ ] ;
391
- let id = || "path.sol:C" . to_string ( ) ;
392
- parse ( & mut natspecs, src, & id ( ) , "C" ) ;
393
- assert_eq ! (
394
- natspecs,
395
- [
396
- // f1
397
- NatSpec {
398
- contract: id( ) ,
399
- function: Some ( "f1" . to_string( ) ) ,
400
- line: "14:134:0" . to_string( ) ,
401
- docs: "forge-config: default.fuzz.runs = 600\n forge-config: default.fuzz.runs = 601" . to_string( ) ,
402
- } ,
403
- // f2
404
- NatSpec {
405
- contract: id( ) ,
406
- function: Some ( "f2" . to_string( ) ) ,
407
- line: "164:208:0" . to_string( ) ,
408
- docs: "forge-config: default.fuzz.runs = 700" . to_string( ) ,
409
- } ,
410
- // f3
411
- NatSpec {
412
- contract: id( ) ,
413
- function: Some ( "f3" . to_string( ) ) ,
414
- line: "226:270:0" . to_string( ) ,
415
- docs: "forge-config: default.fuzz.runs = 800" . to_string( ) ,
416
- } ,
417
- // f4
418
- NatSpec {
419
- contract: id( ) ,
420
- function: Some ( "f4" . to_string( ) ) ,
421
- line: "289:391:0" . to_string( ) ,
422
- docs: "forge-config: default.fuzz.runs = 1024\n forge-config: default.fuzz.max-test-rejects = 500" . to_string( ) ,
423
- } ,
424
- ]
396
+ parse ( & mut natspecs, src, "path.sol:C" , "C" ) ;
397
+ assert_data_eq ! (
398
+ format!( "{natspecs:#?}" ) ,
399
+ str ![ [ r#"
400
+ [
401
+ NatSpec {
402
+ contract: "path.sol:C",
403
+ function: Some(
404
+ "f1",
405
+ ),
406
+ line: "2:14",
407
+ docs: "forge-config: default.fuzz.runs = 600/nforge-config: default.fuzz.runs = 601",
408
+ },
409
+ NatSpec {
410
+ contract: "path.sol:C",
411
+ function: Some(
412
+ "f2",
413
+ ),
414
+ line: "7:8",
415
+ docs: "forge-config: default.fuzz.runs = 700",
416
+ },
417
+ NatSpec {
418
+ contract: "path.sol:C",
419
+ function: Some(
420
+ "f3",
421
+ ),
422
+ line: "8:18",
423
+ docs: "forge-config: default.fuzz.runs = 800",
424
+ },
425
+ NatSpec {
426
+ contract: "path.sol:C",
427
+ function: Some(
428
+ "f4",
429
+ ),
430
+ line: "10:1",
431
+ docs: "forge-config: default.fuzz.runs = 1024/nforge-config: default.fuzz.max-test-rejects = 500",
432
+ },
433
+ ]
434
+ "# ] ]
425
435
) ;
426
436
}
427
437
@@ -444,18 +454,21 @@ contract FuzzInlineConf is DSTest {
444
454
}
445
455
"# ;
446
456
let mut natspecs = vec ! [ ] ;
447
- let id = || "inline/FuzzInlineConf.t.sol:FuzzInlineConf" . to_string ( ) ;
448
- parse ( & mut natspecs, src, & id ( ) , "FuzzInlineConf" ) ;
449
- assert_eq ! (
450
- natspecs,
451
- [
452
- NatSpec {
453
- contract: id( ) ,
454
- function: Some ( "testInlineConfFuzz" . to_string( ) ) ,
455
- line: "141:255:0" . to_string( ) ,
456
- docs: "forge-config: default.fuzz.runs = 1024\n forge-config: default.fuzz.max-test-rejects = 500" . to_string( ) ,
457
- } ,
458
- ]
457
+ parse ( & mut natspecs, src, "inline/FuzzInlineConf.t.sol:FuzzInlineConf" , "FuzzInlineConf" ) ;
458
+ assert_data_eq ! (
459
+ format!( "{natspecs:#?}" ) ,
460
+ str ![ [ r#"
461
+ [
462
+ NatSpec {
463
+ contract: "inline/FuzzInlineConf.t.sol:FuzzInlineConf",
464
+ function: Some(
465
+ "testInlineConfFuzz",
466
+ ),
467
+ line: "8:5",
468
+ docs: "forge-config: default.fuzz.runs = 1024/nforge-config: default.fuzz.max-test-rejects = 500",
469
+ },
470
+ ]
471
+ "# ] ]
459
472
) ;
460
473
}
461
474
@@ -532,30 +545,44 @@ contract FuzzInlineConf2 is DSTest {
532
545
}
533
546
"# ;
534
547
let mut natspecs = vec ! [ ] ;
535
- let id = || "inline/FuzzInlineConf.t.sol:FuzzInlineConf" . to_string ( ) ;
536
- parse ( & mut natspecs, src, & id ( ) , "FuzzInlineConf" ) ;
537
- assert_eq ! (
538
- natspecs,
539
- [ NatSpec {
540
- contract: id( ) ,
541
- function: Some ( "testInlineConfFuzz1" . to_string( ) ) ,
542
- line: "142:181:0" . to_string( ) ,
543
- docs: "forge-config: default.fuzz.runs = 1" . to_string( ) ,
544
- } , ]
548
+ parse ( & mut natspecs, src, "inline/FuzzInlineConf.t.sol:FuzzInlineConf" , "FuzzInlineConf" ) ;
549
+ assert_data_eq ! (
550
+ format!( "{natspecs:#?}" ) ,
551
+ str ![ [ r#"
552
+ [
553
+ NatSpec {
554
+ contract: "inline/FuzzInlineConf.t.sol:FuzzInlineConf",
555
+ function: Some(
556
+ "testInlineConfFuzz1",
557
+ ),
558
+ line: "8:6",
559
+ docs: "forge-config: default.fuzz.runs = 1",
560
+ },
561
+ ]
562
+ "# ] ]
545
563
) ;
546
564
547
565
let mut natspecs = vec ! [ ] ;
548
- let id = || "inline/FuzzInlineConf2.t.sol:FuzzInlineConf2" . to_string ( ) ;
549
- parse ( & mut natspecs, src, & id ( ) , "FuzzInlineConf2" ) ;
550
- assert_eq ! (
551
- natspecs,
552
- [ NatSpec {
553
- contract: id( ) ,
554
- function: Some ( "testInlineConfFuzz2" . to_string( ) ) ,
555
- line: "264:303:0" . to_string( ) ,
556
- // should not get config from previous contract
557
- docs: "forge-config: default.fuzz.runs = 2" . to_string( ) ,
558
- } , ]
566
+ parse (
567
+ & mut natspecs,
568
+ src,
569
+ "inline/FuzzInlineConf2.t.sol:FuzzInlineConf2" ,
570
+ "FuzzInlineConf2" ,
571
+ ) ;
572
+ assert_data_eq ! (
573
+ format!( "{natspecs:#?}" ) ,
574
+ str ![ [ r#"
575
+ [
576
+ NatSpec {
577
+ contract: "inline/FuzzInlineConf2.t.sol:FuzzInlineConf2",
578
+ function: Some(
579
+ "testInlineConfFuzz2",
580
+ ),
581
+ line: "13:5",
582
+ docs: "forge-config: default.fuzz.runs = 2",
583
+ },
584
+ ]
585
+ "# ] ]
559
586
) ;
560
587
}
561
588
@@ -575,24 +602,27 @@ contract FuzzInlineConf is DSTest {
575
602
function testInlineConfFuzz2() {}
576
603
}"# ;
577
604
let mut natspecs = vec ! [ ] ;
578
- let id = || "inline/FuzzInlineConf.t.sol:FuzzInlineConf" . to_string ( ) ;
579
- parse ( & mut natspecs, src, & id ( ) , "FuzzInlineConf" ) ;
580
- assert_eq ! (
581
- natspecs,
582
- [
583
- NatSpec {
584
- contract: id( ) ,
585
- function: None ,
586
- line: "101:140:0" . to_string( ) ,
587
- docs: "forge-config: default.fuzz.runs = 1" . to_string( ) ,
588
- } ,
589
- NatSpec {
590
- contract: id( ) ,
591
- function: Some ( "testInlineConfFuzz1" . to_string( ) ) ,
592
- line: "181:220:0" . to_string( ) ,
593
- docs: "forge-config: default.fuzz.runs = 3" . to_string( ) ,
594
- }
595
- ]
605
+ parse ( & mut natspecs, src, "inline/FuzzInlineConf.t.sol:FuzzInlineConf" , "FuzzInlineConf" ) ;
606
+ assert_data_eq ! (
607
+ format!( "{natspecs:#?}" ) ,
608
+ str ![ [ r#"
609
+ [
610
+ NatSpec {
611
+ contract: "inline/FuzzInlineConf.t.sol:FuzzInlineConf",
612
+ function: None,
613
+ line: "7:1",
614
+ docs: "forge-config: default.fuzz.runs = 1",
615
+ },
616
+ NatSpec {
617
+ contract: "inline/FuzzInlineConf.t.sol:FuzzInlineConf",
618
+ function: Some(
619
+ "testInlineConfFuzz1",
620
+ ),
621
+ line: "9:5",
622
+ docs: "forge-config: default.fuzz.runs = 3",
623
+ },
624
+ ]
625
+ "# ] ]
596
626
) ;
597
627
}
598
628
}
0 commit comments